我目前正在开发 small markdown-editor ,但我有一个问题:
虽然速度非常快,但 Markdown 模块无法发挥魔力,而且由于每次更改时它都会处理整个文本,因此程序将变得无响应,例如按住退格键。
如何使用线程(子进程、QThread)来实现类似以下内容?
- 在处理过程中更改文本时,作业将被推送到队列中。如果队列已包含作业,则该作业将被新作业替换。
- 更改文本且队列中没有作业时,将执行新作业。
- 当作业完成时,队列中的作业将被执行。如果没有,我们就完成了。
澄清
我并不想要上面的算法,而是一种实现持续但不缓慢的渲染类型的方法。我想在“后台”触发渲染作业,但一次一个,确保始终执行最新的渲染作业。
- 用户开始更改文本。
- 当前文本的渲染作业在另一个线程中启动。
- 一旦完成并且渲染的 HTML 替换了旧的 HTML,就会确定是否仍然需要渲染,即刚刚渲染的是最新文本还是较旧的版本。
- 如果文本和渲染仍然不同步,请转到 2。
请注意,澄清上面的算法是实现此目的的一种方法,其中 3.(异步性的确定)被实现为 1-thread-queue。另请注意,只有最新的工作才真正需要完成,但我也希望完成随机的中间工作,以防止一次插入大量新输入的内容。
最佳情况:在快速、连续地输入大型文档时,渲染 View 每隔几个单词左右就会更新一次,而编辑器始终保持响应。
最佳答案
对我来说,这看起来像是 QThreadPool
和 QRunnable
的工作:
- 创建一个派生自
QRunnable
的类,描述“渲染作业” - 创建一个
QThreadPool
,并将maxThreadCount
设置为 1(非常重要,因为一次只能有一个渲染线程)。 - 每次文本更改时,创建“渲染作业”类的实例(可能启用了
autoDelete
),并使用QThreadPool.start() 将其添加到线程池
。QThreadPool
维护自己的待处理作业队列,因此,如果当前有另一个作业正在运行,新作业也不会丢失。在这种情况下,一旦当前作业完成,就会执行它。
要限制创建的渲染作业的数量,您不应直接在 .textChanged
信号上启 Action 业,而应通过 QTimer
间接启 Action 业。每次文本更改时,如果计时器实际发出 .timeout
,则以 500 毫秒的超时时间重新启动计时器,并且仅启动新的“渲染作业”。
在实现“渲染作业”类时,请记住不要直接访问任何 GUI 类。相反,在“渲染作业”类中定义一个信号,渲染完成后,该信号以渲染的 HTML 字符串作为参数发出,并将其与显示小部件的 .setText()
连接。
这并不完全是您所要求的,因为正在运行的渲染作业永远不会中断,而是始终完成。因此,如果文本变化非常快(例如用户按住退格键),预览可能会在一定程度上不同步。但是,我认为这是解决您的问题的最简单、最直接的解决方案,因为它不需要锁定、同步或跟踪当前正在运行的作业。每个作业都以“即发即忘”的方式真正异步运行。
关于python - PyQt:使用线程访问对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6576831/