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:
#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:
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);