Qt 事件系统

Qt是事件驱动的, 程序每个动作都是由某个事件所触发。 Qt事件的类型很多,我们可以通过查看Qt的 manual中的Event System 和 QEvent 来获得各个事件的详细信息。

为了完整起见,一份Qt4.6的事件列表附在本文后面。

事件来源

  • Spontaneous events(自发事件)
    • 从系统得到的消息,比如鼠标按键,键盘按键等。Qt事件循环的时候读取这些事件,转化为QEvent后依次处理
  • Posted events
    • 有Qt或应用程序产生,放入消息队列
    • QCoreApplication::postEvent()
  • Sent events
    • 由Qt或应用程序产生,不放入队列,直接被派发和处理
    • QCoreApplication::sendEvent()

比如考虑重绘事件处理函数 paintEvent(),3种事件都能使得该函数被调用:

  • 当窗口被其他窗口覆盖后,再次重新显示时,系统将产生 spontaneous 事件来请求重绘
  • 当我们调用 update() 时,产生的是 Posted 事件
  • 当我们调用 repaint() 时,产生的是 Sent 事件

事件派发

事件循环

    while (!exit_was_called) {
        while (!posted_event_queue_is_empty) {
            process_next_posted_event();
        }
        while (!spontaneous_event_queue_is_empty) {
            process_next_spontaneous_event();
        }
        while (!posted_event_queue_is_empty) {
            process_next_posted_event();
        }
    }[喝小酒的网摘]http://blog.hehehehehe.cn/a/4034.htm
  • 先处理Qt事件队列中的事件,直至为空
  • 再处理系统消息队列中的消息,直至为空
  • 在处理系统消息的时候会产生新的Qt事件,需要对其再次进行处理

不通过事件循环

sendEvent的事件派发不通过事件循环。QApplication::sendEvent()是通过调用QApplication::notify(),直接进入了事件的派发和处理环节,是同步的。

sendEvent与postEvent的使用

  • 两个函数都是接受一个 QObject * 和一个 QEvent * 作为参数。
  • postEvent 的 event 必须分配在 heep 上。用完后会被Qt自动删除
  • sendEvent 的 event 必须分配在 stack 上。

例子(发送X按键事件到mainWin):

QApplication::postEvent(mainWin, new QKeyEvent(QEvent::KeyPress, Key_X, 'X', 0));
QKeyEvent event(QEvent::KeyPress, Key_X, 'X', 0);
QApplication::sendEvent(mainWin, &event);

notify

所有的事件都最终通过 notify 派发到相应的对象中。

bool QCoreApplication::notify ( QObject * receiver, QEvent * event )

事件过滤

看看notify()调用的内部函数notify_helper()的源码部分:

  • 先通过 Applicaton 安装的过滤器
  • 如果未被过滤,再通过 receiver 安装的过滤器
  • 如果仍未被过滤,才调用 receiver->event() 函数进行派发

/*!internal

  Helper function called by notify()
 */
bool QCoreApplicationPrivate::notify_helper(QObject *receiver, QEvent * event)
{
    // send to all application event filters
    if (sendThroughApplicationEventFilters(receiver, event))
        return true;
    // send to all receiver event filters
    if (sendThroughObjectEventFilters(receiver, event))
        return true;
    // deliver the event
    return receiver->event(event);
}

事件在传递到对象之前(调用obj->event()函数之前),要先能通过 Applicaton 和 obj 安装的过滤器,那么过滤器是怎么安装的:

  • 首先QObject中有一个类型为QObjectList的成员变量,名字为eventFilters
  • 当某个QObject安装了事件过滤器之后, 它会将filterObj的指针保存在eventFilters中
monitoredObj->installEventFilter(filterObj);
  • 在事件到达QObject::event()函数之前,会先查看该对象的eventFilters列表, 如果非空, 就先调用列表中对象的eventFilter()函数.
bool QObject::eventFilter ( QObject * watched, QEvent * event )
  • 事件过滤器函数eventFilter()返回值是bool型
    • 如果返回true, 则表示该事件已经被处理完毕, Qt将直接返回, 进行下一事件的处理
    • 如果返回false, 事件将接着被送往剩下的事件过滤器或是目标对象进行处理

对于 QCoreApplication ,由于也是QObject 派生类,安装过滤器方式与前述相同。

事件转发

对于某些类别的事件, 如果在整个事件的派发过程结束后还没有被处理, 那么这个事件将会向上转发给它的父widget, 直到最顶层窗口.

如何判断一个事件是否被处理了呢? (有两个层次)

  • QApplication::notify(), QObject::eventFilter(), QObject::event() 通过返回bool值来表示是否已处理. “真”表示已经处理, “假”表示事件需要继续传递
  • 另一种是调用QEvent::ignore() 或 QEvent::accept() 对事件进行标识,accept表示事件被处理

为清楚起见,贴一点Qt的源码(来自 QApplication::notify()):

    case QEvent::ToolTip:
    case QEvent::WhatsThis:
    case QEvent::QueryWhatsThis:
        {
            QWidget* w = static_cast<QWidget *>(receiver);
            QHelpEvent *help = static_cast<QHelpEvent*>(e);
            QPoint relpos = help->pos();
            bool eventAccepted = help->isAccepted();
            while (w) {
                QHelpEvent he(help->type(), relpos, help->globalPos());
                he.spont = e->spontaneous();
                res = d->notify_helper(w, w == receiver ? help : &he);
                e->spont = false;
                eventAccepted = (w == receiver ? help : &he)->isAccepted();
                if ((res && eventAccepted) || w->isWindow())
                    break;

                relpos += w->pos();
                w = w->parentWidget();
            }
            help->setAccepted(eventAccepted);
        }
        break;

这儿显示了对 WhatsThis 事件的处理:先派发给 w,如果事件被accepted 或已经是顶级窗口,则停止;否则获取w的父对象,继续派发。

事件处理

  • 重新实现一个特定的事件handler

QObject与QWidget提供了许多特定的事件handlers,分别对应于不同的事件类型。(如paintEvent()对应paint事件)

  • 重新实现QObject::event()

event()函数是所有对象事件的入口,QObject和QWidget中缺省的实现是简单地把事件推入特定的事件handlers。

  • 在QObject安装上事件过滤器

事件过滤器是一个对象,它接收别的对象的事件,在这些事件到达指定目标之间。

  • 在aApp上安装一个事件过滤器,它会监视程序中发送到所有对象的所有事件
  • 重新实现QApplication:notify(),Qt的事件循环与sendEvent()调用这个函数来分发事件,通过重写它,你可以在别人之前看到事件。

事件列表

Qt4.6的事件列表:

  • QAccessibleEvent
  • QActionEvent
  • QChildEvent
  • QCloseEvent
  • QCustomEvent
  • QDragLeaveEvent
  • QDropEvent
    • QDragMoveEvent
      • QDragEnterEvent
  • QDynamicPropertyChangeEvent
  • QFileOpenEvent
  • QFocusEvent
  • QGestureEvent
  • QGraphicsSceneEvent
    • QGraphicsSceneContextMenuEvent
    • QGraphicsSceneDragDropEvent
    • QGraphicsSceneHelpEvent
    • QGraphicsSceneHoverEvent
    • QGraphicsSceneMouseEvent
    • QGraphicsSceneMoveEvent
    • QGraphicsSceneResizeEvent
    • QGraphicsSceneWheelEvent.
  • QHelpEvent
  • QHideEvent
  • QHoverEvent
  • QIconDragEvent
  • QInputEvent
    • QContextMenuEvent
    • QKeyEvent
    • QMouseEvent
    • QTabletEvent
    • QTouchEvent
    • QWheelEvent
  • QInputMethodEvent
  • QMoveEvent
  • QPaintEvent
  • QResizeEvent
  • QShortcutEvent
  • QShowEvent
  • QStatusTipEvent
  • QTimerEvent
  • QWhatsThisClickedEvent
  • QWindowStateChangeEvent
[喝小酒的网摘]http://blog.hehehehehe.cn/a/4034.htm
### 回答1: Qt是一个功能强大的跨平台应用程序开发框架,其中包含了对系统鼠标事件的检测和处理的功能。 Qt提供了一个名为QMouseEvent的类,用于处理鼠标事件。可以通过重写QWidget或QGraphicsItem中的mousePressEvent()、mouseMoveEvent()和mouseReleaseEvent()等函数来检测鼠标按下、移动和释放等事件。 在该类的对应函数中,可以通过调用event->pos()获取鼠标事件发生时的坐标位置。还可以使用event->button()来获取按下的鼠标按钮,如左键、右键等。Qt还提供了其他的函数和属性,使开发人员可以更精确地获取和处理鼠标事件的各种属性。 通过重写鼠标事件函数并编写自定义的响应代码,可以实现对系统鼠标事件的检测和处理。例如,可以在鼠标按下事件中显示一个提示框,或者在鼠标移动事件中改变鼠标光标的样式。 总结来说,Qt提供了丰富的功能和工具来检测和处理系统鼠标事件。开发人员可以根据具体的需求,通过重写相应的函数来实现对鼠标事件的检测和处理,从而实现更加灵活和个性化的用户交互体验。 ### 回答2: 在Qt中,可以通过重写QWidget或QMainWindow的鼠标事件函数来检测系统鼠标事件。 具体步骤如下: 1. 首先,在自定义的窗口类中重写鼠标事件函数,例如鼠标按下事件(QMouseEvent)、鼠标移动事件(QMouseEvent)等。 例如,在重写鼠标按下事件函数(mousePressEvent)时,可以添加以下代码: ```cpp void MyWidget::mousePressEvent(QMouseEvent *event) { // 获取鼠标按下的坐标位置 QPoint pos = event->pos(); // 根据需要进行其他处理,如判断鼠标按下的位置是否在某个区域内等 // 调用父类的鼠标事件函数,保证正常的事件传递 QWidget::mousePressEvent(event); } ``` 2. 然后,通过创建自定义的窗口对象并显示出来,启动Qt事件循环,使窗口能够接收鼠标事件并进行处理。 例如,在主函数中创建窗口对象并显示: ```cpp int main(int argc, char *argv[]) { QApplication a(argc, argv); MyWidget w; w.show(); return a.exec(); } ``` 通过重写鼠标事件函数,我们可以根据具体需要来获取并处理系统鼠标事件,如鼠标按下、鼠标移动、鼠标释放等,从而实现对这些事件的检测和响应。 ### 回答3: Qt是一种流行的跨平台应用程序开发框架,它提供了许多用于检测与处理鼠标事件的功能。 要检测系统鼠标事件,首先需要创建一个Qt应用程序并设置窗口。然后,可以通过重写窗口类的相关事件处理函数来检测鼠标事件。以下是一个示例代码,演示如何使用Qt检测系统鼠标事件: ```cpp #include <QApplication> #include <QMainWindow> #include <QMouseEvent> #include <QMessageBox> class MyMainWindow : public QMainWindow { public: MyMainWindow(QWidget *parent = nullptr) : QMainWindow(parent) { // 设置窗口属性,使之接收鼠标事件 setMouseTracking(true); } protected: // 鼠标移动事件处理函数 void mouseMoveEvent(QMouseEvent *event) override { // 获取鼠标当前位置 int x = event->x(); int y = event->y(); // 在消息框中显示鼠标位置信息 QString message = QString("鼠标当前位置:(%1, %2)").arg(x).arg(y); QMessageBox::information(this, "鼠标移动事件", message); } // 鼠标点击事件处理函数 void mousePressEvent(QMouseEvent *event) override { // 获取鼠标点击位置和按钮 int x = event->x(); int y = event->y(); Qt::MouseButton button = event->button(); // 在消息框中显示鼠标点击信息 QString message = QString("鼠标点击事件:(%1, %2),按钮:%3").arg(x).arg(y).arg(button); QMessageBox::information(this, "鼠标点击事件", message); } }; int main(int argc, char *argv[]) { QApplication app(argc, argv); MyMainWindow mainWindow; mainWindow.show(); return app.exec(); } ``` 以上示例代码创建了一个自定义的MainWindow类,它继承自QMainWindow,并重写了鼠标移动事件处理函数和鼠标点击事件处理函数。在这些函数中,可以获取鼠标位置和按钮信息,并根据需要进行处理。可以使用QMessageBox来显示事件信息。 通过以上代码,我们可以实现系统鼠标事件的检测,例如获取鼠标位置、检测鼠标点击等。具体根据需求可以进行更多的处理及扩展。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值