Obsah
1. Tvorba grafického uživatelského rozhraní v Pythonu s využitím knihovny appJar
2. Základní informace o knihovně appJar
4. První demonstrační příklad – okno s nápisem „Hello world!“
5. Typy widgetů, které knihovna appJar programátorům nabízí
6. Vytvoření okna s několika tlačítky
7. „Přilepení“ tlačítek k okrajům okna
8. Konfigurace umístění tlačítek v dialogu
9. Skupina tlačítek vytvořená jedním příkazem
10. Naprogramování reakce na stisk tlačítek
11. Nastavení základních atributů widgetů
13. Repositář s demonstračními příklady
1. Tvorba grafického uživatelského rozhraní v Pythonu s využitím knihovny appJar
V předchozích devíti částech seriálu o tvorbě grafického uživatelského rozhraní v Pythonu jsme si popsali všechny důležité koncepty, na nichž je postavena knihovna Tkinter, která ve skutečnosti tvoří relativně úzké rozhraní mezi interpretrem programovacího jazyka Python a knihovnou Tk. S využitím Tkinteru je možné začít psát plnohodnotné aplikace vybavené grafickým uživatelským rozhraním, a to poměrně rychle a s malými vstupními znalostmi, ovšem na Tkinteru je stále v některých ohledech patrné, že je skutečně „pouze“ rozhraním ke knihovně určené pro jiný programovací jazyk. Příkladem může být například způsob pojmenování událostí, pojmenování základních kláves či specifikace, který znak v položce menu má být podtržený.
Obrázek 1: Knihovna Tkinter je vybavena i univerzálně použitelným widgetem představujícím kreslicí plochu.
2. Základní informace o knihovně appJar
Aby se vývoj aplikací vybavených grafickým uživatelským rozhraním ještě více zjednodušil, byla vytvořena knihovna nazvaná appJar. Tvůrcem této knihovny je Richard Jarvis, který pracuje jako učitel. Richardovým cílem bylo navrhnout appJar takovým způsobem, aby byla programová tvorba grafického uživatelského rozhraní co nejjednodušší a aby tak bylo možné knihovnu appJar použít ve výuce. Tento cíl se skutečně do značné míry podařilo splnit, takže v současnosti představuje appJar s velkou pravděpodobností ten nejrychlejší a současně i nejjednodušší způsob, jakým lze v Pythonu vytvořit aplikaci s grafickým uživatelským rozhraním, i když je nutné poznamenat, že některé pokročilejší ovládací prvky nejsou k dispozici (některé nejsou k dispozici ani v původním Tkinteru, ostatně i proto vznikla rozšiřující knihovna tkinter.tix, jejíž možnosti si popíšeme příště).
Knihovna appJar ovšem navíc obsahuje i další podpůrné moduly, které jsou při tvorbě GUI užitečné. Jedná se především o:
- Sadu ikon uložených ve formátu PNG, které je možné v aplikacích použít a které jsou uvolněny pod CCPL (Creative Commons Public License). Tyto ikony je možné získat v různých velikostech na adrese http://www.defaulticon.com/.
- Moduly nazvané png.py a tkinter_png.py, které zajišťují načítání rastrových obrázků ve formátu PNG. To mj. znamená, že aplikace s GUI je možné vyvíjet a spouštět i na těch systémech, kde není nainstalována knihovna PIL. Moduly jsou implementovány v čistém Pythonu, takže není nutné provádět překlad (a tím pádem „bojovat“ s překladačem céčka, jeho knihovnami atd.).
- Modul nanojpeg.py, který zajišťuje načítání rastrových obrázků ve formátu JPEG. Opět se jedná o modul psaný v čistém Pythonu, takže se v žádném případě nejedná o nejrychlejší implementace dekodéru JPEGu, ale na druhou stranu je tato implementace plně přenositelná (v tom smyslu, že pouze postačuje nakopírovat zdrojové kódy modulu na počítač vybavený interpretrem Pythonu, bez nutnosti instalovat či konfigurovat překladač céčka).
- Modul TkDND zajišťující v aplikacích funkci „drag and drop“. Jedná se o jediný modul, který vyžaduje nativní knihovny, které jsou součástí instalace appJar.
Obrázek 2: Sada ikon dodávaná společně s knihovnou appJar.
3. Instalace knihovny appJar
Ve skutečnosti není nutné knihovnu appJar složitě instalovat, což je na školách obecně problematické, neboť to vyžaduje koordinaci učitele s administrátorem (samozřejmě je nutné mít na počítači nainstalovaný Python, a to buď Python 2.x nebo 3.x, dokonce ani nejsou vyžadovány poslední verze Pythonu; opět kvůli snadnému použití na školách). Namísto instalace je pouze nutné z projektových stránek umístěných na adrese http://appjar.info/ stáhnout ZIP archiv a ten rozbalit do adresáře s projektem (a popřípadě nastavit .gitignore, aby se vzniklý podadresář neukládal do repositáře vašeho projektu).
Struktura adresáře rozbalené knihovny appJar by měla vypadat zhruba takto:
. ├── appjar.py ├── examples │ └── showcase.py ├── gpl-3.0.txt ├── __init__.py ├── lib │ ├── __init__.py │ ├── license.txt │ ├── nanojpeg.py │ ├── png.py │ ├── README.txt │ ├── tkdnd2.8 │ │ ├── pkgIndex.tcl │ │ ├── tcl_files │ │ │ ├── tkdnd_compat.tcl │ │ │ ├── tkdnd_generic.tcl │ │ │ ├── tkdnd_macosx.tcl │ │ │ ├── tkdnd_unix.tcl │ │ │ └── tkdnd_windows.tcl │ │ ├── tcl_libs │ │ │ ├── libtkdnd2.8_arm.so │ │ │ ├── libtkdnd2.8.dll │ │ │ ├── libtkdnd2.8.dylib │ │ │ ├── libtkdnd2.8_lin32.so │ │ │ ├── libtkdnd2.8_lin64.so │ │ │ ├── libtkdnd2.8.so │ │ │ └── libtkdnd2.8_win64.dll │ │ └── tkdnd.tcl │ ├── TkDND_wrapper.py │ ├── tkinter_png.py │ └── tooltip.py └── resources └── icons ├── 3d-cube.png ├── 3d-cylinder.png ... ... ...
Poznámka: povšimněte si, že modul TkDND využívá nativní knihovnu, takže ho bylo nutné přeložit pro všechny (?) běžné architektury. Je to ostatně patrné při pohledu do adresáře appJar/lib/tkdnd2.8/tcl_libs, v němž by se měly nacházet tyto soubory:
libtkdnd2.8_arm.so libtkdnd2.8.dll libtkdnd2.8.dylib libtkdnd2.8_lin32.so libtkdnd2.8_lin64.so libtkdnd2.8.so libtkdnd2.8_win64.dll
4. První demonstrační příklad – okno s nápisem „Hello world!“
Před podrobnějším popisem vlastností knihovny appJar si ukažme velmi jednoduchý demonstrační příklad, v němž se vytvoří okno obsahující textové návěští s textem „Hello world!“. Pokud nebudeme počítat první řádek, který vlastně není zcela zapotřebí (záleží na tom, jak se bude projekt spouštět), bude mít celý příklad pouhé čtyři textové řádky, což je méně, než v případě přímého použití knihovny Tkinter:
#!/usr/bin/env python from appJar import gui app = gui() app.addLabel("title", "Hello world!") app.go()
Obrázek 3: Takto vypadá dnešní první demonstrační příklad po spuštění.
Poznámka: úvodní řádek se shebangem je možné vynechat, pokud se bude skript spouštět přes interpret Pythonu.
První skutečný řádek programu načte z modulu appJar deklaraci třídy gui:
from appJar import gui
Tato třída skutečně reprezentuje základní prvek grafického uživatelského rozhraní. Zjednodušeně řečeno si můžeme instanci této třídy představit jako hlavní okno aplikace, do něhož můžeme vkládat widgety, vytvářet přes něj další podokna (sub windows) a spouštět smyčku obsluhy událostí. Vytvoření hlavního okna aplikace je triviální:
app = gui()
Na třetím řádku přidáme do hlavního okna aplikace textové návěští. Povšimněte si, že při vytváření návěští zadáváme takzvaný titulek (title) představující jméno návěští a samozřejmě také text, který se má zobrazit uživateli:
app.addLabel("title", "Hello world!")
Na posledním řádku spustíme obsluhu smyčky událostí, a to metodou nazvanou go. Implicitně se této metodě předává pouze self, v případě potřeby je však možné specifikovat jazyk GUI:
app.go()
Poznámka: tato metoda by se měla volat po inicializaci všech prvků GUI. Současně se jedná o poslední explicitně zavolaný příkaz v aplikace – ostatní volání zajistí handler smyčky událostí.
Pro porovnání si ukažme, jak by se podobný příklad implementoval přímo s použitím Tkinteru. V tomto případě musíme explicitně vytvořit nové okno (root), vytvořit návěští, vložit ho do okna a zavolat smyčku obsluhy událostí:
#!/usr/bin/env python from tkinter import * root = Tk() label = Label(root, text="Hello world!") label.pack() root.mainloop()
5. Typy widgetů, které knihovna appJar programátorům nabízí
Knihovna appJar nabízí vývojářům poměrně velké množství widgetů. Některé widgety jsou interně značně komplikované (příkladem může být DatePicker a Properties), ovšem programové ovládání těchto widgetů je ve skutečnosti velmi snadné – většina widgetů pouze na základě uživatelského vstupu nastaví hodnotu specifikovaného atributu. V následující tabulce jsou všechny podporované widgety vypsány:
Jméno widgetu | Stručný popis |
---|---|
Label | textové návěští neměnitelné uživatelem |
Message | několikařádkové textové návěští |
Entry | šest typů vstupních polí (základní + 5 speciálních) |
TextArea | několikařádkové vstupní pole |
Button | klasické „klikací“ tlačítko, existují však i další varianty (tlačítko s ikonou atd.) |
RadioButton | přepínací tlačítko, které je typicky sdružováno do větších skupin |
CheckBox | zaškrtávací tlačítko |
Properties | skupina zaškrtávacích tlačítek |
OptionBox | výběrové pole se seznamem voleb (drop-down box) |
SpinBox | výběrové pole s přetáčením voleb |
ListBox | seznam prvků s možností výběru jednoho prvku či skupiny prvků |
Scale | scrollovací prvek |
DatePicker | výběr data |
Link | klikací odkaz |
WebLink | klikací odkaz |
Grip | ploška sloužící k přesunu okna/dialogu/toolbaru |
Meter | (pasivní) zobrazení průběhu výpočtu atd. |
Separator | (pasivní) horizontální či vertikální oddělení widgetů |
Poznámka: povšimněte si, že widgety typu rozbalovací strom či tabulka (prozatím) nejsou podporovány, což může znepříjemnit vývoj aplikací se složitějším GUI.
6. Vytvoření okna s několika tlačítky
Nejjednodušším aktivním widgetem jsou tlačítka (Button), takže si nějaká přidejme do našeho demonstračního projektu. Zatímco návěští se přidávala metodou addLabel(), u tlačítek se používá metoda addButton(). Této metodě se předávají dva parametry – text zobrazený na tlačítku a reference na callback funkci, která se zavolá při stisku tlačítka. Pokud namísto reference předáme hodnotu None, nebude tlačítko na stisk reagovat (resp. vizuálně se „zamáčkne“, ale nic dalšího se nestane):
#!/usr/bin/env python from appJar import gui app = gui() app.addLabel("title", "Hello world!") app.addButton("Ok", None) app.addButton("Quit", None) app.go()
Obrázek 4: Screenshot druhého demonstračního příkladu.
Existují i další varianty tlačítka, především tlačítko s ikonou a tlačítko s libovolným obrázkem. Tento typ tlačítek si ukážeme příště.
7. „Přilepení“ tlačítek k okrajům okna
Widgety se do okna umisťují automaticky do neviditelné mřížky (grid), ovšem jejich velikost i relativní pozici je samozřejmě možné upravovat. K tomu slouží několik metod třídy gui, které nastavují konfiguraci platnou pro všechny dále vytvářené widgety:
Metoda | Význam |
---|---|
setSticky() | přilepení widgetů k okraji buněk |
setStretch() | řídí chování widgetů při změně velikosti okna |
setExpand() | interně má stejný význam jako setStretch() |
setPadX() | mezery mezi buňkami umístěnými vedle sebe |
setPadY() | mezery mezi buňkami umístěnými nad sebou |
setPadding() | kombinace předchozích dvou metod |
Nejužitečnější je metoda setSticky(), která určuje, ke kterému okraji buňky má být widget „přilepen“. Možné jsou kombinace znaků N, S, W, E (podle světových stran, podobně jako v knihovně Tkinter). V dalším příkladu je specifikováno, aby se všechny widgety přilepily k západnímu (levému) i východnímu (pravému) okraji buňky, což znamená, že tlačítka budou při změně velikosti okna zvětšována a zmenšována a navíc budou mít i stejnou šířku:
#!/usr/bin/env python from appJar import gui app = gui() app.setSticky("we") app.addLabel("title", "Hello world!") app.addButton("Ok", None) app.addButton("Quit", None) app.go()
Obrázek 5: Screenshot třetího demonstračního příkladu.
Poznámka: volání metody setSticky() ovlivní chování všech dále vytvořených widgetů, nejedná se tedy o globální konfiguraci.
8. Konfigurace umístění tlačítek v dialogu
Ve skutečnosti mají metody pro vytvoření widgetů čtveřici nepovinných (pojmenovaných) parametrů sloužících k určení, do jaké buňky mřížky má být widget vložen a kolik buněk zabere:
row=None, column=0, colspan=0, rowspan=0
Implicitně jsou widgety umisťovány pod sebe (row=None, column=0), ale to je samozřejmě možné změnit specifikací parametrů row a column:
#!/usr/bin/env python from appJar import gui app = gui() app.setSticky("we") app.addLabel("title", "Hello world!", colspan=2) app.addButton("Ok", None, 1, 0) app.addButton("Quit", None, 1, 1) app.go()
Alternativně je možné nepovinné parametry při volání pojmenovat, což je čitelnější, protože si nemusíte pamatovat pořadí parametrů:
#!/usr/bin/env python from appJar import gui app = gui() app.setSticky("we") app.addLabel("title", "Hello world!", colspan=2) app.addButton("Ok", None, row=1, column=0) app.addButton("Quit", None, row=1, column=1) app.go()
Obrázek 6: Screenshot čtvrtého demonstračního příkladu.
9. Skupina tlačítek vytvořená jedním příkazem
Poměrně často se setkáme s nutností vložit do okna větší množství tlačítek umístěných vedle sebe. K tomuto účelu je možné použít metodu addButtons(), které se předá seznam jmen tlačítek a dále reference na callback funkci, která se má zavolat při stisku jakéhokoli tlačítka ve skupině. Alternativně lze předat seznam callback funkcí, potom bude po stisku každého tlačítka zavolána odpovídající callback funkce. Případné další nepovinné parametry jsou shodné s metodou addButton():
#!/usr/bin/env python from appJar import gui app = gui() app.setSticky("we") app.addLabel("title", "Hello world!", colspan=2) app.addButtons(["Ok", "Quit"], None, 1, 0) app.go()
Obrázek 7: Screenshot pátého demonstračního příkladu.
10. Naprogramování reakce na stisk tlačítek
Tlačítka v aplikacích samozřejmě neslouží jen pro potěchu oka; naopak musí nějakým způsobem reagovat na akce prováděné uživatelem. K tomu slouží callback funkce, jejichž jméno se zadává při vkládání tlačítek do okna:
app.addButton("Ok", onButtonPress)
Podobně je možné specifikovat callback funkci pro skupinu tlačítek:
app.addButtons(["Ok", "Quit"], onButtonPress, 1, 0)
V případě, že dojde ke stisku nějakého tlačítka, je callback funkce zavolána a do jejího (jediného) parametru se předá text na tlačítku. To znamená, že pokud je jedna callback funkce použita pro obsluhu většího množství tlačítek, můžeme na základě předaného parametru rozhodnout, o jaké tlačítko se jednalo. V následující callback funkci je rozhodování jednoduché:
def onButtonPress(buttonName): if buttonName == "Quit": app.stop() else: app.infoBox("Ok, Ok", "Ok button pressed")
Úplný zdrojový kód příkladu, v němž je použita výše popsaná callback funkce, vypadá následovně:
#!/usr/bin/env python from appJar import gui def onButtonPress(buttonName): if buttonName == "Quit": app.stop() else: app.infoBox("Ok, Ok", "Ok button pressed") app = gui() app.setSticky("we") app.addLabel("title", "Hello world!", colspan=2) app.addButtons(["Ok", "Quit"], onButtonPress, 1, 0) app.go()
Obrázek 8: Screenshot šestého demonstračního příkladu.
Ve skutečných aplikacích bývá výhodnější reagovat na stisk každého tlačítka v samostatné callback funkci:
#!/usr/bin/env python from appJar import gui def onQuitButtonPress(buttonName): app.stop() def onOkButtonPress(buttonName): app.infoBox("Ok, Ok", "Ok button pressed") app = gui() app.setSticky("we") app.addLabel("title", "Hello world!", colspan=2) app.addButtons(["Ok", "Quit"], [onOkButtonPress, onQuitButtonPress], 1, 0) app.go()
11. Nastavení základních atributů widgetů
U widgetů je možné do jisté míry nastavovat i jejich vzhled. Příkladem mohou být textová návěští, u nichž lze nastavit jejich pozadí, a to konkrétně metodou setLabelBg(). Prvním parametrem této metody je titulek návěští, dalším parametrem pak barva. Ta se specifikuje buď jménem nebo hexa trojicí #rrggbb, podobně jako v knihovně Tkinter, na HTML stránkách či v CSS:
app.addLabel("redLabel", "Red") app.setLabelBg("redLabel", "red")
Podívejme se nyní na příklad, v němž je vytvořeno pět návěští, každé s jinou barvou pozadí:
#!/usr/bin/env python from appJar import gui def onButtonPress(buttonName): app.stop() app = gui() app.setSticky("we") app.addLabel("title", "Hello world!", colspan=2) app.addLabel("redLabel", "Red") app.addLabel("orangeLabel", "Orange") app.addLabel("yellowLabel", "Yellow") app.addLabel("greenLabel", "Green") app.addLabel("blueLabel", "Blue") app.setLabelBg("redLabel", "red") app.setLabelBg("orangeLabel", "orange") app.setLabelBg("yellowLabel", "yellow") app.setLabelBg("greenLabel", "green") app.setLabelBg("blueLabel", "blue") app.addButton("Quit", onButtonPress) app.go()
Obrázek 9: Screenshot sedmého demonstračního příkladu.
Pokud preferujete použití trojice barvových složen v šestnáctkové soustavě, není nic jednoduššího:
#!/usr/bin/env python from appJar import gui def onButtonPress(buttonName): app.stop() app = gui() app.setSticky("we") app.addLabel("title", "Hello world!", colspan=2) app.addLabel("redLabel", "Red") app.addLabel("orangeLabel", "Orange") app.addLabel("yellowLabel", "Yellow") app.addLabel("greenLabel", "Green") app.addLabel("blueLabel", "Blue") app.setLabelBg("redLabel", "#ff0000") app.setLabelBg("orangeLabel", "#ff8000") app.setLabelBg("yellowLabel", "#ffff00") app.setLabelBg("greenLabel", "#ff00ff") app.setLabelBg("blueLabel", "#0000ff") app.addButton("Quit", onButtonPress) app.go()
Lze použít i zkrácený zápis hexa tripletu:
#!/usr/bin/env python from appJar import gui def onButtonPress(buttonName): app.stop() app = gui() app.setSticky("we") app.addLabel("title", "Hello world!", colspan=2) app.addLabel("redLabel", "Red") app.addLabel("orangeLabel", "Orange") app.addLabel("yellowLabel", "Yellow") app.addLabel("greenLabel", "Green") app.addLabel("blueLabel", "Blue") app.setLabelBg("redLabel", "#f00") app.setLabelBg("orangeLabel", "#f80") app.setLabelBg("yellowLabel", "#ff0") app.setLabelBg("greenLabel", "#0f0") app.setLabelBg("blueLabel", "#00f") app.addButton("Quit", onButtonPress) app.go()
12. Ovládací prvek link
Widget Link, který se do okna přidává s využitím metody addLink(), se svým chováním podobá tlačítkům, protože taktéž reaguje na kliknutí myší popř. na stisk tlačítka pomocí klávesnice (přesunutí fokusu + použití mezerníku). Vizuálně ovšem widget Link skutečně připomíná hypertextový odkaz používaný na webových stránkách, což je ostatně patrné z tohoto příkladu:
#!/usr/bin/env python from appJar import gui def onLinkClick(link): print(link) app.stop() app = gui() app.addLabel("title", "Hello world!", colspan=2) app.addLink("Quit", onLinkClick) app.go()
Obrázek 10: Okno s odkazem – widgetem Link.
Podívejme se na složitější příklad, který kombinuje odkaz (link) s textovými návěštími, u nichž se změnila barva pozadí:
#!/usr/bin/env python from appJar import gui def onLinkClick(link): print(link) app.stop() app = gui() app.setSticky("we") app.addLabel("title", "Hello world!", colspan=2) app.addLabel("redLabel", "Red") app.addLabel("orangeLabel", "Orange") app.addLabel("yellowLabel", "Yellow") app.addLabel("greenLabel", "Green") app.addLabel("blueLabel", "Blue") app.setLabelBg("redLabel", "red") app.setLabelBg("orangeLabel", "orange") app.setLabelBg("yellowLabel", "yellow") app.setLabelBg("greenLabel", "green") app.setLabelBg("blueLabel", "blue") app.addLink("Quit", onLinkClick) app.go()
Obrázek 11: Screenshot osmého demonstračního příkladu.
13. Repositář s demonstračními příklady
Zdrojové kódy všech osmi dnes popsaných demonstračních příkladů naleznete pod následujícími odkazy:
Poznámka: pro úspěšné spuštění těchto příkladů musíte mít v aktuální adresáři rozbalenou knihovnu appJar!
14. Odkazy na Internetu
- 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/ - 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/