Mercurial > libervia-desktop-kivy
diff src/cagou/core/cagou_main.py @ 86:c711be670ecd
core, chat: upload plugin system:
- extented plugin system so it's not only used with main widget. It is also used for upload widgets and can be extended more
- plugin file name is used to detect the type: plugin_wid_* for main widgets, plugin_upload_* for upload widget plugins
- a new UploadMenu class allows to easily add an upload button which will use loaded plugins
- plugin_info can now specify a list of allowed platforms in "platforms" key
- file upload in chat has been moved to a plugin
author | Goffi <goffi@goffi.org> |
---|---|
date | Sun, 25 Dec 2016 16:41:21 +0100 |
parents | c2a7234d13d2 |
children | 17094a075fd2 |
line wrap: on
line diff
--- a/src/cagou/core/cagou_main.py Sat Dec 24 14:20:49 2016 +0100 +++ b/src/cagou/core/cagou_main.py Sun Dec 25 16:41:21 2016 +0100 @@ -56,7 +56,7 @@ from cagou_widget import CagouWidget from . import widgets_handler from .common import IconButton -from .menu import MenusWidget +from . import menu from importlib import import_module import os.path import glob @@ -152,7 +152,7 @@ self.manager.switch_to(screen) -class RootMenus(MenusWidget): +class RootMenus(menu.MenusWidget): pass @@ -292,7 +292,8 @@ self.app.host = self self.media_dir = self.app.media_dir = config.getConfig(main_config, '', 'media_dir') self.app.default_avatar = os.path.join(self.media_dir, "misc/default_avatar.png") - self._plg_wids = [] # widget plugins + self._plg_wids = [] # main widgets plugins + self._plg_wids_upload = [] # upload widgets plugins self._import_plugins() self._visible_widgets = {} # visible widgets by classes @@ -343,11 +344,30 @@ del self.app._profile_manager super(Cagou, self).postInit(profile_manager) - def _defaultFactory(self, plugin_info, target, profiles): - """factory used to create widget instance when PLUGIN_INFO["factory"] is not set""" + def _defaultFactoryMain(self, plugin_info, target, profiles): + """default factory used to create main widgets instances + + used when PLUGIN_INFO["factory"] is not set + @param plugin_info(dict): plugin datas + @param target: QuickWidget target + @param profiles(iterable): list of profiles + """ main_cls = plugin_info['main'] return self.widgets.getOrCreateWidget(main_cls, target, on_new_widget=None, profiles=iter(self.profiles)) + def _defaultFactoryUpload(self, plugin_info, callback, cancel_cb, profiles): + """default factory used to create upload widgets instances + + @param plugin_info(dict): plugin datas + @param callback(callable): method to call with path to file to upload + @param cancel_cb(callable): call when upload is cancelled + upload widget must be used as first argument + @param profiles(iterable): list of profiles + None if not specified + """ + main_cls = plugin_info['main'] + return main_cls(callback=callback, cancel_cb=cancel_cb) + ## plugins & kv import ## def _import_kv(self): @@ -369,29 +389,65 @@ plugin_glob = "plugin*.py" plug_lst = [os.path.splitext(p)[0] for p in map(os.path.basename, glob.glob(os.path.join(plugins_path, plugin_glob)))] - imported_names = set() # use to avoid loading 2 times plugin with same import name + imported_names_main = set() # used to avoid loading 2 times plugin with same import name + imported_names_upload = set() for plug in plug_lst: plugin_path = 'cagou.plugins.' + plug + + # we get type from plugin name + suff = plug[7:] + if u'_' not in suff: + log.error(u"invalid plugin name: {}, skipping".format(plug)) + continue + plugin_type = suff[:suff.find(u'_')] + + # and select the variable to use according to type + if plugin_type == C.PLUG_TYPE_WID: + imported_names = imported_names_main + default_factory = self._defaultFactoryMain + elif plugin_type == C.PLUG_TYPE_UPLOAD: + imported_names = imported_names_upload + default_factory = self._defaultFactoryUpload + else: + log.error(u"unknown plugin type {type_} for plugin {file_}, skipping".format( + type_ = plugin_type, + file_ = plug + )) + continue + plugins_set = self._getPluginsSet(plugin_type) + mod = import_module(plugin_path) try: plugin_info = mod.PLUGIN_INFO except AttributeError: plugin_info = {} + plugin_info['plugin_file'] = plug + plugin_info['plugin_type'] = plugin_type + + if 'platforms' in plugin_info: + if sys.platform not in plugin_info['platforms']: + log.info(u"{plugin_file} is not used on this platform, skipping".format(**plugin_info)) + continue + # import name is used to differentiate plugins if 'import_name' not in plugin_info: plugin_info['import_name'] = plug - if 'import_name' in imported_names: + if plugin_info['import_name'] in imported_names: log.warning(_(u"there is already a plugin named {}, ignoring new one").format(plugin_info['import_name'])) continue if plugin_info['import_name'] == C.WID_SELECTOR: + if plugin_type != C.PLUG_TYPE_WID: + log.error(u"{import_name} import name can only be used with {type_} type, skipping {name}".format(type_=C.PLUG_TYPE_WID, **plugin_info)) + continue # if WidgetSelector exists, it will be our default widget self.default_wid = plugin_info # we want everything optional, so we use plugin file name # if actual name is not found if 'name' not in plugin_info: - plugin_info['name'] = plug[plug.rfind('_')+1:] + name_start = 8 + len(plugin_type) + plugin_info['name'] = plug[name_start:] # we need to load the kv file if 'kv_file' not in plugin_info: @@ -406,7 +462,7 @@ # factory is used to create the instance # if not found, we use a defaut one with getOrCreateWidget if 'factory' not in plugin_info: - plugin_info['factory'] = self._defaultFactory + plugin_info['factory'] = default_factory # icons for size in ('small', 'medium'): @@ -421,38 +477,53 @@ path = C.DEFAULT_WIDGET_ICON.format(media=self.media_dir) plugin_info[key] = path - self._plg_wids.append(plugin_info) + plugins_set.append(plugin_info) if not self._plg_wids: log.error(_(u"no widget plugin found")) return # we want widgets sorted by names self._plg_wids.sort(key=lambda p: p['name'].lower()) + self._plg_wids_upload.sort(key=lambda p: p['name'].lower()) if self.default_wid is None: # we have no selector widget, we use the first widget as default self.default_wid = self._plg_wids[0] - def getPluggedWidgets(self, except_cls=None): + def _getPluginsSet(self, type_): + if type_ == C.PLUG_TYPE_WID: + return self._plg_wids + elif type_ == C.PLUG_TYPE_UPLOAD: + return self._plg_wids_upload + else: + raise KeyError(u"{} plugin type is unknown".format(type_)) + + def getPluggedWidgets(self, type_=C.PLUG_TYPE_WID, except_cls=None): """get available widgets plugin infos + @param type_(unicode): type of widgets to get + one of C.PLUG_TYPE_* constant @param except_cls(None, class): if not None, widgets from this class will be excluded @return (iter[dict]): available widgets plugin infos """ - for plugin_data in self._plg_wids: + plugins_set = self._getPluginsSet(type_) + for plugin_data in plugins_set: if plugin_data['main'] == except_cls: continue yield plugin_data - def getPluginInfo(self, **kwargs): + def getPluginInfo(self, type_=C.PLUG_TYPE_WID, **kwargs): """get first plugin info corresponding to filters + @param type_(unicode): type of widgets to get + one of C.PLUG_TYPE_* constant @param **kwargs: filter(s) to use, each key present here must also exist and be of the same value in requested plugin info @return (dict, None): found plugin info or None """ - for plugin_info in self._plg_wids: + plugins_set = self._getPluginsSet(type_) + for plugin_info in plugins_set: for k, w in kwargs.iteritems(): try: if plugin_info[k] != w: