' Rapid-Q by William Yu (c)1999-2000 . ' ================================================================================ ' Upload_il_tuo_script_su_Rapidq.it ' Capitolo_12__Grafica_con_Windows La maggior parte delle informazioni contenute in questo capitolo non si applicano alla versione Linux/Unix, eccetto per le prime sezioni. Se sei un programmatore DOS che ti stai convertendo a scrivere per Windows, ci sono alcune differenze nel modo di mostrare la grafica, prevalentemente introdotti dal concetto di eventi e della natura multi- tasking di Windows. 12.1 Grafica sotto DOS versus Windows Se sei abituato alla programmazione in DOS e non conosci molto bene la programmazione Windows, probabilmente noterai alcune differenze maggiori dovute alla programmazione grafica sotto i due diversi OS. Sotto DOS è molto più semplice poichè non devi preccoparti di altri "programmi" che possono interrompere il tuo programma. Questo significa che tu mostri/disegni sullo schermo e non lo cancelli magicamente, tu solo ti preoccupi di cosa devi disegnare il momento successivo. Tuttavia sotto Windows c'è una lieve differenza. Sebbene è perfettamente sicuro disegnare sul tuo form, ciò comporta diverse conseguenze. La più ovvia è che un altro programma potrebbe "interrompere" l'esecuzione/dimostrazione del tuo programma. Pensa al minimizzazione del form oppure se questo viene nascosto dietro una altra finestra. Come fa Windows a sapere cosa deve essere ridisegnato una volta che il tuo programma ha ricevuto di nuovo il focus? Dovresti pensare che Windows è rapido abbastanza da ridisegnare la parte che era nascosta, ma sfortunatamente non siamo così fortunati. Il tuo programma deve dire a Windows che cosa deve essere re-disegnato. Windows ti dirà che il tuo form deve essere re-disegnato mandando i messaggi al tuo form. In particolare, il messaggio OnPaint (WM_PAINT) viene mandato alla finestra che necessita di essere re-disegnata. E' poi di tua responsabilità fornire una procedura OnPaint procedure per il tuo componente/form che lo richiede. 12.2 Quali componenti richiedono di essere re-disegnati? Tutti i componenti visibili attualmente, ma sfortunatamente la maggior parte del lavoro è fatto da Windows. Per esempio, non c'è necessita di fornire un metodo OnPaint per la classe QButton class poichè è già implementata per te. Tu naturalmente puoi scrivere la tua personale classe Button come hai imparato nel capitolo 10, in tal caso scriverai il metodo OnPaint er l'utente. I componenti QForm e QCanvas sono due ovvi candidati per fare grafica. Puoi pensare a QCanvas come al tuo schermo in modalità grafica in DOS , quello che vuoi disegnare sarà direttamente scritto nella procedura OnPaint : DECLARE SUB CanvasPaint (Sender AS QCanvas) CREATE Form AS QForm CREATE Canvas AS QCanvas OnPaint = CanvasPaint END CREATE ShowModal END CREATE SUB CanvasPaint (Sender AS QCanvas) Sender.FillRect(10,10,100,100,&HFF0000) '-- Casella Blue END SUB Se stai cercando di nascondere il form sotto una altra finestra e poi il programma riottiene il focus, la casella blu riapparirà . Ora prova ad scrivere qualcosa che non usi la procedura OnPaint: CREATE Form AS QForm CREATE Canvas AS QCanvas FillRect(10,10,100,100,&HFF0000) '-- Casella Blue END CREATE Form.ShowModal END CREATE Noti niente? Esattamente, non ci sono caselle blu. Il messaggio paint (disegna) è stato mandato quando il tuo form appare la prima volta sul desktop, e poichè non hai scritto nessuna procedura OnPaint, non c'è niente da disegnare. Quindi cosa realmente succede al codice FillRect? Veramene nulla, esso viene ancora eseguito, ma il prossimo messaggio paint lo cancella/sovrascrive. Per comprendere meglio questo, prova questo codice che mostra la casella blu quando premi il pulsante, ma prova a nascondere di nuovo la finestra o a minimizzarla. Rimostra la tua finestra e vedi cosa accade: DECLARE SUB ButtonClick CREATE Form AS QForm CREATE Button AS QButton OnClick = ButtonClick END CREATE CREATE Canvas AS QCanvas END CREATE ShowModal END CREATE SUB ButtonClick Canvas.FillRect(10,10,100,100,&HFF0000) END SUB Quando tu clikki sul pulsante la casella blu viene disegnata, tuttavia, una volta che hai nascosto una parte della finestra e poi la hai rimostrata, probabilmente hai notato che la tua casella blu è scomparsa. 12.3 Disegnare grafica dinamicamente As Come tu hai già notato, se tutti i tuoi grafici sono contenuti nella procedura OnPaint, come disegnare dinamicamente senza cancellarlo magicamente? Per questo scopo, devi disegnare su una BMP che funge da schermo no visibile e poi nella procedura OnPaint, devi solo usare il metodo Draw per mostrare il grafico/disegno. Tu puoi usare il componente QBITMAP come schermo grafco non visibile. Ecco del codice di esempio da testare: DECLARE SUB CanvasPaint (Sender AS QCanvas) DECLARE SUB ButtonClick (Sender AS QButton) ' Crea bitmap per l'impiego come schermo non visibile DIM BitMap AS QBITMAP BitMap.Height = 100 BitMap.Width = 100 BitMap.Paint(0,0,0,0) CREATE Form AS QForm Center Caption = "Simple graphics demonstration" CREATE Canvas AS QCanvas OnPaint = CanvasPaint END CREATE CREATE SquareButton AS QButton Caption = "Draw Square" OnClick = ButtonClick Left = 150 END CREATE CREATE CircleButton AS QButton Caption = "Draw Circle" OnClick = ButtonClick Left = 150 Top = 50 END CREATE CREATE LineButton AS QButton Caption = "Draw Line" OnClick = ButtonClick Left = 150 Top = 100 END CREATE ShowModal END CREATE SUB CanvasPaint (Sender AS QCanvas) Sender.Draw(0,0,Bitmap.BMP) END SUB SUB ButtonClick (Sender AS QButton) SELECT CASE Sender.Caption CASE "Draw Square" Bitmap.FillRect(10,10,50,50,&HFF0000) CASE "Draw Circle" Bitmap.Circle(10,60,50,110,&H0000FF,&H0000FF) CASE "Draw Line" Bitmap.Line(50,50,90,90,&H00FF00) END SELECT Canvas.Repaint '-- Dice a Canvas di ridisegnarsi. END SUB Risultato visibile dell'esempio: [Output] Dettagli del codice: La maggior parte del codice è autoesplicante, basta leggerlo. Il primo pezzo di codice crea ed inizializza il componente Bitmap. Poichè QBitmap è un componente non visibile, esso non riceve il messaggio paint, quindi qualsiasi cosa tu disegni sulla Bitmap resterà lì. Ora che abbiamo pronta la nostra Bitmap, la nostra procedura OnPaint per QCanvas possiamo usare il metodo Draw per dipingere la Bitmap sul Canvas. Questo è quello che tu chiami double buffering (doppio buffering), una tecnica simile è usata in DirectX. 12.4 Ownerdraw List e Combo Boxes (compresa QStringGrid) Ownerdrawn ListBoxes e ComboBoxes permettono di sovrascrivere il metodo default paint qualche volta. In realtà non ti permettono di re-disegnare l'intera interfaccia di una lista o di una combo box,perchè comunque non è questo che tu vorresti. Quello che ti permette di fare è sovrascrivere il metodo paint per i dati. Così invece di mostrare i campi di testo nella tua list o combo boxes, tu puoi attualmente disegnare immagini in essi e/o cambiare il font/colore per ciascun singolo item. Ci sono 2 type di ownerdraw list/combo boxes. * 1. OwnerDrawFixed * 2. OwnerDrawVariable Un ownerdrawn list/combo box fisso significa che tutti i tuoi items avranno una altezza predeterminata. Quindi ora consideriamo il caso dove una delle tue immagini è di 100 pixels alta e una altra che è solo 10 pixels alta. Se tu specifichi una ItemHeight di 10, l'immagine alta 100 pixel attualmente soverchia alcuni items poichè non ha abbastanza spazio per se stessa (questo può essere evitato usando CopyRect al posto di Draw). Ma in alcuni eventi, dovrebbe essere meglio che creare dei campi item variabili dimensionati, quindi dovresti usare invece OwnerDrawVariable . Il solo problema con OwnerDrawVariable list/combo boxes è che richiede che il programmatore specifichi l'altezza di ciascun item, esso non calcola automaticamente questo campo per te. Nella maggior parte dei casi questo non è un grosso problema, ma ciò nonostate dovrai immagazzinare queste informazioni extra per ciascun campo . Come fare questo dipende interamente da te. Per vedere un esempio funzionante di ownerdrawn list/combo boxes, osserva LISTBOX.BAS incluso in EXAMPLES.ZIP. Puoi riscrivere il codice per lavorare con ComboBoxes. Puoi notare che tu non implementi nessun evento OnPaint , ma un evento OnDrawItem. Dettagli del codice: SUB ListBoxDrawItem(Index AS INTEGER, State AS BYTE, Rect AS QRECT) IF State = 0 THEN '-- Selected ListBox.FillRect(Rect.Left, Rect.Top, Rect.Right, Rect.Bottom, &H00FF00) ELSE ListBox.FillRect(Rect.Left, Rect.Top, Rect.Right, Rect.Bottom, &HFFFFFF) END IF ListBox.TextOut(100, Rect.Top+(Rect.Bottom-Rect.Top)/4, ListBox.Item(index), 0, -1) ListBox.Draw(Rect.Left, Rect.Top, Bitmap(Index).BMP) END SUB Fortunatamente il codice è quasi completamente autoesplicantesi, ma alcune cose potresti notare quali il fatto che non disegni l'intera listbox (ie. tutti gli items), ma piuttosto un messaggio paint è mandato ogni volta che un item viene cambiato. Questo item occupa una regione rettangolare nella tua listbox, così quello che OnDrawItem manda a te è la regione di questo item (questo è il parametro Rect). Non dovresti superare questo limite che ti è stato dato ( ie. non cercare di disegnare fuori da questa area, non è la zona dove puoi disegnare!). Non ho parlato di QStringGrid in questa sezione, ma la stessa idea vale per QStringGrid. Dovrai implementare l'evento OnDrawCell, che è simile al modo per gestire OnDrawItem in list/combo boxes. 12.5 Usare DirectX Perchè DirectX è così popolare? Questa domanda può essere affrontata tranquillamente senza entrare nei dettagli. E' più veloce della Windows GDI (e di OpenGL in molti casi ), supporta la maggior parte delle schede grafiche, è gratis per l'uso, e fornisce la maggior parte dei comandi grafici necessari che il programmatore cerca senza doverli implementare da solo. Non si è detto che è ampiamente supporatata e (per alcune persone) molto facile da usare. Come puoi notare, questo non è la mia opinione generale su DirectX, ma l'opinione che in genere si ha di essa. Tutti hanno installato DirectX ? Nella maggior parte dei casi, si , dipende da quale versione si impiega. Non tutti avranno la più recente versione, ma di solito non manca . DirectX per Rapid-Q richiede solo la versione v5.0 e potrebbe persino lavorare dalla versione v3.0 in su (non verificato ). Rapid-Q non supporta ancora tutte le caratteristiche di DirectX, comw il joystick o la programmazione 3D. Per usare DirectX sotto Rapid-Q, dovresti sapere 2 cose. * 1. Tutti i disegni si fanno sulla pagina grafica non visibile * 2. Hai bisogno di FLIP questa pagina ogni volta che la vuoi mostrare Essenzialmente, puoi pensare che questo schermo grafico non-visibile sia implementato già per te (come nell'esempio suesposto dove abbiamo usato QBitmap come pagina grafica non visibile). Puoi pensare che l'operazione di FLIP è come l'operazione di re-disegnare nell'esempio suesposto. Dopo che hai capito questi 2 concetti, qualsiasi altra cosa è praticamente la stessa. Tu disegni sullo schermo DirectX come faresti su QCanvas o QBitmap, poi FLIP le pagine quando sei pronto a mostrare la tua grafica. E' più facile vedere alcuni esempi piuttosto che spiegarlo. Tuttavia c'è un importante argomento, che è QDXTimer. QDXTimer è un timer specializzato per DirectX, esso fornisce una precisione leggermente migliore rispetto a QTimer, e controlla il frame rate (frequenza delle immagini per secondo) in modo che il tuo programmi non sono eseguiti troppo velocemente. Si potrebbe ridurre il frame rate se il tuo programma viene eseguito troppo velocemente, ma non puoi fare di più se il tuo programma sta andando a 5 o 6 frames per secondo. Usa QDXTimer invece dello standard timer di Windows , QTimer , quando crei animazioni grafiche DirectX (non è necessario ma raccomandato). * 1. All drawing is done on the off-screen page * 2. You need to FLIP to this page everytime you want it displayed ' =============================================================================== ' 2003 Holyguard.net - 2007_Abruzzoweb