PyQt4: Layouty, signály a sloty

15. 1. 2007
Doba čtení: 6 minut

Sdílet

V dnešním, v pořadí druhém, díle seriálu o PyQt si povíme o layoutech, které nám pomůžou s rozmístěním widgetů v okně naší příkladové aplikace. Také si vysvětlíme používání signálů a slotů, které jsou jedním ze základních stavebních prvků PyQt4. Povíme si i o funkcích, které se při práci se signály a sloty nejvíce používají.

Signály a sloty – Teorie

Každý objekt v Qt4 má nějaké své signály a sloty. Signály, jak již jejich název napovídá, jsou zprávy, které objekt generuje v případě jeho změny nebo při nějaké důležité události (typickým signálem může být například kliknutí myší na tlačítko). Každý signál může být napojen na slot. Slot je pro změnu klasická funkce, která, pokud je připojena k signálu, je zavolána a umožňuje nějakým způsobem měnit objekt. Slotem může být i funkce v Pythonu. Nyní si povíme o funkcích, které se při práci se signály a sloty nejvíce používají.

SIGNAL ( signal ) – modul QtCore

Funkce SIGNAL přijímá jako svůj jediný parametr jméno signálu v podobě textu. Text převádí do tvaru, kterému rozumí funkce connect, se kterou se tato funkce využívá. Typickým signálem může být signál „clicked()“, který je generován tlačítkem při kliknutí na něj.

QObject.connect ( sender, signal, method )

Tato funkce připojí signál (parametr signal), který je vygenerovaný objektem sender na funkci (metodu) method. Jinak řečeno, pokud vygeneruje sender signál signal, je spuštěna funkce (metoda) method. Proměnná signal je vytvořena pomocí funkce SIGNAL, o které jsme se bavili výše. Pokud bychom tedy chtěli tlačítko z minulého dílu napojit na nějakou funkci pythonu, kterou jsme sami definovali, můžeme tak učinit tímto kódem:

# deklarujeme funkci, na kterou chceme napojit signal
def moje_funkce():
   print "ahoj svete"
# Napojime signal "clicked()" tlacitka PushButton na nasi funkci moje_funkce()
app.connect(PushButton,QtCore.SIGNAL("clicked()"),moje_funkce)

Jen pro úplnost dodávám, že funkci connect předáváme pouze „ukazatel“ na naši funkci moje_funkce(), a proto se do zápisu nepíší závorky. Obdobným způsobem můžeme napojit signál jednoho Qt4 objektu na slot jiného Qt4 objektu. Představme si, že chceme po kliknutí na tlačítko zavřít naše okno. K uzavření okna použijeme jeho slot „close()“. Příkaz pro napojení signálu na slot pak bude vypadat takto:

# Napojime signal "clicked()" tlacitka PushButton na slot close okna MainWindow
app.connect(PushButton,QtCore.SIGNAL("clicked()"),MainWindow.close)

Signály a sloty – Praxe

Náš program z minulého dílu vylepšíme o možnost zavřít jej kliknutím na tlačítko. V podstatě do něj jen vložíme kód, který jsme si vysvětlili v teoretické části. Celý program pak bude vypadat takto:

from PyQt4 import QtCore, QtGui
import sys

app = QtGui.QApplication(sys.argv)

MainWindow = QtGui.QMainWindow()
MainWindow.setWindowTitle("Titulek okna")

PushButton = QtGui.QPushButton(MainWindow)
PushButton.setText("Popisek tlacitka")

# Napojime signal "clicked()" tlacitka PushButton na slot close okna MainWindow
app.connect(PushButton,QtCore.SIGNAL("clicked()"),MainWindow.close)

MainWindow.show()
sys.exit(app.exec_())

Layouty – Teorie

Layout je mřížka či tabulka, do které můžeme umísťovat Qt4 objekty (widgety). Důležitou vlastností layoutu je to, že mění svoji velikost společně s oknem (nebo jiným nadřazeným objektem), takže s použitím layoutu se budou objekty uvnitř okna dynamicky zvětšovat a zmenšovat podle toho, jak velké okno bude. Jediným problémem spojeným s layouty je to, že nemůže být přidán přímo do hlavního okna (QMainWindow). Hlavní okno totiž již obsahuje interní layout, který se stará o zarovnání případných toolbarů nebo nabídek. Layout tedy musíme vložit do prázdného objektu (widget), který vytvoříme funkcí QWidget(). Dále musíme nastavit tento prázdný widget jako centrální widget naší aplikace, což provedeme funkcí setCentralWidget. Nyní si již můžeme popsat všechny funkce a s nimi i tři nejčastěji používané typy layoutů.

QWidget ( parent ) – modul QtGui

Vytvoří prázdný widget s rodičem parent.

QMainWindow.set­CentralWidget(wid­get)

Určí hlavní widget (objekt) okna.

QHBoxLayout ( parent ) – modul QtGui

Funkcí QHBoxLayout() vytvoříme layout, ve kterém se objekty budou řadit horizontálně. Parametr parent je nadřazeným objektem layoutu.

QHBoxLayout.ad­dWidget ( widget, stretch = 0, alignment = 0 )

Funkce přidá do layoutu nový widget, který je definován parametrem widget, na poslední místo v layoutu. Parametr stretch slouží k nastavení rozpínání widgetů v layoutu, pokud roztahujeme okno. Čím větší bude hodnota parametru stretch jednoho widgetu oproti ostatním, tím více se při zvětšování okna bude widget zvětšovat na úkor ostatních widgetů v layoutu. Parametr alignment nastavuje zarovnání widgetu v layoutu. Hodnoty, které může parametr alignment obsahovat jsou uvedeny v následující tabulce.

Hodnoty parametru alignment
Hodnota aligment Význam
QtCore.Qt.AlignLeft Zarovná obsah doleva.
QtCore.Qt.Alig­nRight Zarovná obsah doprava.
QtCore.Qt.AlignTop Zarovná obsah nahoru.
QtCore.Qt.Alig­nBottom Zarovná obsah dolů.
QtCore.Qt.Alig­nHCenter Vycentruje obsah horizontálně.
QtCore.Qt.Alig­nVCenter Vycentruje obsah vertikálně.
QtCore.Qt.Alig­nCenter Vycentruje obsah vertikálně i horizontálně.

Následující obrázky by měly pomoci k pochopení parametru stretch.

PyQt2 1

Hodnota stretch levého tlačítka je 0 a pravého 1.

PyQt2 2

Hodnota stretch levého tlačítka je 0 a pravého 0.

PyQt2 3

Hodnota stretch levého tlačítka je 1 a pravého 2.

QVBoxLayout ( parent ) – modul QtGui

Funkcí QVBoxLayout() vytvoříme layout, ve kterém se objekty budou řadit vertikálně. Parametr parent je nadřazeným prvkem layoutu.

QVBoxLayout.ad­dWidget ( widget, stretch = 0, alignment = 0 )

Tato funkce funguje stejně jako QHBoxLayout.ad­dWidget ( widget, stretch = 0, alignment = 0 ).

QGridLayout ( parent ) – modul QtGui

Funkce QGridLayout() vytváří layout, který si můžeme představit jako tabulku nebo mřížku. Můžeme si vybrat, do kterého sloupce a řádku widget umístíme a nastavit, přes kolik řádků a sloupců bude widget zobrazen.

QGridLayout.ad­dWidget ( widget, row, column, rowSpan, columnSpan, alignment = 0)
QGridLayout.ad­dWidget ( widget, row, column, alignment = 0 )

Funkce přidá do layoutu widget definovaný parametrem widget. Vloží jej do řádku row a sloupce column. Parametr ColumnSpan udává počet sloupců, přes které bude widget zobrazen, a parametr rowSpan udává počet řádků přes které se widget zobrazí. Nesmíme zapomenout na to, že první buňka se nachází v sloupci s indexem 0 a řádkem s indexem 0. Parametr alignment má stejný význam jako u ostatních layoutů. V případě, že chceme widget zobrazit pouze v jedné buňce, můžeme použít druhou možnou syntaxi, které vynechává parametry rowSpan a columnSpan.

bitcoin_skoleni

Layouty – Praxe

PyQt2 4

V praxi si ukážeme pouze QGridLayout. Z ukázky by však mělo být jasné i použití zbylých dvou layoutů. Vytvoříme tři tlačítka. První dvě zobrazíme v jednom řádku vedle sebe. Jedno z nich zarovnáme doleva a druhé doprava. Třetí tlačítko zobrazíme pod nimi a roztáhneme jej přes dva sloupce. Pro procvičení doporučuji například zkusit dát tlačítka do jiného layoutu nebo napojit signály tlačítek na nějaké funkce.

from PyQt4 import QtCore, QtGui
import sys

app = QtGui.QApplication(sys.argv)

MainWindow = QtGui.QMainWindow()
MainWindow.setWindowTitle("Titulek okna")
# Vytvorime hlavni prazdny widget MainWidget s rodicem MainWIndow
MainWidget=QtGui.QWidget(MainWindow)
# Nastavime prazdny widget MainWidget jako centralni widget okna
MainWindow.setCentralWidget(MainWidget)

# Vytvorime QGridLayout
layout=QtGui.QGridLayout(MainWidget)

# Vytvorime tri tlacitka s rodicem MainWidget
Button1 = QtGui.QPushButton("Tlacitko 1",MainWidget)
Button2 = QtGui.QPushButton("Tlacitko 2",MainWidget)
Button3 = QtGui.QPushButton("Tlacitko 3",MainWidget)

# Pridame Button1 do prvniho radku prvniho sloupce a zarovname vlevo
layout.addWidget (Button1,0,0,QtCore.Qt.AlignLeft)
# Pridame Button2 do prvniho radku druheho sloupce a zarovname vpravo
layout.addWidget (Button2,0,1,QtCore.Qt.AlignRight)
# Pridame Button3 do druheho radku prvniho sloupce a to tak,
# ze bude v jednom radku, ale pres dva sloupce.
layout.addWidget (Button3,1,0,1,2)

MainWindow.show()
sys.exit(app.exec_())

Závěr

Další díl bude zaměřen hlavně na widget pro editovaní textu QLineEdit a textový popisek QLabel. Díky nim budeme moci udělat program více interaktivní.

Autor článku