Reputation: 111
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.
Upvotes: 4
Views: 11792
Reputation: 111
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!
Upvotes: 7
Reputation: 414079
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.
Upvotes: 4
Reputation: 40755
Threading is the first way to solve the problem. You can create thread and run long-running blocking function inside that thread (and your GUI won't hang up).
Another way is to use asynchronous networking e.g. using python-gio (GObject-IO) or another library that has a possibility to work with GLib's main loop (like they do with Twisted). This approach is a bit different and uses non-blocking socket operations. Your main loop will make a callback when data from socket (site you're polling) will be available to read. Unfortunately GIO has no high-level HTTP API, so you can use GSocketClient
and manually create HTTP requests structure.
Upvotes: 0