Obsah
1. Jazyk QML (Qt Modeling Language) a PySide 2
2. Porovnání rozdílů mezi PySide a PySide2 při práci s QML
3. Vizuální porovnání rozdílů mezi skripty psanými v Pythonu
4. Přechod od QtQuick 1.0 ke QtQuick 2.0
5. Vizuální porovnání rozdílů mezi oběma QML soubory
6. Univerzální modul určený pro načtení a zobrazení QML aplikace
7. Příklad použití modulu popsaného v předchozí kapitole
8. Kotevní přímky a jejich vliv na umístění a rozměry prvků
9. Použití rastrových obrázků v GUI
10. Načtení a zobrazení vektorových výkresů v GUI
11. Korektní rozmístění vektorových výkresů na plochu okna
12. Relativní velikost výkresu: atribut fillMode
13. Další způsoby rozmístění prvků GUI na ploše okna
17. Tvorba animací v Pyside – použití automaticky modifikované proměnné řídicí animaci
18. Další ukázky animace – plynulá změna velikosti vektorových kreseb
19. Repositář s demonstračními příklady
1. Jazyk QML (Qt Modeling Language) a PySide 2
V předchozím článku jsme se seznámili se základními vlastnostmi jazyka QML určeného pro tvorbu grafického uživatelského rozhraní, nad nímž je postavena technologie Qt Quick. Ovšem prozatím jsme se zabývali pouze tou variantou jazyka QML, která je součástí Qt Quick 1.0, tj. starší verze použité v Qt 4.8.7. To je sice stále používaná verze Qt (je na ní postaveno relativně mnoho podnikových aplikací), ovšem oficiálně již není více než dva roky podporovaná, takže je poněkud problematické nad ní stavět novější aplikace. Novější QtQuick 2.x totiž už vyžaduje Qt 5 (a to konkrétně verze 5.9, 5.10 či 5.11) a tím pádem i PySide 2. Mezi oběma variantami existuje několik rozdílů a z tohoto důvodu si dnes ukážeme několik příkladů postavených nad PySide 2 a Qt Quick 2.0.
Obrázek 1: Příkladem aplikace postavené na PyQt je integrované vývojové prostředí Eric.
2. Porovnání rozdílů mezi PySide a PySide2 při práci s QML
Nejprve se v krátkosti podívejme na to, jaké základní rozdíly nalezneme mezi PySide a PySide2 u kódu (skriptu), který vlastně provádí pouze jedinou činnost – vytvoření hlavního okna aplikace, načtení QML s deklarací GUI a následného vykreslení grafického uživatelského rozhraní do tohoto okna na základě obsahu získaného z QML. Při použití PySide 1 celý kód tohoto skriptu vypadal následovně:
#!/usr/bin/env python # vim: set fileencoding=utf-8 import sys # import "jádra" frameworku Qt i modulu pro GUI from PySide import QtCore from PySide import QtGui # modul pro práci s QML from PySide import QtDeclarative # jméno QML s deklarací grafického uživatelského rozhraní QML_FILE = "173_load_qml_9.qml" # nový widget bude odvozen od QDeclarativeView class MainWindow(QtDeclarative.QDeclarativeView): def __init__(self, parent=None): super(MainWindow, self).__init__(parent) # nastavení titulku hlavního okna aplikace self.setWindowTitle("QML Example") # načtení souboru QML self.setSource(QtCore.QUrl.fromLocalFile(QML_FILE)) # necháme QML změnit velikost okna self.setResizeMode(QtDeclarative.QDeclarativeView.SizeRootObjectToView) def main(): # vytvoření Qt aplikace app = QtGui.QApplication(sys.argv) # vytvoření hlavního okna window = MainWindow() # zobrazení hlavního okna window.show() # spuštění aplikace sys.exit(app.exec_()) if __name__ == '__main__': main()
Z předchozího výpisu vidíme, že třída hlavního okna je odvozena od třídy QtDeclarative.QDeclarativeView. V případě, že použijeme PySide2, je ovšem situace odlišná, neboť hlavní okno se v tomto případě odvodí od odlišné třídy pojmenované QtQuick.QQuickView. Také se bude odlišovat způsob nastavení titulku hlavního okna, neboť se namísto metody setWindowTitle() použije setTitle() (odlišné jsou i další metody, s nimi se však prozatím nesetkáme):
#!/usr/bin/env python # vim: set fileencoding=utf-8 import sys # import "jádra" frameworku Qt i modulu pro GUI from PySide2 import QtCore from PySide2 import QtGui # modul pro práci s QML from PySide2 import QtQuick # jméno QML s deklarací grafického uživatelského rozhraní QML_FILE = "01.qml" # nový widget bude odvozen od QDeclarativeView class MainWindow(QtQuick.QQuickView): def __init__(self, parent=None): super(MainWindow, self).__init__(parent) # nastavení titulku hlavního okna aplikace self.setTitle("QML Example @ PySide2") # načtení souboru QML self.setSource(QtCore.QUrl.fromLocalFile(QML_FILE)) # necháme QML změnit velikost okna self.setResizeMode(QtQuick.QQuickView.SizeRootObjectToView) def main(): # vytvoření Qt aplikace app = QtGui.QGuiApplication(sys.argv) # vytvoření hlavního okna window = MainWindow() # zobrazení hlavního okna window.show() # spuštění aplikace sys.exit(app.exec_()) if __name__ == '__main__': main()
3. Vizuální porovnání rozdílů mezi skripty psanými v Pythonu
Pro lepší přehled rozdílů mezi PySide 1 a PySide 2 je na screenshotu zobrazeném pod tímto odstavcem ukázáno, jak se oba skripty uvedené v předchozí kapitole vizuálně odlišují. I přes snahu o to, aby si oba zdrojové kódy byly co nejpodobnější, vidíme, že se odlišují použité třídy a tím pádem i jejich metody, které jsou volány:
Obrázek 2: Vizuální porovnání rozdílu mezi skriptem napsaným pro PySide 1 a skriptem pro PySide 2.
Následují odkazy na dokumentaci k jednotlivým třídám (a prozatím jedné metodě), které se v PySide 1 a PySide 2 odlišují:
Třída/metoda | PySide 1 | PySide 2 |
---|---|---|
hlavní aplikace | QtGui.QApplication | QtGui.QGuiApplication |
předek okna | QtDeclarative.QDeclarativeView | QtQuick.QQuickView |
titulek okna | setWindowTitle | setTitle |
4. Přechod od QtQuick 1.0 ke QtQuick 2.0
O změnách, které musely být provedeny ve skriptech naprogramovaných v Pythonu, jsme se již zmínili, takže si nyní ukažme, jaké další změny je nutné udělat v samotných QML souborech s popisem grafického uživatelského rozhraní. Prozatím zůstaneme u velmi jednoduchého (až primitivního) příkladu, v němž jsou na plochu okna umístěny tři různobarevné čtverce, u kterých je pro větší efekt nastavena průhlednost a jeden čtverec je otočen:
Obrázek 3: Okno s jednoduchým GUI – na ploše okna jsou zobrazeny tři čtverce s různými vizuálními a geometrickými vlastnostmi.
První varianta QML je určena pro QtQuick 1.0 a tudíž pro PySide 1:
import QtQuick 1.0 Rectangle { id: main width: 320 height: 240 color: "lightgray" Rectangle { id: r1 width: 160 height: 160 color: "red" opacity: 0.5 rotation: 45 anchors.left: parent.left anchors.bottom: parent.bottom } Rectangle { id: r2 width: 160 height: 160 color: "yellow" opacity: 0.5 z: 1 anchors.horizontalCenter: parent.horizontalCenter anchors.top: parent.top } Rectangle { id: r3 width: 160 height: 160 color: "blue" opacity: 0.5 rotation: 45 anchors.right: parent.right anchors.bottom: parent.bottom } }
Druhá varianta je již určena pro QtQuick 2.0 a tudíž pro PySide 2:
import QtQuick 2.0 Rectangle { id: main width: 320 height: 240 color: "lightgray" Rectangle { id: r1 width: 160 height: 160 color: "red" opacity: 0.5 rotation: 45 anchors.left: parent.left anchors.bottom: parent.bottom } Rectangle { id: r2 width: 160 height: 160 color: "yellow" opacity: 0.5 z: 1 anchors.horizontalCenter: parent.horizontalCenter anchors.top: parent.top } Rectangle { id: r3 width: 160 height: 160 color: "blue" opacity: 0.5 rotation: 45 anchors.right: parent.right anchors.bottom: parent.bottom } }
5. Vizuální porovnání rozdílů mezi oběma QML soubory
Zatímco mezi skripty napsanými pro knihovnu PySide 1 a PySide 2 bylo relativně velké množství rozdílů (nebyla – a to naschvál – dodržena zpětná kompatibilita), je tomu u QML souborů jinak, alespoň v našem jednoduchém příkladu grafického uživatelského rozhraní s trojicí čtverců. Ostatně se můžeme podívat na následující screenshot zobrazující všechny rozdíly. Ty nastaly na jediném řádku, ovšem je zapotřebí poznamenat, že se skutečně jedná o primitivní příklad a v praxi (reálná GUI) tomu bude jinak:
Obrázek 4: Vizuální porovnání rozdílu mezi QML napsaným pro QtQuick 1.0 a QML pro QtQuick 2.0.
6. Univerzální modul určený pro načtení a zobrazení QML aplikace
Ve všech demonstračních příkladech popsaných v navazujících kapitolách budeme používat „univerzální“ modul, který je určený pro vytvoření hlavního okna aplikace, načtení zvoleného QML souboru a inicializaci grafického uživatelského rozhraní. Modul neobsahuje přímo spustitelný kód (__main__), takže ho budeme používat jako knihovnu a tím pádem se nám zdrojové kódy příkladů zminimalizují na doslova několik řádků. Ve skutečnosti sice není dále uvedený skript zcela univerzální, protože nedokáže zpracovat QML soubory s deklarací hlavního okna, ovšem to nám pro účely dnešního článku nebude vadit (případná vylepšení budou popsána příště). Úplný zdrojový kód výše popsaného modulu naleznete na adrese https://github.com/tisnik/presentations/blob/master/Python_GUI/PySide2/QmlViewer.py:
# vim: set fileencoding=utf-8 # univerzální prohlížeč QML souborů import sys # import "jádra" frameworku Qt i modulu pro GUI from PySide2 import QtCore from PySide2 import QtGui # modul pro práci s QML from PySide2 import QtQuick # nový widget bude odvozen od QDeclarativeView class MainWindow(QtQuick.QQuickView): def __init__(self, qml_file, parent=None): super(MainWindow, self).__init__(parent) # nastavení titulku hlavního okna aplikace self.setTitle("QML Example @ PySide2: " + qml_file) # načtení souboru QML self.setSource(QtCore.QUrl.fromLocalFile(qml_file)) # necháme QML změnit velikost okna self.setResizeMode(QtQuick.QQuickView.SizeRootObjectToView) def main(qml_file): # vytvoření Qt aplikace app = QtGui.QGuiApplication(sys.argv) # vytvoření hlavního okna window = MainWindow(qml_file) # zobrazení hlavního okna na desktopu window.show() # spuštění aplikace sys.exit(app.exec_())
7. Příklad použití modulu popsaného v předchozí kapitole
Podívejme se nyní, jak se výše popsaný modul QmlViewer použije. Další demonstrační příklad je skutečně velmi krátký, protože obsahuje pouze import modulu QmlViewer, definici QML souboru s popisem grafického uživatelského rozhraní a zavolání funkce main deklarované v modulu (pro rozsáhlejší aplikace je vhodnější provést import QmlViewer, aby nedocházelo k „zašpinění“ jmenného prostoru:
#!/usr/bin/env python # vim: set fileencoding=utf-8 from QmlViewer import * QML_FILE = "02_use_qml_viewer.qml" if __name__ == '__main__': main(QML_FILE)
Obrázek 5: Výsledek v této kapitole popsaného demonstračního příkladu.
8. Kotevní přímky a jejich vliv na umístění a rozměry prvků
Již v předchozí části tohoto seriálu jsme se zmínili o tom, že jednou z možností rozmístění ovládacích prvků grafického uživatelského rozhraní do plochy oken a dialogů představuje použití takzvaných kotevních přímek, což jsou virtuální prvky procházející jak všemi čtyřmi krajními body prvků (vlevo, vpravo, nahoře i dole), tak i jejich středy. Změnou umístění těchto přímek se mění i velikost (plocha) prvků. V dalším příkladu je ukázán způsob rozmístění tří obdélníků v ploše okna.
První obdélník (červený) je „přilepen“ k hornímu, spodnímu a současně i k levému okraji okna. Jeho šířka je nastavena na 64 délkových jednotek (zde pixelů) a vzhledem k tomu, že pravý okraj obdélníku není ničím omezen, bude tato šířka zachována i při změně velikosti okna:
Rectangle { id: r1 width: 64 color: "red" opacity: 0.5 anchors.left: parent.left anchors.top: parent.top anchors.bottom: parent.bottom }
Třetí obdélník (modrý) má taktéž šířku 64 délkových jednotek a je „přilepen“ k hornímu, spodnímu a pravému okraji okna. Jeho šířka zůstane opět zachována i ve chvíli, kdy se velikost okna z různých důvodů změní:
Rectangle { id: r3 width: 64 height: 160 color: "blue" opacity: 0.5 anchors.right: parent.right anchors.top: parent.top anchors.bottom: parent.bottom }
A konečně prostřední (žlutý) obdélník má nastavenou šířku i výšku na 160 délkových jednotek a je vycentrován:
Rectangle { id: r2 width: 160 height: 160 color: "yellow" opacity: 0.5 z: 1 anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter }
Podívejme se nyní na chování všech tří prvků při změně velikosti okna:
Obrázek 6: Rozmístění prvků – původní velikost okna.
Obrázek 7: Rozmístění prvků v širším okně.
U užšího okna dojde k překryvu prvků a to z toho důvodu, že nikde nespecifikujeme jejich vzájemné postavení:
Obrázek 8: Rozmístění prvků v užším okně.
Extrémní případ, kdy se překrývají všechny tři prvky:
Obrázek 9: Rozmístění prvků v ještě užším okně.
Úplný zdrojový kód tohoto příkladu vypadá následovně:
import QtQuick 2.0 Rectangle { id: main width: 320 height: 240 color: "lightgray" Rectangle { id: r1 width: 64 color: "red" opacity: 0.5 anchors.left: parent.left anchors.top: parent.top anchors.bottom: parent.bottom } Rectangle { id: r2 width: 160 height: 160 color: "yellow" opacity: 0.5 z: 1 anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter } Rectangle { id: r3 width: 64 height: 160 color: "blue" opacity: 0.5 anchors.right: parent.right anchors.top: parent.top anchors.bottom: parent.bottom } }
Zdrojový kód demonstračního příkladu využívá modul QmlViewer:
#!/usr/bin/env python # vim: set fileencoding=utf-8 from QmlViewer import * QML_FILE = "03_springy_widgets.qml" if __name__ == '__main__': main(QML_FILE)
9. Použití rastrových obrázků v GUI
Při tvorbě grafického uživatelského rozhraní reálných aplikací se většinou neobejdeme bez nutnosti použití rastrových obrázků, které například mohou sloužit jako ikony. Pro tento účel slouží prvek nazvaný jednoduše Image. Důležitým atributem tohoto prvku je atribut nazvaný source se jménem souboru s uloženým rastrovým obrázkem (tento atribut je typu string a proto by měl být zapsán v uvozovkách). Podporovány jsou samozřejmě i obrázky uložené ve formátu PNG:
Image { id: image source: "images/voronoi.png" anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter }
Obrázek 10: Umístění rastrového obrázku do plochy okna.
Podívejme se nyní na zdrojový kód demonstračního příkladu, v němž je použit rastrový obrázek bez uvedení velikosti a kromě tohoto obrázku dva obdélníky, které tvoří jeho pravý a levý okraj. Vzhledem k tomu, že velikost rastrového obrázku není explicitně zadána, zjistí se z jeho aktuálních rozměrů (metadat), popř. pouze z rozlišení:
import QtQuick 2.0 Rectangle { id: main width: 320 height: 240 color: "lightgray" Rectangle { id: left_rectangle color: "red" opacity: 0.5 anchors.left: parent.left anchors.right: image.left anchors.top: parent.top anchors.bottom: parent.bottom } Image { id: image source: "images/voronoi.png" anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter } Rectangle { id: right_rectangle color: "blue" opacity: 0.5 anchors.left: image.right anchors.right: parent.right anchors.top: parent.top anchors.bottom: parent.bottom } }
Zdrojový kód demonstračního příkladu opět využívá modul QmlViewer:
#!/usr/bin/env python # vim: set fileencoding=utf-8 from QmlViewer import * QML_FILE = "04_raster_image.qml" if __name__ == '__main__': main(QML_FILE)
10. Načtení a zobrazení vektorových výkresů v GUI
Kromě rastrových obrázků podporuje jazyk QML i použití vektorových výkresů (či kreseb) uložených ve formátu SVG. K tomuto účelu se používá stejný prvek nazvaný Image, se kterým jsme se seznámili v předchozí kapitole. Většina vektorových kreseb má sice zadané rozměry, ty nám však nemusí vyhovovat, takže je možné zadat vlastní šířku a výšku. Zvětšení či zmenšení vektorové kresby se samozřejmě obejde bez ztráty kvality výsledku:
Image { id: left_image source: "editors/vim.svg" width: 200 height: 200 anchors.horizontalCenter: parent.horizontalCenter }
Podívejme se nyní na demonstrační příklad, v němž je trojice vektorových kreseb (konkrétně ikon tří slavných textových editorů) umístěna do okna, a to tak, že jsou všechny kresby vycentrovány. Vzhledem k tomu, že kresby mají průhledné pozadí, jsou všechny tři ikony stále dobře rozeznatelné:
Obrázek 11: Umístění tří vektorových kreseb do plochy okna. Všechny kresby jsou vycentrovány a zobrazeny přes sebe.
Následuje úplný zdrojový kód tohoto příkladu:
import QtQuick 2.0 Rectangle { id: main width: 320 height: 240 color: "lightgray" Image { id: left_image source: "editors/vim.svg" width: 200 height: 200 anchors.horizontalCenter: parent.horizontalCenter } Image { id: center_image source: "editors/emacs.svg" width: 200 height: 200 anchors.horizontalCenter: parent.horizontalCenter } Image { id: right_image source: "editors/atom.svg" width: 200 height: 200 anchors.horizontalCenter: parent.horizontalCenter } }
Skript určený pro načtení QML:
#!/usr/bin/env python # vim: set fileencoding=utf-8 from QmlViewer import * QML_FILE = "05_vector_drawings_A.qml" if __name__ == '__main__': main(QML_FILE)
11. Korektní rozmístění vektorových výkresů na plochu okna
Předchozí příklad byl sice zajímavý z technologického hlediska, ale v praktických aplikacích samozřejmě budou zákazníci vyžadovat, aby se jednotlivé prvky grafického uživatelského rozhraní umístily vedle sebe a nikoli na sebe. Úprava je ve skutečnosti snadná – u všech tří vektorových výkresů zvolíme vycentrování ve vertikální ose:
anchors.verticalCenter: parent.verticalCenter
Naproti tomu na horizontální ose bude zarovnání odlišné – první výkres bude „přilepen“ k levému okraji okna, druhý výkres zůstane vycentrován a konečně třetí výkres bude „přilepen“ k okraji pravému:
// první anchors.left: parent.left // druhý anchors.verticalCenter: parent.verticalCenter // třetí anchors.right: parent.right
Obrázek 12: Takto má vypadat korektní rozmístění všech tří vektorových výkresů na ploše okna.
Upravený QML kód vypadá následovně:
import QtQuick 2.0 Rectangle { id: main width: 320 height: 240 color: "lightgray" Image { id: left_image source: "editors/vim.svg" width: 100 height: 100 anchors.verticalCenter: parent.verticalCenter anchors.left: parent.left } Image { id: center_image source: "editors/emacs.svg" width: 100 height: 100 anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter } Image { id: right_image source: "editors/atom.svg" width: 100 height: 100 anchors.verticalCenter: parent.verticalCenter anchors.right: parent.right } }
Skript určený pro načtení QML vypadá prakticky totožně jako skript předchozí, samozřejmě až na odlišné jméno QML výkresu:
#!/usr/bin/env python # vim: set fileencoding=utf-8 from QmlViewer import * QML_FILE = "05_vector_drawings_B.qml" if __name__ == '__main__': main(QML_FILE)
12. Relativní velikost výkresu: atribut fillMode
Další zajímavou možností, která se může hodit například při programování různých prohlížečů obrázků, CAD atd., je použití atributu pojmenovaného fillMode. Název tohoto atributu může být matoucí, protože se nejedná o specifikaci barvy pozadí, která má vyplnit nějakou plochu, ale o určení, jak má widget změnit svoji velikost při změně velikosti svého předka (parent). Můžeme si to vyzkoušet. U prvního obrázku (či kresby) zvolíme, že se má obrázek zvětšovat v závislosti na velikosti předka, ovšem současně musí být zachován poměr šířka:výška:
Image { id: left_image source: "editors/vim.svg" fillMode: Image.PreserveAspectFit anchors.verticalCenter: parent.verticalCenter anchors.left: parent.left anchors.right: center_image.left }
Naproti tomu u obrázku druhého explicitně zvolíme jeho velikost:
Image { id: center_image source: "editors/emacs.svg" width: 100 height: 100 anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter }
Odlišné chování obou obrázků můžeme porovnat na následující dvojici screenshotů:
Obrázek 13: Původní velikost widgetů v okně.
Obrázek 14: Velikost widgetů po zvětšení okna.
Opět si ukažme úplný zdrojový kód tohoto příkladu:
import QtQuick 2.0 Rectangle { id: main width: 320 height: 240 color: "lightgray" Image { id: left_image source: "editors/vim.svg" fillMode: Image.PreserveAspectFit anchors.verticalCenter: parent.verticalCenter anchors.left: parent.left anchors.right: center_image.left } Image { id: center_image source: "editors/emacs.svg" width: 100 height: 100 anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter } Image { id: right_image source: "editors/atom.svg" fillMode: Image.PreserveAspectFit anchors.verticalCenter: parent.verticalCenter anchors.right: parent.right anchors.left: center_image.right } }
13. Další způsoby rozmístění prvků GUI na ploše okna
Prozatím jsme si ukázali pouze jeden způsob rozmístění prvků grafického uživatelského rozhraní na plochu okna popř. na plochu jakéhokoli předka. Jednalo se o použití kotevních přímek, které jsou sice velmi flexibilní, ovšem jejich použití může být problematické například při návrhu uživatelského rozhraní v Qt Creatoru a podobných nástrojích. I z tohoto důvodu QML umožňuje použít další správce geometrie (v QML se sice toto označení většinou nepoužívá, ovšem jedná se o ustálený termín). Mezi alternativní správce geometrie patří především běžná mřížka (grid), dále pak správce pro umístění komponent do jednoho sloupce (column) a další pro umístění komponent do jednoho řádku (row). Jednotlivé správce geometrie je samozřejmě možné kombinovat a použít tak například column pro základní rozvržení hlavního okna (menu, nástrojový pruh, hlavní plocha, stavový řádek) a uvnitř použít správce další, například row pro nástrojový pruh.
14. Správce geometrie Grid
Při použití správce geometrie, který se jmenuje příznačně Grid, se jednotlivé prvky grafického uživatelského rozhraní umisťují do neviditelné mřížky. Velikost mřížky (šířky sloupců a výšky řádků) je automaticky měněna takovým způsobem, aby se do ní všechny vkládané widgety umístily v „rozumné“ či nastavené velikosti. Programově je však možné měnit vzdálenost mezi jednotlivými widgety a tím také měnit velikost mřížky. Taktéž je možné nastavit šířky mezer mezi jednotlivými buňkami a důležité je specifikovat i počet sloupců (zatímco počet řádků se již dopočítá automaticky podle počtu vkládaných widgetů):
Grid { columns: 3 spacing: 2 anchors.verticalCenter: parent.verticalCenter anchors.horizontalCenter: parent.horizontalCenter ... ... ... }
Obrázek 16: Ukázka použití správce geometrie Grid.
V následujícím demonstračním příkladu je do pomyslné mřížky vloženo hned několik prvků grafického uživatelského rozhraní. Konkrétně se jedná o pět obdélníků různých barev a velikostí a taktéž o vektorový obrázek. Povšimněte si (i když samotná mřížka není viditelná), že se šířky sloupců a výšky řádků upravují na základě velikosti jednotlivých komponent do mřížky vkládaných:
import QtQuick 2.0 Rectangle { id: main width: 320 height: 240 color: "lightgray" Grid { columns: 3 spacing: 2 anchors.verticalCenter: parent.verticalCenter anchors.horizontalCenter: parent.horizontalCenter Rectangle { color: "#ff8080" width: 75 height: 75 } Rectangle { color: "yellow" width: 32 height: 75 } Rectangle { color: "#8080ff" width: 75 height: 32 } Rectangle { color: "#8080ff" width: 75 height: 75 } Rectangle { color: "black" width: 10 height: 10 } Image { id: left_image source: "editors/vim.svg" width: 100 height: 100 } } }
Skript pro načtení a zobrazení QML souboru:
#!/usr/bin/env python # vim: set fileencoding=utf-8 from QmlViewer import * QML_FILE = "08_grid.qml" if __name__ == '__main__': main(QML_FILE)
15. Správce geometrie Column
Další správce geometrie se jmenuje Column a jeho chování při rozmisťování prvků přesně odpovídá jeho jménu – prvky jsou jednoduše umístěny do jediného sloupce, přičemž je ovšem samozřejmě možné nastavit jejich velikost a zarovnání doleva a doprava. Šířka sloupce se přizpůsobuje šířce jednotlivých prvků. Příklad použití:
Column { spacing: 2 anchors.verticalCenter: parent.verticalCenter anchors.horizontalCenter: parent.horizontalCenter ... ... ... }
Obrázek 16: Ukázka použití správce geometrie Column.
V demonstračním příkladu použijeme stejné prvky, jako v příkladu předchozím – jednu vektorovou kresbu a sadu obdélníků. Ovšem budeme muset změnit velikost okna, aby se do něj všechny prvky vešly:
import QtQuick 2.0 Rectangle { id: main width: 240 height: 400 color: "lightgray" Column { spacing: 2 anchors.verticalCenter: parent.verticalCenter anchors.horizontalCenter: parent.horizontalCenter Rectangle { color: "#ff8080" width: 75 height: 75 } Rectangle { color: "yellow" width: 32 height: 75 } Rectangle { color: "#8080ff" width: 75 height: 32 } Rectangle { color: "#8080ff" width: 75 height: 75 } Rectangle { color: "black" width: 10 height: 10 } Image { id: left_image source: "editors/vim.svg" width: 100 height: 100 } } }
Skript pro načtení a zobrazení QML souboru:
#!/usr/bin/env python # vim: set fileencoding=utf-8 from QmlViewer import * QML_FILE = "09_column.qml" if __name__ == '__main__': main(QML_FILE)
16. Správce geometrie Row
Jak již zajisté správně tušíte, existuje vedle správce geometrie Column i správce geometrie nazvaný Row, s jehož použitím jsou jednotlivé prvky grafického uživatelského rozhraní umisťovány do jednoho řádku:
Row { spacing: 2 anchors.verticalCenter: parent.verticalCenter anchors.horizontalCenter: parent.horizontalCenter ... ... ... }
Obrázek 17: Ukázka použití správce geometrie Row.
Opět si ukažme příklad, v němž jsou jednotlivé prvky (vektorová kresba a sada obdélníků) umístěny s využitím správce geometrie Row:
import QtQuick 2.0 Rectangle { id: main width: 320 height: 240 color: "lightgray" Row { spacing: 2 anchors.verticalCenter: parent.verticalCenter anchors.horizontalCenter: parent.horizontalCenter Rectangle { color: "#ff8080" width: 75 height: 75 } Rectangle { color: "yellow" width: 32 height: 75 } Rectangle { color: "#8080ff" width: 75 height: 32 } Rectangle { color: "#8080ff" width: 75 height: 75 } Rectangle { color: "black" width: 10 height: 10 } Image { id: left_image source: "editors/vim.svg" width: 100 height: 100 } } }
Skript pro načtení a zobrazení QML souboru je až na jméno QML totožný s předchozími skripty:
#!/usr/bin/env python # vim: set fileencoding=utf-8 from QmlViewer import * QML_FILE = "10_row.qml" if __name__ == '__main__': main(QML_FILE)
17. Tvorba animací v Pyside – použití automaticky modifikované proměnné řídicí animaci
Prozatím jen ve stručnosti se dnes seznámíme s další důležitou vlastností QML (a nepřímo též PySide). Jedná se o podporu animací, které mohou probíhat buď zcela automaticky nebo s využitím pomocných skriptů, jenž mohou reagovat na akce prováděné uživatelem. V dalším příkladu je ukázána základní animace, která využívá řídicí proměnnou nazvanou animatedValue:
property int animatedValue: 0
Tato proměnná je automaticky periodicky měněna od 0 do 9 a potom zpět k nule. Interval změny 0→9 je nastaven na jednu sekundu (1000 ms); interval změny 9→0 je taktéž roven jedné sekundě:
SequentialAnimation on animatedValue { loops: Animation.Infinite PropertyAnimation { to: 10; duration: 1000 } PropertyAnimation { to: 0; duration: 1000 } }
Úplný zdrojový kód příkladu:
import QtQuick 2.0 Rectangle { color: "lightgray" width: 200 height: 200 property int animatedValue: 0 SequentialAnimation on animatedValue { loops: Animation.Infinite PropertyAnimation { to: 10; duration: 1000 } PropertyAnimation { to: 0; duration: 1000 } } Text { anchors.centerIn: parent text: parent.animatedValue } }
Pro jistotu ještě jednou skript pro načtení QML a inicializaci GUI:
#!/usr/bin/env python # vim: set fileencoding=utf-8 from QmlViewer import * QML_FILE = "11_animation_control_variable.qml" if __name__ == '__main__': main(QML_FILE)
18. Další ukázky animace – plynulá změna velikosti vektorových kreseb
Nic nás samozřejmě neomezuje v použití pouze jediné proměnné, jejíž hodnota se automaticky mění. V dalším skriptu jsou použity hned tři proměnné:
property int animatedValue1: 50 property int animatedValue2: 50 property int animatedValue3: 50
Tyto proměnné mění velikosti tří vektorových kreseb umístěných do hlavního okna (situaci máme ulehčenou tím, že všechna tři loga textových editorů mají shodnou šířku a výšku):
Image { id: left_image source: "editors/vim.svg" width: parent.animatedValue1 height: parent.animatedValue1 anchors.verticalCenter: parent.verticalCenter anchors.left: parent.left }
Úplný zdrojový kód příkladu dnes již posledního příkladu vypadá takto:
import QtQuick 2.0 Rectangle { id: main width: 320 height: 240 color: "lightgray" property int animatedValue1: 50 SequentialAnimation on animatedValue1 { loops: Animation.Infinite PropertyAnimation { to: 100; duration: 1000 } PropertyAnimation { to: 50; duration: 1000 } } property int animatedValue2: 50 SequentialAnimation on animatedValue2 { loops: Animation.Infinite PropertyAnimation { to: 100; duration: 2000 } PropertyAnimation { to: 50; duration: 2000 } } property int animatedValue3: 50 SequentialAnimation on animatedValue3 { loops: Animation.Infinite PropertyAnimation { to: 100; duration: 3000 } PropertyAnimation { to: 50; duration: 3000 } } property int animatedValue4: 0 SequentialAnimation on animatedValue4 { loops: Animation.Infinite PropertyAnimation { to: 320; duration: 6000 } PropertyAnimation { to: 0; duration: 6000 } } Image { id: left_image source: "editors/vim.svg" width: parent.animatedValue1 height: parent.animatedValue1 anchors.verticalCenter: parent.verticalCenter anchors.left: parent.left } Image { id: center_image source: "editors/emacs.svg" width: parent.animatedValue2 height: parent.animatedValue2 anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter } Image { id: right_image source: "editors/atom.svg" width: parent.animatedValue3 height: parent.animatedValue3 anchors.verticalCenter: parent.verticalCenter anchors.right: parent.right } Rectangle { id: r1 width: parent.animatedValue4 height: 30 color: "red" opacity: 0.5 anchors.left: parent.left anchors.top: parent.top } }
#!/usr/bin/env python # vim: set fileencoding=utf-8 from QmlViewer import * QML_FILE = "12_animation_vector_images.qml" if __name__ == '__main__': main(QML_FILE)
18. Repositář s demonstračními příklady
Zdrojové kódy všech dnes popsaných demonstračních příkladů byly, podobně jako tomu bylo i v předchozích článcích, uloženy do Git repositáře dostupného na adrese https://github.com/tisnik/presentations. Pokud nechcete klonovat celý repositář, můžete namísto toho použít odkazy na jednotlivé příklady, které naleznete v následující tabulce:
Následuje tabulka s odkazy na soubory QML s popisem grafického uživatelského rozhraní, které taktéž budete potřebovat:
20. Odkazy na Internetu
- QML Tutorial
https://pyside.github.io/docs/pyside/tutorials/qmltutorial/index.html - QML Advanced Tutorial
https://pyside.github.io/docs/pyside/tutorials/qmladvancedtutorial/index.html - User interface markup language
https://en.wikipedia.org/wiki/User_interface_markup_language - UsiXML
https://en.wikipedia.org/wiki/UsiXML - Anchor-based Layout in QML
https://het.as.utexas.edu/HET/Software/html/qml-anchor-layout.html#anchor-layout - PySide.QtDeclarative
https://pyside.github.io/docs/pyside/PySide/QtDeclarative/index.html - PySide and Qt Quick/QML Playground
https://wiki.qt.io/PySide-and-QML-Playground - Hand Coded GUI Versus Qt Designer GUI
https://stackoverflow.com/questions/387092/hand-coded-gui-versus-qt-designer-gui - Qt Creator Manual
http://doc.qt.io/qtcreator/ - Qt Designer Manual
http://doc.qt.io/qt-5/qtdesigner-manual.html - Qt Creator (Wikipedia)
https://en.wikipedia.org/wiki/Qt_Creator - PySide 1.2.1 documentation
https://pyside.github.io/docs/pyside/index.html - PySide na PyPi
https://pypi.org/project/PySide/