[Inteproxy-commits] r92 - in trunk: . inteproxy
scm-commit@wald.intevation.org
scm-commit at wald.intevation.org
Thu May 10 20:38:45 CEST 2007
Author: bh
Date: 2007-05-10 20:38:45 +0200 (Thu, 10 May 2007)
New Revision: 92
Modified:
trunk/ChangeLog
trunk/inteproxy/gtkapp.py
Log:
Reworked the way other threads open dialogs to make it work
properly on windows.
* inteproxy/gtkapp.py (InteProxyApplication.__init__): Initialize
the gtk threads acquire the gtk lock here. See updated class
doc-string for some more details.
(InteProxyApplication.run): gtk.gdk.threads_init() moved to
__init__
(InteProxyApplication.get_password)
(InteProxyApplication.run_fees_dialog): Use call_in_gtk_thread to
run the dialogs.
(InteProxyApplication.is_gtk_thread): New method to determine
whether a thread is the gtk thread.
(InteProxyApplication.call_in_gtk_thread): New method to run a
callback in the gtk thread. Used to run dialogs in the gtk
thread. The method returns when the callback has returned in the
gtk thread.
Modified: trunk/ChangeLog
===================================================================
--- trunk/ChangeLog 2007-05-10 18:24:00 UTC (rev 91)
+++ trunk/ChangeLog 2007-05-10 18:38:45 UTC (rev 92)
@@ -1,5 +1,25 @@
2007-05-10 Bernhard Herzog <bh at intevation.de>
+ Reworked the way other threads open dialogs to make it work
+ properly on windows.
+
+ * inteproxy/gtkapp.py (InteProxyApplication.__init__): Initialize
+ the gtk threads acquire the gtk lock here. See updated class
+ doc-string for some more details.
+ (InteProxyApplication.run): gtk.gdk.threads_init() moved to
+ __init__
+ (InteProxyApplication.get_password)
+ (InteProxyApplication.run_fees_dialog): Use call_in_gtk_thread to
+ run the dialogs.
+ (InteProxyApplication.is_gtk_thread): New method to determine
+ whether a thread is the gtk thread.
+ (InteProxyApplication.call_in_gtk_thread): New method to run a
+ callback in the gtk thread. Used to run dialogs in the gtk
+ thread. The method returns when the callback has returned in the
+ gtk thread.
+
+2007-05-10 Bernhard Herzog <bh at intevation.de>
+
* inteproxy/resources.py (gettext): Handle case where the
translation cannot be loaded.
Modified: trunk/inteproxy/gtkapp.py
===================================================================
--- trunk/inteproxy/gtkapp.py 2007-05-10 18:24:00 UTC (rev 91)
+++ trunk/inteproxy/gtkapp.py 2007-05-10 18:38:45 UTC (rev 92)
@@ -9,7 +9,9 @@
import sys
import os
+import threading
+import gobject
import gtk
import pango
@@ -319,12 +321,30 @@
This InteProxyApplication shows a main window and a status icon if
the GTK version used implements it (requires GTK >= 2.10). If a
status icon is shown the main window is hidden initially.
+
+ The application is meant to be used in a multi-threaded program.
+ There should be only one instance of this class in the program and
+ it should be instantiated before any other gtk calls are made. All
+ gtk calls should be made in the same thread that instantiated this
+ class.
+
+ The reason is that the __init__ method calls gtk.gdk.threads_init()
+ and gtk.gdk.threads_enter() to acquire the gtk lock for the calling
+ thread. On Posix systems it's no problem to make gtk calls from
+ other threads, but there appear to be problems with that on windows.
+ In InteProxy other threads occasionally need to show dialogs. This
+ can be done with the methods get_password and run_fees_dialog in a
+ thread safe way from other threads even on windows.
"""
def __init__(self, server):
"""
Initializes the InteProxyApplication with the server and creates the GUI
"""
+ self.gtk_thread = threading.currentThread()
+ gtk.gdk.threads_init()
+ gtk.gdk.threads_enter()
+
super(InteProxyApplication, self).__init__(server)
self.create_ui()
@@ -374,7 +394,6 @@
enters the gtk main loop.
"""
self.start_inte_proxy_thread(daemon=True)
- gtk.gdk.threads_init()
gtk.main()
def quit(self, *args):
@@ -409,11 +428,7 @@
run_password_dialog function to ask the user. The return value
is the return value of run_password_dialog.
"""
- gtk.gdk.threads_enter()
- try:
- return run_password_dialog(*args)
- finally:
- gtk.gdk.threads_leave()
+ return self.call_in_gtk_thread(run_password_dialog, *args)
def run_fees_dialog(self, *args):
"""Runs the fees dialog.
@@ -421,8 +436,47 @@
Extends the inherited method to be thread safe in GTK, so that
it can be called from the server's worker threads.
"""
- gtk.gdk.threads_enter()
- try:
- run_fees_dialog(*args)
- finally:
- gtk.gdk.threads_leave()
+ return self.call_in_gtk_thread(run_fees_dialog, *args)
+
+ def is_gtk_thread(self):
+ """Returns True if called from the gtk thread.
+ The gtk thread is the thread the application object was
+ instantiated in.
+ """
+ return self.gtk_thread == threading.currentThread()
+
+ def call_in_gtk_thread(self, callback, *args):
+ """Calls the callback in the gtk thread and returns the result
+
+ If the calling thread is the gtk thread -- this is determined
+ with the is_gtk_thread method -- the callback is called
+ directly. Otherwise it is called via an idle handler with the
+ gtk lock acquired. Idle handlers are exectuted in the thread
+ running the main loop so that the callback is also called in the
+ that thread. The thread calling call_in_gtk_thread blocks until
+ that call returns.
+
+ The arguments after the callback argument are passed through to
+ the callback.
+ """
+ if self.is_gtk_thread():
+ # in the gtk thread, call the callback directly
+ return callback(*args)
+ else:
+ # in other threads, call the callback in an idle handler.
+ # The calling thread blocks on an event which is set by the
+ # idle handler when the callback has returned.
+ event = threading.Event()
+
+ def call_in_idletime(*args):
+ gtk.gdk.threads_enter()
+ try:
+ call_in_idletime.result = callback(*args)
+ finally:
+ pass
+ gtk.gdk.threads_leave()
+ event.set()
+
+ gobject.idle_add(call_in_idletime, *args)
+ event.wait()
+ return call_in_idletime.result
More information about the Inteproxy-commits
mailing list