Articoli
Show()
e Hide()
ovvero esso sara` capace
di mostrarsi e di nascondersi.
Diamo per scontata nel lettore la conoscenza di derivazione ed ereditarieta`.
Derivando un oggetto, si puo` istanziarne un'altro che ne aumenta
le capacita` (proprieta`
e metodi).
Quindi il nuovo oggetto fa tutto quello che faceva il precedente (possiamo anche
usarlo come fosse un oggetto del tipo dal quale e` derivato) e qualcosa in piu`.
I sistemi grafici funzionano proprio cosi`: si costruiscono oggetti via via
piu` complessi derivandone altri.
Per esempio, esiste sempre un oggetto che si
limita a disegnare un rettangolo sullo schermo:
da questo si puo` derivare una label, ovvero un rettangolo con un testo all'interno.
Sempre dal rettangolo si puo` derivare una textbox, e da quest'ultima si puo`
derivare una textbox particolare, per esempio per l'input di passwords o
per input formattato.
Dal rettangolo ancora si puo` derivare un bottone, un bottone con testo,
un bottone con immagine... e cosi` via.
Poiche' ogni oggetto possiede le proprieta` e i metodi di quelli da cui e` derivato,
tutti gli oggetti del nostro esempio avranno i metodi Show e Hide, che erano stati
introdotti con il rettangolo.
La casella di testo introdurra` una proprieta` (dato) text dove sara` possibile
scrivere e leggere il testo, e la textbox particolare avra` anch'essa queste
due caratteristiche.
Ogni sistema grafico mette a disposizione tutta una serie di oggetti
di sistema: label, textbox, pannelli, liste: occorre solo assemblarle e usarle.
Occorre prestare attenzione alla regola ``E` UN'' e ``HA UN'' per evitare di
derivare quando invece sarebbe stato corretto aggiungere alle proprieta`.
Esempio: una finestra con tre bottoni sopra e` un oggetto derivato da
una finestra che tra i suoi dati (proprieta`) contiene anche tre oggetti bottone.
Chi usa gli oggetti? Il sistema grafico.
Ovvero il sistema grafico converte le azioni del mouse e della tastiera
in attivazione delle proprieta` degli oggetti:
e` il sistema grafico che chiama la Repaint()
di un oggetto
quando e` ora di ridisegnarlo, o che attiva la Press()
di un bottone
quando e` stato premuto
(per inciso in questo caso la Press sa anche come disegnare il bottone
nella stato ``down'', diverso dallo stato ``up'').
Il sistema grafico genera EVENTI che si tramutano nell'esecuzione di X.Y()
dove X e' un generico oggetto ed Y() una sua funzione membro.
Tutti gli oggetti di sistema hanno delle funzioni membro gia`
definite (per esempio Show()
, Hide()
,
Repaint()
) ma nulla vieta che possano essere ridefinite.
Inoltre hanno delle funzioni segnaposto (virtuali) che devono essere riempite
dall'utente (Press()
di un bottone).
struct rect { int left,top,width,height; };e con due funzioni
Show(rect*)
e Hide(rect*)
che accettano
una struttura rect come parametro, e vi compiono delle operazioni.
Ecco il significato di ``pensare ad oggetti'' : i sistemi grafici anche
se scritti in C con API in C sono di fatto ad oggetti, perche' pensati tali
quando ancora non erano disponibili linguaggi object-oriented come il C++.
E anche oggi i sistemi grafici appena sfornati prediligono le API in C
per una questione di overhead.
Da questo momento in poi penseremo in C++, e scriveremo in C.
Facciamo ancora un esempio di equivalenza:
in C++
class rect { public: Show(); Hide(); protected: int left,top,width,height; }; main() { rect R; R.left=R.top=100; R.Show() } in C struct rect { int left,top,width,height; }; Show(rect &r); Hide(rect &r); main() { rect R; R.left=R.top=100; Show(R); }
resize()
che
viene chiamata quando si ridimensiona la finestra)
e che necessitano comunque della variabile di stato per capire come operare.
Il metodo di suddivisione e` comunque valido come scomposizione di un
problema in sottoproblemi.
Frame mia_finestra; Panel mio_pannello: Textsw mia_textedit;ma non si accede piu` alle sue proprieta` come
mia_finestra.height=300; mia_textedit.top=0;bensi` attraverso la funzione tuttofare xv_set(oggetto,...) dove oggetto sta per l'oggetto di cui vogliamo settare le proprieta` e i parametri aggiuntivi sono le proprieta` da settare con i valori. Inoltre, il tipo di dato Panel, Frame etc non e` la struct che contiene i dati ma un puntatore ad essa. Poiche' e` un puntatore,scrivere Panel pannello; non crea una struct contenente i dati per l'oggetto Panel. Occorre creare dinamicamente un oggetto siffatto e far puntare pannello ad esso. La funzione che effettua questo e` la
xv_create (oggetto, tipo oggetto, ...)
che tratteremo piu` avanti.
La xv_create funge anche da inizializzatore dell' oggetto
(quello che era il costruttore nella classe C++).
Chiariamo con un esempio.
Anziche' per il nostro ipotetico oggetto rect scrivere
rect R; R.top=0; R.left=10;scriveremo
rect R; R=xv_create(...); xv_set(R,XV_TOP,0,XV_LEFT,10,NULL);Notiamo innanzitutto che la xv_set e` una funzione che accetta un numero variabile di parametri: per indicare quando questa lista e' finita si usa NULL (oppure 0). Il primo parametro e` sempre l'oggetto (l'handle dell'oggetto) e i successivi sono coppie COSTANTE che indica la proprieta` da settare ed il suo VALORE. N.B. XV_TOP e XV_LEFT non esistono: in realta` sono XV_X e XV_Y. Per leggere il valore di una proprieta` si usa la xv_get(oggetto,...) allo stesso modo. Ad esempio
printf("coordinata x : %d\n",R.left);diventa
printf("coordinata x : %d\n",(int)xv_get(R,XV_LEFT));notate come non sia piu` necessario indicare la fine della lista di proprieta` poiche' se ne ottiene una alla volta come valore di ritorno. E` bene forzare il tipo di valore di ritorno con un cast per evitare antiestetici warning in compilazione. Questo e` quanto per le proprieta`. E per i metodi ? Come si fa a rilevare che il mouse passa sopra l'oggetto ? Come si fa a rilevare che e` stato selezionato un item di un oggetto lista ? E a installare una propria funzione che venga attivata all'evento Press di un bottone ? Per gli eventi che un oggetto puo` ricevere occore installare delle nostre funzioni in modo che vengano attivate al generarsi di quell'evento. Queste funzioni vengono chiamate anche callback oppure hook (uncino) perche' si ``agganciano'' ad un oggetto. Esse devono avere una lista di parametri ben definita. In genere occorre beccarne una in un esempio per imparare ad usarla. Comunque si notifica al sistema grafico che noi vogliamo usare cioe` installare una certa funzione per un certo evento sempre con la xv_set. Per esempio, supponiamo di avere un oggetto Frame (una finestra vuota) e di voler vedere sulla shell da cui abbiamo lanciato il programma un output quando la finestra subisce un resize.
Frame f; void mia_funzione(Xv_Window window, Event * event) { switch (event_action(event)) { case WIN_RESIZE: printf("width %d height %d \n",(int)xv_get(f,XV_WIDTH,(int)xv_get(f,XV_HEIGHT)); } } main() { f=xv_create(..); xv_set (f, WIN_EVENT_PROC, mia_funzione, WIN_CONSUME_EVENTS, WIN_RESIZE, NULL, NULL); xv_main_loop (f); }Ci sono varie cose da commentare: la prima e` che xv-set,xv_get hanno si' un ruolo predominante ma c'e` dell'altro (event_action ? e poi cosa ci sara` ancora di nuovo ?). Ribadisco che lo scopo di questo articolo e` quello di fornire una chiave di lettura dei sorgenti xview in modo poi che si possa imparare da essi: una trattazione esauriente non e` possibile. La seconda e` la possibilita` di inserire liste di parametri nella lista di parametri di xv_set. Niente di strano: anche queste liste innestate vanno terminate da un NULL. Nel nostro caso, questa lista innestata contiene un elemento (WIN_RESIZE), e il suo inizio e` determinato da WIN_CONSUME_EVENTS che indica quali eventi dovranno essere passati alla mia_funzione. Notate che la lista di eventi da consumare fa parte degli elementi obbligati da passare quando si vuole settare WIN_EVENT_PROC. Infatti gli argomenti di WIN_EVENT_PROC sono il puntatore alla funzione in questione (mia_funzione) e la lista degli elementi da consumare. Una scrittura indentata e` consigliabile per aumentare la leggibilita` di questo codice. In genere le funzioni di notifica di eventi (esempio: per un oggetto lista, la funzione che viene chiamata quando l'utente seleziona l'item i-esimo) si installano con WIN_NOTIFY_PROC. Ho volutamente tralasciato fino ad ora un'altra funzione importante e di carattere generale : la xv-create(oggetto,...). Essa permette di aggiungere dinamicamente oggetti SU, SOPRA, IN un oggetto esistente. Tramite questa si aggiungono pulsanti, menu, liste al frame principale (si popola la finestra). Il primo parametro di xv_create e` l'oggetto sopra il quale si vuole creare un altro oggetto, oppure XV_NULL se si vuole creare un oggetto nuovo, sganciato da qualsiasi altro. Il secondo e` una costante che indica il tipo di oggetto da creare. Si noti che l'operazione di creazione di un oggetto su di uno esistente NON E` la corrispondente della derivazione in C++, ma l'aggiunta tra i dati dell'oggetto di tipo A di un oggetto di tipo B: soddisfa alla domanda ``HA UN'' , non alla ``E` UN''. Anche il puntatore creato da xv_create deve subire un cast prima di essere assegnato. Il frame (finestra) principale di un programma e` creato con
Frame f; main() { f = (Frame) xv_create ( XV_NULL, FRAME, NULL); xv_main_loop (f); }Notiamo la costante NULL che segnala la fine dei parametri. Perche' ? I parametri non sono automaticamente finiti dopo aver indicato il padre ed il tipo dell'oggetto ? No : la xv_create puo` essere usata anche come la xv_set. Cioe` e` possibile unire una chiamata a xv_create e una a xv_set in una sola (a xv_create). Supponiamo che dopo aver creato il frame si voglia settare la sua larghezza a 200: invece di
Frame f; main() { f = (Frame) xv_create ( XV_NULL, FRAME, NULL); xv_set(f,XV_WIDTH,200,NULL); xv_main_loop (f); }si scrive in modo piu` compatto
Frame f; main() { f = (Frame) xv_create ( XV_NULL, FRAME, XV_WIDTH,200, NULL); xv_main_loop (f); }Questo e` utile perche' in genere si crea un oggetto e se ne settano molte proprieta` subito dopo. Rimarrebbe ora da trattare la xv_destroy, per distruggere un oggetto. Tuttavia negli esempi si usa raramente la xv_destroy, segno che tutti gli oggetti grafici allocati da un processo vengono liberati alla fine del processo stesso. Questo era anche ovvio se si pensa che un sistema U**X rimane in genere acceso 24 ore su 24, e a lungo andare la non disallocazione di oggetti potrebbe creare problemi. Quindi, se nei sorgenti della SUN si fa cosi`, possiamo senza dubbio farlo anche noi.