Python Forum

Full Version: Update Gtk.Label in for-loop
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
How can I update a Gtk.Label in a foor loop?
It's not updating via .set_text("bbb").
Is there any trick to do this?

Greetings
TimeMen
post your code in python tags and full traceback if you get any exception
for zahl in range(start, end+1):
                function(self, zahl, steps, counter, prim_counter)
                counter +=1

                if not numlist:
                    if not divlist:
                        if zahl > 1:
                            prim_counter += 1
                    elif divlist:
                        kpt_counter += 1
                elif numlist == divlist:
                    if (sqrt(zahl).is_integer()):
                        psq_counter += 1
                    else:
                        pt_counter +=1
                if numlist != divlist:
                    gt_counter += 1
                val = self.progressbar.get_fraction() + 1/end
                self.progressbar.set_fraction(val)
This is the code, after changing the Label to a Progressbar (as you can see.)
But it won't work too.

There's no exception, It's just not working.
Only at the end, the progressbar is completely filled.
It's only kinda bar, not a "progress"bar..
The problem is that while your application is in the loop it does not allow Gtk to control the GUI (it just "freezes") so only the final modification is effective.

So if your application is going to perform a heavy operation the best option is to launch it in a different process and set a communication channel to be able to check the progress. To do this you can use the multiprocessing module (it is easier than it looks)
Now you can set a timer in the GUI that periodically check the state of your task and update the progressbar.
As the explanation is a little bit confusing, there is a demo code:
#!/usr/bin/env python3

import multiprocessing as mp
import time

import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, GObject


def really_looong_operation(state):
    """
    Do something that will block your app for a long time
    """
    # tick every 200 milliseconds
    tick = 0.2
    state.value = 0
    for k in range(100):
        # In real life this might be invert a huge matrix or whatever...
        time.sleep(tick)
        state.value = k + 1
    # Final activity...
    time.sleep(2*tick)


class PBarDemo(Gtk.Window):

    def __init__(self):
        super().__init__(title="Progressing...")
        self.connect("destroy", Gtk.main_quit)
        self.set_border_width(10)

        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5)
        self.add(vbox)
        
        self.pbar = Gtk.ProgressBar()
        self.pbar.set_show_text(True)
        vbox.pack_start(self.pbar, True, True, 0)

        self.switch = Gtk.Switch()
        self.switch.connect("notify::active", self.on_start_stop)
        vbox.pack_start(self.switch, True, True, 0)

        self.tid = None
        self.proc = None
        self.state = mp.Value('i', 0)
        # Guarantee the start state
        self._stop()
    
    def _stop(self):
        # Guarantee that everything is in "stop mode"
        if self.tid is not None:
            GObject.source_remove(self.tid)
        
        if self.proc is not None and self.proc.is_alive():
            self.proc.terminate()
        self.tid = None
        self.proc = None
        self.pbar.set_fraction(0.0)
        self.pbar.set_text('Ready...')

    def on_start_stop(self, switch, prop):
        # Check this is the right property
        if prop.name != "active":
            return
        
        self._stop()
        if not switch.get_active():
            return
        # Launch the activity... depending of what you want to do,
        # it might be better to use a pool of workers or other tricks
        self.proc = mp.Process(target=really_looong_operation, args=(self.state,))
        self.proc.start()
        # And the timer that update the progressbar
        self.tid = GObject.timeout_add(250, self.running, None)
        self.pbar.set_text("0%")

    def running(self, ignored):
        value = self.state.value
        if value >= 100:
            # Stop working at 100%
            self.proc.join()
            self._stop()
            self.switch.set_active(False)
            # Return false to stop the timer
            return False
        else:
            frac = value / 100
            self.pbar.set_fraction(frac)
            self.pbar.set_text(f"{frac:.0%}")

            # Return True so this timer is considered active
            return True


if __name__ == '__main__':
    win = PBarDemo()
    win.show_all()
    Gtk.main()