目录
事件介绍
事件过滤器
事件处理器
介绍
事件处理函数
鼠标单击事件
鼠标释放事件
其他
重写事件处理函数
自定义按钮
当事件产生被发送到对应的窗口之后,窗口并不会直接处理这个事件,而是对这些事件进行细分,然后根据事件的类型再次进行分发,对应的事件处理器函数得到这个分发的事件之后就开始处理这个事件。
关于窗口事件的分发,对应一个事件分发器,叫做event
[override virtual protected] bool QWidget::event(QEvent *event);
这个类中常用的一些API函数:
比如说 :
void QEvent::accept();该函数的作用是让窗口接受传递过来的事件,事件不会向上层窗口(父窗口)传递
void QEvent::ignore():该函数的作用是让窗口忽略传递过来的事件,事件被传递给父窗口(向上传递)
QEvent::Type QEvent::type() const;得到传递的窗口的事件的类型,返回值是一个枚举类型
不需要人为干预的情况下,事件分发器会自主的完成相关事件的分发。
以下是这个函数的部分源码展示:
bool QWidget::event(QEvent *ev)
{
switch(ev->type())
{
// 鼠标移动
case QEvent::MouseMove:
mouseMoveEvent((QMouseEvent*)event);
break;
// 鼠标按下
case QEvent::MouseButtonPress:
mousePressEvent((QMouseEvent*)event);
break;
// 鼠标释放
case QEvent::MouseButtonRelease:
mouseReleaseEvent((QMouseEvent*)event);
break;
// 鼠标双击
case QEvent::MouseButtonDblClick:
mouseDoubleClickEvent((QMouseEvent*)event);
break;
// 键盘按键被按下事件
case QEvent::KeyPress:
break;
...
...
...
default:
break;
}
}
我们不想让某些触发的事件进入到当前窗口中,可以在事件分发器中进行拦截。
事件分发器函数返回值:
过滤某个事件,只需要在判断出这个事件之后直接返回 true,例如:在一个自定义的Label控件中拦截鼠标按下事件过滤鼠标按下
//事件分发器
bool MyLabel::event(QEvent *e)
{
if (e->type() == QEvent::MouseButtonPress)
{
qDebug() << "event 拦截鼠标按下事件";
return true;
}
return QLabel::event(e);
}
除了使用事件分发器来过滤Qt窗口中产生的事件,还可以通过事件过滤器过滤相关的事件。当Qt的事件通过应用程序对象发送给相关窗口之后,窗口接收到数据之前这个期间可对事件进行过滤,过滤掉的事件就不能被继续处理了。QObject有一个eventFilter()函数,用于建立事件过滤器。
函数原型:
[virtual] bool QObject::eventFilter(QObject *watched, QEvent *event);
参数解释:
基本过程:
举个例子:在一个窗口中有一个多行文本输入框QTextEdit,需要让我们屏蔽掉键盘上的回车键
方法:
用第三种方法,举例子:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include
#include
#include
#include
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
ui->textEdit->installEventFilter(this);//给类对象安装事件过滤器
}
MainWindow::~MainWindow()
{
delete ui;
}
bool MainWindow::eventFilter(QObject *watched, QEvent *event)//重写从QObject类继承的虚函数eventFilter()
{
// 判断对象和事件
if(watched == ui->textEdit && event->type() == QEvent::KeyPress)
{
QKeyEvent* keyEv = (QKeyEvent*)event;
if(keyEv->key() == Qt::Key_Enter || // 小键盘确认
keyEv->key() == Qt::Key_Return) // 大键盘回车
{
QMessageBox::information(this,"information","回车键被按下了");
return true;
}
}
return false;
}
多层嵌套窗口中如果想要过滤掉QTextEdit的某些事件,可以交给其中一个窗口处理,也可以给QTextEdit同时安装多个过滤器:
ui->textEdit->installEventFilter(窗口A对象);
ui->textEdit->installEventFilter(窗口B对象);
ui->textEdit->installEventFilter(窗口C对象);
如果一个对象存在多个事件过滤器,那么,最后一个安装的会第一个执行,也就是说窗口C先进行事件过滤,然后窗口B,最后窗口A。
事件过滤器和被安装过滤器的组件必须在同一线程,否则,过滤器将不起作用。另外,如果在安装过滤器之后,这两个组件到了不同的线程,那么,只有等到二者重新回到同一线程的时候过滤器才会有效。
窗口事件产生之后,事件会经过:事件派发 -> 事件过滤->事件分发->事件处理几个阶段。Qt窗口中对于产生的一系列事件都有默认的处理动作,如果我们有特殊需求就需要在合适的阶段重写事件的处理动作。
事件在QT中的流程:
比如:可对这些事件处理函数进行重写
// 鼠标按下
[virtual protected] void QWidget::mousePressEvent(QMouseEvent *event);
// 鼠标释放
[virtual protected] void QWidget::mouseReleaseEvent(QMouseEvent *event);
// 鼠标移动
[virtual protected] void QWidget::mouseMoveEvent(QMouseEvent *event);
当鼠标左键、鼠标右键、鼠标中键被按下,该函数被自动调用,通过参数可以得到当前按下的是哪个鼠标键
[virtual protected] void QWidget::mousePressEvent(QMouseEvent *event);
Qt::LeftButton 左
Qt::RightButton 右
Qt::MidButton 中
void Widget::mousePressEvent(QMouseEvent *event){
if(event->button()==Qt::LeftButton) {
qDebug()<<"左键按下";
}
[virtual protected] void QWidget::mousePressEvent(QMouseEvent *event);
当鼠标左键、鼠标右键、鼠标中键被释放,该函数被自动调用,通过参数可以得到当前释放的是哪个鼠标键
void Widget::mouseReleaseEvent(QMouseEvent *event){
if(event->button()==Qt::LeftButton) {
qDebug()<<"左键释放";
}
}
鼠标双击事件:[virtual protected] void QWidget::mouseDoubleClickEvent(QMouseEvent *event);
鼠标进入事件:[virtual protected] void QWidget::enterEvent(QEvent *event);
键盘按下事件:[virtual protected] void QWidget::keyPressEvent(QKeyEvent *event);
窗口重绘事件:[virtual protected] void QWidget::paintEvent(QPaintEvent *event);
窗口关闭事件:[virtual protected] void QWidget::closeEvent(QCloseEvent *event);
事件处理器函数都是虚函数,因此我们就可以添加一个标准窗口类的派生类,这样不仅使子类继承了父类的属性,还可以在这个子类中重写父类的虚函数。
代码:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include
#include
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::closeEvent(QCloseEvent *ev)
{
QMessageBox::Button btn = QMessageBox::question(this, "关闭窗口", "您确定要关闭窗口吗?");
if(btn == QMessageBox::Yes)
{
// 接收并处理这个事件
ev->accept();
}
else
{
// 忽略这个事件
ev->ignore();
}
}
void MainWindow::resizeEvent(QResizeEvent *ev)
{
qDebug() << "oldSize: " << ev->oldSize()
<< "currentSize: " << ev->size();
}
与上面的思路是一样的。新添加的按钮类可以让它继承 QPushButton,也可以让它继承其他的窗口类(代价是当鼠标点击事件触发之后需要自己发射自定义信号),这里让添加的子类从QWidget类派生。
#ifndef MYBUTTON_H
#define MYBUTTON_H
#include
class MyButton : public QWidget
{
Q_OBJECT
public:
explicit MyButton(QWidget *parent = nullptr);
void setImage(QString normal, QString hover, QString pressed);
protected:
void mousePressEvent(QMouseEvent* ev);
void mouseReleaseEvent(QMouseEvent* ev);
void enterEvent(QEvent* ev);
void leaveEvent(QEvent* ev);
void paintEvent(QPaintEvent* ev);
signals:
void clicked();
private:
QPixmap m_normal;
QPixmap m_press;
QPixmap m_hover;
QPixmap m_current;
};
#endif // MYBUTTON_H
#include "mybutton.h"
#include
MyButton::MyButton(QWidget *parent) : QWidget(parent)
{
}
void MyButton::setImage(QString normal, QString hover, QString pressed)
{
// 加载图片
m_normal.load(normal);
m_hover.load(hover);
m_press.load(pressed);
m_current = m_normal;
// 设置按钮和图片大小一致
setFixedSize(m_normal.size());
}
void MyButton::mousePressEvent(QMouseEvent *ev)
{
// 鼠标被按下, 发射这个自定义信号
emit clicked();
m_current = m_press;
update();
}
void MyButton::mouseReleaseEvent(QMouseEvent *ev)
{
m_current = m_normal;
update();
}
void MyButton::enterEvent(QEvent *ev)
{
m_current = m_hover;
update();
}
void MyButton::leaveEvent(QEvent *ev)
{
m_current = m_normal;
update();
}
void MyButton::paintEvent(QPaintEvent *ev)
{
QPainter p(this);
p.drawPixmap(rect(), m_current);
}
对QWidget窗口进行提升为MyButton类型(该类型自己写了clicked信号)
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
// 给自定义按钮设置图片
ui->button->setImage(":/ghost-1.png", ":/ghost-2.png", ":/ghost-3.png");
// 处理自定义按钮的鼠标点击事件
connect(ui->button, &MyButton::clicked, this, [=]()
{
QMessageBox::information(this, "按钮", "莫要调戏我...");
});
}
MainWindow::~MainWindow()
{
delete ui;
}