Obsah
1. Nastavení stylů vykreslování widgetů, oken i dialogů v knihovně PySide
2. Konfigurace stylu přes argumenty zadané na příkazovém řádku
3. První demonstrační příklad – zobrazení většiny standardních widgetů a dialogů
4. Způsob vykreslování widgetů, standardní třídy se styly
5. Použití tříd QApplication a QStyleFactory pro změnu stylu vykreslování celé aplikace
6. Druhý demonstrační příklad – výběr stylu vykreslování pro celou aplikaci
7. Galerie standardních stylů dostupných na Linuxu
8. Styl aplikovaný na standardní dialog pro výběr barvy i pro zobrazení zprávy
9. Změna stylů jednotlivých widgetů pomocí metody QWidget.setStyle()
10. Třetí demonstrační příklad – nastavení individuálních stylů jednotlivých widgetů
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
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) ... ... ...
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/presentations/blob/master/Python_GUI/PySide/135_most_widgets.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:
Třída se stylem |
---|
QPlastiqueStyle |
QMacStyle |
QCleanlooksStyle |
QGtkStyle |
QMotifStyle |
QCDEStyle |
QWindowsStyle |
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)
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.
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/presentations. Pokud nechcete klonovat celý repositář, můžete namísto toho použít odkazy na jednotlivé příklady, které naleznete v následující tabulce:
# | Příklad | Adresa |
---|---|---|
1 | 135_most_widgets.py | https://github.com/tisnik/presentations/blob/master/Python_GUI/PySide/135_most_widgets.py |
2 | 136_set_style.py | https://github.com/tisnik/presentations/blob/master/Python_GUI/PySide/136_set_style.py |
3 | 137_widget_set_style.py | https://github.com/tisnik/presentations/blob/master/Python_GUI/PySide/137_widget_set_style.py |
4 | 138_widget_background.py | https://github.com/tisnik/presentations/blob/master/Python_GUI/PySide/138_widget_background.py |
5 | 139_custom_stylesheets.py | https://github.com/tisnik/presentations/blob/master/Python_GUI/PySide/139_custom_stylesheets.py |
6 | 140_print_available_styles.py | https://github.com/tisnik/presentations/blob/master/Python_GUI/PySide/140_print_available_styles.py |
17. Odkazy na Internetu
- PySide 1.2.1 documentation
https://pyside.github.io/docs/pyside/index.html - QStyle
https://pyside.github.io/docs/pyside/PySide/QtGui/QStyle.html - QCommonStyle
https://pyside.github.io/docs/pyside/PySide/QtGui/QCommonStyle.html - QPlastiqueStyle
https://pyside.github.io/docs/pyside/PySide/QtGui/QPlastiqueStyle.html - QMacStyle
https://pyside.github.io/docs/pyside/PySide/QtGui/QMacStyle.html - QCleanlooksStyle
https://pyside.github.io/docs/pyside/PySide/QtGui/QCleanlooksStyle.html - QGtkStyle
https://pyside.github.io/docs/pyside/PySide/QtGui/QGtkStyle.html - QCDEStyle
https://pyside.github.io/docs/pyside/PySide/QtGui/QCDEStyle.html - QMotifStyle
https://pyside.github.io/docs/pyside/PySide/QtGui/QMotifStyle.html - QWindowsStyle
https://pyside.github.io/docs/pyside/PySide/QtGui/QWindowsStyle.html" - QStyleFactory
https://pyside.github.io/docs/pyside/PySide/QtGui/QStyleFactory.html - QStyleOptionHeader
https://pyside.github.io/docs/pyside/PySide/QtGui/QStyleOptionHeader.html - QAbstractSlider
https://pyside.github.io/docs/pyside/PySide/QtGui/AbstractSlider.html - QScrollBar
https://pyside.github.io/docs/pyside/PySide/QtGui/ScrollBar.html - QSlider
https://pyside.github.io/docs/pyside/PySide/QtGui/Slider.html - QDial
https://pyside.github.io/docs/pyside/PySide/QtGui/Dial.html - QImage
https://pyside.github.io/docs/pyside/PySide/QtGui/QImage.html - QPixmap
https://pyside.github.io/docs/pyside/PySide/QtGui/QPixmap.html - QBitmap
https://pyside.github.io/docs/pyside/PySide/QtGui/QBitmap.html - QPaintDevice
https://pyside.github.io/docs/pyside/PySide/QtGui/QPaintDevice.html - QPicture
https://pyside.github.io/docs/pyside/PySide/QtGui/QPicture.html - QPainter
https://pyside.github.io/docs/pyside/PySide/QtGui/QPainter.html - QPainterPath
https://pyside.github.io/docs/pyside/PySide/QtGui/QPainterPath.html - QGradient
https://pyside.github.io/docs/pyside/PySide/QtGui/QGradient.html - QLinearGradient
https://pyside.github.io/docs/pyside/PySide/QtGui/QLinearGradient.html - QRadialGradient
https://pyside.github.io/docs/pyside/PySide/QtGui/QRadialGradient.html - QTableWidget
https://pyside.github.io/docs/pyside/PySide/QtGui/QTableWidget.html - QTableWidgetItem
https://pyside.github.io/docs/pyside/PySide/QtGui/QTableWidgetItem.html - QTreeWidget
https://pyside.github.io/docs/pyside/PySide/QtGui/QTreeWidget.html - QTreeWidgetItem
https://pyside.github.io/docs/pyside/PySide/QtGui/QTreeWidgetItem.html - Afinní zobrazení
https://cs.wikipedia.org/wiki/Afinn%C3%AD_zobrazen%C3%AD - Differences Between PySide and PyQt
https://wiki.qt.io/Differences_Between_PySide_and_PyQt - PySide 1.2.1 tutorials
https://pyside.github.io/docs/pyside/tutorials/index.html - PySide tutorial
http://zetcode.com/gui/pysidetutorial/ - Drawing in PySide
http://zetcode.com/gui/pysidetutorial/drawing/ - Qt Core
https://pyside.github.io/docs/pyside/PySide/QtCore/Qt.html - QLayout
https://pyside.github.io/docs/pyside/PySide/QtGui/QLayout.html - QValidator
https://pyside.github.io/docs/pyside/PySide/QtGui/QValidator.html - QStackedLayout
https://pyside.github.io/docs/pyside/PySide/QtGui/QStackedLayout.html - QFormLayout
https://pyside.github.io/docs/pyside/PySide/QtGui/QFormLayout.html - QBoxLayout
https://pyside.github.io/docs/pyside/PySide/QtGui/QBoxLayout.html - QHBoxLayout
https://pyside.github.io/docs/pyside/PySide/QtGui/QHBoxLayout.html - QVBoxLayout
https://pyside.github.io/docs/pyside/PySide/QtGui/QVBoxLayout.html - QGridLayout
https://pyside.github.io/docs/pyside/PySide/QtGui/QGridLayout.html - QAction
https://pyside.github.io/docs/pyside/PySide/QtGui/QAction.html - QDialog
https://pyside.github.io/docs/pyside/PySide/QtGui/QDialog.html - QMessageBox
https://pyside.github.io/docs/pyside/PySide/QtGui/QMessageBox.html - QErrorMessage
https://pyside.github.io/docs/pyside/PySide/QtGui/QErrorMessage.html - QInputDialog
https://pyside.github.io/docs/pyside/PySide/QtGui/QInputDialog.html - QColorDialog
https://pyside.github.io/docs/pyside/PySide/QtGui/QColorDialog.html - QListWidget
https://pyside.github.io/docs/pyside/PySide/QtGui/QListWidget.html - Signals & Slots
http://doc.qt.io/qt-4.8/signalsandslots.html - Signals and Slots in PySide
http://wiki.qt.io/Signals_and_Slots_in_PySide - Intro to PySide/PyQt: Basic Widgets and Hello, World!
http://www.pythoncentral.io/intro-to-pysidepyqt-basic-widgets-and-hello-world/ - QLineEdit
https://pyside.github.io/docs/pyside/PySide/QtGui/QLineEdit.html - QTextEdit
https://pyside.github.io/docs/pyside/PySide/QtGui/QTextEdit.html - QValidator
https://pyside.github.io/docs/pyside/PySide/QtGui/QValidator.html - QIntValidator
https://pyside.github.io/docs/pyside/PySide/QtGui/QIntValidator.html - QRegExpValidator
https://pyside.github.io/docs/pyside/PySide/QtGui/QRegExpValidator.html - QWidget
https://pyside.github.io/docs/pyside/PySide/QtGui/QWidget.html - QMainWindow
https://pyside.github.io/docs/pyside/PySide/QtGui/QMainWindow.html - QLabel
https://pyside.github.io/docs/pyside/PySide/QtGui/QLabel.html - QAbstractButton
https://pyside.github.io/docs/pyside/PySide/QtGui/QAbstractButton.html - QCheckBox
https://pyside.github.io/docs/pyside/PySide/QtGui/QCheckBox.html - QRadioButton
https://pyside.github.io/docs/pyside/PySide/QtGui/QRadioButton.html - QButtonGroup
https://pyside.github.io/docs/pyside/PySide/QtGui/QButtonGroup.html - QFrame
https://pyside.github.io/docs/pyside/PySide/QtGui/QFrame.html#PySide.QtGui.PySide.QtGui.QFrame - QFrame.frameStyle
https://pyside.github.io/docs/pyside/PySide/QtGui/QFrame.html#PySide.QtGui.PySide.QtGui.QFrame.frameStyle - Leo editor
http://leoeditor.com/ - IPython Qt Console aneb vylepšený pseudoterminál
https://mojefedora.cz/integrovana-vyvojova-prostredi-ve-fedore-ipython-a-ipython-notebook/#k06 - Vývojová prostředí ve Fedoře (4. díl)
https://mojefedora.cz/vyvojova-prostredi-ve-fedore-4-dil/ - Seriál Letní škola programovacího jazyka Logo
http://www.root.cz/serialy/letni-skola-programovaciho-jazyka-logo/ - Educational programming language
http://en.wikipedia.org/wiki/Educational_programming_language - Logo Tree Project:
http://www.elica.net/download/papers/LogoTreeProject.pdf - Hra Breakout napísaná v Tkinteri
https://www.root.cz/clanky/hra-breakout-napisana-v-tkinteri/ - Hra Snake naprogramovaná v Pythone s pomocou Tkinter
https://www.root.cz/clanky/hra-snake-naprogramovana-v-pythone-s-pomocou-tkinter/ - 24.1. turtle — Turtle graphics
https://docs.python.org/3.5/library/turtle.html#module-turtle - TkDND
http://freecode.com/projects/tkdnd - Python Tkinter Fonts
https://www.tutorialspoint.com/python/tk_fonts.htm - The Tkinter Canvas Widget
http://effbot.org/tkinterbook/canvas.htm - Ovládací prvek (Wikipedia)
https://cs.wikipedia.org/wiki/Ovl%C3%A1dac%C3%AD_prvek_%28po%C4%8D%C3%ADta%C4%8D%29 - Rezervovaná klíčová slova v Pythonu
https://docs.python.org/3/reference/lexical_analysis.html#keywords - TkDocs: Styles and Themes
http://www.tkdocs.com/tutorial/styles.html - Drawing in Tkinter
http://zetcode.com/gui/tkinter/drawing/ - Changing ttk widget text color (StackOverflow)
https://stackoverflow.com/questions/16240477/changing-ttk-widget-text-color - The Hitchhiker's Guide to Pyhton: GUI Applications
http://docs.python-guide.org/en/latest/scenarios/gui/ - 7 Top Python GUI Frameworks for 2017
http://insights.dice.com/2014/11/26/5-top-python-guis-for-2015/ - GUI Programming in Python
https://wiki.python.org/moin/GuiProgramming - Cameron Laird's personal notes on Python GUIs
http://phaseit.net/claird/comp.lang.python/python_GUI.html - Python GUI development
http://pythoncentral.io/introduction-python-gui-development/ - Graphic User Interface FAQ
https://docs.python.org/2/faq/gui.html#graphic-user-interface-faq - TkInter
https://wiki.python.org/moin/TkInter - Tkinter 8.5 reference: a GUI for Python
http://infohost.nmt.edu/tcc/help/pubs/tkinter/web/index.html - TkInter (Wikipedia)
https://en.wikipedia.org/wiki/Tkinter - appJar
http://appjar.info/ - appJar (Wikipedia)
https://en.wikipedia.org/wiki/AppJar - appJar na Pythonhosted
http://pythonhosted.org/appJar/ - appJar widgets
http://appjar.info/pythonWidgets/ - Stránky projektu PyGTK
http://www.pygtk.org/ - PyGTK (Wikipedia)
https://cs.wikipedia.org/wiki/PyGTK - Stránky projektu PyGObject
https://wiki.gnome.org/Projects/PyGObject - Stránky projektu Kivy
https://kivy.org/#home - Stránky projektu PyQt
https://riverbankcomputing.com/software/pyqt/intro - PyQt (Wikipedia)
https://cs.wikipedia.org/wiki/PyGTK - Stránky projektu PySide
https://wiki.qt.io/PySide - PySide (Wikipedia)
https://en.wikipedia.org/wiki/PySide - Stránky projektu Kivy
https://kivy.org/#home - Kivy (framework, Wikipedia)
https://en.wikipedia.org/wiki/Kivy_(framework) - QML Applications
http://doc.qt.io/qt-5/qmlapplications.html - KDE
https://www.kde.org/ - Qt
https://www.qt.io/ - GNOME
https://en.wikipedia.org/wiki/GNOME - Category:Software that uses PyGTK
https://en.wikipedia.org/wiki/Category:Software_that_uses_PyGTK - Category:Software that uses PyGObject
https://en.wikipedia.org/wiki/Category:Software_that_uses_PyGObject - Category:Software that uses wxWidgets
https://en.wikipedia.org/wiki/Category:Software_that_uses_wxWidgets - GIO
https://developer.gnome.org/gio/stable/ - GStreamer
https://gstreamer.freedesktop.org/ - GStreamer (Wikipedia)
https://en.wikipedia.org/wiki/GStreamer - Wax Gui Toolkit
https://wiki.python.org/moin/Wax - Python Imaging Library (PIL)
http://infohost.nmt.edu/tcc/help/pubs/pil/ - Why Pyjamas Isn’t a Good Framework for Web Apps (blogpost z roku 2012)
http://blog.pyjeon.com/2012/07/29/why-pyjamas-isnt-a-good-framework-for-web-apps/