Mercurial > libervia-desktop-kivy
changeset 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 |
files | src/cagou.kv src/cagou.py src/cagou_widget.kv src/cagou_widget.py src/constants.py src/contact_list.kv src/contact_list.py src/plugin_wid_contact_list.kv src/plugin_wid_contact_list.py src/plugin_wid_widget_selector.kv src/plugin_wid_widget_selector.py src/widget_selector.kv src/widget_selector.py src/widgets_handler.py |
diffstat | 14 files changed, 342 insertions(+), 213 deletions(-) [+] |
line wrap: on
line diff
--- a/src/cagou.kv Fri Jul 08 20:18:43 2016 +0200 +++ b/src/cagou.kv Sat Jul 09 16:02:44 2016 +0200 @@ -15,7 +15,5 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. #:include profile_manager.kv -#:include contact_list.kv #:include widgets_handler.kv #:include cagou_widget.kv -#:include widget_selector.kv
--- 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__':
--- a/src/cagou_widget.kv Fri Jul 08 20:18:43 2016 +0200 +++ b/src/cagou_widget.kv Sat Jul 09 16:02:44 2016 +0200 @@ -15,15 +15,13 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. +<HeaderWidgetButton>: + size_hint_y: None + height: 44 + <HeaderWidgetSelector>: size_hint: 0.3, 1 - Button: - text: "widget 1" - size_hint_y: None - height: 44 - on_release: root.select('item1') - Button: - text: "widget 2" + auto_width: False <CagouWidget>: BoxLayout:
--- a/src/cagou_widget.py Fri Jul 08 20:18:43 2016 +0200 +++ b/src/cagou_widget.py Sat Jul 09 16:02:44 2016 +0200 @@ -20,16 +20,35 @@ from sat.core import log as logging log = logging.getLogger(__name__) +from kivy.uix.button import Button from kivy.uix.boxlayout import BoxLayout from kivy.uix.dropdown import DropDown +class HeaderWidgetButton(Button): + pass + + class HeaderWidgetSelector(DropDown): - pass + + def __init__(self, cagou_widget): + super(HeaderWidgetSelector, self).__init__() + host = cagou_widget.host + for plugin_info in host.getPluggedWidgets(except_cls=cagou_widget.__class__): + btn = HeaderWidgetButton(text=plugin_info["name"]) + btn.bind(on_release=lambda btn, plugin_info=plugin_info: cagou_widget.switchWidget(plugin_info)) + self.add_widget(btn) class CagouWidget(BoxLayout): - def __init__(self): + def __init__(self, host): + self.host = host BoxLayout.__init__(self, orientation="vertical") - self.selector = HeaderWidgetSelector() + self.selector = HeaderWidgetSelector(self) + + def switchWidget(self, plugin_info): + self.selector.dismiss() + factory = plugin_info["factory"] + new_widget = factory(self.host, plugin_info, None, None) + self.host.switchWidget(self, new_widget)
--- a/src/constants.py Fri Jul 08 20:18:43 2016 +0200 +++ b/src/constants.py Sat Jul 09 16:02:44 2016 +0200 @@ -24,3 +24,4 @@ APP_NAME = "Cagou" LOG_OPT_SECTION = APP_NAME.lower() CONFIG_SECTION = APP_NAME.lower() + WID_SELECTOR = 'selector'
--- a/src/contact_list.kv Fri Jul 08 20:18:43 2016 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,27 +0,0 @@ -# Cagou: desktop/mobile frontend for Salut à Toi XMPP client -# Copyright (C) 2016 Jérôme Poisson (goffi@goffi.org) - -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. - -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. - -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. - -<ContactItem>: - spacing: 20 - Widget: - Avatar: - source: root.data.get('avatar', app.default_avatar) - size_hint: (None, None) - Label: - text: root.jid - bold: True - size_hint: (None, None) - Widget:
--- a/src/contact_list.py Fri Jul 08 20:18:43 2016 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,67 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Cagou: desktop/mobile frontend for Salut à Toi XMPP client -# Copyright (C) 2016 Jérôme Poisson (goffi@goffi.org) - -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. - -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. - -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. - - -from sat.core import log as logging -log = logging.getLogger(__name__) -from sat_frontends.quick_frontend.quick_contact_list import QuickContactList -from kivy.uix.boxlayout import BoxLayout -from kivy.uix.listview import ListView -from kivy.adapters.listadapter import ListAdapter -from kivy import properties -from cagou_widget import CagouWidget -import image - - -class Avatar(image.Image): - pass - - -class ContactItem(BoxLayout): - data = properties.DictProperty() - jid = properties.StringProperty('') - - def __init__(self, **kwargs): - BoxLayout.__init__(self, **kwargs) - - -class ContactList(QuickContactList, CagouWidget): - - def __init__(self, host, target, profiles): - QuickContactList.__init__(self, host, profiles) - CagouWidget.__init__(self) - self.adapter = ListAdapter(data={}, - cls=ContactItem, - args_converter=self.contactDataConverter, - selection_mode='multiple', - allow_empty_selection=True, - ) - self.add_widget(ListView(adapter=self.adapter)) - self.update() - - def contactDataConverter(self, idx, bare_jid): - return {"jid": bare_jid, "data": self._items_cache[bare_jid]} - - def update(self, entities=None, type_=None, profile=None): - log.info("update: %s %s %s" % (entities, type_, profile)) - # FIXME: for now we update on each event - # if entities is None and type_ is None: - self._items_cache = self.items - self.adapter.data = self.items_sorted -
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/plugin_wid_contact_list.kv Sat Jul 09 16:02:44 2016 +0200 @@ -0,0 +1,27 @@ +# Cagou: desktop/mobile frontend for Salut à Toi XMPP client +# Copyright (C) 2016 Jérôme Poisson (goffi@goffi.org) + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. + +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +<ContactItem>: + spacing: 20 + Widget: + Avatar: + source: root.data.get('avatar', app.default_avatar) + size_hint: (None, None) + Label: + text: root.jid + bold: True + size_hint: (None, None) + Widget:
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/plugin_wid_contact_list.py Sat Jul 09 16:02:44 2016 +0200 @@ -0,0 +1,75 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Cagou: desktop/mobile frontend for Salut à Toi XMPP client +# Copyright (C) 2016 Jérôme Poisson (goffi@goffi.org) + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. + +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + + +from sat.core import log as logging +log = logging.getLogger(__name__) +from sat.core.i18n import _ +from sat_frontends.quick_frontend.quick_contact_list import QuickContactList +from kivy.uix.boxlayout import BoxLayout +from kivy.uix.listview import ListView +from kivy.adapters.listadapter import ListAdapter +from kivy import properties +from cagou_widget import CagouWidget +import image + + +PLUGIN_INFO = { + "name": _(u"contacts"), + "main": "ContactList", + "description": _(u"list of contacts"), +} + + +class Avatar(image.Image): + pass + + +class ContactItem(BoxLayout): + data = properties.DictProperty() + jid = properties.StringProperty('') + + def __init__(self, **kwargs): + BoxLayout.__init__(self, **kwargs) + + +class ContactList(QuickContactList, CagouWidget): + + def __init__(self, host, target, profiles): + QuickContactList.__init__(self, host, profiles) + CagouWidget.__init__(self, host) + self.adapter = ListAdapter(data={}, + cls=ContactItem, + args_converter=self.contactDataConverter, + selection_mode='multiple', + allow_empty_selection=True, + ) + self.add_widget(ListView(adapter=self.adapter)) + self.update() + + def contactDataConverter(self, idx, bare_jid): + return {"jid": bare_jid, "data": self._items_cache[bare_jid]} + + def update(self, entities=None, type_=None, profile=None): + log.info("update: %s %s %s" % (entities, type_, profile)) + # FIXME: for now we update on each event + # if entities is None and type_ is None: + self._items_cache = self.items + self.adapter.data = self.items_sorted +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/plugin_wid_widget_selector.kv Sat Jul 09 16:02:44 2016 +0200 @@ -0,0 +1,24 @@ +# Cagou: desktop/mobile frontend for Salut à Toi XMPP client +# Copyright (C) 2016 Jérôme Poisson (goffi@goffi.org) + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. + +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +<WidgetSelItem>: + spacing: 20 + Widget: + Label: + text: root.plugin_info["name"] + bold: True + size_hint: (None, None) + Widget:
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/plugin_wid_widget_selector.py Sat Jul 09 16:02:44 2016 +0200 @@ -0,0 +1,82 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Cagou: desktop/mobile frontend for Salut à Toi XMPP client +# Copyright (C) 2016 Jérôme Poisson (goffi@goffi.org) + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. + +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + + +from sat.core import log as logging +log = logging.getLogger(__name__) +from sat.core.i18n import _ +from constants import Const as C +from kivy.uix.boxlayout import BoxLayout +from kivy.uix.listview import ListView +from kivy.adapters.listadapter import ListAdapter +from kivy import properties +from kivy.uix.behaviors import ButtonBehavior +from cagou_widget import CagouWidget + + +PLUGIN_INFO = { + "name": _(u"widget selector"), + "import_name": C.WID_SELECTOR, + "main": "WidgetSelector", + "description": _(u"show available widgets and allow to select one"), +} + + +class WidgetSelItem(ButtonBehavior, BoxLayout): + plugin_info = properties.DictProperty() + + def __init__(self, **kwargs): + super(WidgetSelItem, self).__init__(**kwargs) + self.host = kwargs['host'] + + def select(self, *args): + log.debug(u"widget selection: {}".format(self.plugin_info["name"])) + factory = self.plugin_info["factory"] + self.host.switchWidget(self, factory(self.host, self.plugin_info, None, None)) + + def deselect(self, *args): + pass + + +class WidgetSelector(CagouWidget): + + def __init__(self, host): + super(WidgetSelector, self).__init__(host) + self.host = host + self.adapter = ListAdapter( + data=host.getPluggedWidgets(except_cls=self.__class__), + cls=WidgetSelItem, + args_converter=self.dataConverter, + selection_mode='single', + allow_empty_selection=True, + ) + self.add_widget(ListView(adapter=self.adapter)) + + @classmethod + def factory(cls, host, plugin_info, target, profiles): + return cls(host) + + def dataConverter(self, idx, plugin_info): + return { + "host": self.host, + "plugin_info": plugin_info, + } + + +PLUGIN_INFO["factory"] = WidgetSelector.factory
--- a/src/widget_selector.kv Fri Jul 08 20:18:43 2016 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,24 +0,0 @@ -# Cagou: desktop/mobile frontend for Salut à Toi XMPP client -# Copyright (C) 2016 Jérôme Poisson (goffi@goffi.org) - -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. - -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. - -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. - -<WidgetSelItem>: - spacing: 20 - Widget: - Label: - text: root.widget_type - bold: True - size_hint: (None, None) - Widget:
--- a/src/widget_selector.py Fri Jul 08 20:18:43 2016 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,69 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Cagou: desktop/mobile frontend for Salut à Toi XMPP client -# Copyright (C) 2016 Jérôme Poisson (goffi@goffi.org) - -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. - -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. - -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. - - -from sat.core import log as logging -log = logging.getLogger(__name__) -from kivy.uix.boxlayout import BoxLayout -from kivy.uix.listview import ListView -from kivy.adapters.listadapter import ListAdapter -from kivy import properties -from contact_list import ContactList -from kivy.uix.behaviors import ButtonBehavior -from cagou_widget import CagouWidget - - -class WidgetSelItem(ButtonBehavior, BoxLayout): - widget_type = properties.StringProperty('') - - def __init__(self, **kwargs): - super(WidgetSelItem, self).__init__(**kwargs) - self.host = kwargs['host'] - self.callback = kwargs['callback'] - - def select(self, *args): - log.debug(u"widget selection: {}".format(self.widget_type)) - self.host.switchWidget(self, self.callback()) - - def deselect(self, *args): - pass - - -class WidgetSelector(CagouWidget): - - def __init__(self, host): - super(WidgetSelector, self).__init__() - self.host = host - self.widget_factory = { - 'contacts': lambda: host.widgets.getOrCreateWidget(ContactList, None, on_new_widget=None), - } - self.adapter = ListAdapter(data=self.widget_factory, - cls=WidgetSelItem, - args_converter=self.dataConverter, - selection_mode='single', - allow_empty_selection=True, - ) - self.add_widget(ListView(adapter=self.adapter)) - - def dataConverter(self, idx, widget_type): - return { - "host": self.host, - "widget_type": widget_type, - "callback": self.widget_factory[widget_type] - }
--- a/src/widgets_handler.py Fri Jul 08 20:18:43 2016 +0200 +++ b/src/widgets_handler.py Sat Jul 09 16:02:44 2016 +0200 @@ -23,7 +23,6 @@ from kivy.uix.boxlayout import BoxLayout from kivy.uix.button import Button from kivy import properties -from widget_selector import WidgetSelector NEW_WIDGET_DIST = 10 @@ -68,8 +67,10 @@ class WidgetsHandler(BoxLayout): - def __init__(self, host, wid, **kw): + def __init__(self, host, wid=None, **kw): self.host = host + if wid is None: + wid=self.default_widget self.vert_wid = self.hor_wid = None BoxLayout.__init__(self, orientation="vertical", **kw) self.blh = BoxLayout(orientation="horizontal") @@ -80,6 +81,10 @@ self.blh.add_widget(self.blv) self.add_widget(self.blh) + @property + def default_widget(self): + return self.host.default_wid['factory'](self.host, self.host.default_wid, None, None) + def removeWidget(self, vertical): if vertical and self.vert_wid is not None: self.remove_widget(self.vert_wid) @@ -91,11 +96,11 @@ def setWidgetSize(self, vertical, size): if vertical: if self.vert_wid is None: - self.vert_wid = WidgetsHandler(self.host, WidgetSelector(self.host), size_hint=(1, None)) + self.vert_wid = WidgetsHandler(self.host, self.default_widget, size_hint=(1, None)) self.add_widget(self.vert_wid, len(self.children)) self.vert_wid.height=size else: if self.hor_wid is None: - self.hor_wid = WidgetsHandler(self.host, WidgetSelector(self.host), size_hint=(None, 1)) + self.hor_wid = WidgetsHandler(self.host, self.default_widget, size_hint=(None, 1)) self.blh.add_widget(self.hor_wid, len(self.blh.children)) self.hor_wid.width=size