Autor podstrony: Krzysztof Zajączkowski

Stronę tą wyświetlono już: 3027 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:

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:

#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include <QResizeEvent> namespace Ui { class MainWindow; } class MainWindow : public QMainWindow { Q_OBJECT void resizeEvent(QResizeEvent *event) override; public: explicit MainWindow(QWidget *parent = 0); ~MainWindow(); private: Ui::MainWindow *ui; }; #endif // MAINWINDOW_H

I w pliku mainwindow.cpp:

#include "mainwindow.h" #include "ui_mainwindow.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); } MainWindow::~MainWindow() { delete ui; } void MainWindow::resizeEvent(QResizeEvent *event){ // nowa metoda, która zostanie wywołana przy zmianie rozmiarów okna QMainWindow::resizeEvent(event); // uruchamianie starej metody klasy bazowej ui->edit->setGeometry(0, 0, event->size().width(), event->size().height()); // ustawiam wymiary kontrolki za pomocą danych z klasy QResizeEvent }

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:

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:

#include "mainwindow.h" #include "ui_mainwindow.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); this->setMouseTracking(true); // włączam śledzenie myszy dla głównego okna programu ui->centralWidget->setMouseTracking(true); // włączam śledzenie myszy dla centralWidget ui->centralWidget->installEventFilter(this); // tutaj podpinam główne okno pod zdarzenia kontrolki centralWidget ui->mainToolBar->installEventFilter(this); // tutaj podpinam główne okno pod zdarzenia kontrolki mainToolBar ui->mainToolBar->setMouseTracking(true); // włączam śledzenie myszy dla mainToolBar } MainWindow::~MainWindow() { delete ui; } void MainWindow::mouseMoveEvent(QMouseEvent *event){ QMainWindow::mouseMoveEvent(event); qDebug() << event->pos(); // wyświetlam położenie myszki w oknie głównym programu } bool MainWindow::eventFilter(QObject *watched, QEvent *event){ if(event->type() == QEvent::MouseMove){ // gdy mam do czynienia z zdarzeniem ruchu myszki qDebug() << "Przechwyciłem zdarzenie dla ruchu myszy"; return false; // zwracam false bo chcę jeszcze wywołać komunikat mouseMoveEvent z klasy MainWindow } return QMainWindow::eventFilter(watched, event); // zwracam to co funkcja filtrująca z klasy bazowej zwróci }

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:

QMouseEvent event(QEvent::MouseMove, QPointF(100, 100), Qt::NoButton, 0, 0); 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:

auto *eventPost = new QMouseEvent(QEvent::MouseMove, QPointF(200, 200), Qt::NoButton, 0, 0); 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