Qt - zdarzenia

Stronę tą wyświetlono już: 250 razy

Zdarzenia i subklassing

Każda klasa okna czy też kontrolki ma pewien zestaw specjalnych metod, które nazywane są uchwytami zdarzeń (ang. events handlers). Metody te są automatycznie wywoływane na z góry określone zdarzenie. Oto lista kilku przykładowych metod tego typu, które są chronione:

  • mousePressEvent - wywoływana, gdy przycisk myszy zostanie wciśnięty na kontrolce lub oknie programu;
  • mouseReleaseEvent - wywoływana, gdy przycisk myszy zostaje zwolniony na kontrolce lub oknie programu;
  • mouseMoveEvent - wywoływana, gdy kursor myszy przemieszcza się nad oknem programu lub kontrolką;
  • resizeEvent - wywoływana, gdy rozmiar okna jest zmieniany;
  • paintEvent - wywoływana, gdy okno lub kontrolka ma zostać odrysowana.

Oczywiście tych metod jest więcej i wszystkie one są chronione co nie znaczy, że nie da się do nich dorwać używając subclassing-u. Takim typowym przykładem subklassing-u jest klasa MainWindow, która z kolei dziedziczy po klasie QMainWindow. Dzięki takiemu zabiegowi można nadpisać metodę z klasy bazowej, która jest odpowiedzialna za obsługę danego zdarzenia. Oto przykład zastosowania tej techniki do wykonania własnych działań na kontrolce umieszczonej w oknie głównym programu gdy ten zmieni swój rozmiar:

W pliku mainwindow.h:

Listing 1
  1. #ifndef MAINWINDOW_H
  2. #define MAINWINDOW_H
  3. #include <QMainWindow>
  4. #include <QResizeEvent>
  5. namespace Ui {
  6. class MainWindow;
  7. }
  8. class MainWindow : public QMainWindow
  9. {
  10. Q_OBJECT
  11. void resizeEvent(QResizeEvent *event) override;
  12. public:
  13. explicit MainWindow(QWidget *parent = 0);
  14. ~MainWindow();
  15. private:
  16. Ui::MainWindow *ui;
  17. };
  18. #endif // MAINWINDOW_H

I w pliku mainwindow.cpp:

Listing 2
  1. #include "mainwindow.h"
  2. #include "ui_mainwindow.h"
  3. MainWindow::MainWindow(QWidget *parent) :
  4. QMainWindow(parent),
  5. ui(new Ui::MainWindow)
  6. {
  7. ui->setupUi(this);
  8. }
  9. MainWindow::~MainWindow()
  10. {
  11. delete ui;
  12. }
  13. void MainWindow::resizeEvent(QResizeEvent *event){ // nowa metoda, która zostanie wywołana przy zmianie rozmiarów okna
  14. QMainWindow::resizeEvent(event); // uruchamianie starej metody klasy bazowej
  15. ui->edit->setGeometry(0, 0, event->size().width(), event->size().height()); // ustawiam wymiary kontrolki za pomocą danych z klasy QResizeEvent
  16. }

Każda metoda przechwytująca zdarzenie przyjmuje jako argument wskaźnik na klasę, która zawsze dziedziczy po klasie QEvent. Warto pamiętać, że informacje związane z danym zdarzeniem powinny być pozyskiwane właśnie z odpowiedniego obiektu dziedziczącego po tejże klasie.

W przypadku zdarzeń związanych z myszką metody przechwytujące jej zdarzenia przyjmują jako argument wskaźnik na obiekt klasy QMouseEvent. Warto też pamiętać, że zdarzenie mouseMoveEvent domyślnie jest wywoływane jedynie gdy dowolny przycisk został wciśnięty i kursor myszy przesunięty, aby zmienić to zachowanie konieczne jest włączenie śledzenia kursora za pomocą metody setMouseTracking:

Listing 3
  1. ui->centralWidget->setMouseTracking(true);

Filtrowanie zdarzeń

Możliwe jest również filtrowanie i przechwytywanie zdarzeń poprzez nadpisanie metody eventFilter oraz użycie metody installEventFilter. Oto przykład:

Listing 4
  1. #include "mainwindow.h"
  2. #include "ui_mainwindow.h"
  3. MainWindow::MainWindow(QWidget *parent) :
  4. QMainWindow(parent),
  5. ui(new Ui::MainWindow)
  6. {
  7. ui->setupUi(this);
  8. this->setMouseTracking(true); // włączam śledzenie myszy dla głównego okna programu
  9. ui->centralWidget->setMouseTracking(true); // włączam śledzenie myszy dla centralWidget
  10. ui->centralWidget->installEventFilter(this); // tutaj podpinam główne okno pod zdarzenia kontrolki centralWidget
  11. ui->mainToolBar->installEventFilter(this); // tutaj podpinam główne okno pod zdarzenia kontrolki mainToolBar
  12. ui->mainToolBar->setMouseTracking(true); // włączam śledzenie myszy dla mainToolBar
  13. }
  14. MainWindow::~MainWindow()
  15. {
  16. delete ui;
  17. }
  18. void MainWindow::mouseMoveEvent(QMouseEvent *event){
  19. QMainWindow::mouseMoveEvent(event);
  20. qDebug() << event->pos(); // wyświetlam położenie myszki w oknie głównym programu
  21. }
  22. bool MainWindow::eventFilter(QObject *watched, QEvent *event){
  23. if(event->type() == QEvent::MouseMove){ // gdy mam do czynienia z zdarzeniem ruchu myszki
  24. qDebug() << "Przechwyciłem zdarzenie dla ruchu myszy";
  25. return false; // zwracam false bo chcę jeszcze wywołać komunikat mouseMoveEvent z klasy MainWindow
  26. }
  27. return QMainWindow::eventFilter(watched, event); // zwracam to co funkcja filtrująca z klasy bazowej zwróci
  28. }

Wysyłanie zdarzeń

Zapewne zastanawiasz się jak zdarzenia są wywoływane? Wiele zdarzeń zaimplementowanych w kontrolkach pochodzi od systemu. Zdarzenia związane z ruchem myszki, czy zmianą rozmiaru okna należą do tej właśnie kategorii zdarzeń. Można oczywiście utworzyć swoją własną klasę dziedziczącą po klasie QEdit i utworzyć własną metodę przechwytującą zdarzenie związane z taką klasą. Nie będę omawiał jak utworzyć taką klasę i metodę bo zdaje się to oczywiste, co jest mniej oczywiste to to, jak takie zdarzenie wywołać? Odpowiedzią na to pytanie są dwie metody QApplication::sendEvent lub QApplication::postEvent. Pierwsza metoda powoduje natychmiastowe wywołanie metody przechwytującej zdarzenie, druga wysyła zdarzenie, które trafia do pętli komunikatów i co jest bardzo ważne po odebraniu obiekt zdarzenia jest niszczony co oznacza, że obiekt klasy zdarzenia musi być utworzony w sposób dynamiczny a nie statyczny. Oto przykład użycia metody QApplication::sendEvent w konstruktorze klasy MainWindow:

Listing 5
  1. QMouseEvent event(QEvent::MouseMove, QPointF(100, 100), Qt::NoButton, 0, 0);
  2. QApplication::sendEvent(this, &event);

Powyższy kawałek kodu wywoła metodę przechwytującą zdarzenie ruchu myszki, czyli mouseMoveEvent, symulując niejako ruch myszki do punktu o współrzędnych x = 100; y = 100.

Podobny efekt można uzyskać za pomocą takiego kodu:

Listing 6
  1. auto *eventPost = new QMouseEvent(QEvent::MouseMove, QPointF(200, 200), Qt::NoButton, 0, 0);
  2. QApplication::postEvent(this, eventPost);
Strony powiązane
strony powiązane
  1. doc.qt.io/archives/qt-4.8/eventsandfilters.html - opis zagadnień związanych z zdarzeniami i filtrowaniem zdarzeń na stronie dokumentacji Qt
  2. doc.qt.io/qt-5/qevent.html - opis klasy QEvent na stronie dokumentacji Qt
  3. doc.qt.io/archives/qt-4.8/qcoreapplication.html#sendEvent - opis metody QApplication::sendEvent na stronie dokumentacji Qt
  4. doc.qt.io/archives/qt-4.8/qcoreapplication.html#postEvent - opis metody QApplication::postEvent na stronie dokumentacji Qt

Komentarze