====== 7. QT - úvod ======
Vzorové příklady najdete v repozitáři ''tutorials'' v adresáři tut07
git pull
cd tut07
Alternativně lze najít kódy také v archivu: {{ :courses:b2b99ppc:tutorials:ppc-tut07.zip |}}
--- //**Aktualizace** 2020/04/14 22:44 - opraveny chyby, dodány chybějící soubory//
===== Aplikace s jednou komponentou =====
QApplication app(argc, argv);
QPushButton b1("Button 1");
b1.show();
return app.exec();
===== Vložení více komponent =====
Mohlo by se zdát, že další komponentu lze přidat vytvořením nového objektu.
QPushButton b1("Button 1");
QPushButton b2("Button 2");
b1.show();
b2.show();
Chování výsledného programu je ale jiné: vytvořila se dvě okna (každé s voláním ''.show()'') a okna se vzájemně překrývají.
Řešením je umístit komponenty na jinou vhodnou komponentu, která pak bude zobrazena. Pro tento účel se hodí instance třídy ''QWidget'', která sama o sobě nemá žádnou grafickou podobu, ale může být rodičovským kontejnerem pro jiné objekty. Příslušnost komponenty k rodičovském kontejneru se vytváří vložením ukazatele na rodičovský objekt do konstruktoru komponenty.
QWidget w;
QPushButton b1("Button 1", &w);
QPushButton b2("Button 2", &w);
w.show();
Po spuštění přeloženého programu to na první pohled vypadá, že nedošlo k žádné změně. Okno je sice pouze jedno, komponenty (tlačítka) jsou ale přes sebe.
Komponenty (widgety) lze absolutně pozicovat metodou ''move()'' a měnit jejich velikost metodou ''resize()''.
QWidget w;
w.resize(100,100);
QPushButton b1("Button 1", &w);
QPushButton b2("Button 2", &w);
b2.move(0, 50);
w.show();
=== Úkoly ===
- Doplňte do aplikace další potomky abstraktní třídy [[https://doc.qt.io/qt-5/qabstractbutton.html|QAbstractButton]] - [[https://doc.qt.io/qt-5/qcheckbox.html|QCheckBox]] a [[https://doc.qt.io/qt-5/qradiobutton.html|QRadioButton]]
- Nakreslete šachovnici 8x8 tlačítek
===== Stylování komponent =====
Qt umožňuje pro stylování komponent využívat styly podobné [[https://www.w3schools.com/css/|kaskádovým stylům]] známým z HTML. Komponenty, které budeme používat, podporují [[https://doc.qt.io/qt-5/stylesheet-customizing.html#box-model|box model]].
QPushButton b1("Button 1", &w);
b1.setStyleSheet("background-color: red;"
"border-style: outset;"
"border-width: 2px;"
"border-radius: 10px;"
"border-color: beige;"
"font: bold 14px;"
"min-width: 10em;"
"padding: 6px;");
=== Úkol ===
- Upravte šachovnici z tlačítek tak, aby vypadala jako opravdová šachovnice
===== Layouty =====
Absolutní pozicování widgetů je pochopitelně velmi nešikovné. Umožňuje sice mít absolutní kontrolu nad polohou komponent, ale i malé v podobě aplikace změny mohou znamenat velké změny v programu.
Qt pro pozicování komponent zavádí tzv. [[https://doc.qt.io/qt-5/layout.html|layouty]], kontejnery, ve kterých je pozice vloženého widgetu řízena pravidlem. Následující příklad vykreslí 10 tlačítek uspořádaných horizontálně v komponentě [[https://doc.qt.io/qt-5/qhboxlayout.html|QHBoxLayout]]. Opět je použita instance třídy [[https://doc.qt.io/qt-5/qwidget.html|QWidget]] jako rodičovský kontejner, jednotlivé komponenty jsou ale vkládány do layoutu a ten je pak kontejneru funkcí [[https://doc.qt.io/qt-5/qwidget.html#setLayout|setLayout()]] přiřazen. Na rozdíl od absolutního pozicování je velikost rodičovského widgetu určena automaticky.
QWidget w;
QHBoxLayout l;
for (int i = 0; i < 10; i++)
{
QString name = QString("%1 %2").arg("button").arg(i);
l.addWidget (new QPushButton (name));
}
w.setLayout (&l);
w.show();
===== Správa zdrojů =====
V odstavci o [[courses:b2b99ppc:tutorials:07#stylovani_komponent|stylování komponent]] jsme si ukázali, jak v kódu měnit styl GUI prvků. Pro skutečné programy je vhodné oddělit grafickou podobu aplikace a implementaci algoritmu. To je možné v případě stylování komponent v QT možné provést v ''styleSheet'' souboru, který je před spuštěním aplikace načten. Díky tomuto přístupu je možné hromadně stylovat celé třídy komponent (tj. instance tříd), specifické prvky podle názvu nebo podle atributu.
Aby ''qmake'' neměl problémy s hledánim souborů, které jsou v aplikaci použity (styly, obrázky, ...), používá QT systém [[https://doc.qt.io/qt-5/resources.html|zdrojů]]. Cesty k použitým zdrojům jsou zapsány v ''.qrc'' souboru, který používá syntaxi založenou na ''XML''. Příklad takové souboru:
images/fig.png
images/img.png
styles/app.qss
Soubor specifikuje, že zdroje aplikace jsou ve dvou adresářích, ''images'' a ''styles''. Pokud chceme v aplikaci používat zdroje, je třeba je aktivovat voláním makra
Q_INIT_RESOURCE(style); // cesta ke qrc souboru, bez přípony
K jednotlivým zdrojům lze pak přistupovat pomocí následující syntaxe
// načení stylesheetu ze souboru
QFile styleSheet(":/styles/app.qss");
// aplikace stylesheetu
app.setStyleSheet(styleSheet.readAll());
Základním způsobem, jak stylovat, je vytvoření stylu pro celou třídu
QAbstractButton
{
background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgba(173,216,230,60%), stop:1 rgba(0,0,139,60%));
border-color: black;
border-style: solid;
border-width: 3px;
border-radius: 6px;
height: 50px;
width: 50px;
}
Tento styl zajistí, že všechny instance všech tříd, derivovaných ze třídy [[https://doc.qt.io/qt-5/qabstractbutton.html|QAbstractButton]] budou vypadat stejně bez toho, aby bylo třeba v ''cpp'' kódu něco dalšího psát.
{{ :courses:b2b99ppc:tutorials:tut07_04_stylesheet.png?150 |}}
Pomocí [[https://doc.qt.io/qt-5/stylesheet-reference.html#list-of-pseudo-states|pseudo stavů]] je možné zachytávat aktuální stav objektu a přizpůsobit mu grafickou podobou, zde např. změnit barvu při najetí myší na objekt:
QAbstractButton:hover
{
background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgba(230,216,173,60%), stop:1 rgba(139,0,0,60%));
}
{{ :courses:b2b99ppc:tutorials:tut07_04_stylesheet_2.png?150 |}}
Pokud je třeba nastavit vlastnosti konkrétního prvku, lze využít selektoru jména
QAbstractButton#plus
{
background-color: none;
background-image: url(:/files/add.png);
}
QAbstractButton#minus
{
background-color: none;
background-image: url(:/files/remove.png);
}
V ''cpp'' souboru pak přiřadíme jméno objektu
QPushButton b1;
b1.setObjectName("minus");
{{ :courses:b2b99ppc:tutorials:tut07_04_stylesheet_3.png?150 |}}
Další možností je vytvoření atributu, který bude použit v metodě [[https://doc.qt.io/qt-5/qobject.html#setProperty|setProperty]] objektu. Přiřazení vlastnosti pak probíhá na základě hodnoty atributu.
QAbstractButton[odd=true]
{
background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgba(173,216,230,60%), stop:1 rgba(0,0,139,60%));
}
QAbstractButton[odd=false]
{
background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgba(230,216,173,60%), stop:1 rgba(130,0,0,60%));
}
for (int i = 0; i < 12; i++)
{
QPushButton *b = new QPushButton;
// ...
b->setProperty("odd", (i%2) ? true : false);
}
{{ :courses:b2b99ppc:tutorials:tut07_05_clock.png?300 |}}