terça-feira, outubro 03, 2006

Curso de wxWidgets, post 5: Criando eventos

Bem, já criamos um programa, fizemos uma janela e colocamos widgets nela (um wxStaticText, um wxTextCtrl e um wxButton). Mas que tal se esse botão fizesse alguma coisa além de enfeitar a janela? :)

Aí é que entram os eventos. O que vamos fazer é associar um evento a um método da classe que será responsável pela ação deste evento. Na nossa janela, iremos associar um evento de clique no botão "Hello!" a um método que iremos criar na classe MeuFrame.

Vamos começar criando um ID para o evento. Basta usar um #define ou um enum para isto. No nosso exemplo, usaremos um enum:

enum
{
ID_BTN_HELLO
};


Esse ID será usado na criação do widget do botão, no lugar do famoso wxID_ANY. Agora, vamos ao método. Ele vai se chamar OnHelloClick(). Eu gosto de colocar "On" como prefixo de um evento e a ação como sufixo, no caso, "Click", mas você pode usar o nome que desejar, como CliqueNoBotao(), por exemplo. Na definição da classe, adicionamos o protótipo do método:

public:
MeuFrame(void);
void OnHelloClick(wxCommandEvent &event);


O método deve retornar void. No caso de um evento de clique em um wxButton, o parâmetro é um objeto do tipo wxCommandEvent, mas isso vai variar de evento para evento (detalhes mais adiante).

Em seguida, implementamos o método:

void MeuFrame::OnHelloClick(wxCommandEvent &event)
{
if (txt_name->GetValue() == wxEmptyString)
wxMessageBox(wxT("Digite um nome na caixa de texto."), wxT("Aviso"), wxICON_EXCLAMATION);
else
wxMessageBox(wxT("Olá, ") + txt_name->GetValue() + wxT("!"), wxT("Olá"), wxICON_INFORMATION);
}


O que acontece acima? Primeiro, verificamos se existe uma string na caixa de textos. Se não houver, a mensagem "Digite um nome na caixa de texto" é exibida. Se houver uma string, ela será usada para exibir a mensagem "Olá, (string)!". Não se preocupe com o conteúdo desse método por enquanto, no próximo post falarei sobre a classe wxString. Porém, vou adiantar um pouco sobre métodos dos widgets.

Logo na primeira linha do método acima, chamamos wxTextCtrl::GetValue(), esse método retorna um objeto do tipo wxString, contendo a string digitada na caixa de texto. Recomendo que você dê uma olhada na relação dos métodos dos widgets ao utilizá-los pela primeira vez, isso é bom para que você saiba o que pode ser feito com cada widget.

Precisamos declarar a tabela de eventos (que será criada ainda). Fazemos isso na definição da classe:

private:
wxStaticText *lb_hello;
wxTextCtrl *txt_name;
wxButton *btn_say;

DECLARE_EVENT_TABLE()


Depois de criar o método e declarar a tabela de eventos da classe, vamos montá-la. Uma tabela de eventos começa com a macro BEGIN_EVENT_TABLE(classe, tipo) e termina com a macro END_EVENT_TABLE(). Entre essas duas macros, outras são inseridas, associando eventos a métodos. No caso do clique do botão, usaremos a macro EVT_BUTTON(id, func):

BEGIN_EVENT_TABLE(MeuFrame, wxFrame)
EVT_BUTTON(ID_BTN_HELLO, OnHelloClick)
END_EVENT_TABLE()


Na macro EVT_BUTTON(), passamos como parâmetro a ID do evento e o método que será chamado. Para saber qual macro usar em cada tipo de widget, basta olhar na sua própria documentação. Por último, basta fazer uma alteração na criação do widget do botão, trocando o valor wxID_ANY por ID_BTN_HELLO (o parâmetro ID do construtor do widget):

lb_say = new wxButton(this, ID_BTN_HELLO, wxT("Hello!"));


Agora, é só compilar e executar o nosso exemplo:



Agora tente criar novos botões e eventos para eles. Leia o tópico "Event handling overview", do manual do wxWidgets. Na seção "Event macros summary" você encontra uma lista de classes de eventos, como a wxCommandEvent usada no exemplo do botão. Lembre-se que para saber qual macro usar em cada evento, basta ler a documentação do widget em questão.

Como está nosso programa até agora?

Após quatro posts de código (do 2 até este), pode ser que as coisas tenham ficado embaralhadas para alguns visitantes. Colei abaixo o código completo do nosso programa.

#include "wx/wx.h"

enum
{
ID_BTN_HELLO
};

class MeuPrograma: public wxApp
{
public:
virtual bool OnInit(void);
};

class MeuFrame: public wxFrame
{
public:
MeuFrame(void);
void OnHelloClick(wxCommandEvent &event);

private:
wxStaticText *lb_hello;
wxTextCtrl *txt_name;
wxButton *btn_say;

DECLARE_EVENT_TABLE()
};

bool MeuPrograma::OnInit(void)
{
MeuFrame *frame = new MeuFrame();
frame->Show();
SetTopWindow(frame);
return true;
}

BEGIN_EVENT_TABLE(MeuFrame, wxFrame)
EVT_BUTTON(ID_BTN_HELLO, MeuFrame::OnHelloClick)
END_EVENT_TABLE()

MeuFrame::MeuFrame(void)
:wxFrame(NULL, wxID_ANY, wxT("Meu Programa"))
{
// Criação dos sizers
wxBoxSizer *sizer_v = new wxBoxSizer(wxVERTICAL);
wxBoxSizer *sizer_h = new wxBoxSizer(wxHORIZONTAL);

// Criação dos widgets
lb_hello = new wxStaticText(this, wxID_ANY, wxT("Digite seu nome na caixa:"));
txt_name = new wxTextCtrl(this, wxID_ANY);
btn_say = new wxButton(this, ID_BTN_HELLO, wxT("Hello!"));

// Posicionamento dos widgets
sizer_h->Add(txt_name, 1, wxALL, 5);
sizer_h->Add(btn_say, 0, wxALL, 5);

sizer_v->Add(lb_hello, 0, wxALL, 5);
sizer_v->Add(sizer_h, 0, wxALL | wxEXPAND, 5);

SetSizerAndFit(sizer_v);
}

void MeuFrame::OnHelloClick(wxCommandEvent &event)
{
if (txt_name->GetValue() == wxEmptyString)
wxMessageBox(wxT("Digite um nome na caixa de texto."), wxT("Aviso"), wxICON_EXCLAMATION);
else
wxMessageBox(wxT("Olá, ") + txt_name->GetValue() + wxT("!"), wxT("Olá"), wxICON_INFORMATION);
}

IMPLEMENT_APP(MeuPrograma)


3 comentários:

Anônimo disse...

Great, dude!
I've been making part of your wxWidgets' on-line course from the beginning. And I'm really enjoying this new learning.
So,
I had trouble when I tried to use wxArrayString as constructor wxComboBox parameter. Into:

wxComboBox(wxWindow* parent, wxWindowID id, const wxString& value, const wxPoint& pos, const wxSize& size, const wxArrayString& choices, long style = 0, const wxValidator& validator = wxDefaultValidator, const wxString& name = "comboBox")

First of all I didn't get to create an array of strings by using the method wxArrayString::wxInsert! Could you help me?

Thanks in advance

Unknown disse...

I wrote an example using wxArrayString and wxComboBox. I declared an object of wxArrayString in my frame class definition and inserted some strings into it at frame construction. Then, I used this object as parameter for wxComboBox constructor. The example is below:

#include "wx/wx.h"

class MyApp: public wxApp
{
public:
virtual bool OnInit(void);
};

class MyFrame: public wxFrame
{
public:
MyFrame(void);

private:
wxArrayString m_array;
wxComboBox *combo;
};

bool MyApp::OnInit(void)
{
MyFrame *frame = new MyFrame();
frame->Show(true);
SetTopWindow(frame);
return true;
}

MyFrame::MyFrame(void)
:wxFrame(NULL, wxID_ANY, wxT("My Frame"))
{
// Append to list
m_array.Add(wxString(wxT("First add")));

// Insert into beggining
m_array.Insert(wxString(wxT("First insert")), 0);

// Insert into position 1
m_array.Insert(wxString(wxT("Second insert")), 1);

// ComboBox construction
combo = new wxComboBox(this, wxID_ANY, wxEmptyString, \
wxDefaultPosition, wxDefaultSize, m_array);
}

IMPLEMENT_APP(MyApp)

Anônimo disse...

Good morning, dude!
Now, I noticed my mistake! Actually, I did a pointer for wxArrayString and I didn't use this line:

m_array.Add(wxString(wxT("First add")));

Perhaps, that's why it didn't work. I own you one, man.

Thanks for help