Stop pygtk GUI from locking up during long-running process

前端 未结 2 1584
孤街浪徒
孤街浪徒 2020-12-16 06:01

I have a process that will take a while (maybe a minute or two) to complete. When I call this from my pygtk GUI the window locks up (darkens and prevents user action) after

相关标签:
2条回答
  • 2020-12-16 06:49

    You should reimplement Thread.run for each of your threads, and start a event loop in them.

    Also, you could make the button press call the start method for a thread, which will then call run, and do your long task. This way, you don't need an event loop in each thread.

    Here is some simple code to explain what I mean for the second option:

    class MyThread(threading.Thread):
    
        def __init__(self, label, button):
            threading.Thread.__init__(self)
            self.label = label
            self.button = button
            self.counter = 0
    
        def run(self):
            time.sleep(20)
    
    def callback():
        label.set_text("Counter: %i" % thread.counter)
        thread.start()
    
    window = gtk.Window()
    label = gtk.Label()
    box = gtk.VBox()
    button = gtk.Button('Test')
    box.pack_start(label)
    box.pack_start(button)
    window.add(box)
    window.show_all()
    
    thread = MyThread(label, button)
    button.connect('clicked', callback)
    

    I use a callback function because I doubt that set_text is thread-safe.

    0 讨论(0)
  • 2020-12-16 07:00

    Please find below a modified version of the second example that works for me:

    import threading
    import time
    import gtk, gobject, glib
    
    gobject.threads_init()
    
    class Test():
        def __init__(self):
            self.counter = 0
            self.label = gtk.Label()
            self.progress_bar = gtk.ProgressBar()
            self.progress_bar_lock = threading.Lock()
            button = gtk.Button("Test")
    
            window = gtk.Window()
    
            box = gtk.VBox()
            box.pack_start(self.label)
            box.pack_start(self.progress_bar)
            box.pack_start(button)
            window.add(box)
    
            window.connect("destroy", lambda _: gtk.main_quit())
            button.connect("clicked", self.on_button_click)
            window.show_all()
    
        def update_label(self, counter):
            self.label.set_text("Thread started (counter: {0})"
                                .format(counter))
            time.sleep(5)
            self.label.set_text("Thread finished (counter: {0})"
                                .format(counter))
            return False
    
        def pulse_progress_bar(self):
            print threading.active_count()
            if threading.active_count() > 1:
                self.progress_bar.pulse()
                return True
    
            self.progress_bar.set_fraction(0.0)
            self.progress_bar_lock.release()
            return False
    
        def on_button_click(self, widget):
            self.counter += 1
            thread = threading.Thread(target=self.update_label,
                                      args=(self.counter,))
            thread.start()
    
            if self.progress_bar_lock.acquire(False):
                glib.timeout_add(250, self.pulse_progress_bar)
    
    
    if __name__ == '__main__':
        test = Test()
        gtk.main()
    

    The changes made are:

    • Avoid waiting in the callback for the thread to finish to keep the main loop processing events.
    • Added progress bar to display when a thread is being executed.
    • Used glib.timeout_add to schedule a callback that pulses the progress bar when some thread is being executed. This has the same effect as polling the thread, but with the advantage that the the main loop is still responsive to other events.
    • Used threading.Lock to provent the callback to be scheduled more than once, regardless of how many times the button is clicked.
    • Added gobject.threads_init that was missing in this example (not in the previous one).

    Now, when clicking on the button, you'll see how the label is clicked and the progress bar pulsed as long as a thread is running.

    0 讨论(0)
提交回复
热议问题