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/