diff frontends/src/quick_frontend/quick_app.py @ 1345:b26dd78de495 frontends_multi_profiles

quick frontends: signal cache: if a signal arrives between the beginning of profile plugging, and the when the profile is actually ready, it is cached and replayed when the profile is ready.
author Goffi <goffi@goffi.org>
date Tue, 24 Feb 2015 20:29:57 +0100
parents e31a07a5614d
children ba41a81d14c2
line wrap: on
line diff
--- a/frontends/src/quick_frontend/quick_app.py	Tue Feb 24 18:21:03 2015 +0100
+++ b/frontends/src/quick_frontend/quick_app.py	Tue Feb 24 20:29:57 2015 +0100
@@ -204,6 +204,8 @@
         """
         ProfileManager.host = self
         self.profiles = ProfilesManager()
+        self.ready_profiles = set() # profiles which are connected and ready
+        self.signals_cache = {} # used to keep signal received between start of plug_profile and when the profile is actualy ready
         self.contact_lists = {}
         self.widgets = QuickWidgetsManager(self)
         if check_options is not None:
@@ -281,18 +283,18 @@
         """widgets currently visible (must be implemented by frontend)"""
         raise NotImplementedError
 
-    def registerSignal(self, functionName, handler=None, iface="core", with_profile=True):
+    def registerSignal(self, function_name, handler=None, iface="core", with_profile=True):
         """Register a handler for a signal
 
-        @param functionName (str): name of the signal to handle
-        @param handler (instancemethod): method to call when the signal arrive, None for calling an automatically named handler (functionName + 'Handler')
+        @param function_name (str): name of the signal to handle
+        @param handler (instancemethod): method to call when the signal arrive, None for calling an automatically named handler (function_name + 'Handler')
         @param iface (str): interface of the bridge to use ('core' or 'plugin')
         @param with_profile (boolean): True if the signal concerns a specific profile, in that case the profile name has to be passed by the caller
         """
         if handler is None:
-            handler = getattr(self, "{}{}".format(functionName, 'Handler'))
+            handler = getattr(self, "{}{}".format(function_name, 'Handler'))
         if not with_profile:
-            self.bridge.register(functionName, handler, iface)
+            self.bridge.register(function_name, handler, iface)
             return
 
         def signalReceived(*args, **kwargs):
@@ -301,10 +303,14 @@
                 if not args:
                     raise exceptions.ProfileNotSetError
                 profile = args[-1]
-            if profile is not None and not self.check_profile(profile):
-                return  # we ignore signal for profiles we don't manage
+            if profile is not None:
+                if not self.check_profile(profile):
+                    if profile in self.profiles:
+                        # profile is not ready but is in self.profiles, that's mean that it's being connecting and we need to cache the signal
+                        self.signals_cache.setdefault(profile, []).append((function_name, handler, args, kwargs))
+                    return  # we ignore signal for profiles we don't manage
             handler(*args, **kwargs)
-        self.bridge.register(functionName, signalReceived, iface)
+        self.bridge.register(function_name, signalReceived, iface)
 
     def addListener(self, type_, callback, profiles_filter=None):
         """Add a listener for an event
@@ -354,8 +360,8 @@
                     listener(*args, **kwargs)
 
     def check_profile(self, profile):
-        """Tell if the profile is currently followed by the application"""
-        return profile in self.profiles
+        """Tell if the profile is currently followed by the application, and ready"""
+        return profile in self.ready_profiles
 
     def postInit(self, profile_manager):
         """Must be called after initialization is done, do all automatic task (auto plug profile)
@@ -368,9 +374,17 @@
     def profilePlugged(self, profile):
         """Method called when the profile is fully plugged, to launch frontend specific workflow
 
+        /!\ if you override the method and don't call the parent, be sure to add the profile to ready_profiles !
+            if you don't, all signals will stay in cache
+
         @param profile(unicode): %(doc_profile)s
         """
-        pass
+        self.ready_profiles.add(profile)
+
+        # profile is ready, we can call send signals that where is cache
+        cached_signals = self.signals_cache.pop(profile, [])
+        for function_name, handler, args, kwargs in cached_signals:
+            log.debug(u"Calling cached signal [%s] with args %s and kwargs %s" % (function_name, args, kwargs))
 
     def asyncConnect(self, profile, callback=None, errback=None):
         if not callback: