C++ a wxWidgets (2. díl)

V celém kurzu budeme postupovat metodou vylepšování stávající aplikace z 1. dílu. Podle plánu se podíváme na vytváření menu, tlačítek a jejich svázání s událostmi. Taky obohatíme naši aplikaci o text.

Menu
Ve wxWidgets se menu rozděluje následovně:

Rozdělení menu

Lišta menu (MenuBar) je objekt, do kterého vkládáme objekty Menu (Menu) a ty obsahují prvky Položky menu (MenuItem).
Nejdřív tedy musíme vytvořit MenuBar a k tomu je určena třída wxMenuBar. Při vytváření třídy předáváme prázdný seznam parametrů, její konstruktor nám vrátí ukazatel na místo v paměti, kde je daný objekt uložen.

wxMenuBar *listaMenu = new wxMenuBar();

V dalším kroku je třeba vytvořit položky menu, které zastupuje třída wxMenu.

wxMenu *soubor = new wxMenu();
wxMenu *napoveda = new wxMenu();

Vytvořili jsme si dvě instance třídy wxMenu, jednu s názvem soubor a druhou napoveda. Konstruktor třídy opět nevyžaduje žádné parametry a vrací ukazatel. Abychom mohli do menu přidat nějaké položky, musíme použít funkci třídy wxMenu Append(int id, const wxString& menuItem). První parametr je id.
U většiny tříd je potřeba při vytváření její instance, předat její ID. ID je unikátní celočíselná konstanta, která se nesmí v programu nikde shodovat s žádnou jinou. My ID použijeme hlavně v událostech, kde jím budeme identifikovat objekt, který událost vyvolal.
Druhým parametrem je řetězec, který se zobrazí na obrazovce jako text položky.

Nakonec musíme všechny menu přidat do lišty, na to slouží opět funkce Append, ale třídy wxMenuBar. Ta má rozdíl v prvním parametru, který je ukazatel na objekt třídy wxMenu.

Náš kód by měl tedy vypadat takto:

#include "Okno.h" //vložíme hlavičkový soubor s deklarací naší třídy
 
Okno::Okno(const wxString& titulek)
         :wxFrame(NULL, wxID_ANY, titulek, wxDefaultPosition, wxSize(300, 150)) {
    //vytvoříme potřebné objekty
    wxMenuBar *listaMenu = new wxMenuBar();
    wxMenu *soubor = new wxMenu();
    wxMenu *napoveda = new wxMenu();
    //vložíme položky do menu
    soubor->Append(ID_KONEC, wxT("Konec"));
    napoveda->Append(ID_NAPOVEDA, wxT("Napoveda"));
    napoveda->Append(ID_OPROGRAMU, wxT("O programu"));
    //vložíme menu do lišty
    listaMenu->Append(soubor, wxT("Soubor"));
    listaMenu->Append(napoveda, wxT("Napoveda"));
    SetMenuBar(listaMenu); //funkce, která vykreslí menu na obrazovce
    Centre();
}

A hlavičkový soubor:

#include //wx/wx.h
 
//definice ID položek menu
#define ID_KONEC 101
#define ID_NAPOVEDA 102
#define ID_OPROGRAMU 103
 
//třída zastupující naše okno
class Okno: public wxFrame {
    public:
        Okno(const wxString& titulek); //deklarace konstruktoru okna
};

Takové menu je nám ale k ničemu, protože po kliknutí stejně nic nedělá. Proto si musíme říct něco o událostech.

Události
Události jsou nejdůležitějším prvkem GUI. Bez nich by naše aplikace byly nepoužitelné. Díky událostem aplikace reagují při kliknutí na tlačítko, vybrání položky z menu, nebo třeba na pohyb myší.
Ve wxWidgets je práce s událostmi velmi jednoduchá. Pomocí funkce Connect svážeme objekt pomocí jeho ID s událostí, na níž se při jejím vyvolání zavolá naše funkce.
Nejdřív si ale musíme vytvořit funkce, které budeme volat.

// definování funkce Konec, která zničí okno
void Okno::Konec(wxCommandEvent& event) {
  Close();
}

Definovali jsme si členskou funkci Konec, která má parametr wxCommandEvent, díky tomu můžeme funkci svázat s událostí typu Command (stisknutí tlačítka, položky menu). Událostí je několik typů, celý přehled je například zde. Handler události bude funkci předán v parametru. Jediný úkol funkce je zavolat funkci Close(), která zavře naše okno. Nesmíme zapomenout vytvořit v deklaraci třídy prototyp funkce.

Teď už stačí jenom funkci spojit s příslušnou událostí a ID objektu, který ji vyvolá. Jak už bylo řečeno máme funkci Connect(int id, wxEventType TypUdálosti, wxObjectEventFunction funkce). Zkusme si přiblížit význam parametrů. První parametr je ID objektu, se kterým chceme událost svázat. Druhým parametrem je typ události, my budeme používat wxEVT_COMMAND_MENU_SELECTED nebo wxEVT_COMMAND_BUTTON_CLICKED, první pro menu a druhý pro tlačítka. A posledním, tedy třetím parametrem je funkce, kterou chceme zavolat. Musíme ji ale předat pomocí funkce wxCommandEventHandler() a jako parametr vložit naší funkci.

Connect(ID_KONEC, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(Konec));

Svázání události provedeme v konstruktoru okna po vytvoření menu.

Existuje ještě druhá možnost svázání událostí a to pomocí deklarace tabulky eventů. Tu ale používat nebudeme, protože je zastaralá a funkce Connect je jí nadřazená.
Podobně si můžete vytvořit funkce i pro ostatní položky menu.

Tlačítka
Ještě jsem slíbil vytvoření nějakých tlačítek. Je to velmi jednoduché, použijeme k tomu objekt wxButton.

wxPanel* panel = new wxPanel(this, wxID_ANY)
wxButton* tlacitkoKonec = new wxButton(panel, ID_TLACITKO_KONEC, wxT("Konec"), wxDefaultPosition, wxDefaultSize);

Protože není dobré tlačítko umísťovat přímo do wxFrame, vytvořili jsme nový panel reprezentovaný objektem wxPanel. O tom si řekneme více v příštím díle, který bude věnován pozicování a tvorbu layoutu programu.
Konstruktor objektu wxButton vyžaduje handle rodičovského objektu, v našem případě panel, dále ID, text, který se má zobrazit a jeho pozici a velikost. ID definujeme stejně jako předchozí, je ale dobré udržovat si určitý systém v jeho hodnotách. Např. pro menu hodnoty 101 – 199, pro tlačítka 201 – 299 atd..
Spojení s událostí provedeme stejně jako u menu, jenom musíme jako typ události předat wxEVT_COMMAND_BUTTON_CLICKED. Protože tlačítko má program ukončit můžeme použít funkci Konec.

Connect(ID_TLACITKO_KONEC, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(Okno::Konec));

Text
K vytvoření prostého textu existuje objekt wxStaticText.

wxStaticText* text = new wxStaticText(panel, wxID_ANY, wxT("Ajtaci.net"), wxPoint(120, 50), wxDefaultSize);

Tímto jsme vytvořili objekt wxStaticText, parametry konstruktoru jsou obdobné jako u tlačítka, takže rodič, ID (my jsme použili makro wxID_ANY, což znamená, že objekt ID nemá, šlo by také použít -1), text k zobrazení, pozice a velikost.
Ke změně textu v průběhu programu, existuje funkce SetLabel, kterou využijeme v příštím díle.

DŮ: Pokuste se vytvořit jakékoliv funkce k dalším položkám menu, popř. nové tlačítka, texty. Pořádně se seznamte s principem událostí, budeme je ještě potřebovat.

Jak už jsem řekl v příštím díle se podíváme na tvorbu a úpravy layoutu našeho okna.

O Daniel Švec

Autor se věnuje vývoji aplikací v C/C++, Python. Zajímá se o robotiku, elektrotechniku, a IT.