事件过滤器及不规则窗体
事件过滤器及不规则窗体文章目录事件过滤器及不规则窗体一、事件过滤器二、不规则窗体一、事件过滤器有时候, 对象需要查看、 甚至要拦截发送到另外对象的事件。 例如, 对话框可能想要拦截按键事件, 不让别的组件接收到; 或者要修改回车键的默认处理。我们已经知道, Qt 创建了 QEvent 事件对象之后, 会调用 QObject的 event()函数处理事件的分发。 显然, 我们可以在 eve...
事件过滤器及不规则窗体
文章目录
一、事件过滤器
有时候, 对象需要查看、 甚至要拦截发送到另外对象的事件。 例如, 对话框可能
想要拦截按键事件, 不让别的组件接收到; 或者要修改回车键的默认处理。
我们已经知道, Qt 创建了 QEvent 事件对象之后, 会调用 QObject的 event()函数处理事件的分发。 显然, 我们可以在 event()函数中实现拦截的操作。
由于 event()函数是 protected 的, 因此, 需要继承已有类。 如果组件很多, 就需
要重写很多个 event()函数。 这当然相当麻烦, 更不用说重写 event()函数还得小心
一堆问题。 好在 Qt 提供了另外一种机制来达到这一目的: 事件过滤器。
1. 事件过滤器
QObject 有一个 eventFilter()函数, 用于建立事件过滤器。 函数原型如下
virtual bool QObject::eventFilter ( QObject * watched, QEvent * event );
这个函数正如其名字显示的那样, 是一个“事件过滤器”。 所谓事件过滤器, 可以
理解成一种过滤代码。 事件过滤器会检查接收到的事件。 如果这个事件是我们感兴趣的类型, 就进行我们自己的处理; 如果不是, 就继续转发。 这个函数返回一个 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;
setCentralWidget(textEdit);
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" << keyEvent->key();
return true;
}
else
{
return false;
}
}
else
{
// pass the event on to the parent class
return QMainWindow::eventFilter(obj, event);
}
}
- MainWindow 是我们定义的一个类。 我们重写了它的 eventFilter()函数。 为了过滤特定组件上的事件, 首先需要判断这个对象是不是我们感兴趣的组件, 然后判断这个事件的类型。 在上面的代码中,
我们不想让 textEdit 组件处理键盘按下的事件。 所以, 首先我们找到这个组件, 如果这个事件是键盘事件,则直接返回true, 也就是过滤掉了这个事件, 其他事件还是要继续处理, 所以返回 false。 对于其它的组件, 我们并不保证是不是还有过滤器, 于是最保险的办法是调用父类的函数 - eventFilter()函数相当于创建了过滤器, 然后我们需要安装这个过滤器。 安装过滤器需要调用 QObject::installEventFilter()函数。 函数的原型如下:
void QObject::installEventFilter ( QObject * filterObj )
- 这个函数接受一个 QObject *类型的参数。 记得刚刚我们说的, eventFilter()函 数是 QObject的一个成员函数, 因此,
任意 QObject 都可以作为事件过滤器(问 题在于, 如果你没有重写eventFilter()函数, 这个事件过滤器是没有任何作用 的 , 因 为 默 认 什 么 都 不 会 过 滤 ) 。 已 经存 在 的 过 滤 器 则 可 以 通 过 QObject::removeEventFilter()函数移除 - 我 们 可 以 向 一 个 对 象 上 面 安 装 多 个 事 件 处 理 器 , 只 要 调 用 多 次installEventFilter()函数。 如果一个对象存在多个事件过滤器, 那么, 最后一个
安装的会第一个执行, 也就是后进先执行的顺序
bool CustomWidget::event(QEvent *e)
{
if (e->type() == QEvent::KeyPress) {
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(e);
if (keyEvent->key() == Qt::Key_Tab) {
qDebug() << "You press tab.";
return true;
}
}
return QWidget::event(e);
}
现在, 我们可以给出一个使用事件过滤器的版本:
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) {
qDebug() << "You press tab.";
return true;
} else {
return false;
}
}
return false;
}
-
事件过滤器的强大之处在于, 我们可以为整个应用程序添加一个事件过滤器。 记得, installEventFilter()函数是 QObject 的函数, QApplication 或者 QCoreApplication对象都是 QObject 的子类, 因此, 我们可以向 QApplication 或QCoreApplication添加事件过滤器。 这种全局的事件过滤器将会在所有其它特性对象的事件过滤器之前调用。 尽管很强大, 但这种行为会严重降低整个应用程序的事件分发效率。 - 注意,
事件过滤器和被安装过滤器的组件必须在同一线程, 否则, 过滤器将不起作用。
另外, 如果在安装过滤器之后, 这两个组件到了不同的线程, 那么, 只有等到二者重新回到同一线程的时候过滤器才会有效
二、不规则窗体
常见的窗体是各种方形的对话框,但有时候也需要非方形的窗体,如圆形,椭圆甚至是不规则形状的对话框。
实现步骤:
- 新建一个项目, 比如项目名称叫做“ShapeWidget”, 给此项目添加一个类
“ShapeWidget”, 基类选择“QWidget”。 - 为了使该不规则窗体可以通过鼠标随意拖拽, 在类中重定义鼠标事件:
mousePressEvent()、 mouseMoveEvent()、 以及绘制函数 paintEvent() - “ShapeWidget”的构造函数部分是实现该不规则窗体的关键, 添加具体代码如
下
//新建一个 Pixmap 对象
QPixmap pixmap;
//加载图片
pixmap.load(":/new/prefix1/image/sunny.png");
//固定窗口大小, 将窗口大小设置为图片大小
setFixedSize( pixmap.width(), pixmap.height() );
//给窗口去掉边框, 设置窗口的 flags
setWindowFlags(Qt::FramelessWindowHint | windowFlags() );
//设置透明背景
setAttribute(Qt::WA_TranslucentBackground);
- 重新实现鼠标事件和绘制函数
void ShareWidget::mousePressEvent(QMouseEvent *ev)
{
if(ev->button() == Qt::LeftButton)
{
// 求出窗口移动之前的坐标
m_dragPoint = ev->globalPos()-frameGeometry().topLeft();
}
if(ev->button() == Qt::RightButton)
{
// 鼠标右键关闭窗口
close();
}
}
void ShareWidget::mouseMoveEvent(QMouseEvent *ev)
{
if(ev->buttons() & Qt::LeftButton)
{
// 如果是鼠标左键拖动, 移动窗口
move(ev->globalPos() - m_dragPoint);
}
}
void ShareWidget::paintEvent(QPaintEvent *ev)
{
Q_UNUSED(ev)
QPainter painter(this);
// 重新绘制图片
painter.drawPixmap(0, 0, QPixmap(":/ButterFly"));
}
更多推荐



所有评论(0)