【Qt】系统相关(1)——事件

📅 2026/7/5 5:31:22
【Qt】系统相关(1)——事件
Qt系统相关1. Qt事件2. 处理事件2.1 enterEvent leaveEvent2.2鼠标事件3. 键盘按键事件4. 定时事件4.1设置定时4.2 获取系统日期及时间5.事件分发器5.1派发器的原理6. 移动窗口和改变大小事件1. Qt事件信号槽与事件之间的关系信号槽我们之前对信号槽的定义是用户进行各种操作就有可能能产生信号我们可以给这个信号关联一个参函数当触发这个信号的时候执行对应的槽函数。事件其实和信号槽机制很像也是用户进行各种操作同样也可能产生事件。同样可以给对应的事件关联处理函数处理逻辑当事件触发的时候就能够处理对应的处理函数。事件本身是操作系统提供的机制Qt也同样把操作系统事件进行了封装拿到了Qt中。但是对应事件的代码边写起来不方便。于是Qt对于事件又进行了封装就得到了信号槽。所以对于信号槽来说事件就是它的底层机制。而实际上Qt开发过程中绝大部分和用户的交互都是通过信号槽机制进行的。但是有些特殊情况下信号槽不一定能完成比如某些用户的动作没有对应的Qt信号那么此时就需要通过重写事件处理函数来手动处理用户的响应。在Qt平台中使用一个对象来表示一个事件。所有的Qt事件均继承于抽象类QEvent。事件是由系统或者Qt平台本身在不同的时刻发出的。当用户按下鼠标、敲下键盘或者是窗口需要重新绘制的时候都会发出⼀个相应的事件。⼀些事件是在用户操作时发出如键盘事件、鼠标事件等另⼀些事件则是由系统本身自动发出如定时器事件。常见的Qt事件如下事件名称描述鼠标事件鼠标左键、鼠标右键、鼠标滚轮鼠标的移动鼠标按键的按下和松开键盘事件按键类型、按键按下、按键松开定时器事件定时时间到达进入离开事件鼠标的进入和离开滚轮事件鼠标滚轮滚动绘屏事件重绘屏幕的某些部分显示隐藏事件窗口的显示和隐藏移动事件窗口位置的变化窗口事件是否为当前窗口大小改变事件窗口大小改变焦点事件键盘焦点移动拖拽事件用鼠标进行拖拽2. 处理事件之前的信号槽机制是通过connect函数进行关联度的但是Qt事件的处理方式就有点不一样了。Qt的事件是通过重写某个事件处理函数来进行的。在Qt中几乎所有的Event事件都是虚函数所有可以进行重写实现。这里用到的是多态机制。步骤一般是首先创建子类继承自Qt中已有的事件类然后在子类中重写处理函数。这样后续事件触发的时候就会通过多态机制执行我们重写的事件处理函数了。2.1 enterEvent leaveEvent示例实现鼠标进入事件enterEvent和离开事件leaveEvent[virtual protected]voidQWidget::enterEvent(QEvent*event)This event handler can be reimplemented in a subclass to receive widget enter events which are passed in the event parameter.An event is sent to the widget when the mouse cursor enters the widget.See alsoleaveEvent(),mouseMoveEvent(),andevent().[virtual protected]voidQWidget::leaveEvent(QEvent*event)This event handler can be reimplemented in a subclass to receive widget leave events which are passed in the event parameter.A leave event is sent to the widget when the mouse cursor leaves the widget.See alsoenterEvent(),mouseMoveEvent(),andevent().首先创建一个Label类继承自QLabel此时就需要在生成的子类中重写上面两个函数注意一定要保证函数名和参数类型保持一致Label.h#includeQLabelclass Label:public QLabel{Q_OBJECT public:Label(QWidget*parent);voidenterEvent(QEvent*envet);voidleaveEvent(QEvent*envet);};Label.cpp#includeQDebugLabel::Label(QWidget*parent):QLabel(parent){}voidLabel::enterEvent(QEvent*envet){(void)envet;qDebug()enterEvent;}voidLabel::leaveEvent(QEvent*envet){(void)envet;qDebug()leaveEvent;}但是这个时候还不能进行触发。现在可以通过两种方式进行触发第一种就是使用我们自己创建的Label类生成第二种就是使用ui界面的升级控件来进行。第一种Widget::Widget(QWidget*parent):QWidget(parent),ui(new Ui::Widget){ui-setupUi(this);Label*labelnewLabel(this);label-setText(这是一个代码生成的标签);}第二种方法右击控件选择提升为此时就可以触发事件了。示例实现一个鼠标移动到按钮上时按钮移动同样的首先要创建一个PushButton继承自QPushBUttonpushButton.h#includeQWidget#includeQPushButtonclass PushButton:public QPushButton{Q_OBJECT public:PushButton(QWidget*parent);voidenterEvent(QEvent*event);// 重写鼠标进入事件public:QRect rect;// 创建一个QRect类来存放父窗口的窗口信息};pushButton.cpp#includeQDebug#includerandomPushButton::PushButton(QWidget*parent):QPushButton(parent){rectparent-geometry();// 获取父窗口的窗口信息srand((unsigned)0);// 设置随机种子}voidPushButton::enterEvent(QEvent*event){QRect rthis-geometry();// 获取按钮的信息qDebug()rrect;intxrand()%(rect.width()-r.width());intyrand()%(rect.height()-r.height());this-setGeometry(x,y,r.width(),r.height());// 重写设置按钮的信息}widget.cpp#includepushbutton.hWidget::Widget(QWidget*parent):QWidget(parent),ui(new Ui::Widget){ui-setupUi(this);PushButton*buttonnewPushButton(this);button-setText(按钮);}2.2鼠标事件函数介绍鼠标按下事件[override virtual protected]voidQLabel::mousePressEvent(QMouseEvent*ev)Reimplements:QWidget::mousePressEvent(QMouseEvent*event).同时这里说明一下这个鼠标按下指的是只要鼠标上的按钮按下就会触发无论是鼠标左键右键侧键中键只要是鼠标上的按钮就可以触发。voidLabel::mousePressEvent(QMouseEvent*ev){if(ev-button()Qt::LeftButton){qDebug()鼠标左键被按下;}elseif(ev-button()Qt::RightButton){qDebug()鼠标右键被按下;}// 这个是以屏幕的左上角为原点qDebug()ev-globalX()ev-globalY();// 这个是以标签的左上角为原点qDebug()ev-x(), ev-y();}同理我们还有鼠标释放事件[override virtual protected]voidQLabel::mouseReleaseEvent(QMouseEvent*ev)Reimplements:QWidget::mouseReleaseEvent(QMouseEvent*event).鼠标双击事件[virtual protected]voidQWidget::mouseDoubleClickEvent(QMouseEvent*event)This event handler,forevent event,can be reimplemented in a subclass to receive mousedoubleclick eventsforthe widget.鼠标移动事件[virtual protected]voidQWidget::mouseMoveEvent(QMouseEvent*event)This event handler,forevent event,can be reimplemented in a subclass to receive mouse move eventsforthe widget.If mouse tracking is switched off,mouse move events only occurifa mouse button is pressedwhilethe mouse is being moved.If mouse tracking is switched on,mouse move events occur evenifno mouse button is pressed.QMouseEvent::pos()reports the position of the mouse cursor,relative to this widget.For press and release events,the position is usually the same as the position of the last mouse move event,but it might be differentifthe users hand shakes.This is a feature of the underlying window system,not Qt.If you want to show a tooltip immediately,whilethe mouse ismoving(e.g.,to get the mouse coordinates with QMouseEvent::pos()and show them as a tooltip),you must first enable mouse tracking as described above.Then,to ensure that the tooltip is updated immediately,you must call QToolTip::showText()instead ofsetToolTip()in your implementation ofmouseMoveEvent().前面的代码我们都是在自定义的Label中完成的并且鼠标的作用范围也只是在Label范围内生效。但是如果我们想在窗口中执行也适可以的我们只需要直接在WidgetQWidget的子类进行扩展就行了。也就是直接在WIdget中重写事件函数即可。但是这个鼠标移动事件和上面的鼠标点击时间有所不同由于我们鼠标只需要随便移动一下就可以触发移动事件也就会产生大量的事件一旦我们要处理一些复杂的任务的时候就很容易让程序的负担加重就很容易卡顿。所以Qt为了程序的流畅性默认情况下是不会对鼠标进行追踪的也就是鼠标移动的时候不会调用mouseMoveEvent函数。而要让程序追踪鼠标就必须主动告诉程序就需要调用setMouseTracking函数。Widget::Widget(QWidget*parent):QWidget(parent),ui(new Ui::Widget){ui-setupUi(this);// 把这个选项设置为true才能够追踪到鼠标的移动位置this-setMouseTracking(true);}Widget::~Widget(){delete ui;}voidWidget::mouseMoveEvent(QMouseEvent*event){qDebug()event-x(), event-y();}鼠标滚轮事件[virtual protected]voidQWidget::wheelEvent(QWheelEvent*event)This event handler,forevent event,can be reimplemented in a subclass to receive wheel eventsforthe widget.If you reimplement this handler,it is very important that youignore()the eventifyoudonot handle it,so that the widgets parent can interpret it.Thedefaultimplementation ignores the event.可以通过delta函数获取滚动的数值。voidWidget::wheelEvent(QWheelEvent*event){totalevent-delta();qDebug()total;}3. 键盘按键事件[virtual protected]voidQWidget::keyPressEvent(QKeyEvent*event)This event handler,forevent event,can be reimplemented in a subclass to receive key press eventsforthe widget.A widget must callsetFocusPolicy()to accept focus initially and have focus in order to receive a key press event.If you reimplement this handler,it is very important that you call the base class implementationifyoudonot act upon the key.Thedefaultimplementation closes popup widgetsifthe user presses the key sequenceforQKeySequence::Cancel(typically the Escape key).Otherwise the event is ignored,so that the widgets parent can interpret it.Note that QKeyEvent starts withisAccepted()true,so youdonot need to call QKeyEvent::accept()-justdonot call the base class implementationifyou act upon the key.voidWidget::keyPressEvent(QKeyEvent*event){qDebug()event-key();// 当我们需要按下组合键时需要用到modifiers函数if(event-key()Qt::Key_Aevent-modifiers()Qt::ControlModifier)qDebug()按下了Ctrl A;}4. 定时事件4.1设置定时Qt中在进行窗口程序的处理过程中经常要周期性的执行某些操作或者制作⼀些动画效果使用定时器就可以实现。所谓定时器就是在间隔⼀定时间后去执行某⼀个任务。定时器在很多场景下都会使用到如弹窗自动关闭之类的功能等。Qt中的定时器分为QTimerEvent和QTimer这2个类。QTimerEvent类用来描述⼀个定时器事件。在使用时需要通过startTimer()函数来开启⼀个定时器这个函数需要输入一个以毫秒为单位的整数作为参数来表明设定的时间它返回的整型值代表这个定时器而且这个定时器唯一标识这个定时器。当定时器溢出时即定时时间到达就会触发timerEvent()函数使用killTimer()关闭一个定时器从而获取该定时器的编号来进行相关操作。QTimer类来实现⼀个定时器它提供了更高层次的编程接口如可以使用信号和槽还可以设置只运行一次的定时器。而QTimer其实就是QTimerEvent封装后的结果。方法一使用QTimerEventtimerEvent函数介绍[virtual protected]voidQObject::timerEvent(QTimerEvent*event)This event handler can be reimplemented in a subclass to receive timer eventsforthe object.QTimer provides a higher-level interface to the timer functionality,and also more general information about timers.The timer event is passed in the event parameter.Widget::Widget(QWidget*parent):QWidget(parent),ui(new Ui::Widget){ui-setupUi(this);// 此处开启定时器timerIdthis-startTimer(1000);}Widget::~Widget(){delete ui;}voidWidget::timerEvent(QTimerEvent*event){// 一个程序种可能存在多个定时器所以需要使用timerId来确定是那个定时器触发的if(event-timerId()!timerId){return;}intvalueui-lcdNumber-intValue();if(value0){// 停止定时器this-killTimer(this-timerId);return;}value-1;ui-lcdNumber-display(value);}方法二使用QTimer#includeQTimer#includeQPushButtonWidget::Widget(QWidget*parent):QWidget(parent),ui(new Ui::Widget){ui-setupUi(this);// 此处开启定时器timerIdthis-startTimer(1000);QTimer*timenewQTimer(this);connect(ui-pushButton,QPushButton::clicked,this,[](){time-start(1000);});connect(time,QTimer::timeout,this,[](){staticintnum1;ui-label-setText(QString::number(num));});connect(ui-pushButton_2,QPushButton::clicked,this,[](){time-stop();});}4.2 获取系统日期及时间在Qt中获取系统日期和时间可以通过QTimer和QDataTime类获取QDateTime类提供了字符串格式的时间。字符串形式的时间输出格式由toString()方法中的format参数列表决定可用的参数列表如下现在我们实现一个例子展示的效果就是我们结合一个定时器每过一秒中就显示当前时间同时各两个按钮一个开始一个停止按钮。#includeQDateTime#includeQTimerWidget::Widget(QWidget*parent):QWidget(parent),ui(new Ui::Widget){ui-setupUi(this);QTimer*timenewQTimer(this);connect(ui-pushButton,QPushButton::clicked,this,[](){time-start(1000);});connect(ui-pushButton_2,QPushButton::clicked,this,[](){time-stop();});connect(time,QTimer::timeout,this,Widget::handel);}Widget::~Widget(){delete ui;}voidWidget::handel(){QString strQDateTime::currentDateTime().toString(yyyy-MM-dd hh:mm:ss);ui-label-setText(str);}5.事件分发器在Qt中事件分发器(EventDispatcher)是⼀个核⼼概念⽤处理GUI应用程序中的事件。事件分发器负责将事件从⼀个对象传递到另⼀个对象直到事件被处理或被取消。每个继承自QObject类或QObject类本身都可以在本类中重写boolevent(QEvent*e)函数来实现相关事件的捕获和拦截。5.1派发器的原理在Qt中我们发送的事件都是传给了QObject对象更具体点是传给了QObject对象的event()函数。所有的事件都会进入到这个函数里面那么我们处理事件就要重写这个event()函数。event()函数本身不去处理事件而是根据事件类型type值调用不同的事件处理函数。事件分发器就是工作在应用程序向下分发事件的过程中如下图如上图事件分发器用于分发事件。在此过程中事件分发器也可以做拦截操作。事件分发器主要是通过bool event(QEvent *e)函数来实现。其返回值为布尔类型若为ture代表拦截不向下分发。Qt中的事件是封装QEvent类中在Qt助手中输入QEvent可以查看其所包括的事件类型如下图所示现在我们实现一个拦截器拦截鼠标点击事件voidWidget::mousePressEvent(QMouseEvent*event){if(event-button()Qt::LeftButton){qDebug()鼠标左键被按下;}}bool Widget::event(QEvent*event){if(event-type()QEvent::MouseButtonPress){qDebug()event鼠标左键被按下;returntrue;// 返回true代表不向下分发}// 剩下的交给父类处理默认处理returnQWidget::event(event);}于是当我们点击鼠标左键的时候就不会执行mousePressEvent了但是上面的代码还是会出现一点问题就是当我们连续点击鼠标左键的时候他还是会执行mousePressEvent。这是当你快速点击鼠标左键时Qt 会检测到这是一个双击操作此时第二个点击产生的事件类型不再是QEvent::MouseButtonPress而是 QEvent::MouseButtonDblClick。由于 event() 没有拦截 MouseButtonDblClick这个事件会走到 return QWidget::event(event); 这一行。然后 Qt 父类的QWidget::event() 分发这个双击事件 → 调用 mouseDoubleClickEvent()。而 mouseDoubleClickEvent() 的默认实现恰恰就是调用mousePressEvent()6. 移动窗口和改变大小事件moveEvent[virtual protected]voidQWidget::moveEvent(QMoveEvent*event)This event handler can be reimplemented in a subclass to receive widget move events which are passed in the event parameter.When the widget receives this event,it is already at the new position.resizeEvent[virtual protected]voidQWidget::resizeEvent(QResizeEvent*event)This event handler can be reimplemented in a subclass to receive widget resize events which are passed in the event parameter.WhenresizeEvent()is called,the widget already has its new geometry.The old size is accessible through QResizeEvent::oldSize().The widget will be erased and receive a paint event immediately after processing the resize event.No drawing needbe(or should be)done inside this handler.执行效果展示voidWidget::moveEvent(QMoveEvent*event){qDebug()event-pos();}voidWidget::resizeEvent(QResizeEvent*event){qDebug()event-size();}