304 lines
11 KiB
Python
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)
|