I miei strumenti coinvolti qui sono GTK e Haskell. Le mie domande sono probabilmente piuttosto banali per chiunque abbia svolto un lavoro di GUI significativo, ma sono stato fuori dall'equivalente delle applicazioni CGI per tutta la mia carriera.
Sto costruendo un'applicazione che visualizza dati tabulari, visualizza gli stessi dati in un modulo grafico e ha un campo di modifica sia per l'inserimento di nuovi dati che per la modifica di dati esistenti. Dopo aver chiesto sulla condivisione delle risorse , ho deciso che tutti i dati coinvolti verranno archiviati in un MVar in modo che ogni componente possa solo leggere lo stato corrente da MVar.
Tutto ciò funziona, ma ora è tempo per me di riorganizzare l'applicazione in modo che possa essere interattiva.
Con questo in mente, ho tre widget: una TextView (per la modifica), una TreeView (per la visualizzazione dei dati) e una DrawingArea (per visualizzare i dati sotto forma di grafico).
I THINK Ho bisogno di fare due cose, e il nocciolo della mia domanda è, sono queste le cose giuste, o c'è un modo migliore.
Il primo: tutti i gestori di eventi, quelle funzioni che verranno chiamate ogni volta che è necessario visualizzare nuovamente, devono essere scritte ad alto livello e quindi passate alla funzione che effettivamente costruisce il widget per cominciare. Ad esempio:
drawStatData :: DrawingArea -> MVar Core.ST -> (Core.ST -> SetRepWorkout.WorkoutStore) -> IO ()
createStatView :: (DrawingArea -> IO ()) -> IO VBox
createUI :: MVar Core.ST -> (Core.ST -> SetRepWorkout.WorkoutStore) -> IO HBox
createUI storeMVar field = do
graphs <- createStatView (\area -> drawStatData area storeMVar field)
hbox <- hBoxNew False 10
boxPackStart hbox graphs PackNatural 0
return hbox
In questo caso, createStatView crea un VBox che contiene una DrawingArea per rappresentare graficamente i dati e potenzialmente altri widget. Allega drawStatData agli eventi di realizzo e exposeEvent per DrawingArea. Vorrei fare qualcosa di simile per TreeView, ma non sono completamente sicuro di ciò dal momento che non l'ho ancora fatto e ciò che sto pensando comporterebbe la sostituzione di TreeModel ogni volta che è necessario aggiornare TreeView.
La mia alternativa a quanto sopra sarebbe ...
drawStatData :: DrawingArea -> MVar Core.ST -> (Core.ST -> SetRepWorkout.WorkoutStore) -> IO ()
createStatView :: IO (VBox, DrawingArea)
... ma in questo caso, organizzerei createUI in questo modo:
createUI :: MVar Core.ST -> (Core.ST -> SetRepWorkout.WorkoutStore) -> IO HBox
createUI storeMVar field = do
(graphbox, graph) <- createStatView (\area -> drawStatData area storeMVar field)
hbox <- hBoxNew False 10
boxPackStart hbox graphs PackNatural 0
on graph realize (drawStatData graph storeMVar field)
on graph exposeEvent (do liftIO $ drawStatData graph storeMVar field
return ())
return hbox
Non sono sicuro di quale sia il migliore, ma questo mi porta a ...
Cosa di seconda: sarà necessario per me allestire un sistema di eventi in modo che vari eventi possano inviare segnali fino ai miei widget. Avrò bisogno di un mediatore di qualche tipo per passare gli eventi in giro e per tradurre gli eventi semantici delle applicazioni sugli eventi reali a cui i miei widget rispondono. È meglio per me trasferire i miei widget indirizzabili nello stack di chiamate fino al livello in cui vive il mediatore o passare il mediatore nello stack delle chiamate e avere i widget registrati direttamente con esso?
Quindi, in sintesi, le mie due domande:
1) passare i widget dallo stack di chiamate a un mediatore globale o passare il mediatore globale verso il basso e fare in modo che i widget si registrino su di esso? 2) passare le mie funzioni di ridisegno ai builder e fare in modo che i builder allegino le funzioni di ridisegno ai widget costruiti, o restituiscano i widget costruiti e abbiano un livello superiore alle funzioni di ridisegno (e potenzialmente collegano insieme alcuni widget)?
Ok, e ...
3) Libri o wiki sull'architettura dell'applicazione GUI, preferibilmente architetture coerenti dove le persone non discutono di dettagli minuti?
L'applicazione nella sua forma attuale (visualizza i dati ma non scrive dati o consente una grande interazione) è disponibile all'indirizzo link . Puoi eseguire l'applicazione andando nella directory principale e digitando
runhaskell -isrc src/Main.hs data/
o ...
cabal build
dist/build/fitness/fitness data/
Potrebbe essere necessario installare le librerie, ma la cabala dovrebbe dirti quali.