# HG changeset patch # User Goffi # Date 1391534511 -3600 # Node ID 7c05c39156a2f6885991b91911faafc1f3fda4bd # Parent 5174657b3378863f53148b6170d087763d6789e6 core (XMLUI), frontends: advancedListContainer part 2: diff -r 5174657b3378 -r 7c05c39156a2 frontends/src/primitivus/xmlui.py --- a/frontends/src/primitivus/xmlui.py Tue Feb 04 18:19:32 2014 +0100 +++ b/frontends/src/primitivus/xmlui.py Tue Feb 04 18:21:51 2014 +0100 @@ -28,13 +28,13 @@ 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 @@ 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): diff -r 5174657b3378 -r 7c05c39156a2 frontends/src/tools/xmlui.py --- a/frontends/src/tools/xmlui.py Tue Feb 04 18:19:32 2014 +0100 +++ b/frontends/src/tools/xmlui.py Tue Feb 04 18:21:51 2014 +0100 @@ -177,6 +177,10 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ """ 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 @@ 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() diff -r 5174657b3378 -r 7c05c39156a2 frontends/src/wix/xmlui.py --- a/frontends/src/wix/xmlui.py Tue Feb 04 18:19:32 2014 +0100 +++ b/frontends/src/wix/xmlui.py Tue Feb 04 18:21:51 2014 +0100 @@ -154,16 +154,43 @@ 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): diff -r 5174657b3378 -r 7c05c39156a2 src/tools/xml_tools.py --- a/src/tools/xml_tools.py Tue Feb 04 18:19:32 2014 +0100 +++ b/src/tools/xml_tools.py Tue Feb 04 18:21:51 2014 +0100 @@ -75,18 +75,15 @@ 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 @@ """ 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 @@ 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 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 @@ 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)