1. 事件
事件
是一个描述应用程序中、发生的某些事情的对象。
在 Qt 中,所有事件都继承自 QEvent
,并且每个事件都有特定的标识符,如:Qt::MouseButtonPress
代表鼠标按下事件。
每个事件对象包含该事件的所有相关信息,如:鼠标事件包含鼠标的坐标、按下的按钮等信息。
2. 事件处理器
事件处理器
是用于处理特定类型事件的成员函数,通常以 event 结尾,如:mousePressEvent 、enterEvent 等。
事件处理器定义了当某个事件发生时,应执行的操作。
事件处理器可以分为两类:
- 预定义事件处理器:由 Qt 提供的标准事件处理函数,可以通过重写它们以实现自定义行为。
class SessionFriendItem : public QWidget
{
Q_OBJECT
public:
SessionFriendItem(QWidget* owner, const QIcon& avatar, const QString& name, const QString& text)
: owner(owner), selected(false)
{
// 1. 设置基本属性和样式
this->setFixedHeight(64);
this->setStyleSheet("QWidget { background-color: rgb(236, 233, 231); }");
// 2. 创建网格布局
QGridLayout* layout = new QGridLayout();
layout->setContentMargins(0, 0, 0, 0);
layout->setVerticalSpacing(0); // 设置竖直方向间距
layout->setHorizontalSpacing(0); // 设置水平方向间距
this->setLayout(layout);
// 3. 创建头像
QPushButton avatar_btn = new QPushButton();
avatar_btn->setFixedSize(QSize(46, 46));
avatar_btn->setIconSize(QSize(46, 46));
avatar_btn->setIcon(avatar);
avatar_btn->setStyleSheet("QPushButton { border: none; background-color: transparent; }");
layout->addWidget(avatar_btn, 0, 0, 2, 2);
// 4. 创建昵称
QLabel* name_label = new QLabel();
name_label->setText(name);
name_label->setFixedHeight(30);
name_label->setStyleSheet("QLabel { font-size: 14px; }");
layout->addWidget(name_label, 0, 2, 1, 6);
// 5. 添加预览消息
QLabel* msg_label = new QLabel();
msg_label->setText(text);
msg_label->setFixedHeight(25);
msg_label->setStyleSheet("QLabel { font-size: 12px; }");
layout->addWidget(msg_label, 1, 2, 1, 6);
}
void mousePressEvent(QMouseEvent* event) override
{
// 1. 恢复兄弟组件的 rgb
QObjectList* children = this->parent()->children();
for (QObject* child : children)
{
if (child->isWidgetType() == false) continue;
SessionFriendItem* temp = dynamic_cast<SessionFriendItem*>(child);
if (temp->selected == true)
{
temp->selected = false;
temp->setStyleSheet("QWidget { background-color: rgb(236, 233, 231); }");
}
}
// 2. 设置当前组件的 rgb
this->selected = true;
this->setStyleSheet("QWidget { background-color: rgb(200, 199, 198); }");
}
void enterEvent(QEnterEvent* event) override
{
// 当前组件被点击,则不处理
if (selected == true) return;
this->setStyleSheet("QWidget { background-color: rgb(222, 220, 218); }");
}
void leaveEvent(QEvent* event) override
{
if (selected == true) return;
this->setStyleSheet("QWidget { background-color: rgb(236, 233, 231); }");
}
private:
QWidget* owner;
bool selected;
};
- 自定义事件处理器:处理一些特殊类型的事件,可以通过事件过滤器或子类化 QObject 来实现。
3. 事件过滤器
在 Qt 中,事件过滤器(eventFilter
)提供了一种机制,允许一个对象 监视并处理 另一个对象的事件。
3.1 基本概念
事件过滤器允许拦截发送给某个对象的所有事件,并在这些事件被该对象处理之前,决定如何处理它们。
步骤:
- 安装事件过滤器:通过调用
installEventFilter()
方法,将一个对象设置为另一个对象的事件过滤器。 - 重写
eventFilter()
函数:在作为事件过滤器的对象中,重写bool eventFilter(QObject* obj, QEvent* event)
函数,来定义具体的事件处理逻辑。
3.2 工作原理
QMainWindow
不能直接设置布局,需要通过中央部件来管理布局。
this->setCentralWidget()
// mainwindow.h
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
bool eventFilter(QObject* object, QEvent* event) override;
QWidget* mainWidget;
QPushButton* button;
private:
Ui::MainWindow *ui;
};
// mainwindow.cpp
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
mainWidget = new QWidget(this);
this->setCentralWidget(mainWidget);
QVBoxLayout* layout = new QVBoxLayout();
mainWidget->setLayout(layout);
button = new QPushButton();
button->setText("点击");
button->setFixedSize(QSize(50, 50));
layout->addWidget(button);
// 安装事件过滤器
button->installEventFilter(this);
}
bool MainWindow::eventFilter(QObject* object, QEvent* event)
{
if (object == button)
{
if (event->type() == QEvent::MouseButtonPress)
{
qDebug() << "Button_Press";
}
else if (event->type() == QEvent::Enter)
{
qDebug() << "Button_Enter";
}
else if (event->type() == QEvent::Leave)
{
qDebug() << "Button_Leave";
}
// return false; // 允许事件继续传递
return true;
}
// 交给其它事件处理器处理
return QMainWindow::eventFilter(object, event);
}
鼠标进入按钮上方,鼠标点击按钮,鼠标离开按钮
installEventFilter
的作用目标
-
事件过滤器的本质:当一个对象 A 调用 installEventFilter(B) ,意味着对象 B 会优先接收到对象 A 的事件,并可以通过 eventFilter() 方法拦截或处理这些事件。
-
如果希望在事件滤波器 B 处理完事件后,仍然让目标对象 A 继续处理该事件,可以在事件滤波器逻辑的末尾 return false。这样,事件会继续按照正常的事件处理流程传递下去。
-
自定义类 能够作为事件过滤器、并被目标对象设置,需要确保该类重载了 eventfilter() 方法;原生的 Qt 对象(如 QWidget)无法直接担任事件过滤器的角色,因为它们没有提供对 eventfilter() 的重载接口。
4. 事件系统
Qt 的事件系统基于事件驱动模型工作,其核心是通过事件队列(Event Queue)实现异步调用。
以下是其基本运作流程:
4.1 事件产生
当用户与应用程序进行交互,或系统内部发生某些变化,就会产生相应的事件。
所有事件均封装为 QEvent 的子类对象。
例如,用户按下鼠标左键,会产生一个 QMouseEvent 对象,该对象包含了关于鼠标点击的所有信息,包括点击的位置、按钮的状态等;
键盘按键的按下或释放会产生 QKeyEvent 对象。
4.2 事件传递
- 对于同步事件:
-
使用
QCoreApplication::sendEvent()
方法时,事件会被立即发送给接收者。 -
发送事件的线程会等待,直到事件被完全处理。
- 对于异步事件:
-
使用
QCoreApplication::postEvent()
方法时,生成的事件并非被立即处理,而是被放入目标对象所属线程的事件队列中,等待调度。 -
QCoreApplication::exec() 启动的主事件循环会从队列中取出事件进行处理(非阻塞),并通过 QCoreApplication::notify() 方法来分发该事件到目标对象(事件通知),确保每个事件都能被正确地发生给它的接收者。
-
在事件被传递给目标对象之前,如果有安装事件过滤器,首先会调用这些过滤器的 eventFilter() 方法;如果该事件没有被过滤器拦截,则继续传递给目标对象。
4.3 事件处理
通过重写 event() 方法,或特定的事件处理器(如 mousePressEvent() ),实现业务逻辑。
如果事件未被处理(如未重写 event() 或未调用基类实现),某些对象会向父对象传递(冒泡机制),直至顶层对象。
事件是否传递取决于事件类型和 accept() / ignore()
的标记。
QMouseEvent 默认 accept() ,QKeyEvent() 默认 ignore() ,QPaintEvent 仅在目标对象处理、不会传递。
void MainWindow::mousePressEvent(QMouseEvent* event)
{
qDebug() << "button is pressed";
event->accept(); // 标记事件已处理
// event->ignore(); // 允许事件继续传递
}
4.4 事件结束
事件对象会在处理完成后,由 Qt 自动销毁;除非事件被通过 deleteLater()
标记为 deferred delete
。