Come organizzo un'applicazione GUI per il passaggio di eventi e per l'impostazione di letture da una risorsa condivisa

3

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.

    
posta Savanni D'Gerinel 19.09.2012 - 16:06
fonte

1 risposta

1

Dopo un paio di settimane di lavoro, ho quella che considero una soluzione soddisfacente che ritengo di poter infine ridefinire in una buona soluzione.

Ho deciso che ci sono due tipi di eventi nel sistema. I miei widget di base; i parametri TreeView, DrawingArea, TextView, Submit e Clear; sono un raggruppamento logico. Quel particolare raggruppamento può (e nella mia domanda, è) essere duplicato. Interagire con la maggior parte di essi fornisce una serie di eventi della GUI, ma quegli eventi della GUI sono importanti solo per altri widget nel raggruppamento. Da quella prospettiva, quando costruisco i widget, collego tutti i loro eventi insieme. Questi eventi non lasciano mai il set di widget.

Inoltre, ho una serie di eventi semantici. Finora, gli eventi ammontano a "Workout Submitted" e "Workout Updated". L'evento "Workout inviato" viene disattivato quando viene premuto il pulsante di invio e i dati nel campo di testo vengono analizzati correttamente. L'applicazione di livello superiore elabora tale evento, aggiorna lo stato dell'applicazione e quindi attiva l'evento "Allenamento aggiornato" per indicare all'intera applicazione che un particolare allenamento è stato aggiornato.

Il risultato di tutto ciò sembra relativamente semplice:

  • Crea un dispatcher globale dell'applicazione che si occupa di eventi che hanno significato nel dominio dell'applicazione
  • Se un gruppo di widget è pensato per funzionare insieme nel suo complesso, costruiscili insieme. Associare tutti gli eventi della GUI in cui si influenzano a vicenda ma non l'applicazione. Consegnare loro un modo per registrarsi per eventi applicativi e un modo per attivare eventi applicativi.
  • Fai in modo che i gestori di eventi prendano il minor numero possibile di parametri, prendendo idealmente solo i dati esatti che sono stati aggiornati o che devono essere passati in giro. I dati ausiliari su come elaborarlo dovrebbero essere associati al gestore nella sezione di codice in cui è definito il gestore. L'applicazione parziale è una cosa fantastica qui, ma in C puoi usare una struttura dati opaca.

whew Non ci è voluto molto tempo per capire, ma capire come effettivamente hackerare il codice è durato alcune ore.

Ho intenzione di fare un approfondimento molto più approfondito sul mio sito web, compresi esempi significativi di codice, in futuro.

    
risposta data 17.10.2012 - 18:36
fonte

Leggi altre domande sui tag