Set text of gtk label using decorator while using template inside .ui file in python

Saw this example of using threads to update a value in this link Threads

Code

import threading
import time

from gi.repository import GLib, Gtk, GObject


def app_main():
    win = Gtk.Window(default_height=50, default_width=300)
    win.connect("destroy", Gtk.main_quit)

    progress = Gtk.ProgressBar(show_text=True)
    win.add(progress)

    def update_progess(i):
        progress.pulse()
        progress.set_text(str(i))
        return False

    def example_target():
        for i in range(50):
            GLib.idle_add(update_progess, i)
            time.sleep(0.2)

    win.show_all()

    thread = threading.Thread(target=example_target)
    thread.daemon = True
    thread.start()


if __name__ == "__main__":
    app_main()
    Gtk.main()

I want to show case two labels on GUI updating their content based on two threads
1) First thread updating the 1st label based on random json generated
2) Second thread updating the 2nd label based on the external signal toggling the toggle button

I have used glade to design a layout I want saved it using GtkBuilder File with .ui extension.
threadsdia

Saw this example where Gtk widgets can be targeted using decorators

This is my example.ui file where I have added <template class="threadswindow" parent="GtkWindow">

<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.22.2 -->
<interface>
  <requires lib="gtk+" version="3.20"/>
  <template class="threadswindow" parent="GtkWindow">
    <property name="can_focus">False</property>
    <signal name="destroy" handler="onDestroy" swapped="no"/>
    <child type="titlebar">
      <placeholder/>
    </child>
    <child>
      <object class="GtkGrid" id="threadsGrid">
        <property name="visible">True</property>
        <property name="can_focus">False</property>
        <child>
          <object class="GtkBox" id="threadsCell">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="orientation">vertical</property>
            <child>
              <object class="GtkBox" id="threadLabelBox">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="halign">center</property>
                <property name="margin_left">10</property>
                <property name="margin_right">10</property>
                <property name="margin_top">10</property>
                <property name="margin_bottom">10</property>
                <child>
                  <object class="GtkLabel" id="thread1Label">
                    <property name="visible">True</property>
                    <property name="can_focus">False</property>
                    <property name="margin_right">24</property>
                    <property name="label" translatable="yes">Thread 1</property>
                  </object>
                  <packing>
                    <property name="expand">False</property>
                    <property name="fill">True</property>
                    <property name="position">0</property>
                  </packing>
                </child>
                <child>
                  <object class="GtkLabel" id="thread2Label">
                    <property name="visible">True</property>
                    <property name="can_focus">False</property>
                    <property name="margin_left">24</property>
                    <property name="label" translatable="yes">Thread 2</property>
                  </object>
                  <packing>
                    <property name="expand">False</property>
                    <property name="fill">True</property>
                    <property name="position">1</property>
                  </packing>
                </child>
              </object>
              <packing>
                <property name="expand">True</property>
                <property name="fill">True</property>
                <property name="position">0</property>
              </packing>
            </child>
            <child>
              <object class="GtkBox" id="threadContentBox">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="margin_left">10</property>
                <property name="margin_right">10</property>
                <property name="margin_top">50</property>
                <property name="margin_bottom">50</property>
                <child>
                  <object class="GtkLabel" id="thread1Value">
                    <property name="visible">True</property>
                    <property name="can_focus">False</property>
                    <property name="margin_right">5</property>
                    <property name="label" translatable="yes">Thread 1 Label Value</property>
                    <signal name="copy-clipboard" handler="populateLabel" swapped="no"/>
                  </object>
                  <packing>
                    <property name="expand">True</property>
                    <property name="fill">True</property>
                    <property name="position">0</property>
                  </packing>
                </child>
                <child>
                  <object class="GtkLabel" id="thread2Value">
                    <property name="visible">True</property>
                    <property name="can_focus">False</property>
                    <property name="margin_left">5</property>
                    <property name="label" translatable="yes">Thread 2 Label Value</property>
                  </object>
                  <packing>
                    <property name="expand">True</property>
                    <property name="fill">True</property>
                    <property name="position">1</property>
                  </packing>
                </child>
              </object>
              <packing>
                <property name="expand">True</property>
                <property name="fill">True</property>
                <property name="position">1</property>
              </packing>
            </child>
            <child>
              <object class="GtkBox" id="threadDescriptionBox">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <child>
                  <object class="GtkLabel" id="thread1DataLabel">
                    <property name="visible">True</property>
                    <property name="can_focus">False</property>
                    <property name="label" translatable="yes">Json Data</property>
                  </object>
                  <packing>
                    <property name="expand">True</property>
                    <property name="fill">True</property>
                    <property name="position">0</property>
                  </packing>
                </child>
                <child>
                  <object class="GtkToggleButton" id="thread2ToggleButton">
                    <property name="label" translatable="yes">togglebutton</property>
                    <property name="visible">True</property>
                    <property name="can_focus">True</property>
                    <property name="receives_default">True</property>
                    <property name="halign">end</property>
                    <property name="valign">center</property>
                    <property name="margin_left">10</property>
                    <property name="margin_right">10</property>
                    <property name="margin_top">10</property>
                    <property name="margin_bottom">10</property>
                    <signal name="toggled" handler="toggleActive" swapped="no"/>
                  </object>
                  <packing>
                    <property name="expand">True</property>
                    <property name="fill">True</property>
                    <property name="position">1</property>
                  </packing>
                </child>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="fill">True</property>
                <property name="position">2</property>
              </packing>
            </child>
          </object>
          <packing>
            <property name="left_attach">1</property>
            <property name="top_attach">1</property>
          </packing>
        </child>
        <child>
          <placeholder/>
        </child>
        <child>
          <placeholder/>
        </child>
        <child>
          <placeholder/>
        </child>
      </object>
    </child>
  </template>
</interface>

This is my python code

import json 
from faker import Faker
import random
from random import randint
fake = Faker('en_US')
import threading
import time

import gi

gi.require_version("Gtk", "3.0")
from gi.repository import Gtk,GLib

class generateJsonData():

    @staticmethod
    def gen_json():
        my_dict = {    'foo': randint(0, 100),    'bar': {'baz': fake.name(),       'poo': float(random.randrange(155, 389))/100   } }
        return my_dict

@Gtk.Template(filename="threads.ui")
class Window1(Gtk.Window):
    # All small no captial letter for name
    __gtype_name__ = "threadswindow"
    
    @Gtk.Template.Callback()
    def onDestroy(self, *args):
        Gtk.main_quit()
        
    @Gtk.Template.Callback()
    def populateLabel(self):
        new_json = generateJsonData.gen_json()
        self.set_text(new_json)
    
    def updateJsonLabel():
        while(True):
            GLib.idle_add(populateLabel)
            time.sleep(0.4)

    # object then signal
    @Gtk.Template.Callback("toggleActive")
    def toggleActive(self,togglebutton):
        print("toggle working")
        
    thread = threading.Thread(target=updateJsonLabel)
    thread.daemon = True
    thread.start()
    
window = Window1()
window.connect("destroy",Gtk.main_quit)
window.show()
Gtk.main()

I get this error

Exception in thread Thread-5 (updateJsonLabel):
Traceback (most recent call last):
  File "/home/sa/miniconda3/envs/gtktest/lib/python3.10/threading.py", line 1009, in _bootstrap_inner
    self.run()
  File "/home/sa/miniconda3/envs/gtktest/lib/python3.10/threading.py", line 946, in run
    self._target(*self._args, **self._kwargs)
  File "/tmp/ipykernel_8296/3732065122.py", line 42, in updateJsonLabel
NameError: name 'populateLabel' is not defined

I have set copy-clipboard signal handle name populateLabel and have a function with same name to set text.

I am new to it don’t know what is going wrong, any kind of guidance would be appreciated

Hello, I have a few suggestions. Firstly, you may want to read the tutorial on python classes before going further. To make a method on a class, you must put a self argument as the first argument to that method. Secondly, you may not need to use threads for this. GLib has a built in timer system usually accessed through the GLib.timeout_add function. Here is an example of this working without the thread, I made a few changes and the most important part is the very last line:


@Gtk.Template(filename="threads.ui")
class Window1(Gtk.Window):
    # All small no captial letter for name
    __gtype_name__ = "threadswindow"

    # Get label for json
    label = Gtk.Template.Child("thread1DataLabel")
    
    @Gtk.Template.Callback()
    def onDestroy(self, *args):
        Gtk.main_quit()
        
    @Gtk.Template.Callback()
    def populateLabel(self):
        new_json = generateJsonData.gen_json()
        # Convert json to string for label
        self.label.set_text(json.dumps(new_json))
        # return True to keep timer running
        return True

    # object then signal
    @Gtk.Template.Callback("toggleActive")
    def toggleActive(self,togglebutton):
        print("toggle working")
    
window = Window1()
window.connect("destroy",Gtk.main_quit)
window.show()
# Time in milliseconds must multiply by 1000
GLib.timeout_add(0.4 * 1000, window.populateLabel)

Please mention if you have any additional questions about this, thank you.

1 Like

@jfrancis Thank you for the reply. Your code worked.