转载:

  https://blog.csdn.net/apollon_krj/article/category/6939539

  https://blog.csdn.net/qq_41072190/article/category/7593738

在Qt中我们可以应用信号与槽对一些鼠标点击的操作进行处理,如: 
QPushbutton::clicked 
QPushbutton::realsead 
QPushbutton::pressed 
而信号与槽的处理属于事件的一种,产生一个信号可以认为是一个信号事件,而槽函数就是对于该信号事件进行处理的回调函数。由于信号与槽属于事件,也就是说信号很强大,但是事件更强大。那么我们就有必要好好总结一下Qt的常用的一些事件了。

1、首先明确事件处理过程: 
事件(event)是由系统或者Qt本身在不同时刻发出的。当用户按下鼠标、敲下键盘,或者其它情况时候都会发出一个相应的事件。一些事件在对用户操作做出相应时发出,如键盘事件等;另外一些则是由系统自动发出,如计时事件等。Qt程序需要在main()函数创建一个QApplication对象,然后调用它的exec()函数。这个函数就是开始Qt的事件循环。在执行exec()函数之后,程序将进入事件循环来监听应用程序的事件,当事件发生时,Qt将创建一个事件对象。Qt中所有事件类都继承自QEvent。在事件对象创建完毕之后,Qt将这个事件对象传递给QObject的event()函数。event()函数并不直接处理事件,而是按照事件对象的类型分派给指定的事件处理函数(event handler)进行处理。

2、常用事件(事件处理函数):

 1 keyPressEvent()             //键盘按下事件
2 keyReleaseEvent() //键盘释放事件
3 mouseDoubleClickEvent() //鼠标双击事件
4 mouseMoveEvent() //鼠标移动事件
5 mousePressEvent() //鼠标按下事件
6 mouseReleaseEvent() //鼠标释放事件
7 timerEvent() //定时器事件
8 dragEnterEvent() //拖拽进入当前窗口事件
9 dragLeaveEvent() //拖拽离开当前窗口事件
10 dragMoveEvent() //拖拽移动事件
11 enterEvent() //进入窗口区域事件
12 leaveEvent() //离开窗口区域事件
13 closeEvent() //关闭窗口事件

以上的事件是比较常用的一些事件。这些事件的回调函数都是虚函数,其成员属性为”protected function”,在其基类中声明,再到具体的派生类中进行父类虚函数的覆写,以实现不同类中对于同一事件的不同处理,以上的虚函数在QWidget中基本都已声明,我们在具体使用时只需要继承QWidget,然后在QWidget的派生类中具体实现即可。

3、QTimerEvent定时器事件: 
测试main()函数不变(基类QWidget,MyWidget继承自QWidget):

#include "mywidget.h"
#include <QApplication> int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MyWidget w;
w.show(); return a.exec();
}

比如,我们采用timerEvent()实现两个计数器:其中一个计数器的事件触发的时间间隔为1s(计数到23s停止),另一个为0.5s(一直计数不停止)。分别显示在不同Label上。

/*myWidget.h*/
#ifndef MYWIDGET_H
#define MYWIDGET_H #include <QWidget> namespace Ui {
class MyWidget;
} class MyWidget : public QWidget
{
Q_OBJECT
public:
explicit MyWidget(QWidget *parent = 0);
~MyWidget();
protected:
//定时器事件
void timerEvent(QTimerEvent *);
private:
Ui::MyWidget *ui;
int timerID_fast; //区分不同定时器,类似于文件描述符
int timerID_slow;
}; #endif // MYWIDGET_H
/*myWidget.cpp*/
#include "mywidget.h"
#include "ui_mywidget.h" MyWidget::MyWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::MyWidget)
{
ui->setupUi(this);
timerID_fast = this->startTimer(1000);//以毫秒为单位,每隔1秒触发一次定时器
timerID_slow = this->startTimer(500);
}
void MyWidget::timerEvent(QTimerEvent *ev)
{
//如果触发定时器的是编号为timerID_fast,则处理,否则处理timerID_slow对应的代码
if(ev->timerId() == timerID_fast){
static int sec = 0;
if(sec == 20){ //定时到20s时停止计时
killTimer(timerID_fast);
}
ui->labelCoordinate->setText(QString("<center><h1>Timer out:%1</h1></center>").arg(sec++));
//<center><h1>Timer out:%1</h1></center>为HTML的写法,中间(center)标题大小(h1)显示
}
else if(ev->timerId() == timerID_slow){
static int sec = 0;
ui->label->setText(QString("<center><h1>Timer out:%1</h1></center>").arg(sec++));
}
}
MyWidget::~MyWidget()
{
delete ui;
}

结果如下:

对于定时器的操作就是设定了每个一段时间产生一个中断,然后去执行我们在派生类中重新覆写的虚函数(这里的虚函数是覆写基类中的,所以其返回值、参数、函数名都和基类的函数名是相同的),显然这是一个回调函数。

4、QMouseEvent鼠标事件: 
鼠标事件应该算得上是GUI编程中用的最多的一个事件了,比如扫雷、棋类游戏等都对于鼠标事件的使用是相当频繁的。而鼠标事件我们就拿press、release、move、enter、leave等几个事件来说说:

QWidget.cpp以及QWidget.h必须要修改,保持创建QWidget时即可,添加Class文件为MyLabel。在标签上显示鼠标move的坐标、鼠标位于标签内还是标签外(当鼠标位于标签内时,会显示move的坐标,所以测试鼠标leave与enter时应注销move的setText()),在qDebug中显示鼠标按键是左键/右键/中键,

/*myLabel.h*/
#ifndef MYLABEL_H
#define MYLABEL_H
//需要对标签进行提升,提升原本的类QLabel为MyLabel
#include <QWidget>
#include <QLabel>
class MyLabel : public QLabel
{
Q_OBJECT
public:
explicit MyLabel(QWidget *parent = 0);
protected:
//鼠标点击事件
void mousePressEvent(QMouseEvent *ev);
//鼠标释放事件
void mouseReleaseEvent(QMouseEvent *ev);
//鼠标移动事件
void mouseMoveEvent(QMouseEvent *ev);
//进入窗口(Label)区域
void enterEvent(QEvent *);
//离开窗口(Label)区域
void leaveEvent(QEvent *);
signals: public slots:
}; #endif // MYLABEL_H
/*myLabel.cpp*/
#include "mylabel.h"
#include <QMouseEvent>
#include <qDebug>
MyLabel::MyLabel(QWidget *parent) : QLabel(parent)
{
//设定追踪鼠标,一开始运行就追踪而不是需要先按下键才会追踪
this->setMouseTracking(true);
} //鼠标按下事件处理函数
void MyLabel::mousePressEvent(QMouseEvent *ev)
{
if(ev->button() == Qt::LeftButton){
qDebug() << "Left";
}else if(ev->button() == Qt::RightButton){
qDebug() << "Right";
}else if(ev->button() == Qt::MidButton){
qDebug() << "Middle";
}
/*
* QString str = QString("abc %1 ^_^ %2").arg(123456).arg("ABCDEF");
* str = abc 123456 ^_^ ABCDEF
*/
int i = ev->x();
int j = ev->y();
QString str = QString("<center><h1>Mouse Press:(%1, %2)</h1></center>").arg(i).arg(j);
this->setText(str);
}
//鼠标抬起事件处理函数
void MyLabel::mouseReleaseEvent(QMouseEvent *ev)
{
QString str = QString("<center><h1>Mouse Release:(%1, %2)</h1></center>").arg(ev->x()).arg(ev->y());
this->setText(str);
}
//鼠标移动事件处理函数
void MyLabel::mouseMoveEvent(QMouseEvent *ev)
{
QString str = QString("<center><h1>Mouse Move:(%1, %2)</h1></center>").arg(ev->x()).arg(ev->y());
//this->setText(str);
}
//鼠标位于标签内
void MyLabel::enterEvent(QEvent * ev)
{
QString str = QString("<center><h1>Mouse Enter</h1></center>");
this->setText(str);
}
//鼠标位于标签外
void MyLabel::leaveEvent(QEvent *ev)
{
QString str = QString("<center><h1>Mouse Leave</h1></center>");
this->setText(str);
}

5、QCloseEvent事件与QMessageBox使用: 
在上例中QWidget加上鼠标关闭事件,点击右上角关闭弹出QMessageBox::question窗口选择是否关闭,该功能用到的accept()与ignore()两个事件处理函数,第一个accept()是接收事件,之后事件就不再向下传递了,而ignore()则是忽略事件不做处理事件会传递给父组件(而不是基类)

void MyWidget::closeEvent(QCloseEvent *ev)
{
int ret = QMessageBox::question(this,"question","Close the Windows?");
if(ret == QMessageBox::Yes){
//关闭窗口
//接收事件,事件不再向下传递
ev->accept();
}else{
//不关闭窗口
//忽略事件,事件继续给父组件传递,由于父组件没有做关于close的接收操作
//所以不做关闭操作
ev->ignore();
}
}

6、QKeyEvent键盘事件: 
ev作为传递事件的参数其参数key()可以获取引发事件(中断)的是哪一个按键,与Qt的枚举变量进行比对,来进行相应的操作:

void MyWidget::keyPressEvent(QKeyEvent *ev)
{
if(ev->key() >= Qt::Key_A && ev->key() <= Qt::Key_Z){
qDebug() << (char)ev->key();
}
else{
qDebug() << ev->key();
}
}

该键盘事件处理函数是对A~Z的字母进行识别并打印(不打印ASCII码,而打印对应字符),而对于非A~Z的字符则打印ASCII码:

常用的一些键的枚举常量如下所示(我们不必要去记忆这些东西,只需要在帮助文档查一下即可,但是还是有必要浏览一遍的):

Qt::Key_Escape  0x01000000
Qt::Key_Tab 0x01000001
Qt::Key_Backtab 0x01000002
Qt::Key_Backspace 0x01000003
Qt::Key_Return 0x01000004
Qt::Key_Enter 0x01000005 Typically located on the keypad.
Qt::Key_Insert 0x01000006
Qt::Key_Delete 0x01000007
Qt::Key_Pause 0x01000008 The Pause/Break key (Note: Not related to pausing media)
Qt::Key_Print 0x01000009
Qt::Key_SysReq 0x0100000a
Qt::Key_Clear 0x0100000b
Qt::Key_Home 0x01000010
Qt::Key_End 0x01000011
Qt::Key_Left 0x01000012
Qt::Key_Up 0x01000013
Qt::Key_Right 0x01000014
Qt::Key_Down 0x01000015
Qt::Key_PageUp 0x01000016
Qt::Key_PageDown 0x01000017
Qt::Key_Shift 0x01000020
Qt::Key_Control 0x01000021 On OS X, this corresponds to the Command keys.
Qt::Key_Meta 0x01000022 On OS X, this corresponds to the Control keys. On Windows keyboards, this key is mapped to the Windows key.
Qt::Key_Alt 0x01000023
Qt::Key_AltGr 0x01001103 On Windows, when the KeyDown event for this key is sent, the Ctrl+Alt modifiers are also set.
Qt::Key_CapsLock 0x01000024
Qt::Key_NumLock 0x01000025
Qt::Key_ScrollLock 0x01000026
Qt::Key_F1 0x01000030
Qt::Key_F2 0x01000031
Qt::Key_F3 0x01000032
Qt::Key_F4 0x01000033
Qt::Key_F5 0x01000034
Qt::Key_F6 0x01000035
Qt::Key_F7 0x01000036
Qt::Key_F8 0x01000037
Qt::Key_F9 0x01000038
Qt::Key_F10 0x01000039
Qt::Key_F11 0x0100003a
Qt::Key_F12 0x0100003b Qt::Key_0 0x30
Qt::Key_1 0x31
Qt::Key_2 0x32
Qt::Key_3 0x33
Qt::Key_4 0x34
Qt::Key_5 0x35
Qt::Key_6 0x36
Qt::Key_7 0x37
Qt::Key_8 0x38
Qt::Key_9 0x39 Qt::Key_A 0x41
Qt::Key_B 0x42
Qt::Key_C 0x43
Qt::Key_D 0x44
Qt::Key_E 0x45
Qt::Key_F 0x46
Qt::Key_G 0x47
Qt::Key_H 0x48
Qt::Key_I 0x49
Qt::Key_J 0x4a
Qt::Key_K 0x4b
Qt::Key_L 0x4c
Qt::Key_M 0x4d
Qt::Key_N 0x4e
Qt::Key_O 0x4f
Qt::Key_P 0x50
Qt::Key_Q 0x51
Qt::Key_R 0x52
Qt::Key_S 0x53
Qt::Key_T 0x54
Qt::Key_U 0x55
Qt::Key_V 0x56
Qt::Key_W 0x57
Qt::Key_X 0x58
Qt::Key_Y 0x59
Qt::Key_Z 0x5a

7、event()事件处理器与eventFilter()事件过滤器: 这两个函数也是虚函数,均声明在基类QObject中。

1 virtual bool event(QEvent * e)

2 virtual bool eventFilter(QObject * watched, QEvent * event)

有时候,我们需要对某个事件进行屏蔽,可以通过在其事件处理的中断函数中进行操作,也可以在event()事件处理函数中进行操作,还可以在eventFilter()事件过滤器中进行操作,以上三种操作:eventFilter()事件过滤器中操作是比较方便的,可以说是指哪打哪,可以随意过滤掉某一个类中的某一个事件。而event()也可以做到,但是event()函数主要功能并不是直接处理事件,而是按照事件对象的类型分派给指定的事件处理函数(event handler)进行处理。 
这种关系可以表示为:

event()是一个根据不同事件做分类处理的功能,具体如下(就类似于switch…case语句或者if..else..语句),这是对原有event()虚函数的覆写(屏蔽定时器,屏蔽除了Y按键的其它按键):

//事件处理器event()只在本控件有效,我们这里通过event()进行事件过滤
bool MyWidget::event(QEvent *ev)
{
//switch的写法
//对于定时器进行功能进行覆写,对键盘的Y键保留中断,其他键忽略。其它时间仍按默认处理
//默认的是在event()的分发层(下一级)进行处理,而定时器的处理直接放在了event()函数中处理
//到了下一层定时器的处理发现return的是true就不再处理,键盘按键也是同理
switch(ev->type()){
case QEvent::Timer: //如果是定时器事件,返回true停止时间传播,忽略定时器中断
//timerEvent(static_cast<QTimerEvent*>(ev)); //不处理直接返回就是干掉了定时器
//取消注释,则是将定时器事件提到event()函数中直接处理
return true;
break;
case QEvent::KeyPress:
if(static_cast<QKeyEvent *>(ev)->key() == Qt::Key_Y){
return QWidget::event(ev);
}
return true;
break;
default:
//默认的则是保留原有event()的处理方式
return QWidget::event(ev);
break;
}
}

当然,用switch能写出来用if…else…也可以,应根据需求进行选择(当分支较多时switch是一个空间换取时间的做法(switch是随机访问会为每个case的指令块生成一个起始地址标记,自然存在一个跳跃表的空间),而if…else…是一个时间效率不及switch的做法,但节省空间。而一般要用event屏蔽事件分支都比较少,二者就无太大区别)。关于if…else…与switch()case的效率比较参考blog:switch与ifelse的效率问题

//if...else...的写法
bool MyWidget::event(QEvent *ev)
{
if(ev->type() == QEvent::Timer){ //定时器特殊处理:忽略
return true;
}
else if(ev->type() == QEvent::KeyPress){ //键盘处理:保留Y键,其它键忽略
if(static_cast<QKeyEvent *>(ev)->key() == Qt::Key_Y){
return QWidget::event(ev);
}
return true;
}
else{
return QWidget::event(ev); //其它事件按原有方式处理
//注意这里一定要把不特殊处理的按原有方式处理
}
}

除了在event()向每个具体的类对象分发事件以前,我们可以用event()进行事件的提前处理,而到达底层具体事件处理时,先查看event()对事件的返回值的状态(true/false),再确定处理方式。而在event()之上,我们也可以通过eventFilter()来完成事件的过滤(提前处理),因为这(eventFilter())就是事件过滤器。说到底无论是事件分发还是事件过滤,都是对事件的提前处理,并向底层返回一个处理状态。只是如果多个类中要对同一事件进行相同的提前处理,需要再多个类中实现多次event()的覆写,eventFilter()则需要一次即可。下例中是对label标签页的事件提前处理/事件过滤:

//ui->label->installEventFilter(this);//在构造函数中需要安装过滤器,这是不同于event的一个地方
//对于不同的类要分别安装过滤器
bool MyWidget::eventFilter(QObject *obj, QEvent *ev)
{
//对标签label进行事件过滤
if(obj == ui->label){
QMouseEvent * env = static_cast<QMouseEvent *>(ev);
if(ev->type() == QEvent::MouseMove){
ui->label->setText(QString("<center><h1>Mouse Move:(%1, %2)"
"</h1></center>").arg(env->x()).arg(env->y()));
return true; //结束传播
}
else if(ev->type() == QEvent::MouseButtonPress){
ui->label->setText(QString("<center><h1>Mouse pressed</h1></center>"));
return true; //结束传播
}
else if(ev->type() == QEvent::MouseButtonRelease){
ui->label->setText(QString("<center><h1>Mouse released</h1></center>"));
return true; //结束传播
}
}
//其余的事件按照默认(未覆写)的处理方式处理
return QWidget::eventFilter(obj,ev);
}

注意:event()、eventFilter()虽然可以用来屏蔽某些事件,但是我们一般不会去修改这两个函数,而是直接在具体的事件处理函数中进行处理操作。

Qt事件与常用事件处理、过滤的更多相关文章

  1. [Qt] 事件机制(一)

    事件主要分为两种: 在与用户交互时发生.比如按下鼠标(mousePressEvent),敲击键盘(keyPressEvent)等 系统自动发生,比如计时器事件(timerEvent)等 每种事件对应一 ...

  2. Qt事件机制---信号通过事件实现,事件可以过滤,事件更底层,事件是基础,信号是扩展。

    转:http://www.cnblogs.com/findumars/p/8001484.html Qt事件机制(是动作发生后,一种通知对象的消息,是被动与主动的总和.先处理自己队列中的消息,然后再处 ...

  3. Qt事件机制浅析(定义,产生,异步事件循环,转发,与信号的区别。感觉QT事件与Delphi的事件一致,而信号则与Windows消息一致)

    Qt事件机制 Qt程序是事件驱动的, 程序的每个动作都是由幕后某个事件所触发.. Qt事件的发生和处理成为程序运行的主线,存在于程序整个生命周期. Qt事件的类型很多, 常见的qt的事件如下: 键盘事 ...

  4. qt事件传递过程和处理

    处理监控系统的时候遇到问题,在MainWidget中创建多个子Widget的时候,原意是想鼠标点击先让MainWidget截获处理后再分派给子Widget去处理,但调试后发现如果子Widget重新实现 ...

  5. 【转】QT事件传递与事件过滤器

        [概览] 1.重载特定事件函数.    比如: mousePressEvent(),keyPressEvent(),  paintEvent() .     2.重新实现QObject::ev ...

  6. Qt事件机制(是动作发生后,一种通知对象的消息,是被动与主动的总和。先处理自己队列中的消息,然后再处理系统消息队列中的消息)

    Qt事件机制 Qt程序是事件驱动的, 程序的每个动作都是由幕后某个事件所触发.. Qt事件的发生和处理成为程序运行的主线,存在于程序整个生命周期. Qt事件的类型很多, 常见的qt的事件如下: 键盘事 ...

  7. Qt事件机制浅析

    Qt事件机制 Qt程序是事件驱动的, 程序的每个动作都是由幕后某个事件所触发.. Qt事件的发生和处理成为程序运行的主线,存在于程序整个生命周期. Qt事件的类型很多, 常见的qt的事件如下: 键盘事 ...

  8. QT事件

    qtevents多线程工作object存储 Another Look at Events(再谈Events) 最近在学习Qt事件处理的时候发现一篇很不错的文章,是2004年季刊的一篇文章,网上有这篇文 ...

  9. Qt 事件机制

    [1]事件 事件是可以被控件识别的操作.如按下确定按钮.选择某个单选按钮或复选框. 每种控件有自己可识别的事件,如窗体的加载.单击.双击等事件,编辑框(文本框)的文本改变事件等等. 事件就是用户对窗口 ...

随机推荐

  1. 一文读懂MySql主从复制机制

    作为一个关系型数据库,MySQL内建地提供数据复制机制,这使得在使用时,可以基于其复制机制实现高可用架构等高级特性,从而使得MySQL无需借助额外的插件或其他工具就具备适用于生产环境.这是MySQL得 ...

  2. CVPR2021| 继SE,CBAM后的一种新的注意力机制Coordinate Attention

    前言: 最近几年,注意力机制用来提升模型性能有比较好的表现,大家都用得很舒服.本文将介绍一种新提出的坐标注意力机制,这种机制解决了SE,CBAM上存在的一些问题,产生了更好的效果,而使用与SE,CBA ...

  3. Mybatis-plus 上

    Mybatis-plus 上 简介 1.什么是Mybatis-plus MyBatis-Plus(简称 MP)是一个 MyBatis的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发 ...

  4. 如何使用Excel发送邮件?

    假设你有一个Excel,其中列出了所有收件人的信息,如下所示: 如果需要向列表中的每个用户发送一封邮件,最好使用当前记录生成一个附件,并且格式如下: 姓名, 发送消息 你应该怎么办?一个一个拷贝发送? ...

  5. Leedcode算法专题训练(树)

    递归 一棵树要么是空树,要么有两个指针,每个指针指向一棵树.树是一种递归结构,很多树的问题可以使用递归来处理. 1. 树的高度 104. Maximum Depth of Binary Tree (E ...

  6. 【2w字干货】ArrayList与LinkedList的区别以及JDK11中的底层实现

    1 概述 本文主要讲述了ArrayList与LinkedList的相同以及不同之处,以及两者的底层实现(环境OpenJDK 11.0.10). 2 两者区别 在详细介绍两者的底层实现之前,先来简单看一 ...

  7. JVM学习笔记(二):JVM基本结构

    1 来源 来源:<Java虚拟机 JVM故障诊断与性能优化>--葛一鸣 章节:第二章 本文是第二章的一些笔记整理. 2 JVM基本参数-Xmx java命令的一般形式如下: java [- ...

  8. 华中科大MOOC 操作系统原理讨论题

    1没有安装操作系统的计算机启动过程和结果? 启动会比较快,但功能很局限,无法使用常见的软件应用,对于普通用户来说,功能很局限,对于专业工程师来说,想使用没有操作系统的计算机也有难度.启动后进入 BIO ...

  9. 1079 Total Sales of Supply Chain

    A supply chain is a network of retailers(零售商), distributors(经销商), and suppliers(供应商)-- everyone invo ...

  10. UVA10780幂和阶乘

    题意:       输入两个整数n,m(1<m<5000,0<n<10000)求最小的k使得m^k是n!的因子. 思路:      比较容易想,一开始手残wa了好几次,我们直接 ...