Avviare wxDev-C++. Creare un nuovo progetto basato su wxWidgets Frame. Denominarlo "Grafica" e salvarlo, come ogni progetto, in una propria cartella.
La GDI (Graphics Device Interface) rappresenta lo strumento per l'utilizzo di dispositivi grafici, quali il monitor, la stampante o un file. La GDI consente al programmatore di mostrare dati sullo schermo o sulla stampante senza conoscere i comandi specifici per utilizzare l'hardware associato.
Per disegnare su un dispositivo, bisogna creare un oggetto device context (DC). In wxWidgets il device context è chiamato wxDC. Il wxDC si definisce come un'area su cui si possono tracciare grafici e figure. Rappresenta un generico dispositivo. Lo stesso codice può essere usato per disegnare su dispositivi diversi: lo schermo o la stampante. Il wxDC non deve essere usato esplicitamente, il programmatore deve scegliere una delle classi derivate:
wxBufferedDC
wxBufferedPaintDC
wxPostScriptDC
wxMemoryDC - è usato per disegnare grafici su una bitmap.
wxPrinterDC - è usato per disegnare grafici su una stampante.
wxScreenDC - è usato per disegnare in un punto qualsiasi del monitor.
wxClientDC - è usato per disegnare sull'area client di una finestra.
wxPaintDC - è usato per disegnare sull'area client di una finestra in seguito ad un evento onPaint.
wxWindowDC
Un device context ha un sistema di coordinate con la sua origine nell'angolo superiore destro della superficie. Questa origine può essere cambiata con SetDeviceOrigin in modo che tutti i grafici vengano disegnati in posizione traslata - questo è particolarmente necessario quando si disegna con wxScrolledWindow. Esiste anche SetAxisOrientation se si preferisce modificare l'orientamento degli assi dal basso verso l'alto.
Quando si usa la grafica bisogna tener presente la distinzione tra unità logiche e unità del dispositivo. Le unità del dispositivo sono tipiche del particolare dispositivo - per uno schermo è il pixel. Per una stampante l'unità del dispositivo è definita dalla risoluzione della stampante e può essere individuata mediante GetSize (fornisce la dimensione di una pagina in unità di dispositivo), oppure con GetSizeMM (fornisce la dimensione di una pagina in millimetri).
Il mapping mode del device context definisce l'unità di misura usata per convertire le unità logiche in unità del dispositivo. Notare che alcuni device context, in particolare wxPostScriptDC, non conoscono altri mapping mode diversi dal wxMM_TEXT.
wxMM_TWIPS | Ogni unità logica è 1/20 di punto, o 1/1440 di pollice. |
wxMM_POINTS | Ogni unità logica è 1 punto, o 1/72 di pollice. |
wxMM_METRIC | Ogni unità logica è 1 millimetro. |
wxMM_LOMETRIC | Ogni unità logica è 1/10 di millimetro. |
wxMM_TEXT | Ogni unità logica è 1 pixel. Questo è il modo default. |
Sul form principale inserire un wxBoxSizer;
All'interno del sizer inserire due wxButton, uno con didascalia "Mostra" e l'altro con didascalia "Salva".
Per mostrare il grafico sullo schermo si userà un pannello in un frame:
Nel menu File, aprire il sottomenu Nuovo e selezionare "Add wxFrame".
Si apre una finestra che chiede il nome da assegnare alla classe, suggerendo "NewFrame", cambiare in "areaDisegno" le caselle "nome della classe", "nome del file" e "titolo" della finestra del frame.
premere il pulsante Create.
Adesso viene mostrata un'altra griglia di punti che non è il form principale ma la nuova area di disegno creata (il Frame).
Se si apre la scheda "Eventi" del frame si vede che all'evento onClose è associato un gestore. Fare clic su questa riga e scegliere <Go To Function> Il gestore di evento è il seguente:
void areaDisegno::OnClose(wxCloseEvent& event)
{
Destroy();
}
Destroy chiude il frame e rilascia la memoria occupata, Close() chiude l'intera finestra, ma non rilascia lo spazio di memoria creato dinamicamente (con new).
Tornare al frame areaDisegno.
Inserire un pannello sul frame e ridimensionarlo fino a coprire l'intero frame "areaDisegno".
Selezionare il frame "areaDisegno" e aprire la scheda "Eventi". Sulla riga onPaint cliccare nella casella vuota e scegliere <Add New Function>. Si apre il file di implementazione della classe "areaDisegno", completare il gestore dell'evento onPaint:
void areaDisegno::areaDisegnoPaint(wxPaintEvent& event) { // insert your code here wxPaintDC dc( WxPanel1 ); Figure(dc); }
L'evento onPaint si verifica quando la finestra deve essere ridisegnata, ad esempio perchè era stata ridotta ad icona e poi ripristinata, oppure era coperta da un'altra finestra e viene scoperta.
Il gestore crea un'istanza di classe wxPaintDC e richiama la funzione (Figure) che contiene le istruzioni per disegnare. A questa funzione passa il device context come parametro.
Posizionarsi prima di questo gestore e scrivere le istruzioni per disegnare:
void Figure(wxDC &dc){ dc.SetPen(wxPen(*wxBLACK, 2, wxSOLID)); dc.SetBrush(wxBrush(*wxGREEN, wxSOLID)); dc.DrawPoint(5, 5); }
La funzione Figure imposta il colore delle linee e il colore di riempimento delle forme. Viene impostata la penna del device contest di colore nero, spessore 2 pixel e stile tratto continuo. Il colore di riempimento è verde. Infine viene disegnato un punto in coordinate 5, 5.
Questo frame verrà creato quando si preme il pulsante "Mostra". Selezionare la scheda contenente il frame principale. Fare clic sul pulsante "Mostra" e nella scheda "Eventi" associare il gestore all'evento onClic. Completare il gestore di evento onClic:
void GraficaFrm::MostraClick(wxCommandEvent& event) { // insert your code here areaDisegno* frm = new areaDisegno(this); frm->Show(); }
Nella sezione delle direttive dello stesso file inserire la seguente riga:
#include "areaDisegno.h"
Compilare ed eseguire il programma. Premere il pulsante Mostra. Si riesce a vedere il puntino?
Si diegni una linea che parte dal punto in coordinate 10,10 e termina nel punto in coordinate 100,100.
void Figure(wxDC &dc) { dc.SetPen(wxPen(*wxBLACK, 2, wxSOLID)); dc.SetBrush(wxBrush(*wxGREEN, wxSOLID)); dc.DrawPoint(5, 5); dc.DrawLine(10, 10, 100, 100); }
Stile | Descrizione |
wxSOLID | Colore continuo. |
wxTRANSPARENT | Nessun Riempimento. |
wxBDIAGONAL_HATCH | Tratteggio inclinato (diagonale da destra a sinistra). |
wxCROSSDIAG_HATCH | Tratteggio con diagonali incrociate. |
wxFDIAGONAL_HATCH | Tratteggio inclinato (diagonale da sinistra a destra). |
wxCROSS_HATCH | Tratteggio con linee orizzonatali e verticali incrociate. |
wxHORIZONTAL_HATCH | Tratteggio con linee orizzontali. |
wxVERTICAL_HATCH | Tratteggio con linee verticali. |
wxSTIPPLE | usa una bitmap per riempire la figura. |
Disegnare un rettangolo il cui vertice superiore destro si trovi in coordinate 50, 50, con i lati di misura 150 e 100, e riempimento con una griglia di linee orizzontali e verticali.
dc.SetBrush(wxBrush(*wxBLACK, wxCROSS_HATCH)); dc.DrawRectangle(50, 50, 150, 100);
Provare gli altri motivi di riempimento.
Disegnare, con un riepimento rosso, un rettangolo con gli spigoli arrotondati (angolo di curvatura 10), in posizione 150, 20 e con i lati di misura 100x50:
dc.SetBrush(*wxRED_BRUSH); dc.DrawRoundedRectangle(150, 20, 100, 50, 10);
Disegnare un rettangolo con gli spigoli arrotondati (assegnare il raggio di curvatura=10), senza bordi e riempimento blu.
dc.SetPen(*wxTRANSPARENT_PEN); dc.SetBrush(wxBrush(*wxBLUE)); dc.DrawRoundedRectangle(250, 80, 100, 50, 10);
Impostare la penna di colore nero e il riempimento di colore nero. Disegnare un cerchio con centro in coordinate 100,150 e raggio 60.
dc.SetPen(wxPen(*wxBLACK, 2, wxSOLID)); dc.SetBrush(*wxBLACK); dc.DrawCircle(100, 150, 60);
Impostare il riempimento di colore bianco e disegnare un ellisse:
dc.SetBrush(*wxWHITE); dc.DrawEllipse(wxRect(120, 120, 150, 50));
Estratto dal manuale wxWidgets: wxDC Class Reference
Blit | Copies from one device context to another. You can specify how much of the original to draw, where drawing should start, the logical function to use, and whether to use a mask if the source is a memory device context. |
Clear | Fills the device context with the current background brush. |
SetClippingRegion DestroyClippingRegion GetClippingBox | Sets and destroys the clipping region, which restricts drawing to a specified area. The clipping region can be specified as a rectangle or a wxRegion. Use GetClippingBox to get the rectangle surrounding the current clipping region. |
DrawArc DrawEllipticArc | Draws an arc or elliptic arc using the current pen and brush. |
DrawBitmap DrawIcon | Draws a wxBitmap or wxIcon at the specified location. The bitmap may have a mask to specify transparency. |
DrawCircle | Draws a circle using the current pen and brush. |
DrawEllipse | Draws an ellipse using the current pen and brush. |
DrawLine DrawLines | Draws a line or number of lines using the current pen. The last point of the line is not drawn. |
DrawPoint | Draws a point using the current pen. |
DrawPolygon DrawPolyPolygon | DrawPolygon draws a filled polygon using an array of points or list of pointers to points, adding an optional offset coordinate. wxWidgets automatically closes the first and last points. DrawPolyPolygon draws one or more polygons at once, which can be a more efficient operation on some platforms. |
DrawRectangle DrawRoundedRectangle | Draws a rectangle or rounded rectangle using the current pen and brush. |
DrawText DrawRotatedText | Draws a text string, or rotated text string, at the specified point using the current text font and the current text foreground and background colors. |
DrawSpline | Draws a spline between all given control points, using the current pen. |
FloodFill | Flood fills the device context starting from the given point, using the current brush color. Ok Returns true if the device context is OK to use. |
SetBackground GetBackground | Sets and gets the background brush used in Clear and in functions that use a complex logical function. The default is wxTRANSPARENT_BRUSH. |
SetBackgroundMode GetBackgroundMode | Sets and gets the background mode for drawing text: wxSOLID or wxTRANSPARENT. Normally, you will want to set the mode to wxTRANSPARENT (the default) so the existing background will be kept when drawing text. |
SetBrush GetBrush | Sets and gets the brush to be used to fill shapes in subsequent drawing operations. The initial value of the brush is undefined. |
SetPen GetPen | Sets and gets the pen to be used to draw the outline of shapes in subsequent drawing operations. The initial value of the pen is undefined. |
SetFont GetFont | Sets and gets the font to be used in DrawText, DrawRotatedText, and GetTextExtent calls. The initial value of the font is undefined. |
SetPalette GetPalette | Sets and gets wxPalette object mapping index values to RGB colors. |
SetTextForeground GetTextForeground SetTextBackground GetTextBackground | Sets and gets the color to be used for text foreground and background. The defaults are black and white, respectively. |
SetLogicalFunction GetLogicalFunction | The logical function determines how a source pixel from a pen or brush color, or source device context if using Blit, combines with a destination pixel in the current device context. The default is wxCOPY, which simply draws with the current color. |
GetPixel | Returns the color at the given point. This is not implemented for wxPostScriptDC and wxMetafileDC. |
GetTextExtent GetPartialTextExtents | Returns metrics for a given text string. |
GetSize GetSizeMM | Returns the dimensions of the device in device units or millimeters. |
StartDoc EndDoc | Starts and ends a document. This is only applicable to printer device contexts. When StartDoc is called, a message will be shown as the document is printed, and EndDoc will hide the message box. |
StartPage EndPage | Starts and ends a page. This is only applicable to printer device contexts. |
DeviceToLogicalX DeviceToLogicalXRel DeviceToLogicalY DeviceToLogicalYRel | Converts device coordinates to logical coordinates, either absolute (for positions) or relative (for widths and heights). |
LogicalToDeviceX LogicalToDeviceXRel LogicalToDeviceY LogicalToDeviceYRel | Converts logical coordinates to device coordinates, either absolute (for positions) or relative (for widths and heights). |
SetMapMode GetMapMode | As described earlier, this determines (along with SetUserScale) how logical units are converted to device units. |
SetAxisOrientation | Sets the x- and y-axis orientation: the direction from lowest to highest values on the axis. The default orientation is to have the x-axis from left to right (true) and the y-axis from top to bottom (false). |
SetDeviceOrigin GetDeviceOrigin | Sets and gets the device origin. You can use this to place a graphic in a particular place on a page, for example. |
SetUserScale GetUserScale | Sets and gets the scale to be applied when converting from logical units to device units. |
Sull'area client di una finestra non si puņ scrivere il testo come si farebbe in una casella di testo, bisogna disegnarlo. Il seguente codice stampa un messaggio:
wxFont font(12, wxFONTFAMILY_SWISS, wxNORMAL, wxBOLD); dc.SetFont(font); dc.SetBackgroundMode(wxTRANSPARENT); dc.SetTextForeground(*wxBLACK); dc.SetTextBackground(*wxWHITE); dc.DrawText(_("grande Napoli"), 200, 200);
In questo frammento di codice viene creato un oggetto font con cui viene impostato il tipo do carattere del device context. Poi si sceglie il colore di primo piano e il colore di sfondo del testo, infine si disegna il messaggio a partire dalle coordinate specificate.
Nel seguente esempio, invece, si passa un altro parametro alla funzione DrawRotateText, che serve per specificare l'orientamento del testo:
for (int angolo = 0; angolo < 360; angolo += 45) dc.DrawRotatedText(_("grande Napoli"), 200, 200, angolo);
Disegnare un arco:
la funzione DrawArc richiede che vengano specificati tre punti attraverso cui passa l'arco, il punto iniziale, il punto finale e il centro della circonferenza a cui appartiene l'arco:
int x = 10, y = 200, raggio = 20; dc.DrawArc(x-raggio, y, x + raggio, y, x, y);
La funzione DrawEllipticArc traccia un arco di ellisse. Richiede, come parametri, la posizione e ladimensione di un rettangolo che contiene l'arco, oltre all'angolo iniziale e finale, specificati rispetto alla lancetta di un orologio sulle 3, dal centro del rettangolo. Se i punti iniziale e finale coincidono viene tracciato un ellisse completo.
Disegna un arco di ellisse inscritto in un rettangolo con lo spigolo superiore sinistro in 10, 100, dimensioni 200x40. la tangente all'arco nel punto iniziale è 270 gradi e nel punto terminale è 420 gradi.
dc.DrawEllipticArc(10, 100, 200, 40, 270, 420);
Per disegnare molte linee si può usare la funzione DrawLines, in sostituzione di tante istruzioni DrawLine. Il seguente esempio traccia linee tra 10 punti memorizzati nell'array pt, ad iniziare dal punto 10, 100:
wxPoint pt[10]; int offsetX = 10; int offsetY = 100; pt[0].x = 0; pt[0].y = 0; pt[1].x = 0; pt[1].y = 20; pt[2].x = 20; pt[2].y = 20; pt[3].x = 20; pt[3].y = 40; pt[4].x = 40; pt[4].y = 40; pt[5].x = 40; pt[5].y = 60; pt[6].x = 60; pt[6].y = 60; pt[7].x = 60; pt[7].y = 80; pt[8].x = 80; pt[8].y = 80; pt[9].x = 80; pt[9].y = 100; dc.DrawLines(10, pt, offsetX, offsetY);
Si possono disegnare forme piene, delimitate da linee, usando DrawPolygon, e molte forme con DrawPolyPolygon.
DrawPolygon aspetta di ricevere, come parametri, il numero di punti, un array di punti,
facoltativamente, l'offset da aggiungere ai punti, e il motivo (opzionale) di riempimento.
DrawPolygonPolygon in più riceve un array di interi che specifica il numero di punti da usare per ogni poligono.
il seguente esempio mostra come disegnare poligonali e multi poligonali.
wxBrush brushHatch(*wxRED, wxFDIAGONAL_HATCH); dc.SetBrush(brushHatch); wxPoint stella[5]; stella[0] = wxPoint(100, 60); stella[1] = wxPoint(60, 150); stella[2] = wxPoint(160, 100); stella[3] = wxPoint(40, 100); stella[4] = wxPoint(140, 150); dc.DrawPolygon(WXSIZEOF(stella), stella, 0, 30); dc.DrawPolygon(WXSIZEOF(stella), stella, 160, 30, wxWINDING_RULE); wxPoint stella2[10]; stella2[0] = wxPoint(0, 100); stella2[1] = wxPoint(-59, -81); stella2[2] = wxPoint(95, 31); stella2[3] = wxPoint(-95, 31); stella2[4] = wxPoint(59, -81); stella2[5] = wxPoint(0, 80); stella2[6] = wxPoint(-47, -64); stella2[7] = wxPoint(76, 24); stella2[8] = wxPoint(-76, 24); stella2[9] = wxPoint(47, -64); int count[2] = {5, 5}; dc.DrawPolyPolygon(WXSIZEOF(count), count, stella2, 450, 150);
Si devono vedere 3 poligonali. Notare che la terza poligonale è spostata orizzontalmente all'offset 450, quindi allargare il frame e il pannello, prima di compilare il programma.
DrawSpline traccia curve passanti attraverso tre punti. Oppure, attraverso i punti specificati in un array:
// Disegna una curva che passa attraverso i 3 punti dc.DrawSpline(10, 100, 200, 200, 50, 230); // Disegna una curva che passa attraverso i punti specificati nell'array wxPoint curva[5]; curva[0] = wxPoint(100, 60); curva[1] = wxPoint(60, 150); curva[2] = wxPoint(160, 100); curva[3] = wxPoint(40, 100); curva[4] = wxPoint(140, 150); dc.DrawSpline(WXSIZEOF(curva), curva);
Per collocare un'immagine su un pannello o su un frame ci sono due funzioni: DrawBitmap e Blit.
DrawBitmap è una forma semplificata di Blit, richiede, come parametri, la bitmap, la posizione, e un valore booleano che specifica la trasparenza. Il codice che segue carica un'immagine trasparente e disegna delle linee di testo al di sopra.
dc.SetPen(wxPen(*wxBLACK, 2, wxSOLID)); dc.SetBrush(wxBrush(*wxGREEN, wxSOLID)); wxString msg = _("Questo testo appare sotto all'immagine"); int y = 75; for (size_t i = 0; i < 10; i++) { y += dc.GetCharHeight() + 5; dc.DrawText(msg, 50, y); } wxBitmap bmp(_("immagine.png"), wxBITMAP_TYPE_PNG); dc.DrawBitmap(bmp, 50, 100, true);
L'esempio precedente richiede che sia presente un file in formato png nella stessa cartella del programma. Inoltre è necessario richiamare la funzione wxInitAllImageHandlers(); alla fine della funzione CreateGuiControls:
////GUI Items Creation End wxInitAllImageHandlers(); }
Il disegno verrà salvato in un file formato PNG e in un file formato JPEG in risposta al clic sul pulsante "Salva". Fare doppio clic sul pulsante per aprire il file con il codice del gestore dell'evento onClic. Completarlo così:
/* Per salvare il disegno in un file creare un componente bitmap, poi un memoryDC, quindi collegare la bitmap al memoryDC in modo da usarla come un foglio su cui disegnare. Richiamare la procedura di disegno rilasciare la bitmap salvare il file. */ // Creare una bitmap larga 300 pixel e alta 200 pixel // denominarla "carta". wxBitmap *carta = new wxBitmap( 300,200); // Creare un memory Device Context wxMemoryDC memDC; // dire a memDC di scrivere su "carta". memDC.SelectObject( *carta ); // Chiamare la funzione Figurefile per disegnare sul memDC FigureFile(memDC); // dire a memDC di disegnare sulla bitmap; // in tal modo si libera "carta" e quindi può scrivere su file. memDC.SelectObject( wxNullBitmap ); // metti il contenuto di "carta" in un file png e in un file jpeg. carta->SaveFile( _T("Quadrato.png"), wxBITMAP_TYPE_PNG, (wxPalette*)NULL ); carta->SaveFile( _T("Quadrato.jpg"), wxBITMAP_TYPE_JPEG, (wxPalette*)NULL ); delete carta;
All'interno dello stesso file, prima del gestore di evento, aggiungere anche la funzione FigureFile (è la stessa usata dal gestore dell'evento "Mostra"):
void FigureFile(wxDC &dc){ dc.SetBrush(*wxWHITE_BRUSH); dc.Clear(); wxColor Blue(0,0,255); wxPen myBluePen(Blue,5,wxSOLID); dc.SetPen(myBluePen); dc.DrawRectangle(0,0,300,200); dc.SetBrush( *wxRED_BRUSH ); dc.SetPen(*wxRED_PEN ); dc.DrawRectangle( 10, 10, 40, 40 ); wxPen myGreenPen(*wxGREEN,3,wxSOLID); dc.SetPen( myGreenPen ); dc.DrawLine( 55, 40, 290, 40); dc.SetTextForeground( *wxBLACK); dc.DrawText(wxT("Disegni di linee e rettangoli"), 50, 60); wxFont BigFont(16,wxFONTFAMILY_ROMAN,wxNORMAL,wxNORMAL,false); dc.SetFont(BigFont); dc.DrawText(wxT("Quadrato rosso."), 60, 10); }
Compilare ed eseguire.
Dopo aver cliccato sul pulsante "Mostra" e aver reso visibile la finestra con il disegno, si può ridimensionare la finestra, la dimensione del disegno non cambia. Si aggiunga un terzo pulsante. Chiamarlo "Stira", e disegnare una figura (questa cambierà le sue dimensioni e proporzioni mentre la finestra viene ridimensionata). Questa figura sia un rettangolo con bordo verde e larghezza fissa.
Nel menu di Code::Blocks scegliere wxSmith → Add wxFrame; accettare il nome proposto (NewFrame). Aggiungere il comando Destroy(), come prima. Sul frame aggiungere un box sizer; nel sizer mettere un pannello e nella finestra delle proprietà specificare la larghezza 200 e l'altezza 200. Spuntare la proprietà Expand. Passare all'elenco degli eventi (icona {}), clic sull'evento OnPaint, scegliere "Add new handler". Completare il gestore così:
void NewFrame::OnPanel1Paint(wxPaintEvent& event) { wxPaintDC dc( WxPanel1 ); dc.SetPen( wxPen( *wxGREEN, 5 ) ); // Penna verde spessa 5 pixel dc.SetBrush(*wxRED_BRUSH); // legge la dimensione della finestra wxSize sz = GetClientSize(); // assegna le dimensioni al rettangolo wxCoord w = sz.x/2 , h = sz.y/2; // Centra il rettangolo sulla finestra, // ma non disegna mai in una posizione negativa. int x = wxMax(0, (sz.x - w)/2); int y = wxMax(0, (sz.y - h)/2); wxRect rectToDraw(x, y, w, h); dc.DrawRectangle(rectToDraw); }
In queste righe di programma si incontrano le proprietà: wxSize, wxCoord, wxRect, e il metodo wxMax. Il loro significato dovrebbe essere chiaro dal nome. La funzione GetClientSize() è la chiave per far dipendere il disegno dalla dimensione della finestra in cui viene visualizzato.
Nel codice della finestra principale aggiungere #include "NewFrame.h". Con un doppio clic sul pulsante "Stira" posizionarsi sul gestore dell'evento onClic e completarlo:
void GraficaFrame::OnButton3Click(wxCommandEvent& event) { NewFrame* ffrm = new NewFrame(this); ffrm->Show(); }
Compilare ed eseguire l'applicazione. Premere il pulsante "Stira" e provare a ridimensionare la finestra. Si nota che il rettangolo non segue le dimensioni della finestra.
Nella finestra delle proprietà cercare la proprietà "Style" e cliccare sul segno + per accedere alla lista degli elementi. Spuntare la casella wxFULL_REPAINT_ON_RESIZE
Realizzare un'applicazione con un menu "disegno", nel quale sia possibile
selezionare la figura da disegnare (Linea, Rettangolo, Cerchio)
Scegliere il colore di riempimento e il colore del bordo della figura
Scegliere lo spessore del bordo della figura