【问题标题】:Is it possible to re-paint/update content in a Qt GUI application although the main thread is blocked?尽管主线程被阻塞,是否可以在 Qt GUI 应用程序中重新绘制/更新内容?
【发布时间】:2017-09-01 10:46:26
【问题描述】:

有很多关于在 GUI 中旋转或移动繁忙指示器的讨论(例如 here),但我找不到一个明确指出不可能重新绘制/更新任何内容的讨论主线程被阻塞时的 GUI 应用程序。

这个问题其实是一个笼统的问题,不一定和Qt GUI应用直接相关。

当 GUI 的主线程正在执行阻塞操作时,不会处理任何事件,也不会重新绘制任何内容。有两种“推荐”的方法:

  1. 使用工作线程
  2. 分块工作并“不那么频繁”更新 UI

问题在于,有些操作根本无法移至工作线程或任何其他异步机制。最好的例子是 UI 本身的序列化和反序列化。在这两种情况下,用户不得在 UI 中愉快地单击并更改设置,而设置、属性等从小部件中获取(在保存期间)或应用于小部件(在加载期间)。这对于线程方法以及分割成块都是正确的。 编辑: 另一个例子是将样式表应用于完整的 GUI。想象一下,用户想要选择一个“暗”方案。所有小部件都需要更新,当前可见的小部件需要重新绘制。在此步骤期间,事件循环无法运行。类似的讨论可以在here找到。

就我而言,我正在使用嵌入式设备,所以还有另一种方法:

  1. 直接覆盖帧缓冲区的特定区域

这种方法感觉很丑陋,会带来很多有问题的场景,而且还需要大量的调试。

最后但悲伤的方法:

  1. 根本不使用任何移动/更新内容,只显示静态内容,例如“...进行中...”

好吧,这只是悲伤......

您同意这些意见吗?有谁知道通常与 Qt 无关的不同方法或概念?

【问题讨论】:

  • 我不确定与 Qt 无关的一般情况,但 Qt 中有 processEvents 函数可以满足您的需求。
  • processEvents 重新进入事件循环,是不必要的。如果你可以使用processEvents,你已经可以分块操作了。
  • 这个问题太宽泛了,是 XY 问题的一个糟糕案例。您正在讨论具体解决方案,而没有详细描述您正在解决的具体问题。如果您具体地询问如何以提供良好用户体验的异步方式很好地反序列化 UI - 只需询问这个问题,而不是询问一些定义不明确的“一般”问题需要涵盖十几个单独的问题。 您遇到问题的特定场景是什么?确切地说反序列化:每个人在使用该术语时都意味着 10 种不同的东西。
  • @KubaOber:感谢您的回复。你似乎真的不喜欢我的问题和我问的风格。我描述的是一个场景,也许我的例子并不完美,但这个场景只是存在。只需将其作为您需要解决的数学问题的描述您无法更改其描述:-) 这个问题是关于 GUI 编程概念的。当然它有点宽泛:-)
  • @BeardedBeaver:感谢您的回复,但 processEvents 并没有做我想要的。它只是推动事件循环的执行,这是不推荐的,此外也不能解决问题。当您有较长的操作在 Qt 本身(或 GUI 框架本身)中执行慢速操作时,您无法调用 processEvents,没有可以拦截的“块”(正如 Kuba Ober 指出的那样)。

标签: qt user-interface busyindicator


【解决方案1】:

问题在于,有些操作根本无法移至工作线程或任何其他异步机制。

我不能同意。这些操作应该分为阻塞和非阻塞操作。所有阻塞的东西都应该异步处理,或者,如果没有可用的非阻塞 API,则交给工作线程。

最好的例子是 UI 本身的序列化和反序列化。

我觉得这是一个特别糟糕的例子,主要是因为我还没有遇到阻塞 GUI 的需要,并且不需要序列化。

在这两种情况下,用户不得在保存或加载设置、属性等时愉快地在 UI 中单击并更改设置。

小部件的构造和销毁应该非常快,如果这就是您所说的“反序列化”UI 的话。回想一下阻塞 I/O 和长解析已在另一个线程中完成。几乎所有的 Qt 小部件都可以快速设置,而那些不是必须的,你别无选择,只能忍受。如果您有自己的小部件在其构造函数或事件处理程序中执行诸如磁盘或注册表访问之类的阻塞操作(存在大量此类“代码”),请修复它们。

如果您只是在谈论设置小部件值,那么这又是超级快速的,并且可以一次性完成。您可能需要一个视图模型来在视图(小部件、QML 视图或QAbstractItemView)和数据源之间进行异步接口。

磁盘 I/O 和磁盘表示的解析/输出属于单独的工作程序。创建内部表示后,可以将其传递给 gui 线程以构建小部件树或填充界面。

您应该使用QAbstractItemModel 或类似接口实现线程安全模型,并将它们填充到工作线程中,并让 GUI 线程实时响应更新。

如果您希望向用户指示界面的一部分尚不可用,例如在填充模型时,您可以通过overlay 执行此操作,确保您暂时禁用对覆盖下的小部件的关注。这并不难,如果您的整个问题可以简化为“如何使 UI 的一部分在修改时无法访问”,我会觉得很有趣。

缺少的关键是 UI 应处理异步响应模型更改其状态。 就我而言,加载完全填充模型所需的数据可能需要一个小时。这并不意味着您的应用程序应该没有响应。只需将 UI 中无法交互的部分设置为无法交互即可。确保需要配置数据的应用程序的其他部分同样不可访问或处于部分可用状态,一旦配置可用,将异步恢复为完整状态。

【讨论】:

  • -1 表示“我不能同意”。你不能不同意我的设置是什么样的 :-) 你告诉我我的嵌入式平台的性能虽然你不知道。例如,应用应用程序范围的样式和重新绘制非常昂贵。问题是是否存在可以处理我的有限场景的概念。也许我可以举出更好的例子。你应该问过;-)
  • 例如,一个解决方案是一个单独的应用程序悬停在另一个应用程序之上,仅在实际 GUI 应用程序繁忙时才显示。通信可以通过某种网络通信来完成。这似乎很难看,但实际上可以解决问题:-)
  • “例如,应用应用程序范围的样式和重新绘制非常昂贵。”我们在这里谈了多久?您多久更改一次应用程序的样式?你做错了其他事情,但如果你不告诉我们到底发生了什么,就不可能说出来。我的观点是,当你做正确的事情时,它不应该是一个问题。这就是为什么我不同意你所声称的问题:正如你自己所说,你还有其他问题需要先解决,但除非你帮助我们,否则我们无法帮助你。
  • 第一次在stackoverflow上遇到这样的讨论行为:-(问题直接讲了主线程被阻塞了。不管为什么被阻塞。我问是因为我想找一个这种场景的概念。我认为我的“第二个应用程序”方法应该让我的观点更加清晰:-)
  • “为什么被阻止并不重要”。这很重要,因为您已经认为它已被阻止。我不买它。让每个人相信你遇到的问题是无法克服的,必须按原样解决,这应该不难。任何解决方法都可能比您真正遇到的阻塞主线程的任何问题更难看,即使这样,它们也可能需要更改架构以跨线程分割小部件绘画等(是的,这是可能的)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-01-02
  • 1970-01-01
  • 1970-01-01
  • 2018-10-12
  • 2021-03-11
  • 1970-01-01
相关资源
最近更新 更多