[Qt]事件-鼠标事件、键盘事件、定时器事件、窗口改变事件、事件分发器与事 ...

打印 上一主题 下一主题

主题 1015|帖子 1015|积分 3045

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
目录
前言:Qt与操纵体系的关系
一、Qt事件
1.事件介绍
2.事件的表现情势
常见的Qt事件:
常见的事件形貌:
3.事件的处理方式
处理鼠标进入和离开事件案例
控件添加到对象树底层原理
二、鼠标事件
1.鼠标按下和开释事件(单击)
2.鼠标双击事件
3.鼠标移动事件
4.鼠标滚轮滚动事件
三、键盘事件 
1.键盘按下单个按键事件
2.组合键按下事件
四、定时器事件
利用说明
Demo:设置倒计时程序 
五、窗口干系事件
1.窗口移动事件
2.窗口巨细改变事件
六、事件分发器与事件过滤器


前言:Qt与操纵体系的关系

        Qt与操纵体系的关系,虽然说Qt是一个跨平台的C++开辟框架,但是Qt的很多能力实在都是操纵体系提供的,而且Qt应用程序也是运行在操纵体系上的,也需要操纵体系的支持。只不外是Qt封装了体系调用的API而已,差别平台下封装差别的体系调用,实现了跨平台的能力。
        对于事件、文件操纵、多线程编程、网络编程等模块的编程下,Qt也是无法独立完成的,肯定是需要操纵体系的支持,以是Qt的很多业务实现都需要操纵体系提供很多的帮助。

一、Qt事件

1.事件介绍

        对于信号槽来说,用户举行的各种操纵都大概会产生信号,可以给信号绑定槽函数,那么当信号触发的时间,就会自动的调用实行对应的槽函数了。事件和信号槽很相似,用户举行各种操纵的时间,也会产生事件,同样可以给事件关联上处理函数或者说处理逻辑,当事件触发的时间,就能够实行对应的函数/逻辑了。

        信号槽机制是Qt的机制,而事件是操纵体系的概念,Qt同样把操纵体系的事件机制举行了封装,就变成了Qt的事件机制,而信号槽机制实在也就是Qt事件的更一步的封装机制。在现实开辟当中绝大多数的情况,都是利用Qt的信号槽机制举行交互的。
        但是会有一些特殊情况,大概用户的一些行为操纵,Qt中没有提供对的信号,也就没法关联信号槽了,此时就需要通过重写事件处理函数的情势,来手动处理事件的相应逻辑了。以是说事件机制可以让程序员根据现实的需求,更加深度化的去定制用户操纵对应的处理逻辑了。

2.事件的表现情势

        Qt中的是利用一个对象来表示一个事件的,所有的Qt事件类都是继承于QEvent抽象类。事件的发出是通过操纵体系或者Qt平台自己在代码逻辑下发出的。一些事件是由用户操纵后发出,例如按下键盘,按下鼠标等。尚有一些事件是由程序或体系自己发出的,例如定时器事件等。
常见的Qt事件:


        对于差别的事件关注的点是不一样的,以是说类内部的实现提供的接口方法也是不一样的。 
常见的事件形貌:

事件说明
鼠标事件鼠标左键、鼠标右键等各种按键、鼠标滚轮、鼠标移动、按键的按下和松开
键盘事件      按键类型、按键按下和松开
定时器事件定时事件到达
进入离开事件  鼠标进入和离开地区
滚轮事件鼠标滚轮滚动
绘屏事件重绘屏幕的某些部分
显示隐藏事件窗口的显示和隐藏
移动事件窗口位置的变化
窗口事件是否为当前窗口
巨细改变事件窗口巨细改变
焦点事件键盘焦点的移动

3.事件的处理方式

        对于信号来说是定义槽函数并和信号举行关联,而事件的处理不太一样,需要重写该事件的处理函数来实现事件的处理。详细是利用多态的机制,创建子类,继承要监控的父类,在子类中重写事件处理函数,后续事件触发的时间,就会通过多态的机制,实行到重写的子类事件实行函数了。
        为什么非要创建子类,在子类中重写事件处理函数呢?由于对于每个控件的相同事件有着差别的处理逻辑,以是需要为每一个控件定制他自己独有的处理方法,固然所有控件用一个也是可以的,直接在QWidget类中重写事件处理函数就可以了,这样就会使所有的控件同一事件的处理操纵一样了。
处理鼠标进入和离开事件案例



        首先创建子类,然后再子类中定义上图中的两个事件处理函数,enterEvent是鼠标进入事件,leaveEvent是鼠标离开事件。之后可以代码中创建我们的子类控件,也就是创建一个父类指针指向子类的对象,然后鼠标进入和离开通过多态机制,就会实行子类重写的函数了。
        也可以通过ui界面的方式,我们拖拽一个父类控件,然后把控件提升为一个子类控件,同时设置该控件的父类即可。原理和上述雷同,就是把标准控件变为了标准控件的自定义子类控件,会继承标准控件的属性和功能。


控件添加到对象树底层原理

        创建一个QPushButton之后,他会调用QPushButton的构造函数,QPushButton继承于QAbstractButton,以是会调用QAbstractButton的构造函数,QAbstractButton继承于QWidget,以是对调用QWidget的构造函数,会一直层层调用,直到调用的QObject的时间位置,他是所有类的基类,那么调用之后,parent参数也层层转达给了QObject类,通过参数的值来判断是否要添加到对象树,以及添加到什么位置上。



二、鼠标事件

        Qt中的鼠标事件是由QMouseEvent类来实现的,当鼠标移动和操纵都会产生鼠标事件。
1.鼠标按下和开释事件(单击)



        鼠标按下事件处理函数是mousePressEvent,鼠标开释事件处理函数是mouseReleaseEvent他带了一个event参数,内部就带有了鼠标的各种属性,例如鼠标点击/开释的位置、哪个按键点击/开释的等等。
        怎样判断是操纵的鼠标哪个键呢,Qt中提供了一些MouseButton枚举变量对应的就是鼠标的哪一个按键了。Qt:eftButton左键、Qt::RightButton右键、Qt::MidButton鼠标滚轮按键。 
        对于mousePressEvent处理函数来说, 无论是鼠标的左键、右键、滚轮等等按键都能举行触发,但是对于一些游戏鼠标的特殊按键是不肯定的,由于那些按键是通过特定的驱动程序转化为一些其他操纵了,不肯定是原生的鼠标点击操纵。
        对于QPushButton的clicked()信号就是对应的一次鼠标按下和一次鼠标开释事件。
  1. void Label::mousePressEvent(QMouseEvent *event)
  2. {
  3.     if(event->button() == Qt::LeftButton)
  4.     {
  5.         qDebug() << "按下左键";
  6.     }
  7.     else if(event->button() == Qt::RightButton)
  8.     {
  9.         qDebug() << "按下右键";
  10.     }
  11.     //当前ev对象,就包含了鼠标点击位置的坐标
  12.     qDebug() << event->x() << ", " << event->y();
  13.     //相对于屏幕左上角的坐标
  14.     qDebug() << event->globalX() << ", " << event->globalY();
  15. }
  16. void Label::mouseReleaseEvent(QMouseEvent *event)
  17. {
  18.     if(event->button() == Qt::LeftButton)
  19.     {
  20.         qDebug() << "释放左键";
  21.     }
  22.     else if(event->button() == Qt::RightButton)
  23.     {
  24.         qDebug() << "释放右键";
  25.     }
  26.     //当前ev对象,就包含了鼠标点击位置的坐标
  27.     qDebug() << event->x() << ", " << event->y();
  28.     //相对于屏幕左上角的坐标
  29.     qDebug() << event->globalX() << ", " << event->globalY();
  30. }
复制代码

2.鼠标双击事件

         鼠标双击事件的处理函数是mouseDoubleClickEvent函数,利用方法和上述单击的没什么区别。对于双击左键的事件隔断是跟随体系的,当鼠标双击的时间,单击事件也会触发,当第一次点击的时间就会触发单击操纵,第二次点击的时间会触发双击操纵,以是如果处理不好可以会出题目。

3.鼠标移动事件


        鼠标移动的事件处理函数是mouseMoveEvent函数,所有的操纵和上述也都是一样的,都是基于event参数举行的操纵。但是我们实现完之后会发现,我们想在鼠标移动的时间打印鼠标所处的位置,却打印不出来,是为什么呢?
        由于鼠标移动差别于鼠标点击操纵,由于鼠标移动会产生大量的事件,在举行一些复杂的程序的时间,一直触发鼠标移动事件会降低程序的服从,以是说为了包管服从,默认是不会对鼠标的移动举行追踪,也就是说鼠标移动的时间不会调用鼠标移动处理函数。怎样打开追踪呢?
   void setMouseTracking(bool enable);
          鼠标移动事件的追踪是有地区限制的,而且没有继承关系,如果说在QPushButton中设置的,那么QWidget的窗口是无法追踪的,如果在QWidegt窗口设置的,那么QPushButton是无法追踪的,但是这样的化QWidget也是无法追踪的,他会拦截事件。 

4.鼠标滚轮滚动事件


        鼠标滚轮滚动的事件处理函数是wheelEvent函数,他的参数不是QMouseEvent类型了,而是变成了一个QWheelEvent滚轮类。该类中生存了一个滚轮移动的距离,正数表示向前移动,负数表示向后移动,可以利用该类中的delta()函数来获取滚动的距离。
   intQGraphicsSceneWheelEvent::delta() const;
  
三、键盘事件 


        Qt中的键盘事件是由QKeyEvent类来实现的,Qt中的QShortCut设置快捷键的类就是Qt信号槽机制封装过的一个用于获取键盘按键的一种方式。
        键盘事件和鼠标事件最大的差别在于,键盘事件的触发,首先需要窗口获得焦点之后,才会触发对应的键盘事件。
1.键盘按下单个按键事件


        键盘按下事件的处理函数是KeyPressEvent函数,函数中的参数就是键盘的各种属性内容,参数内部就包含了用户详细输入的内容了。 可以通过QKeyEvent类中的key函数获取到输入的内容。
        当我们打印key的返回值的时间,会发现我们输入的内容都变成了一些数字,在Qt中把键盘的所有按键都用一个枚举变量Key定义成了各种值的数字。利用Qt::xxx便是键盘按键。

  1. void Widget::keyPressEvent(QKeyEvent *event)
  2. {
  3.     // 获取键盘输入按键
  4.     int key = event->key();
  5.     //判断键盘是否输入的是按键A
  6.     if(key == Qt::Key_A)
  7.     {
  8.         qDebug() << "按下了A键";
  9.     }
  10. }
复制代码
        上述的枚举值Key_A等是不会区分巨细写的,键盘属于物理按键,而巨细写属于的是字符的概念,通过检测键盘shift的状态来区分用户输入的巨细写,而并非这些枚举变量值区分巨细写。

2.组合键按下事件

        按下组合键也是属于键盘输入事件,也是由keyPressEvent函数举行事件处理,对于组合键一般都是ctrl、shift、alt等等组合操纵。Qt内置了对于键盘输入是否利用了组合键的判断,并不是直接判断,而是把上述的ctrl、shift、alt等按键定义为了修改键,并为这些修改键定义了在了Qt内部的KeyboardModifiter枚举变量中。同时在QKeyEvent类中提供了modifiers函数用来判断是否利用了修改键,并返回修改键的值。通过判断是否利用修改键加上普通键,就可以判断出来是否利用了组合键了。

   Qt::KeyboardModifiers modifiers() const;
  1. void Widget::keyPressEvent(QKeyEvent *event)
  2. {
  3.     // 获取用户输入内容
  4.     int key = event->key();
  5.     // 判断是否是ctrl + A
  6.     if(key == Qt::Key::Key_A && event->modifiers() == Qt::ControlModifier)
  7.     {
  8.         qDebug() << "按下了组合键 ctrl + A";
  9.     }
  10. }
复制代码

四、定时器事件

        Qt中举行窗口程序的处理过程中,经常要周期性的实行某些操纵,就需要用到定时器了,定时器会在肯定时间隔断后,去实行某一个任务,这种机制在很多场景中都会用到。
利用说明

       在Qt中定时器事件类是QTimerEvent类。定时器事件的触发是由定时器完成的,那么怎样创建定时器呢?QObject类中提供了startTimer函数,会创建一个定时器对象,同时启动定时器并返回一个定时器的唯一标识符对象,后续就用该标识符操纵定时器了。QObject类还提供了关闭定时器的函数killTimer,转达的就是定时器的标识符。
        当定时器的计时到了一个周期之后,也就是到了设置的时间后,会触发定时器事件,timerEvent则是定时器定时器事件的处理函数,参数就是定时器事件。
   int startTimer(int interval, Qt::TimerType timerType = Qt::CoarseTimer);  // 启动定时器
  void killTimer(int id);                                                                                   // 销毁定时器
          还需要注意的一点是,定时器在一个窗口/类中可以创建多个,而这些定时器想要实现的操纵是不一样的,但是都会调用同一个定时器事件处理函数,以是在触发定时器事件后,首先要获取定时器事件的内部存放的定时器标识符,然后用if-else来确定需要实行什么操纵。
        但是现实开辟中一般都是利用封装好的QTimer类举行定时器的操纵,当触发定时器事件的时间,会产生QTimer::timeout信号,之后绑定槽函数,实现定时器事件的处理会更方便一些。就不需要我们管理销毁定时器、以及区分是哪个定时器触发的事件了。
  1.     QTimer *timer = new QTimer(this);   
  2.     connect(timer, &QTimer::timeout, this, &MyWidget::handleTimeout);
  3.     timer->start(1000); // 启动定时器
复制代码
Demo:设置倒计时程序 

        创建一个LCDNumber控件和一个定时器,设置定时时间为1s,事件到了就调用定时器事件处理函数,在函数内部操纵LCDNumber控件的值-1,就实现了倒计时。
   int timerId() const { return id; }
  1. #include "widget.h"
  2. #include "ui_widget.h"
  3. Widget::Widget(QWidget *parent)
  4.     : QWidget(parent)
  5.     , ui(new Ui::Widget)
  6. {
  7.     ui->setupUi(this);
  8.     //设置初始值
  9.     ui->lcdNumber->display(10);
  10.     //开启定时器
  11.     _timerId = this->startTimer(1000);
  12. }
  13. Widget::~Widget()
  14. {
  15.     delete ui;
  16. }
  17. //定时器事件处理函数
  18. void Widget::timerEvent(QTimerEvent *event)
  19. {
  20.     //获取触发定时器事件是哪一个定时器
  21.     if(event->timerId() != this->_timerId)
  22.     {
  23.         return;
  24.     }
  25.     int value = ui->lcdNumber->intValue();
  26.     if(value <= 0)
  27.     {
  28.         this->killTimer(this->_timerId);
  29.         return;
  30.     }
  31.     ui->lcdNumber->display(value - 1);
  32. }
复制代码

五、窗口干系事件

1.窗口移动事件


        窗口移动事件的的处理函数是moveEvent函数,会带有一个QMobeEvent类型的参数,该类内部最主要的接口就是oldPos和pos,分别代表的是旧的位置,和移动后的新位置。并返回QPoint类型的变量,内部就包含了x,y坐标系的值。
   const QPoint& oldPos() coonst;
  const QPoint& pos() const;
  

2.窗口巨细改变事件


         窗口巨细改变事件的处理函数是resizeEvent函数,参数是QResizeEvent,和窗口移动事件雷同,类内部最主要的函数就是oldSize和size表示原来的巨细和改变后的窗口巨细。返回一个QSize类型的参数,内部包含了长度和宽度等数值,和上面的QSize合起来就是窗口的geomeory属性。
   const QSize& oldSize() coonst;
  const QSize& size() const;
  
六、事件分发器与事件过滤器

        事件分发和事件过滤也是属于Qt事件的机制之一,对于事件分发器来说,会重写一个event函数,会直接获取到所有的事件,并对这些事件在举行分发处理,但是这样很有很大的危险,如果说实现过程中某些逻辑不正确的化,大概会导致很多的事件无法处理,或者无法正确的处理,以是说还是推荐对每个事件单独重写事件处理函数,乃至说更推荐利用信号槽机制举行处理,除非Qt中没有对应的信号机制,在利用Qt的事件机制解决题目。
        事件过滤会比事件分发好一些,事件过滤器对于特定的一些事件就行事件处理,不会涉及到所有事件的重写操纵。但也不推荐利用,也不常见。如果说一个程序在特定场景或特定操纵中需要禁用用户的某些操纵,可以考虑利用事件过滤机制。
        事件过滤器一般也是事件分发器利用的时间,搭配利用的一个机制,先过滤一些事件,然后再举行事件的分发处理。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

天津储鑫盛钢材现货供应商

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表