# Copyright (C) 2007-2015 various contributors (see AUTHORS) # # This file is part of Python-EFL. # # Python-EFL is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 3 of the License, or (at your option) any later version. # # Python-EFL 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 # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this Python-EFL. If not, see . """ TODO ==== * mainmenu ?? * test more widget items * add all the supported widget to KLASSES * documentation for this module * fix (in erigo) the label with markup * properties that need to be fixed: - visibility (will be fixed in eo) * scrollable interface, widgets must be changed to always inherit from Scroller * how to manage translations?? * what when we create the same widget more than one time ? * Radio.group_add accept an obj, not a string !!! * Widget that seems not usable in erigo itsef: - Icon: how to set a standard icon? - ActionSlider: how to configure it? """ import os import json import importlib from collections import OrderedDict try: basestring # py2 except NameError: basestring = str # py3 ################################################################### ## NOTE/TODO: importlib is only implemented from python 2.7 ## From py-efl 1.14 we will need to rise py min version ################################################################### JSON_Version = 3 KLASSES = { 'Elm_Actionslider': ('efl.elementary.actionslider', 'Actionslider'), 'Elm_Bg': ('efl.elementary.background', 'Background'), 'Elm_Box': ('efl.elementary.box', 'Box'), 'Elm_Button': ('efl.elementary.button', 'Button'), 'Elm_Clock': ('efl.elementary.clock', 'Clock'), 'Elm_Check': ('efl.elementary.check', 'Check'), 'Elm_Ctxpopup': ('efl.elementary.ctxpopup', 'Ctxpopup'), 'Elm_Dayselector': ('efl.elementary.dayselector', 'Dayselector'), 'Elm_Diskselector': ('efl.elementary.diskselector', 'Diskselector'), 'Elm_Entry': ('efl.elementary.entry', 'Entry'), 'Elm_Flip': ('efl.elementary.flip', 'Flip'), 'Elm_Frame': ('efl.elementary.frame', 'Frame'), 'Elm_Genlist': ('efl.elementary.genlist', 'Genlist'), 'Elm_Gengrid': ('efl.elementary.gengrid', 'Gengrid'), 'Elm_Icon': ('efl.elementary.icon', 'Icon'), 'Elm_Image': ('efl.elementary.image', 'Image'), 'Elm_Label': ('efl.elementary.label', 'Label'), 'Elm_Layout': ('efl.elementary.layout', 'Layout'), 'Elm_Menu': ('efl.elementary.menu', 'Menu'), 'Elm_Radio': ('efl.elementary.radio', 'Radio'), 'Elm_Separator': ('efl.elementary.separator', 'Separator'), 'Elm_Table': ('efl.elementary.table', 'Table'), 'Elm_Toolbar': ('efl.elementary.toolbar', 'Toolbar'), 'Elm_Win': ('efl.elementary.window', 'Window'), } class ErigoGui(object): """ TODO: Class doc """ def __init__ (self, json_file=None, json_string=None, resources_path=None, verbose=False): self._verbose = verbose self._widgets = {} # key: widget_name data: widget_instance self._data = None # parsed json data if resources_path: self._res_path = resources_path elif json_file: self._res_path = os.path.dirname(json_file) else: self._res_path = os.getcwd() # :/ if json_file is not None: self._print('\n### Generating gui from file: %s' % json_file) self._data = json.load(open(json_file), object_pairs_hook=OrderedDict) elif json_string is not None: self._print('\n### Generating gui from string') self._data = json.loads(json_string, object_pairs_hook=OrderedDict) else: raise RuntimeError('One of "file" or "json_string" is mandatory') if int(self._data['JSON_Version']) != JSON_Version: raise RuntimeError('Wrong json version. Found: %s, wanted: %s' % \ (self._data['JSON_Version'], JSON_Version)) for w_name in self._data['Settings'].get('start_points', []): _widget_generate(self, w_name) def widget_generated(self, name, widget): """ User can redefine this method to be informed when a public widget is created. """ self._print('Widget generated: %s' % widget.__class__) def _print(self, msg): if self._verbose: print(msg) def _resource_find(self, value): name, group = value if name in self._data['Resources']['Images']: fname = os.path.join(self._res_path, self._data['Resources']['Images'][name]) return (fname, group) return value def _widget_generate(self, name, parent_name=None): self._print('\n### Generating widget: %s' % name) # get the widget class object from the correct module w_data = self._data['Widgets'][name] eo_klass_name = w_data['Desc']['class'] module_name, klass_name = KLASSES[eo_klass_name] mod = importlib.import_module(module_name) klass = getattr(mod, klass_name) # class constructor if 'Constructors' in w_data: # Custom (TODO support multiple constructors) params = w_data['Constructors'][eo_klass_name + '.constructor'] params = _params_list_parse(mod, params) w = klass(*params, parent=self._widgets.get(parent_name)) else: # Standard parent = self._widgets[parent_name or w_data['Desc']['parent']] w = klass(parent) self._widgets[name] = w # apply widget properties if 'Properties' in w_data: for eo_name, p_vals in w_data['Properties'].items(): p_name = eo_name.split('.')[-1] if p_name == 'visibility': # TODO remove this hack when Eo will be fixed p_name = 'visible' if p_name != 'constructor': self._print('property %s => %s' % (p_name, p_vals)) p_vals = _params_list_parse(mod, p_vals) if p_name == 'file': p_vals = _resource_find(self, p_vals) if len(p_vals) == 1: setattr(w, p_name, p_vals[0]) else: setattr(w, p_name, p_vals) # call widget methods if 'Methods' in w_data: for eo_name, params in w_data['Methods'].items(): meth_name = eo_name.split('.')[-1] params = _params_list_parse(mod, params) self._print('method %s %s' % (meth_name, params)) if meth_name == 'group_add': # ARGH !!!!!! params = [ self._widgets[params[0]] ] # resolv widget by name getattr(w, meth_name)(*params) # generate widget items if 'Items' in w_data: for item_name, item_data in w_data['Items'].items(): _item_generate(self, w, None, item_name, item_data) # callbacks if 'Callbacks' in w_data: for event_name, cb_data in w_data['Callbacks'].items(): if cb_data[0] == 'Modify': target_name, target_prop, values = cb_data[1:4] target_prop = target_prop.split('.')[-1] self._print('event %s (Modify) %s.%s => %s' % \ (event_name, target_name, target_prop, values)) w._callback_add(str(event_name), _property_modify_cb, self, target_name, target_prop, values) elif cb_data[0] == 'Invoke': cb_alias = cb_data[1] cb_name = self._data['Resources']['Eo_Callbacks'][cb_alias] self._print('event %s (Invoke) %s => %s' % \ (event_name, cb_alias, cb_name)) w._callback_add(str(event_name), getattr(self, cb_name)) elif cb_data[0] == 'Create': w_name, w_parent_name = cb_data[1:3] self._print('event %s (Create) %s' % (event_name, w_name)) w._callback_add(str(event_name), _widget_create_cb, self, w_name, w_parent_name) # make the widget public if requested, and call the widget_generated method if w_data['Desc'].get('public', False) is True: self._print('Installing name: %s' % name) setattr(self, name, w) self.widget_generated(name, w) # generate (and pack) children widgets if eo_klass_name == 'Elm_Win': for c_name in w_data.get('Contains', []): child = _widget_generate(self, c_name) w.resize_object_add(child) elif eo_klass_name == 'Elm_Box': for c_name in w_data.get('Contains', []): child = _widget_generate(self, c_name) w.pack_end(child) elif eo_klass_name == 'Elm_Table': for c_name, (x, y, cspan, rspan) in w_data.get('Contains', {}).items(): child = _widget_generate(self, c_name) w.pack(child, x, y, cspan, rspan) else: for c_name, (swallow,) in w_data.get('Contains', {}).items(): child = _widget_generate(self, c_name) w.part_content_set(swallow, child) return w def _item_generate(self, parent_widget, parent_item, item_name, item_data): self._print("Generating ITEM: %s" % item_name) if parent_widget.__class__.__name__ == 'Toolbar': it = parent_widget.item_append(item_data['icon'], item_data['label']) elif parent_widget.__class__.__name__ == 'Menu': it = parent_widget.item_add(parent_item, item_data['label'], item_data['icon']) elif parent_widget.__class__.__name__ == 'Diskselector': it = parent_widget.item_append(item_data['label'], item_data['icon']) elif parent_widget.__class__.__name__ == 'Ctxpopup': it = parent_widget.item_append(item_data['label'], item_data['icon']) if 'Items' in item_data: for item_name, item_data in item_data['Items'].items(): _item_generate(self, parent_widget, it, item_name, item_data) def _params_list_parse(mod, params): return [ _param_parse(mod, p) for p in params ] def _param_parse(mod, p_value): if isinstance(p_value, basestring): enum_val = None if p_value.startswith('ELM_'): enum_val = getattr(mod, p_value, None) elif p_value.startswith('EVAS_'): from efl import evas enum_val = getattr(evas, p_value, None) if enum_val is not None: return enum_val return p_value def _property_modify_cb(obj, self, widget_name, prop_name, values): w = self._widgets[widget_name] if prop_name == 'visibility': # TODO remove this hack when Eo will be fixed prop_name = 'visible' elif prop_name == 'file': values = _resource_find(self, values) setattr(w, prop_name, values[0] if len(values) == 1 else values) def _widget_create_cb(obj, self, widget_name, parent_name): _widget_generate(self, widget_name, parent_name)