Jazyk QML (Qt Modeling Language) a PySide

24. 5. 2018
Doba čtení: 28 minut

Sdílet

Minule jsme si ukázali použití souborů UI s deklarací grafického rozhraní. Ovšem programátoři používající Python společně s PySide mají ještě jednu možnost – využít jazyk QML neboli Qt Modeling Language.

Obsah

1. Specializované jazyky pro popis grafického uživatelského rozhraní

2. Jazyk QML (Qt Modeling Language)

3. První demonstrační příklad – jednoduchý obdélník

4. Načtení a použití popisu GUI v aplikaci využívající PySide

5. Úplný zdrojový kód prvního demonstračního příkladu

6. Nepatrně složitější příklad – obdélníkové okno s textem

7. Určení vzájemné (relativní) polohy jednotlivých prvků grafického uživatelského rozhraní

8. Třetí demonstrační příklad – pozice tří čtverců v okně

9. Naprogramování reakcí na akce prováděné uživatelem

10. Reakce na stisk tlačítka myši

11. Reakce na stisk klávesy

12. Vylepšení předchozího příkladu – fokus nastavený na hlavní okno

13. Specifikace většího množství kroků při vzniku události

14. Šestý demonstrační příklad – dvě reakce na stisk tlačítka myši

15. Specifikace průhlednosti

16. Nastavení pořadí objektů v Z-ové ose

17. Rotace prvků GUI

18. Obsah dalšího pokračování seriálu

19. Repositář s demonstračními příklady

20. Odkazy na Internetu

1. Specializované jazyky pro popis grafického uživatelského rozhraní

V dnešním článku si řekneme základní informace o jazyku QML určeného pro popis designu i chování grafického uživatelského rozhraní. Prozatím se zaměříme na základní vlastnosti QML, ale v dalších článcích si již ukážeme napojení GUI části na skripty naprogramované v Pythonu s využitím PySide.

V minulosti vzniklo poměrně velké množství doménově specifických jazyků určených pro popis grafického uživatelského rozhraní. Ostatně se stačí podívat na tabulku zobrazenou pod tímto odstavcem, ve které jsou některé tyto jazyky zmíněny (zdaleka se ovšem nejedná o vyčerpávající seznam!):

Zkratka Význam
FXML (pravděpodobně Form XML)
HMVCUL Hierarchical Model View Controller User Interface Language
MARIA Model-based lAnguage foR Interactive Applications
MXML součást Adobe Flex
LZX Laszlo XML
QML Qt Modeling Language
SVG Scalable Vector Graphics
UIML User Interface Markup Language
UsiXML User Interface Extensible Markup Language
WasabiXML deklarace GUI pro Wasabi
XAL eXtensible Application Language
XAML Extensible Application Markup Language
XFD XML Form Definition
XUL XML User Interface Language

Tyto jazyky můžeme rozdělit zhruba do tří skupin:

  1. Jazyky, v nichž je možné popsat pouze design grafického uživatelského rozhraní, tj. jednotlivých oken, dialogů a ovládacích prvků v nich. Návaznost na systém pro správu událostí se řeší jinými prostředky a přitom se typicky používají ID přiřazené jednotlivým prvkům GUI. Toto zásadní omezení má výhodu v tom, že design GUI zůstává přísně deklarativní.
  2. Jazyky, ve kterých je možné specifikovat callback funkce/metody volané při vzniku různých událostí (žádost o vykreslení, klik tlačítkem myši, stisk klávesy, drag and drop atd.)
  3. Jazyky kombinující jak popis designu grafického uživatelského rozhraní, tak i zpracování událostí. Například je možné přímo do popisu GUI vkládat programový kód napsaný v JavaScriptu atd.

2. Jazyk QML (Qt Modeling Language)

Jazyk QML, jehož naprosté základy si vysvětlíme v navazujících kapitolách, patří do třetí skupiny. Obsahuje tedy jak deklarativní, tak i imperativní části, přičemž reakce na události, řízení animací atd. je možné naprogramovat buď v JavaScriptu nebo s využitím callback funkcí/metod naprogramovaných například v C++ nebo v Pythonu. V tomto článku nás bude zajímat především spojení PySide+QML, tj. poslední možnost zahrnující použití Pythonu.

Poměrně důležité je, že jazyk QML není, na rozdíl od mnoha jiných jazyků určených pro popis GUI, založen na XML, což s sebou přináší některé přednosti, ale i zápory. Díky použití složených závorek pro definici a oddělení jednotlivých elementů je zápis čitelnější a kratší, než při použití nějaké aplikace XML (ostatně i zápis JavaScriptu uvnitř QML je přirozenější, než například kombinace HTML+JavaScript). Na druhou stranu však není možné (alespoň ne jednoduše) použít běžné nástroje pro zpracování XML, tj. xpath atd.

Zajímavé jsou i možnosti popisu vzájemné pozice prvků grafického uživatelského rozhraní. Programátor popř. návrhář GUI má k dispozici několik možností popisu rozmístění prvků:

  1. Použití absolutních souřadnic.
  2. Umístění prvků do jednoho sloupce (samotný sloupec je možné vložit do dalšího prvku)
  3. Umístění prvků do jednoho řádku
  4. Umístění prvků do pravidelné mřížky (grid)
  5. Použití takzvaných kotevních přímek (přesnější český termín pravděpodobně není rozšířen)

3. První demonstrační příklad – jednoduchý obdélník

Dnešní první demonstrační příklad je pojat minimalisticky. V jazyku QML deklarujeme obdélník představující plochu hlavního okna aplikace. U tohoto obdélníku specifikujeme pouze jeho rozměry (šířku, výšku); žádné další údaje pro takto jednoduchý příklad nejsou zapotřebí. Obsah souboru pojmenovaného 165_load_qml1.qml vypadá následovně:

import QtQuick 1.0
 
Rectangle {
    width: 320
    height: 240
}

První řádek s příkazem import má formát:

import <jmenný prostor> <verze>

Ve skutečnosti se však dá import použít i v dalších situacích, například pro načtení programového kódu napsaného v JavaScriptu nebo pro načtení obsahu s definicemi typů apod.

Poznámka: můžeme se setkat i s verzí QtQuick 2.x, což ovšem vyžaduje instalaci Qt 5 a tím pádem i PyQt5. V našem článku ovšem používáme standardní PySide pro Qt 4.

Na dalších řádcích můžeme vidět deklaraci objektu, zde konkrétně objektu typu Rectangle i s naplněním jeho dvou atributů. Každý atribut je uveden na samostatném řádku:

Rectangle {
    width: 320
    height: 240
}

Atributy můžeme uvést i na stejném řádku, ovšem tehdy je nutné je oddělit středníky:

Rectangle {
    width: 320; height: 240
}

Specifikace GUI je prozatím čistě deklarativní – vytváříme v něm objekty (Rectangle, Text) a nastavujeme jejich atributy. Objekty mohou obsahovat i další objekty, takže výsledkem bude stromová struktura.

4. Načtení a použití popisu GUI v aplikaci využívající PySide

Deklaraci grafického uživatelského rozhraní velmi jednoduché aplikace již máme připravenou, takže se podívejme, jak ji načteme do skriptu napsaného v Pythonu. K tomuto účelu se používá třída QDeclarativeView z modulu QtDeclarative (v Qt 5 ovšem došlo k přejmenování třídy QQuickView!). Po importu modulu QtDeclarative:

# modul pro práci s QML
from PySide import QtDeclarative

budeme moci využít následující běžné třídy i třídy abstraktní:

ListProperty
QDeclarativeComponent
QDeclarativeContext
QDeclarativeEngine
QDeclarativeError
QDeclarativeExpression
QDeclarativeExtensionInterface
QDeclarativeExtensionPlugin
QDeclarativeImageProvider
QDeclarativeItem
QDeclarativeListReference
QDeclarativeNetworkAccessManagerFactory
QDeclarativeParserStatus
QDeclarativeProperty
QDeclarativePropertyMap
QDeclarativePropertyValueSource
QDeclarativeScriptString
QDeclarativeView

Od třídy QDeclarativeView totiž můžeme odvodit uživatelskou třídu a použít metodu setSource() pro načtení QML. Dále pomocí metody setResizeMode() zajistíme změnu velikosti okna aplikace takovým způsobem, aby odpovídalo ploše deklarované v QML:

# nový widget bude odvozen od QDeclarativeView
class MainWindow(QtDeclarative.QDeclarativeView):
 
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        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)

Takto deklarovanou třídu představující hlavní okno aplikace použijeme naprosto stejným způsobem jako jakékoli jiné hlavní okno:

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_())

Obrázek 1: První demonstrační příklad po svém spuštění.

5. Úplný zdrojový kód prvního demonstračního příkladu

Úplný zdrojový kód dnešního prvního demonstračního příkladu vypadá 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
 
QML_FILE = "165_load_qml_1.qml"
 
 
# nový widget bude odvozen od QDeclarativeView
class MainWindow(QtDeclarative.QDeclarativeView):
 
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        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()

Bez použití QML by příklad mohl vypadat například následovně:

# tento import je zapotřebí kvůli nutnosti zpracování parametrů
# předávaných přes příkazový řádek
import sys
 
# prozatím budeme využívat jen modul QtGui
from PySide import QtGui
 
 
# nový widget bude odvozen od obecného widgetu
class MainWindow(QtGui.QWidget):
 
    def __init__(self):
        # zavoláme konstruktor předka
        super(MainWindow, self).__init__()
 
        # konfigurace GUI + přidání widgetu do okna
        self.prepareGUI()
 
    def prepareGUI(self):
        self.resize(320, 240)
        self.setWindowTitle("Window and label")
 
        # návěští
        label = QtGui.QLabel("Hello world!", self)
        # posun v rámci nadřazeného widgetu
        label.move(100, 100)
 
    def run(self, app):
        # zobrazení okna na obrazovce
        self.show()
        # vstup do smyčky událostí (event loop)
        sys.exit(app.exec_())
 
 
def main():
    app = QtGui.QApplication(sys.argv)
    MainWindow().run(app)
 
 
if __name__ == '__main__':
    main()

6. Nepatrně složitější příklad – obdélníkové okno s textem

Na předchozím příkladu postavíme i všechny další demonstrační příklady. První úprava (nebo vylepšení) spočívá v tom, že budeme specifikovat barvu pozadí plochy okna. K tomu slouží atribut color, kterému je možné předat řetězec se jménem barvy, kód barvy ve formátu #rrggbb (hexa triplet) nebo ve formátu #rrggbbAA (hexa triplet s průhledností). Jména všech podporovaných barev naleznete na stránce https://www.w3.org/TR/SVG/ty­pes.html#ColorKeywords:

import QtQuick 1.0
 
Rectangle {
    width: 320
    height: 240
    color: "lightgray"
}

Dále na plochu okna přidáme další prvek grafického uživatelského rozhraní. Bude se jednat o text představovaný objektem typu Text. Mezi nastavované atributy bude patřit vlastní řetězec a taktéž režim uchycení objektu na plochu:

import QtQuick 1.0
 
Rectangle {
    width: 320
    height: 240
    color: "lightgray"
 
    Text {
        text: "Hello World"
        anchors.centerIn: parent
    }
}

Obrázek 2: Druhý demonstrační příklad po svém spuštění.

Režimy uchycení jsou specifické pro QML a vzhledem k jejich důležitosti pro tvorbu grafického uživatelského rozhraní si je popíšeme v navazující kapitole.

7. Určení vzájemné (relativní) polohy jednotlivých prvků grafického uživatelského rozhraní

V předchozím demonstračním příkladu jsme si mj. ukázali, jak je možné specifikovat umístění nějakého prvku GUI (zde se konkrétně jednalo o text) v rámci plochy okna aplikace nebo libovolného formuláře. Možnosti QML jsou ovšem v tomto ohledu mnohem širší, neboť je možné nastavit jak absolutní pozice prvků, tak i pozice relativní, a to vůči libovolnému jinému prvku. Například můžeme zapsat, že dva prvky GUI (například tlačítka) mají být zobrazeny vedle sebe, tj. pravý okraj levého tlačítka bude shodný s levým okrajem tlačítka pravého a současně budou shodné i jejich y-ové souřadnice. Způsob zápisu relativních pozic prvků GUI je v QML poměrně intuitivní. Vše je založeno na šesti pomyslných přímkách, které jsou automaticky vytvořeny pro každý prvek GUI:

Obrázek 3: Pomyslné přímky, ke kterým je možné vztahovat pozice jednotlivých komponent.
Zdroj: http://doc.qt.io/archives/qt-4.8/images/edges_qml.png

Jak se tyto přímky využijí při určování vzájemné pozice prvků? Podívejme se na příklad, v němž je všem prvkům GUI přiřazen jednoznačný identifikátor, takže se na ně můžeme odkázat. Dále stanovíme, které kotevní přímky mají mít prvky společné. Je již věcí správce geometrie, aby našel korektní řešení zadaného problému :-):

import QtQuick 1.0
 
Rectangle {
    id: main
    width: 320
    height: 240
    color: "lightgray"
 
    Rectangle {
        id: r1
        width: 100
        height: 100
        color: "red"
        anchors.verticalCenter: main.verticalCenter
    }
 
    Rectangle {
        id: r2
        width: 100
        height: 100
        color: "blue"
        anchors.left: r1.right
    }
}

Příklad je samozřejmě možné různými způsoby upravit a dosáhnout tak odlišného rozmístění prvků na ploše. Kromě explicitního použití ID prvků můžeme použít i označení parent pro předka (ne ve smyslu hierarchie tříd, ale umístění objektů do stromu GUI widgetů):

import QtQuick 1.0
 
Rectangle {
    width: 320
    height: 240
    color: "lightgray"
 
    Rectangle {
        width: 160
        height: 160
        color: "red"
        anchors.left: parent.left
        anchors.bottom: parent.bottom
    }
 
    Rectangle {
        width: 160
        height: 160
        color: "green"
        anchors.horizontalCenter: parent.horizontalCenter
        anchors.top: parent.top
    }
 
    Rectangle {
        width: 160
        height: 160
        color: "blue"
        anchors.right: parent.right
        anchors.bottom: parent.bottom
    }
}

8. Třetí demonstrační příklad – pozice tří čtverců v okně

Ve třetím demonstračním příkladu budou na plochu okna vloženy tři různobarevné čtverce. První čtverec bude v okně vertikálně (svisle) vycentrován (anchors.verticalCenter: main.verticalCenter), další na něj bude navazovat (budou se dotýkat hranami), ovšem y-ová pozice bude nulová. A konečně třetí čtverec bude svou spodní hranou zarovnán se spodním okrajem okna:

Obrázek 4: Třetí demonstrační příklad po svém spuštění.

Následuje ukázka QML skriptu, který toto GUI popisuje:

import QtQuick 1.0
 
Rectangle {
    id: main
    width: 320
    height: 240
    color: "lightgray"
 
    Rectangle {
        id: r1
        width: 100
        height: 100
        color: "red"
        anchors.verticalCenter: main.verticalCenter
    }
 
    Rectangle {
        id: r2
        width: 100
        height: 100
        color: "blue"
        anchors.left: r1.right
    }
 
    Rectangle {
        id: r3
        width: 100
        height: 100
        color: "yellow"
        anchors.left: r2.right
        anchors.top: r2.bottom
    }
}

9. Naprogramování reakcí na akce prováděné uživatelem

Všechny příklady, které jsme si až doposud ukázali, popisují pouze statický design grafického uživatelského rozhraní. Nyní se podívejme na to, jakým způsobem je možné naprogramovat alespoň minimální reakce na akce, které uživatel s GUI prvky provádí. Základ je velmi jednoduchý – nadeklarujeme kód (handler) vybrané události, která nad nějakým prvkem GUI vznikne. Může se jednat například o kliknutí tlačítkem myši. Použijeme přitom nový prvek nazvaný příhodně MouseArea. Ten je zcela neviditelný a je možné ho zvětšit takovým způsobem, že svou plochou pokryje libovolný viditelný prvek GUI. MouseArea je specifický tím, že může přijímat a zpracovávat události, které souvisí s použitím myši, tj. přesunutí kurzoru myši nad plochy prvku, opuštění plochy prvku, kliknutí tlačítkem, stisk či puštění tlačítka i akce typu drag and drop. Práce s myší je v určitém ohledu nejjednodušší, protože (na rozdíl od práce s klávesnicí) není nutné řešit nastavení fokusu atd.

Obrázek 5: Výchozí barvy čtverců.

Obrázek 6: Barvy čtverců po kliknutí tlačítkem myši.

10. Reakce na stisk tlačítka myši

Ukažme si tedy jednoduchý příklad, v němž bude možné kliknutím měnit barvy jednotlivých čtverců zobrazených v hlavním okně aplikace. Povšimněte si především způsobu zápisu reakce na událost, a to přímo v jazyku QML, bez nutnosti volat kód v Pythonu (ovšem ne vše je samozřejmě možné naprogramovat takto jednoduchým způsobem):

import QtQuick 1.0
 
Rectangle {
    id: main
    width: 320
    height: 240
    color: "lightgray"
 
    Rectangle {
        id: r1
        width: 100
        height: 100
        color: "gray"
        anchors.verticalCenter: main.verticalCenter
        MouseArea {
            anchors.fill: parent
            onClicked: r1.color = "red"
        }
    }
 
    Rectangle {
        id: r2
        width: 100
        height: 100
        color: "gray"
        anchors.left: r1.right
        MouseArea {
            anchors.fill: parent
            onClicked: parent.color = "blue"
        }
    }
 
    Rectangle {
        id: r3
        width: 100
        height: 100
        color: "gray"
        anchors.left: r2.right
        anchors.top: r2.bottom
        MouseArea {
            anchors.fill: parent
            onClicked: parent.color = "yellow"
        }
    }
}

Vidíme, že přímo v handleru události je možné měnit (prakticky) libovolný atribut libovolného prvku GUI.

11. Reakce na stisk klávesy

Dalším způsobem ovládání aplikací s grafickým uživatelským rozhraním je samozřejmě použití klávesnice. Zde ovšem narazíme na jeden problém – události, které vznikají při stisku jednotlivých kláves, přijímá pouze ten element GUI, který má nastavený fokus. Ostatní elementy nebudou tuto událost získávat a tedy na ni ani nedokáží zareagovat. Ostatně si sami můžeme vyzkoušet, co se stane ve chvíli, kdy spustíme následující demonstrační příklad, v němž (teoreticky) všechny čtverce budou reagovat na stisk kláves 1, 2 nebo 3:

import QtQuick 1.0
 
Rectangle {
    id: main
    width: 320
    height: 240
    color: "lightgray"
 
    Rectangle {
        id: r1
        width: 100
        height: 100
        color: "gray"
        anchors.verticalCenter: main.verticalCenter
        Item {
           anchors.fill: parent
           focus: true
           Keys.onDigit1Pressed: parent.color = "red"
        }
        MouseArea {
            anchors.fill: parent
            onClicked: parent.color = "#ffff00"
        }
    }
 
    Rectangle {
        id: r2
        width: 100
        height: 100
        color: "gray"
        anchors.left: r1.right
        Item {
           anchors.fill: parent
           Keys.onDigit2Pressed: parent.color = "blue"
        }
        MouseArea {
            anchors.fill: parent
            onClicked: parent.color = "#00ffff"
        }
    }
 
    Rectangle {
        id: r3
        width: 100
        height: 100
        color: "gray"
        anchors.left: r2.right
        anchors.top: r2.bottom
        Item {
           anchors.fill: parent
           Keys.onDigit3Pressed: parent.color = "yellow"
        }
        MouseArea {
            anchors.fill: parent
            onClicked: parent.color = "#ff00ff"
        }
    }
}
Poznámka: v příkladu používáme klávesy 1 až 3 z toho důvodu, že se jedná o skupinu kláves, pro něž existuje vlastní (explicitně definovaný) typ události. To ovšem neplatí například pro znakové klávesy, které používají stejnou událost, pouze s odlišným kódem stisknuté klávesy.

12. Vylepšení předchozího příkladu – fokus nastavený na hlavní okno

Jakým způsobem je tedy možné problém s fokusem vyřešit? Ve skutečnosti existuje hned několik způsobů, z nichž nejjednodušší spočívá v tom, že události vytvořené po stisku klávesy budou zpracovávány (nebo chcete li akceptovány) prvkem typu Rectangle, který představuje celou plochu okna aplikace. V handlerech událostí použijeme jednoznačná ID čtverců, kterým budeme nastavovat jejich barvy. Samozřejmě nesmíme zapomenout na to, aby byl fokus skutečně přesunut na plochu okna aplikace (focus: true), jinak nebudou události zpracovány (pokud totiž systém nezjistí, že se událost zpracovala, jednoduše ji zahodí):

Keys.onDigit1Pressed: r1.color = "red"
Keys.onDigit2Pressed: r2.color = "cyan"
Keys.onDigit3Pressed: r3.color = "yellow"

Úplný kód tohoto příkladu vypadá následovně:

import QtQuick 1.0
 
Rectangle {
    id: main
    width: 320
    height: 240
    color: "lightgray"
    Item {
       anchors.fill: parent
       focus: true
       Keys.onDigit1Pressed: r1.color = "red"
       Keys.onDigit2Pressed: r2.color = "cyan"
       Keys.onDigit3Pressed: r3.color = "yellow"
    }
 
    Rectangle {
        id: r1
        width: 100
        height: 100
        color: "gray"
        anchors.verticalCenter: main.verticalCenter
    }
 
    Rectangle {
        id: r2
        width: 100
        height: 100
        color: "gray"
        anchors.left: r1.right
    }
 
    Rectangle {
        id: r3
        width: 100
        height: 100
        color: "gray"
        anchors.left: r2.right
        anchors.top: r2.bottom
    }
}

Oba dva přístupy je samozřejmě možné zkombinovat a reagovat tak jak na stisk kláves, tak i na klik tlačítkem myši:

import QtQuick 1.0
 
Rectangle {
    id: main
    width: 320
    height: 240
    color: "lightgray"
    anchors.fill: parent
    focus: true
    Keys.onDigit1Pressed: r1.color = "red"
    Keys.onDigit2Pressed: r2.color = "cyan"
    Keys.onDigit3Pressed: r3.color = "yellow"
 
    Rectangle {
        id: r1
        width: 100
        height: 100
        color: "gray"
        anchors.verticalCenter: main.verticalCenter
        MouseArea {
            anchors.fill: parent
            onClicked: parent.color = "magenta"
        }
    }
 
    Rectangle {
        id: r2
        width: 100
        height: 100
        color: "gray"
        anchors.left: r1.right
        MouseArea {
            anchors.fill: parent
            onClicked: parent.color = "blue"
        }
    }
 
    Rectangle {
        id: r3
        width: 100
        height: 100
        color: "gray"
        anchors.left: r2.right
        anchors.top: r2.bottom
        MouseArea {
            anchors.fill: parent
            onClicked: parent.color = "green"
        }
    }
}

13. Specifikace většího množství kroků při vzniku události

V případě, že je zapotřebí v handleru události provést větší množství kroků, musíme použít příkazové závorky. Syntaxe zápisu je jednoduchá. V případě, že by se kroky zapisovaly na stejný řádek, musíme opět použít středníky, jinak nejsou nutné:

onClicked: {
    parent.color = "magenta"
    txt.text = "r1 clicked"
}

Obrázek 7: Screenshot dalšího příkladu, v němž se používá složitější handler událostí.

14. Šestý demonstrační příklad – dvě reakce na stisk tlačítka myši

Šestý příklad vychází z příkladů předchozích, ovšem je do něj přidáno zobrazení zpráv o akcích prováděných uživatelem. Z tohoto důvodu je to do plochy okna přidán textový widget s unikátním identifikátorem a v handlerech událostí měníme atribut text tohoto widgetu:

import QtQuick 1.0
 
Rectangle {
    id: main
    width: 320
    height: 240
    color: "lightgray"
    anchors.fill: parent
    focus: true
    Keys.onDigit1Pressed: r1.color = "red"
    Keys.onDigit2Pressed: r2.color = "cyan"
    Keys.onDigit3Pressed: r3.color = "yellow"
 
    Text {
       id: txt
       text: "Click"
       font.pixelSize: 16
       anchors.horizontalCenter: parent.horizontalCenter
       anchors.bottom: parent.bottom
    }
 
    Rectangle {
        id: r1
        width: 100
        height: 100
        color: "gray"
        anchors.verticalCenter: main.verticalCenter
        MouseArea {
            anchors.fill: parent
            onClicked: {
                parent.color = "magenta"
                txt.text = "r1 clicked"
            }
        }
    }
 
    Rectangle {
        id: r2
        width: 100
        height: 100
        color: "gray"
        anchors.left: r1.right
        MouseArea {
            anchors.fill: parent
            onClicked: {
                parent.color = "blue"
                txt.text = "r2 clicked"
            }
        }
    }
 
    Rectangle {
        id: r3
        width: 100
        height: 100
        color: "gray"
        anchors.left: r2.right
        anchors.top: r2.bottom
        MouseArea {
            anchors.fill: parent
            onClicked: {
                parent.color = "green"
                txt.text = "r3 clicked"
            }
        }
    }
}

15. Specifikace průhlednosti

Na závěr dnešního článku si ještě ukážeme, jakým způsobem je možné nastavit průhlednost jednotlivých prvků GUI a také pořadí prvků v Z-ové ose ve chvíli, kdy se nějaké prvky překrývají. Nastavení průhlednosti je snadné, protože stačí nastavit atribut opacity. Hodnotou tohoto atributu by mělo být reálné číslo v rozsahu od 0.0 do 1.0. Samozřejmě se opět podívejme na demonstrační příklad, v němž je jednotlivým čtvercům vloženým do plochy okna aplikace přiřazena různá průhlednost:

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
        anchors.left: parent.left
        anchors.bottom: parent.bottom
    }
 
    Rectangle {
        id: r2
        width: 160
        height: 160
        color: "yellow"
        opacity: 0.5
        anchors.horizontalCenter: parent.horizontalCenter
        anchors.top: parent.top
    }
 
    Rectangle {
        id: r3
        width: 160
        height: 160
        color: "blue"
        opacity: 0.5
        anchors.right: parent.right
        anchors.bottom: parent.bottom
    }
}

16. Nastavení pořadí objektů v Z-ové ose

Ve chvíli, kdy se nějaké prvky překrývají, je většinou nutné explicitně nastavit jejich pořadí. To lze udělat velmi snadno nastavením z-ové souřadnice jednotlivých prvků, a to prakticky stejným způsobem, jakým se tato vlastnost nastavuje ve většině vektorových editorů popř. v HTML či v CSS. Vše bude patrné z následujícího příkladu:

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
        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
        anchors.right: parent.right
        anchors.bottom: parent.bottom
    }
}

Obrázek 8: Nastavení průhlednosti a překryvu prvků grafického uživatelského rozhraní.

Jednoduchou úpravou předchozího zdrojového kódu můžeme dosáhnout toho, že prvky budou překryty v opačném pořadí:

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
        z: 3
        anchors.left: parent.left
        anchors.bottom: parent.bottom
    }
 
    Rectangle {
        id: r2
        width: 160
        height: 160
        color: "yellow"
        opacity: 0.5
        z: 2
        anchors.horizontalCenter: parent.horizontalCenter
        anchors.top: parent.top
    }
 
    Rectangle {
        id: r3
        width: 160
        height: 160
        color: "blue"
        opacity: 0.5
        z: 1
        anchors.right: parent.right
        anchors.bottom: parent.bottom
    }
}

17. Rotace prvků GUI

Na úplný závěr si ukážeme rotaci prvků:

Obrázek 9: Rotace vybraných prvků grafického uživatelského rozhraní.

Rotace se provádí snadno nastavením atributu rotation na hodnotu odlišnou od nuly:

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
    }
}

18. Obsah dalšího pokračování seriálu

V navazující části tohoto seriálu se seznámíme s dalšími možnostmi, které programátorům nabízí jazyk QML. Především si ukážeme odlišné způsoby rozmístění prvků grafického uživatelského rozhraní v okně či formuláři s využitím objektů typu Grid, Row a Column, což je alternativa k dnes zmíněným kotevním přímkám. Také si ukážeme reálné možnosti propojení QML se skripty naprogramovanými v Pythonu s využitím frameworku PySide.

ict ve školství 24

19. 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/pre­sentations. 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

  1. QML Tutorial
    https://pyside.github.io/doc­s/pyside/tutorials/qmltuto­rial/index.html
  2. QML Advanced Tutorial
    https://pyside.github.io/doc­s/pyside/tutorials/qmladvan­cedtutorial/index.html
  3. User interface markup language
    https://en.wikipedia.org/wi­ki/User_interface_markup_lan­guage
  4. UsiXML
    https://en.wikipedia.org/wiki/UsiXML
  5. Anchor-based Layout in QML
    https://het.as.utexas.edu/HET/Sof­tware/html/qml-anchor-layout.html#anchor-layout
  6. PySide.QtDeclarative
    https://pyside.github.io/doc­s/pyside/PySide/QtDeclara­tive/index.html
  7. PySide and Qt Quick/QML Playground
    https://wiki.qt.io/PySide-and-QML-Playground
  8. Hand Coded GUI Versus Qt Designer GUI
    https://stackoverflow.com/qu­estions/387092/hand-coded-gui-versus-qt-designer-gui
  9. Qt Creator Manual
    http://doc.qt.io/qtcreator/
  10. Qt Designer Manual
    http://doc.qt.io/qt-5/qtdesigner-manual.html
  11. Qt Creator (Wikipedia)
    https://en.wikipedia.org/wi­ki/Qt_Creator
  12. QIODevice
    https://pyside.github.io/doc­s/pyside/PySide/QtCore/QI­ODevice.html#PySide.QtCore­.QIODevice
  13. QFile
    https://pyside.github.io/doc­s/pyside/PySide/QtCore/QFi­le.html#PySide.QtCore.QFi­le
  14. QUiLoader
    https://pyside.github.io/doc­s/pyside/PySide/QtUiTools/QU­iLoader.html#PySide.QtUiTo­ols.PySide.QtUiTools.QUiLo­ader.load
  15. QSvgWidget
    https://pyside.github.io/doc­s/pyside/PySide/QtSvg/QSvgWid­get.html
  16. QByteArray
    https://pyside.github.io/doc­s/pyside/PySide/QtCore/QBy­teArray.html
  17. Python Bytes, Bytearray
    https://www.w3resource.com/pyt­hon/python-bytes.php
  18. psep-0101.txt (mj. popis mapování typů Pythonu na třídy v PySide)
    https://github.com/techto­nik/pseps/blob/master/psep-0101.txt
  19. QSvgRenderer
    https://pyside.github.io/doc­s/pyside/PySide/QtSvg/QSvgRen­derer.html
  20. QSvgGenerator
    https://pyside.github.io/doc­s/pyside/PySide/QtSvg/QSvgGe­nerator.html
  21. QIcon
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QIcon­.html
  22. PySide 1.2.1 documentation
    https://pyside.github.io/doc­s/pyside/index.html
  23. QStyle
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QSty­le.html
  24. QCommonStyle
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QCom­monStyle.html
  25. QPlastiqueStyle
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QPlas­tiqueStyle.html
  26. QMacStyle
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QMac­Style.html
  27. QCleanlooksStyle
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QCle­anlooksStyle.html
  28. QGtkStyle
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QGtkSty­le.html
  29. QCDEStyle
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QCDES­tyle.html
  30. QMotifStyle
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QMo­tifStyle.html
  31. QWindowsStyle
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QWin­dowsStyle.html
  32. QStyleFactory
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QSty­leFactory.html
  33. QStyleOptionHeader
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QSty­leOptionHeader.html
  34. QAbstractSlider
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/Abstrac­tSlider.html
  35. QScrollBar
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/Scro­llBar.html
  36. QSlider
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/Sli­der.html
  37. QDial
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/Dial­.html
  38. QImage
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QIma­ge.html
  39. QPixmap
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QPix­map.html
  40. QBitmap
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QBit­map.html
  41. QPaintDevice
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QPa­intDevice.html
  42. QPicture
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QPic­ture.html
  43. QPainter
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QPa­inter.html
  44. QPainterPath
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QPa­interPath.html
  45. QGradient
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QGra­dient.html
  46. QLinearGradient
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QLi­nearGradient.html
  47. QRadialGradient
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QRa­dialGradient.html
  48. QTableWidget
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QTa­bleWidget.html
  49. QTableWidgetItem
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QTa­bleWidgetItem.html
  50. QTreeWidget
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QTre­eWidget.html
  51. QTreeWidgetItem
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QTre­eWidgetItem.html
  52. Afinní zobrazení
    https://cs.wikipedia.org/wi­ki/Afinn%C3%AD_zobrazen%C3%AD
  53. Differences Between PySide and PyQt
    https://wiki.qt.io/Differen­ces_Between_PySide_and_PyQt
  54. PySide 1.2.1 tutorials
    https://pyside.github.io/doc­s/pyside/tutorials/index.html
  55. PySide tutorial
    http://zetcode.com/gui/py­sidetutorial/
  56. Drawing in PySide
    http://zetcode.com/gui/py­sidetutorial/drawing/
  57. Qt Core
    https://pyside.github.io/doc­s/pyside/PySide/QtCore/Qt­.html
  58. QLayout
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QLa­yout.html
  59. QValidator
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QVa­lidator.html
  60. QStackedLayout
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QStac­kedLayout.html
  61. QFormLayout
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QFor­mLayout.html
  62. QBoxLayout
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QBox­Layout.html
  63. QHBoxLayout
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QHBox­Layout.html
  64. QVBoxLayout
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QVBox­Layout.html
  65. QGridLayout
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QGrid­Layout.html
  66. QAction
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QAc­tion.html
  67. QDialog
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QDi­alog.html
  68. QMessageBox
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QMes­sageBox.html
  69. QErrorMessage
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QError­Message.html
  70. QInputDialog
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QIn­putDialog.html
  71. QColorDialog
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QCo­lorDialog.html
  72. QListWidget
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QLis­tWidget.html
  73. Signals & Slots
    http://doc.qt.io/qt-4.8/signalsandslots.html
  74. Signals and Slots in PySide
    http://wiki.qt.io/Signals_an­d_Slots_in_PySide
  75. Intro to PySide/PyQt: Basic Widgets and Hello, World!
    http://www.pythoncentral.io/intro-to-pysidepyqt-basic-widgets-and-hello-world/
  76. QLineEdit
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QLi­neEdit.html
  77. QTextEdit
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QTex­tEdit.html
  78. QValidator
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QVa­lidator.html
  79. QIntValidator
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QIn­tValidator.html
  80. QRegExpValidator
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QRe­gExpValidator.html
  81. QWidget
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QWid­get.html
  82. QMainWindow
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QMa­inWindow.html
  83. QLabel
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QLa­bel.html
  84. QAbstractButton
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QAb­stractButton.html
  85. QCheckBox
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QChec­kBox.html
  86. QRadioButton
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QRa­dioButton.html
  87. QButtonGroup
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QBut­tonGroup.html
  88. QFrame
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QFra­me.html#PySide.QtGui.PySi­de.QtGui.QFrame
  89. QFrame.frameStyle
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QFra­me.html#PySide.QtGui.PySi­de.QtGui.QFrame.frameStyle
  90. Leo editor
    http://leoeditor.com/
  91. IPython Qt Console aneb vylepšený pseudoterminál
    https://mojefedora.cz/integrovana-vyvojova-prostredi-ve-fedore-ipython-a-ipython-notebook/#k06
  92. Vývojová prostředí ve Fedoře (4. díl)
    https://mojefedora.cz/vyvojova-prostredi-ve-fedore-4-dil/
  93. Seriál Letní škola programovacího jazyka Logo
    http://www.root.cz/serialy/letni-skola-programovaciho-jazyka-logo/
  94. Educational programming language
    http://en.wikipedia.org/wi­ki/Educational_programmin­g_language
  95. Logo Tree Project:
    http://www.elica.net/downlo­ad/papers/LogoTreeProject­.pdf
  96. Hra Breakout napísaná v Tkinteri
    https://www.root.cz/clanky/hra-breakout-napisana-v-tkinteri/
  97. Hra Snake naprogramovaná v Pythone s pomocou Tkinter
    https://www.root.cz/clanky/hra-snake-naprogramovana-v-pythone-s-pomocou-tkinter/
  98. 24.1. turtle — Turtle graphics
    https://docs.python.org/3­.5/library/turtle.html#mo­dule-turtle
  99. TkDND
    http://freecode.com/projects/tkdnd
  100. Python Tkinter Fonts
    https://www.tutorialspoin­t.com/python/tk_fonts.htm
  101. The Tkinter Canvas Widget
    http://effbot.org/tkinter­book/canvas.htm
  102. Ovládací prvek (Wikipedia)
    https://cs.wikipedia.org/wi­ki/Ovl%C3%A1dac%C3%AD_prvek_­%28po%C4%8D%C3%ADta%C4%8D%29
  103. Rezervovaná klíčová slova v Pythonu
    https://docs.python.org/3/re­ference/lexical_analysis.html#ke­ywords
  104. TkDocs: Styles and Themes
    http://www.tkdocs.com/tuto­rial/styles.html
  105. Drawing in Tkinter
    http://zetcode.com/gui/tkin­ter/drawing/
  106. Changing ttk widget text color (StackOverflow)
    https://stackoverflow.com/qu­estions/16240477/changing-ttk-widget-text-color
  107. The Hitchhiker's Guide to Pyhton: GUI Applications
    http://docs.python-guide.org/en/latest/scenarios/gui/
  108. 7 Top Python GUI Frameworks for 2017
    http://insights.dice.com/2014/11/26/5-top-python-guis-for-2015/
  109. GUI Programming in Python
    https://wiki.python.org/mo­in/GuiProgramming
  110. Cameron Laird's personal notes on Python GUIs
    http://phaseit.net/claird/com­p.lang.python/python_GUI.html
  111. Python GUI development
    http://pythoncentral.io/introduction-python-gui-development/
  112. Graphic User Interface FAQ
    https://docs.python.org/2/faq/gu­i.html#graphic-user-interface-faq
  113. TkInter
    https://wiki.python.org/moin/TkInter
  114. Tkinter 8.5 reference: a GUI for Python
    http://infohost.nmt.edu/tcc/hel­p/pubs/tkinter/web/index.html
  115. TkInter (Wikipedia)
    https://en.wikipedia.org/wiki/Tkinter
  116. appJar
    http://appjar.info/
  117. appJar (Wikipedia)
    https://en.wikipedia.org/wiki/AppJar
  118. appJar na Pythonhosted
    http://pythonhosted.org/appJar/
  119. appJar widgets
    http://appjar.info/pythonWidgets/
  120. Stránky projektu PyGTK
    http://www.pygtk.org/
  121. PyGTK (Wikipedia)
    https://cs.wikipedia.org/wiki/PyGTK
  122. Stránky projektu PyGObject
    https://wiki.gnome.org/Pro­jects/PyGObject
  123. Stránky projektu Kivy
    https://kivy.org/#home
  124. Stránky projektu PyQt
    https://riverbankcomputin­g.com/software/pyqt/intro
  125. PyQt (Wikipedia)
    https://cs.wikipedia.org/wiki/PyGTK
  126. Stránky projektu PySide
    https://wiki.qt.io/PySide
  127. PySide (Wikipedia)
    https://en.wikipedia.org/wiki/PySide
  128. Stránky projektu Kivy
    https://kivy.org/#home
  129. Kivy (framework, Wikipedia)
    https://en.wikipedia.org/wi­ki/Kivy_(framework)
  130. QML Applications
    http://doc.qt.io/qt-5/qmlapplications.html
  131. KDE
    https://www.kde.org/
  132. Qt
    https://www.qt.io/
  133. GNOME
    https://en.wikipedia.org/wiki/GNOME
  134. Category:Software that uses PyGTK
    https://en.wikipedia.org/wi­ki/Category:Software_that_u­ses_PyGTK
  135. Category:Software that uses PyGObject
    https://en.wikipedia.org/wi­ki/Category:Software_that_u­ses_PyGObject
  136. Category:Software that uses wxWidgets
    https://en.wikipedia.org/wi­ki/Category:Software_that_u­ses_wxWidgets
  137. GIO
    https://developer.gnome.or­g/gio/stable/
  138. GStreamer
    https://gstreamer.freedesktop.org/
  139. GStreamer (Wikipedia)
    https://en.wikipedia.org/wi­ki/GStreamer
  140. Wax Gui Toolkit
    https://wiki.python.org/moin/Wax
  141. Python Imaging Library (PIL)
    http://infohost.nmt.edu/tcc/hel­p/pubs/pil/
  142. Why Pyjamas Isn’t a Good Framework for Web Apps (blogpost z roku 2012)
    http://blog.pyjeon.com/2012/07/29/why-pyjamas-isnt-a-good-framework-for-web-apps/
  143. PySide na PyPi
    https://pypi.org/project/PySide/

Autor článku

Vystudoval VUT FIT a v současné době pracuje na projektech vytvářených v jazycích Python a Go.