pythonpygtkgtk3pyobject

Python,Gtk3: How to make the Progress bar pulsing while other stuffs are running


Based on Classes, i have window which contain a button and progressbar, whenever the button is clicked there two things should happen :

1 - should entried value from dialog pass to class ABCD

2 - While our class ABCD() do his stuff, should our progressbar do regular pulsing untill the class ABCD() finish process.

So the problem is that the progressbar pulse only one time,then stucked there till the class ABCD() finished, then its start pulsing regulary later.

Here is my try:

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

class DialogExample(Gtk.Dialog):

    def __init__(self, parent):
        Gtk.Dialog.__init__(self, "My Dialog", parent, 0,
            (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
             Gtk.STOCK_OK, Gtk.ResponseType.OK))

        self.set_default_size(150, 100)

        self.Myinput = Gtk.Entry()

        box = self.get_content_area()
        box.add(self.Myinput)
        self.show_all()

class DialogWindow(Gtk.Window):

    def __init__(self):
        Gtk.Window.__init__(self, title="Dialog Example")

        self.set_border_width(6)
        Hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
        self.add(Hbox)

        self.button = Gtk.Button("Open dialog")
        self.button.connect("clicked", self.on_button_clicked)

        Hbox.pack_start(self.button, True, True, 0)

        self.progressbar = Gtk.ProgressBar()
        Hbox.pack_start(self.progressbar, True, True, 0)

#~~~~~~ Progress Bar
    def on_timeout(self, user_data):
        """
        Update value on the progress bar
        """
        if self.activity_mode:
            self.progressbar.pulse()
        else:
            new_value = self.progressbar.get_fraction() + 0.01

            if new_value > 1:
                new_value = 0

            self.progressbar.set_fraction(new_value)

        # As this is a timeout function, return True so that it
        # continues to get called
        return True

    def on_button_clicked(self, widget):
        dialog = DialogExample(self)
        response = dialog.run()

        if response == Gtk.ResponseType.OK:
            variable = dialog.Myinput.get_text()
            print("start")
        dialog.destroy()

        #ProgressBar time function

        self.timeout_id = GObject.timeout_add(50, self.on_timeout, None)
        self.activity_mode = False
        self.progressbar.pulse()

        #this for Updating the Windows and make the progressbar pulsing while waiting
        # the class ABCD finish his stuff, finally should stop pulsing.
        while Gtk.events_pending():
            Gtk.main_iteration_do(False)
        passing_instance = ABCD(variable)


class ABCD(object):
    def __init__(self,value_of_dialog):
        self.get_value = value_of_dialog
        self.for_add = "______ add was done"
        self.final_value = self.get_value+self.for_add
        time.sleep(10)
        print("gonna be finished")
        print(self.final_value)



win = DialogWindow()
win.connect("delete-event", Gtk.main_quit)
win.show_all()
Gtk.main()

As we can see here i already try to make pulse and refresh the windows in this part of code

self.timeout_id = GObject.timeout_add(50, self.on_timeout, None)
self.activity_mode = False
self.progressbar.pulse()

#this for Updating the Windows and make the progressbar pulsing while waiting
# the class ABCD finish his stuff, finally should stop pulsing.
while Gtk.events_pending():
    Gtk.main_iteration_do(False)

Otherwise because in my class ABCD() i have time.sleep(10) should the progress bar pulse only for that time 10 seconds later only then stop.

How should this code gonna be, i need someone provide me the correct code, with little explain.


Solution

  • The issue with using sleep in order to emulate the passing of time is that sleep will stop everything that is happening in the thread which in this case prevents the thread to reach Gtk.main() which is needed to make your progressbar pulse or update.

    So in order to do this properly there are 2 options:

    1. Run ABCD in a separate thread such that the main thread can reach Gtk.main(). Which than will make sure that the progressbar moves as expected. A quick example of this looks like this:

      self.abcd_thread = ABCD(variable)
      self.abcd_thread.start()
      
      class ABCD(Thread):
          def __init__(self, value_of_dialog):
              super(ABCD, self).__init__()
              self.get_value = value_of_dialog
              self.for_add = "______ add was done"
              self.final_value = self.get_value+self.for_add
      
          def run(self):
              print "Starting " + self.name
      
              time.sleep(10)
              print("gonna be finished")
              print(self.final_value)
      
              print "Exiting " + self.name
      

      When using this you can use self.abcd_thread.isAlive() to see whether the thread is still computing things. The way to return information heavily depends on the job placed in the thread.

    2. Replace the time.sleep with the following fragment:

      now = time.time()
      while time.time() - now < 10:
          # Insert any code here
          Gtk.main_iteration_do(False)
      

      This will still emulate ABCD doing stuff for 10 seconds but because we call Gtk.main_iteration_do(False) in each iteration of the loop GTK is able to update the interface during the loop.

    In general the second option is the easiest as it only involves making Gtk.main_iteration_do(False) call during whatever your doing. The first option on the other hand is more useful when dealing with complex computations where adding Gtk calls doesn't fit in easily.