当前位置: 首页 > news >正文

北京做百度网站提供营销型网站设计

北京做百度网站,提供营销型网站设计,找公司建网站,广州制作公司网站QT基础教程之七Qt消息机制和事件 事件 事件#xff08;event#xff09;是由系统或者 Qt 本身在不同的时刻发出的。当用户按下鼠标、敲下键盘#xff0c;或者是窗口需要重新绘制的时候#xff0c;都会发出一个相应的事件。一些事件在对用户操作做出响应时发出#xff0c…QT基础教程之七Qt消息机制和事件 事件 事件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(centerh1Move: (%1, %2)/h1/center).arg(QString::number(event-x()),​ QString::number(event-y())));}void EventLabel::mousePressEvent(QMouseEvent *event){this-setText(QString(centerh1Press:(%1, %2)/h1/center).arg(QString::number(event-x()),​ QString::number(event-y())));}void EventLabel::mouseReleaseEvent(QMouseEvent *event){QString msg;msg.sprintf(centerh1Release: (%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(); }EventLabel继承了QLabel覆盖了mousePressEvent()、mouseMoveEvent()和MouseReleaseEvent()三个函数。我们并没有添加什么功能只是在鼠标按下press、鼠标移动move和鼠标释放release的时候把当前鼠标的坐标值显示在这个Label上面。由于QLabel是支持 HTML 代码的因此我们直接使用了 HTML 代码来格式化文字。 QString的arg()函数可以自动替换掉QString中出现的占位符。其占位符以 % 开始后面是占位符的位置例如 %1%2 这种。 QString(“[%1, %2]”).arg(x).arg(y); 语句将会使用x替换 %1y替换 %2因此生成的QString为[x, y]。 在mouseReleaseEvent()函数中我们使用了另外一种QString的构造方法。我们使用类似 C 风格的格式化函数sprintf()来构造QString。 运行上面的代码当我们点击了一下鼠标之后label 上将显示鼠标当前坐标值。 为什么要点击鼠标之后才能在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_castQKeyEvent *(e); ​ if (keyEvent-key() Qt::Key_Tab) { ​ qDebug() You press tab.; ​ return true; ​ }}return QWidget::event(e); }CustomWidget是一个普通的QWidget子类。我们重写了它的event()函数这个函数有一个QEvent对象作为参数也就是需要转发的事件对象。函数返回值是 bool 类型。 如果传入的事件已被识别并且处理则需要返回 true否则返回 false。如果返回值是 true那么 Qt 会认为这个事件已经处理完毕不会再将这个事件发送给其它对象而是会继续处理事件队列中的下一事件。 在event()函数中调用事件对象的accept()和ignore()函数是没有作用的不会影响到事件的传播。 我们可以通过使用QEvent::type()函数可以检查事件的实际类型其返回值是QEvent::Type类型的枚举。我们处理过自己感兴趣的事件之后可以直接返回 true表示我们已经对此事件进行了处理对于其它我们不关心的事件则需要调用父类的event()函数继续转发否则这个组件就只能处理我们定义的事件了。为了测试这一种情况我们可以尝试下面的代码 bool CustomTextEdit::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 false; }CustomTextEdit是QTextEdit的一个子类。我们重写了其event()函数却没有调用父类的同名函数。这样我们的组件就只能处理 Tab 键再也无法输入任何文本也不能响应其它事件比如鼠标点击之后也不会有光标出现。这是因为我们只处理的KeyPress类型的事件并且如果不是KeyPress事件则直接返回 false鼠标事件根本不会被转发也就没有了鼠标事件。 通过查看QObject::event()的实现我们可以理解event()函数同前面的章节中我们所说的事件处理器有什么联系 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事件对象之后会调用QObject的event()函数处理事件的分发。显然我们可以在event()函数中实现拦截的操作。由于event()函数是 protected 的因此需要继承已有类。如果组件很多就需要重写很多个event()函数。这当然相当麻烦更不用说重写event()函数还得小心一堆问题。好在 Qt 提供了另外一种机制来达到这一目的事件过滤器。 QObject有一个eventFilter()函数用于建立事件过滤器。函数原型如下 virtual bool QObject::eventFilter ( QObject * watched, QEvent * event ); 这个函数正如其名字显示的那样是一个“事件过滤器”。所谓事件过滤器可以理解成一种过滤代码。事件过滤器会检查接收到的事件。如果这个事件是我们感兴趣的类型就进行我们自己的处理如果不是就继续转发。这个函数返回一个 bool 类型如果你想将参数 event 过滤出来比如**不想让它继续转发就返回 true否则返回 false。**事件过滤器的调用时间是目标对象也就是参数里面的watched对象接收到事件对象之前。也就是说如果你在事件过滤器中停止了某个事件那么watched对象以及以后所有的事件过滤器根本不会知道这么一个事件。 我们来看一段简单的代码 class MainWindow : public QMainWindow{public:MainWindow();protected:bool eventFilter(QObject *obj, QEvent *event);private:QTextEdit *textEdit;};MainWindow::MainWindow(){textEdit new QTextEdit;setCentralWidget(textEdit);textEdit-installEventFilter(this);}bool MainWindow::eventFilter(QObject *obj, QEvent *event){if (obj textEdit) { ​ if (event-type() QEvent::KeyPress) { ​ QKeyEvent *keyEvent static_castQKeyEvent *(event); ​ qDebug() Ate key press keyEvent-key(); ​ return true; ​ } else { ​ return false; ​ }} else { ​ // pass the event on to the parent class ​ return QMainWindow::eventFilter(obj, event);}}MainWindow是我们定义的一个类。我们重写了它的eventFilter()函数。为了过滤特定组件上的事件首先需要判断这个对象是不是我们感兴趣的组件然后判断这个事件的类型。在上面的代码中我们不想让textEdit组件处理键盘按下的事件。所以首先我们找到这个组件如果这个事件是键盘事件则直接返回 true也就是过滤掉了这个事件其他事件还是要继续处理所以返回 false。对于其它的组件我们并不保证是不是还有过滤器于是最保险的办法是调用父类的函数。 eventFilter()函数相当于创建了过滤器然后我们需要安装这个过滤器。安装过滤器需要调用QObject::installEventFilter()函数。函数的原型如下 void QObject::installEventFilter ( QObject * filterObj ) 这个函数接受一个QObject *类型的参数。记得刚刚我们说的eventFilter()函数是QObject的一个成员函数因此任意QObject都可以作为事件过滤器问题在于如果你没有重写eventFilter()函数这个事件过滤器是没有任何作用的因为默认什么都不会过滤。已经存在的过滤器则可以通过QObject::removeEventFilter()函数移除。 l 我们可以向一个对象上面安装多个事件处理器只要调用多次installEventFilter()函数。如果一个对象存在多个事件过滤器那么最后一个安装的会第一个执行也就是后进先执行的顺序。 还记得我们前面的那个例子吗我们使用event()函数处理了 Tab 键 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);}现在我们可以给出一个使用事件过滤器的版本 bool FilterObject::eventFilter(QObject *object, QEvent *event) {if (object target event-type() QEvent::KeyPress) { ​ QKeyEvent *keyEvent static_castQKeyEvent *(event); ​ if (keyEvent-key() Qt::Key_Tab) { ​ qDebug() You press tab.; ​ return true; ​ } else { ​ return false; ​ }}return false; }事件过滤器的强大之处在于我们可以为整个应用程序添加一个事件过滤器。记得installEventFilter()函数是QObject的函数QApplication或者QCoreApplication对象都是QObject的子类因此我们可以向QApplication或者QCoreApplication添加事件过滤器。**这种全局的事件过滤器将会在所有其它特性对象的事件过滤器之前调用。尽管很强大但这种行为会严重降低整个应用程序的事件分发效率。**因此除非是不得不使用的情况否则的话我们不应该这么做。 注意事件过滤器和被安装过滤器的组件必须在同一线程否则过滤器将不起作用。另外如果在安装过滤器之后这两个组件到了不同的线程那么只有等到二者重新回到同一线程的时候过滤器才会有效。 总结 Qt 的事件是整个 Qt 框架的核心机制之一也比较复杂。说它复杂更多是因为它涉及到的函数众多而处理方法也很多有时候让人难以选择。现在我们简单总结一下 Qt 中的事件机制。 Qt 中有很多种事件鼠标事件、键盘事件、大小改变的事件、位置移动的事件等等。如何处理这些事件实际有两种选择 l 所有事件对应一个事件处理函数在这个事件处理函数中用一个很大的分支语句进行选择其代表作就是 win32 API 的WndProc()函数 LRESULT CALLBACK WndProc(HWND hWnd, ​ UINT message, ​ WPARAM wParam, ​ LPARAM lParam)在这个函数中我们需要使用switch语句选择message参数的类型进行处理典型代码是 switch(message) {case WM_PAINT: ​ break;case WM_DESTROY: ​ break; }每一种事件对应一个事件处理函数。Qt 就是使用的这么一种机制 mouseEvent() keyPressEvent() Qt 具有这么多种事件处理函数肯定有一个地方对其进行分发否则Qt 怎么知道哪一种事件调用哪一个事件处理函数呢这个分发的函数就是event()。显然当QMouseEvent产生之后event()函数将其分发给mouseEvent()事件处理器进行处理。 event()函数会有两个问题 event()函数是一个 protected 的函数这意味着我们要想重写event()必须继承一个已有的类。试想我的程序根本不想要鼠标事件程序中所有组件都不允许处理鼠标事件是不是我得继承所有组件一一重写其event()函数protected 函数带来的另外一个问题是如果我基于第三方库进行开发而对方没有提供源代码只有一个链接库其它都是封装好的。我怎么去继承这种库中的组件呢 event()函数的确有一定的控制不过有时候我的需求更严格一些我希望那些组件根本看不到这种事件。event()函数虽然可以拦截但其实也是接收到了QMouseEvent对象。我连让它收都收不到。这样做的好处是模拟一种系统根本没有那个事件的效果所以其它组件根本不会收到这个事件也就无需修改自己的事件处理函数。这种需求怎么办呢 这两个问题是event()函数无法处理的。于是Qt 提供了另外一种解决方案事件过滤器。事件过滤器给我们一种能力让我们能够完全移除某种事件。事件过滤器可以安装到任意QObject类型上面并且可以安装多个。如果要实现全局的事件过滤器则可以安装到QApplication或者QCoreApplication上面。这里需要注意的是如果使用installEventFilter()函数给一个对象安装事件过滤器那么该事件过滤器只对该对象有效只有这个对象的事件需要先传递给事件过滤器的eventFilter()函数进行过滤其它对象不受影响。如果给QApplication对象安装事件过滤器那么该过滤器对程序中的每一个对象都有效任何对象的事件都是先传给eventFilter()函数。 事件过滤器可以解决刚刚我们提出的event()函数的两点不足 首先事件过滤器不是 protected 的因此我们可以向任何QObject子类安装事件过滤器 其次事件过滤器在目标对象接收到事件之前进行处理如果我们将事件过滤掉目标对象根本不会见到这个事件。 事实上还有一种方法我们没有介绍。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是单例的。
http://www.huolong8.cn/news/91892/

相关文章:

  • 建设网站的企业邮箱低价网站建设
  • 卫龙模仿iphone做网站小程序直播开发
  • 如何做网站管理维护龙岗网站建设方案
  • 网站策划书中应包括市场竞争对手的信息天津塘沽爆炸视频完整
  • 泰州网站制作套餐wordpress前端会员中心
  • wordpress站点图标北京住建网站
  • 无锡建设招标网站传销公司做网站什么罪名
  • 建设微网站多少钱邯郸最新消息
  • 做艺术文字的网站郑州做营销型网站建设
  • 网站建设要那些收费项公司网站维护方案
  • 芜湖移动网站建设大兴快速网站建设哪家好
  • 朝阳网站开发公司seo大全
  • 网站链接视频怎么做短网址在线生成哪个好
  • 打开百度网站建设上海营销网站
  • 怎么建网站教程图解在家怎么利用电脑赚钱
  • 网站模板 餐饮路由器当服务器做网站
  • 临沂网站临沂网站制作页面设计结课总结
  • 网站建设在哪学千锋教育西安校区
  • 确定网站建设目标福州最好的网站设计服务公司
  • 郑州网站建设平台山东网架公司
  • 义乌营销型网站建设中国建设集团官网
  • 广州手机网站建设如保做网站赢利
  • 网站你懂我意思正能量免费软件临沂网站制作计划
  • 微信注册网站wordpress彩色标签云
  • 英文电商网站建设seo快速排名软件价格
  • 找到做网站的公司东莞常平做网站公司
  • 某旅行社网站建设论文网站ftp地址查询
  • 地产网站建设案例上海关键词排名提升
  • 安徽网站设计定制丽水做企业网站的公司
  • 厦门电商网站开发linux建设php网站