# HG changeset patch # User Goffi # Date 1391528960 -3600 # Node ID e8c26e24a6c793348adad0be0761fc3217300991 # Parent 9c6be29c714abc92eda091068f777009c370993e browser side: refactored XMLUI to use the new sat_frontends.tools.xmlui.XMLUI class, first draft diff -r 9c6be29c714a -r e8c26e24a6c7 browser_side/xmlui.py --- a/browser_side/xmlui.py Tue Feb 04 16:49:20 2014 +0100 +++ b/browser_side/xmlui.py Tue Feb 04 16:49:20 2014 +0100 @@ -32,20 +32,186 @@ from pyjamas.ui.CheckBox import CheckBox from pyjamas.ui.ListBox import ListBox from pyjamas.ui.Button import Button +from pyjamas.ui.HTML import HTML from nativedom import NativeDOM +from sat_frontends.tools import xmlui + + +class EmptyWidget(xmlui.EmptyWidget, Label): + + def __init__(self, parent): + Label.__init__(self, '') + + +class TextWidget(xmlui.TextWidget, Label): + + def __init__(self, parent, value): + Label.__init__(self, value) + + +class LabelWidget(xmlui.LabelWidget, TextWidget): + + def __init__(self, parent, value): + TextWidget.__init__(self, parent, value+": ") + + +class JidWidget(xmlui.JidWidget, TextWidget): + pass + + +class DividerWidget(xmlui.DividerWidget, HTML): + + def __init__(self, parent, style='line'): + HTML.__init__(self, "
") # gof: TODO: + + +class StringWidget(xmlui.StringWidget, TextBox): + + def __init__(self, parent, value): + TextBox.__init__(self) + self.setText(value) + + def _xmluiGetValue(self): + return self.getText() + + def _xmluiOnChange(self, callback): + self.addhangeListener(callback) + + +class PasswordWidget(xmlui.PasswordWidget, PasswordTextBox): + + def __init__(self, parent, value): + TextBox.__init__(self) + self.setText(value) + + def _xmluiGetValue(self): + return self.getText() + + def _xmluiOnChange(self, callback): + self.addChangeListener(callback) + + +class TextBoxWidget(xmlui.TextBoxWidget, TextArea): + + def __init__(self, parent, value): + TextArea.__init__(self) + self.setText(value) + + def _xmluiGetValue(self): + return self.getText() + + def _xmluiOnChange(self, callback): + self.addChangeListener(callback) + + +class BoolWidget(xmlui.BoolWidget, CheckBox): + + def __init__(self, parent, state): + CheckBox.__init__(self) + self.setChecked(state) + + def _xmluiGetValue(self): + return "true" if self.isChecked() else "false" + + def _xmluiOnChange(self, callback): + self.addClickListener(callback) -class InvalidXMLUI(Exception): - pass +class ButtonWidget(xmlui.ButtonWidget, Button): + + def __init__(self, parent, value, click_callback): + Button.__init__(self) + + def _xmluiOnClick(self, event): + self.addClickListener(callback) + + +class ListWidget(xmlui.ListWidget, ListBox): + + def __init__(self, parent, options, flags): + ListBox.__init__(self) + self.setMultipleSelect('single' not in flags) + for option in options: + self.addItem(option[0]) + self._xmlui_attr_map = {label: value for value, label in options} + + def _xmluiSelectValue(self, value): + try: + label = [label for label, _value in self._xmlui_attr_map.items() if _value == value][0] + except IndexError: + print(_("WARNING: Can't find value [%s] to select" % value)) + return + self.selectItem(label) + + def _xmluiGetSelectedValues(self): + ret = [] + for label in self.getSelectedItemText(): + ret.append(self._xmlui_attr_map[label]) + return ret + + def _xmluiOnChange(self, callback): + self.addChangeListener(callback) + + +class LiberviaContainer(object): + + def _xmluiAppend(self, widget): + self.append(widget) + + +class AdvancedListContainer(xmlui.AdvancedListContainer, Grid): -class Pairs(Grid): + def __init__(self, parent, columns, selectable='no'): + Grid.__init_(self, 0, columns) + self.columns = columns + self.row = 0 + self.col = 0 + self._xmlui_rows_idx = [] + self._xmlui_selectable = selectable != 'no' + self._xmlui_selected_row = None + self.addTableListener(self) + + def onCellClicked(self, grid, row, col): + if not self._xmlui_selectable: + return + self._xmlui_selected_row = row + try: + self._xmlui_select_cb(self) + except AttributeError: + print "WARNING: no select callback set" + - def __init__(self): + def _xmluiAppend(self, widget): + self.setWidget(self.row, self.col, widget) + self.col += 1 + + def _xmluiAddRow(self, idx): + self._xmlui_rows_idx.insert(self.row, idx) + self.row += 1 + self.col = 0 + self.resizeRows(self.row) + + def _xmluiGetSelectedWidgets(self): + return [self.getWidget(self._xmlui_selected_row, col) for col in range(self.columns)] + + def _xmluiGetSelectedIndex(self): + try: + return self._xmlui_rows_idx[self._xmlui_selected_row] + except TypeError: + return None + + def _xmluiOnSelect(self, callback): + self._xmlui_select_cb = callback + + +class PairsContainer(xmlui.PairsContainer, Grid): + + def __init__(self, parent): Grid.__init__(self, 0, 0) self.row = 0 self.col = 0 - def append(self, widget): + def _xmluiAppend(self, widget): if self.col == 0: self.resize(self.row+1, 2) self.setWidget(self.row, self.col, widget) @@ -54,228 +220,151 @@ self.row +=1 self.col = 0 -class XMLUI(VerticalPanel): + + +class TabsContainer(LiberviaContainer, xmlui.TabsContainer, TabPanel): + + def __init__(self, parent): + TabPanel.__init__(self) + self.setStyleName('liberviaTabPanel') + + def _xmluiAddTab(self, label): + tab_panel = VerticalContainer(self) + self.add(tab_panel, label) + if len(self.getChildren()) == 1: + self.selectTab(0) + return tab_panel + + +class VerticalContainer(LiberviaContainer, xmlui.VerticalContainer, VerticalPanel): + __bases__ = (LiberviaContainer, xmlui.VerticalContainer, VerticalPanel) + + def __init__(self, parent): + VerticalPanel.__init__(self) + + +class WidgetFactory(object): + # XXX: __getattr__ doesn't work here for an unknown reason + + def createVerticalContainer(self, *args, **kwargs): + instance = VerticalContainer(*args, **kwargs) + instance._xmlui_main = self._xmlui_main + return instance + + def createPairsContainer(self, *args, **kwargs): + instance = PairsContainer(*args, **kwargs) + instance._xmlui_main = self._xmlui_main + return instance + + def createTabsContainer(self, *args, **kwargs): + instance = TabsContainer(*args, **kwargs) + instance._xmlui_main = self._xmlui_main + return instance + + def createEmptyWidget(self, *args, **kwargs): + instance = EmptyWidget(*args, **kwargs) + instance._xmlui_main = self._xmlui_main + return instance + + def createTextWidget(self, *args, **kwargs): + instance = TextWidget(*args, **kwargs) + instance._xmlui_main = self._xmlui_main + return instance - def __init__(self, host, xml_data, title = None, options = None, misc = None, close_cb = None): - print "XMLUI init" - VerticalPanel.__init__(self) + def createLabelWidget(self, *args, **kwargs): + instance = LabelWidget(*args, **kwargs) + instance._xmlui_main = self._xmlui_main + return instance + + def createJidWidget(self, *args, **kwargs): + instance = JidWidget(*args, **kwargs) + instance._xmlui_main = self._xmlui_main + return instance + + def createDividerWidget(self, *args, **kwargs): + instance = DividerWidget(*args, **kwargs) + instance._xmlui_main = self._xmlui_main + return instance + + def createStringWidget(self, *args, **kwargs): + instance = StringWidget(*args, **kwargs) + instance._xmlui_main = self._xmlui_main + return instance + + def createPasswordWidget(self, *args, **kwargs): + instance = PasswordWidget(*args, **kwargs) + instance._xmlui_main = self._xmlui_main + return instance + + def createTextBoxWidget(self, *args, **kwargs): + instance = TextBoxWidget(*args, **kwargs) + instance._xmlui_main = self._xmlui_main + return instance + + def createBoolWidget(self, *args, **kwargs): + instance = BoolWidget(*args, **kwargs) + instance._xmlui_main = self._xmlui_main + return instance + + def createButtonWidget(self, *args, **kwargs): + instance = ButtonWidget(*args, **kwargs) + instance._xmlui_main = self._xmlui_main + return instance + + def createListWidget(self, *args, **kwargs): + instance = ListWidget(*args, **kwargs) + instance._xmlui_main = self._xmlui_main + return instance + + + # def __getattr__(self, attr): + # if attr.startswith("create"): + # cls = globals()[attr[6:]] + # cls._xmlui_main = self._xmlui_main + # return cls + + +class XMLUI(xmlui.XMLUI, VerticalPanel): + widget_factory = WidgetFactory() + + def __init__(self, host, xml_data, title = None, flags = None): + self.widget_factory._xmlui_main = self self.dom = NativeDOM() - self.host = host - self.title = title - self.options = options or [] - self.misc = misc or {} - self.close_cb = close_cb - self.__dest = "window" - self.ctrl_list = {} # usefull to access ctrl - self.constructUI(xml_data) + dom_parse = lambda xml_data: self.dom.parseString(xml_data) + self._dest = "window" + VerticalPanel.__init__(self) self.setSize('100%', '100%') + xmlui.XMLUI.__init__(self, host, xml_data, title, flags, dom_parse) def setCloseCb(self, close_cb): self.close_cb = close_cb - def close(self): + def _xmluiClose(self): if self.close_cb: self.close_cb() else: print "WARNING: no close method defined" - def __parseElems(self, node, parent): - """Parse elements inside a tags, and add them to the parent""" - for elem in node.childNodes: - if elem.nodeName != "elem": - raise Exception("Unmanaged tag [%s]" % (elem.nodeName)) - node_id = elem.getAttribute("node_id") - name = elem.getAttribute("name") - node_type = elem.getAttribute("type") - value = elem.getAttribute("value") if elem.hasAttribute('value') else u'' - if node_type=="empty": - ctrl = Label('') - elif node_type=="text": - try: - value = elem.childNodes[0].wholeText - except IndexError: - print ("WARNING: text node has no child !") - ctrl = Label(value) - elif node_type=="label": - ctrl = Label(value+": ") - elif node_type=="string": - ctrl = TextBox() - ctrl.setText(value) - self.ctrl_list[name] = {'node_type':node_type, 'control':ctrl} - elif node_type=="password": - ctrl = PasswordTextBox() - ctrl.setText(value) - self.ctrl_list[name] = {'node_type':node_type, 'control':ctrl} - elif node_type=="textbox": - ctrl = TextArea() - ctrl.setText(value) - self.ctrl_list[name] = {'node_type':node_type, 'control':ctrl} - elif node_type=="bool": - ctrl = CheckBox() - ctrl.setChecked(value=="true") - self.ctrl_list[name] = {'node_type':node_type, 'control':ctrl} - elif node_type=="list": - _options = [(option.getAttribute("label"), option.getAttribute("value")) for option in elem.getElementsByTagName("option")] - attr_map = {label: value for label, value in _options} - ctrl = ListBox() - ctrl.setMultipleSelect(elem.getAttribute("multi")=='yes') - for option in _options: - ctrl.addItem(option[0]) - ctrl.selectItem(value) - self.ctrl_list[name] = {'node_type':node_type, 'control':ctrl, 'attr_map': attr_map} - elif node_type=="button": - callback_id = elem.getAttribute("callback_id") - ctrl = Button(value, self.onButtonPress) - ctrl.param_id = (callback_id,[field.getAttribute('name') for field in elem.getElementsByTagName("field_back")]) - else: - print("FIXME FIXME FIXME: type [%s] is not implemented" % node_type) #FIXME ! - raise NotImplementedError - if self.node_type == 'param': - if isinstance(ctrl, TextBoxBase): - ctrl.addChangeListener(self.onParamChange) - elif isinstance(ctrl, CheckBox): - ctrl.addClickListener(self.onParamChange) - elif isinstance(ctrl, ListBox): - ctrl.addChangeListener(self.onParamChange) - ctrl._param_category = self._current_category - ctrl._param_name = name - parent.append(ctrl) - - def __parseChilds(self, current, elem, wanted = ['layout'], data = None): - """Recursively parse childNodes of an elemen - @param current: widget container with 'append' method - @param elem: element from which childs will be parsed - @param wanted: list of tag names that can be present in the childs to be SàT XMLUI compliant""" - for node in elem.childNodes: - if wanted and not node.nodeName in wanted: - raise InvalidXMLUI("ERROR: unexpected nodeName") - if node.nodeName == "layout": - node_type = node.getAttribute('type') - if node_type == "tabs": - tab_cont = TabPanel() - tab_cont.setStyleName('liberviaTabPanel') - tab_cont.setHeight('100%') - self.__parseChilds(current, node, ['category'], tab_cont) - current.append(tab_cont) - if isinstance(current, CellPanel): - current.setCellHeight(tab_cont, '100%') - if len(tab_cont.getChildren()) > 0: - tab_cont.selectTab(0) - elif node_type == "vertical": - self.__parseElems(node, current) - elif node_type == "pairs": - pairs = Pairs() - self.__parseElems(node, pairs) - current.append(pairs) - else: - print("WARNING: Unknown layout [%s], using default one" % (node_type,)) - self.__parseElems(node, current) - elif node.nodeName == "category": - name = node.getAttribute('name') - label = node.getAttribute('label') - if not name or not isinstance(data,TabPanel): - raise InvalidXMLUI - if self.node_type == 'param': - self._current_category = name #XXX: awful hack because params need category and we don't keep parent - tab_cont = data - tab_body = VerticalPanel() - tab_cont.add(tab_body, label or name) - self.__parseChilds(tab_body, node, ['layout']) - else: - message=_("Unknown tag") - raise NotImplementedError(message) - def constructUI(self, xml_data): - - cat_dom = self.dom.parseString(xml_data) - - top=cat_dom.documentElement - self.node_type = top.getAttribute("type") - self.title = top.getAttribute("title") or self.title - self.session_id = top.getAttribute("session_id") or None - self.submit_id = top.getAttribute("submit") or None - if top.nodeName != "sat_xmlui" or not self.node_type in ['form', 'param', 'window']: - raise InvalidXMLUI - - if self.node_type == 'param': - self.param_changed = set() - - self.__parseChilds(self, cat_dom.documentElement) - - if self.node_type == 'form': + super(XMLUI, self).constructUI(xml_data) + self.add(self.main_cont) + self.setCellHeight(self.main_cont, '100%') + if self.type == 'form': hpanel = HorizontalPanel() hpanel.add(Button('Submit',self.onFormSubmitted)) - if not 'NO_CANCEL' in self.options: + if not 'NO_CANCEL' in self.flags: hpanel.add(Button('Cancel',self.onFormCancelled)) self.add(hpanel) - elif self.node_type == 'param': - assert(isinstance(self.children[0],TabPanel)) + elif self.type == 'param': + assert(isinstance(self.children[0][0],TabPanel)) hpanel = HorizontalPanel() - hpanel.add(Button('Cancel', lambda ignore: self.close())) + hpanel.add(Button('Cancel', lambda ignore: self._xmluiClose())) hpanel.add(Button('Save', self.onSaveParams)) self.add(hpanel) ##EVENTS## - def onButtonPress(self, button): - print "onButtonPress (%s)" % (button,) - callback_id, fields = button.param_id - for field in fields: - ctrl = self.ctrl_list[field] - if isinstance(ctrl['control'],ListBox): - data[field] = '\t'.join(ctrl['control'].getSelectedItemText()) - elif isinstance(ctrl['control'],CheckBox): - data[field] = "true" if ctrl['control'].isChecked() else "false" - else: - data[field] = ctrl['control'].getText() - - self.host.launchAction(callback_id, None) - - def onParamChange(self, widget): - """Called when type is param and a widget to save is modified""" - assert(self.node_type == "param") - print "onParamChange:", widget - self.param_changed.add(widget) - - def onFormSubmitted(self, button): - print "onFormSubmitted" - # FIXME: untested - print "FIXME FIXME FIXME: Form submitting not managed yet" - data = [] - for ctrl_name in self.ctrl_list: - escaped = u"%s%s" % (SAT_FORM_PREFIX, ctrl_name) - ctrl = self.ctrl_list[escaped] - if isinstance(ctrl['control'], ListBox): - data.append((escaped, '\t'.join([ctrl['attr_map'][label] for label in ctrl['control'].getSelectedItemText()]))) - elif isinstance(ctrl['control'], CheckBox): - data.append((escaped, "true" if ctrl['control'].isChecked() else "false")) - else: - data.append((escaped, ctrl['control'].getText())) - if 'action_back' in self.misc: #FIXME FIXME FIXME: WTF ! Must be cleaned - raise NotImplementedError - elif 'callback' in self.misc: - self.misc['callback'](data) - elif self.submit_id is not None: - data = dict(selected_values) - if self.session_id is not None: - data["session_id"] = self.session_id - self.host.launchAction(self.submit_id, data) - else: - print ("WARNING: The form data is not sent back, the type is not managed properly") - - self.close() - - def onFormCancelled(self, button): - self.close() - - def onSaveParams(self, button): - print "onSaveParams" - for ctrl in self.param_changed: - if isinstance(ctrl, CheckBox): - value = "true" if ctrl.isChecked() else "false" - elif isinstance(ctrl, ListBox): - value = '\t'.join(ctrl.getSelectedItemText()) - else: - value = ctrl.getText() - self.host.bridge.call('setParam', None, ctrl._param_name, value, ctrl._param_category) - self.close() + def onSaveParams(self, ignore=None): + def postTreat(name, value, category): + self.host.bridge.call('setParam', None, name, value, category) + xmlui.XMLUI.onSaveParams(self, ignore, postTreat)