Obsah
1. Další užitečné ovládací prvky poskytované frameworkem PySide
2. Využití splashscreenu u aplikací s pomalejší inicializací
3. První demonstrační příklad: zobrazení splashscreenu při spouštění aplikace
5. Třetí demonstrační příklad: automatické skrytí splashscreenu po zobrazení hlavního okna aplikace
6. Scrollbar ve funkci samostatného ovládacího prvku
7. Čtvrtý demonstrační příklad: zobrazení vertikálního scrollbaru
8. Pátý demonstrační příklad: změna orientace scrollbaru z vertikálního na horizontální
9. Události generované při práci se scrollbarem
10. Šestý demonstrační příklad: nastavení výchozí hodnoty scrollbaru
11. Sedmý demonstrační příklad: nastavení dalších parametrů scrollbaru
12. Výběr celočíselné hodnoty s využitím ovládacího prvku QSlider
13. Osmý demonstrační příklad: horizontální posuvník v hlavním okně aplikace
14. Devátý demonstrační příklad: zobrazení různých typů značek u horizontálních posuvníků
15. Desátý demonstrační příklad: svislé (vertikální) posuvníky s různým typem značek
16. Ovládací prvek QDial a způsob jeho použití
17. Zobrazení widgetu QDial na ploše hlavního okna aplikace
18. Repositář s demonstračními příklady
1. Další užitečné ovládací prvky poskytované frameworkem PySide
Ve třináctém článku věnovaném tvorbě aplikací s grafickým uživatelským rozhraním založeným na frameworku PySide si popíšeme některé další užitečné ovládací prvky (widgety), které lze při tvorbě aplikací použít. Nejprve se zmíníme o možnosti zobrazení tzv. splashscreenu při spouštění aplikace a zejména ještě před inicializací a zobrazením hlavního okna. Ve druhé části článku si popíšeme tři ovládací prvky, které jsou určeny pro zadávání (celo)číselných údajů, ale nikoli přímým zápisem jednotlivých cifer (k této činnosti by ostatně dostačovalo běžné vstupní textové pole), ale interaktivně – s využitím scrollbaru, posuvníku (slideru) nebo prvku typu dial, který svým vzhledem i způsobem ovládání připomíná otočné ovládací prvky používané například v audio zařízeních (ovládání hlasitosti atd.).
2. Využití splashscreenu u aplikací s pomalejší inicializací
U mnoha rozsáhlejších aplikací s grafickým uživatelským rozhraním se setkáme s použitím tzv. splashscreenu. Jedná se o jednoduché okno (typicky bez rámečku i bez dalších dekorací), které se zobrazí ihned po spuštění aplikace, tj. tehdy, kdy ještě neproběhla úplná inicializace aplikace a kdy například ještě ani není zobrazeno hlavní okno. Splashscreen typicky obsahuje obrázek s logem aplikace, popř. se zde mohou zobrazovat i další zprávy informující uživatele o postupné inicializaci jednotlivých subsystémů aplikace (viz například sekvence spouštění GIMPu).
Obrázek 1: Logo projektu PySide, které použijeme pro splashscreen v dále uvedených demonstračních příkladech.
Splashscreen si můžeme vytvořit velmi jednoduše, a to s využitím objektu typu QSplashScreen. V nejjednodušším případě předáme tomuto objektu instanci třídy QPixmap s obrázkem, který se má ve splashscreenu zobrazit:
# splashscreen pixmap = QtGui.QPixmap("pixmaps/pysidelogo.png") splash = QtGui.QSplashScreen(pixmap) splash.show()
Následně můžeme vytvořit hlavní okno aplikace, provést inicializaci všech widgetů, datových struktur atd.:
# vytvoření a zobrazení hlavního okna window = MainWindow()
Splashscreen se může automaticky zavřít po zobrazení hlavního okna. Tohoto chování dosáhneme použitím metody QSplashScreen.finish(), které předáme referenci na okno, po jehož zobrazení se splashscreen automaticky zavře:
# zajistíme, aby se po zobrazení hlavního okna splashscreen automaticky skryl splash.finish(window) window.showMainWindow(app)
3. První demonstrační příklad: zobrazení splashscreenu při spouštění aplikace
V prvním demonstračním příkladu je ukázáno základní použití splashscreenu. Komponenta představující splashscreen je inicializována a zobrazena ještě před zobrazením hlavního okna. Ve splashscreenu je zobrazen rastrový obrázek s logem frameworku PySide, který jsme již viděli v předchozí kapitole. Ovšem vzhledem k tomu, že žádným způsobem neřešíme skrytí splashscreenu po inicializaci hlavního okna, budou nakonec obě komponenty na desktopu zobrazeny současně, což je ostatně patrné z následujícího screenshotu:
Obrázek 2: Zobrazení splashscreenu společně s hlavním oknem aplikace.
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 # nový widget bude odvozen od obecného widgetu class MainWindowContent(QtGui.QWidget): def __init__(self): # zavoláme konstruktor předka super(MainWindowContent, self).__init__() # konfigurace GUI + přidání widgetu do okna self.prepareGUI() def prepareGUI(self): # vytvoření widgetů vkládaných do okna quitButton = self.prepareQuitButton() # vytvoření správce geometrie layout = QtGui.QVBoxLayout() # umístění widgetů do okna layout.addWidget(quitButton) # nastavení správce geometrie a vložení všech komponent do okna self.setLayout(layout) def prepareQuitButton(self): # tlačítko quitButton = QtGui.QPushButton('Quit', self) quitButton.resize(quitButton.sizeHint()) # navázání akce na signál quitButton.clicked.connect(QtCore.QCoreApplication.instance().quit) return quitButton # nový widget bude odvozen od obecného hlavního okna class MainWindow(QtGui.QMainWindow): 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(450, 450) self.setWindowTitle("QSplashScreen") # vložení komponenty do okna self.setCentralWidget(MainWindowContent()) def showMainWindow(self, app): # zobrazení okna na obrazovce self.show() def main(): app = QtGui.QApplication(sys.argv) # splashscreen pixmap = QtGui.QPixmap("pixmaps/pysidelogo.png") splash = QtGui.QSplashScreen(pixmap) splash.show() # vytvoření a zobrazení hlavního okna window = MainWindow() window.showMainWindow(app) # vstup do smyčky událostí (event loop) app.exec_() if __name__ == '__main__': main()
4. Druhý demonstrační příklad: využití splashscreenu ještě před inicializací a zobrazením hlavního okna aplikace
Ve druhém příkladu schválně pozdržíme zobrazení hlavního okna, aby bylo patrné, že se splashscreen skutečně zobrazí prakticky okamžitě. Celá věc má ale jeden háček – pokud zobrazení hlavního okna pozdržíme funkcí time.sleep(), splashscreen se nezobrazí ihned podle našeho očekávání. Je tomu tak z toho důvodu, že time.sleep() je blokující operace, takže se ani nezavolá smyčka pro správu událostí. Řešení spočívá v „rozsekání“ intervalu, v němž hlavní vlákno aplikace čeká v time.sleep(), na menší části, přičemž se vždy v každém mezičase zavolá metoda QApplication.processEvents(), která zpracování událostí zajistí.
V dalším kódu je čekání „rozsekáno“ po 100ms, takže se splashscreen zobrazí skutečně prakticky okamžitě:
# uspání hlavního vlákna aplikace na zadaný počet sekund def sleep(app, seconds): print('sleeping') for i in range(0, 10*seconds): print(i) app.processEvents() time.sleep(1/10.0) print('waking up')
Opět se podívejme na 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 # používáme jen pro import funkce sleep import time # uspání hlavního vlákna aplikace na zadaný počet sekund def sleep(app, seconds): print('sleeping') for i in range(0, 10*seconds): print(i) app.processEvents() time.sleep(1/10.0) print('waking up') # nový widget bude odvozen od obecného widgetu class MainWindowContent(QtGui.QWidget): def __init__(self): # zavoláme konstruktor předka super(MainWindowContent, self).__init__() # konfigurace GUI + přidání widgetu do okna self.prepareGUI() def prepareGUI(self): # vytvoření widgetů vkládaných do okna quitButton = self.prepareQuitButton() # vytvoření správce geometrie layout = QtGui.QVBoxLayout() # umístění widgetů do okna layout.addWidget(quitButton) # nastavení správce geometrie a vložení všech komponent do okna self.setLayout(layout) def prepareQuitButton(self): # tlačítko quitButton = QtGui.QPushButton('Quit', self) quitButton.resize(quitButton.sizeHint()) # navázání akce na signál quitButton.clicked.connect(QtCore.QCoreApplication.instance().quit) return quitButton # nový widget bude odvozen od obecného hlavního okna class MainWindow(QtGui.QMainWindow): 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(450, 450) self.setWindowTitle("QSplashScreen") # vložení komponenty do okna self.setCentralWidget(MainWindowContent()) def showMainWindow(self, app): sleep(app, 2) # zobrazení okna na obrazovce self.show() def main(): app = QtGui.QApplication(sys.argv) # splashscreen pixmap = QtGui.QPixmap("pixmaps/pysidelogo.png") splash = QtGui.QSplashScreen(pixmap) splash.show() # vytvoření a zobrazení hlavního okna window = MainWindow() window.showMainWindow(app) # vstup do smyčky událostí (event loop) app.exec_() if __name__ == '__main__': main()
5. Třetí demonstrační příklad: automatické skrytí splashscreenu po zobrazení hlavního okna aplikace
Ve třetím příkladu je zajištěno automatické skrytí splashscreenu ve chvíli, kdy je již zobrazeno hlavní okno aplikace. Skrytí je provedeno asynchronně, tj. nikoli explicitním voláním metody destroy či hide, ale nepřímo specifikací, po zobrazení jakého widgetu či kontejneru se má splashscreen skrýt:
app = QtGui.QApplication(sys.argv) # splashscreen pixmap = QtGui.QPixmap("pixmaps/pysidelogo.png") splash = QtGui.QSplashScreen(pixmap) splash.show() # vytvoření a zobrazení hlavního okna window = MainWindow() window.showMainWindow(app) # zajistíme, aby se po zobrazení hlavního okna splashscreen automaticky skryl splash.finish(window) # vstup do smyčky událostí (event loop) app.exec_()
Poznámka: vyzkoušejte si, jak se projeví prohození řádku s voláním splash.finish() s řádkem obsahujícím window.showMainWindow().
Opět následuje výpis zdrojového kódu 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 # používáme jen pro import funkce sleep import time # uspání hlavního vlákna aplikace na zadaný počet sekund def sleep(app, seconds): print('sleeping') for i in range(0, 10*seconds): print(i) app.processEvents() time.sleep(1/10.0) print('waking up') # nový widget bude odvozen od obecného widgetu class MainWindowContent(QtGui.QWidget): def __init__(self): # zavoláme konstruktor předka super(MainWindowContent, self).__init__() # konfigurace GUI + přidání widgetu do okna self.prepareGUI() def prepareGUI(self): # vytvoření widgetů vkládaných do okna quitButton = self.prepareQuitButton() # vytvoření správce geometrie layout = QtGui.QVBoxLayout() # umístění widgetů do okna layout.addWidget(quitButton) # nastavení správce geometrie a vložení všech komponent do okna self.setLayout(layout) def prepareQuitButton(self): # tlačítko quitButton = QtGui.QPushButton('Quit', self) quitButton.resize(quitButton.sizeHint()) # navázání akce na signál quitButton.clicked.connect(QtCore.QCoreApplication.instance().quit) return quitButton # nový widget bude odvozen od obecného hlavního okna class MainWindow(QtGui.QMainWindow): 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(450, 450) self.setWindowTitle("QSplashScreen") # vložení komponenty do okna self.setCentralWidget(MainWindowContent()) def showMainWindow(self, app): sleep(app, 2) # zobrazení okna na obrazovce self.show() def main(): app = QtGui.QApplication(sys.argv) # splashscreen pixmap = QtGui.QPixmap("pixmaps/pysidelogo.png") splash = QtGui.QSplashScreen(pixmap) splash.show() # vytvoření a zobrazení hlavního okna window = MainWindow() window.showMainWindow(app) # zajistíme, aby se po zobrazení hlavního okna splashscreen automaticky skryl splash.finish(window) # vstup do smyčky událostí (event loop) app.exec_() if __name__ == '__main__': main()
6. Scrollbar ve funkci samostatného ovládacího prvku
Ve frameworku PySide máme k dispozici hned několik ovládacích prvků (widgetů) určených pro interaktivní zadávání celočíselných numerických údajů. Tyto údaje mohou představovat různé veličiny, například podíly barvových složek při míchání barev atd. Všechny tyto ovládací prvky jsou odvozeny od třídy QAbstractSlider. V první řadě se jedná o samostatný scrollbar, tj. scrollbar nenavázaný na žádnou další komponentu, dále pak o posuvník (slider) a PySide programátorům dokonce nabízí i otočný prvek (dial). Nejprve se věnujme klasickému scrollbaru. Ten je možné použít pro mnoho operací, například pro posun dat v jiném widgetu (například při zobrazení dokumentů), ovšem scrollbar může vystupovat i jako zcela samostatný ovládací prvek.
Vytvoření scrollbaru je snadné. Pokud vyžadujeme vertikální scrollbar, použije se konstruktor bez parametrů:
scrollbar = QtGui.QScrollBar()
Při požadavku na použití scrollbaru horizontálního se musí jeho orientace určit explicitně:
scrollbar = QtGui.QScrollBar(QtCore.Qt.Horizontal)
Při práci se scrollbarem se může generovat několik typů událostí, přičemž pravděpodobně nejčastěji budeme reagovat na událost vytvořenou ve chvíli, kdy se změní pozice posuvné části scrollbaru (té se pro větší zmatení také říká posuvník). Touto problematikou se budeme zabývat v deváté kapitole. Samozřejmě je možné programově nastavit mezní hodnoty posuvníku (implicitně 0..99) i jeho aktuální pozici v rámci vybíraných hodnot. Poslední užitečnou operací je stanovení výšky nebo délky posuvné oblasti, čehož se využívá v těch případech, kdy velikost posuvné oblasti například reprezentuje stránku dokumentu (pokud tedy uživatel pracuje se čtyřstránkovým dokumentem, měla by být velikost posuvné oblasti zhruba čtvrtinová v porovnání s celkovou délkou nebo výškou scrollbaru).
7. Čtvrtý demonstrační příklad: zobrazení vertikálního scrollbaru
V dnešním čtvrtém demonstračním příkladu je ukázán způsob vytvoření vertikálního scrollbaru. Tato orientace scrollbaru je výchozí, takže scrollbar vlastně není zapotřebí žádným způsobem konfigurovat a metoda pro vytvoření scrollbaru je tedy jen dvouřádková:
def prepareScrollbar(self): # vytvoření scrollbaru scrollbar = QtGui.QScrollBar() return scrollbar
Scrollbar umístíme do kontejneru naprosto stejným způsobem, jako jakýkoli jiný widget:
# vytvoření widgetů vkládaných do okna scrollBar = self.prepareScrollbar() quitButton = self.prepareQuitButton() # vytvoření správce geometrie layout = QtGui.QVBoxLayout() # umístění widgetů do okna layout.addWidget(scrollBar) layout.addWidget(quitButton) # nastavení správce geometrie a vložení všech komponent do okna self.setLayout(layout)
Obrázek 3: Screenshot čtvrtého demonstračního příkladu se zobrazeným vertikálně orientovaným scrollbarem.
Podívejme se na zdrojový kód příkladu se scrollbarem:
#!/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 MainWindowContent(QtGui.QWidget): def __init__(self): # zavoláme konstruktor předka super(MainWindowContent, self).__init__() # konfigurace GUI + přidání widgetu do okna self.prepareGUI() def prepareGUI(self): # vytvoření widgetů vkládaných do okna scrollBar = self.prepareScrollbar() quitButton = self.prepareQuitButton() # vytvoření správce geometrie layout = QtGui.QVBoxLayout() # umístění widgetů do okna layout.addWidget(scrollBar) layout.addWidget(quitButton) # nastavení správce geometrie a vložení všech komponent do okna self.setLayout(layout) def prepareQuitButton(self): # tlačítko quitButton = QtGui.QPushButton('Quit', self) quitButton.resize(quitButton.sizeHint()) # navázání akce na signál quitButton.clicked.connect(QtCore.QCoreApplication.instance().quit) return quitButton def prepareScrollbar(self): # vytvoření scrollbaru scrollbar = QtGui.QScrollBar() return scrollbar # nový widget bude odvozen od obecného hlavního okna class MainWindow(QtGui.QMainWindow): 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(450, 450) self.setWindowTitle("QScrollBar") # vložení komponenty do okna self.setCentralWidget(MainWindowContent()) def run(self, app): # zobrazení okna na obrazovce self.show() # vstup do smyčky událostí (event loop) app.exec_() def main(): app = QtGui.QApplication(sys.argv) MainWindow().run(app) if __name__ == '__main__': main()
8. Pátý demonstrační příklad: změna orientace scrollbaru z vertikálního na horizontální
Pátý příklad se od příkladu předchozího odlišuje pouze tím, že je scrollbar orientován horizontálně, tj. vodorovně. Jediná změna v programovém kódu spočívá v předání parametru do konstruktoru widgetu QScrollBar:
def prepareScrollbar(self): # vytvoření scrollbaru scrollbar = QtGui.QScrollBar(QtCore.Qt.Horizontal) return scrollbar
Obrázek 4: Screenshot pátého demonstračního příkladu se zobrazeným horizontálně orientovaným scrollbarem.
Opět si ukážeme úplný 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 MainWindowContent(QtGui.QWidget): def __init__(self): # zavoláme konstruktor předka super(MainWindowContent, self).__init__() # konfigurace GUI + přidání widgetu do okna self.prepareGUI() def prepareGUI(self): # vytvoření widgetů vkládaných do okna scrollBar = self.prepareScrollbar() quitButton = self.prepareQuitButton() # vytvoření správce geometrie layout = QtGui.QVBoxLayout() # umístění widgetů do okna layout.addWidget(scrollBar) layout.addWidget(quitButton) # nastavení správce geometrie a vložení všech komponent do okna self.setLayout(layout) def prepareQuitButton(self): # tlačítko quitButton = QtGui.QPushButton('Quit', self) quitButton.resize(quitButton.sizeHint()) # navázání akce na signál quitButton.clicked.connect(QtCore.QCoreApplication.instance().quit) return quitButton def prepareScrollbar(self): # vytvoření scrollbaru scrollbar = QtGui.QScrollBar(QtCore.Qt.Horizontal) return scrollbar # nový widget bude odvozen od obecného hlavního okna class MainWindow(QtGui.QMainWindow): 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(450, 300) self.setWindowTitle("QScrollBar") # vložení komponenty do okna self.setCentralWidget(MainWindowContent()) def run(self, app): # zobrazení okna na obrazovce self.show() # vstup do smyčky událostí (event loop) app.exec_() def main(): app = QtGui.QApplication(sys.argv) MainWindow().run(app) if __name__ == '__main__': main()
9. Události generované při práci se scrollbarem
Při manipulaci se scrollbary samozřejmě mohou vzniknout události různého typu. Popis těchto událostí nalezneme v dokumentaci ke třídě QAbstractSlider:
Událost | Parametry svázané s událostí |
---|---|
actionTriggered | action |
rangeChanged | min, max |
sliderMoved | position |
sliderPressed | × |
sliderReleased | × |
valueChanged | value |
Nejčastěji je nutné reagovat na změnu pozice posuvníku scrollbaru, k čemuž slouží událost reprezentovaná signálem valueChanged. Propojení události s jejím handlerem je snadné:
def prepareScrollbar(self): # vytvoření scrollbaru scrollbar = QtGui.QScrollBar(QtCore.Qt.Horizontal) scrollbar.valueChanged.connect(self.onScrollBarValueChanged) scrollbar.setValue(50) return scrollbar
Samotnému handleru je předána nová hodnota posuvníku, ovšem vzhledem k tomu, že voláme metodu a nikoli obyčejnou funkci, nesmíme zapomenout ani na parametr self:
def onScrollBarValueChanged(self, value): text = "Scrollbar value: {v}".format(v=value) self.label.setText(text)
Obrázek 5: Nastavení výchozí hodnoty scrollbaru.
10. Šestý demonstrační příklad: nastavení výchozí hodnoty scrollbaru
V dnešním šestém demonstračním příkladu je ukázáno, jak lze naprogramovat reakci na změnu pozice posuvníku scrollbaru. Vzhledem k tomu, že v příkladu využíváme funkce prepareScrollbar() a onScrollBarValueChanged() popsané v předchozí kapitole, si zde můžeme bez dalších podrobnějších komentářů přímo ukázat 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 MainWindowContent(QtGui.QWidget): def __init__(self): # zavoláme konstruktor předka super(MainWindowContent, self).__init__() # konfigurace GUI + přidání widgetu do okna self.prepareGUI() def prepareGUI(self): # vytvoření widgetů vkládaných do okna self.label = QtGui.QLabel("") scrollBar = self.prepareScrollbar() quitButton = self.prepareQuitButton() # vytvoření správce geometrie layout = QtGui.QVBoxLayout() # umístění widgetů do okna layout.addWidget(self.label) layout.addWidget(scrollBar) layout.addWidget(quitButton) # nastavení správce geometrie a vložení všech komponent do okna self.setLayout(layout) def prepareQuitButton(self): # tlačítko quitButton = QtGui.QPushButton('Quit', self) quitButton.resize(quitButton.sizeHint()) # navázání akce na signál quitButton.clicked.connect(QtCore.QCoreApplication.instance().quit) return quitButton def prepareScrollbar(self): # vytvoření scrollbaru scrollbar = QtGui.QScrollBar(QtCore.Qt.Horizontal) scrollbar.valueChanged.connect(self.onScrollBarValueChanged) scrollbar.setValue(50) return scrollbar def onScrollBarValueChanged(self, value): text = "Scrollbar value: {v}".format(v=value) self.label.setText(text) # nový widget bude odvozen od obecného hlavního okna class MainWindow(QtGui.QMainWindow): 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(450, 300) self.setWindowTitle("QScrollBar") # vložení komponenty do okna self.setCentralWidget(MainWindowContent()) def run(self, app): # zobrazení okna na obrazovce self.show() # vstup do smyčky událostí (event loop) app.exec_() def main(): app = QtGui.QApplication(sys.argv) MainWindow().run(app) if __name__ == '__main__': main()
Obrázek 6: Scrollbar má nulovou hodnotu přesně uprostřed svého dráhy.
11. Sedmý demonstrační příklad: nastavení dalších parametrů scrollbaru
V dnešním sedmém demonstračním příkladu je ukázáno, jakým způsobem se nastavují další parametry scrollbaru. Jedná se samozřejmě především o specifikaci nejmenší a největší hodnoty, které odpovídají mezní polohy posuvníku (nezávisle na orientaci scrollbaru). Výchozí hodnoty jsou 0 a 100, ale je možné nastavit prakticky libovolné celé číslo z 32bitového rozsahu, i když je nutné říci, že v případě použití větších rozdílů mezi minimální a maximální hodnotou nebude ovládání pomocí klávesnice příliš praktické. Dále je možné nastavit výchozí krok posuvníku při ovládání z klávesnice a v neposlední řadě také krok odpovídající jedné stránce (použitelné ve chvíli, kdy se scrollbar používá pro listování nějakým dokumentem). Toto nastavení současně změní šířku posuvníku, což si sami můžete vyzkoušet změnou parametru metody QScrollBar.setPageStep():
def prepareScrollbar(self): # vytvoření scrollbaru scrollbar = QtGui.QScrollBar(QtCore.Qt.Horizontal) scrollbar.valueChanged.connect(self.onScrollBarValueChanged) scrollbar.setMinimum(-100) scrollbar.setMaximum(100) scrollbar.setSingleStep(1) scrollbar.setPageStep(10) scrollbar.setValue(1) return scrollbar
Následuje výpis zdrojového kódu 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 MainWindowContent(QtGui.QWidget): def __init__(self): # zavoláme konstruktor předka super(MainWindowContent, self).__init__() # konfigurace GUI + přidání widgetu do okna self.prepareGUI() def prepareGUI(self): # vytvoření widgetů vkládaných do okna self.label = QtGui.QLabel("") scrollBar = self.prepareScrollbar() quitButton = self.prepareQuitButton() # vytvoření správce geometrie layout = QtGui.QVBoxLayout() # umístění widgetů do okna layout.addWidget(self.label) layout.addWidget(scrollBar) layout.addWidget(quitButton) # nastavení správce geometrie a vložení všech komponent do okna self.setLayout(layout) def prepareQuitButton(self): # tlačítko quitButton = QtGui.QPushButton('Quit', self) quitButton.resize(quitButton.sizeHint()) # navázání akce na signál quitButton.clicked.connect(QtCore.QCoreApplication.instance().quit) return quitButton def prepareScrollbar(self): # vytvoření scrollbaru scrollbar = QtGui.QScrollBar(QtCore.Qt.Horizontal) scrollbar.valueChanged.connect(self.onScrollBarValueChanged) scrollbar.setMinimum(-100) scrollbar.setMaximum(100) scrollbar.setSingleStep(1) scrollbar.setPageStep(10) scrollbar.setValue(1) return scrollbar def onScrollBarValueChanged(self, value): text = "Scrollbar value: {v}".format(v=value) self.label.setText(text) # nový widget bude odvozen od obecného hlavního okna class MainWindow(QtGui.QMainWindow): 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(450, 300) self.setWindowTitle("QScrollBar") # vložení komponenty do okna self.setCentralWidget(MainWindowContent()) def run(self, app): # zobrazení okna na obrazovce self.show() # vstup do smyčky událostí (event loop) app.exec_() def main(): app = QtGui.QApplication(sys.argv) MainWindow().run(app) if __name__ == '__main__': main()
12. Výběr celočíselné hodnoty s využitím ovládacího prvku QSlider
Scrollbar popsaný v předchozích kapitolách je primárně určen pro orientaci uživatele v rozsáhlejších dokumentech popř. v obrázcích atd. Sice ho je možné použít i pro přímé zadání celočíselné hodnoty, což jsme si ostatně ukázali, ale pro tuto činnost je výhodnější spíše použití posuvníku neboli widgetu typu slider. Předností posuvníku je fakt, že je u něj možné zobrazit značky představující zvolenou škálu hodnot a tak zrychlit změnu hodnot prováděnou uživatelem.
Posuvník se ve frameworku PySide vytváří prakticky stejným způsobem jako scrollbar, tj. při jeho konstrukci specifikujeme orientaci posuvníku (svislou či vodorovnou):
def prepareSlider(self): # vytvoření slideru slider = QtGui.QSlider(QtCore.Qt.Horizontal) slider.valueChanged.connect(self.onSliderValueChanged)
Následně můžeme posuvník umístit do libovolného kontejneru, například do hlavního okna aplikace:
# vytvoření widgetů vkládaných do okna self.label = QtGui.QLabel("") slider = self.prepareSlider() quitButton = self.prepareQuitButton() # vytvoření správce geometrie layout = QtGui.QVBoxLayout() # umístění widgetů do okna layout.addWidget(self.label) layout.addWidget(slider) layout.addWidget(quitButton) # nastavení správce geometrie a vložení všech komponent do okna self.setLayout(layout)
Obrázek 7: Horizontální posuvník v hlavním okně aplikace.
13. Osmý demonstrační příklad: horizontální posuvník v hlavním okně aplikace
V dalším demonstračním příkladu je ukázán způsob umístění horizontálního posuvníku do hlavního okna aplikace. Hlavní části kódu jsme si již vysvětlili v předchozí kapitole, takže se jen podívejme na úplný 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 MainWindowContent(QtGui.QWidget): def __init__(self): # zavoláme konstruktor předka super(MainWindowContent, self).__init__() # konfigurace GUI + přidání widgetu do okna self.prepareGUI() def prepareGUI(self): # vytvoření widgetů vkládaných do okna self.label = QtGui.QLabel("") slider = self.prepareSlider() quitButton = self.prepareQuitButton() # vytvoření správce geometrie layout = QtGui.QVBoxLayout() # umístění widgetů do okna layout.addWidget(self.label) layout.addWidget(slider) layout.addWidget(quitButton) # nastavení správce geometrie a vložení všech komponent do okna self.setLayout(layout) def prepareQuitButton(self): # tlačítko quitButton = QtGui.QPushButton('Quit', self) quitButton.resize(quitButton.sizeHint()) # navázání akce na signál quitButton.clicked.connect(QtCore.QCoreApplication.instance().quit) return quitButton def prepareSlider(self): # vytvoření slideru slider = QtGui.QSlider(QtCore.Qt.Horizontal) slider.valueChanged.connect(self.onSliderValueChanged) return slider def onSliderValueChanged(self, value): text = "Slider value: {v}".format(v=value) self.label.setText(text) # nový widget bude odvozen od obecného hlavního okna class MainWindow(QtGui.QMainWindow): 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(450, 300) self.setWindowTitle("QSlider") # vložení komponenty do okna self.setCentralWidget(MainWindowContent()) def run(self, app): # zobrazení okna na obrazovce self.show() # vstup do smyčky událostí (event loop) app.exec_() def main(): app = QtGui.QApplication(sys.argv) MainWindow().run(app) if __name__ == '__main__': main()
14. Devátý demonstrační příklad: zobrazení různých typů značek u horizontálních posuvníků
Posuvníky se od klasických scrollbarů odlišují především tím, že je u nich možné zobrazit (osové) značky. Ty lze umístit na libovolnou stranu posuvníku, popř. na obě strany. Pro nastavení stylů značek se používá metoda QSlider.setTickPosition(), které se předá jedna z konstant:
- QtGui.QSlider.NoTicks
- QtGui.QSlider.TicksBothSides
- QtGui.QSlider.TicksAbove
- QtGui.QSlider.TicksBelow
- QtGui.QSlider.TicksLeft
- QtGui.QSlider.TicksRight
Všechny kombinace můžete vidět na dalším screenshotu:
Obrázek 8: Horizontální posuvníky s různými kombinacemi značek.
Opět se podívejme na zdrojový kód příkladu, po jehož spuštění se zobrazí různé typy značek u posuvníků:
#!/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 MainWindowContent(QtGui.QWidget): def __init__(self): # zavoláme konstruktor předka super(MainWindowContent, self).__init__() # konfigurace GUI + přidání widgetu do okna self.prepareGUI() def prepareGUI(self): # vytvoření widgetů vkládaných do okna quitButton = self.prepareQuitButton() # vytvoření správce geometrie layout = QtGui.QVBoxLayout() # umístění widgetů do okna tickPositions = ( QtGui.QSlider.NoTicks, QtGui.QSlider.TicksBothSides, QtGui.QSlider.TicksAbove, QtGui.QSlider.TicksBelow, QtGui.QSlider.TicksLeft, QtGui.QSlider.TicksRight ) for tickPosition in tickPositions: layout.addWidget(QtGui.QLabel(str(tickPosition))) layout.addWidget(self.prepareSlider(tickPosition)) layout.addWidget(quitButton) # nastavení správce geometrie a vložení všech komponent do okna self.setLayout(layout) def prepareQuitButton(self): # tlačítko quitButton = QtGui.QPushButton('Quit', self) quitButton.resize(quitButton.sizeHint()) # navázání akce na signál quitButton.clicked.connect(QtCore.QCoreApplication.instance().quit) return quitButton def prepareSlider(self, tickPosition): # vytvoření slideru slider = QtGui.QSlider(QtCore.Qt.Horizontal) slider.setTickInterval(4) slider.setValue(1) slider.setTickPosition(tickPosition) return slider # nový widget bude odvozen od obecného hlavního okna class MainWindow(QtGui.QMainWindow): 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(450, 450) self.setWindowTitle("QSlider") # vložení komponenty do okna self.setCentralWidget(MainWindowContent()) def run(self, app): # zobrazení okna na obrazovce self.show() # vstup do smyčky událostí (event loop) app.exec_() def main(): app = QtGui.QApplication(sys.argv) MainWindow().run(app) if __name__ == '__main__': main()
15. Desátý demonstrační příklad: svislé (vertikální) posuvníky s různým typem značek
Nepatrnou úpravou předchozího příkladu je možné zabezpečit zobrazení vertikálních posuvníků s různými typy značek:
tickPositions = ( QtGui.QSlider.NoTicks, QtGui.QSlider.TicksBothSides, QtGui.QSlider.TicksAbove, QtGui.QSlider.TicksBelow, QtGui.QSlider.TicksLeft, QtGui.QSlider.TicksRight ) for tickPosition in tickPositions: subLayout.addWidget(self.prepareSlider(tickPosition))
Metoda pro vytvoření posuvníku vypadá takto:
def prepareSlider(self, tickPosition): # vytvoření slideru slider = QtGui.QSlider(QtCore.Qt.Vertical) slider.setTickInterval(4) slider.setValue(1) slider.setTickPosition(tickPosition) return slider
Obrázek 9: Vertikální posuvníky s různými kombinacemi značek.
Zdrojový kód dnešního desátého demonstračního příkladu vypadá takto:
#!/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 MainWindowContent(QtGui.QWidget): def __init__(self): # zavoláme konstruktor předka super(MainWindowContent, self).__init__() # konfigurace GUI + přidání widgetu do okna self.prepareGUI() def prepareGUI(self): # vytvoření widgetů vkládaných do okna quitButton = self.prepareQuitButton() # vytvoření prvního správce geometrie topLayout = QtGui.QVBoxLayout() # vytvoření druhého správce geometrie subLayout = QtGui.QHBoxLayout() # umístění widgetů do okna tickPositions = ( QtGui.QSlider.NoTicks, QtGui.QSlider.TicksBothSides, QtGui.QSlider.TicksAbove, QtGui.QSlider.TicksBelow, QtGui.QSlider.TicksLeft, QtGui.QSlider.TicksRight ) for tickPosition in tickPositions: subLayout.addWidget(self.prepareSlider(tickPosition)) topLayout.addLayout(subLayout) topLayout.addWidget(quitButton) # nastavení správce geometrie a vložení všech komponent do okna self.setLayout(topLayout) def prepareQuitButton(self): # tlačítko quitButton = QtGui.QPushButton('Quit', self) quitButton.resize(quitButton.sizeHint()) # navázání akce na signál quitButton.clicked.connect(QtCore.QCoreApplication.instance().quit) return quitButton def prepareSlider(self, tickPosition): # vytvoření slideru slider = QtGui.QSlider(QtCore.Qt.Vertical) slider.setTickInterval(4) slider.setValue(1) slider.setTickPosition(tickPosition) return slider # nový widget bude odvozen od obecného hlavního okna class MainWindow(QtGui.QMainWindow): 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(450, 300) self.setWindowTitle("QSlider") # vložení komponenty do okna self.setCentralWidget(MainWindowContent()) def run(self, app): # zobrazení okna na obrazovce self.show() # vstup do smyčky událostí (event loop) app.exec_() def main(): app = QtGui.QApplication(sys.argv) MainWindow().run(app) if __name__ == '__main__': main()
16. Ovládací prvek QDial a způsob jeho použití
Od třídy QAbstractSlider je odvozen i další ovládací prvek nazvaný příhodně QDial. Skutečně se jedná o prvek, který se podobá potenciometru či laditelnému kondenzátoru na rádiích (v dnešních audio zařízeních ovšem i otočné prvky mívají díky použití inkrementálního snímače digitální výstup). Vlastnosti QDialu se podobají klasickému slideru, samozřejmě až na odlišný vzhled. I způsob vytvoření popř. registrace handleru událostí je shodný:
def prepareDial(self): # vytvoření widget dial = QtGui.QDial() dial.valueChanged.connect(self.onDialValueChanged) return dial
Obrázek 10: Otočný ovládací prvek QDial.
17. Zobrazení widgetu QDial na ploše hlavního okna aplikace
Podívejme se nyní na způsob použití otočného ovládacího prvku. Ze zdrojového kódu je patrné, že se tento widget používá prakticky stejně, jako klasický posuvník, samozřejmě až na vizuální rozdíly. Otočný prvek sice v okně zabírá mnohem více místa, ovšem pro některé účely a pro některé typy aplikací to nemusí být kritické (například u hudebních aplikací je použití otočného prvku mnohem přirozenější):
#!/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 MainWindowContent(QtGui.QWidget): def __init__(self): # zavoláme konstruktor předka super(MainWindowContent, self).__init__() # konfigurace GUI + přidání widgetu do okna self.prepareGUI() def prepareGUI(self): # vytvoření widgetů vkládaných do okna self.label = QtGui.QLabel("") dial = self.prepareDial() quitButton = self.prepareQuitButton() # vytvoření správce geometrie layout = QtGui.QVBoxLayout() # umístění widgetů do okna layout.addWidget(self.label) layout.addWidget(dial) layout.addWidget(quitButton) # nastavení správce geometrie a vložení všech komponent do okna self.setLayout(layout) def prepareQuitButton(self): # tlačítko quitButton = QtGui.QPushButton('Quit', self) quitButton.resize(quitButton.sizeHint()) # navázání akce na signál quitButton.clicked.connect(QtCore.QCoreApplication.instance().quit) return quitButton def prepareDial(self): # vytvoření widget dial = QtGui.QDial() dial.valueChanged.connect(self.onDialValueChanged) return dial def onDialValueChanged(self, value): text = "Dial value: {v}".format(v=value) self.label.setText(text) # nový widget bude odvozen od obecného hlavního okna class MainWindow(QtGui.QMainWindow): 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(450, 450) self.setWindowTitle("QDial") # vložení komponenty do okna self.setCentralWidget(MainWindowContent()) def run(self, app): # zobrazení okna na obrazovce self.show() # vstup do smyčky událostí (event loop) app.exec_() 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 tomu bylo i v předchozích článcích, uloženy do Git repositáře dostupného na adrese https://github.com/tisnik/presentations. Pokud nechcete klonovat celý repositář, můžete namísto toho použít odkazy na jednotlivé příklady, které naleznete v následující tabulce:
19. Odkazy na Internetu
- PySide 1.2.1 documentation
https://pyside.github.io/docs/pyside/index.html - QAbstractSlider
https://pyside.github.io/docs/pyside/PySide/QtGui/AbstractSlider.html - QScrollBar
https://pyside.github.io/docs/pyside/PySide/QtGui/ScrollBar.html - QSlider
https://pyside.github.io/docs/pyside/PySide/QtGui/Slider.html - QDial
https://pyside.github.io/docs/pyside/PySide/QtGui/Dial.html - QImage
https://pyside.github.io/docs/pyside/PySide/QtGui/QImage.html - QPixmap
https://pyside.github.io/docs/pyside/PySide/QtGui/QPixmap.html - QBitmap
https://pyside.github.io/docs/pyside/PySide/QtGui/QBitmap.html - QPaintDevice
https://pyside.github.io/docs/pyside/PySide/QtGui/QPaintDevice.html - QPicture
https://pyside.github.io/docs/pyside/PySide/QtGui/QPicture.html - QPainter
https://pyside.github.io/docs/pyside/PySide/QtGui/QPainter.html - QPainterPath
https://pyside.github.io/docs/pyside/PySide/QtGui/QPainterPath.html - QGradient
https://pyside.github.io/docs/pyside/PySide/QtGui/QGradient.html - QLinearGradient
https://pyside.github.io/docs/pyside/PySide/QtGui/QLinearGradient.html - QRadialGradient
https://pyside.github.io/docs/pyside/PySide/QtGui/QRadialGradient.html - QTableWidget
https://pyside.github.io/docs/pyside/PySide/QtGui/QTableWidget.html - QTableWidgetItem
https://pyside.github.io/docs/pyside/PySide/QtGui/QTableWidgetItem.html - QTreeWidget
https://pyside.github.io/docs/pyside/PySide/QtGui/QTreeWidget.html - QTreeWidgetItem
https://pyside.github.io/docs/pyside/PySide/QtGui/QTreeWidgetItem.html - Afinní zobrazení
https://cs.wikipedia.org/wiki/Afinn%C3%AD_zobrazen%C3%AD - 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/ - Drawing in PySide
http://zetcode.com/gui/pysidetutorial/drawing/ - Qt Core
https://pyside.github.io/docs/pyside/PySide/QtCore/Qt.html - QLayout
https://pyside.github.io/docs/pyside/PySide/QtGui/QLayout.html - QValidator
https://pyside.github.io/docs/pyside/PySide/QtGui/QValidator.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 - QAction
https://pyside.github.io/docs/pyside/PySide/QtGui/QAction.html - QMessageBox
https://pyside.github.io/docs/pyside/PySide/QtGui/QMessageBox.html - QListWidget
https://pyside.github.io/docs/pyside/PySide/QtGui/QListWidget.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/ - QLineEdit
https://pyside.github.io/docs/pyside/PySide/QtGui/QLineEdit.html - QTextEdit
https://pyside.github.io/docs/pyside/PySide/QtGui/QTextEdit.html - QValidator
https://pyside.github.io/docs/pyside/PySide/QtGui/QValidator.html - QIntValidator
https://pyside.github.io/docs/pyside/PySide/QtGui/QIntValidator.html - QRegExpValidator
https://pyside.github.io/docs/pyside/PySide/QtGui/QRegExpValidator.html - QWidget
https://pyside.github.io/docs/pyside/PySide/QtGui/QWidget.html - QMainWindow
https://pyside.github.io/docs/pyside/PySide/QtGui/QMainWindow.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/