terça-feira, janeiro 09, 2007

Curso de wxWidgets, post 11: Construindo barras de ferramentas

Agora vamos dar uma incrementada no nosso editor de textos, adicionando uma barra de ferramentas nele. A classe resposável por isto é a wxToolBar.

O procedimento básico é criar a barra, instanciando um objeto da classe e em seguida, atribuir um método ao evento de clique dos botões da barra.

Como sempre, vamos iniciar pelo construtor:

wxToolBar(wxWindow* parent, wxWindowID id, const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxDefaultSize, long style = wxTB_HORIZONTAL | wxNO_BORDER,
const wxString& name = wxPanelNameStr)

parent: A janela que conterá a barra.

id: ID da barra.

pos: Posição.

size: Tamanho.

style: O estilo da barra.

name: Nome da barra.


Apesar dos parâmetros pos e size, nós não precisamos especificar tamanho e a posição da barra de ferramentas. Apenas iremos criá-la normalmente e "prendê-la" no topo da janela. Para isto, vamos declarar a barra como um atributo privado da classe do nosso frame e instanciá-la no construtor.

Na definição da classe de MeuFrame:
private:
wxToolBar *tbar;


No construtor de MeuFrame:

tbar = new wxToolBar(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTB_TEXT);


Precisaremos também adicionar IDs para os botões da barra na lista de enum do nosso código:

enum
{
ID_BTNLOAD,
ID_BTNSAVE,
ID_TBAR_LOAD,
ID_TBAR_SAVE,
ID_TBAR_ABOUT
};


Agora que a barra de ferramentas está criada, podemos adicionar nossos botões a ela. Fazemos isso também no construtor de MeuFrame, depois da instanciação da barra.

Para o nosso editor de textos, iremos adicionar três botões, "Abrir", "Salvar" e "Sobre", além de um separador antes do "Sobre". Isto será feito através do método wxToolBar::AddTool() e wxToolBar::AddSeparator() para o separador:

tbar->AddTool(ID_TBAR_LOAD, wxString(wxT("Abrir")), \
wxBitmap(wxT("/usr/share/pixmaps/gnome-folder.png"), wxBITMAP_TYPE_PNG));

tbar->AddTool(ID_TBAR_SAVE, wxString(wxT("Salvar")), \
wxBitmap(wxT("/usr/share/pixmaps/gnome-cd.png"), wxBITMAP_TYPE_PNG));

tbar->AddSeparator();

tbar->AddTool(ID_TBAR_ABOUT, wxString(wxT("Sobre")), \
wxBitmap(wxT("/usr/share/pixmaps/gnome-info.png"), wxBITMAP_TYPE_PNG));

tbar->Realize();


Perceba que eu usei o método wxToolBar::Realize() após adicionar os botões. Isto irá atualizar a barra.

No código acima, usei algumas imagens que estão instaladas no meu computador. Você pode alterar o caminho das imagens acima para outras existentes no seu sistema. Obviamente, se você está compilando no Windows, os caminhos acima não funcionarão.

No final deste post, falarei um pouco sobre o uso da classe wxBitmap.

Por último, vamos dizer ao frame quem é sua barra de ferramentas. Para isto, usamos o método wxFrame::SetToolBar():

// Insere a barra de ferramentas
SetToolBar(tbar);


Neste ponto, a barra já irá aparecer na janela do editor.

Criando eventos para a barra de ferramentas

A parte mais simples já foi feita. Agora vamos dar vida à barra!

No nosso exemplo do editor de textos, fiz uma modificação para facilitar o código. Criei os métodos Abrir(), Salvar() e Sobre(). Os códigos de carregamento e gravação do arquivo, que antes estavam em OnLoadClick() e OnSaveClick() foram pra Abrir() e Salvar() respectivamente. Sobre() tem um código para exibir um pop-up.

Mas o método mais importante que iremos criar é o MeuFrame::OnToolBarClick(). Veja sua definição:

void OnToolBarClick(wxCommandEvent &event);


Este método tratará os eventos de clique na barra de ferramentas. Antes de mostrar sua implementação, veja a definição do evento na tabela de eventos:

EVT_TOOL_RANGE(ID_TBAR_LOAD, ID_TBAR_ABOUT, MeuFrame::OnToolBarClick)


Para os botões normais (wxButton), nós precisamos definir um evento para cada botão. No caso da barra de ferramentas, definimos o range dos IDs dos botões da barra. No nosso exemplo, associamos este eventos a todos os IDs desde ID_TBAR_LOAD até ID_TBAR_ABOUT. Isso é muito útil quando criamos uma barra com muitos botões.

Você pode ver os outros eventos disponíveis para o wxToolBar na documentação da classe.

Mas como na definição, todos os IDs irão chamar o mesmo método, como iremos dar funções diferentes para cada botão da barra? Devemos tratar isto dentro do método. Agora mostrarei sua implementação:

// Método para a barra de ferramentas
void MeuFrame::OnToolBarClick(wxCommandEvent &event)
{
switch (event.GetId())
{
case ID_TBAR_LOAD:
Abrir();
break;

case ID_TBAR_SAVE:
Salvar();
break;

case ID_TBAR_ABOUT:
Sobre();
break;
}
}


O switch (event.GetId()) vai criar um switch com a ID do evento. Então, podemos dar diferentes tarefas para os botões dentro do switch, como mostrado acima.

Bem, este é o básico sobre wxToolBar. Nosso editor de texto agora está assim:



Tente modificar as imagens da barra (detalhes abaixo), ou adicionar novos botões. Lembre-se de checar as IDs na definição do evento caso você adicione ou remova botões.

Alguns tópicos sobre o uso do wxBitmap na barra de ferramentas

Não falei sobre wxBitmap ainda, também não vou entrar em detalhes agora, falarei apenas o suficiente para usar imagens no wxToolBar.

A classe wxBitmap serve basicamente para tratar imagens, exibindo-as no seu programa ou em outros widgets. No caso do wxToolBar, nós usamos o wxBitmap para definir as imagens que serão exibidas em cada botão da barra.

No nosso exemplo, eu usei um construtor bastante simples:

wxBitmap(const wxString& name, long type)


Este construtor carrega uma imagem de um arquivo do tipo especificado. Como usei arquivos .PNG, usei o tipo wxBITMAP_TYPE_PNG. Existem diversos outros tipos que você pode encontrar na documentação do construtor.

Mas o wxBitmap também pode tratar tipos de uma outra classe chamada wxImage. O tipo que usei, por exemplo, não é de wxBitmap e sim de wxImage.

O tipo wxBMPHandler está diponível por padrão no programa. Para usar outros tipos (como o wxBITMAP_TYPE_PNG que eu usei), devemos "instalar" eles. Para isto, usamos o método wxInitAllImageHandlers(), que deve ser chamado no método OnInit() da classe principal do nosso programa.

Então, para colocar imagens nos botões da barra de ferramentas, devemos iniciar os handlers para todos os tipos suportados com wxInitAllImageHandlers() no método OnInit() da classe principal. Em seguida, abrir a imagem com wxBitmap e passar o objeto como parâmetro de wxToolBar::AddTool().

Futuramente irei falar melhor sobre wxBitmap e wxImage, mas por enquanto, deixarei apenas o suficiente para usarmos o wxToolBar.

Veja o código-fonte completo do editor de textos no estado atual:

/* A Casa de Just - http://jpjust.blogspot.com
* Curso de wxWidgets: Um editor de textos
*
* O objetivo deste código-fonte é demonstrar diversas classes
* ensinadas no curso de wxWidgets do blog "A Casa de Just".
*
* As aulas do curso de wxWidgets podem ser encontradas em forma
* de posts no blog: http://jpjust.blogspot.com
*
* Copyright (c) João Paulo Just
* A Casa de Just - http://jpjust.blogspot.com
* 9 de janeiro de 2007, 22:45, Ilhéus, BA, Brasil.
*/

#include
#include

// IDs
enum
{
ID_BTNLOAD,
ID_BTNSAVE,
ID_TBAR_LOAD,
ID_TBAR_SAVE,
ID_TBAR_ABOUT
};

// Classe do programa
class MeuPrograma: public wxApp
{
public:
// Método principal
virtual bool OnInit(void);
};

// Classe do frame
class MeuFrame: public wxFrame
{
public:
// Construtor
MeuFrame(void);

// Eventos
void OnLoadClick(wxCommandEvent &event);
void OnSaveClick(wxCommandEvent &event);
void OnToolBarClick(wxCommandEvent &event);

// Métodos auxiliares
void Abrir(void);
void Salvar(void);
void Sobre(void);

DECLARE_EVENT_TABLE()

private:
// Widgets do frame
wxButton *btn_load;
wxButton *btn_save;

wxStaticText *lb_filename;

wxTextCtrl *txt_file;

wxToolBar *tbar;
};

// Inicialização do programa
bool MeuPrograma::OnInit(void)
{
wxInitAllImageHandlers(); // Inicia todos os hadlers de imagens do wxBitmap

MeuFrame *frame = new MeuFrame();
frame->Show();
SetTopWindow(frame);
return true;
}

// Tabela de eventos
BEGIN_EVENT_TABLE(MeuFrame, wxFrame)
EVT_BUTTON(ID_BTNLOAD, MeuFrame::OnLoadClick)
EVT_BUTTON(ID_BTNSAVE, MeuFrame::OnSaveClick)
EVT_TOOL_RANGE(ID_TBAR_LOAD, ID_TBAR_ABOUT, MeuFrame::OnToolBarClick)
END_EVENT_TABLE()

// Construtor do frame
MeuFrame::MeuFrame(void)
:wxFrame(NULL, wxID_ANY, wxT("Arquivos"))
{
// Sizer e widgets
wxBoxSizer *sizer_h = new wxBoxSizer(wxHORIZONTAL);
wxBoxSizer *sizer_v = new wxBoxSizer(wxVERTICAL);

btn_load = new wxButton(this, ID_BTNLOAD, wxT("Abrir"));
btn_save = new wxButton(this, ID_BTNSAVE, wxT("Salvar"));

lb_filename = new wxStaticText(this, wxID_ANY,
wxT("Sem titulo.txt"));

txt_file = new wxTextCtrl(this, wxID_ANY, wxEmptyString,
wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE);

// Barra de ferramentas
tbar = new wxToolBar(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTB_TEXT);

tbar->AddTool(ID_TBAR_LOAD, wxString(wxT("Abrir")), \
wxBitmap(wxT("/usr/share/pixmaps/gnome-folder.png"), wxBITMAP_TYPE_PNG));

tbar->AddTool(ID_TBAR_SAVE, wxString(wxT("Salvar")), \
wxBitmap(wxT("/usr/share/pixmaps/gnome-cd.png"), wxBITMAP_TYPE_PNG));

tbar->AddSeparator();

tbar->AddTool(ID_TBAR_ABOUT, wxString(wxT("Sobre")), \
wxBitmap(wxT("/usr/share/pixmaps/gnome-info.png"), wxBITMAP_TYPE_PNG));

tbar->Realize();

// Posicionamento
sizer_h->Add(btn_load, 0, wxALL, 5);
sizer_h->Add(btn_save, 0, wxALL, 5);
sizer_h->Add(lb_filename, 1, wxALL, 5);

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

SetSizerAndFit(sizer_v);

// Insere a barra de ferramentas
SetToolBar(tbar);

// Boas-vindas
wxMessageBox(wxT("Seja bem-vindo(a) a mais um maravilhoso programa! :)"),
wxT("Bem-vindo(a)!"), wxICON_INFORMATION);
}

// Abre um arquivo
void MeuFrame::Abrir(void)
{
wxString Filename;

// Abre diálogo de arquivo
Filename = wxFileSelector(wxT("Selecione o arquivo"),
wxEmptyString, wxEmptyString, wxEmptyString,
wxT("*.txt"), wxOPEN | wxFILE_MUST_EXIST);

// Se arquivo inválido, sai do método
if (Filename == wxEmptyString)
return;

txt_file->LoadFile(Filename);

// Atualiza o nome
lb_filename->SetLabel(Filename);
}

// Salva o arquivo aberto
void MeuFrame::Salvar(void)
{
wxString Filename;

// Abre diálogo de arquivo
Filename = wxFileSelector(wxT("Selecione o arquivo"),
wxEmptyString, wxEmptyString, wxEmptyString,
wxT("*.txt"), wxSAVE | wxOVERWRITE_PROMPT);

// Se arquivo inválido, sai do método
if (Filename == wxEmptyString)
return;

txt_file->SaveFile(Filename);

// Atualiza o nome do arquivo
lb_filename->SetLabel(Filename);
}

// Janela "Sobre"
void MeuFrame::Sobre(void)
{
wxMessageBox(wxT("Curso de wxWidgets\n\nhttp://jpjust.blogspot.com"), wxT("Sobre"), wxICON_INFORMATION);
}

// Botão de leitura
void MeuFrame::OnLoadClick(wxCommandEvent &event)
{
Abrir();
}

// Botão de salvamento
void MeuFrame::OnSaveClick(wxCommandEvent &event)
{
Salvar();
}

// Método para a barra de ferramentas
void MeuFrame::OnToolBarClick(wxCommandEvent &event)
{
switch (event.GetId())
{
case ID_TBAR_LOAD:
Abrir();
break;

case ID_TBAR_SAVE:
Salvar();
break;

case ID_TBAR_ABOUT:
Sobre();
break;
}
}

IMPLEMENT_APP(MeuPrograma)

7 comentários:

Anônimo disse...

Caro Just!!
primeiro Feliz 2007 (bem atrasado!) - Felicidades e muita programação nesse ano rpz!! :)
Fiquei muito contente quando entrei no blog e vi que o curso de wxwidgets voltou!! - dá mais ânimo pra estudar wxwidgets!!
Uma pergunta - num sei como tá seu planejamento de post - mas vai ter algum tópico sobre wxwidgets e banco dados?
abraço

Unknown disse...

Olá Anderson :)

Apesar de nunca ter usado banco de dados com wx, pretendo estudar essa parte e postar sobre isso no blog.

Assim que li seu comentário, dei uma olhada na documentação no wx e encontrei essa página:

http://www.wxwidgets.org/manuals/stable/wx_odbcoverview.html#odbcoverview

É um ponto de partida pra quem quer usar banco de dados com wx. Estarei lendo essa página ainda nessa semana pra começar a aprender. :)

Gostei da sua sugestão, se tiver mais idéias, é só mandar.

Unknown disse...

fla Just,
lembra que esses dias te mandei um email perguntado sobre como aplicar estilos em um texto (fonte, negrito, etc). Ai vc me mandou um código explicando e tal - só que tentei, e tentei e os estilos nunca pegavam na no texto selecionado do wxTextCtrl. Só fui conseguir aplicar estilo depois que mudei de wxTextCtrl para um wxRichTextCtrl.
No caso desse teu exemplo de editor de textos num seria melhor usar um wxRichaTextCtrl?
http://www.wxwidgets.org/manuals/stable/wx_wxrichtextctrl.html
vlw

Unknown disse...

+
pra quem programa no Ruindows (como eu! - que infelicidade! - já fiz de tudo pro wx funfar no meu debian - mas num tem jeito - já apelei até pra formatação e reinstalação do sistema - rsrssr). Um boa pra quem usam ruindows é o wxDevC++ (vc já conhece?):
http://wxdsgn.sourceforge.net/index.php
fácil instalação (next, next...) ajuda bastante.. auto completação de código, vários widgets, etc

Anônimo disse...

e as dúvidas não para de surgir..!!
just existe alguma técnica pra deixar programa já compilado (.exe) menos pesado. Pq qualquer janelinha com alguns botões dá um executável de 3,4,5... megas. Tem como deixar o exe menor? ou a falha é do programador aqui mesmo? rsrs
flw

Unknown disse...

E aí, Anderson.

Veja se você não tá compilando o wx em modo debug. Se tiver usando o wxDev, acho que ele vem em modo debug por padrão. Nesse modo, todo o código fonte e outras informações de depuração vão junto com o código, por isso ele cresce.

Pra você ter uma idéia, o maior executável do WinPolicy tem 1 MB, e só porque ele contém várias imagens embutidas. Se fosse no modo debug, ia ser de 10 MB pra lá.

Anônimo disse...

Muito bom o material, apesar de ter sido postado há 10 anos atrás, os códigos continuam funcionais.