core (XMLUI), frontends: advancedListContainer part 2:

This commit is contained in:
Goffi 2014-02-04 18:21:51 +01:00
commit 18aeb3a6aa
4 changed files with 142 additions and 36 deletions

View file

@ -28,13 +28,13 @@ from sat_frontends.tools import xmlui
class PrimitivusEvents(object):
""" Used to manage change event of Primitivus widgets """
def _change_callback(self, ctrl, *args, **kwargs):
def _event_callback(self, ctrl, *args, **ktkwargs):
"""" Call xmlui callback and ignore any extra argument """
args[-1](ctrl)
def _xmluiOnChange(self, callback):
""" Call callback with widget as only argument """
urwid.connect_signal(self, 'change', self._change_callback, callback)
urwid.connect_signal(self, 'change', self._event_callback, callback)
class PrimitivusEmptyWidget(xmlui.EmptyWidget, urwid.Text):
@ -132,17 +132,29 @@ class PrimitivusListWidget(xmlui.ListWidget, sat_widgets.List, PrimitivusEvents)
return [option.value for option in self.getSelectedValues()]
class PrimitivusAdvancedListContainer(xmlui.AdvancedListContainer, sat_widgets.TableContainer):
class PrimitivusAdvancedListContainer(xmlui.AdvancedListContainer, sat_widgets.TableContainer, PrimitivusEvents):
def __init__(self, parent, columns):
options = {'ADAPT':(), 'HIGHLIGHT':()}
sat_widgets.TableContainer.__init__(self, columns=columns, options=options)
def __init__(self, parent, columns, selectable='no'):
options = {'ADAPT':()}
if selectable != 'no':
options['HIGHLIGHT'] = ()
sat_widgets.TableContainer.__init__(self, columns=columns, options=options, row_selectable = selectable!='no')
def _xmluiAppend(self, widget):
self.addWidget(widget)
def _xmluiAddRow(self):
pass
def _xmluiAddRow(self, idx):
self.setRowIndex(idx)
def _xmluiGetSelectedWidgets(self):
return self.getSelectedWidgets()
def _xmluiGetSelectedIndex(self):
return self.getSelectedIndex()
def _xmluiOnSelect(self, callback):
""" Call callback with widget as only argument """
urwid.connect_signal(self, 'click', self._event_callback, callback)
class PrimitivusPairsContainer(xmlui.PairsContainer, sat_widgets.TableContainer):

View file

@ -177,6 +177,10 @@ class XMLUI(object):
self._main_cont = None
self.constructUI(xml_data)
def escape(self, name):
""" return escaped name for forms """
return u"%s%s" % (Const.SAT_FORM_PREFIX, name)
@property
def main_cont(self):
return self._main_cont
@ -220,8 +224,18 @@ class XMLUI(object):
columns = int(node.getAttribute('columns'))
except (TypeError, ValueError):
raise DataError("Invalid columns")
cont = self.widget_factory.createAdvancedListContainer(parent, columns)
self._parseChilds(cont, node, ('row',))
selectable = node.getAttribute('selectable') or 'no'
auto_index = node.getAttribute('auto_index') == 'true'
data = {'index': 0} if auto_index else None
cont = self.widget_factory.createAdvancedListContainer(parent, columns, selectable)
callback_id = node.getAttribute("callback") or None
if callback_id is not None:
if selectable == 'no':
raise ValueError("can't have selectable=='no' and callback_id at the same time")
cont._xmlui_callback_id = callback_id
cont._xmluiOnSelect(self.onAdvListSelect)
self._parseChilds(cont, node, ('row',), data)
else:
warning(_("Unknown container [%s], using default one") % type_)
cont = self.widget_factory.createVerticalContainer(parent)
@ -246,7 +260,12 @@ class XMLUI(object):
self._parseChilds(new_tab, node, ('widget', 'container'))
elif node.nodeName == 'row':
parent._xmluiAddRow()
try:
index = str(data['index'])
data['index'] += 1
except TypeError:
index = node.getAttribute('index') or None
parent._xmluiAddRow(index)
self._parseChilds(parent, node, ('widget', 'container'))
elif node.nodeName == "widget":
@ -299,11 +318,11 @@ class XMLUI(object):
try:
ctrl._xmluiOnChange(self.onParamChange)
ctrl._param_category = self._current_category
ctrl._param_name = name.split(Const.SAT_PARAM_SEPARATOR)[1]
except AttributeError:
if not isinstance(ctrl, (EmptyWidget, TextWidget)):
warning(_("No change listener on [%s]" % ctrl))
ctrl._xmlui_name = name
parent._xmluiAppend(ctrl)
else:
@ -351,6 +370,25 @@ class XMLUI(object):
assert(self.type == "param")
self.param_changed.add(ctrl)
def onAdvListSelect(self, ctrl):
data = {}
widgets = ctrl._xmluiGetSelectedWidgets()
for wid in widgets:
try:
name = self.escape(wid._xmlui_name)
value = wid._xmluiGetValue()
ret[name] = value
except AttributeError:
pass
idx = ctrl._xmluiGetSelectedIndex()
if idx is not None:
data['index'] = idx
callback_id = ctrl._xmlui_callback_id
if callback_id is None:
warning(_("No callback_id found"))
return
self.host.launchAction(callback_id, data, profile_key = self.host.profile)
def onButtonPress(self, button):
""" Called when an XMLUI button is clicked
Launch the action associated to the button
@ -360,11 +398,12 @@ class XMLUI(object):
callback_id, fields = button._xmlui_param_id
data = {}
for field in fields:
escaped = self.escape(field)
ctrl = self.ctrl_list[field]
if isinstance(ctrl['control'], ListWidget):
data[field] = u'\t'.join(ctrl['control']._xmluiGetSelected())
data[escaped] = u'\t'.join(ctrl['control']._xmluiGetSelected())
else:
data[field] = ctrl['control']._xmluiGetValue()
data[escaped] = ctrl['control']._xmluiGetValue()
self.host.launchAction(callback_id, data, profile_key = self.host.profile)
def onFormSubmitted(self, ignore=None):
@ -374,7 +413,7 @@ class XMLUI(object):
"""
selected_values = []
for ctrl_name in self.ctrl_list:
escaped = u"%s%s" % (Const.SAT_FORM_PREFIX, ctrl_name)
escaped = self.escape(ctrl_name)
ctrl = self.ctrl_list[ctrl_name]
if isinstance(ctrl['control'], ListWidget):
selected_values.append((escaped, u'\t'.join(ctrl['control']._xmluiGetSelectedValues())))
@ -407,6 +446,7 @@ class XMLUI(object):
value = u'\t'.join(ctrl._xmluiGetSelectedValues())
else:
value = ctrl._xmluiGetValue()
self.host.bridge.setParam(ctrl._param_name, value, ctrl._param_category,
param_name = ctr._xmlui_name.split(Const.SAT_PARAM_SEPARATOR)[1]
self.host.bridge.setParam(param_name, value, ctrl._param_category,
profile_key=self.host.profile)
self._xmluiClose()

View file

@ -154,16 +154,43 @@ class WixContainer(object):
self.sizer.Add(widget, self._xmlui_proportion, flag=wx.EXPAND)
class AdvancedListContainer(WixContainer, xmlui.AdvancedListContainer, wx.Panel):
class AdvancedListContainer(WixContainer, xmlui.AdvancedListContainer, wx.ScrolledWindow):
def __init__(self, parent, columns):
wx.Panel.__init__(self, parent)
def __init__(self, parent, columns, selectable='no'):
wx.ScrolledWindow.__init__(self, parent)
self._xmlui_selectable = selectable != 'no'
if selectable:
columns += 1
self.sizer = wx.FlexGridSizer(cols=columns)
self.SetSizer(self.sizer)
self._xmlui_select_cb = None
self._xmlui_select_idx = None
self._xmlui_select_widgets = []
def _xmluiAddRow(self):
pass
def _xmluiAddRow(self, idx):
# XXX: select_button is a Q&D way to implement row selection
# FIXME: must be done properly
if not self._xmlui_selectable:
return
select_button = wx.Button(self, wx.ID_OK, label=_("select"))
self.sizer.Add(select_button)
def click_cb(event, idx=idx):
cb = self._xmlui_select_cb
self._xmlui_select_idx = idx
# TODO: fill self._xmlui_select_widgets
if cb is not None:
cb(self)
event.Skip()
self.Bind(wx.EVT_BUTTON, click_cb)
def _xmluiGetSelectedWidgets(self):
return self._xmlui_select_widgets
def _xmluiGetSelectedIndex(self):
return self._xmlui_select_idx
def _xmluiOnSelect(self, callback):
self._xmlui_select_cb = callback
class PairsContainer(WixContainer, xmlui.PairsContainer, wx.Panel):

View file

@ -75,18 +75,15 @@ def dataForm2XMLUI(form, submit_id, session_id=None):
if form.instructions:
form_ui.addText('\n'.join(form.instructions), 'instructions')
labels = [field for field in form.fieldList if field.label]
if labels:
# if there is no label, we don't need to use pairs
form_ui.changeContainer("pairs")
form_ui.changeContainer("pairs")
for field in form.fieldList:
widget_type, widget_args, widget_kwargs = _dataFormField2XMLUIData(field)
if labels:
if field.label:
form_ui.addLabel(field.label)
else:
form_ui.addEmpty()
label = field.label or field.var
if label:
form_ui.addLabel(label)
else:
form_ui.addEmpty()
form_ui.addWidget(widget_type, *widget_args, **widget_kwargs)
@ -151,11 +148,15 @@ def XMLUIResult2DataFormResult(xmlui_data):
"""
return {key[len(SAT_FORM_PREFIX):]: value for key, value in xmlui_data.iteritems() if key.startswith(SAT_FORM_PREFIX)}
def formEscape(name):
""" Return escaped name for forms """
return u"%s%s" % (SAT_FORM_PREFIX, name)
def XMLUIResultToElt(xmlui_data):
""" Construct result domish.Element from XMLUI result
@xmlui_data: data returned by frontends for XMLUI form
"""
form = data_form.Form('result')
form = data_form.Form('submit')
form.makeFields(XMLUIResult2DataFormResult(xmlui_data))
return form.toElement()
@ -310,6 +311,11 @@ class RowElement(Element):
def __init__(self, parent):
assert(isinstance(parent, AdvancedListContainer))
super(RowElement, self).__init__(parent.xmlui, parent)
if parent.next_row_idx is not None:
if parent.auto_index:
raise exceptions.DataError(_("Can't set row index if auto_index is True"))
self.elem.setAttribute('index', parent.next_row_idx)
parent.next_row_idx = None
class HeaderElement(Element):
@ -393,12 +399,19 @@ class TabsContainer(Container):
class AdvancedListContainer(Container):
type = "advanced_list"
def __init__(self, xmlui, name=None, headers=None, items=None, columns=None, parent=None):
def __init__(self, xmlui, callback_id=None, name=None, headers=None, items=None, columns=None, selectable = 'no', auto_index = False, parent=None):
"""Create an advanced list
@param headers: optional headers informations
@param items: list of Item instances
@param callback_id: id of the method to call when selection is done
@param items: list of widgets to add (just the first row)
@param columns: number of columns in this table, or None to autodetect
@param selectable: one of:
'no': nothing is done
'single': one row can be selected
@param auto_index: if True, indexes will be generated by frontends, starting from 0
@return: created element
"""
assert selectable in ('no', 'single')
if not items and columns is None:
raise DataError(_("either items or columns need do be filled"))
if headers is None:
@ -418,19 +431,33 @@ class AdvancedListContainer(Container):
if items:
self.addItems(items)
self.elem.setAttribute('columns', str(self._columns))
if callback_id is not None:
self.elem.setAttribute('callback', callback_id)
self.elem.setAttribute('selectable', selectable)
self.auto_index = auto_index
if auto_index:
self.elem.setAttribute('auto_index', 'true')
self.next_row_idx = None
def addHeaders(self, headers):
for header in headers:
self.addHeader(header)
def addHeader(self, header):
pass # TODO
def addItems(self, items):
for item in items:
self.append(item)
def setRowIndex(self, idx):
""" Set index for next row
index are returned when a row is selected, in data's "index" key
@param idx: string index to associate to the next row
"""
self.next_row_idx = idx
def append(self, child):
if isinstance(child, RowElement):
return super(AdvancedListContainer, self).append(child)