2 Botones
Otra forma de enviar comandos a una aplicación es mediante botones.
Aunque el API de Windows diferencia varios tipos de botones (botones pulsables, checkboxes, radiobuttons...), en wxWidgets cada uno de ellos se considera un tipo de control diferente, y para cada uno hay una clase dedicada. En este capítulo nos centraremos en los botones pulsables (push buttons) o simplemente botones.
Su uso es más frecuente en diálogos, ya que estos no pueden disponer de menús, y es la única forma de enviarles comandos.
Un botón no es más que una pequeña ventana con un texto que indica la acción asociada, y opcionalmente con un mapa de bits, en la que el usuario puede hacer click para desencadenar la acción.
Aprovecharemos el tema de los botones para mostrar el otro tipo de aplicaciones: las basadas en dialogo.
Cuando nuestra aplicación no necesite hacer uso del área de cliente normalmente usaremos un diálogo.
Estructura de una aplicación basada en diálogo
De nuevo, necesitaremos declarar dos clases, y la plantilla lo hace por nosotros.
Clase App
La declaración de nuestra clase derivada de wxApp es idéntica a la de una aplicación basada en marco.
#ifndef WX002APP_H #define WX002APP_H #include <wx/app.h> class wx002App : public wxApp { public: virtual bool OnInit(); }; #endif // WX002APP_H
Y la definición es prácticamente la misma, salvo que se crea un objeto derivado de la clase wxDialog en lugar de la clase wxFrame.
#include "wx002App.h" #include "wx002Main.h" IMPLEMENT_APP(wx002App); bool wx002App::OnInit() { wx002Dialog* dlg = new wx002Dialog(0L, _("wxWidgets Application Template")); dlg->SetIcon(wxICON(aaaa)); // To Set App Icon dlg->Show(); return true; }
Clase Dialog
En la plantilla, la declaración de la clase derivada de wxDialog tiene esta forma:
#ifndef WX002MAIN_H #define WX002MAIN_H #include "wx002App.h" #include <wx/button.h> #include <wx/statline.h> class wx002Dialog: public wxDialog { public: wx002Dialog(wxDialog *dlg, const wxString& title); ~wx002Dialog(); protected: enum { idBtnQuit = 1000, idBtnAbout }; wxStaticText* m_staticText1; wxButton* BtnAbout; wxStaticLine* m_staticline1; wxButton* BtnQuit; private: void OnClose(wxCloseEvent& event); void OnQuit(wxCommandEvent& event); void OnAbout(wxCommandEvent& event); DECLARE_EVENT_TABLE() }; #endif // WX002MAIN_H
Como vemos, los eventos se manejan de la misma forma que en el caso de los marcos, hasta aquí no hay demasiadas diferencias. También podemos crear una tabla de eventos dinámica, en eso un diálogo y un marco no muestran diferencias.
Igual que en ejemplo anterior, usaremos un enumerado para definir nuestros identificadores para los botones, y para los métodos para procesar los eventos usaremos la misma regla, y empezarán con "On".
La definición de la clase diálogo también es muy parecida a la de la clase marco:
#include "wx002Main.h" BEGIN_EVENT_TABLE(wx002Dialog, wxDialog) EVT_CLOSE(wx002Dialog::OnClose) EVT_BUTTON(idBtnQuit, wx002Dialog::OnQuit) EVT_BUTTON(idBtnAbout, wx002Dialog::OnAbout) END_EVENT_TABLE() wx002Dialog::wx002Dialog(wxDialog *dlg, const wxString &title) : wxDialog(dlg, -1, title) { this->SetSizeHints(wxDefaultSize, wxDefaultSize); wxBoxSizer* bSizer1; bSizer1 = new wxBoxSizer(wxHORIZONTAL); m_staticText1 = new wxStaticText(this, wxID_ANY, wxT("Welcome To\nwxWidgets"), wxDefaultPosition, wxDefaultSize, 0); m_staticText1->SetFont(wxFont(20, 74, 90, 90, false, wxT("Arial"))); bSizer1->Add(m_staticText1, 0, wxALL|wxEXPAND, 5); wxBoxSizer* bSizer2; bSizer2 = new wxBoxSizer(wxVERTICAL); BtnAbout = new wxButton(this, idBtnAbout, wxT("&About"), wxDefaultPosition, wxDefaultSize, 0); bSizer2->Add(BtnAbout, 0, wxALL, 5); m_staticline1 = new wxStaticLine(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL); bSizer2->Add(m_staticline1, 0, wxALL|wxEXPAND, 5); BtnQuit = new wxButton(this, idBtnQuit, wxT("&Quit"), wxDefaultPosition, wxDefaultSize, 0); bSizer2->Add(BtnQuit, 0, wxALL, 5); bSizer1->Add(bSizer2, 1, wxEXPAND, 5); this->SetSizer(bSizer1); this->Layout(); bSizer1->Fit(this); } wx002Dialog::~wx002Dialog() { } void wx002Dialog::OnClose(wxCloseEvent &event) { Destroy(); } void wx002Dialog::OnQuit(wxCommandEvent &event) { Destroy(); } void wx002Dialog::OnAbout(wxCommandEvent &event) { wxString msg = wxbuildinfo(long_f); wxMessageBox(msg, _("Welcome to...")); }
Pero aquí es donde se pueden apreciar más diferencias.
Para empezar, vemos que en la tabla de eventos tenemos un tipo nuevo, EVT_BUTTON. Este es el evento que recibirá el diálogo cuando el usuario pulse un botón. En los parámetros indicaremos el identificador del botón y el método que se invocará cuando se reciba el evento desde el botón con ese identificador.
Lo siguiente nuevo que encontramos son los sizers. Dedicaremos un capítulo a ellos, pero básicamente se trata de un mecanismo para distribuir los controles dentro de una ventana, permitiendo alinearlos y espaciarlos automáticamente de varias formas diferentes. Esto puede resultar más práctico que calcular las coordenadas y dimensiones en unidades lógicas para cada control, e independiza la estructura gráfica de un diálogo del sistema operativo.
Por ahora baste decir que disponemos de diferentes tipos de sizers, agrupados en dos categorías: wxBoxSizer y wxGridSizer, es decir, de caja y de cuadrícula, cada uno de ellos con sus clases derivadas más específicas.
Nuestro diálogo solo usa los de la primera clase, que permite insertar los controles colocados y espaciados vertical u horizontalmente.
Los sizers pueden anidarse, que es lo que se hace en este ejemplo.
- Primero creamos un sizer horizontal.
- Insertamos un control estático en el sizer.
- Creamos un segundo sizer vertical.
- En el segundo sizer insertamos dos botones: About y Quit, y una barra estática.
- Insertamos el segundo sizer en el primero.
- Establecemos el primer sizer como el sizer del diálogo.
De esta forma crearemos un diálogo con la siguiente disposición:

Crear un boton
Para crear un botón usaremos uno de sus constructores. En muchas de las clases que encapsulan controles y ventanas disponemos de un constructor por defecto, que está destinado a la creación de objetos en dos fases. En la primera fase se invoca a este constructor que no requiere parámetros, y en la segunda fase se llama a un método Create, que es el que realmente crea el objeto, y que suele tener los mismos parámetros que otro constructor sobrecargado.
Este es el caso de la clase wxButton, aunque de momento usaremos el constructor con parámetros.
El constructor requiere ocho parámetros, aunque solo los dos primeros son obligatorios:
- parent: puntero a la ventana padre. La ventana padre es siempre aquella en la que se insertará el botón, y que recibirá los eventos correspondientes desde él.
- id: identificador. Un valor único que identifica el botón y que junto a cada evento indica qué botón lo ha generado.
- label: texto que se mostrará en el botón.
- pos: coordenadas de la posición de la esquina superior izquierda del botón. Cuando se usen sizers esta posición se calcula automáticamente, por lo que tendremos usar el valor de la posición por defecto, wxDefaultPosition, que además es el valor que se usará si no especificamos este parámetro.
- size: tamaño del botón. Si usamos el valor por defecto, wxDefaultSize y no usamos sizers, el tamaño se ajusta automáticamente en función del texto del botón. Si se usan sizers, el tamaño se ajusta por ellos.
- style: cero o más de los estilos de ventana del botón, combinados con OR.
- validator: un validador opcional. Hablaremos de validadores en otro capítulo. Baste decir por ahora que se encargarán de convertir variables C++ a contenidos de controles, validar esos contenidos para detectar errores de ámbito antes de volver a convertirlo a variables C++, y manejar ciertos eventos.
- name: nombre de la ventana.
En el ejemplo de la plantilla se usan sizers, por lo que en realidad bastaría con indicar los tres primeros parámetros:
wxButton *BtnAbout = new wxButton(this, idBtnAbout, wxT("&About"));
Pero si no queremos usar sizers, una alternativa podría ser:
this->SetSize(320, 150); wxStaticText* m_staticText1 = new wxStaticText(this, wxID_ANY, wxT("Welcome To\nwxWidgets"), wxPoint(30,30), wxSize(180,200), 0); m_staticText1->SetFont(wxFont(20, wxFONTFAMILY_ROMAN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_THIN, false, wxT("Arial"))); new wxButton(this, idBtnAbout, wxT("&About"), wxPoint(220, 20), wxDefaultSize, 0); new wxStaticLine(this, wxID_ANY, wxPoint(180,50), wxSize(130,3), wxLI_HORIZONTAL); new wxButton(this, idBtnQuit, wxT("&Quit"), wxPoint(220,60), wxDefaultSize, 0);
El método SetSize, que wxDialog hereda de la clase wxWindow, sirve para establecer las dimensiones del diálogo.
No nos preocuparemos de momento de los controles estáticos, los veremos en otro capítulo.
Para insertar los botones usaremos el constructor, indicando la posición mediante un objeto wxPoint y el tamaño mediante un objeto wxSize, o también podemos dejar que el tamaño se calcule automáticamente usando el valor wxDefaultSize.
Activar y desactivar botones
Como ocurre con los menús, a veces puede ser conveniente evitar que el usuario pueda activar un botón, ya sea porque la acción no tenga sentido o pueda tener consecuencias no deseadas si se activa.
Los botones son controles, es decir, son clases que heredan de la clase wxControl, que a su vez hereda de wxWindow. Esta última clase dispone del método Enable(). Este método permite activar o desactivar una ventana, y por lo tanto un control o botón en función de un parámetro de tipo booleano. También dispone de un método Disable() que equivale a Enable(false).
Si se desactiva un botón no será posible interactuar con él, y por lo tanto no generará eventos.
Por ejemplo, podemos modificar nuestro ejemplo para que el botón "Quit" solo se active después de haber pulsado "About". Para ello, al crear el botón de salida lo deshabilitamos:
BtnQuit = new wxButton(this, idBtnQuit, wxT("&Quit"), wxPoint(220,60), wxDefaultSize, 0); BtnQuit->Disable(); // O BtnQuit->Enable(false);
Y al procesar el evento que se produce al pulsar "About" lo habilitamos:
void wx002Dialog::OnAbout(wxCommandEvent &event) { ... BtnQuit->Enable(); // El valor por defecto para el parámetro es true }
Podemos averiguar si un botón está activado mediante el método IsEnabled(). Esto nos permite que en nuestro ejemplo el evento EVT_CLOSE no cierre la aplicación si el botón "Quit" está desactivado.
void wx002Dialog::OnClose(wxCloseEvent &event) { if(BtnQuit->IsEnabled()) Destroy(); }
Desactivar un botón solo impide interactuar con él, pero sigue mostrándose en la ventana, aunque atenuado para indicar al usuario que el botón no está activo.
Ocultar y mostrar botones
El mismo objetivo se puede conseguir ocultando y mostrando el botón. En lugar de mostrarlo atenuado, podemos ocultarlo por completo. Para ello usaremos el método Hide() de wxWindow, y para mostrarlo usaremos el método Show().
Esto nos proporciona una forma de reutilizar el espacio en el diálogo para diferentes configuraciones. Por ejemplo. podemos crear todos los botones que necesitemos en la aplicación, aunque ocupen el mismo espacio en el área de cliente, y que solo algunos se muestren en diferentes circunstancias, y ocular el resto. Más adelante veremos que hay otras formas más simples de hacer esto, mediante un tipo especial de diálogo de la clase wxWizard, que puede contener varias páginas. Este es el tipo de diálogo que se usa, por ejemplo, para crear un proyecto en Code::Blocks.
También podemos averiguar si un botón está oculto mediante el método IsShown().
Botón por defecto
Casi siempre querremos que algunos de los botones tengan un comportamiento especial.
Concretamente, nos puede interesar que uno de los botones se active cuando pulsamos la tecla intro. Este botón es el que se denomina "botón por defecto", y evidentemente, solo uno de los botones en un diálogo puede tener esta propiedad. El valor de retorno será un puntero al botón por defecto anterior, o NULL si no lo había.
En los diálogos con un botón "Aceptar" u "Ok", este suele ser el botón por defecto, pero en general dependerá de que opción sea la más conveniente en nuestro caso.
Para asignar un botón por defecto se usa el método SetDefault() sobre el botón que queramos que tenga ese comportamiento. Por ejemplo:
BtnAbout = new wxButton(this, idBtnAbout, wxT("&About"), wxPoint(220, 20), wxDefaultSize, 0); BtnAbout->SetDefault();
Botón de escape
En los diálogos también es frecuente que uno de los botones se active al pulsar la tecla ESC. Este botón suele ser el de "Cancelar", pero de nuevo, dependerá de la aplicación.
En este caso, la clase wxButton no dispone de un método para asignar el botón de escape. Pero ese método existe en la clase wxDialog. Se trata del método SetEscapeId(), indicando en el parámetro el identificador del botón:
BtnQuit = new wxButton(this, idBtnQuit, wxT("&Quit"), wxPoint(220,60), wxDefaultSize, 0); this->SetEscapeId(idBtnQuit);
Añadir mapa de bits a un botón
La clase wxButton hereda métodos desde la clase wxAnyButton que permiten añadir mapas de bits a los botones.
Para cada botón se pueden establecer cuatro mapas de bits, cada uno correspondiente a un estado diferente del botón:
- Al estado normal del botón le corresponde el mapa de bits que se establece con el método SetBitmapLabel().
- SI queremos un mapa de bits diferente cuando el cursor del ratón esté sobre el botón, podremos establecerlo con SetBitmapCurrent().
- Podemos asignar un mapa de bits diferente para cuando el botón tiene el foco mediante SetBitmapFocus().
- Y otro distinto cuando el botón está presionado, usando el método SetBitmapPressed().
- Por último, se puede establecer un mapa de bits para el botón desactivado con SetBitmapDisabled().
Si solo queremos un único mapa de bits independientemente del estado del botón, simplemente usaremos el método SetBitmap(). O bien usar ese método y cualquiera de los otros que nos interese, por ejemplo, SetBitmapDisabled() si queremos que el aspecto del mapa de bits sea distinto cuando desactivemos el botón.
También podemos especificar en qué zona del botón se mostrará el mapa de bits. Mediante el método SetBitmapPosition(), indicando como parámetro una de las constantes wxDirection: wxLEFT, wxRIGHT, wxTOP o wxBOTTOM.
Como ya vimos en el tema de los menús, si queremos utilizar imágenes PNG en nuestros programas tendremos que incluir el fichero de cabecera imagpng.h, y añadir el manejador de PNG:
#include <wx/imagpng.h> ... wxImage::AddHandler(new wxPNGHandler);
Igual que en el capítulo anterior, usaremos el fichero de recursos para incluir los mapas de bits en nuestro proyecto:
bmsalir16 RCDATA "salir16x16.png" bmsalir32 RCDATA "salir32x32.png" bmnosalir16 RCDATA "nosalir16x16.png" bmnosalir32 RCDATA "nosalir32x32.png"
Y ya podremos usar estos gráficos en nuestros botones:
BtnQuit->SetBitmap(wxBITMAP_PNG(bmsalir16)); BtnQuit->SetBitmapDisabled(wxBITMAP_PNG(bmnosalir16));
Adicionalmente podemos crear botones con una imagen asociada usando la clase específica wxBitmapButton. La diferencia con wxButton, es que con esta clase podemos especificar el mapa de bits directamente en el constructor. Además, como esta clase hereda de wxButton, seguimos teniendo acceso a sus métodos y a los de las clases base, como los de wxAnyButton.
Ejemplo 2
Nombre | Fichero | Fecha | Tamaño | Contador | Descarga |
---|---|---|---|---|---|
Ejemplo 2 | wx002.zip | 2024-12-02 | 6919 bytes | 6 |