【问题标题】:What is the functional difference between these two qt signal-slot connections?这两个 qt 信号槽连接之间的功能区别是什么?
【发布时间】:2018-02-22 16:43:05
【问题描述】:

我正在使用 Qt 5.9.2 和 Visual Studio 2015 和 QtDesigner 来编写 Windows GUI 应用程序。我尝试通过以下调用连接我的一项操作:

connect(ui.myAction, &QAction::triggered, memberPtrToObjX_, &ClassX::Run);

但是,在单击菜单栏中的 myAction 后,并不总是触发 ClassX::Run。调查这个问题,我想,使用 lambda 语法的相同信号槽连接是有效的:

connect(ui.myAction, &QAction::triggered, [this](bool run) { memberPtrToObjX_->Run(run); });

我很确定,这两个调用在语法上都是正确的。除了两个调用都返回一个有效的QMetaObject::Connection,如果我保存返回值并使用运算符 bool() 进行检查。

显然我可以坚持使用有效的 lambda 版本,但我很困惑,更愿意知道我的“解决方案”背后的原因。这两个调用之间是否有任何功能差异,可以解释不同的行为?

【问题讨论】:

  • 我看到的唯一区别是:在第一个版本中,使用memberPtrToObjX_ 中的当前指针(在connect() 时)(总是)。第 2 版在触发信号时使用memberPtrToObjX_ 中的当前指针。如果在connect() 之后修改memberPtrToObjX_ - 这会有所不同。
  • 第二种形式总是会产生Qt::DirectConnection而不是Qt::AutoConnection类型的连接,这意味着发射器和接收器必须始终在同一个线程上(一般来说)。
  • @Scheff 谢谢!这是一个很好的观点,从理论上回答了我的问题。不幸的是,它不是这里的解决方案。事实上,我很确定,指针没有改变。我通过将我的 lambda 调用更改为 connect(ui.myAction, &QAction::triggered, [memberPtrToObjX_](bool run) { memberPtrToObjX_->Run(run); }); 非常满意。现在memberPtrToObjX_ 应该被复制捕获,类似于第一个版本。但仍然始终触发 ClassX::Run。
  • 你能提供一个minimal reproducible example吗?如果memberPtrToObjX_没有修改并且它的指针lives in同一个线程,第一个和第二个版本应该没有区别。 (在这种情况下,第一个connect() 会将Qt::AutoConnection 解析为Qt::DirectConnection。这实际上是@G.M. 已经暗示的。)否则,(指针“住在”另一个线程中)然后......我相信,在这个情况下,第二个版本是一个“定时炸弹”,似乎效果更好。
  • 出于好奇:你确定checked arg. (在QAction::triggered(bool checked))没有任何意想不到的效果吗? (我只是问,因为我不知道您的QAction 是如何配置的,以及您在ClassX::Run(bool) 中对checked 做了什么。)尽管如此,它仍然无法解释connect()s 的不同行为......

标签: qt lambda connect signals-slots


【解决方案1】:

QObject::connect() 的两个调用(由 OP 暴露)在调用 connect() 后修改 this->memberPtrToObjX_ 的情况下表现不同。

第一个

connect(ui.myAction, &QAction::triggered, memberPtrToObjX_, &ClassX::Run);

致电
QMetaObject::Connection QObject::connect(<br> const QObject *sender, PointerToMemberFunction signal,<br> const QObject *receiver, PointerToMemberFunction method,<br> Qt::ConnectionType type = Qt::AutoConnection).

从 sender 对象中的 signal 到 receiver 中的 method 创建给定类型的连接> 对象。返回可用于稍后断开连接的连接句柄。

因此,this-&gt;memberPtrToObjX_ 中的当前指针被连接为信号接收器。如果this-&gt;memberPtrToObjX_在connect()之后被修改,这对信号连接没有任何影响。

第二个

connect(ui.myAction, &QAction::triggered, [this](bool run) { memberPtrToObjX_->Run(run); });

致电
QMetaObject::Connection QObject::connect(<br> const QObject *sender, PointerToMemberFunction signal,<br> Functor functor).

创建一个从sender对象中的signal到functor的连接,并返回连接的句柄。

因此,(后面的函子)lambda 被连接为接收器。 lambda 在执行时(即触发信号时)解析 this-&gt;memberPtrToObjX_ 中的指针。


第二个区别(最初在 G.M. 的评论中发现)是连接类型:

第一个版本使用默认值Qt::AutoConnection(因为它没有明确定义)。带有 lambda 的版本始终使用 Qt::DirectConnection。

如果this-&gt;memberPtrToObjX_ 中的指针不在同一线程中"live",则会出现差异。在这种情况下,Qt::AutoConnection 被解析为 Qt::QueuedConnection 而不是 Qt::DirectConnection。

我假设this-&gt;memberPtrToObjX_ 中的指针将“生活”在同一个QThread 中。如果不是,则第二个版本(带有 lambda)变得非常有问题,因为它调用对象的成员函数“生活”在不同的线程中(此时很难判断该线程在做什么)。这只是似乎效果更好,但很可能是一个“定时炸弹”。

【讨论】:

  • 您想补充一些关于Qt::AutoConnection 与Qt::DirectConnection 的区别的信息吗?然后我很乐意接受您的回答,因为它清楚地回答了发布的问题,并且可能在其他情况下有用。解决我的实际问题是我自己的独立任务。
猜你喜欢
  • 2011-07-14
  • 2016-11-04
  • 2021-09-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-02-15
  • 1970-01-01
  • 2016-10-22
相关资源
最近更新 更多