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

时间:2016-09-18 09:17:27

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

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

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

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

有什么类似的吗?也许存在一个函数来实现自定义python代码到GTK3 + mainloop?

2 个答案:

答案 0 :(得分:3)

要成功定期从线程中的事件更新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应用程序中运行线程的更新版本。一个简化的示例,显示文本字段中越来越多的猴子。请参阅代码中的注释:

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

enter image description here ... enter image description here ... enter image description here

#!/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()

答案 1 :(得分:1)

找到解决方案:

  

GObject.timeout_add

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

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

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

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