【VS Code 与 Qt6】运用事件过滤器批量操作子级组件

这篇具有很好参考价值的文章主要介绍了【VS Code 与 Qt6】运用事件过滤器批量操作子级组件。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

如果某个派生自 QObject 的类重写 eventFilter 方法,那它就成了事件过滤器(Event Filter)。该方法的声明如下:

virtual bool eventFilter(QObject *watched, QEvent *event);

watched 参数是监听事件的对象,即事件的接收者;event 参数当然就是待处理的事件了。事件过滤器(也可以翻译为“筛选器”)可在接收者之前拦截事件,处理完毕后还可以决定是否把事件转发给接收者。如果不想转发给事件接收者,就返回 true;若还想让事件继续传播就返回 false。

这玩意儿最有益的用途就是:你的顶层窗口上有 K 个子级组件(正常情形是 QWidget 的子类),如果组件没有定义你想用的信号,只能通过处理事件的途径解决,可你又不想只为了处理一个事件就派生一个类(比如,QLabel组件在鼠标悬浮时做点事情),就可以用上事件过滤器了。顶层窗口类重写 eventFilter 方法,拦截发往子组件的事件(如mouseMove)直接处理,这样能节省 N 百行代码。

重写了 eventFilter 方法的类就成了事件的过滤者,而调用 installEventFilter 方法安装过滤器的类才是事件的原始接收者。就拿上文咱们举的 QLabel 组件的例,假设顶层窗口的类名是 DuckWindow,那么,DuckWindow 重写 eventFilter 方法,它就是事件的拦截者;而 QLabel 组件就是事件的原始接收者,所以,调用 installEventFilter 方法的是它。即 QLabel::installEventFilter( DuckWindow )。

不知道老周这样说大伙伴们能否理解。就是负责过滤事件的对象重写 eventFilter 方法;被别人过滤的对象才调用 installEventFilter 方法。

我们用示例说事。下面咱们要做的练习是这样的:

我定义了一个类叫 MyWindow,继承 QWidget 类,作为顶层窗口。然后在窗口里,我用一个 QHBoxLayout 布局,让窗口内的子级组件水平排列。但每个子组件的颜色不同。常规做法是写个自定义组件类,从构造函数或通过成员函数传一个 QColor 对象过去,然后重写 paintEvent 方法绘图。这种做法肯定没问题的。但是!我要是不想写自定义类呢,那就得考虑事件过滤器了,把 paintEvent 事件过滤,直接用某颜色给子组件画个背景就行了。

头文件声明 MyWindow 类。

#ifndef MYWIN
#define MYWIN

#include <QWidget>
#include <QHBoxLayout>
#include <QPainter>
#include <QEvent>
#include <QColor>
#include <QRect>

class MyWindow : public QWidget
{
    Q_OBJECT
public:
    MyWindow(QWidget* parent=nullptr);
    bool eventFilter(QObject *obj, QEvent *event) override;
private:
    // 私有成员,画痘痘用的
    void paintSomething(QPainter *p, const QColor &color, const QRect &paintRect);
    // 布局
    QHBoxLayout *layout;
    // 三个子级组件
    QWidget *w1, *w2, *w3;
}; 

#endif

这里提一下这个 eventFilter 方法,这厮声明为 public 和 protected 都是可行的。老周这里就声明为 public,与基类的声明一致。

paintSomething 是私有方法,自定义用来画东西的。有伙伴们会问:QPainter 的 paintDevice 不是可以获取到绘图设置(这里指窗口或组件)的大小的矩形区域吗,为啥要从参数传个 QRect?因为这个 rect 来自 QPaintEvent 对象的事件参数,它指的可不一定窗口/组件的整个矩形区域。如果是局部重绘,这个矩形可能就是其中一小部分区域。所以,咱们用事件传递过来的矩形区域绘图。

窗口布局用的是 QHBoxLayout,非常简单的布局方式,子级组件在窗口上水平排列。

下面代码实现构造函数,初始化各个对象。

MyWindow::MyWindow(QWidget *parent)
    : QWidget(parent)
{
    // 初始化
    layout = new QHBoxLayout;
    this->setLayout(layout);
    w1 = new QWidget(this);
    w2 = new QWidget(this);
    w3 = new QWidget(this);
    layout->addWidget(w1);
    layout->addWidget(w2);
    layout->addWidget(w3);
    // 安装事件过滤器
    w1->installEventFilter(this);
    w2->installEventFilter(this);
    w3->installEventFilter(this);
}

只有在被拦截的对象上调用 installEventFilter 方法绑定过滤器后,事件过滤器才会生效。此处,由于 MyWindow 类重写了 eventFilter 方法,所以过滤器就是 this。

下面是 eventFilter 方法的实现代码,只过滤 paint 事件即可,其他传给基类自己去玩。

bool MyWindow::eventFilter(QObject *obj, QEvent *event)
{
    // 如果是paint事件
    // 这里“与”判断事件接收者是不是在那三个子组件中
    // 防止有其他意外对象出现
    // 不过这里不会发生,因为只有install了过滤器的对象才会被拦截事件
    if(event->type() == QEvent::Paint
        && (obj==w1 || obj==w2 || obj==w3))
    {
        QPaintEvent* pe = static_cast<QPaintEvent*>(event);
        QWidget* uiobj = static_cast<QWidget*>(obj);
        QPainter painter;
        // 注意这里,绘图设备不是this了,而是接收绘图事件的对象
        // 由于它要求的类型是QPaintDevcie*,所以要进行类型转换
        // 转换后的uiobj变量的类型是QWidget*,传参没问题
        painter.begin(uiobj);
        if(w1 == uiobj)
        {
            // 红色
            paintSomething(&painter, QColor("red"), pe->rect());
        }
        if(w2 == uiobj)
        {
            // 橙色
            paintSomething(&painter, QColor("orange"), pe->rect());
        }
        if(w3 == uiobj)
        {
            // 紫色
            paintSomething(&painter, QColor("purple"), pe->rect());
        }
        painter.end();
        return true;
    }
    return QWidget::eventFilter(obj, event);
}

拦截并处理了 paint 事件后,记得返回 true,这样事件就不会传给目标对象了(咱们帮它处理了,不必再重复处理,毕竟 QWidget 类默认的 paint 事件是啥也不做)。

下面代码是 paintSomething 方法。只是画了颗巨型青春痘……哦不,是一个椭圆。

void MyWindow::paintSomething(QPainter *p, const QColor &color, const QRect &paintRect)
{
    // 设置画刷
    p->setBrush(QBrush(color));
    // 无轮廓
    p->setPen(Qt::NoPen);
    // 画椭圆
    p->drawEllipse(paintRect);
}

setPen中设定 NoPen 是为了在绘制圆时去掉轮廓,默认会画上轮廓线的。

最后,该到 main 函数了。

int main(int argc, char **argv)
{
    QApplication app(argc,argv);
    MyWindow wind;
    // 窗口标题
    wind.setWindowTitle("干点杂活");
    // 调整窗口大小
    wind.resize(321, 266);
    wind.show();
    return QApplication::exec();
}

运行一下,看,横躺着三颗痘痘,多好看。

【VS Code 与 Qt6】运用事件过滤器批量操作子级组件

 

再来一例,这次咱们拦截的是窗口的 close 事件,当窗口要关闭的时候,咱们输出一条调试信息。

#ifndef 奶牛
#define 奶牛

#include <QObject>
#include <QEvent>

class MyFilter : public QObject
{
protected:
    bool eventFilter(QObject *obj, QEvent *e) override;
};

#endif

这次我们不从任何可视化类型派生,而是直接派生自 QObject 类。这里只是重写 eventFilter 方法,没有用到信号和 cao,所以,可以不加 Q_OBJECT 宏。也就是说咱们这个过滤器是独立用的,不打算加入到 Qt 的对象树中。

下面是实现代码:

bool MyFilter::eventFilter(QObject *obj, QEvent *e)
{
    if(e->type() == QEvent::Close)
    {
        // 此处要类型转换
        QWidget* window = qobject_cast<QWidget*>(obj);
        // 看看这货是不是窗口(有可能是控件)
        if(window->windowFlags() & Qt::Window)
        {
            // 获取这个窗口的标题
            QString title = window->windowTitle();
            // 输出调试信息
            qDebug() << "正在关闭的窗口:" << title;
        }
    }
    // 事件继续传递
    return false;
}

最好返回 false,把事件继续传递给窗口,毕竟窗口可能在关闭时要做一些重要的事,比如保存打开的文件。QWidget 的 WindowFlags 如果包含 Window 值,表明它是一个窗口。

下面直接写main函数。

int main(int argc, char **argv)
{
    QApplication app(argc, argv);

    MyFilter *filter = new MyFilter;
    // 弄三个窗口试试
    QWidget *win1 = new QWidget;
    win1->setWindowTitle("狗头");
    win1->installEventFilter(filter);
    win1->show();

    QWidget *win2 = new QWidget;
    win2->setWindowTitle("鸡头");
    win2->installEventFilter(filter);
    win2->show();

    QWidget *win3 = new QWidget;
    win3->setWindowTitle("鼠头");
    win3->installEventFilter(filter);
    win3->show();

    return QApplication::exec();
    // 可选
    delete filter;
    filter = nullptr;
}

filter 是指针类型,它没有添加到 Qt 对象树中,不会自动清理,在exec返回后用 delete 解决它。在清理时有个好习惯,就是 del 之后把指针变量重设为 null,这样下次再引用变量时不容易产生错误,只要 if(! filter) 就能测出它是空的。

反正程序都退出了,所以此处你也可以让它泄漏一下也无妨。程序挂了后进程空间会被系统收回。

当然,用Qt专供的“作用域”指针也不错,超出作用域自动XX掉。

int main(int argc, char **argv)
{
    QApplication app(argc, argv);

    QScopedPointer<MyFilter> filter(new MyFilter);
    // 弄三个窗口试试
    QWidget *win1 = new QWidget;
    win1->setWindowTitle("狗头");
    win1->installEventFilter(filter.data());
    win1->show();

    QWidget *win2 = new QWidget;
    win2->setWindowTitle("鸡头");
    win2->installEventFilter(filter.data());
    win2->show();

    QWidget *win3 = new QWidget;
    win3->setWindowTitle("鼠头");
    win3->installEventFilter(filter.data());
    win3->show();

    return QApplication::exec();
}

QScopedPointer 通过构造函数引用要封装的对象,要访问被封装的指针对象,可以使用 data 成员。

运行之后,会出现三个窗口。逐个关闭,会输出以下调试信息:

【VS Code 与 Qt6】运用事件过滤器批量操作子级组件

 

---------------------------------------------------------------------------------------

最后老周扯点别的。

咱们知道,Qt官方推出 Python for Qt,名曰 PySide。五月份的时候,老周遇到一个问题:PySide6 无法加载 QML 文件,报的错误是加载 dll 失败,找不到指定的模块。

网上的方法都是不行的,首先,Qt 在版本号相同(均为 6.5.1)的情况下,C++是可以正常加载 QML 文件的。不管是生成资源文件还是直接访问文件均可。但 Python 是报错的。这至少说明我的机器上不缺某些 .dll,不然C++代码应该也报错。

接着,老周想是不是Qt官方编译的有问题,于是,我把自己编译的Qt动态库替换 PySide6 里面的动态链接库。报错依旧,那就排除编译的差异性。

那么,老周就想到,就是 Python 的问题了,3.7 到 3.10 几个版本测试也报错;用不同路径建的虚拟环境也报错;更换 Qt 版本(6.0 到 6.5)同样报错。

这时可以直接肯定就是 Python 的问题了。不是版本号的问题,是 Windows 商店安装的 Python 就会报错,非 Windows 商店安装的就正常。

不过,还得再加一句话:想把 Qt 用得 666 还是用 C++ 吧,用 Python 仅适合初学和娱乐。由于 Rust 可以调用 C/C++ 代码,所以你是可以尝试用 Rust 的。Rust 也不是什么鬼自动内存管理,要 GC 用 .NET 就完事了。Rust 的重点是内存安全。看似挺诱人,官方也把牛吹得入木四分。可用了之后(和用 Go 一样的感觉),是真的没 C++ 好用。C++ 能背负上这么多的历史包袱也不是靠吹的。当然 C++ 内存泄漏也没你想的那么恐怖。养成好习惯,作用域短,存放数据不多的对象就直接栈分配就行了;要在不同代码上下文传递对象,或分配的数据较大的,用指针。指针类型的变量,在不要的时候坚决干掉,然后记得设置变量为 nullptr。养成这些好习惯基本没多大问题。

一般代码你写惯了是不会忘记 delete 的,容易遗漏的是庞大复杂的代码之间会共享某些对象,在很多地方会引用到某对象。于是,码着码着就头晕了,就不记得销毁了。

会被多处引用的对象,可以写上注释提醒自己或别人要清理它,或者加个书签。写完代码后去看看书签列表,就会想起有哪些对象还没销毁。代码写复杂了会容易混,经常会访问已清理的对象。于是,不妨在访问指针变量前 if 语句一下,if (ptr),在 bool 表达式中,若指针类型的变量是空会得到 false,非空为 true。这样就可以避免许多低级错误。

哪怕是不常用指针的语言也不见得不出事。C# 里面你要是访问 null 的变量(VB 是 Nothing)也会报那个很经典的错误:“未将对象引用设置到对象的实例”,就是 NullReferenceException。在.NET 代码中你只要看到这货就得明白肯定有某个为 null 的对象被访问了。

C++ 里面,这样写就能实例化 MyClass 类,只是分配在栈上。

MyClass x;

但在 C# 中,初始值是 null,即未初始化的,初始化你还得 new。哦,顺便想起个事,C# 中数据的隐式基类是 Array,所以它是引用类型,初始值也是 null 的,就算你数据组里面的元素是值类型,但数组自身是引用类型。委托也是引用类型。有的刚入门的同学会以为委托是值类型。

C++函数按“引用”传值的话,一般会用到指针、引用参数,如 int *p、const int &a、const char *w(不能改)等,C# 中如果是引用类型,直接声明就行了,如 MyClass x,值类型可以用 ref 关键字,ref int v。

C# 中 int?、double? 等可以让其成为引用类型,你可以类比 C 中的 int* 等。文章来源地址https://www.toymoban.com/news/detail-479137.html

到了这里,关于【VS Code 与 Qt6】运用事件过滤器批量操作子级组件的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Qt的事件过滤器installEventFilter

    一、介绍 WPF中使用AddHandler来监听事件,那么QT呢?Qt的事件模型是使用一个QObject对象,来监视发送其他QObject对象的事件,在事件到达之前对其进行处理。这里要使用一个函数 Qt助手的解释如下: 在对象上安装一个事件过滤器filterObj。如下: 其中monitoredObj、filterObj都是QObje

    2024年02月12日
    浏览(47)
  • Qt 事件过滤器使用QPainter绘制温度

    Qt的 eventFilter 是一个事件过滤器,可以用来捕获和处理Qt对象的事件。事件过滤器可以被安装到一个对象上,以便在该对象上拦截和处理包含特定类型和内容的事件。下面是 eventFilter 的简单使用介绍: 创建一个类,并继承自 QObject 。这个类将作为事件过滤器的实现。 在该类

    2024年02月12日
    浏览(37)
  • Qt之事件过滤器讲解并且实现快捷键切换鼠标焦点

    现在有一个类似于下方图的ui,用户需要在输入前一行内容后,需要摁下指定案件能够跳转到下一行继续进行输入。 一种更为直接的解决方案是子类化 QLineEdit 并且重新实现鼠标事件 keyPressEvent() ,然后调用 focusNextChild() 。 首先需要创建一个子类MyLineEdit继承于QLineEdit类。然后重

    2024年02月12日
    浏览(57)
  • 【VS Code+Qt6】拖放操作

    由于老周的示例代码都是用 VS Code + CMake + Qt 写的,为了不误导人,在标题中还是加上“VS Code”好一些。 上次咱们研究了剪贴板的基本用法,也了解了叫 QMimeData 的重要类。为啥要强调这个类?因为接下来扯到的拖放操作也是和它有关系。哦,对了,咱们先避开一下主题,关

    2024年02月06日
    浏览(47)
  • web3j的基础用法-6合约的监听器事件Event和过滤器EthFilter,以及NullPointed,调用失败导致的bug解决

    本篇以Uniswap为例(https://uniswap.org/) 合约地址 :0x1f9840a85d5af5bf1d1762f925bdaddc4201f984 (Uni) 监听合约Tranfer事件 调用代码 核心代码实现在这里 之前实验全量区块,导致请求多次失败,是由于个人RPC节点的请求和数据有限,为了测试出结果,从13763721L block到当前,结果毫秒级返

    2024年02月11日
    浏览(50)
  • 【VS Code 与 Qt6】QAction 类的一些事

    QAction 类表示用户命令的一种抽象,包括命令文本、图标、命令触发后要执行的代码。菜单、工具栏按钮往往存在相同的功能,将这些命令独立抽出来,放到 QAction 以象上,可避免编写重复的代码。比如“文件”菜单下有“保存”命令,工具栏上也会有“保存”按钮。因此,

    2024年02月15日
    浏览(55)
  • 【VS Code 与 Qt6】QCheckBox的图标为什么不会切换?

    本篇专门扯一下有关 QCheckBox 组件的一个问题。老周不水字数,直接上程序,你看了就明白。 QCheckBox、QRadioButton、QPushButton 都是 QAbstractButton 的子类,所以这几个家伙都归属于按钮组件。在 QAbstractButton 类中已定义有 checkable 属性,表示按钮是否支持 check 操作。这种按钮就类似

    2024年02月07日
    浏览(41)
  • jQuery选择器(二)(基本过滤器,内容过滤器,可见过滤器)

    写在前面 jQuery是一个快速、简洁的 JavaScript 框架,是继Prototype之后又一个优秀的 JavaScript 代码库。jQuery的设计宗旨是“WriteLess,DoMore”,即倡导写更少的代码,做 更多的事情。jQuery封装了 JavaScript 常用的功能代码,提供一种简便的 JavaScript 设计模式,优化HTML文档操作、事件

    2024年02月02日
    浏览(54)
  • Gateway自定义过滤器——全局过滤器

    首先,我们要知道全局过滤器其实是特殊路由过滤器(特殊的GatewayFilter),会有条件地作用于所有路由。 为什么要自定义全局过滤器?就好比是看大门的保安大叔,平时主要是做好进出大门外来人员登记即可,但是因为新冠疫情,现在还需要给外来人员测量体温等等。而已有的

    2024年02月16日
    浏览(47)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包