Qt事件系统
事件处理方法
Qt提供的事件处理方法有:
- 方法一:重新实现部件的
paintEvent()
、mousePressEvent()
等的事件处理函数,该方法是最常用的一种方法,但是只能用来处理特定部件的特定事件。 - 方法二:重新实现
notify()
函数,可以在事件过滤器得到事件之前就得到他们。但是它一次只能处理一个事件。 - 方法三:向
QApplication
对象上安装事件过滤器。这样实现的功能与使用notify()
相同,优点是可以同时处理多个事件。 - 方法四:重新实现
event()
函数,该函数可以在事件到达默认的事件处理函数之前获得该事件。 - 方法五:在对象上安装事件过滤器,使用事件过滤器可以在一个界面类中同时处理不同子部件的不同事件。事件过滤器实现的方法需要两个函数完成一个部件对其它部件的事件的监视:安装事件过滤器
installEventFilter()
和实现事件过滤eventFilter()
。
事件过滤器处理事件eventFilter()
方法五事件过滤器操作方法如下:
- 首先在自定义类中重写
event()
函数,在该函数中可以使用ev->type()
获取到想要捕捉的事件,最后返回父类的event()
,该函数的返回值如果是true表示用户要处理这个事件,不向下处理了,代码参考如下:
bool MyLineEdit::event(QEvent *ev)
{
if(ev->type() == QEvent::KeyPress) // 捕捉键盘按下事件
{
qDebug() << tr("MyLineEdit的键盘按下事件");
// 转换未键盘事件
return true;
}
return QLineEdit::event(ev); // 执行父类的event()默认操作
}
事件类型由
QEvent::Type
枚举类型表示。如果不处理该事件就忽略,使用event->ignore()
表示忽略该事件,要在代码的最后调用该函数。
- 然后在主窗口构造函数中给控件安装事件过滤器
lineEdit->installEventFilter(this)
,再重写eventFilter()
函数,在该函数中判断对象是否为要处理的控件,判断事件是否是需要处理的事件,代码如下,如果对特定的事件处理后,不希望它在后面的传递过程中再被处理就返回true,否则就返回false。
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
// 1. 为lineEdit安装事件过滤器
ui->lineEdit->installEventFilter(this);
}
bool MainWindow::eventFilter(QObject *watched, QEvent *event)
{
// 2. 重写eventFilter()事件过滤器
if(watched == ui->lineEdit)
{
// 如果是lineEdit部件,就处理该部件的事件
if(event->type() == QEvent::KeyPress)
{
// 如果是需要的事件可以进行强制转换,如
// QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
// 如果事件是键盘按下事件,就处理
qDebug() << tr("MainWindow通过事件过滤器处理键盘按下事件");
return true;
}
}
// 返回父类的默认事件过滤器函数
return QWidget::eventFilter(watched,event);
}
事件的传递顺序:事件过滤器eventFilter()
→焦点部件的event()
函数→焦点部件的事件处理函数如keyPressEvent()
(如果焦点部件忽略了该事件,那么执行父部件的事件处理函数)。要注意:event()函数和事件处理函数是在焦点部件内重新定义的,事件过滤器eventFilter()
是在焦点部件的父部件中定义的。
事件处理的传递顺序如下:
事件分发event()
从上面的图中可以得到如果没有设置事件处理器,那么事件会按照顺序进入event()
中,只需要重写该函数即可,如上面在自定义类中重写了event()
函数来捕捉键盘按下的事件。该函数返回true表示用户自己处理这个事件,不要向下传递了,否则返回false。
bool MyLineEdit::event(QEvent *ev)
{
if(ev->type() == QEvent::KeyPress) // 捕捉键盘按下事件
{
qDebug() << tr("MyLineEdit的键盘按下事件");
// 转换未键盘事件
return true;
}
return QLineEdit::event(ev); // 执行父类的event()默认操作
}
Qt提供的事件处理类
Qt提供的事件处理类有:
- 鼠标事件
QMouseEvent
QMouseEvent
可以知道鼠标哪个键按下了、鼠标移动到哪个位置等信息。Qt提供的常用鼠标事件有:
- 鼠标进入**
enterEvent()
、
鼠标离开leaveEvent()
、
鼠标按下
mousePressEvent()
、
鼠标移动mouseMoveEvent()
、
鼠标释放mouseReleaseEvent()
、
鼠标双击mouseDoubleClickEvent()
** 等。
鼠标事件类常用的方法有:
- 获取在部件中的位置
x()
,y()
;获取在整个窗口中的位置globalX()
、globalY()
。- 获取鼠标按键
buttons()
,鼠标按键Qt::LeftButton
、Qt::RightButton
;- 判断组合键推荐使用的是
QMouseEvent::buttons() & 具体的鼠标事件
;
- 滚轮事件
QWheelEvent
Qt提供的捕获滚轮的事件是**
wheelEvent()
**。QWheelEvent
类中常用的方法有:
- 获取滚轮移动的距离**
delta()
,滚轮旋转一下默认是15°,delta()
**返回 15 ∗ 8 = 120 15*8=120 15∗8=120。滚轮向远离使用者的方向旋转时,返回正值,滚轮向靠近使用者的方向旋转时,返回负值。可以根据该函数的返回值判断滚轮的移动方向。
- 按键事件
QKeyEvent
QKeyEvent
类描述。当有按键按下或者松开的时候,按键事件将会发送消息给QWidget
。
按键事件包含一个特殊的接收标记,标记接收者是否处理该按键事件。
键盘上的每一个键在Qt作为枚举类型存在,如ESC
键为Qt::Key_Escape
;
对按键按下事件进行处理,要重载按键按下处理函数
keyPressEvent()
;对按键松开事件进行处理,要重载按键松开处理函数
keyReleaseEvent()
;这两个函数声明如下:
void keyPressEvent(QKeyEvent *event); void keyReleaseEvent(QKeyEvent *event);
在按键事件处理函数中,可以使用
event->key()
得到按下或释放的按键的代码。
示例:
void Dialog::keyReleaseEvent(QKeyEvent *event)
{
int keys = event->key(); // 按键代码
switch (keys) {
case Qt::Key_L: // 按下L键
{
// do something
}
break;
case Qt::Key_Escape: // 按下ESC键,退出程序
{
// do something
}
break;
// ...
default:
{
// do something
}
break;
}
}
出现的问题:参考链接https://blog.csdn.net/sinat_21107433/article/details/101158863博主出现方向键无法响应的问题,查看文档描述,必须调用
setFocusPolicy()
后才能接收按键事件,可以通过调用函数setFocusPolicy()
或者在界面设置FocusPolicy
。
QKeyEvent()
常用的方法有:
- 使用
key()
可以获取键盘按下了哪个键或者从哪个键释放了;- 对于
Ctrl
和Shift
等修饰键,需要用modifiers()
来获取,Qt::KeyboardModifier
枚举了所有的修饰键。- 避免按键重复,可以通过函数
isAutoRepeat()
判断是否重复,如果重复不做处理即可。
- 定时器事件
QTimerEvent
和定时器类QTimer
QtimerEvent
类描述一个定时器事件。QObject的子类使用startTimer()
就可以开启一个定时器,该函数参数单位是毫秒,表示设定的时间,函数返回值表示这个定时器。当定时器溢出时可以在timerEvent()
函数中进行需要的操作,通过函数timerId()
可以获取startTimer()
返回的定时器。
Qt提供了更高层次的定时器编程接口QTimer
类,可以使用信号和槽,还可以设置定时一次。使用步骤:
- 首先需要创建定时器对象,
QTimer *timer = new QTimer(this);
- 然后将定时器的超时信号与自定义的时间更新槽函数连接,
connect(timer,&QTimer::timeout,this,&MyLCDNumber::timerUpdate);
- 其次设置溢出时间,
timer->setInterval(1000);
- 最后开始定时,
timer->start(0); // 设置溢出时间为1s并启动定时器
- 需要停止定时器调用
timer->stop()
即可。- 具体操作如下:
MyLCDNumber::MyLCDNumber(QWidget *parent) :
QWidget(parent),
ui(new Ui::MyLCDNumber)
{
ui->setupUi(this);
QTimer *timer = new QTimer(this);
// 关联定时器的超时信号都自定义槽函数中
connect(timer,&QTimer::timeout,this,&MyLCDNumber::timerUpdate);
timer->setInterval(1000);
timer->start(0); // 设置溢出时间为1s并启动定时器
}
void MyLCDNumber::timerUpdate()
{
QTime time = QTime::currentTime();
QString text = time.toString("hh:mm:ss");
if(time.second() %2 ==0 ) // second()获取秒的值
{
text[5] = ' '; // 每隔1s将:显示为空格
}
ui->lcdNumber->display(text);
}
QTimer
提供的singleShot()
方法可以开启只运行一次的定时器,使用参考:QTimer::singleShot(10000,this,&MainWindow::close);
表示让程序10s后自动关闭。
简单介绍下随机数:
Qt提供
qrand()
和qsrand()
实现获取随机数。通常在使用qsrand()
函数产生随机数之前,一般要使用qsrand()
设置不同的初值,如果不设置初值,每次运行获取的随机数都相同。示例代码:
qsrand(QTime(0,0,0).secsTo(QTime::currentTime())); // 使用qsrand()设置不同的初值,secsTo()函数表示两个时间点之间所包含的描述 // 上面一行表示从零点整到当前时间经过的秒数。 rand = qrand()%300; // 获取[0,299]之间的数据
发送事件sendEvent()
和postEvnet()
Qt也提供了发送事件的功能,是QCoreApplication
类的sendEvent()
和postEvent()
函数。这两个函数的主要区别是:
sendEvent()
会立即处理给定的事件,需要在栈上创建QEvent
对象;postEvent()
会将事件放到等待调度队列中,当Qt的下一次主事件循环运行时才会处理它,需要在堆上创建QEvent
对象,事件被发送后事件队列会自动删除它。
使用示例:
// 向spinBox部件添加向上方向键被按下的事件
QKeyEvent myEvent(QEvent::KeyPress,Qt::Key_Up,Qt::NoModifier);
qApp->sendEvent(ui->spinBox,&myEvent);