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

V minulém díle jsem slíbil malý úvod do tvorby rozložení aplikace. Takže se do toho pusťme.

V zásadě máme 2 možnosti jak v programu rozložit všechny prvky:

  • Absolutní pozicování
  • Sizery

Absolutní pozicování
Tuto možnost již umíme, jedná se o to, že všem prvkům přidělíme přesné pozice a velikosti. Pro většinu menších aplikací je to řešení dostačující, ale má spousty nevýhod.

  • Na různých platformách může aplikace vypadat jinak/deformovaně
  • Při ručním měněním velikosti okna prvky zůstavají stejné a objevuje se nevyužitá plocha
  • Při změně fontů se aplikace opět deformuje
  • Pokud budete chtít později změnit vzhled aplikace, znamená to bezmála celé začít znova

No tak vidíte, že s tímhle bychom daleko nedošli. V téhle souvislosti ještě zmíním prvek wxPanel, který jsem použili v minulém díle. Je to vlastně prvek, který vymezí určitý prostor v okně. Ten pak může být třeba nějak ohraničený jako rámeček. Ale hlavně se pak použije jako rodič, do kterého vkládáme další prvky, jako jsou tlačítka, textová políčka, apod.. Panelů můžeme mít v jednom okně několik, klidně můžeme udělat i celou hierarchii panelů. To ovšem reálné aplikaci nemá moc význam.

Sizery
Sizery (ang. Sizer) jsou speciální prvky na dynamické rozložení okna aplikace. Můžeme si vybrat z těchto sizerů:

  • wxBoxSizer
  • wxStaticBoxSizer
  • wxGridSizer
  • wxFlexGridSizer

Dokonce i objekt wxFrame, který je použitý jako základ aplikace má jeden vestavěný sizer. Takže pokud vložíme jenom jeden prvek, automaticky se roztáhne na velikost celého wxFrame. Můžete si to jednoduše ověřit že upravíte řádek v naší aplikaci následovně:

wxPanel* panel = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBORDER);

Přidaly jsme poslední parametr style, který znamená styl vykreslení panelu. Vložili jsme makro wxBORDERr, což zaručí vykreslení rámečku. Maker můžeme klidně vložit více a zapomocí bitového operátoru OR | je spojit.
Pojdmě si ale postupně vysvětlit funkci jednotlivých sizerů.
wxBoxSizer
Tento sizer umožňuje vložit všechny prvky do řádků nebo sloupců, taky můžeme několik takových sizerů vkládat do sebe.

wxBoxSizer(int orientace);

Takhle je v hlaviččce definovaný jeho konstruktor. Má jediný parametr orientace, který nastavujeme makry wxVERTICAL a wxHORIZONTAL. Jak už určitě víte, tak nastavuje orientaci sizeru, tzn. jak se budou prvky vkládat. Díky této vlastnosti můžeme vytvořit profesionální rozložení. Prvky poté vkádáme pomocí funkce Add

Add(wxWindow* prvek, int proporce, int flag, int rámeček);

Je to metoda objektu wxBoxSizer, proto ji musíme taky tak zavolat. Její první parametr je jakýkoliv widget co chceme vložit. Může to být tlačítko, textové pole, listbox, nebo i celý panel. Dalším parametrem je proporce, který může nabývat tří hodnot – 0, 1, 2. Hodnota 0 znamená, že se objekt nebude nijak měnit. A objekt s hodnotou 2 se bude měnit 2x více než objekt s hodnotou 1. Třetí parametr jsou flagy, tím určíme styl zobrazení objektu, např. jestli se má roztáhnout, nebo jestli bude mít rámeček, pokud ano, tak jeho šířku nastavíme v posledním parametru.
A nakonec musíme ještě sizer přidělit nějakému panelu. To obsatarává členská funkce SetSizer() objektu wxPanel, které jako parametr předáme náš výslední sizer.

Nyní si zkuste představit následující problém: potřebujeme do okna vložit 4 tlačítka, ale tak aby měly všechny stejnou velikost a odstup mezi sebou.
Problém by se dal vyřešit jednoduše, stačilo by vytvořil 4 tlačítka, kterým by jsme nastavily takovou velikost a pozici, aby odpovídala zadání. Jenže takové řešení by mělo všechno výše popsané problémy.
Proto na využijeme sizery a uděláme následující rozložení. Budeme mět hlavní sizer, který bude zabírat velikost celé plochy. Do něho vložíme dva horizontální sizery, a do každého z nich 2 tlačítka. Velikost a odstup vytvoříme pomocí rámečku a proporce.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
wxPanel* hlavniPanel = new wxPanel(this, wxID_ANY);
wxBoxSizer* hlavniBox = new wxBoxSizer(wxVERTICAL);
wxBoxSizer* vrchniBox = new wxBoxSizer(wxHORIZONTAL);
wxBoxSizer* spodniBox = new wxBoxSizer(wxHORIZONTAL);
wxButton* tlacitko1 = new wxButton(hlavniPanel, wxID_ANY, wxt("1"));
wxButton* tlacitko2 = new wxButton(hlavniPanel, wxID_ANY, wxT("2"));
wxButton* tlacitko3 = new wxButton(hlavniPanel, wxID_ANY, wxT("3"));
wxButton* tlacitko4 = new wxButton(hlavniPanel, wxID_ANY, wxT("4"));
vrchniBox->Add(tlacitko1, 1, wxEXPAND | wxRIGHT | wxBOTTOM, 10);
vrchniBox->Add(tlacitko2, 1, wxEXPAND | wxLEFT | wxBOTTOM, 10);
spodniBox->Add(tlacitko3, 1, wxEXPAND |wxRIGHT | wxTOP, 10);
spodniBox->Add(tlacitko4, 1, wxEXPAND | wxLEFT | wxTOP, 10);
hlavniBox->Add(vrchniBox, 1, wxEXPAND);
hlavniBox->Add(spodniBox, 1, wxEXPAND);
hlavniPanel->SetSizer(hlavniBox);

Kód by měl být jasný, na začátku vytvoříme hlavní panel, který je roztáhlý přes celou plochu okno aplikace, poté jsme vytvořili 3 sizery, první je hlavní s vertikální orientací, a zbylé dva mají horizontální orientaci. Všechny vytvořené tlačítka jsme postupně vkládali do sizeru, všechny s propocí 1, následují flagy, nastavující zobrazení. WxEXPAND znamená, že se objekt roztáhne do celého prostoru a protože máme proporci u obou 1, tak výsledek je, že každé má stejnou velikost. Další 2 flagy určují kde bude zobrazený rámeček, k tomu slouží 4 makra:

  • wxLEFT
  • wxRIGHT
  • wxBOTTOM
  • wxTOP

Podle ang. názvů poznáme na jaké straně bude rámeček zobrazen. A posledním parametrem určíme onu šířku rámečku. Nakonec vložíme oba horizontální sizery do hlavního sizeru opět s flagem wxEXPAND a pomocí funkce SetSizer sizer nastavíme.
wxStaticBoxSizer
Sizer wxStaticBoxSizer je odvozený od třídy wxBoxSizer, rozdíl je pouze v tom, že přidává statické pole kolem sizeru.
wxGridSizer
WxGridSizer je dvourozměrný sizer, jeho klíčová vlastnost je že v konstruktoru uvedeme počet řádků a sloupcu a mezeru mezi buňkami. Poté už stačí jenom vkládat objekty a sizer se o všechno další postará.

wxGridSizer(int řádků, int sloupců, int hmezera, int vmezera);

Jak jsem už řekl první dva parametry reprezentují počet řádků a sloupců, další dva horizontální a vertikální mezeru. Pro přidávání prvků platí stejná funkce jako pro wxBoxSizer.
wxFlexGridSizer
Poslední sizer je podobný předchozímu. Taky se zde nastavuje počet řádku, sloupců a mezery. WxFlexGridSizer poskytuje určitou flexibilitu, všechny jeho buňky mají stejnou výšku v řadě, všechny buňky mají stejnou šířku sloupce. Ale všechny řádky nebo sloupce nemusí mít nutně stejnou šířku nebo výšku. Opět se s ním pracuje stejně jako s jiným sizerem.

wxFlexGridSizer(int řádků, int sloupců, int hmezera, int vmezera);

Takže jsme viděli, že sizery jsou velmi užitečná věc a práce s nimi není těžká. Důležité je si pouze vybrat správný sizer pro vaši aplikaci.

DÚ: Zkuste si vytvořit grafické rozhraní nějaké jednoduché kalkulačky. Nápověda. wxGridSizer

V příštím díle se seznámíme s dalšími užitečnými widgety a dialogem.

O Daniel Švec

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