python-efl/efl/utils/erigo.py

304 lines
11 KiB
Python

# 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 <http://www.gnu.org/licenses/>.
"""
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)