Benny H. Benny H. - 11 months ago 68
Python Question

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

In a separate thread, I check for information in the pySerial Buffer (Infinite Loop). If new information is available, I'd like to show that Input in a

. After Googling on the subject it turns out doing Gtk -Stuff inside threads is a killer. Random errors will appear and so on, which was also a problem I ran into.

I decided to use a queue to synchronize the thread with the GUI. Putting information into a queue is pretty simple, but how do I suppose to check in the mainloop if there are any entries in the queue?

Some kind of event would be nice, which triggers if any new information is available.

Is there anything of that kind? Maybe a function exists to implement custom python code into the GTK3+ mainloop?

Answer Source

To successfully periodically update the GUI from events in a thread, we cannot simply use threading to start a second process. Like you mention, it will lead to conflicts.

Here is where GObject comes in, to, as it is put in 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

When we replace gobject.threads_init() by GObject.threads_init() and gobject.idle_add by GObject.idle_add(), we pretty much have the updated version of how to run threads in a Gtk application. A simplified example, showing an increasing number of Monkeys in your textfield. See the comments in the code:

Example, counting an updated number of monkeys in your 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()

        scrolledwindow = Gtk.ScrolledWindow()
        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")

        # 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
        # 3. Start the thread

    def counting_monkeys(self):
        # replace this with your thread to update the text
        n = 1
        while True:
            newtext = str(n)+" monkey" if n == 1 else str(n)+" monkeys"
                self.textbuffer.set_text, newtext,
            n += 1

def run_gui():
    window = InterFace()
    # 4. this is where we call GObject.threads_init()