Hlavní navigace

Nastavení stylů vykreslování widgetů, oken i dialogů v knihovně PySide

Pavel Tišnovský

V dnešní části seriálu o knihovnách určených pro tvorbu GUI v Pythonu si ukážeme, jakým způsobem je možné v knihovně PySide nastavit styl vykreslování ovládacích prvků (widgetů), oken i celých dialogů.

Doba čtení: 39 minut

11. Změna stylů jednotlivých widgetů pomocí metody QWidget.setStyleSheet()

12. Čtvrtý demonstrační příklad – změna pozadí jednotlivých widgetů

13. Kaskádní styly v knihovně PySide

14. Stylesheet platný pro celou aplikaci

15. Pátý demonstrační příklad – použití stylesheetu platného pro celou aplikaci

16. Repositář s demonstračními příklady

17. Odkazy na Internetu

1. Nastavení stylů vykreslování widgetů, oken i dialogů v knihovně PySide

V diskuzi pod předchozím článkem mj. zazněl i oprávněný dotaz, proč se na screenshotech použitých v tomto seriálu používá styl widgetů a dialogů, který připomíná aplikace naprogramované s využitím „konkurenční“ knihovny GTK+ a který pro svůj minimalismus vypadá poněkud zastarale. Knihovna Qt, nad níž je framework PySide postaven, ovšem umožňuje dosti flexibilně měnit styl vykreslování widgetů, oken i standardních dialogů, a to dokonce na několika úrovních: pro celé desktopové prostředí, pro celou aplikaci popř. dokonce pro jednotlivé widgety. Navíc je možné ke standardním stylům doprogramovat i styly vlastní, nebo se dá do určité míry upravit vzhled widgetů s využitím CSS (Cascading Style Sheets). V dalších kapitolách si jednotlivé možnosti popíšeme (kromě nastavení desktopového prostředí, to již spadá mimo rámec tohoto seriálu).

Obrázek 1: Screenshot z předchozího článku.

2. Konfigurace stylu přes argumenty zadané na příkazovém řádku

Jednou z nejjednodušších možností nastavení „globálního“ stylu platného pro celou aplikaci (včetně standardních dialogů) je použití přepínače -style jméno_stylu na příkazové řádce. Tento přepínač je (podobně jako některé další přepínače) automaticky zpracován třídou QApplication. Připomeňme si, jakým způsobem se konstruuje instance třídy QApplication, přesněji řečeno jaké parametry se jí předávají:

def main():
    app = QtGui.QApplication(sys.argv)
    ...
    ...
    ...
Poznámka: vzhledem k tomu, že standardní přepínače jsou automaticky zpracovány třídou QApplication a následně jsou odstraněny ze seznamu argumentů příkazového řádku, je vhodné argumenty programově zpracovat po vytvoření instance QApplication. Díky tomu se již nemusíme o argumenty určené pro konfiguraci knihovny PySide (a tím pádem i pro Qt) starat.

Mezi podporovaná jména stylů patří:

  • CDE
  • Cleanlooks
  • GTK+
  • Motif
  • Plastique
  • Windows

Jména stylů byla získána tímto pomocným skriptem:

#!/usr/bin/env python
# vim: set fileencoding=utf-8
 
import sys
 
# import modulu pro GUI
from PySide import QtGui
 
 
def main():
    for key in QtGui.QStyleFactory.keys():
        print(key)
 
 
if __name__ == '__main__':
    main()

3. První demonstrační příklad – zobrazení většiny standardních widgetů a dialogů

Dnešní první demonstrační příklad, jehož zdrojový kód můžete získat z adresy https://github.com/tisnik/pre­sentations/blob/master/Pyt­hon_GUI/PySide/135_most_wid­gets.py, je poněkud delší, než příklady, s nimiž jsme se setkali v předchozích částech tohoto seriálu. Je tomu tak z toho důvodu, že v příkladu používáme většinu popsaných ovládacích prvků (widgetů), nástrojový pruh (toolbar), hlavní menu i některé standardní dialogy. Mnoho typů widgetů používáme samozřejmě z toho důvodu, aby bylo patrné, jakým způsobem se změna stylu projeví na různých ovládacích prvcích.

Obrázek 2: První demonstrační příklad vykreslený výchozím stylem napodobujícím GTK (výchozí styl je však závislý na konfiguraci systému, použitém desktopovém prostředí atd.).

Výchozí styl lze přepnout z příkazové řádky:

./135_most_widgets.py -style motif
./135_most_widgets.py -style plastique
./135_most_widgets.py -style windows

atd.

Následuje výpis zdrojového kódu demonstračního příkladu:

#!/usr/bin/env python
# vim: set fileencoding=utf-8
 
import sys
 
# import "jádra" frameworku Qt i modulu pro GUI
from PySide import QtCore
from PySide import QtGui
 
 
# 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()
        openFileButton = self.prepareOpenFileButton()
        colorDialogButton = self.prepareColorDialogButton()
        messageBoxButton = self.prepareMessageBoxButton()
 
        # vytvoření testovacích widgetů vkládaných do okna
        tree = self.prepareTree()
        slider = self.prepareSlider()
        lineEdit = self.prepareLineEdit()
        textEdit = self.prepareTextEdit()
        dial = self.prepareDial()
 
        # různě ostylovaná návěští
        testLabel1 = QtGui.QLabel("Normal/Default")
        testLabel2 = QtGui.QLabel("Box")
        testLabel3 = QtGui.QLabel("Panel")
        testLabel4 = QtGui.QLabel("Win Panel")
        testLabel5 = QtGui.QLabel("HLine")
        testLabel6 = QtGui.QLabel("VLine")
        testLabel7 = QtGui.QLabel("StyledPanel")
 
        testLabel2.setFrameStyle(QtGui.QFrame.Box)
        testLabel3.setFrameStyle(QtGui.QFrame.Panel)
        testLabel4.setFrameStyle(QtGui.QFrame.WinPanel)
        testLabel5.setFrameStyle(QtGui.QFrame.HLine)
        testLabel6.setFrameStyle(QtGui.QFrame.VLine)
        testLabel7.setFrameStyle(QtGui.QFrame.StyledPanel)
 
        # testovací přepínací tlačítka
        testRadioButton1 = QtGui.QRadioButton("radio button #1")
        testRadioButton2 = QtGui.QRadioButton("radio button #2")
        testRadioButton3 = QtGui.QRadioButton("radio button #3")
 
        # testovací zaškrtávací tlačítka
        testCheckBox1 = QtGui.QCheckBox("check box 1")
        testCheckBox2 = QtGui.QCheckBox("check box 2")
 
        # které tlačítko bude vybráno
        testRadioButton3.setChecked(True)
        testCheckBox2.setChecked(True)
 
        # vytvoření správců geometrie
        topLayout = QtGui.QHBoxLayout()
        leftLayout = QtGui.QVBoxLayout()
        centerLayout = QtGui.QVBoxLayout()
        rightLayout = QtGui.QVBoxLayout()
 
        # umístění widgetů do okna - levý sloupec
        leftLayout.addWidget(tree)
        leftLayout.addWidget(slider)
 
        # umístění widgetů do okna - prostřední sloupec
        centerLayout.addWidget(lineEdit)
        centerLayout.addWidget(textEdit)
        centerLayout.addWidget(openFileButton)
        centerLayout.addWidget(colorDialogButton)
        centerLayout.addWidget(messageBoxButton)
        centerLayout.addWidget(quitButton)
 
        # umístění widgetů do okna - pravý sloupec
        rightLayout.addWidget(testRadioButton1)
        rightLayout.addWidget(testRadioButton2)
        rightLayout.addWidget(testRadioButton3)
        rightLayout.addWidget(testCheckBox1)
        rightLayout.addWidget(testCheckBox2)
 
        rightLayout.addWidget(testLabel1)
        rightLayout.addWidget(testLabel2)
        rightLayout.addWidget(testLabel3)
        rightLayout.addWidget(testLabel4)
        rightLayout.addWidget(testLabel5)
        rightLayout.addWidget(testLabel6)
        rightLayout.addWidget(testLabel7)
        rightLayout.addWidget(dial)
 
        # umístění layoutů do hlavního layoutu
        topLayout.addLayout(leftLayout)
        topLayout.addLayout(centerLayout)
        topLayout.addLayout(rightLayout)
 
        # nastavení správce geometrie a vložení všech komponent do okna
        self.setLayout(topLayout)
 
    def prepareOpenFileButton(self):
        # tlačítko
        openFileButton = QtGui.QPushButton('Open file...', self)
        openFileButton.resize(openFileButton.sizeHint())
 
        # navázání akce na signál
        openFileButton.clicked.connect(self.showOpenFileDialog)
        return openFileButton
 
    def prepareMessageBoxButton(self):
        # tlačítko
        messageBoxButton = QtGui.QPushButton('Message Box', self)
        messageBoxButton.resize(messageBoxButton.sizeHint())
 
        # navázání akce na signál
        messageBoxButton.clicked.connect(self.showCustomMessageBox)
        return messageBoxButton
 
    def prepareLineEdit(self):
        # jednořádkové vstupní textové pole
        lineEdit = QtGui.QLineEdit(self)
        # naplnění textového pole textem
        lineEdit.setText(u"příliš žluťoučký kůň úpěl ďábelské ódy")
        return lineEdit
 
    def prepareTextEdit(self):
        # víceřádkové vstupní textové pole
        textEdit = QtGui.QTextEdit(self)
 
        # nastavení základních vlastností textového pole
        textEdit.setAcceptRichText(False)
        textEdit.setLineWrapMode(QtGui.QTextEdit.NoWrap)
 
        # vložení obsahu souboru do víceřádkového textového pole
        with open("01_empty_window.py", "r") as fin:
            content = fin.read().decode('utf8')
            textEdit.insertPlainText(content)
 
        return textEdit
 
    def prepareColorDialogButton(self):
        # tlačítko s popisem
        colorDialogButton = QtGui.QPushButton('Select color', self)
        colorDialogButton.resize(colorDialogButton.sizeHint())
 
        # navázání akce na signál
        colorDialogButton.clicked.connect(self.showColorDialog)
        return colorDialogButton
 
    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 showOpenFileDialog(self):
        # handler po stlačení tlačítka "Open file..."
        fileName = QtGui.QFileDialog.getOpenFileName(self, "Open file", u".")
 
        self.showMessageBox(u'Vybraný soubor\n{f}'.format(f=fileName))
 
    def showColorDialog(self):
        # handler po stlačení tlačítka "Select color"
        colorDialog = QtGui.QColorDialog()
        colorDialog.setCurrentColor(QtGui.QColor("#aabbcc"))
        result = colorDialog.exec_()
 
        selected = colorDialog.selectedColor()
        message = "Selected color: {r} {g} {b}\nClicked on: {c}".format(
            r=selected.red(),
            g=selected.green(),
            b=selected.blue(),
            c="Ok" if result == 1 else "Cancel")
 
        self.showMessageBox(message)
 
    def showMessageBox(self, text):
        msgBox = QtGui.QMessageBox()
        msgBox.setText(text)
        msgBox.setIcon(QtGui.QMessageBox.Information)
        msgBox.exec_()
 
    def showCustomMessageBox(self):
        # handler po stlačení tlačítka "Message Box"
        # vytvoření dialogu
        msgBox = QtGui.QMessageBox()
 
        # nastavení zprávy a ikony, která se má zobrazit vedle zprávy
        msgBox.setText(u'Zpráva')
        msgBox.setIcon(QtGui.QMessageBox.Critical)
 
        # nastavení tlačítek, které mají být součástí dialogu
        msgBox.addButton("Help", QtGui.QMessageBox.HelpRole)
        msgBox.addButton("Accept", QtGui.QMessageBox.AcceptRole)
        msgBox.addButton("Reject", QtGui.QMessageBox.RejectRole)
 
        # zobrazení dialogu
        msgBox.exec_()
 
    def prepareTree(self):
        # vytvoření stromu
        tree = QtGui.QTreeWidget(self)
        tree.setHeaderLabel("strom")
        tree.setColumnCount(1)
 
        # naplnění stromu daty
        items = []
        for i in range(1, 11):
            item = QtGui.QTreeWidgetItem(None, ["prvek #{i}".format(i=i)])
            items.append(item)
            QtGui.QTreeWidgetItem(item, ["podprvek A"])
            QtGui.QTreeWidgetItem(item, ["podprvek B"])
            QtGui.QTreeWidgetItem(item, ["podprvek C"])
        tree.insertTopLevelItems(0, items)
 
        # po vložení všech prvků do stromu je můžeme rozbalit
        skip = False
        for item in items:
            if not skip:
                item.setExpanded(True)
            skip = not skip
 
        return tree
 
    def prepareSlider(self):
        # vytvoření slideru
        slider = QtGui.QSlider(QtCore.Qt.Horizontal)
 
        return slider
 
    def prepareDial(self):
        # vytvoření widgetu
        dial = QtGui.QDial()
 
        return dial
 
 
# 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(320, 240)
        self.setWindowTitle("Common widgets")
 
        # 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():
    app = QtGui.QApplication(sys.argv)
    MainWindow().run(app)
 
 
if __name__ == '__main__':
    main()

4. Způsob vykreslování widgetů, standardní třídy se styly

Vzhledem k tomu, že vykreslování všech prvků GUI (s výjimkou dekorace oken) řídí knihovna Qt (dostupná přes PySide), je možné relativně snadno změnit styl vykreslování „pouhou“ reimplementací kódu, který se stará o vykreslení jednotlivých widgetů. To lze provést implementací nové třídy odvozené od třídy QStyle, což má tu výhodu, že je možné předefinovat vzhled pouze některých widgetů. Ve skutečnosti již několik tříd přímo či nepřímo odvozených od QStyle existuje a je možné je vybrat buď při startu a inicializaci aplikace nebo při jejím běhu. Standardně se jedná o tyto třídy:

Poznámka: konkrétní vzhled widgetů je ještě závislý na použitém operačním systému atd. To se týká především třídy QWindowsStyle, jež se snaží widgety přizpůsobit konkrétnímu systému (od Windows XP výše).

5. Použití tříd QApplication a QStyleFactory pro změnu stylu vykreslování celé aplikace

Ve chvíli, kdy budeme potřebovat změnit styl vykreslování celé aplikace, tj. všech oken, dialogů i ovládacích prvků (widgetů), máme dvě možnosti. Lze samozřejmě použít již výše zmíněný přepínač -style, což je sice flexibilní řešení, ovšem pro některé uživatele může být poměrně komplikované tuto konfiguraci provést. Druhou možností je přímo ve zdrojovém kódu aplikace zavolat statickou metodu pojmenovanou QApplication.setStyle(), které se předá název požadovaného stylu. Volání se provede následujícím způsobem:

QtGui.QApplication.setStyle("plastique")

Funkce setStyle je statická z toho důvodu, aby ji bylo možné zavolat ještě před inicializací aplikace a začátkem tvorby jednotlivých oken a dialogů. Doporučuje se ji skutečně zavolat ještě před programovým řádkem:

app = QtGui.QApplication(sys.argv)

Ve skutečnosti však ve druhém příkladu uvidíte, že se dá změna stylu provést i v běžící aplikaci (minimálně na Linuxu).

Jména aktuálně podporovaných stylů vrací další statická funkce, tentokrát QStyleFactory.keys():

QtGui.QStyleFactory.keys()

6. Druhý demonstrační příklad – výběr stylu vykreslování pro celou aplikaci

V dnešním druhém demonstračním příkladu je ukázáno, jak lze provést změnu stylu vykreslování, a to dokonce dvěma způsoby. Nejprve je nastaven výchozí styl ještě před vlastní inicializací aplikace (což je nejbezpečnější způsob):

QtGui.QApplication.setStyle("plastique")
app = QtGui.QApplication(sys.argv)

Následně vytvoříme položky menu pro všechny styly, které jsou aktuálně k dispozici a které programově nalezneme (nabídka stylů se totiž může lišit):

# položka Style v hlavním menu
styleMenu = menubar.addMenu('&Style')
 
# jednotlivé položky menu s nabízenými styly
for key in QtGui.QStyleFactory.keys():
    styleMenuItem = QtGui.QAction(key, self)
    styleMenuItem.triggered.connect(lambda key=key: self.setStyle(key))
    styleMenu.addAction(styleMenuItem)
Poznámka: povšimněte si triku, kterým zajistíme, aby se handler setStyle pokaždé volal s jiným parametrem. Musíme zde použít anonymní funkci a navíc pro ni vytvořit lokální parametr key.

Handler zajišťující nastavení stylu vybraného uživatelem je velmi jednoduchý, protože se mu přímo předá jméno stylu (viz též předchozí kapitolu s popisem metody QApplication.setStyle()):

def setStyle(self, styleName):
    # nastavení vybraného stylu
    QtGui.QApplication.setStyle(styleName)

Úplný zdrojový kód tohoto demonstračního příkladu vypadá následovně:

#!/usr/bin/env python
# vim: set fileencoding=utf-8
 
import sys
 
# import "jádra" frameworku Qt i modulu pro GUI
from PySide import QtCore
from PySide import QtGui
 
 
# nový widget bude odvozen od obecného widgetu
class MainWindowContent(QtGui.QWidget):
 
    def __init__(self):
        # zavoláme konstruktor předka
        super(MainWindowContent, self).__init__()
 
        # konfigurace GUI + přidání widgetu do okna
        self.prepareGUI()
 
    def prepareGUI(self):
 
        # tlačítka, na které je navázán handler
        quitButton = self.prepareQuitButton()
        openFileButton = self.prepareOpenFileButton()
        colorDialogButton = self.prepareColorDialogButton()
        messageBoxButton = self.prepareMessageBoxButton()
 
        # vytvoření testovacích widgetů vkládaných do okna
        tree = self.prepareTree()
        slider = self.prepareSlider()
        lineEdit = self.prepareLineEdit()
        textEdit = self.prepareTextEdit()
        dial = self.prepareDial()
 
        # různě ostylovaná návěští
        testLabel1 = QtGui.QLabel("Normal/Default")
        testLabel2 = QtGui.QLabel("Box")
        testLabel3 = QtGui.QLabel("Panel")
        testLabel4 = QtGui.QLabel("Win Panel")
        testLabel5 = QtGui.QLabel("HLine")
        testLabel6 = QtGui.QLabel("VLine")
        testLabel7 = QtGui.QLabel("StyledPanel")
 
        testLabel2.setFrameStyle(QtGui.QFrame.Box)
        testLabel3.setFrameStyle(QtGui.QFrame.Panel)
        testLabel4.setFrameStyle(QtGui.QFrame.WinPanel)
        testLabel5.setFrameStyle(QtGui.QFrame.HLine)
        testLabel6.setFrameStyle(QtGui.QFrame.VLine)
        testLabel7.setFrameStyle(QtGui.QFrame.StyledPanel)
 
        # testovací přepínací tlačítka
        testRadioButton1 = QtGui.QRadioButton("radio button #1")
        testRadioButton2 = QtGui.QRadioButton("radio button #2")
        testRadioButton3 = QtGui.QRadioButton("radio button #3")
 
        # testovací zaškrtávací tlačítka
        testCheckBox1 = QtGui.QCheckBox("check box 1")
        testCheckBox2 = QtGui.QCheckBox("check box 2")
 
        # které tlačítko bude vybráno
        testRadioButton3.setChecked(True)
        testCheckBox2.setChecked(True)
 
        # vytvoření správců geometrie
        topLayout = QtGui.QHBoxLayout()
        leftLayout = QtGui.QVBoxLayout()
        centerLayout = QtGui.QVBoxLayout()
        rightLayout = QtGui.QVBoxLayout()
 
        # umístění widgetů do okna - levý sloupec
        leftLayout.addWidget(tree)
        leftLayout.addWidget(slider)
 
        # umístění widgetů do okna - prostřední sloupec
        centerLayout.addWidget(lineEdit)
        centerLayout.addWidget(textEdit)
        centerLayout.addWidget(openFileButton)
        centerLayout.addWidget(colorDialogButton)
        centerLayout.addWidget(messageBoxButton)
        centerLayout.addWidget(quitButton)
 
        # umístění widgetů do okna - pravý sloupec
        rightLayout.addWidget(testRadioButton1)
        rightLayout.addWidget(testRadioButton2)
        rightLayout.addWidget(testRadioButton3)
        rightLayout.addWidget(testCheckBox1)
        rightLayout.addWidget(testCheckBox2)
 
        rightLayout.addWidget(testLabel1)
        rightLayout.addWidget(testLabel2)
        rightLayout.addWidget(testLabel3)
        rightLayout.addWidget(testLabel4)
        rightLayout.addWidget(testLabel5)
        rightLayout.addWidget(testLabel6)
        rightLayout.addWidget(testLabel7)
        rightLayout.addWidget(dial)
 
        # umístění layoutů do hlavního layoutu
        topLayout.addLayout(leftLayout)
        topLayout.addLayout(centerLayout)
        topLayout.addLayout(rightLayout)
 
        # nastavení správce geometrie a vložení všech komponent do okna
        self.setLayout(topLayout)
 
    def prepareOpenFileButton(self):
        # tlačítko
        openFileButton = QtGui.QPushButton('Open file...', self)
        openFileButton.resize(openFileButton.sizeHint())
 
        # navázání akce na signál
        openFileButton.clicked.connect(self.showOpenFileDialog)
        return openFileButton
 
    def prepareMessageBoxButton(self):
        # tlačítko
        messageBoxButton = QtGui.QPushButton('Message Box', self)
        messageBoxButton.resize(messageBoxButton.sizeHint())
 
        # navázání akce na signál
        messageBoxButton.clicked.connect(self.showCustomMessageBox)
        return messageBoxButton
 
    def prepareLineEdit(self):
        # jednořádkové vstupní textové pole
        lineEdit = QtGui.QLineEdit(self)
        # naplnění textového pole textem
        lineEdit.setText(u"příliš žluťoučký kůň úpěl ďábelské ódy")
        return lineEdit
 
    def prepareTextEdit(self):
        # víceřádkové vstupní textové pole
        textEdit = QtGui.QTextEdit(self)
 
        # nastavení základních vlastností textového pole
        textEdit.setAcceptRichText(False)
        textEdit.setLineWrapMode(QtGui.QTextEdit.NoWrap)
 
        # vložení obsahu souboru do víceřádkového textového pole
        with open("01_empty_window.py", "r") as fin:
            content = fin.read().decode('utf8')
            textEdit.insertPlainText(content)
 
        return textEdit
 
    def prepareColorDialogButton(self):
        # tlačítko s popisem
        colorDialogButton = QtGui.QPushButton('Select color', self)
        colorDialogButton.resize(colorDialogButton.sizeHint())
 
        # navázání akce na signál
        colorDialogButton.clicked.connect(self.showColorDialog)
        return colorDialogButton
 
    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 showOpenFileDialog(self):
        # handler po stlačení tlačítka "Open file..."
        fileName = QtGui.QFileDialog.getOpenFileName(self, "Open file", u".")
 
        self.showMessageBox(u'Vybraný soubor\n{f}'.format(f=fileName))
 
    def showColorDialog(self):
        # handler po stlačení tlačítka "Select color"
        colorDialog = QtGui.QColorDialog()
        colorDialog.setCurrentColor(QtGui.QColor("#aabbcc"))
        result = colorDialog.exec_()
 
        selected = colorDialog.selectedColor()
        message = "Selected color: {r} {g} {b}\nClicked on: {c}".format(
            r=selected.red(),
            g=selected.green(),
            b=selected.blue(),
            c="Ok" if result == 1 else "Cancel")
 
        self.showMessageBox(message)
 
    def showMessageBox(self, text):
        msgBox = QtGui.QMessageBox()
        msgBox.setText(text)
        msgBox.setIcon(QtGui.QMessageBox.Information)
        msgBox.exec_()
 
    def showCustomMessageBox(self):
        # handler po stlačení tlačítka "Message Box"
        # vytvoření dialogu
        msgBox = QtGui.QMessageBox()
 
        # nastavení zprávy a ikony, která se má zobrazit vedle zprávy
        msgBox.setText(u'Zpráva')
        msgBox.setIcon(QtGui.QMessageBox.Critical)
 
        # nastavení tlačítek, které mají být součástí dialogu
        msgBox.addButton("Help", QtGui.QMessageBox.HelpRole)
        msgBox.addButton("Accept", QtGui.QMessageBox.AcceptRole)
        msgBox.addButton("Reject", QtGui.QMessageBox.RejectRole)
 
        # zobrazení dialogu
        msgBox.exec_()
 
    def prepareTree(self):
        # vytvoření stromu
        tree = QtGui.QTreeWidget(self)
        tree.setHeaderLabel("strom")
        tree.setColumnCount(1)
 
        # naplnění stromu daty
        items = []
        for i in range(1, 11):
            item = QtGui.QTreeWidgetItem(None, ["prvek #{i}".format(i=i)])
            items.append(item)
            QtGui.QTreeWidgetItem(item, ["podprvek A"])
            QtGui.QTreeWidgetItem(item, ["podprvek B"])
            QtGui.QTreeWidgetItem(item, ["podprvek C"])
        tree.insertTopLevelItems(0, items)
 
        # po vložení všech prvků do stromu je můžeme rozbalit
        skip = False
        for item in items:
            if not skip:
                item.setExpanded(True)
            skip = not skip
 
        return tree
 
    def prepareSlider(self):
        # vytvoření slideru
        slider = QtGui.QSlider(QtCore.Qt.Horizontal)
 
        return slider
 
    def prepareDial(self):
        # vytvoření widgetu
        dial = QtGui.QDial()
 
        return dial
 
 
# 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(320, 240)
        self.setWindowTitle("QMessageBox")
 
        # 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)
 
        # položka Style v hlavním menu
        styleMenu = menubar.addMenu('&Style')
 
        # jednotlivé položky menu s nabízenými styly
        for key in QtGui.QStyleFactory.keys():
            styleMenuItem = QtGui.QAction(key, self)
            styleMenuItem.triggered.connect(lambda key=key: self.setStyle(key))
            styleMenu.addAction(styleMenuItem)
 
        # 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 setStyle(self, styleName):
        # nastavení vybraného stylu
        QtGui.QApplication.setStyle(styleName)
 
    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. Galerie standardních stylů dostupných na Linuxu

V této kapitole je ukázáno, jak bude vypadat náš demonstrační příklad při použití různých stylů. Povšimněte si, že styl ovlivňuje jak všechny widgety v hlavním okně, tak i způsob zobrazení standardních dialogů:

Obrázek 3: První demonstrační příklad s nastaveným stylem Windows.

Obrázek 4: První demonstrační příklad s nastaveným stylem Motif.

Obrázek 5: První demonstrační příklad s nastaveným stylem CDE.

Obrázek 6: První demonstrační příklad s nastaveným stylem Plastique.

Obrázek 7: První demonstrační příklad s nastaveným stylem GTK+.

Obrázek 8: První demonstrační příklad s nastaveným stylem Cleanlooks.

8. Styl aplikovaný na standardní dialog pro výběr barvy i pro zobrazení zprávy

Nejvíce je patrný vliv změny stylu o standardního dialogu pro výběr barvy a to z toho důvodu, že tento dialog obsahuje větší množství ovládacích prvků, zejména „přetáčecích“ editačních políček určených pro zadání barvových složek v modelu HSV a RGB:

Obrázek 9: Dialog pro výběr barvy s nastaveným stylem Windows.

Obrázek 10: Dialog pro výběr barvy s nastaveným stylem Motif.

Obrázek 11: Dialog pro výběr barvy s nastaveným stylem CDE.

Obrázek 12: Dialog pro výběr barvy s nastaveným stylem Plastique.

Obrázek 13: Dialog pro výběr barvy s nastaveným stylem GTK+.

Obrázek 14: Dialog pro výběr barvy s nastaveným stylem Cleanlooks.

U standardních dialogů se zprávou a informační ikonou dochází pochopitelně jak ke změně tvarů tlačítek, tak i ke změně ikony, která by měla odpovídat nativním widgetům:

Obrázek 15: Dialog se zprávou s nastaveným stylem Windows.

Obrázek 16: Dialog se zprávou s nastaveným stylem Motif.

Obrázek 17: Dialog se zprávou s nastaveným stylem CDE.

Obrázek 18: Dialog se zprávou s nastaveným stylem Plastique.

Obrázek 19: Dialog se zprávou s nastaveným stylem GTK+.

Obrázek 20: Dialog se zprávou s nastaveným stylem Cleanlooks.

9. Změna stylů jednotlivých widgetů pomocí metody QWidget.setStyle()

Podívejme se nyní, jak je možné zajistit změnu stylu jednotlivých widgetů. Nejprve si připravíme sekvenci se všemi dostupnými styly. Alternativně je možné připravit i slovník, kde klíčem budou jména stylů a hodnotami instance třídy QStyle a jejich potomků (to je pravděpodobně praktičtější, my však budeme chtít použít všechny dostupné styly, takže vytváříme jen sekvenci). Povšimněte si, jak se vytvoří objekt se stylem pomocí tovární metody QStyleFactory.create:

@staticmethod
def prepareStyles():
    # vytvoření sekvence se všemi dostupnými styly
    style_names = QtGui.QStyleFactory.keys()
    styles = [QtGui.QStyleFactory.create(style_name)
              for style_name in style_names]
    return styles

Připravíme si funkci, která vytvoří horizontální posuvník a přitom použije styl předaný do funkce v parametru style. Příprava takové funkce je snadná (pokud samozřejmě nebudeme potřebovat specifikovat i další parametry posuvníku, například rozsah, značky apod.):

def prepareSlider(self, style):
    # vytvoření slideru
    slider = QtGui.QSlider(QtCore.Qt.Horizontal)
    slider.setStyle(style)
    return slider

Posuvník následně můžeme do stejného okna vykreslit hned několikrát, pokaždé s různým stylem a navíc nezávisle na stylu nastaveném pro celou aplikaci:

for style in styles:
    slider = self.prepareSlider(style)
    leftLayout.addWidget(slider)

10. Třetí demonstrační příklad – nastavení individuálních stylů jednotlivých widgetů

V dnešním třetím demonstračním příkladu je vytvořeno hlavní okno, které obsahuje ovládací prvky uspořádané do tří sloupců. Každý widget je přitom vykreslen několikrát, pokaždé s jiným stylem. Můžeme si tak jednoduše na jediném okně ověřit, jak se změna stylu projeví na jednotlivých widgetech a do jaké míry se změní jejich chování, tj. reakce na operace prováděné uživatelem:

Obrázek 21: Screenshot dnešního třetího demonstračního příkladu při použití globálního stylu Windows.

Vykreslení jednořádkových vstupních textových polí různým stylem:

def prepareLineEdit(self, style):
    # jednořádkové vstupní textové pole
    lineEdit = QtGui.QLineEdit(self)
    # naplnění textového pole textem
    lineEdit.setText(u"příliš žluťoučký kůň úpěl ďábelské ódy")
    lineEdit.setStyle(style)
    return lineEdit
 
# umístění widgetů do okna - levý sloupec
for style in styles:
    lineEdit = self.prepareLineEdit(style)
    leftLayout.addWidget(lineEdit)

Obrázek 22: Screenshot dnešního třetího demonstračního příkladu při použití globálního stylu Motif.

Vykreslení posuvníků různým stylem (viz předchozí kapitolu):

def prepareSlider(self, style):
    # vytvoření slideru
    slider = QtGui.QSlider(QtCore.Qt.Horizontal)
    slider.setStyle(style)
    return slider
 
for style in styles:
    slider = self.prepareSlider(style)
    leftLayout.addWidget(slider)

Obrázek 23: Screenshot dnešního třetího demonstračního příkladu při použití globálního stylu CDE.

Vykreslení přepínacích tlačítek (po dvojicích):

def prepareRadioButton(self, style, text, checked):
    # vytvoření přepínacího tlačítka
    radioButton = QtGui.QRadioButton(text)
    radioButton.setStyle(style)
    radioButton.setChecked(checked)
    return radioButton
 
for style in styles:
    # testovací přepínací tlačítka
    testRadioButton1 = self.prepareRadioButton(style, "radio button #1", False)
    testRadioButton2 = self.prepareRadioButton(style, "radio button #2", True)
    # vložení přepínacích tlačítek na plochu okna
    centerLayout.addWidget(testRadioButton1)
    centerLayout.addWidget(testRadioButton2)
    centerLayout.addWidget(QtGui.QLabel(""))

Obrázek 24: Screenshot dnešního třetího demonstračního příkladu při použití globálního stylu Plastique.

Vykreslení zaškrtávacích tlačítek (vždy po dvojicích):

def prepareCheckBox(self, style, text, checked):
    # vytvoření zaškrtávacího tlačítka
    checkBox = QtGui.QCheckBox(text)
    checkBox.setStyle(style)
    checkBox.setChecked(checked)
    return checkBox
 
# umístění widgetů do okna - pravý sloupec
for style in styles:
    # testovací zaškrtávací tlačítka
    testCheckBox1 = self.prepareCheckBox(style, "check box #1", False)
    testCheckBox2 = self.prepareCheckBox(style, "check box #2", True)
    # vložení widgetů tlačítek na plochu okna
    rightLayout.addWidget(testCheckBox1)
    rightLayout.addWidget(testCheckBox2)
    rightLayout.addWidget(QtGui.QLabel(""))

Obrázek 25: Screenshot dnešního třetího demonstračního příkladu při použití globálního stylu GTK+.

Opět se podívejme na úplný zdrojový kód tohoto příkladu:

#!/usr/bin/env python
# vim: set fileencoding=utf-8
 
import sys
 
# import "jádra" frameworku Qt i modulu pro GUI
from PySide import QtCore
from PySide import QtGui
 
 
# nový widget bude odvozen od obecného widgetu
class MainWindowContent(QtGui.QWidget):
 
    def __init__(self):
        # zavoláme konstruktor předka
        super(MainWindowContent, self).__init__()
 
        # konfigurace GUI + přidání widgetu do okna
        self.prepareGUI()
 
    @staticmethod
    def prepareStyles():
        # vytvoření sekvence se všemi dostupnými styly
        style_names = QtGui.QStyleFactory.keys()
        styles = [QtGui.QStyleFactory.create(style_name)
                  for style_name in style_names]
        return styles
 
    def prepareGUI(self):
        styles = MainWindowContent.prepareStyles()
 
        # tlačítka, na které je navázán handler
        quitButton = self.prepareQuitButton()
 
        # vytvoření správců geometrie
        topLayout = QtGui.QHBoxLayout()
        leftLayout = QtGui.QVBoxLayout()
        centerLayout = QtGui.QVBoxLayout()
        rightLayout = QtGui.QVBoxLayout()
 
        # umístění widgetů do okna - levý sloupec
        for style in styles:
            lineEdit = self.prepareLineEdit(style)
            leftLayout.addWidget(lineEdit)
 
        for style in styles:
            slider = self.prepareSlider(style)
            leftLayout.addWidget(slider)
 
        leftLayout.addWidget(QtGui.QLabel(""))
        leftLayout.addWidget(quitButton)
 
        # umístění widgetů do okna - prostřední sloupec
        for style in styles:
            # testovací přepínací tlačítka
            testRadioButton1 = self.prepareRadioButton(style, "radio button #1", False)
            testRadioButton2 = self.prepareRadioButton(style, "radio button #2", True)
            # vložení přepínacích tlačítek na plochu okna
            centerLayout.addWidget(testRadioButton1)
            centerLayout.addWidget(testRadioButton2)
            centerLayout.addWidget(QtGui.QLabel(""))
 
        # umístění widgetů do okna - pravý sloupec
        for style in styles:
            # testovací zaškrtávací tlačítka
            testCheckBox1 = self.prepareCheckBox(style, "check box #1", False)
            testCheckBox2 = self.prepareCheckBox(style, "check box #2", True)
            # vložení widgetů tlačítek na plochu okna
            rightLayout.addWidget(testCheckBox1)
            rightLayout.addWidget(testCheckBox2)
            rightLayout.addWidget(QtGui.QLabel(""))
 
        # umístění layoutů do hlavního layoutu
        topLayout.addLayout(leftLayout)
        topLayout.addLayout(centerLayout)
        topLayout.addLayout(rightLayout)
 
        # nastavení správce geometrie a vložení všech komponent do okna
        self.setLayout(topLayout)
 
    def prepareLineEdit(self, style):
        # jednořádkové vstupní textové pole
        lineEdit = QtGui.QLineEdit(self)
        # naplnění textového pole textem
        lineEdit.setText(u"příliš žluťoučký kůň úpěl ďábelské ódy")
        lineEdit.setStyle(style)
        return lineEdit
 
    def prepareSlider(self, style):
        # vytvoření slideru
        slider = QtGui.QSlider(QtCore.Qt.Horizontal)
        slider.setStyle(style)
        return slider
 
    def prepareRadioButton(self, style, text, checked):
        # vytvoření přepínacího tlačítka
        radioButton = QtGui.QRadioButton(text)
        radioButton.setStyle(style)
        radioButton.setChecked(checked)
        return radioButton
 
    def prepareCheckBox(self, style, text, checked):
        # vytvoření zaškrtávacího tlačítka
        checkBox = QtGui.QCheckBox(text)
        checkBox.setStyle(style)
        checkBox.setChecked(checked)
        return checkBox
 
    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(320, 240)
        self.setWindowTitle("QMessageBox")
 
        # 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)
 
        # položka Style v hlavním menu
        styleMenu = menubar.addMenu('&Style')
 
        # jednotlivé položky menu s nabízenými styly
        for key in QtGui.QStyleFactory.keys():
            styleMenuItem = QtGui.QAction(key, self)
            styleMenuItem.triggered.connect(lambda key=key: self.setStyle(key))
            styleMenu.addAction(styleMenuItem)
 
        # 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 setStyle(self, styleName):
        # nastavení vybraného stylu
        QtGui.QApplication.setStyle(styleName)
 
    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()

Obrázek 26: Screenshot dnešního třetího demonstračního příkladu při použití globálního stylu Cleanlooks.

11. Změna stylů jednotlivých widgetů pomocí metody QWidget.setStyleSheet()

Styl vykreslování se nemusí nastavovat pouze pro celou aplikaci. Knihovna PySide totiž u všech widgetů nabízí metodu QWidget.setStyleSheet(). Této metodě je možné předat řetězec se zápisem specifikace CSS (Cascading Style Sheet). Tímto velmi jednoduchým způsobem je možné ovlivnit především popředí a pozadí widgetu, jeho okraj (border), použitý font atd. Ukažme si to na způsobu změny pozadí nějakého ovládacího prvku, například tlačítka QPushButton. Barvu pozadí lze specifikovat různě, například jménem barvy:

"background-color: red"

popř. přímo hexa tripletem s barvovými složkami RGB (známým z HTML a samozřejmě i z CSS):

"background-color: #c0ff40"

nebo dokonce:

"background-color: rgba(188, 188, 188, 50)"

Metodu setStyleSheet můžeme využít například v uživatelské funkci nazvané prepareButtonWithBackground, které se předá požadovaná barva pozadí zapsaná řetězcem. Může se jednat jak o jméno barvy („red“), tak i o její kód („#c0ff40“) atd. Z kódu barvy se vytvoří úplná specifikace CSS, která se nakonec použije:

def prepareButtonWithBackground(self, background):
    # tlačítko s popisem
    button = QtGui.QPushButton(background, self)
 
    # nastavení stylu
    styleSheet = "background-color: {background}".format(background=background)
    button.setStyleSheet(styleSheet)
 
    button.resize(button.sizeHint())
 
    return button

12. Čtvrtý demonstrační příklad – změna pozadí jednotlivých widgetů

V dnešním čtvrtém demonstračním příkladu si ukážeme způsob změny pozadí několika tlačítek umístěných do hlavního okna aplikace. Tlačítka jsou vytvořena funkcí prepareButtonWithBackground popsanou v předchozí kapitole. Celkem jsou vytvořena tři tlačítka s různým pozadím:

button1 = self.prepareButtonWithBackground("#e08080")
button2 = self.prepareButtonWithBackground("lightblue")
button3 = self.prepareButtonWithBackground("yellow")

Výsledná podoba hlavního okna aplikace bude vypadat takto (stále je však možné vybrat styl zobrazení celé aplikace, což je nezávislé na nastavení pozadí vybraných prvků):

Obrázek 27: Hlavní okno aplikace se třemi tlačítky, u nichž bylo změno pozadí. Čtvrté tlačítko má pozadí specifikované použitým stylem.

Následuje výpis zdrojového kódu tohoto demonstračního příkladu:

#!/usr/bin/env python
# vim: set fileencoding=utf-8
 
import sys
 
# import "jádra" frameworku Qt i modulu pro GUI
from PySide import QtCore
from PySide import QtGui
 
 
# 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()
 
        # další tlačítka
        button1 = self.prepareButtonWithBackground("#e08080")
        button2 = self.prepareButtonWithBackground("lightblue")
        button3 = self.prepareButtonWithBackground("yellow")
 
        # vytvoření správců geometrie
        topLayout = QtGui.QHBoxLayout()
 
        # vložení widgetů do okna
        topLayout.addWidget(button1)
        topLayout.addWidget(button2)
        topLayout.addWidget(button3)
        topLayout.addWidget(QtGui.QLabel(""))
        topLayout.addWidget(quitButton)
 
        # nastavení správce geometrie a vložení všech komponent do okna
        self.setLayout(topLayout)
 
    def prepareButtonWithBackground(self, background):
        # tlačítko s popisem
        button = QtGui.QPushButton(background, self)
 
        # nastavení stylu
        styleSheet = "background-color: {background}".format(background=background)
        button.setStyleSheet(styleSheet)
 
        button.resize(button.sizeHint())
 
        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(320, 240)
        self.setWindowTitle("QMessageBox")
 
        # 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)
 
        # položka Style v hlavním menu
        styleMenu = menubar.addMenu('&Style')
 
        # jednotlivé položky menu s nabízenými styly
        for key in QtGui.QStyleFactory.keys():
            styleMenuItem = QtGui.QAction(key, self)
            styleMenuItem.triggered.connect(lambda key=key: self.setStyle(key))
            styleMenu.addAction(styleMenuItem)
 
        # 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 setStyle(self, styleName):
        # nastavení vybraného stylu
        QtGui.QApplication.setStyle(styleName)
 
    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. Kaskádní styly v knihovně PySide

V knihovně PySide je možné použít kaskádní styly nejenom pro změnu vzhledu jednotlivých ovládacích prvků, ale i pro nastavení stylu vykreslování celé aplikace. Zápis kaskádních stylů odpovídá klasickým CSS používaným při tvorbě HTML stránek. Jeden z podstatných rozdílů spočívá v tom, jak jsou chápány selektory. V CSS je možné styl přiřadit libovolné značce (její jméno je selektorem), v PySide se namísto jména značky používají přímo jména tříd jednotlivých ovládacích prvků. Některé vlastnosti mají v HTML i PySide prakticky stejný význam (příkladem může být barva pozadí prvku), ovšem některé nalezneme jen v HTML (ty nás nebudou zajímat) a další zase pouze v PySide resp. v knihovně Qt.

Jména widgetů, u nichž je možné měnit jejich vlastnosti s využitím kaskádních stylů:

Název widgetu
QAbstractScrollArea
QCheckBox
QColumnView
QComboBox
QDateEdit
QDateTimeEdit
QDialog
QDialogButtonBox
QDockWidget
QDoubleSpinBox
QFrame
QGroupBox
QHeaderView
QLabel
QLineEdit
QListView
QListWidget
QMainWindow
QMenu
QMenuBar
QMessageBox
QProgressBar
QPushButton
QRadioButton
QScrollBar
QSizeGrip
QSlider
QSpinBox
QSplitter
QStatusBar
QTabBar
QTabWidget
QTableView
QTableWidget
QTextEdit
QTimeEdit
QToolBar
QToolButton
QToolBox
QToolTip
QTreeView
QTreeWidget
QWidget

Nastavit je možné následující vlastnosti, ovšem ne všechny samozřejmě mají význam u všech elementů (například u některých elementů nelze nastavit okraje – border atd.):

Jméno vlastnosti
alternate-background-color
background
background-color
background-image
background-repeat
background-position
background-attachment
background-clip
background-origin
border
border-top
border-right
border-bottom
border-left
border-color
border-top-color
border-right-color
border-bottom-color
border-left-color
border-image
border-radius
border-top-left-radius
border-top-right-radius
border-bottom-right-radius
border-bottom-left-radius
border-style
border-top-style
border-right-style
border-bottom-style
border-left-style
border-width
border-top-width
border-right-width
border-bottom-width
border-left-width
bottom
button-layout
color
dialogbuttonbox-buttons-have-icons
font
font-family
font-size
font-style
font-weight
gridline-color
height
icon-size
image
image-position
left
lineedit-password-character
lineedit-password-mask-delay
margin
margin-top
margin-right
margin-bottom
margin-left
max-height
max-width
messagebox-text-interaction-flags
min-height
min-width
opacity*
outline
outline-color
outline-offset
outline-style
outline-radius
outline-bottom-left-radius
outline-bottom-right-radius
outline-top-left-radius
outline-top-right-radius
padding
padding-top
padding-right
padding-bottom
padding-left
paint-alternating-row-colors-for-empty-area
position
right
selection-background-color
selection-color
show-decoration-selected
spacing
subcontrol-origin
subcontrol-position
titlebar-show-tooltips-on-buttons
widget-animation-duration
text-align
text-decoration
top
width

14. Stylesheet platný pro celou aplikaci

Podívejme se nyní, jakým způsobem je možné nastavit stylesheet, který bude platný pro celou aplikaci. Je to snadné. Nejdříve do řetězce uložíme vlastní stylesheet (klidně ho můžeme načíst z externího souboru atd.) a následně zavoláme metodu QApplication.setStyleSheet(sheet):

styleSheet = """
    QPushButton { color: #404040;
                  background-color: rgba(188, 188, 188, 50);
                  font-size: 12px;
                  border: 2px solid #c0c0c0;
                }
    QLabel { color: #404040;
             background-color: rgba(255, 188, 20, 50);
             font-size: 14px;
           }
"""
 
self.setStyleSheet(styleSheet)

Alternativně je možné stylesheet předat i přes parametry příkazové řádky:

./test.py -stylesheet jméno_souboru.css

15. Pátý demonstrační příklad – použití stylesheetu platného pro celou aplikaci

V dnešním pátém a současně i posledním demonstračním příkladu je ukázán způsob použití stylesheetu platného pro celou aplikaci. Ve stylesheetu nastavujeme vlastnosti pouze dvou widgetů, konkrétně textového popisku QLabel a standardního tlačítka QPushButton. Sami si můžete vyzkoušet vliv modifikace změny stylesheetu na způsob zobrazení hlavního okna aplikace.

Obrázek 28: Screenshot pátého demonstračního příkladu s aplikací vlastního stylesheetu.

MIF18 tip v článku témata

Opět následuje výpis zdrojového kódu tohoto demonstračního příkladu:

#!/usr/bin/env python
# vim: set fileencoding=utf-8
 
import sys
 
# import "jádra" frameworku Qt i modulu pro GUI
from PySide import QtCore
from PySide import QtGui
 
 
# 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()
 
        # další tlačítka
        button1 = self.prepareButtonWithBackground("#e08080")
        button2 = self.prepareButtonWithBackground("lightblue")
        button3 = self.prepareButtonWithBackground("yellow")
 
        # vytvoření správců geometrie
        topLayout = QtGui.QHBoxLayout()
 
        # vložení widgetů do okna
        topLayout.addWidget(button1)
        topLayout.addWidget(button2)
        topLayout.addWidget(button3)
        topLayout.addWidget(QtGui.QLabel(""))
        topLayout.addWidget(quitButton)
 
        # nastavení správce geometrie a vložení všech komponent do okna
        self.setLayout(topLayout)
 
    def prepareButtonWithBackground(self, background):
        # tlačítko s popisem
        button = QtGui.QPushButton(background, self)
 
        # nastavení stylu
        styleSheet = "background-color: {background}".format(background=background)
        button.setStyleSheet(styleSheet)
 
        button.resize(button.sizeHint())
 
        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(320, 240)
        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)
 
        # položka Style v hlavním menu
        styleMenu = menubar.addMenu('&Style')
 
        # jednotlivé položky menu s nabízenými styly
        for key in QtGui.QStyleFactory.keys():
            styleMenuItem = QtGui.QAction(key, self)
            styleMenuItem.triggered.connect(lambda key=key: self.setStyle(key))
            styleMenu.addAction(styleMenuItem)
 
        # 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())
 
        styleSheet = """
            QPushButton { color: #404040;
                          background-color: rgba(188, 188, 188, 50);
                          font-size: 12px;
                          border: 2px solid #c0c0c0;
                        }
            QLabel { color: #404040;
                     background-color: rgba(255, 188, 20, 50);
                     font-size: 14px;
                   }
        """
        self.setStyleSheet(styleSheet)
 
    def setStyle(self, styleName):
        # nastavení vybraného stylu
        QtGui.QApplication.setStyle(styleName)
 
    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")
    # QtGui.QApplication.setStyleSheet("background-color: #407040; color: white")
    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 pěti 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:

17. Odkazy na Internetu

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