问题1:Qt中常见的事件有哪些?
答:鼠标事件(QMouseEvent)、键盘事件(QKeyEvent)、绘制事件(QPaintEvent)、窗口尺寸改变(QResizeEvent)、滚动事件(QScrollEvent)、控件显示(QShowEvent)、控件隐藏(QHideEvent)、定时器事件(QTimerEvent)等等。。
问题2:Qt是事件驱动的,这句话该怎么理解呢?
Qt将系统产生的信号(软件中断)转换成Qt事件,并且将事件封装成类,所有的事件类都是由QEvent派生的,事件的产生和处理就是Qt程序的主轴,且伴随着整个程序的运行周期。因此我们说,Qt是事件驱动的。
问题3:Qt事件是由谁产生的?Qt是如何将信号转换成事件的?
答:Qt的官方手册说,事件有两个来源:程序外部和程序内部,多数情况下来自操作系统并且通过spontaneous()函数返回true来获知事件来自于程序外部,当spontaneous()返回false时说明事件来自于程序内部,就像例程1创建一个事件并把它分发出去。
问题4:Qt事件是由谁接收的?
答:QObject!它是所有Qt类的基类!是Qt对象模型的核心!QObject类的三大核心功能其中之一就是:事件处理。QObject通过event()函数调用获取事件。所有的需要处理事件的类都必须继承自Qobject,可以通过重定义event()函数实现自定义事件处理或者将事件交给父类。
问题5:事件处理的流程是什么样的?
答:事件有别于信号的重要一点:事件是一个类对象具有特定的类型,事件多数情况下是被分发到一个队列中(事件队列),当队列中有事件时就不停的将队列中的事件发送给QObject对象,当队列为空时就阻塞地等待事件,这个过程就是事件循环!
QCoreApplication::exec()开启了这种循环,一直到QCoreApplication::exit()被调用才终止,所以说事件循环是伴随着Qt程序的整个运行周期!
另外一种同步处理情形是通过sendEvent()将事件发送出去,直接进入事件的传送和处理流程。
事件处理流程如图所示:
例程1:sendEvent同步事件分发
-
/*!
-
* \brief Widget::Widget 使用sendEvent同步分发事件
-
* 使用QPushButton模拟键盘的回删和向前删除按键
-
* \param parent
-
*/
-
Widget::Widget(QWidget *parent) :
-
QWidget(parent),
-
ui(
new Ui::Widget)
-
{
-
ui->setupUi(
this);
-
}
-
-
Widget::~Widget()
-
{
-
delete ui;
-
}
-
-
void Widget::on_button_back_clicked()
-
{
-
int key = Qt::Key_Backspace;
//
-
-
QKeyEvent EventPress(QEvent::KeyPress, key, Qt::NoModifier);
-
QApplication::sendEvent(ui->textEdit, &EventPress);
-
-
QKeyEvent EventRelease(QEvent::KeyRelease, key, Qt::NoModifier);
-
QApplication::sendEvent(ui->textEdit, &EventRelease);
-
}
-
-
void Widget::on_button_delete_clicked()
-
{
-
int key = Qt::Key_Delete;
//
-
-
QKeyEvent EventPress(QEvent::KeyPress, key, Qt::NoModifier);
-
QApplication::sendEvent(ui->textEdit, &EventPress);
-
-
QKeyEvent EventRelease(QEvent::KeyRelease, key, Qt::NoModifier);
-
QApplication::sendEvent(ui->textEdit, &EventRelease);
-
}
postEvent和sendEvent的关系就像Qt::QueuedConnection和Qt::DirectConnection的关系,只不过前两者是分发事件后两者是发送消息罢了,机制上postEvent和QueuedConnected是异步通信,而另外两种是同步通信。
例程2:postEvent异步事件分发
-
int count =
0;
-
-
/*!
-
* \brief Widget::Widget 使用postEvent异步分发事件
-
* 连续分发10个事件,在事件处理函数中逐个处理
-
* \param parent
-
*/
-
Widget::Widget(QWidget *parent) :
-
QWidget(parent),
-
ui(
new Ui::Widget)
-
{
-
ui->setupUi(
this);
-
-
int i =
1;
-
while(i <=
10)
-
{
-
//postEvent传递的事件必须是通过new创建的
-
qDebug() <<
"分发第" << i <<
"个事件";
-
QApplication::postEvent(
this,
new QEvent(NewType));
-
i++;
-
}
-
}
-
-
void Widget::customEvent(QEvent *event)
-
{
-
//使用延时模拟处理过程
-
if(event->type() == NewType)
-
{
-
qDebug() <<
"现在时间:" <<
-
QTime::currentTime().toString(
"hh::mm:ss.zzz");
-
qDebug() <<
"第" << ++count <<
"次收到了事件!处理事件需要一点时间!";
-
Delay(
1000*
2);
-
}
-
-
QWidget::customEvent(event);
-
}
-
-
Widget::~Widget()
-
{
-
delete ui;
-
}
-
-
void Widget::Delay(
unsigned
int msec)
-
{
-
QTime start = QTime::currentTime();
-
-
QTime end;
-
do{
-
end = QTime::currentTime();
-
}
while(start.msecsTo(end) <= msec);
-
}
问题6:事件过滤器机制?
事件的传送和处理流程的第一站是事件过滤器eventFilter(),某个对象A可以通过给另一个对象B安装事件处理器,实现对对象B事件的监听或者拦截功能。我们可以给A取名监听器,B取名接收器。一个对象可以监听多个对象,一个对象也可以被多个事件监听。事件过滤器返回true则表示事件已经处理完毕,否则传递给下一个监听器或者接收器本身。
例程3:事件过滤器
-
/*!
-
* \brief Widget::Widget 事件过滤器
-
* 不借助Tab键的情况下使用Space键实现控件跳转
-
* \param parent
-
*/
-
Widget::Widget(QWidget *parent) :
-
QWidget(parent),
-
ui(
new Ui::Widget)
-
{
-
ui->setupUi(
this);
-
-
ui->lineEdit_user->setText(QString(
"lee"));
-
focusNextChild();
-
ui->lineEdit_password->setText(QString(
"*******"));
-
-
//监听控件
-
ui->lineEdit_user->installEventFilter(
this);
-
ui->lineEdit_password->installEventFilter(
this);
-
ui->button_accept->installEventFilter(
this);
-
ui->button_cancel->installEventFilter(
this);
-
}
-
-
bool Widget::eventFilter(QObject *watched, QEvent *event)
-
{
-
//定义事件处理动作
-
if (watched == ui->lineEdit_user || watched == ui->lineEdit_password
-
|| watched == ui->button_accept || watched == ui->button_cancel)
-
{
-
if (event->type() == QEvent::KeyPress)
-
{
-
QKeyEvent *e =
static_cast<QKeyEvent *>(event);
-
if(e->key() == Qt::Key_Space)
-
{
-
focusNextChild();
-
return
true;
-
}
-
}
-
}
-
return QWidget::eventFilter(watched, event);
-
}
-
-
Widget::~Widget()
-
{
-
delete ui;
-
}
-
-
void Widget::on_button_cancel_clicked()
-
{
-
qApp->quit();
-
}
值得注意的一点是QCoreApplication虽然负责事件分发,但本身也是继承自QObject的,所以在分发事件之前,也要检查自身是否被别的对象安装了事件过滤器,事件过滤器可能会过滤掉一些事件不发布。
例程4:QCoreApplication安装事件过滤器
widget.cpp
-
/*!
-
* \brief Filter::eventFilter 用于监听qApp的监听器
-
* \return
-
*/
-
bool Filter::eventFilter(QObject *obj, QEvent *event)
-
{
-
//阻止所有的鼠标点击事件
-
if(event->type() == QEvent::MouseButtonPress)
-
{
-
qDebug() <<
"sorry everybody, I gonna filter all the mouse event!";
-
return
true;
-
}
-
return QObject::eventFilter(obj,event);
-
}
-
-
/*!
-
* \brief Widget::Widget
-
* \param parent
-
*/
-
Widget::Widget(QWidget *parent) :
-
QWidget(parent),
-
ui(
new Ui::Widget)
-
{
-
ui->setupUi(
this);
-
}
-
-
Widget::~Widget()
-
{
-
delete ui;
-
}
-
-
void Widget::mousePressEvent(QMouseEvent *event)
-
{
-
qDebug() <<
"mouse press!";
-
-
QWidget::mousePressEvent(event);
-
}
main.c
-
#include "widget.h"
-
#include <QApplication>
-
-
int main(int argc, char *argv[])
-
{
-
QApplication a(argc, argv);
-
-
Filter filter;
-
a.installEventFilter(&filter);
-
-
Widget w;
-
w.show();
-
-
return a.exec();
-
}
也可以通过重新实现QCoreApplication的notify(),自定义对事件的处理动作。
例程5:QCoreApplication子类化并重写notify
newapplication.h
-
#ifndef NEWAPPLICATION_H
-
#define NEWAPPLICATION_H
-
-
#include <QApplication>
-
-
class NewApplication :
public QApplication
-
{
-
public:
-
NewApplication(
int argc,
char **argv) : QApplication(argc,argv) {}
-
-
virtual bool notify(QObject *, QEvent *);
-
-
};
-
-
#endif // NEWAPPLICATION_H
newapplication.cpp
-
#include "newapplication.h"
-
#include <QMouseEvent>
-
#include <QDebug>
-
-
bool NewApplication::notify(QObject *receiver, QEvent *event)
-
{
-
if(event->type() == QMouseEvent::MouseButtonPress)
-
{
-
qDebug() <<
"sorry everybody I gonna filter all the mouse press event";
-
return
true;
-
}
-
-
return QApplication::notify(receiver,event);
-
}
widget.h
-
#ifndef WIDGET_H
-
#define WIDGET_H
-
-
#include <QWidget>
-
#include <QMouseEvent>
-
-
namespace Ui {
-
class Widget;
-
}
-
-
class Widget :
public QWidget
-
{
-
Q_OBJECT
-
-
public:
-
explicit Widget(QWidget *parent = 0);
-
~Widget();
-
-
protected:
-
void mousePressEvent(QMouseEvent *event);
-
-
private slots:
-
void on_pushButton_clicked();
-
-
private:
-
Ui::Widget *ui;
-
};
-
-
#endif // WIDGET_H
widget.cpp
-
#include "widget.h"
-
#include "ui_widget.h"
-
#include <QDebug>
-
-
Widget::Widget(QWidget *parent) :
-
QWidget(parent),
-
ui(
new Ui::Widget)
-
{
-
ui->setupUi(
this);
-
}
-
-
Widget::~Widget()
-
{
-
delete ui;
-
}
-
-
void Widget::mousePressEvent(QMouseEvent *event)
-
{
-
qDebug() <<
"I am mainwindow Widget, I got a mouse event!";
-
-
QWidget::mousePressEvent(event);
-
}
-
-
void Widget::on_pushButton_clicked()
-
{
-
qDebug() <<
"I am push button , I got a mouse event!";
-
}
main.cpp
-
#include "widget.h"
-
#include <QApplication>
-
#include "newapplication.h"
-
-
int main(int argc, char *argv[])
-
{
-
// QApplication a(argc, argv);
-
NewApplication a(argc, argv);
-
-
Widget w;
-
w.show();
-
-
return a.exec();
-
}
运行结果:点击界面的任意位置,事件都被qApp过滤。
小结:事件处理的方式
1.重新实现对象的特定事件处理函数,例如mousePressEvent、keyPressEvent 、showEvent等,处理完毕后将事件交给父类;
2.重新实现event函数,处理完毕后将事件交给父类;
3.在对象上安装事件过滤器,让其他对象控制此对象的事件行为;
4.给主程序QCoreApplication安装事件过滤器,在调用notify进行事件分发之前,会根据过滤器判断对事件的处理(例如:丢弃);
5.子类化QCoreApplication,重新实现notify事件分发函数;
问题7.怎么使用自定义事件?
情景:自定义事件对于特定的操作是很有用的,定义一种连续点击10次鼠标的事件NewMouseEvent,连续点击10次屏幕唤醒屏幕校准程序。
自定义事件
newmouseevent.h
-
#ifndef MYEVENT_H
-
#define MYEVENT_H
-
-
#include <QEvent>
-
#include <QString>
-
-
class NewMouseEvent :
public QEvent
-
{
-
public:
-
explicit NewMouseEvent() : QEvent(MouseTenClick) {}
-
const
static Type MouseTenClick =
static_cast<Type>(QEvent::User+
0x10);
-
};
-
-
#endif // MYEVENT_H
widget.h
-
#ifndef MYEVENT_H
-
#define MYEVENT_H
-
-
#include <QEvent>
-
#include <QString>
-
-
class NewMouseEvent :
public QEvent
-
{
-
public:
-
explicit NewMouseEvent() : QEvent(MouseTenClick) {}
-
const
static Type MouseTenClick =
static_cast<Type>(QEvent::User+
0x10);
-
};
-
-
#endif // MYEVENT_H
widget.cpp
-
#include "widget.h"
-
#include "ui_widget.h"
-
#include "newmouseevent.h"
-
#include <QMouseEvent>
-
#include <QTimer>
-
-
/*!
-
* \brief Widget::Widget
-
* 创建并分发一种新的事件:鼠标连续点击10次
-
* \param parent
-
*/
-
Widget::Widget(QWidget *parent) :
-
QWidget(parent),
-
ui(
new Ui::Widget)
-
{
-
ui->setupUi(
this);
-
-
ui->label->installEventFilter(
this);
-
-
ui->label->setText(tr(
"请连续点击屏幕以唤醒屏幕校准功能!"));
-
ui->label->adjustSize();
-
-
m_timer =
new QTimer;
-
m_timer->setInterval(
1000);
-
m_timer->start();
-
connect(m_timer, SIGNAL(timeout()), SLOT(clearCount()));
-
-
}
-
-
Widget::~Widget()
-
{
-
delete ui;
-
}
-
-
void Widget::mousePressEvent(QMouseEvent *event)
-
{
-
QWidget::mousePressEvent(event);
-
}
-
-
void Widget::mouseReleaseEvent(QMouseEvent *event)
-
{
-
if(event->button() != Qt::LeftButton)
-
return;
-
-
if(m_timer->isActive())
-
m_timer->stop();
//如果计时器在运行,则停止然后重新开始
-
m_timer->start();
-
-
count++;
-
-
if(
10 == count)
-
{
-
count =
0;
-
-
NewMouseEvent event;
-
qApp->sendEvent(ui->label, &event);
-
}
-
-
QWidget::mouseReleaseEvent(event);
-
}
-
-
bool Widget::eventFilter(QObject *obj, QEvent *event)
-
{
-
if(obj == ui->label && event->type()== NewMouseEvent::MouseTenClick)
-
{
-
ui->label->setText(tr(
"你连续点击了10次屏幕,校准程序正在启动!"));
-
ui->label->adjustSize();
-
return
true;
-
}
-
-
return QWidget::eventFilter(obj,event);
-
}
-
-
void Widget::clearCount()
-
{
-
count =
0;
-
}
运行结果
连续点击10次鼠标算一次自定义事件
问题8:接受者对象中途被删除会发生什么?被监听者被删除会怎么样?
发送失败?程序崩溃?
widget.h
-
#ifndef WIDGET_H
-
#define WIDGET_H
-
-
#include <QWidget>
-
#include <QLabel>
-
-
namespace Ui {
-
class Widget;
-
}
-
-
class Widget :
public QWidget
-
{
-
Q_OBJECT
-
-
public:
-
explicit Widget(QWidget *parent = 0);
-
~Widget();
-
-
protected:
-
-
private slots:
-
void slotSendEvent();
-
void deleteLabel();
-
-
private:
-
Ui::Widget *ui;
-
-
QLabel *m_label;
-
-
};
-
-
#endif // WIDGET_H
widget.cpp
-
#include "widget.h"
-
#include "ui_widget.h"
-
#include <QTimer>
-
#include <QResizeEvent>
-
#include <QDebug>
-
-
/*!
-
* \brief Widget::Widget
-
* 在事件循环分发事件给接收者之前,接收者被删除
-
* \param parent
-
*/
-
Widget::Widget(QWidget *parent) :
-
QWidget(parent),
-
ui(
new Ui::Widget)
-
{
-
ui->setupUi(
this);
-
-
//创建小窗口
-
m_label =
new QLabel(
this);
-
m_label->setStyleSheet(QString(
"border:1px solid red;"));
-
m_label->setGeometry(QRect(
0,
0,
200,
100));
-
-
//在qApp发送事件之前销毁小窗口
-
QTimer::singleShot(
1000,
this, SLOT(deleteLabel()));
-
//qApp发送事件给小窗口
-
QTimer::singleShot(
2000,
this, SLOT(slotSendEvent()));
-
-
}
-
-
Widget::~Widget()
-
{
-
delete ui;
-
}
-
-
void Widget::slotSendEvent()
-
{
-
QResizeEvent re(QSize(300,200), QSize(200,100));
-
-
qDebug() <<
"qApp发送了一个事件给小窗口!";
-
qApp->sendEvent(m_label, &re);
-
}
-
-
void Widget::deleteLabel()
-
{
-
qDebug() <<
"小窗口被销毁了!";
-
delete m_label;
-
m_label =
NULL;
-
}
运行结果