[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