How to modify GTK interface from another thread?

I need to launch an infinite loop without blocking the app. So I’m using another thread for this loop. But when I’m trying to modify content of the interface, the app spontaneously crashes with this error Gtk:ERROR:../gtk/gtkcssnode.c:322:lookup_in_global_parent_cache: assertion failed: (node->cache == NULL).

As I find out, this is happening because GTK doesn’t allow to modify interface outside main thread.
Because of Gtk.MediaControls doesn’t have a built-in signal for detecting timestamp changes, I’m forced to use infinite loop.

The code:

    def highlight_text(self, *args):
        attributes = Pango.AttrList().from_string("0 -1 weight bold")
        attributes_default = Pango.AttrList().from_string("")
        print(attributes.to_string())
        while shared.shared.is_editing_mode == True:
            try:
                childs = []
                for child in self.lyrics_lines_box:
                    childs.append(child)
                timestamps = []
                for child in childs:
                    timestamps.append(arg_timing_parser(child.get_text()))
                    child.set_attributes(None)

                current_index = 0

                timestamp = self.controls.get_media_stream().get_timestamp() // 1000
                for i in range(len(timestamps) - 1):
                    if timestamp >= timestamps[i] and timestamp < timestamps[i+1]:
                        childs[i].set_attributes(attributes)
                        break
                time.sleep(0.01)

            except (TypeError, AttributeError):
                pass

    def highlight_text_init(self, *args):
        shared.shared.is_editing_mode = True
        highlight_thread = threading.Thread(target=self.highlight_text, args=self)
        highlight_thread.start()

You can schedule UI updates from within the thread by using GLib.idle_add_once().

You’re not forced at all.

Gtk.MediaStream:timestamp is a property, which means you can connect to the “notify” signal to get changes in the value:

def timestamp_changed(self, media_stream, _):
    timestamp = media_stream.get_timestamp()
    # ...

media_stream = self.controls.get_media_stream()
media_stream.connect("notify::timestamp", self.on_timestamp_changed)
2 Likes

Thank you very much! It seems I still have a lot to learn about this toolkit in the future.:heart:

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.