【问题标题】:How can I update a Gtk.TextView from events from a different thread?如何从来自不同线程的事件更新 Gtk.TextView?
【发布时间】:2016-09-18 09:17:26
【问题描述】:

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

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

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

有类似的吗?也许有一个函数可以在 GTK3+ 主循环中实现自定义 python 代码?

【问题讨论】:

    标签: python user-interface queue gtk3 python-multithreading


    【解决方案1】:

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

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

    在应用程序初始化时调用 gobject.threads_init()。然后你正常启动你的线程,但确保线程从不直接执行任何 GUI 任务。相反,您使用 gobject.idle_add 来安排 GUI 任务在主线程中执行

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

    例如,计算 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()
    

    【讨论】:

    • 这比玩白队列更容易,非常感谢!
    【解决方案2】:

    找到解决方案:

    GObject.timeout_add

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

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

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

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

    【讨论】:

    • gtk3也一样,只需要导入GObject而不是gobject,调用GObject.timeout_add函数也一样
    猜你喜欢
    • 1970-01-01
    • 2023-03-06
    • 1970-01-01
    • 2017-04-10
    • 2010-10-18
    • 1970-01-01
    • 1970-01-01
    • 2013-05-30
    相关资源
    最近更新 更多