Qt:从非gui线程设置覆盖游标



前一段时间我写了一个小RAII类来包装QApplication上的setOverrideCursor()restoreOverrideCursor()方法。构造该类将设置游标,析构函数将恢复它。由于重写游标是一个堆栈,所以这工作得很好,如:

{
   CursorSentry sentry;
   // code that takes some time to process
}

后来,我发现在某些情况下,处理代码有时会花很长时间来处理(比如超过半秒),而在其他情况下,它几乎是瞬间的(因为缓存)。很难事先确定哪种情况会发生,所以它仍然总是通过创建一个CursorSentry对象来设置等待游标。但是,这可能会导致令人不快的"闪烁",光标将迅速从等待光标转向正常光标。

所以我认为我是聪明的,我添加了一个单独的线程来管理游标覆盖。现在,当生成CursorSentry时,它向游标线程发出一个请求以进入等待状态。当它被销毁时,它告诉线程返回到正常状态。如果CursorSentry的存活时间超过一定时间(50毫秒),则处理游标更改并设置覆盖游标。否则,更改请求将被丢弃。

问题是,游标线程在技术上不能改变游标,因为它不是GUI线程。在大多数情况下,它确实可以工作,但有时,如果我真的很不幸,当GUI线程与其他一些X11调用混合在一起时,会发生更改光标的调用,并且整个应用程序会陷入死锁。这通常只发生在GUI线程几乎在光标线程决定设置覆盖游标的同一时刻完成处理的情况下。

那么,有人知道从非gui线程设置覆盖游标的安全方法吗?请记住,大多数情况下,GUI线程将忙于处理内容(这就是为什么需要等待游标),所以我不能只是将事件放入GUI线程队列中,因为它将不会被处理,直到为时已晚。此外,将我正在谈论的处理移动到一个单独的线程是不切实际的,因为这是在绘制事件期间发生的,并且当它完成时需要做GUI工作(弄清楚要画什么)。

在设置覆盖游标时添加延迟的任何其他想法也会很好。

我认为除了信号槽连接到GUI线程之后的qApp->processEvents()调用之外,没有任何其他方法,但就像你说的,当GUI线程被捆绑时,这可能不会很好地工作。

QCoreApplication::processEvents的文档也有一些长事件处理的推荐用法:

这个函数重载procesevents()。处理暂挂事件调用线程maxtime毫秒或直到没有更多要处理的事件,以较短的为准。

你可以调用这个函数偶尔,当你的程序忙于做一个长时间的操作(例如:复制文件)。

调用此函数只处理事件调用线程。

如果可能的话,在paint事件中分解长时间的调用,并定期检查它花费了多长时间。在这些检查中,让它在GUI线程中设置覆盖游标。

通常QProgressBar可以很好地向用户传达相同的信息。

另一个很有帮助的选择是在GUI线程之外渲染到QImage缓冲区,然后在完成后将其发布到GUI。

最新更新