Hlavní navigace

Práce s vektorovým formátem SVG ve frameworku PySide

Pavel Tišnovský

V dnešním článku o frameworku PySide se seznámíme s použitím formátu SVG (Scalable Vector Graphics), a to jak při zobrazování jednotlivých widgetů, tak i při rasterizaci SVG do bitmapového obrázku (QBitmap, QPixmap).

Doba čtení: 34 minut

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

18. Odkazy na Internetu

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/pre­sentations. Pokud nechcete klonovat celý repositář, můžete namísto toho použít odkazy na jednotlivé příklady, které naleznete v následující tabulce:

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

  1. Vektorový grafický formát SVG
    http://www.root.cz/clanky/vektorovy-graficky-format-svg/
  2. Cesty v souborech typu Scalable Vector Graphics
    http://www.root.cz/clanky/cesty-v-souborech-typu-scalable-vector-graphics/
  3. Scalable Vector Graphics a základní geometrické tvary
    http://www.root.cz/clanky/scalable-vector-graphics-a-zakladni-geometricke-tvary/
  4. Vlastnosti cest a základních geometrických tvarů v SVG
    http://www.root.cz/clanky/vlastnosti-cest-a-zakladnich-geometrickych-tvaru-v-svg/
  5. 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/
  6. Gradientní výplně a textové objekty v SVG
    http://www.root.cz/clanky/gradientni-vyplne-a-textove-objekty-v-svg/
  7. Grafický formát SVG a animace
    http://www.root.cz/clanky/graficky-format-svg-a-animace/
  8. Pokročilejší animace ve formátu SVG
    http://www.root.cz/clanky/pokrocilejsi-animace-ve-formatu-svg/
  9. Podpora skriptování v grafickém formátu SVG
    http://www.root.cz/clanky/podpora-skriptovani-v-grafickem-formatu-svg/
  10. 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

  1. QSvgWidget
    https://pyside.github.io/doc­s/pyside/PySide/QtSvg/QSvgWid­get.html
  2. QByteArray
    https://pyside.github.io/doc­s/pyside/PySide/QtCore/QBy­teArray.html
  3. Python Bytes, Bytearray
    https://www.w3resource.com/pyt­hon/python-bytes.php
  4. psep-0101.txt (mj. popis mapování typů Pythonu na třídy v PySide)
    https://github.com/techto­nik/pseps/blob/master/psep-0101.txt
  5. QSvgRenderer
    https://pyside.github.io/doc­s/pyside/PySide/QtSvg/QSvgRen­derer.html
  6. QSvgGenerator
    https://pyside.github.io/doc­s/pyside/PySide/QtSvg/QSvgGe­nerator.html
  7. QIcon
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QIcon­.html
  8. PySide 1.2.1 documentation
    https://pyside.github.io/doc­s/pyside/index.html
  9. QStyle
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QSty­le.html
  10. QCommonStyle
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QCom­monStyle.html
  11. QPlastiqueStyle
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QPlas­tiqueStyle.html
  12. QMacStyle
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QMac­Style.html
  13. QCleanlooksStyle
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QCle­anlooksStyle.html
  14. QGtkStyle
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QGtkSty­le.html
  15. QCDEStyle
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QCDES­tyle.html
  16. QMotifStyle
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QMo­tifStyle.html
  17. QWindowsStyle
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QWin­dowsStyle.html
  18. QStyleFactory
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QSty­leFactory.html
  19. QStyleOptionHeader
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QSty­leOptionHeader.html
  20. QAbstractSlider
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/Abstrac­tSlider.html
  21. QScrollBar
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/Scro­llBar.html
  22. QSlider
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/Sli­der.html
  23. QDial
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/Dial­.html
  24. QImage
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QIma­ge.html
  25. QPixmap
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QPix­map.html
  26. QBitmap
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QBit­map.html
  27. QPaintDevice
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QPa­intDevice.html
  28. QPicture
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QPic­ture.html
  29. QPainter
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QPa­inter.html
  30. QPainterPath
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QPa­interPath.html
  31. QGradient
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QGra­dient.html
  32. QLinearGradient
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QLi­nearGradient.html
  33. QRadialGradient
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QRa­dialGradient.html
  34. QTableWidget
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QTa­bleWidget.html
  35. QTableWidgetItem
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QTa­bleWidgetItem.html
  36. QTreeWidget
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QTre­eWidget.html
  37. QTreeWidgetItem
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QTre­eWidgetItem.html
  38. Afinní zobrazení
    https://cs.wikipedia.org/wi­ki/Afinn%C3%AD_zobrazen%C3%AD
  39. Differences Between PySide and PyQt
    https://wiki.qt.io/Differen­ces_Between_PySide_and_PyQt
  40. PySide 1.2.1 tutorials
    https://pyside.github.io/doc­s/pyside/tutorials/index.html
  41. PySide tutorial
    http://zetcode.com/gui/py­sidetutorial/
  42. Drawing in PySide
    http://zetcode.com/gui/py­sidetutorial/drawing/
  43. Qt Core
    https://pyside.github.io/doc­s/pyside/PySide/QtCore/Qt­.html
  44. QLayout
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QLa­yout.html
  45. QValidator
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QVa­lidator.html
  46. QStackedLayout
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QStac­kedLayout.html
  47. QFormLayout
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QFor­mLayout.html
  48. QBoxLayout
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QBox­Layout.html
  49. QHBoxLayout
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QHBox­Layout.html
  50. QVBoxLayout
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QVBox­Layout.html
  51. QGridLayout
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QGrid­Layout.html
  52. QAction
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QAc­tion.html
  53. QDialog
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QDi­alog.html
  54. QMessageBox
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QMes­sageBox.html
  55. QErrorMessage
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QError­Message.html
  56. QInputDialog
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QIn­putDialog.html
  57. QColorDialog
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QCo­lorDialog.html
  58. QListWidget
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QLis­tWidget.html
  59. Signals & Slots
    http://doc.qt.io/qt-4.8/signalsandslots.html
  60. Signals and Slots in PySide
    http://wiki.qt.io/Signals_an­d_Slots_in_PySide
  61. Intro to PySide/PyQt: Basic Widgets and Hello, World!
    http://www.pythoncentral.io/intro-to-pysidepyqt-basic-widgets-and-hello-world/
  62. QLineEdit
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QLi­neEdit.html
  63. QTextEdit
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QTex­tEdit.html
  64. QValidator
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QVa­lidator.html
  65. QIntValidator
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QIn­tValidator.html
  66. QRegExpValidator
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QRe­gExpValidator.html
  67. QWidget
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QWid­get.html
  68. QMainWindow
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QMa­inWindow.html
  69. QLabel
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QLa­bel.html
  70. QAbstractButton
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QAb­stractButton.html
  71. QCheckBox
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QChec­kBox.html
  72. QRadioButton
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QRa­dioButton.html
  73. QButtonGroup
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QBut­tonGroup.html
  74. QFrame
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QFra­me.html#PySide.QtGui.PySi­de.QtGui.QFrame
  75. QFrame.frameStyle
    https://pyside.github.io/doc­s/pyside/PySide/QtGui/QFra­me.html#PySide.QtGui.PySi­de.QtGui.QFrame.frameStyle
  76. Leo editor
    http://leoeditor.com/
  77. IPython Qt Console aneb vylepšený pseudoterminál
    https://mojefedora.cz/integrovana-vyvojova-prostredi-ve-fedore-ipython-a-ipython-notebook/#k06
  78. Vývojová prostředí ve Fedoře (4. díl)
    https://mojefedora.cz/vyvojova-prostredi-ve-fedore-4-dil/
  79. Seriál Letní škola programovacího jazyka Logo
    http://www.root.cz/serialy/letni-skola-programovaciho-jazyka-logo/
  80. Educational programming language
    http://en.wikipedia.org/wi­ki/Educational_programmin­g_language
  81. Logo Tree Project:
    http://www.elica.net/downlo­ad/papers/LogoTreeProject­.pdf
  82. Hra Breakout napísaná v Tkinteri
    https://www.root.cz/clanky/hra-breakout-napisana-v-tkinteri/
  83. Hra Snake naprogramovaná v Pythone s pomocou Tkinter
    https://www.root.cz/clanky/hra-snake-naprogramovana-v-pythone-s-pomocou-tkinter/
  84. 24.1. turtle — Turtle graphics
    https://docs.python.org/3­.5/library/turtle.html#mo­dule-turtle
  85. TkDND
    http://freecode.com/projects/tkdnd
  86. Python Tkinter Fonts
    https://www.tutorialspoin­t.com/python/tk_fonts.htm
  87. The Tkinter Canvas Widget
    http://effbot.org/tkinter­book/canvas.htm
  88. Ovládací prvek (Wikipedia)
    https://cs.wikipedia.org/wi­ki/Ovl%C3%A1dac%C3%AD_prvek_­%28po%C4%8D%C3%ADta%C4%8D%29
  89. Rezervovaná klíčová slova v Pythonu
    https://docs.python.org/3/re­ference/lexical_analysis.html#ke­ywords
  90. TkDocs: Styles and Themes
    http://www.tkdocs.com/tuto­rial/styles.html
  91. Drawing in Tkinter
    http://zetcode.com/gui/tkin­ter/drawing/
  92. Changing ttk widget text color (StackOverflow)
    https://stackoverflow.com/qu­estions/16240477/changing-ttk-widget-text-color
  93. The Hitchhiker's Guide to Pyhton: GUI Applications
    http://docs.python-guide.org/en/latest/scenarios/gui/
  94. 7 Top Python GUI Frameworks for 2017
    http://insights.dice.com/2014/11/26/5-top-python-guis-for-2015/
  95. GUI Programming in Python
    https://wiki.python.org/mo­in/GuiProgramming
  96. Cameron Laird's personal notes on Python GUIs
    http://phaseit.net/claird/com­p.lang.python/python_GUI.html
  97. Python GUI development
    http://pythoncentral.io/introduction-python-gui-development/
  98. Graphic User Interface FAQ
    https://docs.python.org/2/faq/gu­i.html#graphic-user-interface-faq
  99. TkInter
    https://wiki.python.org/moin/TkInter
  100. Tkinter 8.5 reference: a GUI for Python
    http://infohost.nmt.edu/tcc/hel­p/pubs/tkinter/web/index.html
  101. TkInter (Wikipedia)
    https://en.wikipedia.org/wiki/Tkinter
  102. appJar
    http://appjar.info/
  103. appJar (Wikipedia)
    https://en.wikipedia.org/wiki/AppJar
  104. appJar na Pythonhosted
    http://pythonhosted.org/appJar/
  105. appJar widgets
    http://appjar.info/pythonWidgets/
  106. Stránky projektu PyGTK
    http://www.pygtk.org/
  107. PyGTK (Wikipedia)
    https://cs.wikipedia.org/wiki/PyGTK
  108. Stránky projektu PyGObject
    https://wiki.gnome.org/Pro­jects/PyGObject
  109. Stránky projektu Kivy
    https://kivy.org/#home
  110. Stránky projektu PyQt
    https://riverbankcomputin­g.com/software/pyqt/intro
  111. PyQt (Wikipedia)
    https://cs.wikipedia.org/wiki/PyGTK
  112. Stránky projektu PySide
    https://wiki.qt.io/PySide
  113. PySide (Wikipedia)
    https://en.wikipedia.org/wiki/PySide
  114. Stránky projektu Kivy
    https://kivy.org/#home
  115. Kivy (framework, Wikipedia)
    https://en.wikipedia.org/wi­ki/Kivy_(framework)
  116. QML Applications
    http://doc.qt.io/qt-5/qmlapplications.html
  117. KDE
    https://www.kde.org/
  118. Qt
    https://www.qt.io/
  119. GNOME
    https://en.wikipedia.org/wiki/GNOME
  120. Category:Software that uses PyGTK
    https://en.wikipedia.org/wi­ki/Category:Software_that_u­ses_PyGTK
  121. Category:Software that uses PyGObject
    https://en.wikipedia.org/wi­ki/Category:Software_that_u­ses_PyGObject
  122. Category:Software that uses wxWidgets
    https://en.wikipedia.org/wi­ki/Category:Software_that_u­ses_wxWidgets
  123. GIO
    https://developer.gnome.or­g/gio/stable/
  124. GStreamer
    https://gstreamer.freedesktop.org/
  125. GStreamer (Wikipedia)
    https://en.wikipedia.org/wi­ki/GStreamer
  126. Wax Gui Toolkit
    https://wiki.python.org/moin/Wax
  127. Python Imaging Library (PIL)
    http://infohost.nmt.edu/tcc/hel­p/pubs/pil/
  128. 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/
Našli jste v článku chybu?