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 about 10 seconds.
I'd like to stop this from happening, but I'm not sure how. I thought multithreading would be the answer, but it doesn't seem to be working. I've tried two different methods I found online. First, I modified this FAQ to take a long running function. Secondly I tried using threading.Thread directly like in this answer, but that also locks up.
My two samples are below. I'm new to multithreading, so maybe it's not the solution I'm looking for. I'm basically just trying to keep the GUI from locking up so I can update with a progress bar and let the user use a cancel button.
#Sample 1
import threading
import time
import gobject
import gtk
gobject.threads_init()
class MyThread(threading.Thread):
def __init__(self, label, button):
super(MyThread, self).__init__()
self.label = label
self.button = button
self.counter = 0
button.connect("clicked", self.on_button_click)
self.quit = False
def update_label(self, counter):
self.label.set_text("Counter: %i" % counter)
time.sleep(20)
return False
def on_button_click(self, widget):
self.counter += 1
gobject.idle_add(self.update_label, self.counter)
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()
window.connect("destroy", lambda _: gtk.main_quit())
thread = MyThread(label, button)
thread.start()
gtk.main()
thread.quit = True
#####################################
#Sample 2
from threading import Thread
import time
import gobject
import gtk
class Test():
def __init__(self):
self.counter = 0
self.label = gtk.Label()
button = gtk.Button("Test")
window = gtk.Window()
box = gtk.VBox()
box.pack_start(self.label)
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("Counter: %i" % counter)
time.sleep(20)
return False
def on_button_click(self, widget):
self.counter += 1
thread = Thread(target=self.update_label, args=(self.counter,))
thread.start()
while thread.is_alive():
pass
thread.stop()
test = Test()
gtk.main()
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:
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.threading.Lock
to provent the callback to be scheduled more than once, regardless of how many times the button is clicked.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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With