山西做杂粮的网站,网站开发项目实训报告,稳稳在哪个网站做的消防直播,织梦小说网站源码OVERVIEW 事件与事件过滤器一、事件1.鼠标事件创建子类MyLabel重写鼠标事件提升Label控件为MyLabel 2.定时器事件timerEventQTimer 3.事件分发器#xff08;event函数#xff09;event函数重写event函数深入 二、事件过滤器1.事件过滤器2.事件处理的五个层次 事件与事件过滤器… OVERVIEW 事件与事件过滤器一、事件1.鼠标事件创建子类MyLabel重写鼠标事件提升Label控件为MyLabel 2.定时器事件timerEventQTimer 3.事件分发器event函数event函数重写event函数深入 二、事件过滤器1.事件过滤器2.事件处理的五个层次 事件与事件过滤器 一、事件
事件event是由系统或者Qt本身在不同的时刻发出的主要包括用户事件与定时器事件
当用户按下鼠标、敲下键盘或者是窗口需要重新绘制的时候都会发出一个相应的事件一些事件是在对用户操作做出响应时进行发出如键盘事件等另一些事件则是由系统自动发出如计时器事件。
Qt 程序需要在main()函数创建一个QApplication对象然后调用其exec()函数这个函数就是开始Qt的事件循环。在执行exec()函数之后程序将进入事件循环来监听应用程序的事件。当事件发生时 Qt 将创建一个事件对象Qt 中所有事件类都继承于QEvent在事件对象创建完毕后Qt 将这个事件对象传递给QObject的event()函数。event()函数并不直接处理事件而是按照事件对象的类型分派给特定的事件处理函数event handler
在所有组件的父类QWidget中定义了很多事件处理的回调函数如
keyPressEvent()keyReleaseEvent()mouseDoubleClickEvent()mouseMoveEvent()mousePressEvent()mouseReleaseEvent()等
1.鼠标事件 鼠标双击、鼠标按下、鼠标释放、鼠标移动、鼠标轨迹追踪 enterEvent进入控件Widget事件
以上这些函数都是protected virtual的可以在子类中重新实现重写的鼠标事件的主要步骤如下
创建label关联提升的子类创建label并提升重写鼠标事件 鼠标进入鼠标离开鼠标按下鼠标释放鼠标移动追踪状态激活
如果想要对控件进行构造、析构、事件捕捉等自定义操作需要利用自定义控件来实现。这里省略对控件ui界面的设计直接对widget控件的功能进行自定义操作操作如下
创建子类MyLabel
创建QLabel的子类MyLabel类右键项目添加新文件选择C中的Cclass不带有ui界面如下图所示 设置好需要继承的父类以及根据语义确定需要自定义的类名称这里起名为MyLabel 重写鼠标事件
在MyLabel中重写鼠标的各种事件包括
void enterEvent(QEvent *event);
void leaveEvent(QEvent *event);
//重写虚函数
virtual void mousePressEvent(QMouseEvent *ev);
virtual void mouseReleaseEvent(QMouseEvent *ev);
virtual void mouseMoveEvent(QMouseEvent *ev);具体代码如下所示
#ifndef MYLABEL_H
#define MYLABEL_H#include QMainWindow
#include QLabelclass MyLabel : public QLabel {Q_OBJECT
public:explicit MyLabel(QWidget *parent nullptr);void enterEvent(QEvent *event);void leaveEvent(QEvent *event);//重写虚函数virtual void mousePressEvent(QMouseEvent *ev);virtual void mouseReleaseEvent(QMouseEvent *ev);virtual void mouseMoveEvent(QMouseEvent *ev);
signals:
public slots:
};#endif // MYLABEL_Hvoid MyLabel::enterEvent(QEvent *event) {qDebug() mouse entered.;
}void MyLabel::leaveEvent(QEvent *event) {qDebug() mouse leave.;
}void MyLabel::mousePressEvent(QMouseEvent *ev) {qDebug() mouse pressed.;QString str1 QString(mouse pressed at position(%1, %2).).arg(ev-x()).arg(ev-y());qDebug() str1;
}void MyLabel::mouseReleaseEvent(QMouseEvent *ev) {qDebug() mouse released.;QString str1;str1.sprintf(mouse released at position(%1, %2)., ev-x(), ev-y());qDebug() str1;
}void MyLabel::mouseMoveEvent(QMouseEvent *ev) {qDebug() mouse moved.;QString str1 QString(mouse moved at position(%1, %2).).arg(ev-x()).arg(ev-y());qDebug() str1;
}MyLabel继承了QLabel覆盖了mousePressEvent()、mouseMoveEvent()和MouseReleaseEvent()三个函数。并没有添加什么功能只是在鼠标按下press、鼠标移动move和鼠标释放release的时候把当前鼠标的坐标值打印出来。 QString的arg()函数可以自动替换掉QString中出现的占位符。其占位符以 % 开始后面是占位符的位置例如 %1%2 这种。 QString([%1, %2]).arg(x).arg(y); 语句将会使用x替换 %1y替换 %2因此生成的QString为[x, y]。 在mouseReleaseEvent()函数中使用了另外一种QString的构造方法。类似 C 风格的格式化函数sprintf()来构造QString。
在上面的代码中只有在点击鼠标之后移动鼠标才能在mouseMoveEvent()函数中显示鼠标坐标值
这是因为QWidget中有一个mouseTracking属性该属性用于设置是否追踪鼠标。只有鼠标被追踪时mouseMoveEvent()才会发出。如果mouseTracking是 false默认组件在至少一次鼠标点击之后才能够被追踪发出mouseMoveEvent()事件。如果mouseTracking为 true则mouseMoveEvent()直接可以被发出。
可以在main()函数中添加如下代码label-setMouseTracking(true); 这样鼠标移动操作就可以直接被捕获而不用先点击鼠标了。
MyLabel::MyLabel(QWidget *parent) : QLabel(parent) {setMouseTracking(true);//设置鼠标追踪状态
}提升Label控件为MyLabel
在需要利用MyLabel控件的ui界面中加入Label控件并有点点击将其提升为MyLabel控件从而重写的各种方法可以该控件上起作用
最后达到捕获鼠标事件的效果 2.定时器事件
由定时器timer定时触发的时间叫做timerEvent定时器事件
timerEvent
class MainWindow : public QMainWindow {Q_OBJECT
public:MainWindow(QWidget *parent nullptr);~MainWindow();//重写定时器的事件void timerEvent(QTimerEvent *);int tid1;int tid2;int tid3;
private:Ui::MainWindow *ui;
};MainWindow::MainWindow(QWidget *parent):QMainWindow(parent), ui(new Ui::MainWindow) {ui-setupUi(this);tid1 startTimer(1000);tid2 startTimer(2000);tid3 startTimer(3000);
}void MainWindow::timerEvent(QTimerEvent *ev) {if (ev-timerId() tid1) {static int num1 1;ui-label2-setText(QString::number(num1));} else if (ev-timerId() tid2) {static int num2 1;ui-label3-setText(QString::number(num2));} else if (ev-timerId() tid3) {static int num3 1;ui-label4-setText(QString::number(num3));}
}QTimer
定时器的使用除了timerEvent的方式还有一个专门的定时器类QTimer该类非常的强大提供了很多的功能
MainWindow::MainWindow(QWidget *parent):QMainWindow(parent), ui(new Ui::MainWindow) {ui-setupUi(this);QTimer *timer new QTimer(this);timer-start(500);connect(timer, QTimer::timeout, this, [](){static int num4 1;ui-label5-setText(QString::number(num4));});connect(ui-btn, QPushButton::clicked, this, [](){if (timer-isActive()) timer-stop();else timer-start();});
}3.事件分发器event函数
事件分发eventDispatch 事件对象创建完毕后Qt 将这个事件对象传递给QObject的event()函数。event()函数并不直接处理事件而是将这些事件对象按照它们不同的类型分发给不同的事件处理器event handlerevent()函数主要用于事件的分发。
event函数重写
如果开发者希望在事件不向下分发之而是做一些自定义的操作则需要重写event()函数。
通过自定义的event函数处理过感兴趣的事件之后可以直接返回 true表示我们已经对此事件进行了处理对于其它我们不关心的事件则需要调用父类的event()函数继续转发否则这个组件就只能处理我们定义的事件了。
例1要在某个QWidget组件中监听鼠标点击的按下就可以继承QWidget并重写它的event()函数截断事件分发的流程来达到目的
void MyLabel::mousePressEvent(QMouseEvent *ev) {qDebug() mouse pressed.;QString str1 QString(mouse pressed at position(%1, %2).).arg(ev-x()).arg(ev-y());qDebug() str1;
}bool MyLabel::event(QEvent *e) {//如果ev-typy为鼠标按下事件 则在event事件分发中做拦截操作if (e-type() QEvent::MouseButtonPress) {QMouseEvent *ev static_castQMouseEvent *(e);QString str1 QString(event dispatch catched: mouse pressed at position(%1, %2).).arg(ev-x()).arg(ev-y());qDebug() str1;return true;//true代表用户自己处理不向下分发}//其他的事件交给父类处理 默认处理return QLabel::event(e);
}例2要在某个QWidget组件中监听 tab 键的按下就可以继承QWidget并重写它的event()函数来达到这个目的
bool CustomWidget::event(QEvent *e) {if (e-type() QEvent::KeyPress) {QKeyEvent *keyEvent static_castQKeyEvent *(e);if (keyEvent-key() Qt::Key_Tab) {qDebug() You press tab.;return true;}}return QWidget::event(e);
}CustomWidget是QWidget的子类这里重写了其event()函数event函数有一个QEvent对象作为参数需要转发的事件对象。
返回true表示传入的事件已被识别并且处理不会再将这个事件发送给其它对象会继续处理事件队列中的下一事件。在event()函数中调用事件对象的accept()和ignore()函数是没有作用的不会影响到事件的传播。
可以通过使用QEvent::type()函数可以检查事件的实际类型其返回值是QEvent::Type类型的枚举。
event函数深入
Qt的event函数实际上本质是使用QEvent::type()判断事件类型然后根据类型调用对应事件处理器
其实际是通过事件处理器来响应一个具体的事件这相当于event()函数将具体事件的处理“委托”给具体的事件处理器。
而这些事件处理器是 protected virtual 的因此只要重写了某个事件处理器即可让 Qt 调用开发者自己实现的版本。由此可以见event()是一个集中处理不同类型的事件的地方。
switch (event-type()) {case QEvent::MouseMove:mouseMoveEvent((QMouseEvent*)event);break;// ...
}结论
如果不想重写一大堆事件处理器就可以重写这个event()函数通过QEvent::type()判断不同的事件。由于重写event()函数需要十分小心注意父类的同名函数调用容易出问题所以一般还是建议只重写事件处理器当然也必须记得是不是应该调用父类的同名处理器。这其实暗示了event()函数的另外一个作用屏蔽掉某些不需要的事件处理器。正如前面的CustomTextEdit例子看到的那样创建了一个只能响应 tab 键的组件。这种作用是重写事件处理器所不能实现的。
二、事件过滤器
1.事件过滤器
有时候对象需要查看、甚至要拦截发送到另外对象的事件。如对话框可能想要拦截按键事件不让别的组件接收到或者要修改回车键的默认处理。Qt 创建了QEvent事件对象之后会调用QObject的event()函数处理事件的分发显然可以在event()函数中实现拦截的操作。
event()函数会有两个问题
event()函数是protected的函数 这意味着要想重写event()必须继承一个已有的类如果程序不需要鼠标事件程序中所有组件都不允许处理鼠标事件那就得继承所有组件并重写其event()函数这显然当然相当麻烦更不用说重写event()函数还得小心一堆问题。如果程序基于第三方库开发在不知道第三方库源码的情况下无法进行这种组件继承操作。 event()函数的确有一定的控制但是组件仍然接收到了QMouseEvent对象感知到了事件的发生 事件过滤器在目标对象接收到事件之前进行处理目标对象根本不会见到这个事件。
这两个问题是event()函数无法处理的于是Qt 提供了另外一种机制来解决事件过滤器。 QObject的eventFilter()函数用于建立事件过滤器函数原型如下
virtual bool QObject::eventFilter ( QObject * watched, QEvent * event );函数解释
事件过滤器会检查接收到的事件如果该事件是感兴趣的类型就进行自定义处理否则继续转发。函数返回值为true表示事件已处理返回值为false表示继续进行事件转发。事件过滤器的调用时间是目标对象接收到事件对象之前如果在事件过滤器中停止了某事件那么watched对象以及以后所有的事件过滤器也不会知道这么一个事件已经被过滤。
事件过滤器使用步骤1.为控件安装事件过滤器2.重写eventFilter事件
class MainWindow : public QMainWindow {Q_OBJECT
public:MainWindow(QWidget *parent nullptr);~MainWindow();//重写事件过滤器bool eventFilter(QObject *watched, QEvent *event);
private:Ui::MainWindow *ui;
};MainWindow::MainWindow(QWidget *parent):QMainWindow(parent), ui(new Ui::MainWindow) {ui-setupUi(this);//步骤1 为label1安装事件过滤器ui-label1-installEventFilter(this);//传递需要安装事件过滤器的父类
}//步骤2 重写eventfilter事件
bool MainWindow::eventFilter(QObject *watched, QEvent *event) {if (watched ui-label1 event-type() QEvent::MouseButtonPress) {QMouseEvent *ev static_castQMouseEvent*(event);QString str1 QString(eventFilter: mouse pressed at position(%1, %2).).arg(ev-x()).arg(ev-y());qDebug() str1;return true;}return QMainWindow::eventFilter(watched, event);
}重写了MainWindow类中的eventFilter()函数为了过滤特定组件上的事件首先判断该对象是否为感兴趣的组件然后判断这个事件的类型。watched ui-label1 event-type() QEvent::MouseButtonPress 对鼠标点击事件进行拦截如果这个事件是目标事件则进行自定义操作后直接返回 true过滤掉了这个事件 其他事件需要继续处理所以返回 false。对于其它的组件并不能保证是否还有过滤器于是最保险的方式是调用父类的函数。 安装过滤器需要调用QObject::installEventFilter()函数。函数的原型如下 void QObject::installEventFilter(QObject * filterObj);该函数接受一个QObject *类型的参数eventFilter()函数是QObject的一个成员函数因此任意QObject都可以作为事件过滤器如果没有重写eventFilter()函数这个事件过滤器是没有任何作用的默认什么都不会过滤。已经存在的过滤器则可以通过QObject::removeEventFilter()函数移除。 可以向1个对象上面安装多个事件处理器只要调用多次installEventFilter()函数。如果一个对象存在多个事件过滤器那么最后一个安装的会第一个执行即后进先执行的顺序。
特别注意事件处理器必须在同一线程才能使用生效
件过滤器和被安装过滤器的组件必须在同一线程否则过滤器将不起作用。如果在安装过滤器之后这两个组件到了不同的线程只有等到二者重新回到同一线程的时候过滤器才会有效。installEventFilter()函数是QObject的函数QApplication或者QCoreApplication对象都是QObject的子类因此可以向QApplication或者QCoreApplication添加事件过滤器这种全局的事件过滤器将会在所有其它特性对象的事件过滤器之前调用。尽管这种全局事件过滤器很强大但会严重降低整个应用程序的事件分发效率因此一般不会这么处理。
2.事件处理的五个层次
QCoreApplication::notify()
事件拦截实际上还有一种方法Qt 事件的调用最终都会追溯到QCoreApplication::notify()函数因此最大的控制权实际上是重写QCoreApplication::notify()。该函数声明
virtual bool QCoreApplication::notify(QObject *receiver, QEvent *event);该函数会将event发送给receiver也就是调用receiver-event(event)其返回值就是来自receiver的事件处理器。
该函数为任意线程的任意对象的任意事件调用因此不存在事件过滤器的线程的问题。不过并不推荐这么做因为notify()函数只有一个而事件过滤器要灵活得多。
Qt 的事件处理实际上是有五个层次
重写paintEvent()、mousePressEvent()等事件处理函数。这是最普通、最简单的形式同时功能也最简单。重写event()函数。event()函数是所有对象的事件入口QObject和QWidget中的实现默认是把事件传递给特定的事件处理函数。在特定对象上面安装事件过滤器。该过滤器仅过滤该对象接收到的事件。在QCoreApplication::instance()上面安装事件过滤器。该过滤器将过滤所有对象的所有事件因此和notify()函数一样强大但是它更灵活因为可以安装多个过滤器。全局的事件过滤器可以看到 disabled 组件上面发出的鼠标事件。全局过滤器有一个问题只能用在主线程。重写QCoreApplication::notify()函数。这是最强大的和全局事件过滤器一样提供完全控制并且不受线程的限制。但是全局范围内只能有一个被使用因为QCoreApplication是单例的。