首页 > 其他分享 >Qt消息机制和事件

Qt消息机制和事件

时间:2024-03-27 15:30:27浏览次数:22  
标签:Qt 鼠标 QEvent 事件 机制 event 函数

事件

事件(event)是由系统或者 Qt 本身在不同的时刻发出的。当用户按下鼠标、敲下键盘,或者是窗口需要重新绘制的时候,都会发出一个相应的事件。一些事件在对用户操作做出响应时发出,如键盘事件等;另一些事件则是由系统自动发出,如计时器事件。

在前面我们也曾经简单提到,Qt 程序需要在main()函数创建一个QApplication对象,然后调用它的exec()函数。这个函数就是开始 Qt 的事件循环。在执行exec()函数之后,程序将进入事件循环来监听应用程序的事件。当事件发生时,Qt 将创建一个事件对象。Qt 中所有事件类都继承于QEvent。在事件对象创建完毕后,Qt 将这个事件对象传递给QObject的event()函数。event()函数并不直接处理事件,而是按照事件对象的类型分派给特定的事件处理函数(event handler),关于这一点,会在后边详细说明。

在所有组件的父类QWidget中,定义了很多事件处理的回调函数,如

keyPressEvent()
keyReleaseEvent()
mouseDoubleClickEvent()
mouseMoveEvent()
mousePressEvent()
mouseReleaseEvent() 等。

这些函数都是 protected virtual 的,也就是说,我们可以在子类中重新实现这些函数。下面来看一个例子:

class EventLabel : public QLabel

{

protected:

    void mouseMoveEvent(QMouseEvent *event);

    void mousePressEvent(QMouseEvent *event);

    void mouseReleaseEvent(QMouseEvent *event);

};

 

void EventLabel::mouseMoveEvent(QMouseEvent *event)

{

this->setText(QString("<center><h1>Move: (%1, %2)

</h1></center>").arg(QString::number(event->x()),

            QString::number(event->y())));

}

 

void EventLabel::mousePressEvent(QMouseEvent *event)

{

    this->setText(QString("<center><h1>Press:(%1, %2)

</h1></center>").arg(QString::number(event->x()),

                QString::number(event->y())));

}

 

void EventLabel::mouseReleaseEvent(QMouseEvent *event)

{

    QString msg;

    msg.sprintf("<center><h1>Release: (%d, %d)</h1></center>",

                event->x(), event->y());

    this->setText(msg);

}

 

int main(int argc, char *argv[])

{

    QApplication a(argc, argv);

 

    EventLabel *label = new EventLabel;

    label->setWindowTitle("MouseEvent Demo");

    label->resize(300, 200);

    label->show();

 

    return a.exec();

}
  1. EventLabel继承了QLabel,覆盖了mousePressEvent()、mouseMoveEvent()和MouseReleaseEvent()三个函数。我们并没有添加什么功能,只是在鼠标按下(press)、鼠标移动(move)和鼠标释放(release)的时候,把当前鼠标的坐标值显示在这个Label上面。由于QLabel是支持 HTML 代码的,因此我们直接使用了 HTML 代码来格式化文字。
  2. QString的arg()函数可以自动替换掉QString中出现的占位符。其占位符以 % 开始,后面是占位符的位置,例如 %1,%2 这种。

QString("[%1, %2]").arg(x).arg(y);

语句将会使用x替换 %1,y替换 %2,因此,生成的QString为[x, y]。

  • 在mouseReleaseEvent()函数中,我们使用了另外一种QString的构造方法。我们使用类似 C 风格的格式化函数sprintf()来构造QString。

运行上面的代码,当我们点击了一下鼠标之后,label 上将显示鼠标当前坐标值。

f32aa9a775b544f98770fe36e2c63d65.png

为什么要点击鼠标之后才能在mouseMoveEvent()函数中显示鼠标坐标值?

这是因为QWidget中有一个mouseTracking属性,该属性用于设置是否追踪鼠标。只有鼠标被追踪时,mouseMoveEvent()才会发出。如果mouseTracking是 false(默认即是),组件在至少一次鼠标点击之后,才能够被追踪,也就是能够发出mouseMoveEvent()事件。如果mouseTracking为 true,则mouseMoveEvent()直接可以被发出。

知道了这一点,我们就可以在main()函数中添加如下代码:

label->setMouseTracking(true);

在运行程序就没有这个问题了。

 

event()

事件对象创建完毕后,Qt 将这个事件对象传递给QObject的event()函数。event()函数并不直接处理事件,而是将这些事件对象按照它们不同的类型,分发给不同的事件处理器(event handler)。

如上所述,event()函数主要用于事件的分发。所以,如果你希望在事件分发之前做一些操作,就可以重写这个event()函数了。例如,我们希望在一个QWidget组件中监听 tab 键的按下,那么就可以继承QWidget,并重写它的event()函数,来达到这个目的:

bool CustomWidget::event(QEvent *e)

{

    if (e->type() == QEvent::KeyPress) {

        QKeyEvent *keyEvent = static_cast<QKeyEvent *>(e);

        if (keyEvent->key() == Qt::Key_Tab) {

            qDebug() << "You press tab.";

            return true;

        }

    }

    return QWidget::event(e);

}

CustomWidget是一个普通的QWidget子类。我们重写了它的event()函数,这个函数有一个QEvent对象作为参数,也就是需要转发的事件对象。函数返回值是 bool 类型。

  1. 如果传入的事件已被识别并且处理,则需要返回 true,否则返回 false。如果返回值是 true,那么 Qt 会认为这个事件已经处理完毕,不会再将这个事件发送给其它对象,而是会继续处理事件队列中的下一事件。
  2. 在event()函数中,调用事件对象的accept()和ignore()函数是没有作用的,不会影响到事件的传播

我们可以通过使用QEvent::type()函数可以检查事件的实际类型,其返回值是QEvent::Type类型的枚举。我们处理过自己感兴趣的事件之后,可以直接返回 true,表示我们已经对此事件进行了处理;对于其它我们不关心的事件,则需要调用父类的event()函数继续转发,否则这个组件就只能处理我们定义的事件了。为了测试这一种情况,我们可以尝试下面的代码:

bool CustomTextEdit::event(QEvent *e)

{

   if (e->type() == QEvent::KeyPress)

{

        QKeyEvent *keyEvent = static_cast<QKeyEvent *>(e);

       if (keyEvent->key() == Qt::Key_Tab)

{

            qDebug() << "You press tab.";

            return true;

       }

    }

    return false;

}

CustomTextEdit是QTextEdit的一个子类。我们重写了其event()函数,却没有调用父类的同名函数。这样,我们的组件就只能处理 Tab 键,再也无法输入任何文本,也不能响应其它事件,比如鼠标点击之后也不会有光标出现。这是因为我们只处理的KeyPress类型的事件,并且如果不是KeyPress事件,则直接返回 false,鼠标事件根本不会被转发,也就没有了鼠标事件。

通过查看QObject::event()的实现,我们可以理解,event()函数同前面的章节中我们所说的事件处理器有什么联系:

//!!! Qt5

bool QObject::event(QEvent *e)

{

    switch (e->type()) {

    case QEvent::Timer:

        timerEvent((QTimerEvent*)e);

        break;



    case QEvent::ChildAdded:

    case QEvent::ChildPolished:

    case QEvent::ChildRemoved:

        childEvent((QChildEvent*)e);

        break;

    // ...

    default:

        if (e->type() >= QEvent::User) {

            customEvent(e);

            break;

        }

        return false;

    }

    return true;

}

这是 Qt 5 中QObject::event()函数的源代码(Qt 4 的版本也是类似的)。我们可以看到,同前面我们所说的一样,Qt 也是使用QEvent::type()判断事件类型,然后调用了特定的事件处理器。比如,如果event->type()返回值是QEvent::Timer,则调用timerEvent()函数。可以想象,QWidget::event()中一定会有如下的代码:

switch (event->type()) {

    case QEvent::MouseMove:

        mouseMoveEvent((QMouseEvent*)event);

        break;

    // ...

}

事实也的确如此。timerEvent()和mouseMoveEvent()这样的函数,就是我们前面章节所说的事件处理器 event handler。也就是说,event()函数中实际是通过事件处理器来响应一个具体的事件。这相当于event()函数将具体事件的处理“委托”给具体的事件处理器。而这些事件处理器是 protected virtual 的,因此,我们重写了某一个事件处理器,即可让 Qt 调用我们自己实现的版本。

由此可以见,event()是一个集中处理不同类型的事件的地方。如果你不想重写一大堆事件处理器,就可以重写这个event()函数,通过QEvent::type()判断不同的事件。鉴于重写event()函数需要十分小心注意父类的同名函数的调用,一不留神就可能出现问题,所以一般还是建议只重写事件处理器(当然,也必须记得是不是应该调用父类的同名处理器)。这其实暗示了event()函数的另外一个作用:屏蔽掉某些不需要的事件处理器。正如我们前面的CustomTextEdit例子看到的那样,我们创建了一个只能响应 tab 键的组件。这种作用是重写事件处理器所不能实现的。

 

标签:Qt,鼠标,QEvent,事件,机制,event,函数
From: https://blog.csdn.net/ffffffeiyu/article/details/137043191

相关文章

  • 极高创新性!基于斑马算法优化并行卷积神经网络注意力机制结合支持向量机ZOA-PCNN-AT-SV
     ✅作者简介:热爱科研的Matlab仿真开发者,修心和技术同步精进,代码获取、论文复现及科研仿真合作可私信。......
  • Qt小知识4.QWindow和QWidget
    1引言QWindow和QWidget都是Qt框架中用于创建和管理窗口的类,但它们在设计上服务于不同的目的和场景。这两者的区别不仅体现在API设计上,还体现在它们在Qt框架中的角色和使用方式上。2典型区别2.1继承结构和依赖QWidget继承自QObject和QPaintDevice,是所有窗口部......
  • Android中运动事件的处理
    1.目录目录1.目录2.前言3.程序演示4.第二种程序示例5.扩展2.前言        触摸屏(TouchScreen)和滚动球(TrackBall)是Android中除了键盘之外的主要输入设备。如果需要使用触摸屏和滚动球,主要可以通过使用运动事件(MotionEvent)用于接收它们的信息。触摸屏和滚动球......
  • e-chart实现上下图标使用一个tooltip移入事件
    有的需求需要一次移入事件展示多个图标的tooltip方便用户查看,效果如下链接如下:e-chart主要配置是tooltip:{trigger:'axis',axisPointer:{animation:false}},axisPointer:{link:[{xAxisIndex:'all'}]......
  • Qt实现TFTP Server和 TFTP Client(四)
    3.3ServerServer包括下面3个类:ServerSocketTFtpServerTFtpServerWidget3.3.1ServerSocketServerSocket从BaseUdp派生实现write接口.3.3.1.1ServerSocket定义classQUdpSocket;classServerSocket:publicBaseUdp{public:ServerSocket(QUdpSocket*socket......
  • Qt - 音视频播放2
    解码器下载链接:LAVfilters 音视频播放播放内存中的音乐QFileread("./music/Nevada.mp3");  if(!read.open(QIODevice::ReadOnly)) {    qDebug()<<"文件打开失败,请重试~"; }  //下面用的对象,必须动态申请,不然构造函数执行完毕,局部变量内存......
  • js事件循环
            js的事件循环是js的一种执行机制。    众所周知,js是一门单线程的语言,也就是同步的,同一时间只能做一件事情。这是因为浏览器仅分配给了js一个主线程来执行任务,且一次只能执行一个任务。这些待执行的任务排好队伍,依次执行。    但是js某些任务......
  • VS2022软件打包 生成和事后事件处理
    VS2022软件打包生成和事后事件处理 示例目标:将编译后的文件拷贝到新的文件,并重命名方便软件打包 生成前:删除目标目录:rd/s/q"$(SolutionDir)..\setup\$(ConfigurationName)\"生成后-拷贝重命名:copy"$(TargetPath)"$(TargetDir)JCZX-2024.exe"copy"$(TargetPat......
  • 信号的概念和机制
    1.信号的概念和机制理解信号可以参考生活中,烽火、狼烟等信号的特点:1.简单;2.不能携带大量信息;3.满足某个特设条件才发送1.1.信号的机制信号时软件层面的“中断”,信号VS中断VS异常,三个概念可以一起学习每个进程收到的所有信号,都是由内核负责发送、内核处理的简单地说,unix的......
  • JS之windows常见事件onload
    //window.onload=function(){//varbtn=document.querySelector('button');//btn.addEventListener('click',function(){//alert('点击我');//})//}//window.......