- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
如果某个派生自 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(); }
运行一下,看,横躺着三颗痘痘,多好看.
。
再来一例,这次咱们拦截的是窗口的 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 成员.
运行之后,会出现三个窗口。逐个关闭,会输出以下调试信息:
。
--------------------------------------------------------------------------------------- 。
最后老周扯点别的.
咱们知道,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* 等.
最后此篇关于【VSCode与Qt6】运用事件过滤器批量操作子级组件的文章就讲到这里了,如果你想了解更多关于【VSCode与Qt6】运用事件过滤器批量操作子级组件的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我正在努力做到这一点 在我的操作中从数据库获取对象列表(确定) 在 JSP 上打印(确定) 此列表作为 JSP 中的可编辑表出现。我想修改然后将其提交回同一操作以将其保存在我的数据库中(失败。当我使用
我有以下形式的 Linq to Entities 查询: var x = from a in SomeData where ... some conditions ... select
我有以下查询。 var query = Repository.Query() .Where(p => !p.IsDeleted && p.Article.ArticleSections.Cou
我正在编写一个应用程序包,其中包含一个主类,其中主方法与GUI类分开,GUI类包含一个带有jtabbedpane的jframe,它有两个选项卡,第一个选项卡包含一个jtable,称为jtable1,第
以下代码产生错误 The nested query is not supported. Operation1='Case' Operation2='Collect' 问题是我做错了什么?我该如何解决?
我已经为 HA redis 集群(2 个副本、1 个主节点、3 个哨兵)设置了本地 docker 环境。只有哨兵暴露端口(10021、10022、10023)。 我使用的是 stackexchange
我正在 Desk.com 中构建一个“集成 URL”,它使用 Shopify Liquid 模板过滤器语法。对于开始日期为 7 天前而结束日期为现在的查询,此 URL 需要包含“开始日期”和“结束日期
你一定想过。然而情况却不理想,python中只能使用类似于 i++/i--等操作。 python中的自增操作 下面代码几乎是所有程序员在python中进行自增(减)操作的常用
我需要在每个使用 github 操作的手动构建中显示分支。例如:https://gyazo.com/2131bf83b0df1e2157480e5be842d4fb 我应该显示分支而不是一个。 最佳答
我有一个关于 Perl qr 运算符的问题: #!/usr/bin/perl -w &mysplit("a:b:c", /:/); sub mysplit { my($str, $patt
我已经使用 ArgoUML 创建了一个 ERD(实体关系图),我希望在一个类中创建两个操作,它们都具有 void 返回类型。但是,我只能创建一个返回 void 类型的操作。 例如: 我能够将 book
Github 操作仍处于测试阶段并且很新,但我希望有人可以提供帮助。我认为可以在主分支和拉取请求上运行 github 操作,如下所示: on: pull_request push: b
我正在尝试创建一个 Twilio 工作流来调用电话并记录用户所说的内容。为此,我正在使用 Record,但我不确定要在 action 参数中放置什么。 尽管我知道 Twilio 会发送有关调用该 UR
我不确定这是否可行,但值得一试。我正在使用模板缓冲区来减少使用此算法的延迟渲染器中光体积的过度绘制(当相机位于体积之外时): 使用廉价的着色器,将深度测试设置为 LEQUAL 绘制背面,将它们标记在模
有没有聪明的方法来复制 和 重命名 文件通过 GitHub 操作? 我想将一些自述文件复制到 /docs文件夹(:= 同一个 repo,不是远程的!),它们将根据它们的 frontmatter 重命名
我有一个 .csv 文件,其中第一列包含用户名。它们采用 FirstName LastName 的形式。我想获取 FirstName 并将 LastName 的第一个字符添加到它上面,然后删除空格。然
Sitecore 根据 Sitecore 树中定义的项目名称生成 URL, http://samplewebsite/Pages/Sample Page 但我们的客户有兴趣降低所有 URL(页面/示例
我正在尝试进行一些计算,但是一旦我输入金额,它就会完成。我只是希望通过单击按钮而不是自动发生这种情况。 到目前为止我做了什么: Angular JS - programming-fr
我的公司创建了一种在环境之间移动文件的复杂方法,现在我们希望将某些构建的 JS 文件(已转换和缩小)从一个 github 存储库移动到另一个。使用 github 操作可以实现这一点吗? 最佳答案 最简
在我的代码中,我创建了一个 JSONArray 对象。并向 JSONArray 对象添加了两个 JSONObject。我使用的是 json-simple-1.1.jar。我的代码是 package j
我是一名优秀的程序员,十分优秀!