Obsah
1. Tvorba GUI v Pythonu s využitím frameworku PySide: další dostupné ovládací prvky
3. Zobrazení textového návěští zobrazujícího neformátovaný text
4. Textový popisek používající vybrané HTML značky pro zformátování textu
5. Styly rámců zobrazených okolo popisku i okolo dalších vybraných widgetů
7. Režimy zobrazení trojrozměrného reliéfu rámce
8. Textový popisek, jehož text je možné vybrat nebo i editovat
9. Aktivní textový popisek – zobrazení stránky ve webovém prohlížeči po výběru odkazu
10. Ovládací prvek QPushButton a události generované po jeho stisku
11. Pořadí posílání signálů po stisku tlačítka
12. Zaškrtávací pole (check box)
13. Nastavení a zjišťování stavu zaškrtávacích polí
14. Zaškrtávací pole se třemi stavy (tristate)
15. Nastavení a zjišťování stavu zaškrtávacích polí se třemi možnými stavy
16. Sdružení zaškrtávacích polí do skupin s exkluzivitou výběru
17. Praktická ukázka použití objektu typu QButtonGroup
18. Repositář s demonstračními příklady
1. Tvorba GUI v Pythonu s využitím frameworku PySide: další dostupné ovládací prvky
K popisu možností nabízených populární knihovnou PySide se dostáváme již potřetí. Dnes se budeme zabývat těmi ovládacími prvky, které sice na první pohled mohou vypadat jednoduše až primitivně, ovšem knihovna PySide (resp. přesněji řečeno framework Qt, který je knihovnou PySide pouze „obalen“) umožňuje i takto jednoduché prvky používat takovými způsoby, které by si v jiných frameworcích vyžádaly použití mnohem komplikovanějších komponent. Postupně si popíšeme tyto ovládací prvky:
Komponenta | Stručný popis |
---|---|
QLabel | textové či grafické (obrázek, video) návěští |
QPushButton | klasické tlačítko |
QRadioButton | přepínací tlačítko |
QCheckBox | zaškrtávací tlačítko (pole) |

Obrázek 1: Třídy představující widgety, kterými se budeme zabývat v navazujících kapitolách.
2. Textový popisek (návěští)
Zdánlivě nejjednodušším ovládacím prvkem mnoha grafických uživatelských rozhraní je textový popisek, nazývaný také (textové) návěští neboli label. Tento ovládací prvek plní v mnohých frameworcích pouze zcela pasivní úlohu, protože dokáže zobrazit jednoduchý neformátovaný text, a to mnohdy na jediném řádku. Ovšem ve frameworku PySide je tomu jinak, protože textová návěští zde mají mnohem širší oblasti použití. Mezi podporované vlastnosti patří zejména:
- Možnost zobrazení víceřádkového textu.
- Text může být naformátován, přičemž se pro formátování používá podmnožina jazyka HTML.
- Součástí formátování může být i odlišná barva jednotlivých znaků.
- K textu může být přiřazen i rastrový obrázek (pixmapa).
- Návěští může reagovat na klik vybraným tlačítkem myši.
- Text návěští je možné vybrat myší či klávesnicí (tato vlastnost se musí nakonfigurovat).
- Text návěští je dokonce možné i editovat (i tato vlastnost se musí nakonfigurovat).
- Návěští může obsahovat hypertextový odkaz, který se po výběru zobrazí v prohlížeči.
- V neposlední řadě si všimněte, že QLabel je odvozen od QFrame, což znamená, že okolo návěští je možné vytvořit různé typy rámečků.
K většině výše uvedených vlastností se postupně dopracujeme v navazujících kapitolách.
Obrázek 2: První typ widgetu už částečně známe z předchozích dvou částí tohoto seriálu. Povšimněte si, že QLabel je odvozen od QFrame.
3. Zobrazení textového návěští zobrazujícího neformátovaný text
Začneme ovšem jen pozvolna, protože si na začátku ukážeme, jak se textové návěští používá pro zobrazení jednoduchého neformátovaného textu. Vzhledem k tomu, že jsme na českém serveru, bude textový popisek obsahovat i znaky s nabodeníčky. PySide podporuje Unicode, takže si jen musíme dát pozor na to, aby byl Unicode použit i ve zdrojovém kódu, což platí především pro Python 2.x.
Na začátku zdrojového kódu nezapomeňte určit kódování (já ho nastavuji současně pro Vim, rozpoznávány jsou ale i jiné možnosti):
# vim: set fileencoding=utf-8
U řetězce předávaného konstruktoru návěští se ujistěte, že je v Unicode, a to i v Pythonu 2.x:
testLabel = QtGui.QLabel(u"Textové návěští")
Výsledek by měl vypadat následovně:

Obrázek 3: Správně zobrazené návěští ve chvíli, kdy všude používáme Unicode.
Pokud vyvíjíte aplikaci přímo v Pythonu 3.x a neuvede se explicitně prefix řetězce v kódování Unicode, může se (většinou až u zákazníka :-) objevit tato hrůza, pokud se aplikace spustí v Pythonu 2.x:

Obrázek 4: Nekorektně zobrazené návěští vinou neuvedeného kódování řetězce.
Podívejme se nyní na zdrojový kód prvního demonstračního příkladu, který byl použit pro vytvoření okna s jednoduchým textovým návěštím a taktéž s tlačítkem sloužícím pro uzavření aplikace. Pro jednoduchost používáme správce rozvržení QVBoxLayout, který jednotlivé komponenty umisťuje pod sebe. Podobnou šablonu budeme používat i u dalších příkladů:
#!/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 # 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): # velikost není potřeba specifikovat # self.resize(320, 240) self.setWindowTitle("QLabel widget") # textové návěští # pozor na nutnost použití prefixu "u" v Pythonu 2.x testLabel = QtGui.QLabel(u"Textové návěští") # tlačítko pro ukončení aplikace quitButton = QtGui.QPushButton("Quit") # vytvoření správce geometrie layout = QtGui.QVBoxLayout() # umístění widgetů do okna layout.addWidget(testLabel) layout.addWidget(quitButton) # nastavení správce geometrie a vložení všech komponent do okna self.setLayout(layout) # navázání akce na stisk tlačítka pro ukončení aplikace quitButton.clicked.connect(self.quit) def run(self, app): # zobrazení okna na obrazovce self.show() # vstup do smyčky událostí (event loop) app.exec_() def quit(self): print("Closing...") self.close() def main(): app = QtGui.QApplication(sys.argv) MainWindow().run(app) if __name__ == '__main__': main()
4. Textový popisek používající vybrané HTML značky pro zformátování textu
Ve druhém příkladu si ukážeme další užitečnou vlastnost textového návěští. Tento ovládací prvek totiž dokáže rozpoznat, že mu předáváme nikoli běžný čistý text (plain text), ale část formátovaného textu s HTML. V tomto případě je HTML interpretováno, čehož je možné využít například pro zobrazení vícebarevného návěští. Mimochodem si opět povšimněte poctivého použití prefixu „u“:
text = u"<font color='black'>černá</font><br />" \ u"<font color='blue'>modrá</font><br />" \ u"<font color='red'>čevená</font><br />" \ u"<font color='magenta'>fialová</font><br />" \ u"<font color='green'>zelená</font><br />" \ u"<font color='cyan'>azurová</font><br />" \ u"<font color='yellow'>žlutá</font><br />" \ u"<font color='white'>bílá</font><br />" testLabel = QtGui.QLabel(text)
Takto vytvořené návěští se zobrazí následujícím způsobem:

Obrázek 5: Návěští, jehož text se interpretoval jako HTML.
Jakékoli snahy o interpretaci řetězce můžeme zakázat:
testLabel.setTextFormat(QtCore.Qt.PlainText)
Výsledek potom bude vypadat takto:

Obrázek 6: Návěští, kde se text zobrazil přesně tak, jak byl zapsán ve zdrojovém kódu (okno bylo zkráceno, protože se nevešlo na plochu desktopu).
Úplný zdrojový kód dnešního druhé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 # 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): # velikost není potřeba specifikovat # self.resize(320, 240) self.setWindowTitle("QLabel widget") # textové návěští # pozor na nutnost použití prefixu "u" v Pythonu 2.x text = u"<font color='black'>černá</font><br />" \ u"<font color='blue'>modrá</font><br />" \ u"<font color='red'>čevená</font><br />" \ u"<font color='magenta'>fialová</font><br />" \ u"<font color='green'>zelená</font><br />" \ u"<font color='cyan'>azurová</font><br />" \ u"<font color='yellow'>žlutá</font><br />" \ u"<font color='white'>bílá</font><br />" testLabel = QtGui.QLabel(text) # tlačítko pro ukončení aplikace quitButton = QtGui.QPushButton("Quit") # vytvoření správce geometrie layout = QtGui.QVBoxLayout() # umístění widgetů do okna layout.addWidget(testLabel) layout.addWidget(quitButton) # nastavení správce geometrie a vložení všech komponent do okna self.setLayout(layout) # navázání akce na stisk tlačítka pro ukončení aplikace quitButton.clicked.connect(self.quit) def run(self, app): # zobrazení okna na obrazovce self.show() # vstup do smyčky událostí (event loop) app.exec_() def quit(self): print("Closing...") self.close() def main(): app = QtGui.QApplication(sys.argv) MainWindow().run(app) if __name__ == '__main__': main()
5. Styly rámců zobrazených okolo popisku i okolo dalších vybraných widgetů
Již ve druhé kapitole jsem se zmínil o tom, že ovládací prvek návěští je reprezentován třídou QLabel, která je odvozena od třídy QFrame. To nám zcela správně napovídá, že se okolo návěští může zobrazit rámeček, jehož styl je možné programově nakonfigurovat tak, aby to co nejvíce vyhovovalo uživatelům. Nabídka stylů je poměrně široká, o čemž se ostatně můžeme přesvědčit i pohledem na následující tabulku ukazující různé styly okrajů kombinované s jejich variabilní šířkou:

Obrázek 7: Různé styly a šířky okrajů podporované všemi ovládacími prvky odvozenými od QFrame
Originál obrázku naleznete na wiki PySide, konkrétně na stránce http://pyside.github.io/docs/pyside/PySide/QtGui/QFrame.html?highlight=qframe#PySide.QtGui.QFrame
Styl je možné specifikovat kombinací prvků z následujících dvou množin:
QFrame.NoFrame |
QFrame.Box |
QFrame.Panel |
QFrame.StyledPanel |
QFrame.HLine |
QFrame.VLine |
QFrame.WinPanel |
a:
QFrame.Plain |
QFrame.Raised |
QFrame.Sunken |
6. Ukázka změny typu rámce
Ve třetím demonstračním příkladu je ukázáno, jak je možné nastavit typ rámce u šesti návěští:
testLabel2 = QtGui.QLabel("Box") testLabel3 = QtGui.QLabel("Panel") testLabel4 = QtGui.QLabel("WinPanel") testLabel5 = QtGui.QLabel("HLine") testLabel6 = QtGui.QLabel("VLine") testLabel7 = QtGui.QLabel("StyledPanel") testLabel2.setFrameStyle(QtGui.QFrame.Box) testLabel3.setFrameStyle(QtGui.QFrame.Panel) testLabel4.setFrameStyle(QtGui.QFrame.WinPanel) testLabel5.setFrameStyle(QtGui.QFrame.HLine) testLabel6.setFrameStyle(QtGui.QFrame.VLine) testLabel7.setFrameStyle(QtGui.QFrame.StyledPanel)
Navíc vytvoříme jedno návěští zcela bez textu, ovšem s nastaveným rámečkem na styl HLine, což ve skutečnosti není žádný rámeček, ale horizontální čára procházející přímo středem návěští:
# horizontální oddělovač horizontalLine = QtGui.QLabel() horizontalLine.setFrameStyle(QtGui.QFrame.HLine)
Po vložení všech ovládacích prvků do okna získáme takto vypadající aplikaci:

Obrázek 8: Osm textových návěští, každé s jiným rámečkem. Poslední návěští obsahuje jen horizontální čáru, ale žádný text.
Opět si uveďme celý zdrojový kód tohoto příkladu:
#!/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 # 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): # velikost není potřeba specifikovat # self.resize(320, 240) self.setWindowTitle("QLabel widget") # textová návěští testLabel1 = QtGui.QLabel("Normal/Default") testLabel2 = QtGui.QLabel("Box") testLabel3 = QtGui.QLabel("Panel") testLabel4 = QtGui.QLabel("WinPanel") testLabel5 = QtGui.QLabel("HLine") testLabel6 = QtGui.QLabel("VLine") testLabel7 = QtGui.QLabel("StyledPanel") testLabel2.setFrameStyle(QtGui.QFrame.Box) testLabel3.setFrameStyle(QtGui.QFrame.Panel) testLabel4.setFrameStyle(QtGui.QFrame.WinPanel) testLabel5.setFrameStyle(QtGui.QFrame.HLine) testLabel6.setFrameStyle(QtGui.QFrame.VLine) testLabel7.setFrameStyle(QtGui.QFrame.StyledPanel) # horizontální oddělovač horizontalLine = QtGui.QLabel() horizontalLine.setFrameStyle(QtGui.QFrame.HLine) # tlačítko pro ukončení aplikace quitButton = QtGui.QPushButton("Quit") # vytvoření správce geometrie layout = QtGui.QVBoxLayout() # umístění widgetů do okna layout.addWidget(testLabel1) layout.addWidget(testLabel2) layout.addWidget(testLabel3) layout.addWidget(testLabel4) layout.addWidget(testLabel5) layout.addWidget(testLabel6) layout.addWidget(testLabel7) layout.addWidget(horizontalLine) layout.addWidget(quitButton) # nastavení správce geometrie a vložení všech komponent do okna self.setLayout(layout) # navázání akce na stisk tlačítka pro ukončení aplikace quitButton.clicked.connect(self.quit) def run(self, app): # zobrazení okna na obrazovce self.show() # vstup do smyčky událostí (event loop) app.exec_() def quit(self): print("Closing...") self.close() def main(): app = QtGui.QApplication(sys.argv) MainWindow().run(app) if __name__ == '__main__': main()
7. Režimy zobrazení trojrozměrného reliéfu rámce
Ve skutečnosti není styl rámců zobrazených okolo návěští určen jedinou hodnotou, ale kombinací většího množství konstant. Podívejme se například na způsob kombinace stylů „Box“, „Panel“ a „WinPanel“ s konstantami „Plain“ (2D zobrazení), „Raised“ (okraje vystupují nad plochu okna) a„Sunkem“ (okraje jsou naopak vyryté do plochy okna):
testLabel11.setFrameStyle(QtGui.QFrame.Box | QtGui.QFrame.Plain) testLabel21.setFrameStyle(QtGui.QFrame.Panel | QtGui.QFrame.Plain) testLabel31.setFrameStyle(QtGui.QFrame.WinPanel | QtGui.QFrame.Plain) testLabel12.setFrameStyle(QtGui.QFrame.Box | QtGui.QFrame.Raised) testLabel22.setFrameStyle(QtGui.QFrame.Panel | QtGui.QFrame.Raised) testLabel32.setFrameStyle(QtGui.QFrame.WinPanel | QtGui.QFrame.Raised) testLabel13.setFrameStyle(QtGui.QFrame.Box | QtGui.QFrame.Sunken) testLabel23.setFrameStyle(QtGui.QFrame.Panel | QtGui.QFrame.Sunken) testLabel33.setFrameStyle(QtGui.QFrame.WinPanel | QtGui.QFrame.Sunken)
Všech devět kombinací stylů rámců se zobrazí takto:

Obrázek 9: Devět kombinací stylů rámců zobrazených okolo textového návěští.
Samozřejmě nezapomeneme uvést úplný zdrojový kód příkladu:
#!/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 # 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): # velikost není potřeba specifikovat # self.resize(320, 240) self.setWindowTitle("QLabel widget") # textová návěští testLabel11 = QtGui.QLabel("Plain Box") testLabel21 = QtGui.QLabel("Plain Panel") testLabel31 = QtGui.QLabel("Plain WinPanel") testLabel12 = QtGui.QLabel("Raised Box") testLabel22 = QtGui.QLabel("Raised Panel") testLabel32 = QtGui.QLabel("Raised WinPanel") testLabel13 = QtGui.QLabel("Sunken Box") testLabel23 = QtGui.QLabel("Sunken Panel") testLabel33 = QtGui.QLabel("Sunken WinPanel") testLabel11.setFrameStyle(QtGui.QFrame.Box | QtGui.QFrame.Plain) testLabel21.setFrameStyle(QtGui.QFrame.Panel | QtGui.QFrame.Plain) testLabel31.setFrameStyle(QtGui.QFrame.WinPanel | QtGui.QFrame.Plain) testLabel12.setFrameStyle(QtGui.QFrame.Box | QtGui.QFrame.Raised) testLabel22.setFrameStyle(QtGui.QFrame.Panel | QtGui.QFrame.Raised) testLabel32.setFrameStyle(QtGui.QFrame.WinPanel | QtGui.QFrame.Raised) testLabel13.setFrameStyle(QtGui.QFrame.Box | QtGui.QFrame.Sunken) testLabel23.setFrameStyle(QtGui.QFrame.Panel | QtGui.QFrame.Sunken) testLabel33.setFrameStyle(QtGui.QFrame.WinPanel | QtGui.QFrame.Sunken) # horizontální oddělovač horizontalLine = QtGui.QLabel() horizontalLine.setFrameStyle(QtGui.QFrame.HLine) # tlačítko pro ukončení aplikace quitButton = QtGui.QPushButton("Quit") # vytvoření správce geometrie layout = QtGui.QGridLayout() # umístění widgetů do okna layout.addWidget(testLabel11, 1, 1) layout.addWidget(testLabel21, 2, 1) layout.addWidget(testLabel31, 3, 1) layout.addWidget(testLabel12, 1, 2) layout.addWidget(testLabel22, 2, 2) layout.addWidget(testLabel32, 3, 2) layout.addWidget(testLabel13, 1, 3) layout.addWidget(testLabel23, 2, 3) layout.addWidget(testLabel33, 3, 3) layout.addWidget(horizontalLine, 4, 2) layout.addWidget(quitButton, 5, 2) # nastavení správce geometrie a vložení všech komponent do okna self.setLayout(layout) # navázání akce na stisk tlačítka pro ukončení aplikace quitButton.clicked.connect(self.quit) def run(self, app): # zobrazení okna na obrazovce self.show() # vstup do smyčky událostí (event loop) app.exec_() def quit(self): print("Closing...") self.close() def main(): app = QtGui.QApplication(sys.argv) MainWindow().run(app) if __name__ == '__main__': main()
8. Textový popisek, jehož text je možné vybrat nebo i editovat
Textové návěští nemusí být představováno pouze neměnným textem, protože programátor může umožnit uživatelům výběr tohoto textu (myší i klávesnicí) a popř. dokonce i editaci zobrazeného textu. Zatímco editace je podle mého názoru spíše technologická zajímavost, možnost výběru textu na návěští je v mnoha případech velmi užitečná (ostatně u webových aplikací lze myší vybrat prakticky libovolný text bez omezení):

Obrázek 10: Zdánlivě obyčejná návěští.
Konfigurace návěští, jehož obsah je možné vybrat:
testLabel1.setTextInteractionFlags(QtCore.Qt.TextSelectableByMouse)

Obrázek 11: Výběr textu v prvním návěští pomocí myši.
Konfigurace návěští, jehož obsah je možné vybrat i editovat:
testLabel2.setTextInteractionFlags(QtCore.Qt.TextSelectableByMouse | QtCore.Qt.TextEditable)

Obrázek 12: Editace textu ve druhém návěští po kliknutí levým tlačítkem myši (povšimněte si textového kurzoru).
Opět si uveďme celý zdrojový kód demonstračního příkladu:
#!/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 # 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): # velikost není potřeba specifikovat # self.resize(320, 240) self.setWindowTitle("QLabel widget") # textové návěští # pozor na nutnost použití prefixu "u" v Pythonu 2.x testLabel1 = QtGui.QLabel(u"tento text je možné vybírat") testLabel2 = QtGui.QLabel(u"tento text je možné editovat") # text v návěští bude možné vybírat testLabel1.setTextInteractionFlags(QtCore.Qt.TextSelectableByMouse) # text v návěští bude možné vybírat a editovat testLabel2.setTextInteractionFlags(QtCore.Qt.TextSelectableByMouse | QtCore.Qt.TextEditable) # tlačítko pro ukončení aplikace quitButton = QtGui.QPushButton("Quit") # vytvoření správce geometrie layout = QtGui.QVBoxLayout() # umístění widgetů do okna layout.addWidget(testLabel1) layout.addWidget(testLabel2) layout.addWidget(quitButton) # nastavení správce geometrie a vložení všech komponent do okna self.setLayout(layout) # navázání akce na stisk tlačítka pro ukončení aplikace quitButton.clicked.connect(self.quit) def run(self, app): # zobrazení okna na obrazovce self.show() # vstup do smyčky událostí (event loop) app.exec_() def quit(self): print("Closing...") self.close() def main(): app = QtGui.QApplication(sys.argv) MainWindow().run(app) if __name__ == '__main__': main()
9. Aktivní textový popisek – zobrazení stránky ve webovém prohlížeči po výběru odkazu
V posledním příkladu, v němž se zabýváme „obyčejným“ textovým návěštím, je na hlavní okno aplikace umístěn odkaz, po jehož výběru (kliknutím levým tlačítkem myši) se otevře cíl odkazu ve webovém prohlížeči. Výchozí webový prohlížeč je vybrán v konfiguraci desktopového prostředí a odkaz (link) se vytvoří poměrně jednoduše a přímočaře:
# textové návěští # pozor na nutnost použití prefixu "u" v Pythonu 2.x testLabel = QtGui.QLabel(u"<a href='http://www.root.cz'>Root</a>") # pro jistotu explicitně nastavíme testLabel.setTextFormat(QtCore.Qt.RichText) # povolení otevření stránky v browseru testLabel.setOpenExternalLinks(True)
Pozor si musíte dát jen na to, aby byl odkaz skutečně zapsán formou standardního hypertextového odkazu v HTML, jinak nebude link funkční!

Obrázek 13: Okno s funkčním hypertextovým odkazem.
Samozřejmě nezapomeneme uvést úplný zdrojový kód příkladu s návěštím nakonfigurovaným do funkce hypertextového odkazu:
#!/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 # 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): # velikost není potřeba specifikovat # self.resize(320, 240) self.setWindowTitle("QLabel widget") # textové návěští # pozor na nutnost použití prefixu "u" v Pythonu 2.x testLabel = QtGui.QLabel(u"<a href='http://www.root.cz'>Root</a>") # pro jistotu explicitně nastavíme testLabel.setTextFormat(QtCore.Qt.RichText) # povolení otevření stránky v browseru testLabel.setOpenExternalLinks(True) # tlačítko pro ukončení aplikace quitButton = QtGui.QPushButton("Quit") # vytvoření správce geometrie layout = QtGui.QVBoxLayout() # umístění widgetů do okna layout.addWidget(testLabel) layout.addWidget(quitButton) # nastavení správce geometrie a vložení všech komponent do okna self.setLayout(layout) # navázání akce na stisk tlačítka pro ukončení aplikace quitButton.clicked.connect(self.quit) def run(self, app): # zobrazení okna na obrazovce self.show() # vstup do smyčky událostí (event loop) app.exec_() def quit(self): print("Closing...") self.close() def main(): app = QtGui.QApplication(sys.argv) MainWindow().run(app) if __name__ == '__main__': main()
10. Ovládací prvek QPushButton a události generované po jeho stisku
S dalším ovládacím prvkem jsme se již setkali v předchozích dvou částech tohoto seriálu, takže si dnes jen řekneme, jaké signály lze společně s ním využít. Jedná se o widget nazvaný QPushButton, který se skutečně používá pro vkládání běžného tlačítka do oken a dialogů. Z diagramu tříd zobrazeného pod tímto odstavcem je patrné, že QPushButton je odvozený od třídy QAbstractButton, z níž dědí své základní chování (ovšem již ne vzhled). Na rozdíl od výše popsaného widgetu QLabel zde však mezi předky nepatří třída QFrame, což mj. znamená, že pokud budeme chtít okolo tlačítka vytvořit nějaký konfigurovatelný okraj, je nutné tlačítko vložit do dalšího widgetu.

Obrázek 14: Hierarchie tříd s vyznačením třídy představující widget QPushButton.
Ve třídě QAbstractButton jsou deklarovány čtyři signály:
Jméno signálu | Význam |
---|---|
clicked | vyslán po stisku a puštění tlačítka |
pressed | vyslán ve chvíli, kdy došlo ke stlačení |
released | vyslán ve chvíli, kdy došlo k puštění tlačítka |
toggled | vyslán po změně stavu tlačítka (zapnuto/vypnuto) |
Ve skutečnosti jsou však společně s widgetem QPushButton používány pouze první tři signály, většinou dokonce pouze signál clicked. Pokud je totiž nutné vložit do okna aplikace přepínací tlačítko (tedy spíše přepínač), je výhodnější použít QToolButton, protože právě u nástrojového pruhu se setkáme s nutností mít jedno z tlačítek zamáčknuté (typickým příkladem je grafický editor, v němž je vybrán vždy jeden z kreslicích nástrojů).
11. Pořadí posílání signálů po stisku tlačítka
Podívejme se nyní na způsob, jakým se používají první tři signály, tedy clicked, pressed a released. V testovací aplikaci vytvoříme běžné tlačítko a zaregistrujeme příjemce těchto tří signálů. Vytvoření tlačítka se provede jednoduše:
# testovací tlačítko testButton = QtGui.QPushButton("Press me")
Navázání jediné callback funkce na tři signály (ve skutečnosti se zavolá anonymní funkce, která callback funkci předá další parametr):
# navázání akce na signál testButton.clicked.connect(lambda: buttonEvent('clicked')) testButton.pressed.connect(lambda: buttonEvent('pressed')) testButton.released.connect(lambda: buttonEvent('released'))
Samotná callback funkce je primitivní:
def buttonEvent(eventName): print("Event " + eventName)
Po spuštění aplikace se podíváme, v jakém pořadí se budou informace o příjmu signálů zobrazovat po stisku (a puštění) tlačítka:
Event pressed Event released Event clicked
Informace o stisku je samozřejmě poslána ihned ve chvíli, kdy uživatel na tlačítko klikl. Po dokončení kliku se nejdříve vyšle signál o puštění tlačítka a teprve po něm signál o samotném kliku. To v praxi znamená, že pokud budete chtít reagovat již na první stisk tlačítka, je nutné použít signál pressed, na rozdíl od některých dalších widget toolkitů.

Obrázek 15: Okno s testovacím tlačítkem.
Demonstrační příklad s testovacím tlačítkem 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 def buttonEvent(eventName): print("Event " + eventName) # 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): # velikost není potřeba specifikovat # self.resize(320, 240) self.setWindowTitle("QButton signals") # testovací tlačítko testButton = QtGui.QPushButton("Press me") # tlačítko pro ukončení aplikace quitButton = QtGui.QPushButton("Quit") # vytvoření správce geometrie layout = QtGui.QVBoxLayout() # umístění widgetů do okna layout.addWidget(testButton) layout.addWidget(quitButton) # nastavení správce geometrie a vložení všech komponent do okna self.setLayout(layout) # navázání akce na signál testButton.clicked.connect(lambda: buttonEvent('clicked')) testButton.pressed.connect(lambda: buttonEvent('pressed')) testButton.released.connect(lambda: buttonEvent('released')) # navázání akce na stisk tlačítka pro ukončení aplikace quitButton.clicked.connect(self.quit) def run(self, app): # zobrazení okna na obrazovce self.show() # vstup do smyčky událostí (event loop) app.exec_() def quit(self): print("Closing...") self.close() def main(): app = QtGui.QApplication(sys.argv) MainWindow().run(app) if __name__ == '__main__': main()
12. Zaškrtávací pole (check box)
Dalším ovládacím prvkem, který má již složitější chování, než jednoduchý QPushButton, je widget nazvaný QCheckBox. I tento widget je odvozen od třídy QAbstractButton, takže může při změně svého stavu vysílat již zmíněné čtyři typy signálů:
Jméno signálu | Význam |
---|---|
clicked | vyslán po stisku a puštění tlačítka |
pressed | vyslán ve chvíli, kdy došlo ke stlačení |
released | vyslán ve chvíli, kdy došlo k puštění tlačítka |
toggled | vyslán po změně stavu tlačítka (zapnuto/vypnuto) |

Obrázek 16: Hierarchie tříd s vyznačením třídy představující widget QCheckBox.
Widgety QCheckBox mohou být použity různým způsobem:
- Každé zaškrtávací tlačítko může pracovat samostatně a mít jen dva možné stavy (zaškrtnuto, nezaškrtnuto).
- Každé zaškrtávací tlačítko může pracovat samostatně a mít tři možné stavy (zaškrtnuto, nezaškrtnuto, třetí stav reprezentovaný znakem -, který může znamenat „žádná změna“).
- Tlačítka mohou být spojena do skupiny s exkluzivitou výběru jen jediného tlačítka v daný okamžik. Jedná se vlastně o obdobu přepínačů, které si popíšeme o několik kapitol dále.
13. Nastavení a zjišťování stavu zaškrtávacích polí
V prvním příkladu, který je věnován použití zaškrtávacích tlačítek, si ukážeme základní způsob použití těchto prvků GUI. Nejprve vytvoříme dvojici tlačítek a nastavíme jejich stav, tj. zda je ve výchozím nastavení tlačítko zaškrtnuto či nikoli:
# testovací zaškrtávací tlačítka self.testCheckBox1 = QtGui.QCheckBox("check box 1") self.testCheckBox2 = QtGui.QCheckBox("check box 2") self.testCheckBox1.setCheckState(QtCore.Qt.Unchecked) self.testCheckBox2.setCheckState(QtCore.Qt.Checked)
Při běhu aplikace se stav zaškrtávacích tlačítek může zjistit takto:
state = "checked" if checkbox.isChecked() else "unchecked" print("Checkbox {name} is {state}".format(name=name, state=state))
Metodu isChecked můžeme použít jen ve chvíli, kdy používáme tlačítka se dvěma možnými stavy!

Obrázek 17: Zaškrtávací tlačítka se dvěma možnými stavy.
Zdrojový kód demonstračního příkladu:
#!/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 def buttonEvent(eventName): print("Event " + eventName) # 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): # velikost není potřeba specifikovat # self.resize(320, 240) self.setWindowTitle("QButton signals") # testovací zaškrtávací tlačítka self.testCheckBox1 = QtGui.QCheckBox("check box 1") self.testCheckBox2 = QtGui.QCheckBox("check box 2") self.testCheckBox1.setCheckState(QtCore.Qt.Unchecked) self.testCheckBox2.setCheckState(QtCore.Qt.Checked) # tlačítko pro zjištění stavů checkboxů testButton = QtGui.QPushButton("Print state") # tlačítko pro ukončení aplikace quitButton = QtGui.QPushButton("Quit") # vytvoření správce geometrie layout = QtGui.QVBoxLayout() # umístění widgetů do okna layout.addWidget(self.testCheckBox1) layout.addWidget(self.testCheckBox2) layout.addWidget(testButton) layout.addWidget(quitButton) # nastavení správce geometrie a vložení všech komponent do okna self.setLayout(layout) # po stisku testovacího tlačítka se zavolá metoda testButton.clicked.connect(self.printState) # navázání akce na stisk tlačítka pro ukončení aplikace quitButton.clicked.connect(self.quit) def printState(self): print("-" * 50) MainWindow.printStateForCheckbox("#1", self.testCheckBox1) MainWindow.printStateForCheckbox("#2", self.testCheckBox2) @staticmethod def printStateForCheckbox(name, checkbox): state = "checked" if checkbox.isChecked() else "unchecked" print("Checkbox {name} is {state}".format(name=name, state=state)) def run(self, app): # zobrazení okna na obrazovce self.show() # vstup do smyčky událostí (event loop) app.exec_() def quit(self): print("Closing...") self.close() def main(): app = QtGui.QApplication(sys.argv) MainWindow().run(app) if __name__ == '__main__': main()
14. Zaškrtávací pole se třemi stavy (tristate)
V případě, že je zapotřebí zobrazit tři možné stavy, například „povoleno, zakázáno, beze změny“, lze pro tento účel využít třístavová zaškrtávací pole. Musíme si však uvědomit, že je nutné explicitně povolit použití třetího stavu a navíc se při rozhodování, který stav je aktuálně zvolen, již nemůže použít metoda isChecked s pravdivostní návratovou hodnotou, ale složitější metoda checkState vracející jednu z konstant:
- QtCore.Qt.CheckState.Unchecked
- QtCore.Qt.CheckState.Checked
- QtCore.Qt.CheckState.PartiallyChecked

Obrázek 18: Zaškrtávací pole se třemi možnými stavy.
Třístavová zaškrtávací pole se vytvoří takto:
self.testCheckBox3 = QtGui.QCheckBox("check box 3") self.testCheckBox4 = QtGui.QCheckBox("check box 4") self.testCheckBox5 = QtGui.QCheckBox("check box 5") # třístavové checkboxy self.testCheckBox3.setTristate(True) self.testCheckBox4.setTristate(True) self.testCheckBox5.setTristate(True)
Poté je již možné nastavit jejich výchozí stav:
self.testCheckBox3.setCheckState(QtCore.Qt.Unchecked) self.testCheckBox4.setCheckState(QtCore.Qt.PartiallyChecked) self.testCheckBox5.setCheckState(QtCore.Qt.Checked)
Zjištění stavu konkrétního zaškrtávacího pole je již snadné:
state = checkbox.checkState()
15. Nastavení a zjišťování stavu zaškrtávacích polí se třemi možnými stavy
V následujícím demonstračním příkladu je ukázána kombinace zaškrtávacích polí se dvěma stavy i se třemi možnými stavy:
#!/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 def buttonEvent(eventName): print("Event " + eventName) # 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): # velikost není potřeba specifikovat # self.resize(320, 240) self.setWindowTitle("QButton signals") # testovací zaškrtávací tlačítka self.testCheckBox1 = QtGui.QCheckBox("check box 1") self.testCheckBox2 = QtGui.QCheckBox("check box 2") self.testCheckBox3 = QtGui.QCheckBox("check box 3") self.testCheckBox4 = QtGui.QCheckBox("check box 4") self.testCheckBox5 = QtGui.QCheckBox("check box 5") # dvoustavové checkboxy self.testCheckBox1.setCheckState(QtCore.Qt.Unchecked) self.testCheckBox2.setCheckState(QtCore.Qt.Checked) # třístavové checkboxy self.testCheckBox3.setTristate(True) self.testCheckBox4.setTristate(True) self.testCheckBox5.setTristate(True) self.testCheckBox3.setCheckState(QtCore.Qt.Unchecked) self.testCheckBox4.setCheckState(QtCore.Qt.PartiallyChecked) self.testCheckBox5.setCheckState(QtCore.Qt.Checked) # horizontální oddělovač horizontalLine = QtGui.QLabel() horizontalLine.setFrameStyle(QtGui.QFrame.HLine) # tlačítko pro zjištění stavů checkboxů testButton = QtGui.QPushButton("Print state") # tlačítko pro ukončení aplikace quitButton = QtGui.QPushButton("Quit") # vytvoření správce geometrie layout = QtGui.QVBoxLayout() # umístění widgetů do okna layout.addWidget(self.testCheckBox1) layout.addWidget(self.testCheckBox2) layout.addWidget(horizontalLine) layout.addWidget(self.testCheckBox3) layout.addWidget(self.testCheckBox4) layout.addWidget(self.testCheckBox5) layout.addWidget(testButton) layout.addWidget(quitButton) # nastavení správce geometrie a vložení všech komponent do okna self.setLayout(layout) # po stisku testovacího tlačítka se zavolá metoda testButton.clicked.connect(self.printState) # navázání akce na stisk tlačítka pro ukončení aplikace quitButton.clicked.connect(self.quit) def printState(self): print("-" * 50) MainWindow.printStateForCheckbox("#1", self.testCheckBox1) MainWindow.printStateForCheckbox("#2", self.testCheckBox2) MainWindow.printStateForCheckbox("#3", self.testCheckBox3) MainWindow.printStateForCheckbox("#4", self.testCheckBox4) MainWindow.printStateForCheckbox("#5", self.testCheckBox5) @staticmethod def printStateForCheckbox(name, checkbox): state = checkbox.checkState() print("Checkbox {name} is in state {state}".format(name=name, state=state)) def run(self, app): # zobrazení okna na obrazovce self.show() # vstup do smyčky událostí (event loop) app.exec_() def quit(self): print("Closing...") self.close() def main(): app = QtGui.QApplication(sys.argv) MainWindow().run(app) if __name__ == '__main__': main()
16. Sdružení zaškrtávacích polí do skupin s exkluzivitou výběru
V některých případech může být vyžadováno, aby se určitá zaškrtávací pole nedala vybrat současně. Tento problém je sice možné řešit programově (reakcí na signály s nastavením stavu ostatních polí), je to však zbytečně komplikované. Elegantnějším řešením je spojení zaškrtávacích polí do jediné skupiny. Ta přitom není nijak vizuálně prezentována, takže taková pole mohou být na dialogu rozmístěna podle potřeb programátora či zákazníka.

Obrázek 19: Skupina zaškrtávacích polí, z nichž může být vybráno jen jedno.

Obrázek 20: Skupina zaškrtávacích polí, z nichž může být vybráno jen jedno.
Skupinu tlačítek je nutné vytvořit explicitně konstruktorem QButtonGroup a následně je nutné nastavit „exkluzivitu výběru“, tj. že lze vybrat maximálně jediné zaškrtávací pole:
self.buttonGroup = QtGui.QButtonGroup() self.buttonGroup.setExclusive(True)
Jednotlivá zaškrtávací pole vytvoříme obvyklým způsobem:
self.testCheckBox1 = QtGui.QCheckBox("check box 1") self.testCheckBox2 = QtGui.QCheckBox("check box 2") self.testCheckBox3 = QtGui.QCheckBox("check box 3") self.testCheckBox4 = QtGui.QCheckBox("check box 4") self.testCheckBox5 = QtGui.QCheckBox("check box 5")
Ovšem posléze je musíme přidat do skupiny (nebo více skupin):
self.buttonGroup.addButton(self.testCheckBox1) self.buttonGroup.addButton(self.testCheckBox2) self.buttonGroup.addButton(self.testCheckBox3) self.buttonGroup.addButton(self.testCheckBox4) self.buttonGroup.addButton(self.testCheckBox5)
Zjištění, které tlačítko bylo zaškrtnuto, je velmi snadné – není zapotřebí (typicky v programové smyčce) procházet všemi poli a volat isChecked, ale použít namísto toho metodu checkedButton:
checked = self.buttonGroup.checkedButton()
Tato metoda vrátí instanci tlačítka/pole, ovšem text na tlačítku taktéž získáme snadno:
print("Checked button: " + self.buttonGroup.checkedButton().text())
17. Praktická ukázka použití objektu typu QButtonGroup
V následujícím demonstračním příkladu je ukázáno vytvoření několika zaškrtávacích polí sdružených do jedné skupiny. Sami si vyzkoušejte, že lze vždy vybrat (zaškrtnout) maximálně jedno pole:
#!/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 def buttonEvent(eventName): print("Event " + eventName) # 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): # velikost není potřeba specifikovat # self.resize(320, 240) self.setWindowTitle("QButton signals") self.buttonGroup = QtGui.QButtonGroup() self.buttonGroup.setExclusive(True) # testovací zaškrtávací tlačítka self.testCheckBox1 = QtGui.QCheckBox("check box 1") self.testCheckBox2 = QtGui.QCheckBox("check box 2") self.testCheckBox3 = QtGui.QCheckBox("check box 3") self.testCheckBox4 = QtGui.QCheckBox("check box 4") self.testCheckBox5 = QtGui.QCheckBox("check box 5") self.buttonGroup.addButton(self.testCheckBox1) self.buttonGroup.addButton(self.testCheckBox2) self.buttonGroup.addButton(self.testCheckBox3) self.buttonGroup.addButton(self.testCheckBox4) self.buttonGroup.addButton(self.testCheckBox5) # stav checkboxů self.testCheckBox1.setCheckState(QtCore.Qt.Checked) # tlačítko pro zjištění stavů checkboxů testButton = QtGui.QPushButton("Print state") # tlačítko pro ukončení aplikace quitButton = QtGui.QPushButton("Quit") # vytvoření správce geometrie layout = QtGui.QVBoxLayout() # umístění widgetů do okna layout.addWidget(self.testCheckBox1) layout.addWidget(self.testCheckBox2) layout.addWidget(self.testCheckBox3) layout.addWidget(self.testCheckBox4) layout.addWidget(self.testCheckBox5) layout.addWidget(testButton) layout.addWidget(quitButton) # nastavení správce geometrie a vložení všech komponent do okna self.setLayout(layout) # po stisku testovacího tlačítka se zavolá metoda testButton.clicked.connect(self.printState) # navázání akce na stisk tlačítka pro ukončení aplikace quitButton.clicked.connect(self.quit) def printState(self): print("-" * 50) MainWindow.printStateForCheckbox("#1", self.testCheckBox1) MainWindow.printStateForCheckbox("#2", self.testCheckBox2) MainWindow.printStateForCheckbox("#3", self.testCheckBox3) MainWindow.printStateForCheckbox("#4", self.testCheckBox4) MainWindow.printStateForCheckbox("#5", self.testCheckBox5) print("Checked button: " + self.buttonGroup.checkedButton().text()) @staticmethod def printStateForCheckbox(name, checkbox): state = checkbox.checkState() print("Checkbox {name} is in state {state}".format(name=name, state=state)) def run(self, app): # zobrazení okna na obrazovce self.show() # vstup do smyčky událostí (event loop) app.exec_() def quit(self): print("Closing...") self.close() def main(): app = QtGui.QApplication(sys.argv) MainWindow().run(app) if __name__ == '__main__': main()
18. Repositář s demonstračními příklady
Zdrojové kódy všech jedenácti dnes popsaných demonstračních příkladů byly opět, podobně jako 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:

Obrázek 21: Skupina přepínacích tlačítek, která bude popsaná příště.
19. Odkazy na Internetu
- PySide 1.2.1 documentation
https://pyside.github.io/docs/pyside/index.html - Differences Between PySide and PyQt
https://wiki.qt.io/Differences_Between_PySide_and_PyQt - PySide 1.2.1 tutorials
https://pyside.github.io/docs/pyside/tutorials/index.html - PySide tutorial
http://zetcode.com/gui/pysidetutorial/ - Qt Core
https://pyside.github.io/docs/pyside/PySide/QtCore/Qt.html - QLayout
https://pyside.github.io/docs/pyside/PySide/QtGui/QLayout.html - QStackedLayout
https://pyside.github.io/docs/pyside/PySide/QtGui/QStackedLayout.html - QFormLayout
https://pyside.github.io/docs/pyside/PySide/QtGui/QFormLayout.html - QBoxLayout
https://pyside.github.io/docs/pyside/PySide/QtGui/QBoxLayout.html - QHBoxLayout
https://pyside.github.io/docs/pyside/PySide/QtGui/QHBoxLayout.html - QVBoxLayout
https://pyside.github.io/docs/pyside/PySide/QtGui/QVBoxLayout.html - QGridLayout
https://pyside.github.io/docs/pyside/PySide/QtGui/QGridLayout.html - Signals & Slots
http://doc.qt.io/qt-4.8/signalsandslots.html - Signals and Slots in PySide
http://wiki.qt.io/Signals_and_Slots_in_PySide - Intro to PySide/PyQt: Basic Widgets and Hello, World!
http://www.pythoncentral.io/intro-to-pysidepyqt-basic-widgets-and-hello-world/ - QWidget
https://pyside.github.io/docs/pyside/PySide/QtGui/QWidget.html - QLabel
https://pyside.github.io/docs/pyside/PySide/QtGui/QLabel.html - QAbstractButton
https://pyside.github.io/docs/pyside/PySide/QtGui/QAbstractButton.html - QCheckBox
https://pyside.github.io/docs/pyside/PySide/QtGui/QCheckBox.html - QRadioButton
https://pyside.github.io/docs/pyside/PySide/QtGui/QRadioButton.html - QButtonGroup
https://pyside.github.io/docs/pyside/PySide/QtGui/QButtonGroup.html - QFrame
https://pyside.github.io/docs/pyside/PySide/QtGui/QFrame.html#PySide.QtGui.PySide.QtGui.QFrame - QFrame.frameStyle
https://pyside.github.io/docs/pyside/PySide/QtGui/QFrame.html#PySide.QtGui.PySide.QtGui.QFrame.frameStyle - Leo editor
http://leoeditor.com/ - IPython Qt Console aneb vylepšený pseudoterminál
https://mojefedora.cz/integrovana-vyvojova-prostredi-ve-fedore-ipython-a-ipython-notebook/#k06 - Vývojová prostředí ve Fedoře (4. díl)
https://mojefedora.cz/vyvojova-prostredi-ve-fedore-4-dil/ - Seriál Letní škola programovacího jazyka Logo
http://www.root.cz/serialy/letni-skola-programovaciho-jazyka-logo/ - Educational programming language
http://en.wikipedia.org/wiki/Educational_programming_language - Logo Tree Project:
http://www.elica.net/download/papers/LogoTreeProject.pdf - Hra Breakout napísaná v Tkinteri
https://www.root.cz/clanky/hra-breakout-napisana-v-tkinteri/ - Hra Snake naprogramovaná v Pythone s pomocou Tkinter
https://www.root.cz/clanky/hra-snake-naprogramovana-v-pythone-s-pomocou-tkinter/ - 24.1. turtle — Turtle graphics
https://docs.python.org/3.5/library/turtle.html#module-turtle - TkDND
http://freecode.com/projects/tkdnd - Python Tkinter Fonts
https://www.tutorialspoint.com/python/tk_fonts.htm - The Tkinter Canvas Widget
http://effbot.org/tkinterbook/canvas.htm - Ovládací prvek (Wikipedia)
https://cs.wikipedia.org/wiki/Ovl%C3%A1dac%C3%AD_prvek_%28po%C4%8D%C3%ADta%C4%8D%29 - Rezervovaná klíčová slova v Pythonu
https://docs.python.org/3/reference/lexical_analysis.html#keywords - TkDocs: Styles and Themes
http://www.tkdocs.com/tutorial/styles.html - Drawing in Tkinter
http://zetcode.com/gui/tkinter/drawing/ - Changing ttk widget text color (StackOverflow)
https://stackoverflow.com/questions/16240477/changing-ttk-widget-text-color - The Hitchhiker's Guide to Pyhton: GUI Applications
http://docs.python-guide.org/en/latest/scenarios/gui/ - 7 Top Python GUI Frameworks for 2017
http://insights.dice.com/2014/11/26/5-top-python-guis-for-2015/ - GUI Programming in Python
https://wiki.python.org/moin/GuiProgramming - Cameron Laird's personal notes on Python GUIs
http://phaseit.net/claird/comp.lang.python/python_GUI.html - Python GUI development
http://pythoncentral.io/introduction-python-gui-development/ - Graphic User Interface FAQ
https://docs.python.org/2/faq/gui.html#graphic-user-interface-faq - TkInter
https://wiki.python.org/moin/TkInter - Tkinter 8.5 reference: a GUI for Python
http://infohost.nmt.edu/tcc/help/pubs/tkinter/web/index.html - TkInter (Wikipedia)
https://en.wikipedia.org/wiki/Tkinter - appJar
http://appjar.info/ - appJar (Wikipedia)
https://en.wikipedia.org/wiki/AppJar - appJar na Pythonhosted
http://pythonhosted.org/appJar/ - appJar widgets
http://appjar.info/pythonWidgets/ - Stránky projektu PyGTK
http://www.pygtk.org/ - PyGTK (Wikipedia)
https://cs.wikipedia.org/wiki/PyGTK - Stránky projektu PyGObject
https://wiki.gnome.org/Projects/PyGObject - Stránky projektu Kivy
https://kivy.org/#home - Stránky projektu PyQt
https://riverbankcomputing.com/software/pyqt/intro - PyQt (Wikipedia)
https://cs.wikipedia.org/wiki/PyGTK - Stránky projektu PySide
https://wiki.qt.io/PySide - PySide (Wikipedia)
https://en.wikipedia.org/wiki/PySide - Stránky projektu Kivy
https://kivy.org/#home - Kivy (framework, Wikipedia)
https://en.wikipedia.org/wiki/Kivy_(framework) - QML Applications
http://doc.qt.io/qt-5/qmlapplications.html - KDE
https://www.kde.org/ - Qt
https://www.qt.io/ - GNOME
https://en.wikipedia.org/wiki/GNOME - Category:Software that uses PyGTK
https://en.wikipedia.org/wiki/Category:Software_that_uses_PyGTK - Category:Software that uses PyGObject
https://en.wikipedia.org/wiki/Category:Software_that_uses_PyGObject - Category:Software that uses wxWidgets
https://en.wikipedia.org/wiki/Category:Software_that_uses_wxWidgets - GIO
https://developer.gnome.org/gio/stable/ - GStreamer
https://gstreamer.freedesktop.org/ - GStreamer (Wikipedia)
https://en.wikipedia.org/wiki/GStreamer - Wax Gui Toolkit
https://wiki.python.org/moin/Wax - Python Imaging Library (PIL)
http://infohost.nmt.edu/tcc/help/pubs/pil/ - 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/