comparison 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
comparison
equal deleted inserted replaced
1344:1679ac59f701 1345:b26dd78de495
202 @param create_bridge: method to use to create the Bridge 202 @param create_bridge: method to use to create the Bridge
203 @param check_options: method to call to check options (usually command line arguments) 203 @param check_options: method to call to check options (usually command line arguments)
204 """ 204 """
205 ProfileManager.host = self 205 ProfileManager.host = self
206 self.profiles = ProfilesManager() 206 self.profiles = ProfilesManager()
207 self.ready_profiles = set() # profiles which are connected and ready
208 self.signals_cache = {} # used to keep signal received between start of plug_profile and when the profile is actualy ready
207 self.contact_lists = {} 209 self.contact_lists = {}
208 self.widgets = QuickWidgetsManager(self) 210 self.widgets = QuickWidgetsManager(self)
209 if check_options is not None: 211 if check_options is not None:
210 self.options = check_options() 212 self.options = check_options()
211 else: 213 else:
279 @property 281 @property
280 def visible_widgets(self): 282 def visible_widgets(self):
281 """widgets currently visible (must be implemented by frontend)""" 283 """widgets currently visible (must be implemented by frontend)"""
282 raise NotImplementedError 284 raise NotImplementedError
283 285
284 def registerSignal(self, functionName, handler=None, iface="core", with_profile=True): 286 def registerSignal(self, function_name, handler=None, iface="core", with_profile=True):
285 """Register a handler for a signal 287 """Register a handler for a signal
286 288
287 @param functionName (str): name of the signal to handle 289 @param function_name (str): name of the signal to handle
288 @param handler (instancemethod): method to call when the signal arrive, None for calling an automatically named handler (functionName + 'Handler') 290 @param handler (instancemethod): method to call when the signal arrive, None for calling an automatically named handler (function_name + 'Handler')
289 @param iface (str): interface of the bridge to use ('core' or 'plugin') 291 @param iface (str): interface of the bridge to use ('core' or 'plugin')
290 @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 292 @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
291 """ 293 """
292 if handler is None: 294 if handler is None:
293 handler = getattr(self, "{}{}".format(functionName, 'Handler')) 295 handler = getattr(self, "{}{}".format(function_name, 'Handler'))
294 if not with_profile: 296 if not with_profile:
295 self.bridge.register(functionName, handler, iface) 297 self.bridge.register(function_name, handler, iface)
296 return 298 return
297 299
298 def signalReceived(*args, **kwargs): 300 def signalReceived(*args, **kwargs):
299 profile = kwargs.get('profile') 301 profile = kwargs.get('profile')
300 if profile is None: 302 if profile is None:
301 if not args: 303 if not args:
302 raise exceptions.ProfileNotSetError 304 raise exceptions.ProfileNotSetError
303 profile = args[-1] 305 profile = args[-1]
304 if profile is not None and not self.check_profile(profile): 306 if profile is not None:
305 return # we ignore signal for profiles we don't manage 307 if not self.check_profile(profile):
308 if profile in self.profiles:
309 # profile is not ready but is in self.profiles, that's mean that it's being connecting and we need to cache the signal
310 self.signals_cache.setdefault(profile, []).append((function_name, handler, args, kwargs))
311 return # we ignore signal for profiles we don't manage
306 handler(*args, **kwargs) 312 handler(*args, **kwargs)
307 self.bridge.register(functionName, signalReceived, iface) 313 self.bridge.register(function_name, signalReceived, iface)
308 314
309 def addListener(self, type_, callback, profiles_filter=None): 315 def addListener(self, type_, callback, profiles_filter=None):
310 """Add a listener for an event 316 """Add a listener for an event
311 317
312 /!\ don't forget to remove listener when not used anymore (e.g. if you delete a widget) 318 /!\ don't forget to remove listener when not used anymore (e.g. if you delete a widget)
352 for listener, profiles_filter in listeners.iteritems(): 358 for listener, profiles_filter in listeners.iteritems():
353 if profile is None or not profiles_filter or profile in profiles_filter: 359 if profile is None or not profiles_filter or profile in profiles_filter:
354 listener(*args, **kwargs) 360 listener(*args, **kwargs)
355 361
356 def check_profile(self, profile): 362 def check_profile(self, profile):
357 """Tell if the profile is currently followed by the application""" 363 """Tell if the profile is currently followed by the application, and ready"""
358 return profile in self.profiles 364 return profile in self.ready_profiles
359 365
360 def postInit(self, profile_manager): 366 def postInit(self, profile_manager):
361 """Must be called after initialization is done, do all automatic task (auto plug profile) 367 """Must be called after initialization is done, do all automatic task (auto plug profile)
362 368
363 @param profile_manager: instance of a subclass of Quick_frontend.QuickProfileManager 369 @param profile_manager: instance of a subclass of Quick_frontend.QuickProfileManager
366 profile_manager.autoconnect([self.options.profile]) 372 profile_manager.autoconnect([self.options.profile])
367 373
368 def profilePlugged(self, profile): 374 def profilePlugged(self, profile):
369 """Method called when the profile is fully plugged, to launch frontend specific workflow 375 """Method called when the profile is fully plugged, to launch frontend specific workflow
370 376
377 /!\ if you override the method and don't call the parent, be sure to add the profile to ready_profiles !
378 if you don't, all signals will stay in cache
379
371 @param profile(unicode): %(doc_profile)s 380 @param profile(unicode): %(doc_profile)s
372 """ 381 """
373 pass 382 self.ready_profiles.add(profile)
383
384 # profile is ready, we can call send signals that where is cache
385 cached_signals = self.signals_cache.pop(profile, [])
386 for function_name, handler, args, kwargs in cached_signals:
387 log.debug(u"Calling cached signal [%s] with args %s and kwargs %s" % (function_name, args, kwargs))
374 388
375 def asyncConnect(self, profile, callback=None, errback=None): 389 def asyncConnect(self, profile, callback=None, errback=None):
376 if not callback: 390 if not callback:
377 callback = lambda dummy: None 391 callback = lambda dummy: None
378 if not errback: 392 if not errback: