Threading in Gtk python
Solution 1
So I finally managed to get it to work. I needed to say:
from gi.repository import Gtk,GObject
GObject.threads_init()
Class Gui:
.....
......
def on_update_click():
Thread(target=update).start()
At first I used:
thread.start_new_thread(update())
in the on_update_click function. As mentioned my J.F Sebastian this was incorrect as this would immediately call this thread. This froze my whole computer.
I then just added:
Thread(target=update).start()
The on_update_clicked function only worked once the main Thread Gtk.main() was closed. So the threads were not running simultaneously.
by adding: GObject.threads_init()
this allowed for the threads to run serially to the python interpreter: Threads in Gtk!
Solution 2
thread.start_new_thread(update())
is wrong. It calls update()
immediately in the main thread and you shouldn't use thread
module directly; use threading
module instead.
You could call threading.current_thread()
to find out which thread executes update()
.
To simplify your code you could run all gtk code in the main thread and use blocking operations to retrieve web-pages and run them in background threads.
Based on the extended example from GTK+ 3 tutorial:
#!/usr/bin/python
import threading
import urllib2
from Queue import Queue
from gi.repository import Gtk, GObject
UPDATE_TIMEOUT = .1 # in seconds
_lock = threading.Lock()
def info(*args):
with _lock:
print("%s %s" % (threading.current_thread(), " ".join(map(str, args))))
class MyWindow(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self, title="Hello World")
self.button = Gtk.Button(label="Click Here")
self.button.connect("clicked", self.on_button_clicked)
self.add(self.button)
self.updater = Updater()
self._update_id = None
self.update()
def on_button_clicked(self, widget):
info('button_clicked')
self.update()
def update(self):
if self._update_id is not None:
GObject.source_remove(self._update_id)
self.updater.add_update(self.done_updating) # returns immediately
# call in UPDATE_TIMEOUT seconds
self._update_id = GObject.timeout_add(
int(UPDATE_TIMEOUT*1000), self.update)
def done_updating(self, task_id):
info('done updating', task_id)
self.button.set_label("done updating %s" % task_id)
class Updater:
def __init__(self):
self._task_id = 0
self._queue = Queue(maxsize=100) #NOTE: GUI blocks if queue is full
for _ in range(9):
t = threading.Thread(target=self._work)
t.daemon = True
t.start()
def _work(self):
# executed in background thread
opener = urllib2.build_opener()
for task_id, done, args in iter(self._queue.get, None):
info('received task', task_id)
try: # do something blocking e.g., urlopen()
data = opener.open('http://localhost:5001').read()
except IOError:
pass # ignore errors
# signal task completion; run done() in the main thread
GObject.idle_add(done, *((task_id,) + args))
def add_update(self, callback, *args):
# executed in the main thread
self._task_id += 1
info('sending task ', self._task_id)
self._queue.put((self._task_id, callback, args))
GObject.threads_init() # init threads?
win = MyWindow()
win.connect("delete-event", Gtk.main_quit)
win.show_all()
Gtk.main()
Note: GObject.idle_add()
is the only gtk-related function that is called from different threads.
See also Multi-threaded GTK applications – Part 1: Misconceptions.
zeref
Updated on June 20, 2022Comments
-
zeref almost 2 years
So I'm busy writing an application that needs to check for updates from a website after a certain amount ouf time, I'm using python with Gtk +3
main.py file
class Gui: ... def on_update_click(): update() app=Gui() Gtk.main()
update.py file
def update(): #check site for updates time.sleep(21600) #check again in 6hrs
I suspect I'll have to use threading. my thinking is:
Gtk.main() runs the main thread.
when the user clicks the update button, update() runs in the background. #thread 2
Is my thinking correct or have I missed something?
EDIT: Ok,
on_update_click function:Thread(target=update).start().
K, computer does not freeze anymore :D
so what happens now is that only when I close Gtk.main() does the update thread only start. It's good that is continues to update when the UI is closed, but i'd also like it to start when the UI is up.
-
jfs over 11 yearson_update_click() misses
self
argument.
-
-
zeref over 11 yearsI dont understand what you mean by: Run long-running blocking function
-
Rostyslav Dzinko over 11 yearsConnecting to site and fetching page contents can be a long-running operation - up to several seconds. If you make it with e.g. urllib, your GUI (the app is single-threaded for now) will freeze for the time update function is performing. That means blocking: GUI event handlers can't be executed until update is done. So that's why I advice either run update() in thread or use asynchronous (non-blocking) network operations.
-
zeref over 11 yearsOk, so in the on_upate_click function added