Tartalomjegyzék
Elrendezéskezelés a wxWidgetsben
Egy tipikus alkalmazás többféle komponenst tartalmaz. Ezek a komponensek egy tároló komponensen belül vannak. A programozónak kezelni kell az alkalmazás elrendezéseit. Ez nem könnyű feladat. A wxWidgetsben két lehetőség van:
- abszolút pozicionálás
- méretezők (sizers)
Abszolút pozicionálás
A programozó minden komponens esetén meghatározza a pozíciókat és a méreteket pixelben. Amikor abszolút pozicionálást használsz, több dolgot is tudnod kell.
- A komponens mérete és pozíciója nem változik, ha az ablak átméretezésre kerül.
- Az alkalmazás különböző képen néz ki változó platformok esetén
- Az alkalmazásban a fontok megváltozása tönkre teheti az elrendezést.
- Ha megváltoztatod az elrendezést, az egészet kell változtatni kompletten, amely unalmas és időtrabló
Azonban előfordulhat, hogy néha használhatjuk az abszolút pozicionálást. Például ez az oktatóanyag. Nem akarok nagyon bonyolult példát készíteni, így én gyakran használok abszolút pozicionálást a téma bemutatásához. A valós életben való programozás során azonban méretezőt használok. A példánkban egy egyszerű szövegszerkesztő váza van. Ha átméretezzük az ablakot, a wxTextCtrl külső mérete nem változott, amint az várható volt.
Before resizement Figure: before resizement
After resizement Figure: after resizement
- absolute.h
#include <wx/wx.h> class Absolute : public wxFrame { public: Absolute(const wxString& title); wxMenuBar *menubar; wxMenu *file; wxMenu *edit; wxMenu *help; wxTextCtrl *textctrl; };
- absolute.cpp
#include "absolute.h" Absolute::Absolute(const wxString& title) : wxFrame(NULL, -1, title, wxPoint(-1, -1), wxSize(250, 180)) { wxPanel *panel = new wxPanel(this, -1); menubar = new wxMenuBar; file = new wxMenu; edit = new wxMenu; help = new wxMenu; menubar->Append(file, wxT("&File")); menubar->Append(edit, wxT("&Edit")); menubar->Append(help, wxT("&Help")); SetMenuBar(menubar); textctrl = new wxTextCtrl(panel, -1, wxT(""), wxPoint(-1, -1), wxSize(250, 150)); Centre(); }
- main.h
#include <wx/wx.h> class MyApp : public wxApp { public: virtual bool OnInit(); };
- main.cpp
#include "main.h" #include "absolute.h" IMPLEMENT_APP(MyApp) bool MyApp::OnInit() { Absolute *absolute = new Absolute(wxT("Absolute")); absolute->Show(true); return true; }
Ez egy példa, ahol abszolút pozicionálást használunk. Mi megadjuk egy wxTextCtrl komponens pozícióját a panel komponensen.
textctrl = new wxTextCtrl(panel, -1, wxT(""), wxPoint(-1, -1), wxSize(250, 150));
A wxTextCtrl komponens konstruktorában végezzük az abszolút pozicionálást. Esetünkben alapértelmezett pozíciókat adunk meg az komponens számára. A szélesség 250px a magasság 150px;
Méretezők használata
A wxWidgetsben a méretezők mindent megoldanak, mint az említettük az abszolút pozicionálásnál. A következő méretezők közül választhatunk:
- wxBoxSizer
- wxStaticBoxSizer
- wxGridSizer
- wxFlexGridSizer
- wxGridBagSizer
Átméretezés előtt ide majd kép jön
Átméretezés után ide majd kép jön
- sizer.h
#include <wx/wx.h> class Sizer : public wxFrame { public: Sizer(const wxString& title); wxMenuBar *menubar; wxMenu *file; wxMenu *edit; wxMenu *help; wxTextCtrl *textctrl; };
- sizer.cpp
#include "sizer.h" Sizer::Sizer(const wxString& title) : wxFrame(NULL, -1, title, wxPoint(-1, -1), wxSize(250, 180)) { menubar = new wxMenuBar; file = new wxMenu; edit = new wxMenu; help = new wxMenu; menubar->Append(file, wxT("&File")); menubar->Append(edit, wxT("&Edit")); menubar->Append(help, wxT("&Help")); SetMenuBar(menubar); textctrl = new wxTextCtrl(this, -1, wxT(""), wxPoint(-1, -1), wxSize(250, 150)); Centre(); }
- main.h
#include <wx/wx.h> class MyApp : public wxApp { public: virtual bool OnInit(); };
- main.cpp
#include "main.h" #include "sizer.h" IMPLEMENT_APP(MyApp) bool MyApp::OnInit() { Sizer *sizer = new Sizer(wxT("Sizer")); sizer->Show(true); return true; }
Ok, most azt mondod nem látsz egyetlen méretezőt sem a példában? Nos, a kód egy kicsit bonyolult. Valójában elhelyeztünk a wxTextCtrl wxFrame komponensen belül. A wxFrame komponensnek van egy speciális beépített méretezője. Csak egy komponenst tehetünk wxFrame tárolójába. A gyermek komponens elfoglalja az egész helyet, a szélekhez, a menükhöz, az eszköztárhoz és a státuszsorhoz.
wxBoxSizer
Ez a méretező megengedi több komponens elhelyezését egy sorban vagy egy oszlopban. Egy méretezőben elhelyezhetünk egy másik méretezőt. Ezzel a módszerrel egész komplex rétegeket hozhatunk létre.
wxBoxSizer(int orient) wxSizerItem* Add(wxWindow* window, int proportion = 0, int flag = 0, int border = 0)
Az orientáció (orient) lehet wxVERTICAL vagy wxHORIZONTAL. Egy komponenst helyezünk el a wxBoxSizerban az Add() metódus segítségével. Most meg kell néznünk a paramétereket.
A proportion (arány) paraméter meghatározza hogyan változnak az arányok a komponensek cseréje esetén. Vegyünk fel három gombot, amelynek arány paramétere (proportion) 0, 1 és 2. Adjuk azokat egy vízszintes wxBoxSizerhez. Ha a gomb arány 0, méretváltozás esetén a gomb mérete változatlan. 1-es érték esetén a gomb kitölti a maradék helyet. A gomb arány 2 esetén a mérete dupla akkora lesz mint 1-es érték esetén.
A jelző paraméterrel beállítható egy wxBoxSizeren belüli viselkedés. Vezérelheted a szegélyt a komponensek között. Egy kis helyet adhatunk meg a komponensek között pixelben. A szegélyhez oldalakat határozhatunk meg, amely megmondja hol alkalmazzuk azokat. A | operátorral kombinálhatjuk a beállításokat, pl.: wxLEFT | wxBOTTOM. A következő jelzők választhatók:
- wxLEFT
- wxRIGHT
- wxBOTTOM
- wxTOP
- wxALL
border around a panel Figure: border around a panel
- border.h
#include <wx/wx.h> class Border : public wxFrame { public: Border(const wxString& title); };
- border.cpp
#include "border.h" Border::Border(const wxString& title) : wxFrame(NULL, wxID_ANY, title, wxDefaultPosition, wxSize(250, 200)) { wxColour col1, col2; col1.Set(wxT("#4f5049")); col2.Set(wxT("#ededed")); wxPanel *panel = new wxPanel(this, -1); panel->SetBackgroundColour(col1); wxBoxSizer *vbox = new wxBoxSizer(wxVERTICAL); wxPanel *midPan = new wxPanel(panel, wxID_ANY); midPan->SetBackgroundColour(col2); vbox->Add(midPan, 1, wxEXPAND | wxALL, 20); panel->SetSizer(vbox); Centre(); }
- main.h
#include <wx/wx.h> class MyApp : public wxApp { public: virtual bool OnInit(); };
- main.cpp
#include "main.h" #include "border.h" IMPLEMENT_APP(MyApp) bool MyApp::OnInit() { Border *border = new Border(wxT("Border")); border->Show(true); return true; }
Ebben a példában két panelt készítünk. A második panel körül kicsit több hely van.
vbox->Add(midPan, 1, wxEXPAND | wxALL, 20);
Nekünk 20 px vastag szegélyünk van a midPan panel körül. A wxALL jelző mind a négy szegélyre alkalmazza a beállítást. Ha a wxEXPAND jelzőt használjuk, a komponens összes helyet elfoglalja majd. Végül beállíthatjuk a komponens igazítását. A következő jelzők használhatók:
- wxALIGN_LEFT
- wxALIGN_RIGHT
- wxALIGN_TOP
- wxALIGN_BOTTOM
- wxALIGN_CENTER_VERTICAL
- wxALIGN_CENTER_HORIZONTAL
- wxALIGN_CENTER
Két gombot szeretnék elhelyezni az ablak jobb alsó sarkában.
- align.h
#include <wx/wx.h> class Align : public wxFrame { public: Align(const wxString& title); };
- align.cpp
#include "align.h" Align::Align(const wxString& title) : wxFrame(NULL, -1, title, wxPoint(-1, -1), wxSize(300, 200)) { wxPanel *panel = new wxPanel(this, -1); wxBoxSizer *vbox = new wxBoxSizer(wxVERTICAL); wxBoxSizer *hbox1 = new wxBoxSizer(wxHORIZONTAL); wxBoxSizer *hbox2 = new wxBoxSizer(wxHORIZONTAL); wxButton *ok = new wxButton(panel, -1, wxT("Ok")); wxButton *cancel = new wxButton(panel, -1, wxT("Cancel")); hbox1->Add(new wxPanel(panel, -1)); vbox->Add(hbox1, 1, wxEXPAND); hbox2->Add(ok); hbox2->Add(cancel); vbox->Add(hbox2, 0, wxALIGN_RIGHT | wxRIGHT | wxBOTTOM, 10); panel->SetSizer(vbox); Centre(); }
- main.h
#include <wx/wx.h> class MyApp : public wxApp { public: virtual bool OnInit(); };
- main.cpp
#include "main.h" #include "align.h" IMPLEMENT_APP(MyApp) bool MyApp::OnInit() { Align *align = new Align(wxT("Align")); align->Show(true); return true; }
Három méretezőt készítünk. Egy függőleges és két vízszintes méretezőt használunk. A két vízszintes méretezőt az egy függőlegesben helyezzük el.
hbox1->Add(new wxPanel(panel, -1)); vbox->Add(hbox1, 1, wxEXPAND);
Adjunk egy wxPanelt az első vízszintes méretezőhöz. Az arányt (proportion) 1-re állítjuk, és hozzáadunk egy wxEXPAND jelzőt. Így a méretező kitölti az összes helyet kivéve a hbox2-őt.
vbox->Add(hbox2, 0, wxALIGN_RIGHT | wxRIGHT | wxBOTTOM, 10);
A gombok helye a hbox2 méretezőben van. A hbox2 jobbra igazított és egy kevés helyet hagyunk neki.
aligning buttons Figure: aligning buttons
Használjunk osztályt
A következő példában bemutatunk több fontos fogalmat
- gotoclass.h
#include <wx/wx.h> class GotoClass : public wxFrame { public: GotoClass(const wxString& title); };
- gotoclass.cpp
#include "gotoclass.h" GotoClass::GotoClass(const wxString& title) : wxFrame(NULL, -1, title, wxPoint(-1, -1), wxSize(450, 400)) { wxPanel *panel = new wxPanel(this, -1); wxBoxSizer *vbox = new wxBoxSizer(wxVERTICAL); wxBoxSizer *hbox1 = new wxBoxSizer(wxHORIZONTAL); wxStaticText *st1 = new wxStaticText(panel, wxID_ANY, wxT("Class Name")); hbox1->Add(st1, 0, wxRIGHT, 8); wxTextCtrl *tc = new wxTextCtrl(panel, wxID_ANY); hbox1->Add(tc, 1); vbox->Add(hbox1, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 10); vbox->Add(-1, 10); wxBoxSizer *hbox2 = new wxBoxSizer(wxHORIZONTAL); wxStaticText *st2 = new wxStaticText(panel, wxID_ANY, wxT("Matching Classes")); hbox2->Add(st2, 0); vbox->Add(hbox2, 0, wxLEFT | wxTOP, 10); vbox->Add(-1, 10); wxBoxSizer *hbox3 = new wxBoxSizer(wxHORIZONTAL); wxTextCtrl *tc2 = new wxTextCtrl(panel, wxID_ANY, wxT(""), wxPoint(-1, -1), wxSize(-1, -1), wxTE_MULTILINE); hbox3->Add(tc2, 1, wxEXPAND); vbox->Add(hbox3, 1, wxLEFT | wxRIGHT | wxEXPAND, 10); vbox->Add(-1, 25); wxBoxSizer *hbox4 = new wxBoxSizer(wxHORIZONTAL); wxCheckBox *cb1 = new wxCheckBox(panel, wxID_ANY, wxT("Case Sensitive")); hbox4->Add(cb1); wxCheckBox *cb2 = new wxCheckBox(panel, wxID_ANY, wxT("Nested Classes")); hbox4->Add(cb2, 0, wxLEFT, 10); wxCheckBox *cb3 = new wxCheckBox(panel, wxID_ANY, wxT("Non-Project Classes")); hbox4->Add(cb3, 0, wxLEFT, 10); vbox->Add(hbox4, 0, wxLEFT, 10); vbox->Add(-1, 25); wxBoxSizer *hbox5 = new wxBoxSizer(wxHORIZONTAL); wxButton *btn1 = new wxButton(panel, wxID_ANY, wxT("Ok")); hbox5->Add(btn1, 0); wxButton *btn2 = new wxButton(panel, wxID_ANY, wxT("Close")); hbox5->Add(btn2, 0, wxLEFT | wxBOTTOM , 5); vbox->Add(hbox5, 0, wxALIGN_RIGHT | wxRIGHT, 10); panel->SetSizer(vbox); Centre(); }
- main.h
#include <wx/wx.h> class MyApp : public wxApp { public: virtual bool OnInit(); };
- main.cpp
#include "main.h" #include "gotoclass.h" IMPLEMENT_APP(MyApp) bool MyApp::OnInit() { GotoClass *gotoclass = new GotoClass(wxT("GotoClass")); gotoclass->Show(true); return true; }
Itt a teljes példában használjuk a wxBoxSizer komponenst. Itt a réteg a lényeg. Készítünk egy függőleges méretezőt. Beleteszünk öt vízszintes méretezőt.
vbox->Add(hbox3, 1, wxLEFT | wxRIGHT | wxEXPAND, 10);
vbox->Add(-1, 25);
Már tudjuk, hogy a komponensek közötti távolságot beállíthatjuk a szegéllye paraméterrel. De ezt kényszerítve oldjuk meg. Az Add() metódusban megadhatjuk csak egy szegélyt az összes oldalra. A példánkban a jobb oldalon 10 képpontot kapunk a balhoz képest. De alul nem tudunk 25 képpontot kapni. Akkor 10 vagy 0 képpontot készítünk. Ha kihagyjuk a wxBOTTOM-ot. Így ha különböző értékekre van szükségünk hozzáadhatunk extra helyeket. Az Add() metódussal, beszúrhatunk komponenseket és helyet is.
vbox->Add(hbox5, 0, wxALIGN_RIGHT | wxRIGHT, 10);
A helyünkön van két gomb az ablak jobb oldalán. Hogyan csináltuk? Három dolgot lényeges ehhez. Az arány az igazítás (align) jelző és a wxEXPAND jelző. Az aránynak nullának kell lennie. A gomboknak így nem változhat a mérete, amikor átméretezzük az ablakot. Meg kell adnunk a wxEXPAND jelzőt. A gombok csak a kijelölt helyet foglalják el. Végül megadjuk a wxALIGN_RIGHT jelzőt. Vízszintesen a méretező az ablak baloldalától jobbra terjed ki. Így, ha mi megadjuk wxALIGN_RIGHT jelzőt, a gombok helye jobboldalon lesz. Ez pontosan az amit szerettünk volna.
GotoClass Kép jön ide.
wxGridSizer
wxGridSizer lays out widgets in two dimensional table. Each cell within the table has the same size. wxGridSizer(int rows, int cols, int vgap, int hgap)
A konstruktorban magadhatjuk sorok és az oszlopok számát a táblázatban, a vízszintes és a függőleges cellák közötti hellyel együtt. A példánkban készítünk kalkulátor vázat. Az egy kész példa a wxGridSizer használatára.
gridsizer.h
#include <wx/wx.h> class GridSizer : public wxFrame { public: GridSizer(const wxString& title); wxMenuBar *menubar; wxMenu *file; wxBoxSizer *sizer; wxGridSizer *gs; wxTextCtrl *display; };
gridsizer.cpp
#include "gridsizer.h" GridSizer::GridSizer(const wxString& title) : wxFrame(NULL, -1, title, wxPoint(-1, -1), wxSize(270, 220)) { menubar = new wxMenuBar; file = new wxMenu; SetMenuBar(menubar); sizer = new wxBoxSizer(wxVERTICAL); display = new wxTextCtrl(this, -1, wxT(""), wxPoint(-1, -1), wxSize(-1, -1), wxTE_RIGHT); sizer->Add(display, 0, wxEXPAND | wxTOP | wxBOTTOM, 4); gs = new wxGridSizer(4, 4, 3, 3); gs->Add(new wxButton(this, -1, wxT("Cls")), 0, wxEXPAND); gs->Add(new wxButton(this, -1, wxT("Bck")), 0, wxEXPAND); gs->Add(new wxStaticText(this, -1, wxT("")), 0, wxEXPAND); gs->Add(new wxButton(this, -1, wxT("Close")), 0, wxEXPAND); gs->Add(new wxButton(this, -1, wxT("7")), 0, wxEXPAND); gs->Add(new wxButton(this, -1, wxT("8")), 0, wxEXPAND); gs->Add(new wxButton(this, -1, wxT("9")), 0, wxEXPAND); gs->Add(new wxButton(this, -1, wxT("/")), 0, wxEXPAND); gs->Add(new wxButton(this, -1, wxT("4")), 0, wxEXPAND); gs->Add(new wxButton(this, -1, wxT("5")), 0, wxEXPAND); gs->Add(new wxButton(this, -1, wxT("6")), 0, wxEXPAND); gs->Add(new wxButton(this, -1, wxT("*")), 0, wxEXPAND); gs->Add(new wxButton(this, -1, wxT("1")), 0, wxEXPAND); gs->Add(new wxButton(this, -1, wxT("2")), 0, wxEXPAND); gs->Add(new wxButton(this, -1, wxT("3")), 0, wxEXPAND); gs->Add(new wxButton(this, -1, wxT("-")), 0, wxEXPAND); gs->Add(new wxButton(this, -1, wxT("0")), 0, wxEXPAND); gs->Add(new wxButton(this, -1, wxT(".")), 0, wxEXPAND); gs->Add(new wxButton(this, -1, wxT("=")), 0, wxEXPAND); gs->Add(new wxButton(this, -1, wxT("+")), 0, wxEXPAND); sizer->Add(gs, 1, wxEXPAND); SetSizer(sizer); SetMinSize(wxSize(270, 220)); Centre(); }
main.h
#include <wx/wx.h> class MyApp : public wxApp { public: virtual bool OnInit(); };
main.cpp
#include "main.h" #include "gridsizer.h" IMPLEMENT_APP(MyApp) bool MyApp::OnInit() { GridSizer *gs = new GridSizer(wxT("GridSizer")); gs->Show(true); return true; }
A példánkban, beállítottunk egy függőleges méretezőt a wxFrame számára. Megadtunk egy statikus szöveget és egy grid méretezőt a függőleges méretezőben. Vegyük észre, hogy kezeljük a Bck és a Close gombok közötti helyet. Egy egyszerű statikus szöveget teszünk oda.
A trükk egészen általános.
gs->Add(new wxButton(this, -1, wxT("Cls")), 0, wxEXPAND);
Többször meghívjuk az Add() metódust. A komponensek helye a táblán belül a hozzáadás sorrendje. Az első sor töltődik először fel, majd a második, stb.
Ide jön egy kép: GridSizer
wxFlexGridSizer
Ez a méretező hasonlít a wxGridSizerhez. Az szintén rendezi a widgeteket kétdimenziós táblában, viszont ad némi rugalmasságot. A wxGridSizer cellái egyező méretűek. Minden oszlopban azonos a cellák szélessége. Minden sorban azonos a cellák magassága. A wxFlexGridSizer viszont megengedi az eltérő szélességet és magasságot a sorokban és oszlopokban?
wxFlexGridSizer(int rows, int cols, int vgap, int hgap)
A sorok és az oszlopok számát meg kell adnunk a sor és oszlopszámmal. A vgap és a hgap adott mennyiségű helyet biztosít a widgetek között minden irányban.
Valamikor a fejlesztőknek kellett elhelyezni a widgeteket a programablakban. Ma már azonban a wxFlexGridSizer rugalmasan teszi lehetőv, szabályos de szép elrendezés kialakítását.
flexgridsizer.h
#include <wx/wx.h> class FlexGridSizer : public wxFrame { public: FlexGridSizer(const wxString& title); };
flexgridsizer.cpp
#include "flexgridsizer.h" FlexGridSizer::FlexGridSizer(const wxString& title) : wxFrame(NULL, -1, title, wxPoint(-1, -1), wxSize(270, 220)) { wxPanel *panel = new wxPanel(this, -1); wxBoxSizer *hbox = new wxBoxSizer(wxHORIZONTAL); wxFlexGridSizer *fgs = new wxFlexGridSizer(3, 2, 9, 25); wxStaticText *thetitle = new wxStaticText(panel, -1, wxT("Title")); wxStaticText *author = new wxStaticText(panel, -1, wxT("Author")); wxStaticText *review = new wxStaticText(panel, -1, wxT("Review")); wxTextCtrl *tc1 = new wxTextCtrl(panel, -1); wxTextCtrl *tc2 = new wxTextCtrl(panel, -1); wxTextCtrl *tc3 = new wxTextCtrl(panel, -1, wxT(""), wxPoint(-1, -1), wxSize(-1, -1), wxTE_MULTILINE); fgs->Add(thetitle); fgs->Add(tc1, 1, wxEXPAND); fgs->Add(author); fgs->Add(tc2, 1, wxEXPAND); fgs->Add(review, 1, wxEXPAND); fgs->Add(tc3, 1, wxEXPAND); fgs->AddGrowableRow(2, 1); fgs->AddGrowableCol(1, 1); hbox->Add(fgs, 1, wxALL | wxEXPAND, 15); panel->SetSizer(hbox); Centre(); }
main.h
#include <wx/wx.h> class MyApp : public wxApp { public: virtual bool OnInit(); };
main.cpp
#include "main.h" #include "flexgridsizer.h" IMPLEMENT_APP(MyApp) bool MyApp::OnInit() { FlexGridSizer *fgs = new FlexGridSizer(wxT("FlexGridSizer")); fgs->Show(true); return true; }
A példában egy párbeszédablakot készítünk, ahol egy adatbázis adatait adhatjuk meg.
wxBoxSizer *hbox = new wxBoxSizer(wxHORIZONTAL); ... hbox->Add(fgs, 1, wxALL | wxEXPAND, 15);
Készítünk egy vízszintes méretezőboxot sorba, 15px helyet hagyva a widgetek körül.
fgs->Add(thetitle);
Több komponenst is a méretezőhöz adunk, mint a gridsizer-nél.
fgs->AddGrowableRow(2, 1); fgs->AddGrowableCol(1, 1);
Három sort készítünk és második oszlop többszörözhető. Így mi növekvő szöveges kontrollokat készítünk, az ablak átméretezésénél. Elsőnek, két szöveges kontrollt fogunk növelni vízszintes irányban, a harmadik, mindkét irányba. Nem felejtsük el a komponenseket beállítani tágíthatónak (wxEXPAND), sorrendben, ahogy következik.
Ide kép jön: FlexGridSizer