Obsah
1. Práce s vektorovým formátem SVG ve frameworku PySide
2. Pomocný skript určený pro vytvoření vektorového obrázku uloženého ve formátu SVG
3. Třída QSvgWidget představující prvek GUI pro zobrazení SVG
4. První demonstrační příklad – použití třídy QSvgWidget pro zobrazení vektorové kresby
5. SVG výkres uložený v poli bajtů
6. Druhý demonstrační příklad: použití SVG uloženého v poli bajtů
7. Formát SVG použitý pro uložení vektorových ikon
8. Třetí demonstrační příklad – tlačítka s vektorovými ikonami
9. Změna rozměrů vektorových ikon
10. Čtvrtý demonstrační příklad – vektorové ikony s pevně nastavenými rozměry
11. Vektorové ikony ve standardních dialozích
12. Pátý demonstrační příklad – standardní dialog s vektorovou ikonou
13. Použití SVG při vykreslování rastrové i vektorové grafiky s použitím třídy QPainter
14. Rasterizace SVG – třída QSvgRenderer
15. Šestý demonstrační příklad – vykreslení SVG do obrázku typu QImage
16. Repositář s demonstračními příklady
17. Články o možnostech a vlastnostech formátu SVG
1. Práce s vektorovým formátem SVG ve frameworku PySide
S možnostmi nabízenými populárním vektorovým formátem SVG (Scalable Vector Graphics) jsme se již na stránkách Rootu seznámili, viz též odkazy na příslušné články, které najdete v sedmnácté kapitole. Formát SVG, resp. obecněji řečeno vektorová grafika, je ve frameworku PySide podporována, a to dokonce na několika úrovních. V první řadě je podporováno vykreslení (rasterizace) vektorové kresby do bitmapového obrázku typu QBitmap či mnohem častěji do QPixmap. To je sice pro některé typy aplikací skutečně užitečná vlastnost, ovšem zajímavější a praktičtější je možnost použít vektorové kresby uložené ve formátu SVG i při vykreslování jednotlivých ovládacích prvků (widgetů). Je tomu tak z toho důvodu, že aplikace může být provozována na zařízeních s různou velikostí a rozlišením displeje, od starších smartphonů až po desktopy.
Na tomto místě je ovšem nutno říci, že ze striktního pohledu PySide sice není celé postaveno na vektorové grafice a tudíž GUI nemusí být plně škálovatelné, ale pokud se všechny bitmapové ikony nahradí za SVG (což již možné je, a to dokonce velmi jednoduše), lze zbytek grafického uživatelského rozhraní nastylovat způsobem, který jsme si popsali již v předchozích dvou článcích [1] [2].
2. Pomocný skript určený pro vytvoření vektorového obrázku uloženého ve formátu SVG
Ještě předtím, než si popíšeme, jakým způsobem je možné vektorové kresby uložené ve formátu SVG použít v grafickém uživatelském rozhraní, si ukažme jednoduchý skript (napsaný samozřejmě v Pythonu), který ukázkovou vektorovou kresbu vytvoří. Tento skript nepoužívá žádnou specializovanou knihovnu, ale generuje SVG řádek po řádku jen s využitím základních nástrojů pro formátování řetězců. Následuje výpis zdrojového kódu tohoto krátkého skriptu:
#!/usr/bin/env python # vim: set fileencoding=utf-8 from math import sin, cos def main(): size = 480 with open("logo.svg", "w") as fout: fout.write("<svg xmlns='http://www.w3.org/2000/svg' version='1.1' width='{w}' height='{h}'>\n".format(w=size, h=size)) green = 255 for i, r, red, blue in zip(range(0, 128), range(128, 0, -1), range(255, 0, -2), range(0, 256, 2)): a = i / 12.0 b = i + 80.0 x = size / 2 + b * cos(a) y = size / 2 + b * sin(a) p = "<circle cx='{x}' cy='{y}' r='{r}' ".format(x=x, y=y, r=r) q = "fill='rgb({r}, {g}, {b})' style='fill-opacity:.06'/>\n".format(r=red, g=green, b=blue) r = "fill='none' stroke='black'/>\n" fout.write(p+q) fout.write(p+r) fout.write("</svg>\n") if __name__ == '__main__': main()
Obrázek 1: Výsledná kresba uložená do formátu SVG.
Soubor typu SVG vygenerovaný tímto skriptem by měl vypadat přibližně takto (je zobrazen jen jeho začátek a konec):
<svg xmlns='http://www.w3.org/2000/svg' version='1.1' width='480' height='480'> <circle cx='320.0' cy='240.0' r='128' fill='rgb(255, 255, 0)' style='fill-opacity:.06'/> <circle cx='320.0' cy='240.0' r='128' fill='none' stroke='black'/> ... ... ... </svg>
3. Třída QSvgWidget představující prvek GUI pro zobrazení SVG
Nyní se podívejme na způsob zobrazení vektorové kresby v grafickém uživatelském rozhraní. Pro tento účel existuje ve frameworku PySide ovládací prvek nazvaný přímočaře QSvgWidget. Vzhledem k tomu, že se jedná o potomka třídy QWidget, znamená to, že je možné tento widget vložit do libovolného okna či dialogu. Vlastní vykreslování SVG je prováděno v závislosti na možnostech konkrétního zařízení: buď čistě softwarově nebo rasterizací na grafickém akcelerátoru. Aby byla třída QSvgWidget užitečná, obsahuje i metodu load() určenou pro načtení kresby ve formátu SVG, a to buď ze souboru nebo z pole bajtů, které je ve frameworku PySide reprezentováno objekty typu QByteArray.
Nejprve si ukážeme, jakým způsobem se načte SVG z externího souboru. Díky existenci výše zmíněné metody load() je to triviální. Funkce, která vytvoří objekt typu QSvgWidget a načte do něj externí SVG soubor, může vypadat takto:
def prepareSVGWidget(self, filename): svgWidget = QtSvg.QSvgWidget() svgWidget.load(filename) return svgWidget
Umístění tohoto widgetu do okna s dalšími prvky grafického uživatelského rozhraní se provádí naprosto stejným způsobem jako u všech dalších ovládacích prvků:
def prepareGUI(self): # tlačítka, na které je navázán handler quitButton = self.prepareQuitButton() # widget s vektorovým obrázkem svgWidget = self.prepareSVGWidget("logo.svg") # vytvoření správců geometrie topLayout = QtGui.QVBoxLayout() # vložení widgetů do okna topLayout.addWidget(svgWidget) topLayout.addWidget(QtGui.QLabel("")) topLayout.addWidget(quitButton) # nastavení správce geometrie a vložení všech komponent do okna self.setLayout(topLayout)
Obrázek 2: Ovládací prvek typu QSvgWidget vložený do hlavního okna aplikace.
4. První demonstrační příklad – použití třídy QSvgWidget pro zobrazení vektorové kresby
Výše uvedené úryvky kódu určeného pro načtení a následné zobrazení kresby uložené ve formátu SVG jsou použity v dnešním prvním demonstračním příkladu, jehož úplný zdrojový kód můžete vidět pod tímto odstavcem. Po zobrazení hlavního okna aplikace se pokuste změnit jeho velikost a zjistit, zda a jakým způsobem tento widget reaguje na změnu své velikosti:
#!/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 from PySide import QtSvg # 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): # tlačítka, na které je navázán handler quitButton = self.prepareQuitButton() # widget s vektorovým obrázkem svgWidget = self.prepareSVGWidget("logo.svg") # vytvoření správců geometrie topLayout = QtGui.QVBoxLayout() # vložení widgetů do okna topLayout.addWidget(svgWidget) topLayout.addWidget(QtGui.QLabel("")) topLayout.addWidget(quitButton) # nastavení správce geometrie a vložení všech komponent do okna self.setLayout(topLayout) def prepareSVGWidget(self, filename): svgWidget = QtSvg.QSvgWidget() svgWidget.load(filename) return svgWidget def prepareQuitButton(self): # tlačítko s popisem 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): # velikost není potřeba specifikovat # self.resize(400, 300) self.setWindowTitle("Custom Stylesheets") # hlavní menu menubar = self.menuBar() # příkaz File/Quit fileQuitItem = QtGui.QAction(QtGui.QIcon('icons/application-exit.png'), '&Quit', self) fileQuitItem.triggered.connect(self.close) fileQuitItem.setStatusTip('Quit the application') fileQuitItem.setShortcut('Ctrl+Q') # položka File v hlavním menu fileMenu = menubar.addMenu('&File') fileMenu.addAction(fileQuitItem) # tlačítko Quit quitAction = QtGui.QAction(QtGui.QIcon('icons/application-exit.png'), '&Quit', self) quitAction.triggered.connect(self.close) quitAction.setStatusTip('Quit the application') # tlačítko About aboutAction = QtGui.QAction(QtGui.QIcon('icons/dialog-information.png'), '&About', self) aboutAction.triggered.connect(self.aboutDialog) aboutAction.setStatusTip('About this application') # nástrojový pruh self.toolbar = self.addToolBar('title') # přidání tlačítek na nástrojový pruh self.toolbar.addAction(quitAction) self.toolbar.addAction(aboutAction) # vložení komponenty do okna self.setCentralWidget(MainWindowContent()) def aboutDialog(self): msgBox = QtGui.QMessageBox() msgBox.setText('About:\n...\n...\n...') msgBox.setIcon(QtGui.QMessageBox.Information) msgBox.exec_() def run(self, app): # zobrazení okna na obrazovce self.show() # vstup do smyčky událostí (event loop) app.exec_() def main(): QtGui.QApplication.setStyle("plastique") app = QtGui.QApplication(sys.argv) MainWindow().run(app) if __name__ == '__main__': main()
5. SVG výkres uložený v poli bajtů
Z předchozího textu již víme, jak lze provést načtení kresby přímo z externího souboru metodou QSvgWidget.load(). Existuje však ještě jedna varianta – získání kresby z pole bajtů, které je ve frameworku PySide reprezentováno objekty typu QByteArray. Tato třída je v některých případech a některými programátory (často například C++kaři) používána například při psaní kódu, v němž se sdílí větší bloky dat. Je tomu tak z toho důvodu, že QByteArray podporuje takzvané implicitní sdílení neboli copy-on-write, takže v případě, že se data používaná na více místech nemění, nedochází k jejich zbytečnému klonování. Ovšem pole bajtů se může hodit i v dalších oblastech, například tehdy, kdy je nutné modifikovat znaky v delší řetězci (klasické řetězce v Pythonu jsou totiž neměnitelné, takže některé operace s nimi nemusí být příliš efektivní).
Nicméně se podívejme na (poněkud umělý) příklad, v němž se vektorový obrázek nejdříve načte do pole bajtů a teprve poté se toto pole bajtů předá do metody QSvgWidget.load():
def prepareSVGWidget(self): svgWidget = QtSvg.QSvgWidget() content = QtCore.QByteArray(""" <svg xmlns='http://www.w3.org/2000/svg' version='1.1' width='480' height='480'> <circle cx='320.0' cy='240.0' r='128' fill='rgb(0, 255, 255)' style='fill-opacity:.25'/> <circle cx='320.0' cy='240.0' r='128' fill='none' stroke='black'/> <circle cx='254.891453105' cy='335.850115412' r='111' fill='rgb(221, 34, 255)' style='fill-opacity:.25'/> <circle cx='254.891453105' cy='335.850115412' r='111' fill='none' stroke='black'/> <circle cx='140.39542436' cy='291.214534183' r='96' fill='rgb(255, 191, 64)' style='fill-opacity:.25'/> <circle cx='140.39542436' cy='291.214534183' r='96' fill='none' stroke='black'/> </svg> """) svgWidget.load(content) return svgWidget
Obrázek 3: Druhý demonstrační příklad s jednoduchou vektorovou kresbou.
Poznámka: ve skutečnosti většinou není explicitní použití objektu typu QByteArray nezbytné, protože je možné přímo použít typy bytes popř. bytearray. Bližší informace o tom, jak jsou mapovány nativní typy Pythonu na objekty v PySide, lze v případě potřeby nalézt v dokumentu psep-0101.txt (jedná se o užitečný dokument, zejména ve chvíli, kdy je nutné přepsat nějakou část programu psanou v C++ a Qt do PySide).
6. Druhý demonstrační příklad: použití SVG uloženého v poli bajtů
Opět si, nyní pouze pro úplnost, ukažme, jakým způsobem je možné tento kód použít v ucelenější aplikaci. Aby nebyl kód příliš dlouhý, obsahuje SVG výkres použitý ve druhém demonstračním příkladu jen několik geometrických entit:
#!/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 from PySide import QtSvg # 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): # tlačítka, na které je navázán handler quitButton = self.prepareQuitButton() # widget s vektorovým obrázkem svgWidget = self.prepareSVGWidget() # vytvoření správců geometrie topLayout = QtGui.QVBoxLayout() # vložení widgetů do okna topLayout.addWidget(svgWidget) topLayout.addWidget(QtGui.QLabel("")) topLayout.addWidget(quitButton) # nastavení správce geometrie a vložení všech komponent do okna self.setLayout(topLayout) def prepareSVGWidget(self): svgWidget = QtSvg.QSvgWidget() content = QtCore.QByteArray(""" <svg xmlns='http://www.w3.org/2000/svg' version='1.1' width='480' height='480'> <circle cx='320.0' cy='240.0' r='128' fill='rgb(0, 255, 255)' style='fill-opacity:.25'/> <circle cx='320.0' cy='240.0' r='128' fill='none' stroke='black'/> <circle cx='254.891453105' cy='335.850115412' r='111' fill='rgb(221, 34, 255)' style='fill-opacity:.25'/> <circle cx='254.891453105' cy='335.850115412' r='111' fill='none' stroke='black'/> <circle cx='140.39542436' cy='291.214534183' r='96' fill='rgb(255, 191, 64)' style='fill-opacity:.25'/> <circle cx='140.39542436' cy='291.214534183' r='96' fill='none' stroke='black'/> </svg> """) svgWidget.load(content) return svgWidget def prepareQuitButton(self): # tlačítko s popisem 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): # velikost není potřeba specifikovat # self.resize(400, 300) self.setWindowTitle("Custom Stylesheets") # hlavní menu menubar = self.menuBar() # příkaz File/Quit fileQuitItem = QtGui.QAction(QtGui.QIcon('icons/application-exit.png'), '&Quit', self) fileQuitItem.triggered.connect(self.close) fileQuitItem.setStatusTip('Quit the application') fileQuitItem.setShortcut('Ctrl+Q') # položka File v hlavním menu fileMenu = menubar.addMenu('&File') fileMenu.addAction(fileQuitItem) # tlačítko Quit quitAction = QtGui.QAction(QtGui.QIcon('icons/application-exit.png'), '&Quit', self) quitAction.triggered.connect(self.close) quitAction.setStatusTip('Quit the application') # tlačítko About aboutAction = QtGui.QAction(QtGui.QIcon('icons/dialog-information.png'), '&About', self) aboutAction.triggered.connect(self.aboutDialog) aboutAction.setStatusTip('About this application') # nástrojový pruh self.toolbar = self.addToolBar('title') # přidání tlačítek na nástrojový pruh self.toolbar.addAction(quitAction) self.toolbar.addAction(aboutAction) # vložení komponenty do okna self.setCentralWidget(MainWindowContent()) def aboutDialog(self): msgBox = QtGui.QMessageBox() msgBox.setText('About:\n...\n...\n...') msgBox.setIcon(QtGui.QMessageBox.Information) msgBox.exec_() def run(self, app): # zobrazení okna na obrazovce self.show() # vstup do smyčky událostí (event loop) app.exec_() def main(): QtGui.QApplication.setStyle("plastique") app = QtGui.QApplication(sys.argv) MainWindow().run(app) if __name__ == '__main__': main()
7. Formát SVG použitý pro uložení vektorových ikon
Další forma podpory formátu SVG ve frameworku PySide se již přímo dotýká grafického uživatelského rozhraní. PySide totiž podporuje načítání vektorových obrázků do ikon, přesněji řečeno do instancí třídy QIcon. Je to opět velmi jednoduché – pokud se konstruktoru QIcon předá jméno souboru se SVG výkresem, je výkres či kresba načtena, rasterizována a použita jako ikona. Ukažme si to na příkladu pomocné metody sloužící pro vytvoření tlačítka typu QPushButton s ikonou. Tlačítko se nejdříve běžným způsobem vytvoří a následně se mu přiřadí ikona:
def prepareButtonWithIcon(self, label, filename): icon = QtGui.QIcon(filename) button = QtGui.QPushButton(label) button.setIcon(icon) return button
Voláním této metody můžeme vytvořit tlačítka s ikonami a názvy tří populárních textových editorů:
# widgety s vektorovým obrázkem vimButton = self.prepareButtonWithIcon("Vim", "editors/vim.svg") emacsButton = self.prepareButtonWithIcon("Emacs", "editors/emacs.svg") atomButton = self.prepareButtonWithIcon("Atom", "editors/atom.svg")
Následně můžeme tato tlačítka vložit do hlavního okna aplikace:
# vytvoření správců geometrie topLayout = QtGui.QVBoxLayout() # vložení widgetů do okna topLayout.addWidget(QtGui.QLabel("Select editor:")) topLayout.addWidget(vimButton) topLayout.addWidget(emacsButton) topLayout.addWidget(atomButton) topLayout.addWidget(QtGui.QLabel("")) topLayout.addWidget(quitButton) # nastavení správce geometrie a vložení všech komponent do okna self.setLayout(topLayout)
Výsledek může vypadat například takto (v závislosti na použitém stylu):
Obrázek 4: Třetí demonstrační příklad s vektorovými ikonami.
8. Třetí demonstrační příklad – tlačítka s vektorovými ikonami
V dnešním třetím demonstračním příkladu je ukázáno, jak je možné použít formát SVG pro uložení vektorových ikon, které jsou následně zobrazeny u tlačítek typu QPushButton. Povšimněte si, že použití vektorových ikon je prakticky stejně snadné jako použití ikon reprezentovaných rastrovými obrázky:
#!/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 from PySide import QtSvg # 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): # tlačítka, na které je navázán handler quitButton = self.prepareQuitButton() # widgety s vektorovým obrázkem vimButton = self.prepareButtonWithIcon("Vim", "editors/vim.svg") emacsButton = self.prepareButtonWithIcon("Emacs", "editors/emacs.svg") atomButton = self.prepareButtonWithIcon("Atom", "editors/atom.svg") # vytvoření správců geometrie topLayout = QtGui.QVBoxLayout() # vložení widgetů do okna topLayout.addWidget(QtGui.QLabel("Select editor:")) topLayout.addWidget(vimButton) topLayout.addWidget(emacsButton) topLayout.addWidget(atomButton) topLayout.addWidget(QtGui.QLabel("")) topLayout.addWidget(quitButton) # nastavení správce geometrie a vložení všech komponent do okna self.setLayout(topLayout) def prepareButtonWithIcon(self, label, filename): icon = QtGui.QIcon(filename) button = QtGui.QPushButton(label) button.setIcon(icon) return button def prepareQuitButton(self): # tlačítko s popisem 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): # velikost není potřeba specifikovat # self.resize(400, 300) self.setWindowTitle("Custom Stylesheets") # hlavní menu menubar = self.menuBar() # příkaz File/Quit fileQuitItem = QtGui.QAction(QtGui.QIcon('icons/application-exit.png'), '&Quit', self) fileQuitItem.triggered.connect(self.close) fileQuitItem.setStatusTip('Quit the application') fileQuitItem.setShortcut('Ctrl+Q') # položka File v hlavním menu fileMenu = menubar.addMenu('&File') fileMenu.addAction(fileQuitItem) # tlačítko Quit quitAction = QtGui.QAction(QtGui.QIcon('icons/application-exit.png'), '&Quit', self) quitAction.triggered.connect(self.close) quitAction.setStatusTip('Quit the application') # tlačítko About aboutAction = QtGui.QAction(QtGui.QIcon('icons/dialog-information.png'), '&About', self) aboutAction.triggered.connect(self.aboutDialog) aboutAction.setStatusTip('About this application') # nástrojový pruh self.toolbar = self.addToolBar('title') # přidání tlačítek na nástrojový pruh self.toolbar.addAction(quitAction) self.toolbar.addAction(aboutAction) # vložení komponenty do okna self.setCentralWidget(MainWindowContent()) def aboutDialog(self): msgBox = QtGui.QMessageBox() msgBox.setText('About:\n...\n...\n...') msgBox.setIcon(QtGui.QMessageBox.Information) msgBox.exec_() def run(self, app): # zobrazení okna na obrazovce self.show() # vstup do smyčky událostí (event loop) app.exec_() def main(): QtGui.QApplication.setStyle("plastique") app = QtGui.QApplication(sys.argv) MainWindow().run(app) if __name__ == '__main__': main()
9. Změna rozměrů vektorových ikon
Nevýhodou předchozího příkladu bylo to, že ikony umístěné na tlačítkách, byly dosti malé, a to přesto, že vektorové výkresy/kresby je možné v případě potřeby prakticky libovolně škálovat (zvětšovat, zmenšovat, rotovat atd.). Pokud budeme chtít změnit rozměr ikon na tlačítkách, je nutné použít metodu setIconSize, která ovšem nepřísluší třídě QIcon, ale třídě QPushButton (zcela přesně řečeno třídě QAbstractButton). Podívejme se tedy, jak lze ikony zvětšit na rozměry 80×80 pixelů, kde již budou loga textových editorů jasně rozpoznatelná:
def prepareButtonWithIcon(self, label, filename): icon = QtGui.QIcon(filename) button = QtGui.QPushButton(label) button.setIcon(icon) button.setIconSize(QtCore.QSize(80, 80)) return button
Obrázek 5: Čtvrtý demonstrační příklad s vektorovými ikonami, u nichž byly explicitně nastaveny rozměry.
10. Čtvrtý demonstrační příklad – vektorové ikony s pevně nastavenými rozměry
Opět si ukažme úplný zdrojový kód (dnes již čtvrtého) demonstračního příkladu, tentokrát pro situaci, kdy budeme vyžadovat zobrazení tlačítek s ikonami o rozměrech 80×80 pixelů:
#!/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 from PySide import QtSvg # 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): # tlačítka, na které je navázán handler quitButton = self.prepareQuitButton() # widgety s vektorovým obrázkem vimButton = self.prepareButtonWithIcon("Vim", "editors/vim.svg") emacsButton = self.prepareButtonWithIcon("Emacs", "editors/emacs.svg") atomButton = self.prepareButtonWithIcon("Atom", "editors/atom.svg") # vytvoření správců geometrie topLayout = QtGui.QVBoxLayout() # vložení widgetů do okna topLayout.addWidget(QtGui.QLabel("Select editor:")) topLayout.addWidget(vimButton) topLayout.addWidget(emacsButton) topLayout.addWidget(atomButton) topLayout.addWidget(QtGui.QLabel("")) topLayout.addWidget(quitButton) # nastavení správce geometrie a vložení všech komponent do okna self.setLayout(topLayout) def prepareButtonWithIcon(self, label, filename): icon = QtGui.QIcon(filename) button = QtGui.QPushButton(label) button.setIcon(icon) button.setIconSize(QtCore.QSize(80, 80)) return button def prepareQuitButton(self): # tlačítko s popisem 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): # velikost není potřeba specifikovat # self.resize(400, 300) self.setWindowTitle("Custom Stylesheets") # hlavní menu menubar = self.menuBar() # příkaz File/Quit fileQuitItem = QtGui.QAction(QtGui.QIcon('icons/application-exit.png'), '&Quit', self) fileQuitItem.triggered.connect(self.close) fileQuitItem.setStatusTip('Quit the application') fileQuitItem.setShortcut('Ctrl+Q') # položka File v hlavním menu fileMenu = menubar.addMenu('&File') fileMenu.addAction(fileQuitItem) # tlačítko Quit quitAction = QtGui.QAction(QtGui.QIcon('icons/application-exit.png'), '&Quit', self) quitAction.triggered.connect(self.close) quitAction.setStatusTip('Quit the application') # tlačítko About aboutAction = QtGui.QAction(QtGui.QIcon('icons/dialog-information.png'), '&About', self) aboutAction.triggered.connect(self.aboutDialog) aboutAction.setStatusTip('About this application') # nástrojový pruh self.toolbar = self.addToolBar('title') # přidání tlačítek na nástrojový pruh self.toolbar.addAction(quitAction) self.toolbar.addAction(aboutAction) # vložení komponenty do okna self.setCentralWidget(MainWindowContent()) def aboutDialog(self): msgBox = QtGui.QMessageBox() msgBox.setText('About:\n...\n...\n...') msgBox.setIcon(QtGui.QMessageBox.Information) msgBox.exec_() def run(self, app): # zobrazení okna na obrazovce self.show() # vstup do smyčky událostí (event loop) app.exec_() def main(): QtGui.QApplication.setStyle("plastique") app = QtGui.QApplication(sys.argv) MainWindow().run(app) if __name__ == '__main__': main()
11. Vektorové ikony ve standardních dialozích
Vektorové ikony načtené ze souborů SVG se samozřejmě mohou použít i ve standardních dialozích namísto výchozích ikon. V tomto případě ale musíme postupovat ve více krocích a vlastně provést rasterizaci explicitně. Nejdříve načteme příslušný SVG soubor a vytvoříme z něho ikonu:
# načtení ikony icon = QtGui.QIcon(filename)
Dále provedeme rasterizaci do bitmapy či pixmapy o zadané velikosti:
# vytvoření pixmapy a její nastavení jako ikony pro dialog pixmap = icon.pixmap(200, 200)
Následně ikonu přiřadíme příslušnému dialogu:
# vytvoření dialogu msgBox = QtGui.QMessageBox() msgBox.setIconPixmap(pixmap)
Celá funkce, která vytvoří standardní dialog, nakonfiguruje u něj tlačítka (zde jedno tlačítko Ok), načte do něj vektorovou ikonu a dialog následně zobrazí, může vypadat takto:
def showMessageBox(self, filename): # tlačítko, která mají být součástí dialogu buttons = QtGui.QMessageBox.Ok # vytvoření dialogu msgBox = QtGui.QMessageBox() # nastavení zprávy a ikony, která se má zobrazit vedle zprávy msgBox.setStandardButtons(buttons) msgBox.setText(u'') # načtení ikony icon = QtGui.QIcon(filename) # vytvoření pixmapy a její nastavení jako ikony pro dialog pixmap = icon.pixmap(200, 200) msgBox.setIconPixmap(pixmap) # zobrazení dialogu msgBox.exec_()
Obrázek 6: Standardní dialog vytvořený v pátém demonstračním příkladu.
Obrázek 7: Standardní dialog vytvořený v pátém demonstračním příkladu.
Obrázek 8: Standardní dialog vytvořený v pátém demonstračním příkladu.
12. Pátý demonstrační příklad – standardní dialog s vektorovou ikonou
Způsob vytvoření dialogu, v němž je použita vektorová ikona, je ukázán v dnešním pátém demonstračním příkladu, jehož zdrojový kód je zobrazen pod tímto odstavcem:
#!/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 from PySide import QtSvg # 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): # tlačítka, na které je navázán handler quitButton = self.prepareQuitButton() # widgety s vektorovým obrázkem vimButton = self.prepareButtonWithIcon("Vim", "editors/vim.svg") emacsButton = self.prepareButtonWithIcon("Emacs", "editors/emacs.svg") atomButton = self.prepareButtonWithIcon("Atom", "editors/atom.svg") # vytvoření správců geometrie topLayout = QtGui.QVBoxLayout() # vložení widgetů do okna topLayout.addWidget(QtGui.QLabel("Select editor:")) topLayout.addWidget(vimButton) topLayout.addWidget(emacsButton) topLayout.addWidget(atomButton) topLayout.addWidget(QtGui.QLabel("")) topLayout.addWidget(quitButton) # nastavení správce geometrie a vložení všech komponent do okna self.setLayout(topLayout) def prepareButtonWithIcon(self, label, filename): icon = QtGui.QIcon(filename) button = QtGui.QPushButton(label) button.setIcon(icon) button.setIconSize(QtCore.QSize(40, 40)) # navázání akce na signál button.clicked.connect(lambda :self.showMessageBox(filename)) return button def prepareQuitButton(self): # tlačítko s popisem 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 showMessageBox(self, filename): # tlačítko, která mají být součástí dialogu buttons = QtGui.QMessageBox.Ok # vytvoření dialogu msgBox = QtGui.QMessageBox() # nastavení zprávy a ikony, která se má zobrazit vedle zprávy msgBox.setStandardButtons(buttons) msgBox.setText(u'') # načtení ikony icon = QtGui.QIcon(filename) # vytvoření pixmapy a její nastavení jako ikony pro dialog pixmap = icon.pixmap(200, 200) msgBox.setIconPixmap(pixmap) # zobrazení dialogu msgBox.exec_() # 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): # velikost není potřeba specifikovat # self.resize(400, 300) self.setWindowTitle("Custom Stylesheets") # hlavní menu menubar = self.menuBar() # příkaz File/Quit fileQuitItem = QtGui.QAction(QtGui.QIcon('icons/application-exit.png'), '&Quit', self) fileQuitItem.triggered.connect(self.close) fileQuitItem.setStatusTip('Quit the application') fileQuitItem.setShortcut('Ctrl+Q') # položka File v hlavním menu fileMenu = menubar.addMenu('&File') fileMenu.addAction(fileQuitItem) # tlačítko Quit quitAction = QtGui.QAction(QtGui.QIcon('icons/application-exit.png'), '&Quit', self) quitAction.triggered.connect(self.close) quitAction.setStatusTip('Quit the application') # tlačítko About aboutAction = QtGui.QAction(QtGui.QIcon('icons/dialog-information.png'), '&About', self) aboutAction.triggered.connect(self.aboutDialog) aboutAction.setStatusTip('About this application') # nástrojový pruh self.toolbar = self.addToolBar('title') # přidání tlačítek na nástrojový pruh self.toolbar.addAction(quitAction) self.toolbar.addAction(aboutAction) # vložení komponenty do okna self.setCentralWidget(MainWindowContent()) def aboutDialog(self): msgBox = QtGui.QMessageBox() msgBox.setText('About:\n...\n...\n...') msgBox.setIcon(QtGui.QMessageBox.Information) msgBox.exec_() def run(self, app): # zobrazení okna na obrazovce self.show() # vstup do smyčky událostí (event loop) app.exec_() def main(): QtGui.QApplication.setStyle("plastique") app = QtGui.QApplication(sys.argv) MainWindow().run(app) if __name__ == '__main__': main()
13. Použití SVG při vykreslování rastrové i vektorové grafiky s použitím třídy QPainter
Výkresy či kresby uložené ve formátu SVG je možné použít i při vykreslování rastrové a vektorové grafiky. Již z předchozích článků víme, že vykreslování se provádí s využitím instance třídy QPainter. Připomeňme si jen, jak celý postup vypadá.
Nejdříve se vytvoří instance třídy QImage, do níž se bude vykreslování provádět:
# vytvoření instance třídy QImage self.image = QtGui.QImage(MainWindow.IMAGE_WIDTH, MainWindow.IMAGE_HEIGHT, QtGui.QImage.Format_RGB32)
Následně se získá instance třídy QPainter:
# vytvoření objektu typu QPainter s předáním # reference na "pokreslovaný" objekt qp = QtGui.QPainter(self.image)
Nyní je již možné vykreslit celou scénu, například:
# nastavení barvy kreslení qp.setPen(QtGui.QColor(64, 255, 64)) # vykreslení úsečky qp.drawLine(10, 10, MainWindow.IMAGE_WIDTH-10, MainWindow.IMAGE_HEIGHT-10)
A nakonec získat výsledný rastrový obrázek:
# vytvoření instance třídy QPixmap z objektu QImage return QtGui.QPixmap.fromImage(self.image)
14. Rasterizace SVG – třída QSvgRenderer
Ve chvíli, kdy existuje instance třídy QPainter, je možné načíst a vykreslit i SVG výkres. V tomto případě ale nepoužijeme ani třídu QIcon ani QSvgWidget, ale je nutné použít třídu nazvanou QSvgRenderer. I tato třída dokáže načíst externí výkres/kresbu, ovšem navíc obsahuje i metodu render, které lze předat referenci na objekt typu QPainter. Ve skutečnosti je celé vykreslení jednoduché:
qp = QtGui.QPainter(self.image) ... ... ... renderer = QtSvg.QSvgRenderer("logo.svg") renderer.render(qp) ... ... ... return QtGui.QPixmap.fromImage(self.image)
Obrázek 9: Šestý demonstrační příklad, v níž se do obrázku vykreslila i kresba uložená v SVG.
15. Šestý demonstrační příklad – vykreslení SVG do obrázku typu QImage
V dnešním šestém a současně i posledním demonstračním příkladu je ukázáno, jak je možné vytvořit objekt typu QPainter, vykreslit nějaké geometrické tvary (či text, popř. změnit jednotlivé pixely) a následně ještě rasterizovat obsah SVG výkresu. Zdrojový kód tohoto příkladu je zobrazen pod odstavcem:
#!/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 from PySide import QtSvg # nový widget bude odvozen od obecného hlavního okna class MainWindow(QtGui.QMainWindow): # rozměry rastrového obrázku IMAGE_WIDTH = 256 IMAGE_HEIGHT = 256 def __init__(self): # zavoláme konstruktor předka super(MainWindow, self).__init__() self.prepareImage() # konfigurace GUI + přidání widgetu do okna self.prepareGUI() def prepareImage(self): # vytvoření instance třídy QImage self.image = QtGui.QImage(MainWindow.IMAGE_WIDTH, MainWindow.IMAGE_HEIGHT, QtGui.QImage.Format_RGB32) # vytvoření objektu typu QPainter s předáním # reference na "pokreslovaný" objekt qp = QtGui.QPainter(self.image) # nastavení barvy kreslení qp.setPen(QtGui.QColor(64, 255, 64)) # vykreslení úsečky qp.drawLine(10, 10, MainWindow.IMAGE_WIDTH-10, MainWindow.IMAGE_HEIGHT-10) renderer = QtSvg.QSvgRenderer("logo.svg") renderer.render(qp) # vytvoření instance třídy QPixmap z objektu QImage self.pixmap = QtGui.QPixmap.fromImage(self.image) def prepareGUI(self): # velikost okna nezadávejte ručně - špatně se počítá kvůli toolbaru # self.resize(256, 300) self.setWindowTitle('QPainter') # tlačítko Quit quitAction = QtGui.QAction(QtGui.QIcon('icons/application-exit.png'), '&Quit', self) quitAction.triggered.connect(self.close) quitAction.setStatusTip('Quit the application') quitAction.setShortcut('Ctrl+Q') # nástrojový pruh self.toolbar = self.addToolBar('title') self.toolbar.setMovable(False) # přidání tlačítka na nástrojový pruh self.toolbar.addAction(quitAction) # doprostřed okna přidáme návěští s rastrovým obrázkem self.addLabelWithPixmap() # zobrazení hlavního okna self.show() def addLabelWithPixmap(self): # vytvoření návěští label = QtGui.QLabel("test") # přiřazení rastrového obrázku k návěští label.setPixmap(self.pixmap) # vložení návěští do hlavního okna self.setCentralWidget(label) 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. Repositář s demonstračními příklady
Zdrojové kódy všech šesti dnes popsaných demonstračních příkladů společně s jedním pomocným skriptem 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:
# | Příklad | Adresa |
---|---|---|
1 | 146_svg_widget.py | https://github.com/tisnik/presentations/blob/master/Python_GUI/PySide/146_svg_widget.py |
2 | 147_svg_widget_content.py | https://github.com/tisnik/presentations/blob/master/Python_GUI/PySide/147_svg_widget_content.py |
3 | 148_svg_as_icon.py | https://github.com/tisnik/presentations/blob/master/Python_GUI/PySide/148_svg_as_icon.py |
4 | 149_svg_as_resized_icon.py | https://github.com/tisnik/presentations/blob/master/Python_GUI/PySide/149_svg_as_resized_icon.py |
5 | 150_message_box_with_svg_icon.py | https://github.com/tisnik/presentations/blob/master/Python_GUI/PySide/150_message_box_with_svg_icon.py |
6 | 151_svg_renderer.py | https://github.com/tisnik/presentations/blob/master/Python_GUI/PySide/151_svg_renderer.py |
7 | generate_logo.py | https://github.com/tisnik/presentations/blob/master/Python_GUI/PySide/generate_logo.py |
Následuje tabulka s odkazy na soubory obsahující ikony uložené ve formátu SVG. Tyto soubory jsou používány výše zmíněnými demonstračními příklady:
17. Články o možnostech a vlastnostech formátu SVG
- Vektorový grafický formát SVG
http://www.root.cz/clanky/vektorovy-graficky-format-svg/ - Cesty v souborech typu Scalable Vector Graphics
http://www.root.cz/clanky/cesty-v-souborech-typu-scalable-vector-graphics/ - Scalable Vector Graphics a základní geometrické tvary
http://www.root.cz/clanky/scalable-vector-graphics-a-zakladni-geometricke-tvary/ - Vlastnosti cest a základních geometrických tvarů v SVG
http://www.root.cz/clanky/vlastnosti-cest-a-zakladnich-geometrickych-tvaru-v-svg/ - SVG – styly výplní a značky připojované ke křivkám
http://www.root.cz/clanky/svg-styly-vyplni-a-znacky-pripojovane-ke-krivkam/ - Gradientní výplně a textové objekty v SVG
http://www.root.cz/clanky/gradientni-vyplne-a-textove-objekty-v-svg/ - Grafický formát SVG a animace
http://www.root.cz/clanky/graficky-format-svg-a-animace/ - Pokročilejší animace ve formátu SVG
http://www.root.cz/clanky/pokrocilejsi-animace-ve-formatu-svg/ - Podpora skriptování v grafickém formátu SVG
http://www.root.cz/clanky/podpora-skriptovani-v-grafickem-formatu-svg/ - Zpracování událostí při skriptování výkresů SVG
http://www.root.cz/clanky/zpracovani-udalosti-pri-skriptovani-vykresu-svg/
18. Odkazy na Internetu
- QSvgWidget
https://pyside.github.io/docs/pyside/PySide/QtSvg/QSvgWidget.html - QByteArray
https://pyside.github.io/docs/pyside/PySide/QtCore/QByteArray.html - Python Bytes, Bytearray
https://www.w3resource.com/python/python-bytes.php - psep-0101.txt (mj. popis mapování typů Pythonu na třídy v PySide)
https://github.com/techtonik/pseps/blob/master/psep-0101.txt - QSvgRenderer
https://pyside.github.io/docs/pyside/PySide/QtSvg/QSvgRenderer.html - QSvgGenerator
https://pyside.github.io/docs/pyside/PySide/QtSvg/QSvgGenerator.html - QIcon
https://pyside.github.io/docs/pyside/PySide/QtGui/QIcon.html - PySide 1.2.1 documentation
https://pyside.github.io/docs/pyside/index.html - QStyle
https://pyside.github.io/docs/pyside/PySide/QtGui/QStyle.html - QCommonStyle
https://pyside.github.io/docs/pyside/PySide/QtGui/QCommonStyle.html - QPlastiqueStyle
https://pyside.github.io/docs/pyside/PySide/QtGui/QPlastiqueStyle.html - QMacStyle
https://pyside.github.io/docs/pyside/PySide/QtGui/QMacStyle.html - QCleanlooksStyle
https://pyside.github.io/docs/pyside/PySide/QtGui/QCleanlooksStyle.html - QGtkStyle
https://pyside.github.io/docs/pyside/PySide/QtGui/QGtkStyle.html - QCDEStyle
https://pyside.github.io/docs/pyside/PySide/QtGui/QCDEStyle.html - QMotifStyle
https://pyside.github.io/docs/pyside/PySide/QtGui/QMotifStyle.html - QWindowsStyle
https://pyside.github.io/docs/pyside/PySide/QtGui/QWindowsStyle.html - QStyleFactory
https://pyside.github.io/docs/pyside/PySide/QtGui/QStyleFactory.html - QStyleOptionHeader
https://pyside.github.io/docs/pyside/PySide/QtGui/QStyleOptionHeader.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 - QDialog
https://pyside.github.io/docs/pyside/PySide/QtGui/QDialog.html - QMessageBox
https://pyside.github.io/docs/pyside/PySide/QtGui/QMessageBox.html - QErrorMessage
https://pyside.github.io/docs/pyside/PySide/QtGui/QErrorMessage.html - QInputDialog
https://pyside.github.io/docs/pyside/PySide/QtGui/QInputDialog.html - QColorDialog
https://pyside.github.io/docs/pyside/PySide/QtGui/QColorDialog.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/