赞
踩
问题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事件分发函数;
情景:自定义事件对于特定的操作是很有用的,定义一种连续点击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次鼠标算一次自定义事件
发送失败?程序崩溃?
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;
- }

运行结果
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。