Obsah
1. Tvorba GUI v Pythonu s využitím frameworku PySide: widgety pro textový vstup
2. Jednořádkové vstupní textové pole
3. První demonstrační příklad: zobrazení textového pole, čtení zapsaného textu
5. Omezení maximálního počtu zapsaných znaků
7. Druhý demonstrační příklad: vylepšené textové pole
8. Definice vstupní masky pro zápis specifických vstupních dat
9. Třetí demonstrační příklad: vylepšené textové pole se vstupní maskou
10. Použití validátorů pro textové pole
11. Čtvrtý demonstrační příklad: validátor typu QIntValidator
12. Pátý demonstrační příklad: validátor typu QRegExpValidator
13. Ovládací prvek pro zápis i zobrazení víceřádkových dokumentů s formátováním
14. Šestý demonstrační příklad: widget QTextEdit ve funkci víceřádkového textového pole
16. Sedmý demonstrační příklad: widget QTextEdit ve funkci jednoduchého prohlížeče HTML stránek
17. Repositář s demonstračními příklady
1. Tvorba GUI v Pythonu s využitím frameworku PySide: widgety pro textový vstup
V dnešním pokračování seriálu o tvorbě aplikací s grafickým uživatelským rozhraním v Pythonu se budeme zabývat popisem dalších ovládacích prvků, které nalezneme v knihovně PySide. Zaměříme se přitom na widgety sloužící pro vstup textu, ať již textu jednořádkového, víceřádkového či dokonce tzv. rich textu, tj. textu, který je naformátován a který může v případě potřeby obsahovat i další objekty. V knihovně PySide nalezneme následující čtyři ovládací prvky, které jsou pro tyto účely připravené:
QLineEdit | vstup jednoho řádku textu |
QPlainTextEdit | jednoduché vstupní víceřádkové textové pole |
QTextEdit | vstupní textové pole (umožňuje práci s tabulkami, obrázky atd.) |
QTextBrowser | komponenta s textem, který může obsahovat hypertextové odkazy |
2. Jednořádkové vstupní textové pole
Prvním ovládacím prvkem, s nímž se v dnešním článku seznámíme, je widget nazvaný jednoduše QLineEdit. Jak již jeho název napovídá, je tento prvek určen pro vstup jednořádkového textu a vzhledem k velmi časté potřebě zadat do aplikace jediný údaj (adresa, jméno uživatele, jeho heslo, PIN, název vyhledávaného výrobku atd.) patří tento typ widgetů mezi nejpoužívanější prvky grafického uživatelského rozhraní vůbec. Ovšem aby nedošlo k omylu: QLineEdit je sice skutečně možné použít pro pouhý vstup textu, ale jeho možnosti jsou ve skutečnosti větší, protože je například umožněno specifikovat masku určující, jak má text zadávaný uživatelem vypadat, takže je možné například připravit vstupní textové pole určené pro zápis strukturovaného telefonního čísla, DIČ atd. Dále lze k textovému poli přidat takzvaný validátor, což je objekt kontrolující, zda uživatelem zapsaný text odpovídá nějakým kritériím, například zda se jedná o celé číslo z určeného rozsahu, platné datum apod. Vše si samozřejmě ukážeme v několika demonstračních příkladech.

Obrázek 1: Několik vstupních textových polí v dialogu pro vytvoření nového projektu v IDE Eric.
3. První demonstrační příklad: zobrazení textového pole, čtení zapsaného textu
Ukažme si nyní nejjednodušší formu jednořádkového vstupního textového pole. To vytvoříme snadno konstruktorem QLineEdit, kterému předáme jediný parametr – referenci na kontejner, ve kterém se bude textové pole nacházet:
lineEdit = QtGui.QLineEdit(self)

Obrázek 2: Textové pole se zobrazí zcela běžným způsobem a při práci s textem je možné použít schránku (clipboard), selection buffer atd.
Textové pole umístíme běžným způsobem do kontejneru s využitím správců geometrie. Aby byl příklad trošku složitější, zkombinujeme možnosti dvou správců geometrie: vertikálního a horizontálního:
# vytvoření prvního správce geometrie topLayout = QtGui.QVBoxLayout() # vytvoření druhého správce geometrie subLayout = QtGui.QHBoxLayout() # umístění widgetů do okna topLayout.addWidget(self.lineEdit) topLayout.addLayout(subLayout) # tlačítka vložíme do druhého správce geometrie subLayout.addWidget(showTextButton) subLayout.addWidget(quitButton) # nastavení správce geometrie a vložení všech komponent do okna self.setLayout(topLayout)
Text zadaný uživatelem do vstupního textového pole přečteme pomocí metody text():
text = self.lineEdit.text()

Obrázek 3: Při práci s textovým polem je plně podporován Unicode, a to jak při zápisu textu, tak i při jeho čtení Pythonovským programem.
Následuje výpis zdrojového kódu prvního demonstračního příkladu, v němž je použito textové pole a po stisku tlačítka „Show text“ se zobrazí dialog s textem, který uživatel do pole zapsal:
#!/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): quitButton = self.prepareQuitButton() showTextButton = self.prepareShowTextButton() self.lineEdit = self.prepareLineEdit() # vytvoření prvního správce geometrie topLayout = QtGui.QVBoxLayout() # vytvoření druhého správce geometrie subLayout = QtGui.QHBoxLayout() # umístění widgetů do okna topLayout.addWidget(self.lineEdit) topLayout.addLayout(subLayout) # tlačítka vložíme do druhého správce geometrie subLayout.addWidget(showTextButton) subLayout.addWidget(quitButton) # nastavení správce geometrie a vložení všech komponent do okna self.setLayout(topLayout) def prepareShowTextButton(self): # druhé tlačítko showTextButton = QtGui.QPushButton('Show text', self) showTextButton.resize(showTextButton.sizeHint()) # navázání akce na signál showTextButton.clicked.connect(self.showTextDialog) return showTextButton def prepareQuitButton(self): # tlačítko 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 prepareLineEdit(self): # jednořádkové vstupní textové pole lineEdit = QtGui.QLineEdit(self) return lineEdit def showTextDialog(self): msgBox = QtGui.QMessageBox() text = self.lineEdit.text() msgBox.setText(u'Text: {t}'.format(t=text)) msgBox.setIcon(QtGui.QMessageBox.Information) msgBox.exec_() # nový widget bude odvozen od obecného hlavního okna class MainWindow(QtGui.QMainWindow): def __init__(self): # zavoláme konstruktor předka super(MainWindow, self).__init__() # konfigurace GUI + přidání widgetu do okna self.prepareGUI() def prepareGUI(self): # velikost není potřeba specifikovat # self.resize(320, 240) self.setWindowTitle("QMainWindow + QListWidget") # vložení komponenty do okna self.setCentralWidget(MainWindowContent()) 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. Nastavení výchozího textu
Pokud je zapotřebí, aby se v textovém poli už při zobrazení okna nebo dialogu nacházel výchozí text, lze pro tento účel použít metodu nazvanou příhodně setText, které se řetězec předá:
# jednořádkové vstupní textové pole lineEdit = QtGui.QLineEdit(self) lineEdit.setText(u'příliš žluťoučký kůň')
Poznámka: prefix „u“ je u řetězce uveden kvůli kompatibilitě s Pythonem 2.x.
Alternativně je možné text předat již konstruktoru objektů typu QLineEdit. Jedná se o první parametr, protože reference na kontejner, do kterého se textové pole ukládá, se posune na druhou pozici:
# jednořádkové vstupní textové pole lineEdit = QtGui.QLineEdit(u'příliš žluťoučký kůň', self)

Obrázek 4: Jednořádkové textové pole s výchozím textem.
5. Omezení maximálního počtu zapsaných znaků
Poměrně často se setkáme s nutností omezit maximální počet znaků, které se do textového pole mohou zadat. Výchozí maximální kapacita je totiž nastavena na hodnotu 32767, což je pro naprostou většinu aplikací příliš mnoho. Omezení maximálního počtu znaků je snadné – stačí zavolat metodu setMaxLength, které se počet znaků předá:
# jednořádkové vstupní textové pole lineEdit = QtGui.QLineEdit(self) lineEdit.setText(u'příliš žluťoučký kůň') lineEdit.setMaxLength(20)
Poznámka: zadaná hodnota 20 skutečně odpovídá počtu znaků, nikoli bajtů. Pozor je nutné dát především na to, že například výše použitý řetězec „příliš žluťoučký kůň“ má přesně dvacet znaků, ovšem například při použití UTF-8 je jeho délka 30 bajtů. Na rozdíly narazíte například při použití některých relačních databází, které počítají délku řetězců v bajtech.
Pokud se uživatel pokusí zapsat delší řetězec, nebude tato operace povolena. Ani při snaze zkopírovat delší řetězec přes schránku se nezapíše více, než nastavený maximální počet znaků. Totéž ovšem platí i pro text nastavený přes metodu setText. Ostatně si vyzkoušejte sami, co se stane:
# jednořádkové vstupní textové pole lineEdit = QtGui.QLineEdit(self) lineEdit.setText(u'tento řetězec je v každém případě delší než 20 znaků') lineEdit.setMaxLength(20)
6. Nastavení zarovnání textu
V některých případech může být výhodné, aby byl text v jednořádkovém textovém poli zarovnaný doprava či na střed a nikoli doleva. I toho je možné dosáhnout, a to velmi snadno. Podívejme se na následující úryvek kódu, v němž je zarovnání nastaveno s využitím metody nazvané setAlignment:
# jednořádkové vstupní textové pole lineEdit = QtGui.QLineEdit(self) lineEdit.setText(u'příliš žluťoučký kůň') lineEdit.setMaxLength(20) lineEdit.setAlignment(QtCore.Qt.AlignRight)

Obrázek 5: Jednořádkové textové pole s výchozím textem, který je zarovnán doprava.
Pro nastavení zarovnání je možné použít tyto konstanty a jejich kombinace:
Konstanta | Význam |
---|---|
Qt.AlignLeft | zarovnání doleva (výchozí hodnota) |
Qt.AlignRight | zarovnání doprava |
Qt.AlignHCenter | vycentrování textu |
Qt.AlignJustify | u tohoto widgetu odpovídá Qt.AlignLeft |
Qt.AlignTop | vertikální zarovnání nahoru |
Qt.AlignBottom | vertikální zarovnání dolů |
Qt.AlignVCenter | vertikální zarovnání na střed (výchozí hodnota) |
Qt.AlignCenter | horizontální i vertikální zarovnání na střed |
7. Druhý demonstrační příklad: vylepšené textové pole
V dnešním druhém demonstračním příkladu je ukázáno několik modifikací textového pole (všechny jsme si popsali v předchozích třech kapitolách):
- Je specifikován výchozí text, který je v textovém poli implicitně zobrazen.
- Maximální počet znaků, které je možné zapsat, je omezen na 20.
- Text je zarovnán doprava a nikoli doleva (horizontální zarovnání na střed zůstalo nezměněno, většinou ani nemá příliš velký význam se ho pokoušet změnit).

Obrázek 6: Po přečtení textu z widgetu QLineEdit získáme plnohodnotný text v Unicode.
Následuje výpis zdrojového kódu 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() def prepareGUI(self): quitButton = self.prepareQuitButton() showTextButton = self.prepareShowTextButton() self.lineEdit = self.prepareLineEdit() # vytvoření prvního správce geometrie topLayout = QtGui.QVBoxLayout() # vytvoření druhého správce geometrie subLayout = QtGui.QHBoxLayout() # umístění widgetů do okna topLayout.addWidget(self.lineEdit) topLayout.addLayout(subLayout) # tlačítka vložíme do druhého správce geometrie subLayout.addWidget(showTextButton) subLayout.addWidget(quitButton) # nastavení správce geometrie a vložení všech komponent do okna self.setLayout(topLayout) def prepareShowTextButton(self): # druhé tlačítko showTextButton = QtGui.QPushButton('Show text', self) showTextButton.resize(showTextButton.sizeHint()) # navázání akce na signál showTextButton.clicked.connect(self.showTextDialog) return showTextButton def prepareQuitButton(self): # tlačítko 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 prepareLineEdit(self): # jednořádkové vstupní textové pole lineEdit = QtGui.QLineEdit(self) lineEdit.setText(u'příliš žluťoučký kůň') lineEdit.setMaxLength(20) lineEdit.setAlignment(QtCore.Qt.AlignRight) return lineEdit def showTextDialog(self): msgBox = QtGui.QMessageBox() text = self.lineEdit.text() msgBox.setText(u'Text: {t}'.format(t=text)) msgBox.setIcon(QtGui.QMessageBox.Information) msgBox.exec_() # nový widget bude odvozen od obecného hlavního okna class MainWindow(QtGui.QMainWindow): def __init__(self): # zavoláme konstruktor předka super(MainWindow, self).__init__() # konfigurace GUI + přidání widgetu do okna self.prepareGUI() def prepareGUI(self): # velikost není potřeba specifikovat # self.resize(320, 240) self.setWindowTitle("QMainWindow + QListWidget") # vložení komponenty do okna self.setCentralWidget(MainWindowContent()) 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()
8. Definice vstupní masky pro zápis specifických vstupních dat
Pro jednořádkové textové pole je možné specifikovat i takzvanou masku, která určuje, jaké typy znaků a na jakých pozicích se mohou v poli objevit. Ukažme si jednoduchý příklad. Pokud budeme chtít zajistit, aby uživatel do textového pole mohl zadat pouze devítimístné telefonní číslo (a žádné další znaky), může použít tuto masku:
# jednořádkové vstupní textové pole lineEdit = QtGui.QLineEdit(self) lineEdit.setMaxLength(9) lineEdit.setInputMask('999999999')

Obrázek 7: Takto vypadá textové pole, v němž je zadaná pouze vstupní maska, ale žádný text napsaný uživatelem.
Maska však může být i složitější. V případě, že číslice mají být odděleny mezerou (nebo libovolným jiným znakem), lze to opět snadno zařídit:
# jednořádkové vstupní textové pole lineEdit = QtGui.QLineEdit(self) lineEdit.setMaxLength(11) lineEdit.setInputMask('999 999 999')

Obrázek 8: Zápis telefonního čísla. Mezery mezi trojicemi číslic jsou do pole vloženy automaticky.
Při definici masky se používají následující zástupné znaky:
Zástupný znak | Význam |
---|---|
A | znaky velké i malé abecedy |
a | dtto, ovšem znak(y) nejsou na daném místě povinné |
N | znaky velké i malé abecedy + číslice |
n | dtto, ovšem znak(y) nejsou na daném místě povinné |
X | libovolný znak (povinný) |
x | libovolný znak (nepovinný) |
9 | číslice 0–9 |
0 | nepovinná číslice 0–9 |
D | číslice 1–9 (bez nuly) |
d | nepovinná číslice 1–9 |
# | číslice 0–9 popř. znak +, – |
H | hexadecimální číslice 0–9, a-f, A-F |
h | nepovinná hexadecimální číslice 0–9, a-f, A-F |
B | binární číslice 0 nebo 1 |
b | nepovinná binární číslice 0 nebo 1 |
> | další znaky budou převedeny na velká písmena |
< | další znaky budou převedeny na malá písmena |
! | ruší platnost > a < |
\ |

Obrázek 9: Tuto podobu má telefonní číslo přečtení z widgetu. Povšimněte si existencí mezer předepsaných maskou.
Dokonce je možné určit, jaké znaky se v textovém poli zobrazí na těch místech, která mají být uživatelem vyplněna. Tento znak zapište za středník. Pro jednoduchost použijeme podtržítko, ale můžete zkusit použít hvězdičku, otazník atd.:
# jednořádkové vstupní textové pole lineEdit = QtGui.QLineEdit(self) lineEdit.setMaxLength(11) lineEdit.setInputMask('999 999 999;_')
Poznámka: podtržítka jsou sice ve vstupním textovém poli viditelná, ale ve skutečnosti nejsou součástí textu vráceného metodou text().
9. Třetí demonstrační příklad: vylepšené textové pole se vstupní maskou
Ve třetím demonstračním příkladu je ukázáno, jak lze použít textové pole se vstupní maskou. Maskou specifikujeme, že textové pole bude akceptovat zápis devítimístného telefonního čísla, v němž jsou vždy trojice číslic od sebe odděleny mezerou. Podívejme se na 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() def prepareGUI(self): quitButton = self.prepareQuitButton() showTextButton = self.prepareShowTextButton() self.lineEdit = self.prepareLineEdit() # vytvoření prvního správce geometrie topLayout = QtGui.QVBoxLayout() # vytvoření druhého správce geometrie subLayout = QtGui.QHBoxLayout() # umístění widgetů do okna topLayout.addWidget(self.lineEdit) topLayout.addLayout(subLayout) # tlačítka vložíme do druhého správce geometrie subLayout.addWidget(showTextButton) subLayout.addWidget(quitButton) # nastavení správce geometrie a vložení všech komponent do okna self.setLayout(topLayout) def prepareShowTextButton(self): # druhé tlačítko showTextButton = QtGui.QPushButton('Show text', self) showTextButton.resize(showTextButton.sizeHint()) # navázání akce na signál showTextButton.clicked.connect(self.showTextDialog) return showTextButton def prepareQuitButton(self): # tlačítko 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 prepareLineEdit(self): # jednořádkové vstupní textové pole lineEdit = QtGui.QLineEdit(self) lineEdit.setPlaceholderText(u'telefonní číslo') lineEdit.setMaxLength(11) lineEdit.setInputMask('999 999 999;_') return lineEdit def showTextDialog(self): msgBox = QtGui.QMessageBox() text = self.lineEdit.text() msgBox.setText(u'Text: {t}'.format(t=text)) msgBox.setIcon(QtGui.QMessageBox.Information) msgBox.exec_() # nový widget bude odvozen od obecného hlavního okna class MainWindow(QtGui.QMainWindow): def __init__(self): # zavoláme konstruktor předka super(MainWindow, self).__init__() # konfigurace GUI + přidání widgetu do okna self.prepareGUI() def prepareGUI(self): # velikost není potřeba specifikovat # self.resize(320, 240) self.setWindowTitle("QMainWindow + QListWidget") # vložení komponenty do okna self.setCentralWidget(MainWindowContent()) 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()
10. Použití validátorů pro textové pole
Masky specifikované pro textová pole sice umožňují do určité míry zajistit, že uživatel do pole zadá jen správné znaky, ovšem to v mnoha případech nemusí být dostačující kontrola. Například vstupní pole pro zadání rodného čísla by mělo tolerovat pouze takové vstupy, které jsou dělitelné jedenácti (s několika zákonem stanovenými výjimkami). Takovéto složitější podmínky již není možné zajistit pouze pomocí masky a je nutné využít složitější techniku. Jedná se o takzvané validátory, které mohou být k textovému poli přiřazeny metodou setValidator:
# jednořádkové vstupní textové pole lineEdit = QtGui.QLineEdit(self) lineEdit.setValidator(........)
Validátor je objekt s několika metodami, především s metodami validate a fixup. Metoda validate je průběžně volána při editaci textového pole a může na základě aktuálně zadaného vstupu vracet hodnotu Invalid (vstup je zcela jistě špatný), Intermediate (vstup ještě není dokončen, ale není zcela špatný – například se teprve zapisují první číslice rodného čísla) a Acceptable (vstup je korektní). Naproti tomu metoda fixup může na konci editace provést v textu změny, typicky odmazání bílých znaků ze začátku a konce, vymazání mezer mezi číslicemi atd.
Kromě programátorem deklarovaných validátorů existuje i několik připravených validátorů. S nimi se setkáme v navazujících dvou kapitolách.
11. Čtvrtý demonstrační příklad: validátor typu QIntValidator
Podívejme se nyní na použití validátoru typu QIntValidator. Jak již víme, je možné tento validátor použít ve chvíli, kdy potřebujeme omezit údaje zadávané do textového pole na celá čísla v předem známém rozsahu. Řešení ve chvíli, kdy například vyžadujeme zadání celého čísla od 1 do 15000, je snadné:
# jednořádkové vstupní textové pole lineEdit = QtGui.QLineEdit(self) lineEdit.setPlaceholderText(u'cena') lineEdit.setMaxLength(11) lineEdit.setValidator(QtGui.QIntValidator(1, 15000, self)) return lineEdit

Obrázek 10: Pokud číslo začíná na 9, bude povoleno zadat maximálně čtyři číslice.

Obrázek 11: Pokud číslo začíná na 1, bude povoleno zadat i pět číslic, ale jen do maximální hodnoty 15000.
Zdrojový kód tohoto 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): quitButton = self.prepareQuitButton() showTextButton = self.prepareShowTextButton() self.lineEdit = self.prepareLineEdit() # vytvoření prvního správce geometrie topLayout = QtGui.QVBoxLayout() # vytvoření druhého správce geometrie subLayout = QtGui.QHBoxLayout() # umístění widgetů do okna topLayout.addWidget(self.lineEdit) topLayout.addLayout(subLayout) # tlačítka vložíme do druhého správce geometrie subLayout.addWidget(showTextButton) subLayout.addWidget(quitButton) # nastavení správce geometrie a vložení všech komponent do okna self.setLayout(topLayout) def prepareShowTextButton(self): # druhé tlačítko showTextButton = QtGui.QPushButton('Show text', self) showTextButton.resize(showTextButton.sizeHint()) # navázání akce na signál showTextButton.clicked.connect(self.showTextDialog) return showTextButton def prepareQuitButton(self): # tlačítko 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 prepareLineEdit(self): # jednořádkové vstupní textové pole lineEdit = QtGui.QLineEdit(self) lineEdit.setPlaceholderText(u'cena') lineEdit.setMaxLength(11) lineEdit.setValidator(QtGui.QIntValidator(1, 15000, self)) return lineEdit def showTextDialog(self): msgBox = QtGui.QMessageBox() text = self.lineEdit.text() msgBox.setText(u'Text: {t}'.format(t=text)) msgBox.setIcon(QtGui.QMessageBox.Information) msgBox.exec_() # nový widget bude odvozen od obecného hlavního okna class MainWindow(QtGui.QMainWindow): def __init__(self): # zavoláme konstruktor předka super(MainWindow, self).__init__() # konfigurace GUI + přidání widgetu do okna self.prepareGUI() def prepareGUI(self): # velikost není potřeba specifikovat # self.resize(320, 240) self.setWindowTitle("QMainWindow + QListWidget") # vložení komponenty do okna self.setCentralWidget(MainWindowContent()) 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()
12. Pátý demonstrační příklad: validátor typu QRegExpValidator
Podobně jako validátor QIntValidator můžeme použít i další validátor, který je přímo součástí frameworku PySide. Tento validátor se jmenuje QRegExpValidator a již podle jeho názvu můžeme poznat, že slouží k validaci textu zadaného uživatelem na základě regulárního výrazu. Použití tohoto validátoru je snadné; ukažme si tedy jeho využití pro validaci, zda zadaný text odpovídá výrazu regulárnímu [a-z]{6}[0–9]{2} (šest znaků malé abecedy následované dvěma číslicemi – takto omezeny jsou loginy do mnoha systémů, i když dnes tato omezení většinou postrádají původní význam). Regulární výraz je reprezentován instancí objektu typu QRegExp:
# jednořádkové vstupní textové pole lineEdit = QtGui.QLineEdit(self) lineEdit.setPlaceholderText(u'login name') lineEdit.setMaxLength(8) regexp = QtCore.QRegExp("[a-z]{6}[0-9]{2}") lineEdit.setValidator(QtGui.QRegExpValidator(regexp, self))

Obrázek 12: Použití validátoru typu QRegExpValidator.
Opět následuje výpis zdrojového kódu 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() def prepareGUI(self): quitButton = self.prepareQuitButton() showTextButton = self.prepareShowTextButton() self.lineEdit = self.prepareLineEdit() # vytvoření prvního správce geometrie topLayout = QtGui.QVBoxLayout() # vytvoření druhého správce geometrie subLayout = QtGui.QHBoxLayout() # umístění widgetů do okna topLayout.addWidget(self.lineEdit) topLayout.addLayout(subLayout) # tlačítka vložíme do druhého správce geometrie subLayout.addWidget(showTextButton) subLayout.addWidget(quitButton) # nastavení správce geometrie a vložení všech komponent do okna self.setLayout(topLayout) def prepareShowTextButton(self): # druhé tlačítko showTextButton = QtGui.QPushButton('Show text', self) showTextButton.resize(showTextButton.sizeHint()) # navázání akce na signál showTextButton.clicked.connect(self.showTextDialog) return showTextButton def prepareQuitButton(self): # tlačítko 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 prepareLineEdit(self): # jednořádkové vstupní textové pole lineEdit = QtGui.QLineEdit(self) lineEdit.setPlaceholderText(u'login name') lineEdit.setMaxLength(8) regexp = QtCore.QRegExp("[a-z]{6}[0-9]{2}") lineEdit.setValidator(QtGui.QRegExpValidator(regexp, self)) return lineEdit def showTextDialog(self): msgBox = QtGui.QMessageBox() text = self.lineEdit.text() msgBox.setText(u'Text: {t}'.format(t=text)) msgBox.setIcon(QtGui.QMessageBox.Information) msgBox.exec_() # nový widget bude odvozen od obecného hlavního okna class MainWindow(QtGui.QMainWindow): def __init__(self): # zavoláme konstruktor předka super(MainWindow, self).__init__() # konfigurace GUI + přidání widgetu do okna self.prepareGUI() def prepareGUI(self): # velikost není potřeba specifikovat # self.resize(320, 240) self.setWindowTitle("QMainWindow + QListWidget") # vložení komponenty do okna self.setCentralWidget(MainWindowContent()) 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()
13. Ovládací prvek pro zápis i zobrazení víceřádkových dokumentů s formátováním
Další widget, s nímž se dnes seznámíme, slouží pro zobrazení a vkládání víceřádkového textu. Ve skutečnosti dokonce existují dva takové widgety; jeden z nich se jmenuje QPlainTextEdit a druhý QTextEdit. První z těchto ovládacích prvků dobře poslouží v případě, že potřebujeme pracovat s plain textem (zdrojové kódy atd.), druhý widget již umožňuje použití rich textu, tedy formátování znaků, slov i celých odstavců. První widget se vytvoří snadno:
# víceřádkové vstupní textové pole textEdit = QtGui.QPlainTextEdit(self)
Podobného chování dosáhneme i u druhého typu widgetu:
# víceřádkové vstupní textové pole textEdit = QtGui.QTextEdit(self) textEdit.setAcceptRichText(False)

Obrázek 13: Víceřádkový dokument (část zdrojového kódu) zobrazený bez použití rich text formátu.
Pokud potřebujeme získat text zadaný uživatelem, postačuje u obou widgetu zavolat metodu toPlainText, která text vrátí a to včetně znaků pro konec řádku (\n):
text = self.textEdit.toPlainText()
14. Šestý demonstrační příklad: widget QTextEdit ve funkci víceřádkového textového pole
V dnešním předposledním demonstračním příkladu je ukázáno použití widgetu typu QTextEdit pro zobrazení víceřádkového textu:
#!/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): quitButton = self.prepareQuitButton() showTextButton = self.prepareShowTextButton() self.textEdit = self.prepareTextEdit() # vytvoření prvního správce geometrie topLayout = QtGui.QVBoxLayout() # vytvoření druhého správce geometrie subLayout = QtGui.QHBoxLayout() # umístění widgetů do okna topLayout.addWidget(self.textEdit) topLayout.addLayout(subLayout) # tlačítka vložíme do druhého správce geometrie subLayout.addWidget(showTextButton) subLayout.addWidget(quitButton) # nastavení správce geometrie a vložení všech komponent do okna self.setLayout(topLayout) def prepareShowTextButton(self): # druhé tlačítko showTextButton = QtGui.QPushButton('Show text', self) showTextButton.resize(showTextButton.sizeHint()) # navázání akce na signál showTextButton.clicked.connect(self.showTextDialog) return showTextButton def prepareQuitButton(self): # tlačítko 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 prepareTextEdit(self): # víceřádkové vstupní textové pole textEdit = QtGui.QTextEdit(self) textEdit.setAcceptRichText(False) return textEdit def showTextDialog(self): msgBox = QtGui.QMessageBox() text = self.textEdit.toPlainText() msgBox.setText(u'Text:\n----------------\n{t}\n----------------'.format(t=text)) msgBox.setIcon(QtGui.QMessageBox.Information) msgBox.exec_() # nový widget bude odvozen od obecného hlavního okna class MainWindow(QtGui.QMainWindow): def __init__(self): # zavoláme konstruktor předka super(MainWindow, self).__init__() # konfigurace GUI + přidání widgetu do okna self.prepareGUI() def prepareGUI(self): # velikost není potřeba specifikovat # self.resize(320, 240) self.setWindowTitle("QMainWindow + QListWidget") # vložení komponenty do okna self.setCentralWidget(MainWindowContent()) 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()
15. Základní zpracování HTML
Podívejme se nyní na způsob, jakým je možné ovládací prvek QTextEdit použít ve funkci jednoduchého zobrazovače HTML stránek (může se například jednat o dokumentaci k aplikaci atd.). Nejprve prvek jednoduše vytvoříme:
textEdit = QtGui.QTextEdit(self)
Dále nastavíme dvě důležité vlastnosti – prvek bude podporovat rich text formát a navíc nebude editovatelný (tj. bude read only). Vlastnost read only je ovšem platná pro vstup od uživatele (ten nebude povolen), zatímco programové změny obsahu textového pole povoleny jsou:
textEdit.setAcceptRichText(True) textEdit.setReadOnly(True)
V posledním kroku se pokusíme do widgetu načíst obsah HTML stránky:
import io with io.open("test.html", encoding="utf-8") as fin: textEdit.setHtml(fin.read())
Poznámka: funkci io.open zde používám z toho důvodu, aby bylo možné specifikovat kódování HTML stránky, a to i v Pythonu 2.x.

Obrázek 14: Na tomto screenshotu si povšimněte, že jsou správně zpracovány i styly.
16. Sedmý demonstrační příklad: widget QTextEdit ve funkci jednoduchého prohlížeče HTML stránek
V sedmém a současně i dnešním posledním příkladu si ukážeme, jakým způsobem je možné vytvořit velmi jednoduchý prohlížeč HTML stránek. Tento prohlížeč dokáže pracovat s většinou základních značek HTML, zpracuje CSS (i když některé typy selektorů nejsou podporovány) a zobrazuje i tabulky a obrázky. Ovšem například na odkazy budete klikat marně – aby fungovaly tak, jak se očekává, bylo by nutné namísto widgetu QTextEdit použít od něj odvozený widget QTextBrowser, s jehož možnostmi se podrobněji seznámíme v navazujícím článku.

Obrázek 15: V dolní části tohoto screenshotu můžete vidět způsob zobrazení tabulek.
Opět se podívejme na výpis zdrojového kódu dnešního posledního demonstračního příkladu:
#!/usr/bin/env python # vim: set fileencoding=utf-8 import sys import io # 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): quitButton = self.prepareQuitButton() self.textEdit = self.prepareTextEdit() # vytvoření správce geometrie topLayout = QtGui.QVBoxLayout() # umístění widgetů do okna topLayout.addWidget(self.textEdit) topLayout.addWidget(quitButton) # nastavení správce geometrie a vložení všech komponent do okna self.setLayout(topLayout) def prepareShowTextButton(self): # druhé tlačítko showTextButton = QtGui.QPushButton('Show text', self) showTextButton.resize(showTextButton.sizeHint()) # navázání akce na signál showTextButton.clicked.connect(self.showTextDialog) return showTextButton def prepareQuitButton(self): # tlačítko 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 prepareTextEdit(self): # jednořádkové vstupní textové pole textEdit = QtGui.QTextEdit(self) textEdit.setAcceptRichText(True) textEdit.setReadOnly(True) with io.open("test.html", encoding="utf-8") as fin: textEdit.setHtml(fin.read()) return textEdit # 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): self.resize(640, 480) self.setWindowTitle("QMainWindow + QListWidget") # vložení komponenty do okna self.setCentralWidget(MainWindowContent()) 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()

Obrázek 16: Widget QTextEdit dokáže zobrazit i obrázky vložené do HTML pomocí značky <img>.
17. Repositář s demonstračními příklady
Zdrojové kódy všech sedmi dnes popsaných demonstračních příkladů 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 |
---|---|
93_line_edit.py | https://github.com/tisnik/presentations/blob/master/Python_GUI/PySide/93_line_edit.py |
94_line_edit_set_properties.py | https://github.com/tisnik/presentations/blob/master/Python_GUI/PySide/94_line_edit_set_properties.py |
95_line_edit_input_mask.py | https://github.com/tisnik/presentations/blob/master/Python_GUI/PySide/95_line_edit_input_mask.py |
96_line_edit_int_validator.py | https://github.com/tisnik/presentations/blob/master/Python_GUI/PySide/96_line_edit_int_validator.py |
97_line_edit_regexp_validator.py | https://github.com/tisnik/presentations/blob/master/Python_GUI/PySide/97_line_edit_regexp_validator.py |
98_text_edit.py | https://github.com/tisnik/presentations/blob/master/Python_GUI/PySide/98_text_edit.py |
99_text_edit_as_html_viewer.py | https://github.com/tisnik/presentations/blob/master/Python_GUI/PySide/99_text_edit_as_html_viewer.py |
18. Odkazy na Internetu
- PySide 1.2.1 documentation
https://pyside.github.io/docs/pyside/index.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 - 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 - QMessageBox
https://pyside.github.io/docs/pyside/PySide/QtGui/QMessageBox.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/