Na codzień tworzę aplikacje w Javie i to przez używanie SWING-a ukształtowało się moje podejście do obsługi zdarzeń, które mają miejsce w komponentach graficznych wyświetlanych użytkownikom. Stąd, kiedy zaczynałem swoją przygodę z Qt 4 i PyQt pewną niespodzianką był mechanizm obsługi zdarzeń, który zastosowano w tej bibliotece. Odbiega od tego z Javy, gdzie robi się to za pomocą tak zwanych listenerów, czyli klas implementujących pewien interfejs definiujący metodę, która jest wywoływana jako rezultat zdarzenia. Na przykład kliknięcie na przycisk powoduje między innymi uruchomienie metody actionPerformed z interfejsu ActionListener, którego implementację rejestruje się dla każdego przycisku.
W przypadku Qt stosuje się tak zwane sygnały (signals) i sloty (slots). Zasada jest tutaj dość prosta - komponent graficzny - na przykład przycisk QPushButton w momencie, gdy zostanie kliknięty emituje sygnał, którego etykieta wygląda następująco clicked(bool). Takie sformułowanie oznacza, że w reakcji na kliknięcie przycisku do podanego slotu zostanie przekazana wartość logiczna (typ bool). Łatwo się domyślić, że rolę slotu pełni inna metoda, która w reakcji na zdarzenie powinna wykonać jakąś operację. Jedynym wymaganiem, jakie musi spełniać slot, to akceptować taką samą liczbę parametrów, tego samego typu, jak to jest zdefiniowane dla sygnału, z którym jest związany. W opisywanym przypadku metoda - slot ma zdefiniowany jeden parametr, przez który będzie przekazana wartość logiczna.
Jednak aby wystąpiła reakcja na zdarzenie, trzeba wybrany sygnał połączyć ze zdefiniowanym slotem. Kontynuując przykład z przyciskiem i sygnałem clicked(bool) utwórzmy metodę klikniecie(zaznaczony). Gdy połączymy sygnał clicked i metodę klikniecie, to w chwili, gdy użytkownik naciśnie za pomocą myszy przycisk, zostanie wywołana metoda klikniecie, a parametr zaznaczony otrzyma wartość logiczną True albo False. Jeżeli tworzymy interfejs przy pomocy PyQt to istnieje kilka sposobów na zdefiniowanie połącznia:
- stosując metodę QObject.connect(QObject nadajnik, QtCore.SIGNAL(etykieta), QObject odbiornik, QtCore.SLOT(etykieta)). Nadajnik to obiekt - źródło sygnału, natomiast odbiornik, to obiekt, w którym znajduje się metoda - slot. SIGNAL i SLOT to dwie metody, które służą do określenia sygnału i slotu. Robi się to podając ich etykiety zapisane w postaci łańcucha znaków. Na przykład QObject.connect(przycisk, SIGNAL("clicked(bool)"), poleTxt, SLOT("klikniecie(zaznaczony)")).
- Drugi ze sposobów, to pewne uproszczenie pierwszej i polega na tym, że zamiast podawać wprost obiekt - odbiornik i etykietę metody, można bezpośrednio wskazać jej referencję, czyli: QObject.connect( przycisk, SIGNAL("clicked(bool)"), poleTxt.klikniecie).
- Wreszcie ostatni ze sposobów, najbliższy konwencji tworzenia programów w Pythonie. Sygnały w PyQt zostały zaimplementowane w ten sposób, że każdy z nich jest także obiektem. Możemy się więc odwołać do niego bezpośrednio i wskazać docelową metodę, która powinna być wywołana w reakcji na zdarzenie: przycisk.clicked.connect(poleTxt.klikniecie).
Poniższy kod prezentuje krótki program, w którym zastosowano wszystkie opisane wcześniej metody obsługi zdarzeń w Qt 4 / PyQt
class Program(object): def __init__(self): przycisk = QPushButton("Przycisk") QObject.connect(przycisk, SIGNAL("clicked(bool)"), self, SLOT("klikniecie(zaznaczony)")) QObject.connect(przycisk, SIGNAL("clicked(bool)"), self.klikniecie)) przycisk.clicked.connect(self.klikniecie) def klikniecie(self, zaznaczony): print 'klikniecie przycisku ', zaznaczony
W kolejnym artykule opisałem zasadę tworzenia sygnałów Qt w Pythonie.