赞
踩
The Event System
在Qt中,事件是派生自抽象QEvent
类的对象,它表示应用程序内发生的事情或应用程序需要知道的外部活动的结果。事件可以由QObject子类的任何实例接收和处理,但它们与小部件尤其相关。
Qt程序需要在main()函数创建一个QApplication对象,然后调用它的exec()函数。这个函数就是开始Qt的事件循环。在执行exec()函数之后,程序将进入事件循环来监听应用程序的事件,当事件发生时,Qt将创建一个事件对象。
当事件发生时,Qt通过构造适当的QEvent
子类的实例来创建一个事件对象来表示它,并通过调用它的event()函数将它交付给QObject的一个特定实例(或它的一个子类)。
这个函数event()
不处理事件本身;根据交付的事件类型,它为该特定类型的事件调用事件处理程序,并根据事件是被接受还是被忽略发送响应。
一些事件,如QMouseEvent和QKeyEvent,来自窗口系统;还有一些,比如QTimerEvent,来自其他来源;有些来自应用程序本身。
大多数事件类型都有特殊的类,特别是QResizeEvent、QPaintEvent、QMouseEvent、QKeyEvent和QCloseEvent。每个类都是QEvent的子类,并添加特定于事件的函数。例如,QResizeEvent添加了size()和oldSize(),以使小部件能够发现它们的尺寸是如何被更改的。
有些类支持多个实际事件类型。QMouseEvent支持按下鼠标按钮、双击、移动和其他相关操作。
每个事件都有一个关联的类型,在QEvent:: type中定义,这可以用作运行时类型信息的方便来源,以快速确定给定事件对象是从哪个子类构建的。
由于程序需要以各种复杂的方式进行响应,Qt的事件传递机制非常灵活。
传递事件的通常方式是调用虚函数。例如,QPaintEvent通过调用QWidget::paintEvent()来传递。这个虚函数负责进行适当的响应,通常是重新绘制小部件。如果在虚函数的实现中不执行所有必要的工作,则可以调用基类的实现。
例如,下面的代码处理自定义按钮控件上的鼠标左键单击,同时将所有其他按钮单击传递给基本QPushButton类:
void CustomButton::mousePressEvent(QMouseEvent* ev)override
{
if (ev->button() == Qt::MouseButton::LeftButton)
{
//在这里处理鼠标左键
qInfo() << "left buttondown";
}
else
{
//将其他案件传递给基类处理
QPushButton::mousePressEvent(ev);
}
}
注意:如果这样做了,按钮的clicked()和pressed()信号将不能被触发!
class CustomButton : public QPushButton { public: CustomButton(QWidget* parent = nullptr) :QPushButton(parent) { //不能触发信号 connect(this, &QPushButton::clicked, this, []() {qInfo() << "clicked"; }); connect(this, &QPushButton::pressed, this, []() {qInfo() << "pressed"; }); } void mousePressEvent(QMouseEvent* ev)override { if (ev->button() == Qt::MouseButton::LeftButton) { //在这里处理鼠标左键 qInfo() << "left buttondown"; } else { //将其他案件传递给基类处理 QPushButton::mousePressEvent(ev); } } };
想要让父类也能够处理左键消息,可以把父类的mousePressEvent放在最后调用(不放在else中)。
void CustomButton::mousePressEvent(QMouseEvent* ev)override
{
if (ev->button() == Qt::MouseButton::LeftButton)
{
//在这里处理鼠标左键
qInfo() << "left buttondown";
}
//将其他案件传递给基类处理
QPushButton::mousePressEvent(ev);
}
void CustomButton::mousePressEvent(QMouseEvent* ev)override { //获取鼠标按键 类型为枚举:Qt::MouseButton:: qInfo() << ev->button(); //如果同时有多个鼠标按键按下,需要判断左键是否按下 qInfo() << (ev->buttons() & Qt::MouseButton::LeftButton); //获取鼠标坐标 position()返回浮点坐标,可以使用pos()获取整型坐标 qInfo() << ev->position(); //鼠标在本控件上的坐标 qInfo() << ev->scenePosition(); //鼠标在本控件所在的窗口位置 qInfo() << ev->globalPosition(); //鼠标相对于屏幕的坐标 //将其他案件传递给基类处理 QPushButton::mousePressEvent(ev); }
void CustomButton::mouseReleaseEvent(QMouseEvent*ev)override
{
QPushButton::mouseReleaseEvent(ev);
}
void CustomButton::mouseDoubleClickEvent(QMouseEvent*ev)override
{
qInfo() << __FUNCTION__;
}
如果关闭了鼠标跟踪,则只有在移动鼠标时按下鼠标按钮时才会发生鼠标移动事件。如果打开了鼠标跟踪,即使没有按下鼠标按钮,也会发生鼠标移动事件。
按钮是自动追踪鼠标的,但是QWidget是不会的,如果要给QWidget的子类重写鼠标移动,需要使用void setMouseTracking(bool enable)
启用鼠标追踪。
void CustomButton::mouseMoveEvent(QMouseEvent* ev)override
{
qInfo() << __FUNCTION__ << ev->pos();
}
返回轮子旋转的相对量,单位为八分之一度。正值表示转轮向前旋转,远离用户;负值表示转轮向后向用户旋转。angleDelta().y()提供自上一个事件以来旋转普通垂直鼠标滚轮的角度。如果鼠标有水平滚轮,angleDelta().x()提供水平鼠标滚轮旋转的角度,否则就是0。有些鼠标允许用户倾斜滚轮来进行水平滚动,有些触摸板支持水平滚动手势;它也会出现在angleDelta().x()中。
大多数鼠标类型的工作步长为15度,在这种情况下,delta值是120的倍数;即120单位* 1/8 = 15度。
然而,有些鼠标的滚轮分辨率更高,发送的delta值小于120单位(小于15度)。为了支持这种可能性,可以累计添加来自事件的增量值,直到达到120的值,然后滚动小部件,或者可以部分滚动小部件以响应每个轮事件。
void CustomButton::wheelEvent(QWheelEvent* ev)override
{
//获取滚轮滚动方向
QPoint numDegrees = ev->angleDelta();
qInfo() <<"水平:" << numDegrees.x()/8 <<"垂直:" << numDegrees.y()/8;
}
在做窗口应用程序时,为了使窗口更简洁,有时候会将窗口setWindowFlag();属性设置为Qt::WindowType::FramelessWindowHint
,这时,窗口将不可拖动,但我们可以通过以下代码尝试解决无边框窗体的拖动问题。
步骤:
mousePressEvent
时获取鼠标点击的pos局部位置,并且保存mouseMoveEvent
时与该起始位置相减得到移动的位置。代码:
class Widget : public QWidget { Q_OBJECT public: Widget(QWidget* parent = nullptr) :QWidget(parent) { setWindowFlag(Qt::WindowType::FramelessWindowHint); } void mousePressEvent(QMouseEvent*ev)override { pressPos = ev->pos(); //保存起始位置 } void mouseMoveEvent(QMouseEvent* ev)override { if (ev->buttons() & Qt::MouseButton::LeftButton) { move(ev->globalPosition().toPoint() - pressPos); //获取全局位置 } } private: QPoint pressPos; };
void keyPressEvent(QKeyEvent* ev)override { //获取按键 qInfo() <<"key:" << Qt::Key(ev->key()); //返回此键生成的Unicode文本。不同平台按下Shift、Control、Alt和Meta等修饰键时的返回值不同,可能返回空字符串。 qInfo() << "text:" << ev->text(); //获取按下了什么键盘修饰符(shift、control、alt、meta(windows键)、keypad(小键盘,即数字键)) qInfo() << ev->modifiers(); //1,判断是否按下了组合键(快捷键、加速键) 事实上,**我们可以忽略这种方法,直接使用下面的第二个方法** if (ev->modifiers() & Qt::KeyboardModifier::ControlModifier && ev->key() == Qt::Key::Key_S) { qInfo() << "1 保存,保存"; } //2,判断是否按下了组合键,方便的函数: 简单!!! if (ev->matches(QKeySequence::StandardKey::Save)) { qInfo() <<"2 保存、保存"; } }
void keyReleaseEvent(QKeyEvent* ev)override
{
//同按下
}
class Widget : public QWidget { Q_OBJECT public: Widget(QWidget* parent = nullptr) :QWidget(parent) { setWindowFlag(Qt::WindowType::FramelessWindowHint); resize(640, 480); button = new QPushButton("按钮", this); button->setFocusPolicy(Qt::FocusPolicy::NoFocus); } void keyPressEvent(QKeyEvent* ev)override { switch (ev->key()) { case Qt::Key::Key_Up: button->move(button->pos() + QPoint(0, -1)); break; case Qt::Key::Key_Down: button->move(button->pos() + QPoint(0, 1)); break; case Qt::Key::Key_Left: button->move(button->pos() + QPoint(-1, 0)); break; case Qt::Key::Key_Right: button->move(button->pos() + QPoint(1, 0)); break; } } private: QPushButton* button = nullptr; };
当Qt从窗口系统接收到一个顶级小部件的窗口关闭请求时,将用给定的事件调用此事件处理程序。
默认情况下,接受事件并关闭小部件。您可以重新实现此函数,以更改小部件响应窗口关闭请求的方式。例如,您可以通过在所有事件上调用ignore()来防止窗口关闭。
主窗口应用程序通常使用该函数的重新实现来检查用户的工作是否已保存,并在关闭前请求权限。
void Widget::closeEvent(QCloseEvent* ev)override
{
auto ret = QMessageBox::question(this, "温馨提示", "你有未保存的操作,是否保存并关闭?");
if (ret == QMessageBox::StandardButton::Yes)
{
//保存并关闭
ev->accept();
}
else
{
//不保存也不关闭
ev->ignore();
}
}
除隐藏和显示窗口外,窗口最小化会发送窗口隐藏事件,正常显示会发送窗口显示事件。
void Widget::showEvent(QShowEvent* ev)override
{
qInfo() << "我显示啦~";
}
void Widget::hideEvent(QHideEvent* ev)override
{
qInfo() << "我隐藏啦~";
}
void Widget::moveEvent(QMoveEvent* ev)override
{
qInfo() << "Widget moved" << "oldPos" << ev->oldPos() << "newPos" << ev->pos();
}
void Widget::resizeEvent(QResizeEvent* ev)override
{
qInfo() << "Widget SizeChanged" << "oldSize" << ev->oldSize() << "newSize" << ev->size();
}
这个事件处理程序可以在子类中重新实现,以接收小部件的键盘焦点事件(焦点接收)。
小部件通常必须将focuspolicy()设置为Qt::NoFocus以外的东西,以便接收焦点事件。(注意,程序员可以在任何小部件上调用setFocus(),即使是那些通常不接受焦点的小部件。)
默认实现更新小部件(不指定focusPolicy()的窗口除外)。
void Widget::focusInEvent(QFocusEvent* event)override
{
qInfo() << "我有焦点啦?";
}
void Widget::focusOutEvent(QFocusEvent* event)override
{
qInfo() << "我有焦点没啦?";
}
重写此 QLineEdit
以实现焦点放到此上面会实现颜色的改变,焦点取消则恢复。
class LineEdit:public QLineEdit{
public:
using QLineEdit::QLineEdit;
void focusInEvent(QFocusEvent* ev)override{
this->setStyleSheet("background-color: lightblue");
}
void focusOutEvent(QFocusEvent* ev)override{
this->setStyleSheet("background-color: white");
}
private:
};
void Widget::contextMenuEvent(QContextMenuEvent* ev)
{
qInfo() << "报告长官,请求弹出上下文菜单,也就是右键菜单!"
<< "请弹出在\n"
<< "全局坐标:"<<ev->globalPos()
<< "局部坐标:"<<ev->pos() << "位置";
}
如果需要检测程序中,某些东西是否发生了改变,可以通过void QWidget::changeEvent(QEvent *event)
来检测。
QEvent::WindowStateChange
窗口状态改变为例!void Widget::changeEvent(QEvent* ev)override
{
if (ev->type() == QEvent::WindowStateChange)
{
auto we = static_cast<QWindowStateChangeEvent*>(ev);
qInfo() << "oldState" << we->oldState();
qInfo() << "curState" << this->windowState();
}
}
class Widget : public QWidget { Q_OBJECT public: Widget(QWidget* parent = nullptr) :QWidget(parent) { //启动一个定时器,并返回定时器ID _timerID = startTimer(1000); } void timerEvent(QTimerEvent* ev)override { if (ev->timerId() == _timerID) { //把控件往右移动5个像素 move(this->pos() + QPoint(5, 0)); if(x()>1080) { killTimer(_timerID); } qInfo()<<_timerID<<"timeout"; } } private: int _timerID; };
Drag and Drop
设置QWidget
窗口接受拖拽:
setAcceptDrops(true);
void dragEnterEvent(QDragEnterEvent *ev);
void dropEvent(QDropEvent *ev)
之后通过 QMimaData来获得内容:
auto thing=ev->mimeData();
传递事件的通常方式是调用虚函数。例如,QMouseEvent通过调用QWidget::mousePressEvent()来传递。这个虚函数负责进行适当的响应,通常是处理鼠标点击,并发出对应的信号。如果在虚函数的实现中不执行所有必要的工作,则可能需要调用基类的实现。
如果希望替换基类的事件处理函数,则必须自己实现所有内容。但是,如果您只想扩展基类的功能,那么您可以实现您想要的功能,并调用基类来获得您不想处理的任何情况的默认行为。
有时,没有这样一个特定于事件的函数,或者特定于事件的函数是不够的。最常见的例子是按Tab键。通常情况下,QWidget会拦截这些来移动键盘焦点,但也有一些小部件本身需要Tab键。
这些对象可以重新实现QObject::event()(通用事件处理程序),并在通常处理之前或之后执行它们的事件处理,或者它们可以完全替换函数。一个非常不寻常的小部件既解释Tab又具有特定于应用程序的自定义事件,它可能包含以下event()函数:
bool Widget::event(QEvent* ev)override
{
//如果是按键按下事件
if (ev->type() == QEvent::Type::KeyPress)
{
QKeyEvent* ke = static_cast<QKeyEvent*>(ev);
if (ke->key() == Qt::Key::Key_Tab)
{
//对tab键按下进行处理
qInfo() << "处理 tab 按键...";
return true; //返回true表示此事件已经处理了,不会继续传播
}
}
return QWidget::event(ev);
}
注意,对于所有未处理的情况,仍然调用QWidget::event(),并且返回值指示是否处理了事件;true值防止事件被发送到其他对象。
前面通过事件处理函数实现了无边框窗口的拖动,如何通过事件分发函数实现呢?
bool event(QEvent* ev)override { if (ev->type() == QEvent::MouseButtonPress) { auto me = static_cast<QMouseEvent*>(ev); pressPos = me->pos(); return true; } else if (ev->type() == QEvent::MouseMove) { auto me = static_cast<QMouseEvent*>(ev); //如果鼠标左键是按下的 if(me->buttons() & Qt::MouseButton::LeftButton) this->move(me->globalPosition().toPoint() - pressPos); return true; } return QWidget::event(ev); }
许多应用程序都希望创建和发送它们自己的事件。通过构造合适的事件对象并使用QCoreApplication::sendEvent()
和QCoreApplication::postEvent()
发送事件,您可以以与Qt自己的事件循环完全相同的方式发送事件。
**sendEvent()**立即处理事件。当它返回时,事件过滤器和/或对象本身已经处理了该事件。对于许多事件类,都有一个名为isAccepted()的函数,它告诉您事件是被最后一个调用的处理程序接受还是拒绝的。
**postEvent()**将事件发送到队列中,以便稍后进行分派。下次Qt的主事件循环运行时,它会分发所有发布的事件,并进行一些优化。例如,如果有几个调整大小事件,它们将被压缩为一个。同样适用于绘制事件:QWidget::update()调用postEvent(),它通过避免多次重绘来消除闪烁并提高速度。
要创建自定义类型的事件,需要定义一个事件号,该事件号必须大于QEvent::User,并且可能需要继承QEvent的子类,以便传递关于自定义事件的特定信息。有关更多详细信息,请参阅QEvent文档。
enum EventType
{
NumberEvent = QEvent::Type::User
};
class NumberChangeEvent : public QEvent { public: NumberChangeEvent(qint32 num) :QEvent(QEvent::Type(EventType::NumberEvent)) //关键一步! 注册事件类型枚举 , _number(num) { } ~NumberChangeEvent() { qInfo() << __FUNCTION__; } qint32 number() { return _number; } private: qint32 _number = 0; };
class Widget : public QWidget { Q_OBJECT public: Widget(QWidget* parent = nullptr) :QWidget(parent) { resize(640, 480); setWindowFlag(Qt::WindowType::FramelessWindowHint); button = new QPushButton("发送事件", this); //点击按钮发送NumberChangeEvent事件 connect(button, &QPushButton::clicked, this, [=]() { static int i = 0; NumberChangeEvent ne(i++); QApplication::sendEvent(this, &ne); }); } //customEvent专门用来接受自定义事件 void customEvent(QEvent* ev)override { if (ev->type() == EventType::NumberEvent) { auto ne = static_cast<NumberChangeEvent*>(ev); qInfo() << "number Event"<<ne->number(); } } private: QPushButton* button = nullptr; };
connect(button, &QPushButton::clicked, this, [=]()
{
static int i = 0;
//会自动释放
NumberChangeEvent ne(i);
QApplication::sendEvent(this, &ne);
//需要手动释放
auto pne = new NumberChangeEvent(i);
QApplication::sendEvent(this, pne);
delete pne;
++i;
});
sendEvent会让接受事件的对象立即处理事件,处理完成之后函数返回
connect(button, &QPushButton::clicked, this, [=]()
{
static int i = 0;
//不能放在栈区
//---1,postEvent提交的事件,会在事件处理后,自动delete(栈区的无法delete)
//---2,因为事件提交之后还没有处理,这个函数结束会释放事件(访问错误)
//NumberChangeEvent ne(i);
//QApplication::postEvent(this, &ne);
//只能放在堆区,但是不需要手动释放
auto pne = new NumberChangeEvent(i);
QApplication::postEvent(this, pne);
++i;
});
postEvent是把事件提交到事件队列,不管有没有处理事件,都立即返回
Qt的事件产生之后,不是直接传递给了对象的,需要经过一系列的过程。
当控件忽略事件时,事件会继续往上传播,这里的传播是指传播给父组件。
accept()
函数 和ignore()
函数:
class Button : public QPushButton { public: Button(const QString& text,QWidget* parent = nullptr) :QPushButton(text,parent) { } void mousePressEvent(QMouseEvent* ev)override { qInfo() << __FUNCTION__; //默认是接受事件的,父组件不会收到鼠标点击事件 //ev->accept(); //如果忽略事件,父组件就会收到鼠标点击事件 //ev->ignore(); } }; class Widget : public QWidget { Q_OBJECT public: Widget(QWidget* parent = nullptr) :QWidget(parent) { resize(640, 480); auto button = new Button("button", this); } void mousePressEvent(QMouseEvent* ev)override { qInfo() << __FUNCTION__; } };
class Button : public QPushButton { public: Button(const QString& text,QWidget* parent = nullptr) :QPushButton(text,parent) { } bool event(QEvent* ev)override { if (ev->type() == QEvent::Type::MouseButtonPress) { qInfo() << __FUNCTION__; //return false; //返回false,表示我没有处理这个事件,可以传递给父组件 //return true; //返回true,表示我处理了这个事件,不再继续传递了 } return QPushButton::event(ev); } }; class Widget : public QWidget { Q_OBJECT public: Widget(QWidget* parent = nullptr) :QWidget(parent) { resize(640, 480); auto button = new Button("button", this); } bool event(QEvent* ev)override { if (ev->type() == QEvent::Type::MouseButtonPress) { qInfo() << __FUNCTION__; } return QWidget::event(ev); } };
有时,对象需要查看(可能还需要拦截)传递给另一个对象的事件。例如,对话框通常想要过滤一些小部件的按键;例如,修改返回键处理。
QObject::installEventFilter()函数通过设置一个事件过滤器来实现这一点,使指定的过滤器对象在其QObject::eventFilter()函数中接收目标对象的事件。事件过滤器在目标对象处理事件之前处理事件,允许它根据需要检查和丢弃事件。可以使用QObject::removeEventFilter()函数删除现有的事件过滤器。
当调用过滤器对象的eventFilter()实现时,它可以接受或拒绝事件,并允许或拒绝对事件的进一步处理。如果所有事件过滤器都允许对事件进行进一步处理(每个返回false),则将事件发送到目标对象本身。如果其中一个停止处理(通过返回true),目标和任何后面的事件过滤器都不会看到事件。
bool FilterObject::eventFilter(QObject *object, QEvent *event)
{
if (object == target && event->type() == QEvent::KeyPress) {
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
if (keyEvent->key() == Qt::Key_Tab) {
// Special tab handling
return true;
} else
return false;
}
return false;
}
上面的代码展示了截获发送到特定目标小部件的Tab键按下事件的另一种方法。在这种情况下,过滤器处理相关事件并返回true以阻止它们被进一步处理。所有其他事件都被忽略,过滤器返回false,允许通过安装在目标小部件上的任何其他事件过滤器将它们发送到目标小部件。
通过在QApplication或QCoreApplication对象上安装事件过滤器,还可以为整个应用程序筛选所有事件。这种全局事件过滤器在特定于对象的过滤器之前调用。这是非常强大的,但它也减慢了整个应用程序中每个单个事件的事件交付;通常应该使用讨论的其他技术。
处理事件有五种不同的方法,以下列出了所有五种方法:
其他技术:
bool QCoreApplication::notify()
。这是非常强大的,提供完全的控制,但是一次只能有一个子类是活动的(就是说不能实现不同方式的处理,因为实例化应用程序对象只能有一个)。前面通过事件处理函数、事件分发实现了无边框窗口的拖动,如何通过事件过滤实现呢?
class Widget : public QWidget { Q_OBJECT public: Widget(QWidget* parent = nullptr) :QWidget(parent) { resize(640, 480); setWindowFlag(Qt::WindowType::FramelessWindowHint); setMouseTracking(true); this->installEventFilter(this); } bool eventFilter(QObject* object, QEvent* ev)override { auto w = qobject_cast<QWidget*>(object); if (!w) return false; if (ev->type() == QEvent::MouseButtonPress) { auto me = static_cast<QMouseEvent*>(ev); pressPos = me->pos(); return true; } else if (ev->type() == QEvent::MouseMove) { auto me = static_cast<QMouseEvent*>(ev); if (me->buttons() & Qt::MouseButton::LeftButton) w->move(me->globalPosition().toPoint() - pressPos); return true; } return QObject::eventFilter(object, ev); } private: QPoint pressPos; };
这是最简单的,在自己类里面重写eventFilter来过滤事件,但是如果我有很多个无边框窗口,都要实现移动,这个方法就不太方便了。
可以先写一个拖拽控件的过滤器对象,然后哪里需要就在那里添加!
class DragWidgetFilter :public QObject { public: DragWidgetFilter(QObject* parent = nullptr) :QObject(parent) { } bool eventFilter(QObject* object,QEvent*ev)override { auto w = qobject_cast<QWidget*>(object); if (!w) return false; if (ev->type() == QEvent::MouseButtonPress) { auto me = static_cast<QMouseEvent*>(ev); pressPos = me->pos(); } else if (ev->type() == QEvent::MouseMove) { auto me = static_cast<QMouseEvent*>(ev); if (me->buttons() & Qt::MouseButton::LeftButton) w->move(me->globalPosition().toPoint() - pressPos); } return QObject::eventFilter(object,ev); } private: QPoint pressPos; }; class Widget : public QWidget { Q_OBJECT public: Widget(QWidget* parent = nullptr) :QWidget(parent) { resize(640, 480); setWindowFlag(Qt::WindowType::FramelessWindowHint); setMouseTracking(true); this->installEventFilter(new DragWidgetFilter(this)); } }
这个枚举类型定义了Qt中有效的事件类型。事件类型和每个类型的专门类如下:
常量 | 值 | 描述 |
---|---|---|
QEvent::None | 0 | 不是一个事件 |
QEvent::ActionAdded | 114 | 一个新 action 被添加(QActionEvent) |
QEvent::ActionChanged | 113 | 一个 action 被改变(QActionEvent) |
QEvent::ActionRemoved | 115 | 一个 action 被移除(QActionEvent) |
QEvent::ActivationChange | 99 | Widget 的顶层窗口激活状态发生了变化 |
QEvent::ApplicationActivate | 121 | 这个枚举已被弃用,使用 ApplicationStateChange 代替 |
QEvent::ApplicationActivated | ApplicationActivate | 这个枚举已被弃用,使用 ApplicationStateChange 代替 |
QEvent::ApplicationDeactivate | 122 | 这个枚举已被弃用,使用 ApplicationStateChange 代替 |
QEvent::ApplicationFontChange | 36 | 应用程序的默认字体发生了变化 |
QEvent::ApplicationLayoutDirectionChange | 37 | 应用程序的默认布局方向发生了变化 |
QEvent::ApplicationPaletteChange | 38 | 应用程序的默认调色板发生了变化 |
QEvent::ApplicationStateChange | 214 | 应用程序的状态发生了变化 |
QEvent::ApplicationWindowIconChange | 35 | 应用程序的图标发生了变化 |
QEvent::ChildAdded | 68 | 一个对象获得孩子(QChildEvent) |
QEvent::ChildPolished | 69 | 一个部件的孩子被抛光(QChildEvent) |
QEvent::ChildRemoved | 71 | 一个对象时区孩子(QChildEvent) |
QEvent::Clipboard | 40 | 剪贴板的内容发生改变 |
QEvent::Close | 19 | Widget 被关闭(QCloseEvent) |
QEvent::CloseSoftwareInputPanel | 200 | 一个部件要关闭软件输入面板(SIP) |
QEvent::ContentsRectChange | 178 | 部件内容区域的外边距发生改变 |
QEvent::ContextMenu | 82 | 上下文弹出菜单(QContextMenuEvent) |
QEvent::CursorChange | 183 | 部件的鼠标发生改变 |
QEvent::DeferredDelete | 52 | 对象被清除后将被删除(QDeferredDeleteEvent) |
QEvent::DragEnter | 60 | 在拖放操作期间鼠标进入窗口部件(QDragEnterEvent) |
QEvent::DragLeave | 62 | 在拖放操作期间鼠标离开窗口部件(QDragLeaveEvent) |
QEvent::DragMove | 61 | 拖放操作正在进行(QDragMoveEvent) |
QEvent::Drop | 63 | 拖放操作完成(QDropEvent) |
QEvent::DynamicPropertyChange | 170 | 动态属性已添加、更改或从对象中删除 |
QEvent::EnabledChange | 98 | 部件的 enabled 状态已更改 |
QEvent::Enter | 10 | 鼠标进入部件的边界(QEnterEvent) |
QEvent::EnterEditFocus | 150 | 编辑部件获得焦点进行编辑,必须定义 QT_KEYPAD_NAVIGATION |
QEvent::EnterWhatsThisMode | 124 | 当应用程序进入“What’s This?”模式,发送到 toplevel 顶层部件 |
QEvent::Expose | 206 | 当其屏幕上的内容无效,发送到窗口,并需要从后台存储刷新 |
QEvent::FileOpen | 116 | 文件打开请求(QFileOpenEvent) |
QEvent::FocusIn | 8 | 部件或窗口获得键盘焦点(QFocusEvent) |
QEvent::FocusOut | 9 | 部件或窗口失去键盘焦点(QFocusEvent) |
QEvent::FocusAboutToChange | 23 | 部件或窗口焦点即将改变(QFocusEvent) |
QEvent::FontChange | 97 | 部件的字体发生改变 |
QEvent::Gesture | 198 | 触发了一个手势(QGestureEvent) |
QEvent::GestureOverride | 202 | 触发了手势覆盖(QGestureEvent) |
QEvent::GrabKeyboard | 188 | Item 获得键盘抓取(仅限 QGraphicsItem) |
QEvent::GrabMouse | 186 | 项目获得鼠标抓取(仅限 QGraphicsItem) |
QEvent::GraphicsSceneContextMenu | 159 | 在图形场景上的上下文弹出菜单(QGraphicsScene ContextMenuEvent) |
QEvent::GraphicsSceneDragEnter | 164 | 在拖放操作期间,鼠标进入图形场景(QGraphicsSceneDragDropEvent) |
QEvent::GraphicsSceneDragLeave | 166 | 在拖放操作期间鼠标离开图形场景(QGraphicsSceneDragDropEvent) |
QEvent::GraphicsSceneDragMove | 165 | 在场景上正在进行拖放操作(QGraphicsSceneDragDropEvent) |
QEvent::GraphicsSceneDrop | 167 | 在场景上完成拖放操作(QGraphicsSceneDragDropEvent) |
QEvent::GraphicsSceneHelp | 163 | 用户请求图形场景的帮助(QHelpEvent) |
QEvent::GraphicsSceneHoverEnter | 160 | 鼠标进入图形场景中的悬停项(QGraphicsSceneHoverEvent) |
QEvent::GraphicsSceneHoverLeave | 162 | 鼠标离开图形场景中一个悬停项(QGraphicsSceneHoverEvent) |
QEvent::GraphicsSceneHoverMove | 161 | 鼠标在图形场景中的悬停项内移动(QGraphicsSceneHoverEvent) |
QEvent::GraphicsSceneMouseDoubleClick | 158 | 鼠标在图形场景中再次按下(双击)(QGraphicsSceneMouseEvent) |
QEvent::GraphicsSceneMouseMove | 155 | 鼠标在图形场景中移动(QGraphicsSceneMouseEvent) |
QEvent::GraphicsSceneMousePress | 156 | 鼠标在图形场景中按下(QGraphicsSceneMouseEvent) |
QEvent::GraphicsSceneMouseRelease | 157 | 鼠标在图形场景中释放(QGraphicsSceneMouseEvent) |
QEvent::GraphicsSceneMove | 182 | 部件被移动(QGraphicsSceneMoveEvent) |
QEvent::GraphicsSceneResize | 181 | 部件已调整大小(QGraphicsSceneResizeEvent) |
QEvent::GraphicsSceneWheel | 168 | 鼠标滚轮在图形场景中滚动(QGraphicsSceneWheelEvent) |
QEvent::Hide | 18 | 部件被隐藏(QHideEvent) |
QEvent::HideToParent | 27 | 子部件被隐藏(QHideEvent) |
QEvent::HoverEnter | 127 | 鼠标进入悬停部件(QHoverEvent) |
QEvent::HoverLeave | 128 | 鼠标留离开悬停部件(QHoverEvent) |
QEvent::HoverMove | 129 | 鼠标在悬停部件内移动(QHoverEvent) |
QEvent::IconDrag | 96 | 窗口的主图标被拖走(QIconDragEvent) |
QEvent::IconTextChange | 101 | 部件的图标文本发生改变(已弃用) |
QEvent::InputMethod | 83 | 正在使用输入法(QInputMethodEvent) |
QEvent::InputMethodQuery | 207 | 输入法查询事件(QInputMethodQueryEvent) |
QEvent::KeyboardLayoutChange | 169 | 键盘布局已更改 |
QEvent::KeyPress | 6 | 键盘按下(QKeyEvent) |
QEvent::KeyRelease | 7 | 键盘释放(QKeyEvent) |
QEvent::LanguageChange | 89 | 应用程序翻译发生改变 |
QEvent::LayoutDirectionChange | 90 | 布局的方向发生改变 |
QEvent::LayoutRequest | 76 | 部件的布局需要重做 |
QEvent::Leave | 11 | 鼠标离开部件的边界 |
QEvent::LeaveEditFocus | 151 | 编辑部件失去编辑的焦点,必须定义 QT_KEYPAD_NAVIGATION |
QEvent::LeaveWhatsThisMode | 125 | 当应用程序离开“What’s This?”模式,发送到顶层部件 |
QEvent::LocaleChange | 88 | 系统区域设置发生改变 |
QEvent::NonClientAreaMouseButtonDblClick | 176 | 鼠标双击发生在客户端区域外 |
QEvent::NonClientAreaMouseButtonPress | 174 | 鼠标按钮按下发生在客户端区域外 |
QEvent::NonClientAreaMouseButtonRelease | 175 | 鼠标按钮释放发生在客户端区域外 |
QEvent::NonClientAreaMouseMove | 173 | 鼠标移动发生在客户区域外 |
QEvent::MacSizeChange | 177 | 用户更改了部件的大小(仅限 OS X) |
QEvent::MetaCall | 43 | 通过 QMetaObject::invokeMethod() 调用异步方法 |
QEvent::ModifiedChange | 102 | 部件修改状态发生改变 |
QEvent::MouseButtonDblClick | 4 | 鼠标再次按下(QMouseEvent) |
QEvent::MouseButtonPress | 2 | 鼠标按下(QMouseEvent) |
QEvent::MouseButtonRelease | 3 | 鼠标释放(QMouseEvent) |
QEvent::MouseMove | 5 | 鼠标移动(QMouseEvent) |
QEvent::MouseTrackingChange | 109 | 鼠标跟踪状态发生改变 |
QEvent::Move | 13 | 部件的位置发生改变(QMoveEvent) |
QEvent::NativeGesture | 197 | 系统检测到手势(QNativeGestureEvent) |
QEvent::OrientationChange | 208 | 屏幕方向发生改变(QScreenOrientationChangeEvent) |
QEvent::Paint | 12 | 需要屏幕更新(QPaintEvent) |
QEvent::PaletteChange | 39 | 部件的调色板发生改变 |
QEvent::ParentAboutToChange | 131 | 部件的 parent 将要更改 |
QEvent::ParentChange | 21 | 部件的 parent 发生改变 |
QEvent::PlatformPanel | 212 | 请求一个特定于平台的面板 |
QEvent::PlatformSurface | 217 | 原生平台表面已创建或即将被销毁(QPlatformSurfaceEvent) |
QEvent::Polish | 75 | 部件被抛光 |
QEvent::PolishRequest | 74 | 部件应该被抛光 |
QEvent::QueryWhatsThis | 123 | 如果部件有“What’s This?”帮助,应该接受事件 |
QEvent::ReadOnlyChange | 106 | 部件的 read-only 状态发生改变 |
QEvent::RequestSoftwareInputPanel | 199 | 部件想要打开软件输入面板(SIP) |
QEvent::Resize | 14 | 部件的大小发生改变(QResizeEvent) |
QEvent::ScrollPrepare | 204 | 对象需要填充它的几何信息(QScrollPrepareEvent) |
QEvent::Scroll | 205 | 对象需要滚动到提供的位置(QScrollEvent) |
QEvent::Shortcut | 117 | 快捷键处理(QShortcutEvent) |
QEvent::ShortcutOverride | 51 | 按下按键,用于覆盖快捷键(QKeyEvent) |
QEvent::Show | 17 | 部件显示在屏幕上(QShowEvent) |
QEvent::ShowToParent | 26 | 子部件被显示 |
QEvent::SockAct | 50 | Socket 激活,用于实现 QSocketNotifier |
QEvent::StateMachineSignal | 192 | 信号被传递到状态机(QStateMachine::SignalEvent) |
QEvent::StateMachineWrapped | 193 | 事件是一个包装器,用于包含另一个事件(QStateMachine::WrappedEvent) |
QEvent::StatusTip | 112 | 状态提示请求(QStatusTipEvent) |
QEvent::StyleChange | 100 | 部件的样式发生改变 |
QEvent::TabletMove | 87 | Wacom 写字板移动(QTabletEvent) |
QEvent::TabletPress | 92 | Wacom 写字板按下(QTabletEvent) |
QEvent::TabletRelease | 93 | Wacom 写字板释放(QTabletEvent) |
QEvent::OkRequest | 94 | Ok 按钮在装饰前被按下,仅支持 Windows CE |
QEvent::TabletEnterProximity | 171 | Wacom 写字板进入接近事件(QTabletEvent),发送到 QApplication |
QEvent::TabletLeaveProximity | 172 | Wacom 写字板离开接近事件(QTabletEvent),发送到 QApplication |
QEvent::ThreadChange | 22 | 对象被移动到另一个线程。这是发送到此对象的最后一个事件在上一个线程中,参见:QObject::moveToThread() |
QEvent::Timer | 1 | 定时器事件(QTimerEvent) |
QEvent::ToolBarChange | 120 | 工具栏按钮在 OS X 上进行切换 |
QEvent::ToolTip | 110 | 一个 tooltip 请求(QHelpEvent) |
QEvent::ToolTipChange | 184 | 部件的 tooltip 发生改变 |
QEvent::TouchBegin | 194 | 触摸屏或轨迹板事件序列的开始(QTouchEvent) |
QEvent::TouchCancel | 209 | 取消触摸事件序列(QTouchEvent) |
QEvent::TouchEnd | 196 | 触摸事件序列结束(QTouchEvent) |
QEvent::TouchUpdate | 195 | 触摸屏事件(QTouchEvent) |
QEvent::UngrabKeyboard | 189 | Item 失去键盘抓取(QGraphicsItem) |
QEvent::UngrabMouse | 187 | Item 失去鼠标抓取(QGraphicsItem、QQuickItem) |
QEvent::UpdateLater | 78 | 部件应该排队在以后重新绘制 |
QEvent::UpdateRequest | 77 | 部件应该被重绘 |
QEvent::WhatsThis | 111 | 部件应该显示“What’s This”帮助(QHelpEvent) |
QEvent::WhatsThisClicked | 118 | 部件的“What’s This”帮助链接被点击 |
QEvent::Wheel | 31 | 鼠标滚轮滚动(QWheelEvent) |
QEvent::WinEventAct | 132 | 发生了 Windows 特定的激活事件 |
QEvent::WindowActivate | 24 | 窗口已激活 |
QEvent::WindowBlocked | 103 | 窗口被模态对话框阻塞 |
QEvent::WindowDeactivate | 25 | 窗户被停用 |
QEvent::WindowIconChange | 34 | 窗口的图标发生改变 |
QEvent::WindowStateChange | 105 | 窗口的状态(最小化、最大化或全屏)发生改变(QWindowStateChangeEvent) |
QEvent::WindowTitleChange | 33 | 窗口的标题发生改变 |
QEvent::WindowUnblocked | 104 | 一个模态对话框退出后,窗口将不被阻塞 |
QEvent::WinIdChange | 203 | 本地窗口的系统标识符发生改变 |
QEvent::ZOrderChange | 126 | 部件的 z 值发生了改变,该事件不会发送给顶层窗口 |
用户事件的值应该介于 QEvent:: 和 QEvent::MaxUser之间。
常量 | 值 | 描述 |
---|---|---|
QEvent::User | 1000 | 用户定义的事件 |
QEvent::MaxUser | 65535 | 最后的用户事件 ID |
为方便起见,可以使用 [static] int QEvent::registerEventType(int *hint* = -1)
函数来注册和存储一个自定义事件类型,这样做会避免意外地重用一个自定义事件类型。
事件(QEvent) | 信号(SIGNAL) | |
---|---|---|
与QObject的关系 | 由具体对象进行处理 | 由具体对象主动产生 |
对程序影响 | 改写事件处理函数可能导致程序行为发生改变 | 信号是否存在对应的槽函数不会改变程序行为 |
两者的联系 | 一般而言,信号在具体的事件处理函数中产生 |
信号和事件是两个不同层面的东西,发出者不同,作用不同。Qt中,所有的QObject的子类实例均可对事件接收和处理!
# Widget.h -------------- #ifndef WIDGET_H #define WIDGET_H #include <QWidget> #include <QDebug> #include <QMessageBox> #include <QMoveEvent> #include <QResizeEvent> #include <QPushButton> #include <QLineEdit> #include <QMimeData> #include <QTimer> #include <QEvent> #include <QApplication> class Button:public QPushButton{ public: using QPushButton::QPushButton; void enterEvent(QEnterEvent* ev)override{ setStyleSheet("background-color:red"); } void leaveEvent(QEvent* ev)override{ setStyleSheet("background-color:white"); } //测试事件传播 void mousePressEvent(QMouseEvent* ev)override{ //默认接受,当前组件接受事件处理,不会传播给父控件了 //ev->accept(); qInfo()<<"Button::mousePressEvent"; //如果忽略,则会传播给父控件处理,当前不处理 //ev->ignore(); } bool event(QEvent* ev)override{ if (ev->type()==QEvent::Type::MouseButtonPress){ qInfo()<<"Button::event::MouseButtonPress"; //return false; //会传递给父控件 //return true; //不会传递给父控件 } return QPushButton::event(ev); } private: }; class LineEdit:public QLineEdit{ public: using QLineEdit::QLineEdit; void focusInEvent(QFocusEvent* ev)override{ this->setStyleSheet("background-color: lightblue"); } void focusOutEvent(QFocusEvent* ev)override{ this->setStyleSheet("background-color: white"); } private: }; //自定义事件 enum EventType{ My_Event=QEvent::Type::User+1 }; class CustomEvent:public QEvent{ public: CustomEvent(int age,const QString& name) :QEvent(QEvent::Type(EventType::My_Event)),age(age),name(name){} void show(){ qInfo()<<name<<age; } private: int age; QString name; }; class Widget : public QWidget { Q_OBJECT public: Widget(QWidget *parent = nullptr); ~Widget(); public: //关闭事件,close时调用 void closeEvent(QCloseEvent* ev)override; void showEvent(QShowEvent* ev)override; void moveEvent(QMoveEvent* ev)override; void resizeEvent(QResizeEvent* ev)override; void enterEvent(QEnterEvent* ev)override; void leaveEvent(QEvent* ev)override; void mousePressEvent(QMouseEvent* ev)override; void mouseReleaseEvent(QMouseEvent* ev)override; void mouseDoubleClickEvent(QMouseEvent* ev)override; void mouseMoveEvent(QMouseEvent* ev)override; void wheelEvent(QWheelEvent* ev)override; void keyPressEvent(QKeyEvent* ev)override; void keyReleaseEvent(QKeyEvent* ev)override; void focusInEvent(QFocusEvent* ev)override; void focusOutEvent(QFocusEvent* ev)override; void changeEvent(QEvent* ev)override; void contextMenuEvent(QContextMenuEvent* ev)override; void dragEnterEvent(QDragEnterEvent* ev)override; void dropEvent(QDropEvent* ev)override; void timerEvent(QTimerEvent* ev)override; bool event(QEvent* ev)override; //事件分发函数 void customEvent(QEvent* ev)override; //处理自定义事件的分发函数 void testEventDiffuse(); //事件传播 public: //定时器 void testTimer(); void testDIYEvent(); public slots: //定时器处理的槽函数 void onTimerHandle(); private: Button* btn; QPoint pressPos; LineEdit* edit; int timeId; int cnt=0; }; #endif // WIDGET_H
# Widget.cpp ------------- #include "widget.h" Widget::Widget(QWidget *parent) : QWidget(parent) { resize(640,480); //启用鼠标追踪:mousemove事件 setMouseTracking(true); //设置窗口无边框 //setWindowFlags(Qt::WindowType::FramelessWindowHint); //设置关闭时销毁 setAttribute(Qt::WidgetAttribute::WA_DeleteOnClose); //设置窗口接受拖拽 setAcceptDrops(true); btn=new Button("变色按钮"); btn->show(); edit=new LineEdit(this); edit->setGeometry(100,100,100,30); edit->setFocus(); testEventDiffuse(); } Widget::~Widget() { btn->deleteLater(); edit->deleteLater(); } void Widget::closeEvent(QCloseEvent *ev) { //QMessageBox::warning(this,"提示","你有未保存的操作,是否继续") ; qInfo()<<__FUNCTION__; } void Widget::showEvent(QShowEvent *ev) { qInfo()<<__FUNCTION__; } void Widget::moveEvent(QMoveEvent *ev) { qInfo()<<__FUNCTION__<<ev->oldPos()<<ev->pos(); } void Widget::resizeEvent(QResizeEvent *ev) { qInfo()<<__FUNCTION__<<ev->oldSize()<<ev->size(); } void Widget::enterEvent(QEnterEvent *ev) { } void Widget::leaveEvent(QEvent *ev) { } void Widget::mousePressEvent(QMouseEvent* ev){ //qInfo()<<"Widget::mousePressEvent"; if (ev->button()==Qt::MouseButton::LeftButton){ btn->move(btn->pos()+QPoint(5,0)); //保存第一次拖动的点 pressPos=ev->pos(); } } void Widget::mouseReleaseEvent(QMouseEvent* ev){ if (ev->button()==Qt::MouseButton::LeftButton){ btn->move(btn->pos()+QPoint(0,5)); } } void Widget::mouseDoubleClickEvent(QMouseEvent* ev){ //qInfo()<<ev->button(); } void Widget::mouseMoveEvent(QMouseEvent* ev){ //qInfo()<<"Widget::mouseMoveEvent"; //如果有鼠标按下再移动,则无法通过ev->button获得按键 //通过buttons获得之前已经捕获的状态 if (ev->buttons() & Qt::MouseButton::LeftButton){ //qInfo()<<__FUNCTION__<<ev->buttons(); //拖动窗口 this->move(ev->globalPosition().toPoint()-pressPos); } } void Widget::wheelEvent(QWheelEvent* ev){ //qInfo()<<__FUNCTION__<<ev->angleDelta()<<ev->angleDelta().x(),ev->angleDelta().y(); } void Widget::keyPressEvent(QKeyEvent *ev) { //Q_UNUSED(ev); switch (ev->key()) { case Qt::Key_Up: btn->move(btn->pos()+QPoint(0,-1)); break; case Qt::Key_Left: btn->move(btn->pos()+QPoint(-1,0)); break; case Qt::Key_Right: btn->move(btn->pos()+QPoint(1,0)); break; case Qt::Key_Down: btn->move(btn->pos()+QPoint(0,1)); break; default: break; } //快捷键 if (ev->matches(QKeySequence::SelectAll)){ qInfo()<<"Ctrl + A"; } } void Widget::keyReleaseEvent(QKeyEvent *ev) { } void Widget::focusInEvent(QFocusEvent *ev) { //焦点事件 } void Widget::focusOutEvent(QFocusEvent *ev) { } void Widget::changeEvent(QEvent *ev) { //qInfo()<<__FUNCTION__; } void Widget::contextMenuEvent(QContextMenuEvent *ev) { qInfo() << "报告长官,请求弹出上下文菜单,也就是右键菜单!" << "请弹出在\n" << "全局坐标:"<<ev->globalPos() << "局部坐标:"<<ev->pos() << "位置"; } void Widget::dragEnterEvent(QDragEnterEvent *ev) { //拖拽 ev->acceptProposedAction(); //接受拖拽 auto thing=ev->mimeData(); //qInfo()<<thing->text(); } void Widget::dropEvent(QDropEvent *ev) { const QMimeData* thing=ev->mimeData(); //qInfo()<<thing->text(); } void Widget::timerEvent(QTimerEvent *ev) { //处理定时器事件 if (ev->timerId()==timeId){ //qInfo()<<__FUNCTION__<<timeId; } } bool Widget::event(QEvent *ev) { //在此来控制相应事件的处理 switch (ev->type()) { case QEvent::Type::MouseButtonPress: qInfo()<<"Widget::event::MouseButtonPress"; break; default: break; } return QWidget::event(ev); //事件分发 } void Widget::customEvent(QEvent *ev) { switch (ev->type()) { case EventType::My_Event: { auto cev=static_cast<CustomEvent*>(ev); cev->show(); break; } default: break; } } void Widget::testEventDiffuse() { //事件传播 auto btn=new Button("事件传播",this); btn->setGeometry(10,10,100,30); } void Widget::testTimer() { QTimer* time=new QTimer(this); //1. 方法一 //connect(time,&QTimer::timeout,this,&Widget::onTimerHandle); //2. 方法二: 推荐! time->callOnTimeout(this,&Widget::onTimerHandle); //绑定槽函数 time->start(1000); //每隔1s进行一次 //只会触发一次的定时器 QTimer::singleShot(5000,[](){qInfo()<<"我只会被调用一次!";}); timeId = startTimer(2000); //QObject的定时器 } void Widget::testDIYEvent() { auto btn=new QPushButton("发送事件",this); connect(btn,&QPushButton::clicked,this,[=](){ //1. 使用sendEvent发送事件: 栈区变量,堆区需要delete CustomEvent ev1(18,"于良浩"); QApplication::sendEvent(this,&ev1); //2. 使用postEvent发送事件:只能是堆区变量,栈区无效 auto ev2=new CustomEvent(20,"喜羊羊"); QApplication::postEvent(this,ev2); }); } void Widget::onTimerHandle() { //qInfo()<<__FUNCTION__<<cnt++; }
# main.cpp
-----------
#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。