Mercurial > libervia-desktop-kivy
diff src/cagou.py @ 14:21a432afd06d
plugin system, first draft:
- widgets are now handled with plugins, ContactList and WidgetSelector have been changed to be pluggins
- everything in PLUGIN_INFO (including PLUGIN_INFO itself) is optional. Best guess is used if a value is missing
- WidgetsHandler use default widget from plugins, which is WidgetSelector for now
- PLUGIN_INFO is used in the same way as for backed plugins, with (for now) following possible keys:
- "name": human readable name
- "description": long description of what widget do
- "import_name": unique short name used as reference for import
- "main": main class name
- "factory": callback used to instanciate a widget (see Cagou._defaultFactory)
- "kv_file": path to the kv language file to load (same filename with ".kv" will be used if key is not found)
other data should be added quickly, as a path to a file used as icon
- host.getPluggedWidgets can be used to find loaded widgets. except_cls can be used to excluse a class (self.__class__ usually)
- fixed host.switchWidget when old widget is already a CagouWidget
- CagouWidget header new display the available widgets
author | Goffi <goffi@goffi.org> |
---|---|
date | Sat, 09 Jul 2016 16:02:44 +0200 |
parents | 12a189fbb9ba |
children | 56838ad5c84b |
line wrap: on
line diff
--- a/src/cagou.py Fri Jul 08 20:18:43 2016 +0200 +++ b/src/cagou.py Sat Jul 09 16:02:44 2016 +0200 @@ -18,6 +18,7 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. +from sat.core.i18n import _ import logging_setter logging_setter.set_logging() from constants import Const as C @@ -31,13 +32,15 @@ import kivy.support kivy.support.install_gobject_iteration() from kivy.app import App +from kivy.lang import Builder import xmlui from profile_manager import ProfileManager from widgets_handler import WidgetsHandler from kivy.uix.boxlayout import BoxLayout -from widget_selector import WidgetSelector from cagou_widget import CagouWidget +from importlib import import_module import os.path +import glob class CagouRootWidget(BoxLayout): @@ -69,13 +72,88 @@ self.app.host = self media_dir = self.app.media_dir = self.bridge.getConfig("", "media_dir") self.app.default_avatar = os.path.join(media_dir, "misc/default_avatar.png") + self._plg_wids = [] # widget plugins + self._import_plugins() def run(self): self.app.run() + def _defaultFactory(self, host, plugin_info, target, profiles): + """factory used to create widget instance when PLUGIN_INFO["factory"] is not set""" + main_cls = plugin_info['main'] + return self.widgets.getOrCreateWidget(main_cls, target, on_new_widget=None, profiles=profiles) + + def _import_plugins(self): + """Import all plugins""" + self.default_wid = None + plugins_path = os.path.dirname(__file__) + plug_lst = [os.path.splitext(p)[0] for p in map(os.path.basename, glob.glob(os.path.join(plugins_path, "plugin*.py")))] + imported_names = set() # use to avoid loading 2 times plugin with same import name + for plug in plug_lst: + mod = import_module(plug) + try: + plugin_info = mod.PLUGIN_INFO + except AttributeError: + plugin_info = {} + + # 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: + 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 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:] + + # we need to load the kv file + if 'kv_file' not in plugin_info: + plugin_info['kv_file'] = u'{}.kv'.format(plug) + Builder.load_file(plugin_info['kv_file']) + + # what is the main class ? + main_cls = getattr(mod, plugin_info['main']) + plugin_info['main'] = main_cls + + # 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 + + self._plg_wids.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()) + + 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): + """get available widgets plugin infos + + @param except_cls(None, class): if not None, + widgets from this class will be excluded + @return (list[dict]): available widgets plugin infos + """ + lst = self._plg_wids[:] + if except_cls is not None: + for plugin_info in lst: + if plugin_info['main'] == except_cls: + lst.remove(plugin_info) + break + return lst + def plugging_profiles(self): - widget_selector = WidgetSelector(self) - self.app.root.change_widgets([WidgetsHandler(self, widget_selector)]) + self.app.root.change_widgets([WidgetsHandler(self)]) def setPresenceStatus(self, show='', status=None, profile=C.PROF_KEY_NONE): log.info(u"Profile presence status set to {show}/{status}".format(show=show, status=status)) @@ -86,13 +164,22 @@ old(CagouWidget): CagouWidget instance or a child new(CagouWidget): new widget instance """ - for w in old.walk_reverse(): - if isinstance(w, CagouWidget): - parent = w.parent - idx = parent.children.index(w) - parent.remove_widget(w) - parent.add_widget(new, index=idx) - break + to_change = None + if isinstance(old, CagouWidget): + to_change = old + else: + for w in old.walk_reverse(): + if isinstance(w, CagouWidget): + to_change = w + break + + if to_change is None: + log.error(u"no CagouWidget found when trying to switch widget") + else: + parent = to_change.parent + idx = parent.children.index(to_change) + parent.remove_widget(to_change) + parent.add_widget(new, index=idx) if __name__ == '__main__':