QT事件详解
1.事件处理介绍
Qt 程序需要在
main()
函数创建一个QCoreApplication
对象,然后调用它的exec()
函数。这个函数就是开始 Qt 的事件循环。在执行exec()
函数之后,程序将进入事件循环来监听应用程序的事件。当事件发生时,Qt 将创建一个事件对象。Qt 中所有事件类都继承于QEvent
。在事件对象创建完毕后,Qt 将这个事件对象传递给QObject
的event()
函数。event()
函数并不直接处理事件,而是按照事件对象的类型分派给特定的事件处理函数(event handler)。
#include <QApplication>
#include <QWidget>
#include <QLabel>
#include <QMouseEvent>
class EventLabel : public QLabel
{
protected:
void mouseMoveEvent(QMouseEvent *event); //声明明三个事件函数
void mousePressEvent(QMouseEvent *event);
void mouseReleaseEvent(QMouseEvent *event);
};
void EventLabel::mouseMoveEvent(QMouseEvent *event) //鼠标移动事件函数,显示出鼠标的坐标
{
this->setText(QString("<center><h1>Move: (%1, %2)</h1></center>")
.arg(QString::number(event->x()), QString::number(event->y())));
}
void EventLabel::mousePressEvent(QMouseEvent *event) //按下时,获得鼠标坐标
{
this->setText(QString("<center><h1>Press: (%1, %2)</h1></center>")
.arg(QString::number(event->x()), QString::number(event->y())));
}
void EventLabel::mouseReleaseEvent(QMouseEvent *event) //松开鼠标时,获取鼠标的坐标
{
QString msg;
msg.sprintf("<center><h1>Release: (%d, %d)</h1></center>",
event->x(), event->y());
this->setText(msg);
}
//上述需要点击鼠标移动才能出发mouseMoveEvent时间,可以设置为什么要点击鼠标之后才能在mouseMoveEvent()函数中显示鼠标坐标值?这是因为QWidget中有一个mouseTracking属性,该属性用于设置是否追踪鼠标。只有鼠标被追踪时,mouseMoveEvent()才会发出。如果mouseTracking是 false(默认即是),组件在至少一次鼠标点击之后,才能够被追踪,也就是能够发出mouseMoveEvent()事件。如果mouseTracking为 true,则mouseMoveEvent()直接可以被发出
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
EventLabel *label = new EventLabel;
label->setWindowTitle("MouseEvent Demo");
label->resize(300, 200);
label->setMouseTracking(true);
label->show();
return app.exec();
}
1.知识点:
//样例QString.arg函数
this->setText(QString("<center><h1>Press: (%1, %2)</h1></center>")
.arg(QString::number(event->x()), QString::number(event->y())));
//使用的是QString("[%1, %2]").arg(x, y);
//使用 x 替换 %1,y 替换 %2,因此,这个语句生成的QString为 [x, y]。
//样例 static_cast<type-id>(expression)
//解释:static_cast是一个c++运算符,功能是把一个表达式转换为某种类型,但没有运行时类型检查来保证转换的安全性。
int i;
float f = 166.97;
i = static_cast<int>(f); //i值为166
2.下面继承分析
class CustomButton : public QPushButton
{
Q_OBJECT
public:
CustomButton(QWidget *parent) : QPushButton(parent)
{
}
protected:
void mousePressEvent(QMouseEvent *event)
{
qDebug() << "CustomButton";
}
};
class CustomButtonEx : public CustomButton
{
Q_OBJECT
public:
CustomButtonEx(QWidget *parent) : CustomButton(parent)
{
}
protected:
void mousePressEvent(QMouseEvent *event)
{
qDebug() << "CustomButtonEx";
}
};
class CustomWidget : public QWidget
{
Q_OBJECT
public:
CustomWidget(QWidget *parent) : QWidget(parent)
{
}
protected:
void mousePressEvent(QMouseEvent *event)
{
qDebug() << "CustomWidget";
}
};
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0) : QMainWindow(parent)
{
CustomWidget *widget = new CustomWidget(this);
CustomButton *cbex = new CustomButton(widget);
cbex->setText(tr("CustomButton"));
CustomButtonEx *cb = new CustomButtonEx(widget);
cb->setText(tr("CustomButtonEx"));
QVBoxLayout *widgetLayout = new QVBoxLayout(widget);
widgetLayout->addWidget(cbex);
widgetLayout->addWidget(cb);
this->setCentralWidget(widget);
}
protected:
void mousePressEvent(QMouseEvent *event)
{
qDebug() << "MainWindow";
}
};
当CustButton继承QPushButton;而CustButtonEx继承CustButton;同时都在MainWidow中作为控件显示。仅仅作为单机各个控件,都是仅仅打印自己重写的mousePressEvent()事件;如果想要让孩子同时可以执行父亲相同的事件,需要重新添加父亲的事件 CustButton::mousePressEvent(ent); 添加到孩子执行的事件函数中;
其次,还有另一中方案,就是使用 accept() 和 ignore() 函数,其中 ent->accept() 是默认状态,不做处理,仅仅执行自己重写部分。 ignore() 说明我们想让事件继续传播,所以就传递给父类组件,同时启动父类的事件。
事件的传播是在组件层次上面的,而不是依靠类继承机制。
2.事件过滤器
准确地说事件过滤器就是在父类窗口上,查询子类控件的事件的类型进而处理的一种方式
介绍:
这个函数返回一个 bool 类型,如果你想将参数 event 过滤出来,比如,不想让它继续转发,就返回 true,否则返回 false。事件过滤器的调用时间是目标对象(也就是参数里面的
watched
对象)接收到事件对象之前。也就是说,如果你在事件过滤器中停止了某个事件,那么,watched
对象以及以后所有的事件过滤器根本不会知道这么一个事件。class MainWindow : public QMainWindow { public: MainWindow(); protected: bool eventFilter(QObject *obj, QEvent *event); private: QTextEdit *textEdit; } MainWindow :: MainWindow() { textEdit = new QTextEdit; setCentraWidget(textEdit); //installEventFilter 是由需要过滤的实例化对象调用的,以父类为基础 textEdit->installEventFilter(this); } bool MainWindow :: eventFilter(QObject *obj, QEvent *event) { if(obj == textEdit) { if(event->type() == QEvent::KeyPress) { QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event); qDebug("Ate key press"); return true; //也就是过滤掉textEdit的KeyPress事件 } else { return false; } } else { //pass the event on to the parent class return QMainWindow::eventFilter(obj, event); } }
eventFilter()
函数相当于创建了过滤器,然后我们需要安装这个过滤器。安装过滤器需要调用
QObject::installEventFilter()`函数//用于安装过滤器函数 void QObject::installEventFilter(QObject *filterObj); //用于移除过滤器 void QObject::removeEventFilter(QObject *filterObj);
如果我们向一个对象安装多个事件处理器,只能调用多次 installEventFilter() 函数,如果一个对象存在多个事件过滤器,那么最后一个安装的第一个执行,也就是后进先执行的顺序。
注意:
注意,如果你在事件过滤器中 delete 了某个接收组件,务必将函数返回值设为 true。否则,Qt 还是会将事件分发给这个接收组件,从而导致程序崩溃
事件过滤器和被安装过滤器的组件必须在同一线程,否则,过滤器将不起作用。另外,如果在安装过滤器之后,这两个组件到了不同的线程,那么,只有等到二者重新回到同一线程的时候过滤器才会有效
3.自定义事件举例
首先,继承QEvent;
其次,定义事件类型,通过registerEventType()函数进行自动创建一个全局唯一的事件类型;
再其次,使用sendEvent()[同步处理] 和 postEvent()函数[异步处理] 发送事件;
最后,重写QObject::event()方法处理自定义事件
但是,自定义事件用在何处?具体什么用处?还不得而知