如何从不同线程的事件更新 Gtk.TextView?

How can I update a Gtk.TextView from events from a different thread?

在一个单独的线程中,我检查 pySerial 缓冲区(无限循环)中的信息。如果有新信息可用,我想在 Gtk.TextView 中显示该输入。在对这个主题进行谷歌搜索后,发现在线程中执行 Gtk -Stuff 是一个杀手。会出现随机错误等等,这也是我运行遇到的问题。

我决定使用队列将线程与 GUI 同步。将信息放入队列非常简单,但是如果队列中有任何条目,我该如何检查主循环呢?

某种事件会很好,如果有任何新信息可用就会触发。

有这样的吗?也许存在将自定义 python 代码实现到 GTK3+ 主循环中的函数?

找到解决方案:

GObject.timeout_add

http://www.pygtk.org/pygtk2reference/gobject-functions.html#function-gobject--timeout-add

此 GObject 函数每 X 毫秒执行一次对自己函数的回调。在这里找到了一个简单的解释:

https://gist.github.com/jampola/473e963cff3d4ae96707

所以我可以每 X 毫秒处理从队列中收集的所有信息。处理线程的好方法!

要通过线程中的事件成功地定期更新 GUI,我们不能简单地使用 threading 来启动第二个进程。就像你说的那样,它会导致冲突。

这是 GObject 出现的地方,因为它被放入 this (slightly outdated) link :

call gobject.threads_init() at applicaiton initialization. Then you launch your threads normally, but make sure the threads never do any GUI tasks directly. Instead, you use gobject.idle_add to schedule GUI task to executed in the main thread

当我们将 gobject.threads_init() 替换为 GObject.threads_init() 并将 gobject.idle_add 替换为 GObject.idle_add() 时,我们几乎已经有了如何在一个线程中 运行 线程的更新版本Gtk 应用程序。一个简化的示例,在您的文本字段中显示越来越多的猴子。看代码中的注释:

例如,计算 TextView 中猴子的更新数量:

......

#!/usr/bin/env python3
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, GObject
import time
from threading import Thread

class InterFace(Gtk.Window):

    def __init__(self):

        Gtk.Window.__init__(self, title="Test 123")

        maingrid = Gtk.Grid()
        maingrid.set_border_width(10)
        self.add(maingrid)

        scrolledwindow = Gtk.ScrolledWindow()
        scrolledwindow.set_hexpand(True)
        scrolledwindow.set_vexpand(True)
        scrolledwindow.set_min_content_height(50)
        scrolledwindow.set_min_content_width(150)
        maingrid.attach(scrolledwindow, 0,0,1,1)

        self.textfield = Gtk.TextView()
        self.textbuffer = self.textfield.get_buffer()
        self.textbuffer.set_text("Let's count monkeys")
        self.textfield.set_wrap_mode(Gtk.WrapMode.WORD)
        scrolledwindow.add(self.textfield)
        
        # 1. define the tread, updating your text
        self.update = Thread(target=self.counting_monkeys)
        # 2. Deamonize the thread to make it stop with the GUI
        self.update.setDaemon(True)
        # 3. Start the thread
        self.update.start()

    def counting_monkeys(self):
        # replace this with your thread to update the text
        n = 1
        while True:
            time.sleep(2)
            newtext = str(n)+" monkey" if n == 1 else str(n)+" monkeys"
            GObject.idle_add(
                self.textbuffer.set_text, newtext,
                priority=GObject.PRIORITY_DEFAULT
                )
            n += 1
        
def run_gui():
    window = InterFace()
    # 4. this is where we call GObject.threads_init()
    GObject.threads_init()
    window.show_all()
    Gtk.main()

run_gui()