Mercurial > libervia-backend
diff frontends/src/quick_frontend/quick_app.py @ 1290:faa1129559b8 frontends_multi_profiles
core, frontends: refactoring to base Libervia on QuickFrontend (big mixed commit):
/!\ not finished, everything is still instable !
- bridge: DBus bridge has been modified to allow blocking call to be called in the same way as asynchronous calls
- bridge: calls with a callback and no errback are now possible, default errback log the error
- constants: removed hack to manage presence without OrderedDict, as an OrderedDict like class has been implemented in Libervia
- core: getLastResource has been removed and replaced by getMainResource (there is a global better management of resources)
- various style improvments: use of constants when possible, fixed variable overlaps, import of module instead of direct class import
- frontends: printInfo and printMessage methods in (Quick)Chat are more generic (use of extra instead of timestamp)
- frontends: bridge creation and option parsing (command line arguments) are now specified by the frontend in QuickApp __init__
- frontends: ProfileManager manage a more complete plug sequence (some stuff formerly manage in contact_list have moved to ProfileManager)
- quick_frontend (quick_widgets): QuickWidgetsManager is now iterable (all widgets are then returned), or can return an iterator on a specific class (return all widgets of this class) with getWidgets
- frontends: tools.jid can now be used in Pyjamas, with some care
- frontends (XMLUI): profile is now managed
- core (memory): big improvment on entities cache management (and specially resource management)
- core (params/exceptions): added PermissionError
- various fixes and improvments, check diff for more details
author | Goffi <goffi@goffi.org> |
---|---|
date | Sat, 24 Jan 2015 01:00:29 +0100 |
parents | e3a9ea76de35 |
children | 6c7d89843f1b |
line wrap: on
line diff
--- a/frontends/src/quick_frontend/quick_app.py Sat Jan 24 00:15:01 2015 +0100 +++ b/frontends/src/quick_frontend/quick_app.py Sat Jan 24 01:00:29 2015 +0100 @@ -22,11 +22,9 @@ from sat.core.log import getLogger log = getLogger(__name__) from sat.core import exceptions -from sat_frontends.bridge.DBus import DBusBridgeFrontend from sat_frontends.tools import jid from sat_frontends.quick_frontend.quick_widgets import QuickWidgetsManager from sat_frontends.quick_frontend import quick_chat -from optparse import OptionParser from sat_frontends.quick_frontend.constants import Const as C @@ -78,20 +76,54 @@ contact_list.fill() #The waiting subscription requests - waitingSub = self.bridge.getWaitingSub(self.profile) - for sub in waitingSub: - self.host.subscribeHandler(waitingSub[sub], sub, self.profile) + self.bridge.getWaitingSub(self.profile, callback=self._plug_profile_gotWaitingSub) + + def _plug_profile_gotWaitingSub(self, waiting_sub): + for sub in waiting_sub: + self.host.subscribeHandler(waiting_sub[sub], sub, self.profile) + + self.bridge.getRoomsJoined(self.profile, callback=self._plug_profile_gotRoomsJoined) + + def _plug_profile_gotRoomsJoined(self, rooms_args): + #Now we open the MUC window where we already are: + for room_args in rooms_args: + self.host.roomJoinedHandler(*room_args, profile=self.profile) + + self.bridge.getRoomsSubjects(self.profile, callback=self._plug_profile_gotRoomsSubjects) + + def _plug_profile_gotRoomsSubjects(self, subjects_args): + for subject_args in subjects_args: + self.host.roomNewSubjectHandler(*subject_args, profile=self.profile) + + #Presence must be requested after rooms are filled + self.host.bridge.getPresenceStatuses(self.profile, callback=self._plug_profile_gotPresences) + - #Now we open the MUC window where we already are: - for room_args in self.bridge.getRoomsJoined(self.profile): - self.host.roomJoinedHandler(*room_args, profile=self.profile) + def _plug_profile_gotPresences(self, presences): + def gotEntityData(data, contact): + for key in ('avatar', 'nick'): + if key in data: + self.host.entityDataUpdatedHandler(contact, key, data[key], self.profile) - for subject_args in self.bridge.getRoomsSubjects(self.profile): - self.host.roomNewSubjectHandler(*subject_args, profile=self.profile) + for contact in presences: + for res in presences[contact]: + jabber_id = ('%s/%s' % (jid.JID(contact).bare, res)) if res else contact + show = presences[contact][res][0] + priority = presences[contact][res][1] + statuses = presences[contact][res][2] + self.host.presenceUpdateHandler(jabber_id, show, priority, statuses, self.profile) + self.host.bridge.getEntityData(contact, ['avatar', 'nick'], self.profile, callback=lambda data, contact=contact: gotEntityData(data, contact), errback=lambda failure, contact=contact: log.debug("No cache data for {}".format(contact))) - #Finaly, we get the waiting confirmation requests - for confirm_id, confirm_type, data in self.bridge.getWaitingConf(self.profile): - self.host.askConfirmationHandler(confirm_id, confirm_type, data, self.profile) + #Finaly, we get the waiting confirmation requests + self.bridge.getWaitingConf(self.profile, callback=self._plug_profile_gotWaitingConf) + + def _plug_profile_gotWaitingConf(self, waiting_confs): + for confirm_id, confirm_type, data in waiting_confs: + self.host.askConfirmationHandler(confirm_id, confirm_type, data, self.profile) + + # At this point, profile should be fully plugged + # and we launch frontend specific method + self.host.profilePlugged(self.profile) def _getParamError(self, ignore): log.error(_("Can't get profile parameter")) @@ -132,20 +164,27 @@ class QuickApp(object): """This class contain the main methods needed for the frontend""" - def __init__(self): + def __init__(self, create_bridge, check_options=None): + """Create a frontend application + + @param create_bridge: method to use to create the Bridge + @param check_options: method to call to check options (usually command line arguments) + """ ProfileManager.host = self self.profiles = ProfilesManager() self.contact_lists = {} self.widgets = QuickWidgetsManager(self) - self.check_options() + if check_options is not None: + self.options = check_options() + else: + self.options = None # widgets - self.visible_widgets = set() # widgets currently visible (must be filled by frontend) self.selected_widget = None # widget currently selected (must be filled by frontend) ## bridge ## try: - self.bridge = DBusBridgeFrontend() + self.bridge = create_bridge() except exceptions.BridgeExceptionNoService: print(_(u"Can't connect to SàT backend, are you sure it's launched ?")) sys.exit(1) @@ -202,6 +241,11 @@ except (TypeError, AttributeError): return self.profiles.chooseOneProfile() + @property + def visible_widgets(self): + """widgets currently visible (must be implemented by frontend)""" + raise NotImplementedError + def registerSignal(self, functionName, handler=None, iface="core", with_profile=True): """Register a handler for a signal @@ -211,7 +255,7 @@ @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, "%s%s" % (functionName, 'Handler')) + handler = getattr(self, "{}{}".format(functionName, 'Handler')) if not with_profile: self.bridge.register(functionName, handler, iface) return @@ -236,24 +280,15 @@ @param profile_manager: instance of a subclass of Quick_frontend.QuickProfileManager """ - if self.options.profile: + if self.options and self.options.profile: profile_manager.autoconnect([self.options.profile]) - def check_options(self): - """Check command line options""" - usage = _(""" - %prog [options] + def profilePlugged(self, profile): + """Method called when the profile is fully plugged, to launch frontend specific workflow - %prog --help for options list - """) - parser = OptionParser(usage=usage) # TODO: use argparse - - parser.add_option("-p", "--profile", help=_("Select the profile to use")) - - (self.options, args) = parser.parse_args() - if self.options.profile: - self.options.profile = self.options.profile.decode('utf-8') - return args + @param profile(unicode): %(doc_profile)s + """ + pass def asyncConnect(self, profile, callback=None, errback=None): if not callback: @@ -281,7 +316,7 @@ will be called when profiles are choosen and are to be plugged soon """ - raise NotImplementedError + pass def unplug_profile(self, profile): """Tell the application to not follow anymore the profile""" @@ -292,6 +327,14 @@ def clear_profile(self): self.profiles.clear() + def addContactList(self, profile): + """Method to subclass to add a contact list widget + + will be called on each profile session build + @return: a ContactList widget + """ + return NotImplementedError + def newWidget(self, widget): raise NotImplementedError @@ -331,7 +374,7 @@ def sendMessage(self, to_jid, message, subject='', mess_type="auto", extra={}, callback=None, errback=None, profile_key=C.PROF_KEY_NONE): if callback is None: - callback = lambda: None + callback = lambda dummy=None: None # FIXME: optional argument is here because pyjamas doesn't support callback without arg with json proxy if errback is None: errback = lambda failure: self.showDialog(failure.fullname, failure.message, "error") self.bridge.sendMessage(to_jid, message, subject, mess_type, extra, profile_key, callback=callback, errback=errback) @@ -500,10 +543,9 @@ @profile: current profile """ from_jid = jid.JID(from_jid_s) if from_jid_s != C.ENTITY_ALL else C.ENTITY_ALL - for widget in self.visible_widgets: - if isinstance(widget, quick_chat.QuickChat): - if from_jid == C.ENTITY_ALL or from_jid.bare == widget.target.bare: - widget.updateChatState(from_jid, state) + for widget in self.widgets.getWidgets(quick_chat.QuickChat): + if from_jid == C.ENTITY_ALL or from_jid.bare == widget.target.bare: + widget.updateChatState(from_jid, state) def _subscribe_cb(self, answer, data): entity, profile = data @@ -554,8 +596,9 @@ self.contact_lists[profile].setCache(entity, 'nick', value) elif key == "avatar": if entity in self.contact_lists[profile]: - filename = self.bridge.getAvatarFile(value) - self.contact_lists[profile].setCache(entity, 'avatar', filename) + def gotFilename(filename): + self.contact_lists[profile].setCache(entity, 'avatar', filename) + self.bridge.getAvatarFile(value, callback=gotFilename) def askConfirmationHandler(self, confirm_id, confirm_type, data, profile): raise NotImplementedError