conduit r1414 - in trunk: . conduit conduit/modules/BoxDotNetModule conduit/modules/FlickrModule conduit/modules/GoogleModule conduit/utils



Author: jstowers
Date: Mon Apr  7 09:30:20 2008
New Revision: 1414
URL: http://svn.gnome.org/viewvc/conduit?rev=1414&view=rev

Log:
2008-04-07  John Stowers  <john stowers gmail com>

	* conduit/Synchronization.py: Add run_blocking_dataprovider_function_calls()
	which allows an arbitary function to be executed by the sync manager, and
	get a (callback via sync-completed) when the function returns

	* conduit/utils/__init__.py: Add run_dialog_non_blocking() which does
	as the name suggests.

	* conduit/modules/FlickrModule/FlickrModule.py:
	* conduit/modules/GoogleModule/GoogleModule.py:	
	* conduit/modules/BoxDotNetModule/BoxDotNetModule.py:
	* conduit/modules/BoxDotNetModule/config.glade: Use the above two functions
	to prevent lockups when forced to login from the configure dialog.
	Fixes #526292



Modified:
   trunk/ChangeLog
   trunk/conduit/Synchronization.py
   trunk/conduit/modules/BoxDotNetModule/BoxDotNetModule.py
   trunk/conduit/modules/BoxDotNetModule/config.glade
   trunk/conduit/modules/FlickrModule/FlickrModule.py
   trunk/conduit/modules/GoogleModule/GoogleModule.py
   trunk/conduit/utils/__init__.py

Modified: trunk/conduit/Synchronization.py
==============================================================================
--- trunk/conduit/Synchronization.py	(original)
+++ trunk/conduit/Synchronization.py	Mon Apr  7 09:30:20 2008
@@ -131,12 +131,32 @@
         for c in self.syncWorkers:
             self.syncWorkers[c].join(timeout)
 
-    def refresh_dataprovider(self, cond, dataprovider):
+    def run_blocking_dataprovider_function_calls(self, dataprovider, callback, *functions):
+        #need to get the conduit assocated with this dataprovider because the sync-completed
+        #signal is emmited from the conduit object
+        conds = []
+        conds.extend(conduit.GLOBALS.app.guiSyncSet.get_all_conduits())
+        conds.extend(conduit.GLOBALS.app.dbusSyncSet.get_all_conduits())
+        for c in conds:
+            for dpw in c.get_all_dataproviders():
+                if dataprovider == dpw.module:
+                    #found it!
+                    if c not in self.syncWorkers:
+                        #connect the supplied callback
+                        c.connect("sync-completed",callback)
+                        #start the thread
+                        bfcw = BlockingFunctionCallWorker(c, *functions)
+                        self._start_worker_thread(c, bfcw)
+                    return
+
+        log.info("Could not create BlockingFunctionCallWorker")            
+
+    def refresh_dataprovider(self, cond, dataproviderWrapper):
         if cond in self.syncWorkers:
-            log.info("Refresh dataprovider already in progress")
+            log.info("Refresh dataproviderWrapper already in progress")
             self.join_one(cond)            
 
-        threadedWorker = RefreshDataProviderWorker(cond, dataprovider)
+        threadedWorker = RefreshDataProviderWorker(cond, dataproviderWrapper)
         self._start_worker_thread(cond, threadedWorker)
 
     def refresh_conduit(self, cond):
@@ -796,7 +816,30 @@
         conduit.GLOBALS.mappingDB.save()
         self.cond.emit("sync-completed", self.aborted, self.did_sync_error(), self.did_sync_conflict())
 
-                
+class BlockingFunctionCallWorker(_ThreadedWorker):
+    """
+    Calls the provided (blocking) function in a new thread. When
+    the function returns a sync-completed signal is sent
+    """
+    def __init__(self, cond, *functions):
+        _ThreadedWorker.__init__(self)
+        self.cond = cond
+        self.functions = functions
+        self.setName("%s functions" % len(self.functions))
+
+    def run(self):
+        try:
+            #FIXME: Set the status text on the dataprovider
+            for f in self.functions:
+                log.debug("FunctionCall %s beginning" % f.__name__)
+                f()
+            self.aborted = False
+        except Exception, e:
+            log.warn("FunctionCall error: %s" % e)
+            self.aborted = True
+
+        self.cond.emit("sync-completed", self.aborted, False, False)
+
 class DeletedData(DataType.DataType):
     """
     Simple wrapper around a deleted item. If an item has been deleted then

Modified: trunk/conduit/modules/BoxDotNetModule/BoxDotNetModule.py
==============================================================================
--- trunk/conduit/modules/BoxDotNetModule/BoxDotNetModule.py	(original)
+++ trunk/conduit/modules/BoxDotNetModule/BoxDotNetModule.py	Mon Apr  7 09:30:20 2008
@@ -278,16 +278,41 @@
         """
         import gtk
         import gobject
+        def on_login_finish(*args):
+            if self.token:
+                build_folder_store()
+                foldernamecombo.set_sensitive(True)
+            else:
+                foldernamecombo.set_sensitive(False)
+            #dlg.window.set_cursor(None)
+            
+        def on_response(sender, responseID):
+            if responseID == gtk.RESPONSE_OK:
+                self.foldername = foldernamecombo.child.get_text()
+                
         def load_button_clicked(button):
-            if not self.token:
-                self._login()
-
-            build_folder_store()
+            #dlg.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
+            conduit.GLOBALS.syncManager.run_blocking_dataprovider_function_calls(
+                                            self,
+                                            on_login_finish,
+                                            self._login)
 
         def build_folder_store():
-            self.folder_store.clear()
+            folder_store.clear()
+            folder_count = 0
+            folder_iter = None
             for folder_name in self._get_folders().keys():
-                self.folder_store.append((folder_name,))
+                iter = folder_store.append((folder_name,))
+                if folder_name != "" and folder_name == self.foldername:
+                    folder_iter = iter
+                folder_count += 1
+
+            if folder_iter:
+                foldernamecombo.set_active_iter(folder_iter)
+            elif self.foldername:
+                foldernamecombo.child.set_text(self.foldername)
+            elif folder_count:
+                foldernamecombo.set_active(0)
 
         tree = Utils.dataprovider_glade_get_widget(
                         __file__,
@@ -295,38 +320,33 @@
                         "BoxDotNetConfigDialog")
 
         #get a whole bunch of widgets
-        foldername_entry = tree.get_widget("foldername_entry")
+        foldernamecombo = tree.get_widget("foldernamecombo")
         load_button = tree.get_widget("load_button")
+        dlg = tree.get_widget("BoxDotNetConfigDialog")
 
         # setup combobox
-        self.folder_store = gtk.ListStore(gobject.TYPE_STRING)
-        foldername_entry.set_model(self.folder_store)
+        folder_store = gtk.ListStore(gobject.TYPE_STRING)
+        foldernamecombo.set_model(folder_store)
         cell = gtk.CellRendererText()
-        foldername_entry.pack_start(cell, True)
-        foldername_entry.set_text_column(0)
+        foldernamecombo.pack_start(cell, True)
+        foldernamecombo.set_text_column(0)
 
         # already load the folders if we're logged in
         if self.token:
             build_folder_store()
+            foldernamecombo.set_sensitive(True)
+        else:
+            foldernamecombo.child.set_text(self.foldername)
+            foldernamecombo.set_sensitive(False)
 
         # load button
         load_button.connect('clicked', load_button_clicked)
 
-        #preload the widgets
-        foldername_entry.child.set_text(self.foldername)
-
         # run the dialog
-        dlg = tree.get_widget("BoxDotNetConfigDialog")
-        response = Utils.run_dialog (dlg, window)
-        if response == True:
-            # get the values from the widgets
-            self.foldername = foldername_entry.child.get_text()
-
-        del self.folder_store
-        dlg.destroy()
+        Utils.run_dialog_non_blocking(dlg, on_response, window)
 
     def is_configured (self, isSource, isTwoWay):
-        return len (self.foldername) > 0
+        return len(self.foldername) > 0
 
     def get_configuration(self):
         return {

Modified: trunk/conduit/modules/BoxDotNetModule/config.glade
==============================================================================
--- trunk/conduit/modules/BoxDotNetModule/config.glade	(original)
+++ trunk/conduit/modules/BoxDotNetModule/config.glade	Mon Apr  7 09:30:20 2008
@@ -100,7 +100,7 @@
 	  <property name="spacing">0</property>
 
 	  <child>
-	    <widget class="GtkComboBoxEntry" id="foldername_entry">
+	    <widget class="GtkComboBoxEntry" id="foldernamecombo">
 	      <property name="visible">True</property>
 	      <property name="add_tearoffs">False</property>
 	      <property name="has_frame">True</property>

Modified: trunk/conduit/modules/FlickrModule/FlickrModule.py
==============================================================================
--- trunk/conduit/modules/FlickrModule/FlickrModule.py	(original)
+++ trunk/conduit/modules/FlickrModule/FlickrModule.py	Mon Apr  7 09:30:20 2008
@@ -262,27 +262,33 @@
         """
         import gobject
         import gtk
-        def load_click(button, window, usernameEntry, photosetCombo):
-            #set Window cursor to loading
-            #FIXME: Doest do anything because this call blocks the mainloop
-            #anyway
-            window.window.set_cursor(
-                            gtk.gdk.Cursor(gtk.gdk.WATCH))
-            self._set_username(usernameEntry.get_text())
-            self._login()
+        def on_login_finish(*args):
             if self.logged_in:
-                build_photoset_model(photosetCombo)
-            window.window.set_cursor(None)
+                build_photoset_model()
+                
+        def on_response(sender, responseID):
+            if responseID == gtk.RESPONSE_OK:
+                self._set_username(username.get_text())
+                self.photoSetName = photosetCombo.get_active_text()
+                self.showPublic = publicCb.get_active()
+                self.imageSize = self._resize_combobox_get_active(resizecombobox)
+        
+        def load_click(button, window, usernameEntry):
+            self._set_username(usernameEntry.get_text())
+            conduit.GLOBALS.syncManager.run_blocking_dataprovider_function_calls(
+                                            self,
+                                            on_login_finish,
+                                            self._login)            
 
         def username_changed(entry, load_button):
             load_button.set_sensitive (len(entry.get_text()) > 0)
 
-        def build_photoset_model(photosetCombo):
-            self.photoset_store.clear()
+        def build_photoset_model():
+            photoset_store.clear()
             photoset_count = 0
             photoset_iter = None
             for name, photoSetId in self._get_photosets():       
-                iter = self.photoset_store.append((name,))
+                iter = photoset_store.append((name,))
                 if name == self.photoSetName:
                     photoset_iter = iter
                 photoset_count += 1
@@ -304,12 +310,13 @@
         username = tree.get_widget("username")
         load_button = tree.get_widget('load_button')
         ok_button = tree.get_widget('ok_button')
+        dlg = tree.get_widget("FlickrTwoWayConfigDialog")
 
         resizecombobox = tree.get_widget("resizecombobox")
         self._resize_combobox_build(resizecombobox, self.imageSize)
 
         #signals
-        load_button.connect('clicked', load_click, window, username, photosetCombo)
+        load_button.connect('clicked', load_click, window, username)
         username.connect('changed', username_changed, load_button)
         
         #preload the widgets
@@ -317,8 +324,8 @@
         username.set_text(self.username)
 
         #setup photoset combo
-        self.photoset_store = gtk.ListStore (gobject.TYPE_STRING)
-        photosetCombo.set_model(self.photoset_store)
+        photoset_store = gtk.ListStore (gobject.TYPE_STRING)
+        photosetCombo.set_model(photoset_store)
         cell = gtk.CellRendererText()
         photosetCombo.pack_start(cell, True)
         photosetCombo.set_text_column(0)
@@ -328,18 +335,7 @@
         load_button.set_sensitive(enabled)
         
         # run dialog 
-        dlg = tree.get_widget("FlickrTwoWayConfigDialog")
-        response = Utils.run_dialog(dlg, window)
-
-        if response == True:
-            #get the values from the widgets
-            self._set_username(username.get_text())
-            self.photoSetName = photosetCombo.get_active_text()
-            self.showPublic = publicCb.get_active()
-            self.imageSize = self._resize_combobox_get_active(resizecombobox)
-        dlg.destroy()    
-
-        del self.photoset_store
+        Utils.run_dialog_non_blocking(dlg, on_response, window)
        
     def is_configured (self, isSource, isTwoWay):
         return len(self.username) > 0 and len(self.photoSetName) > 0

Modified: trunk/conduit/modules/GoogleModule/GoogleModule.py
==============================================================================
--- trunk/conduit/modules/GoogleModule/GoogleModule.py	(original)
+++ trunk/conduit/modules/GoogleModule/GoogleModule.py	Mon Apr  7 09:30:20 2008
@@ -639,30 +639,35 @@
         """
         import gobject
         import gtk
-        def login_click(button, window, usernameEntry, passwordEntry, album_combo):
-            #set Window cursor to loading
-            #FIXME: Doest do anything because this call blocks the mainloop
-            #anyway
-            window.window.set_cursor(
-                            gtk.gdk.Cursor(gtk.gdk.WATCH))
+        def on_login_finish(*args):
+            if self.loggedIn:
+                build_album_model()
+                
+        def on_response(sender, responseID):
+            if responseID == gtk.RESPONSE_OK:
+                self._set_username(username.get_text())
+                self._set_password(password.get_text())
+                self.albumName = album_combo.get_active_text()
+                self.imageSize = self._resize_combobox_get_active(resizecombobox)
+
+        def login_click(button, window, usernameEntry, passwordEntry):
             self._set_username(usernameEntry.get_text())
             self._set_password(passwordEntry.get_text())
-            self._login()
-            if self.loggedIn:
-                build_album_model(album_combo)
-                #album_combo.set_sensitive(True)
-            window.window.set_cursor(None)
+            conduit.GLOBALS.syncManager.run_blocking_dataprovider_function_calls(
+                                            self,
+                                            on_login_finish,
+                                            self._login)        
 
         def username_password_changed(sender, username, password, login_button):
             login_button.set_sensitive(
                             len(username.get_text()) > 0 and len(password.get_text()) > 0)
 
-        def build_album_model(album_combo):
-            self.album_store.clear()
+        def build_album_model():
+            album_store.clear()
             album_count = 0
             album_iter = None
             for name, album in self._get_albums():       
-                iter = self.album_store.append((name,))
+                iter = album_store.append((name,))
                 if name == self.albumName:
                     album_iter = iter
                 album_count += 1
@@ -685,12 +690,13 @@
         password = tree.get_widget('password')
         album_combo = tree.get_widget('album_combobox')
         login_button = tree.get_widget("login_button")
+        dlg = tree.get_widget("PicasaTwoWayConfigDialog")        
 
         resizecombobox = tree.get_widget("resize_combobox")
         self._resize_combobox_build(resizecombobox, self.imageSize)
 
         #connect to signals
-        login_button.connect('clicked', login_click, window, username, password, album_combo)
+        login_button.connect('clicked', login_click, window, username, password)
         username.connect('changed', username_password_changed, username, password, login_button)
         password.connect('changed', username_password_changed, username, password, login_button)
         
@@ -699,8 +705,8 @@
         password.set_text(self.password)
 
         #setup album combo
-        self.album_store = gtk.ListStore(gobject.TYPE_STRING)
-        album_combo.set_model (self.album_store)
+        album_store = gtk.ListStore(gobject.TYPE_STRING)
+        album_combo.set_model (album_store)
         cell = gtk.CellRendererText()
         album_combo.pack_start(cell, True)
         album_combo.set_text_column(0)
@@ -711,17 +717,7 @@
         #album_combo.set_sensitive(enabled)
 
         # Now run the dialog 
-        dlg = tree.get_widget("PicasaTwoWayConfigDialog")
-        response = Utils.run_dialog (dlg, window)
-        if response == True:
-            self._set_username(username.get_text())
-            self._set_password(password.get_text())
-            self.albumName = album_combo.get_active_text()
-            self.imageSize = self._resize_combobox_get_active(resizecombobox)
-
-        # cleanup
-        del self.album_store
-        dlg.destroy()    
+        Utils.run_dialog_non_blocking(dlg, on_response, window)
         
     def get_configuration(self):
         conf = GoogleBase.get_configuration(self)

Modified: trunk/conduit/utils/__init__.py
==============================================================================
--- trunk/conduit/utils/__init__.py	(original)
+++ trunk/conduit/utils/__init__.py	Mon Apr  7 09:30:20 2008
@@ -137,6 +137,34 @@
 
     return dialog.run() == gtk.RESPONSE_OK
 
+def run_dialog_non_blocking(dialog, resp_cb, window=None):
+    """
+    Runs a given dialog, and makes it transient for
+    the given window if any
+    @param dialog: dialog 
+    @param window: gtk window
+    @returns: True if the user clicked OK to exit the dialog
+    """
+    import gtk.gdk
+
+    dialog.connect("response", resp_cb)
+    dialog.connect("response", lambda dlg, resp: dlg.destroy())
+
+    if window:
+        dialog.set_transient_for(window)
+        #FIXME: Doesnt work
+        #fake modality by making window ignore key events
+        #events = window.window.get_events()
+        #window.window.set_events(
+        #        events & ~(gtk.gdk.KEY_PRESS_MASK | gtk.gdk.KEY_RELEASE_MASK))
+        #connect to response signal to restore original events
+        #dialog.connect(
+        #        "response",
+        #        lambda dlg, resp, win, originalEvents: window.window.set_events(originalEvents),
+        #        window,events)
+
+    dialog.show()
+
 def md5_string(string):
     """
     Returns the md5 of the supplied string in readable hexdigest string format



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]