Capítulo 4: Soy un artista... ¡No me coartes!

De doc.ubuntu-es
Saltar a: navegación, buscar

Contenido

Introducción

En este capítulo no vamos a introducir nada nuevo, tan sólo vamos a trabajar con dos elementos un tanto singulares, ambos dedicados a desarrollarnos como artistas, pues el primero son los sizers, que nos ayudarán enormemente a organizar nuestras ventanas, y el segundo es wxWindow, que serán las ventanas donde pintaremos.

Parecerá chocante la forma de moverse de wxWidgets en cuanto a nomenclatura, pues las wxWindow serán para nosotros lo que para el resto de la humanidad son las Canvas, wxFrame, como ya vimos en capítulos anteriores, no serán pantallas (para pintar por ejemplo), sino ventanas de cabecera. El único que mantiene una cierta línea clásica es wxDialog, que aunque no llegaremos a usarlo, os podeis imaginar que su uso será del todo análogo.

Por tanto presentemos el plan de trabajo para este capítulo:

  • Plan de trabajo del capítulo 4:
    • Creación de una wxWindow:
      Crearemos nuestra primera ventana para pintar, dentro del propio TopFrame.
    • Organización de los elementos en la ventana (sizers):
      Usando los sizers colocaremos a nuestro gusto todos los elementos que tenemos.
      • Creación de algunos elementos basura:
        Crearemos unos cuantos cuadros de texto y labels para tener mas objetos que poder colocar.
      • Colocación de los elementos.
    • Pintado de la wxWindow:
      • Pintado directo (wxPaintDC).
      • Pintado sobre un bitmap (wxMemoryDC).
      • Pintado con contexto gráfico.

El último punto es realmente interesante, y una de las más modernas implementaciones de estas magníficas librerías. No obstante esta herramienta nos costará algunos disgustos cuando compilemos en Windows, lo primero porque en Windows tienes que añadir estas librerías manualmente, y lo segundo porque sufren de una alta inestabilidad.

Hecha esta breve introducción, procedamos a trabajar un poco...

Creación de una wxWindow:

Como siempre, con nuestra API bien cerquita, acudimos a ella y buscamos wxWindow, y nos informamos de todas las posibilidades que tiene (que son muchas). Para proceder con una wxWindow, al igual que con una wxFrame, y con un wxDialog, nos crearemos una clase heredada de wxWindow.

Ya introducimos esta idea cuando creamos nuestra wxFrame, y ya adelantabamos que cuando nos encontráramos con elementos complicados (una ventana, un dialogo, un canvas, una malla...) debíamos actuar de esta forma.

Bueno, ya que hemos alineado un poco nuestras ideas, recuperamos nuestro proyecto donde lo dejamos, y creamos dos archivos, llamados "mycanvas.cpp" y "mycanvas.h", y como siempre los limpiamos.

Y declaramos nuestra nueva clase, en "mycanvas.h":

 class MyCanvas : public wxWindow
 {
 public:
 	MyCanvas(SampleApp * AApp, wxWindow *parent, wxWindowID id, const wxPoint& 
       pos=wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = 0);
 
 	void drawSomething();
     wxMemoryDC dc;
     wxGraphicsContext *gc;
 
 protected: // event handlers (NO virtual)
 	void OnPaint(wxPaintEvent& event);
 	void OnMaximize(wxSizeEvent& event);
 private:
 	wxBitmap m_pixels;
 	SampleApp * App;
 
 	// any class wishing to process wxWidgets events must use this macro
 	DECLARE_EVENT_TABLE()
 };

En un acto de previsión, hemos ido añadiendo todo aquello que creemos que vamos a necesitar, tal vez sería buena idea que buscarais en el API todos los elementos que hemos añadido.

El siguiente punto es crear nuestra clase, en "mycanvas.cpp":

 #include <wx/wx.h>
 #include <wx/icon.h>
 #include <wx/font.h>
 #include <wx/numdlg.h>
 #include <wx/string.h>
 #include <wx/event.h>
 #include <wx/textctrl.h>
 #include <wx/wfstream.h>
 #include <wx/datstrm.h>
 #include <wx/txtstrm.h>
 #include <wx/access.h>
 #include <wx/window.h>
 #include <wx/treectrl.h>
 #include <wx/spinctrl.h>
 #include <wx/collpane.h>
 #include <wx/stdpaths.h>
 #include <wx/datetime.h>
 #include <wx/graphics.h>
 #include "main.h"
 #include "mycanvas.h"
 
 BEGIN_EVENT_TABLE(MyCanvas, wxWindow)
 	EVT_PAINT(MyCanvas::OnPaint)			//to can paint in window
 	EVT_SIZE(MyCanvas::OnMaximize)			//answer to size changes (p.ej. maximize events)	
 END_EVENT_TABLE()
 
 const int BMPW = 3000;	//auxvars
 const int BMPH = 2000;	//auxvars
 
 MyCanvas::MyCanvas(SampleApp * AApp, wxWindow *parent, wxWindowID id, const wxPoint& 
  pos = wxDefaultPosition*/, const wxSize& size /*= wxDefaultSize*/, long style /*= 0*/)
 :wxWindow(parent,id,pos,size,style),
 m_pixels(BMPW,BMPH,-1)
 {
     App = AApp;
 }
 
 /***************************************************************/
 /**************	OnMaximize	************************************/
 /************** answer to Size changes	************************/
 /************** Used to resize all elements	********************/
 /***************************************************************/
 void MyCanvas::OnMaximize(wxSizeEvent& WXUNUSED(event))
 {
 }
 
 /***************************************************************/
 /**************	OnPaint	****************************************/
 /************** answer to paint events *************************/
 /************** Prepares the window to paint it ****************/
 /***************************************************************/
 void MyCanvas::OnPaint(wxPaintEvent& WXUNUSED(event))
 {
 }
 
 /***************************************************************/
 /**************	drawSomething	********************************/
 /************** Paint the canvas	****************************/
 /***************************************************************/
 void MyCanvas::drawSomething()
 {
 }

Paremos un segundo a explicar a que viene cada cosa.

  1. De repente añado un montón de librerías, que ni siquiera nos son necesarias... Esas librerías son las típicas que casi seguro tarde o temprano terminareis por usar, es por eso que las añado.
  2. ¿Un sizeevent?... Como maximizamos nuestra TopFrame, es previsible que el tamaño de esta ventana cambie junto a el, On Maximize será el encargado de arreglar los posibles desperfectos.
  3. ¿Dos constantes?... Efectivamente, si miramos esas líneas de código:
 (...)
 const int BMPW = 3000;	//auxvars
 const int BMPH = 2000;	//auxvars
 
 MyCanvas::MyCanvas(SampleApp * AApp, wxWindow *parent, wxWindowID id, const wxPoint& 
  pos = wxDefaultPosition*/, const wxSize& size /*= wxDefaultSize*/, long style /*= 0*/)
 :wxWindow(parent,id,pos,size,style),
 m_pixels(BMPW,BMPH,-1)
 (...)

Lo que hacemos es crear un ancho y un alto, y montar con ellos nuestro bitmap (m_pixels). Ya veremos su utilidad, de momento nos bastará con saber que estos valores deben ser superiores a la resolución de pantalla, o por lo menos al tamaño de la ventana.

Respecto a las funciones, pues lo mas interesante es que tenemos un drawSomething(), que será al que llamaremos para pintar, y que será el que cree el evento para que OnPaint haga el trabajo. Esto es un primer esbozo, porque pronto veremos como no trabajaremos exactamente así.

Bueno, y ahora solo resta añadirla a topframe, así que empezamos por "topframe.h":

 (...)
 private:
 	SampleApp * App;
 	MyCanvas * m_drawPanel;
     DECLARE_EVENT_TABLE()
 };

Haber incluido esta clase en la cabecera de topframe, nos obliga a que siempre que se incluya "topframe.h", haya que incluir antes "mycnavas.h", y por otro lado, "mycanvas.h" requiere de "main.h" antes de ella. Así, en "main.cpp" debemos añadir lo siguiente:

 (...)
 #include "main.h"
 #include "mycanvas.h"
 #include "topframe.h"
 (...)

Y añadimos y construimos MyCanvas en "topframe.cpp":

 (...)
 #include "main.h"
 #include "mycanvas.h"
 #include "topframe.h"
 #include "header.h"
 (...)

(...)

 	ProgressBar = new wxGauge(ControlPanel, wxID_ANY, 100, wxPoint(3,3), wxSize(250,32));
 	MyCanvas * drawPanel = new MyCanvas(App,this, wxID_ANY, wxPoint(0,35), wxSize(640 , 480), wxTAB_TRAVERSAL | wxSUNKEN_BORDER);
     m_drawPanel = drawPanel;
 
     //toolbar

(...)

Con lo que creamos nuestra ventana en el punto wxPoint(0,35) con un tamaño de wxSize(640 , 480).

Compilamos y ejecutamos... ¡Y allí está! ¡nuestra ventana!

Pero el aspecto no es precisamente el deseado ¿verdad?, para arreglar esto vamos a recurrir a los sizers.

Organización de los elementos en la ventana (sizers):

Los sizers son una herramienta indispensable en la programación con wxWidgets, y se deben tener siempre presentes a la hora de programar. Nosotros concretamente vamos a usar wxBoxSizer, aunque existen algunas otras variantes que pueden ser muy interesantes. Pero para poder dar un poco más de juego, vamos primero a crear un montón de elementos basura.

Creación de algunos elementos basura:

 (Construyendo...)
Capítulo 3: ¡Qué aburrido! yo quiero interactuar Nuestra primera interfaz gráfica con CodeBlocks y wxWidgets Capítulo 5: El lado oscuro (compilando para Windows)
Herramientas personales