Mercurial > libervia-web
view browser_side/xmlui.py @ 299:e4f586fc6101
server and browser side: updated callback system to follow core changes
author | Goffi <goffi@goffi.org> |
---|---|
date | Tue, 24 Dec 2013 02:00:30 +0100 |
parents | fe83837d3491 |
children | bfbd9d6eb901 |
line wrap: on
line source
#!/usr/bin/python # -*- coding: utf-8 -*- """ Libervia: a Salut à Toi frontend Copyright (C) 2011, 2012, 2013 Jérôme Poisson <goffi@goffi.org> This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program 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 Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. """ from pyjamas.ui.VerticalPanel import VerticalPanel from pyjamas.ui.HorizontalPanel import HorizontalPanel from pyjamas.ui.CellPanel import CellPanel from pyjamas.ui.TabPanel import TabPanel from pyjamas.ui.Grid import Grid from pyjamas.ui.Label import Label from pyjamas.ui.TextBoxBase import TextBoxBase from pyjamas.ui.TextBox import TextBox from pyjamas.ui.PasswordTextBox import PasswordTextBox from pyjamas.ui.TextArea import TextArea from pyjamas.ui.CheckBox import CheckBox from pyjamas.ui.ListBox import ListBox from pyjamas.ui.Button import Button from nativedom import NativeDOM class InvalidXMLUI(Exception): pass class Pairs(Grid): def __init__(self): Grid.__init__(self, 0, 0) self.row = 0 self.col = 0 def append(self, widget): if self.col == 0: self.resize(self.row+1, 2) self.setWidget(self.row, self.col, widget) self.col += 1 if self.col == 2: self.row +=1 self.col = 0 class XMLUI(VerticalPanel): def __init__(self, host, xml_data, title = None, options = None, misc = None, close_cb = None): print "XMLUI init" VerticalPanel.__init__(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) self.setSize('100%', '100%') def setCloseCb(self, close_cb): self.close_cb = close_cb def close(self): if self.close_cb: self.close_cb() else: print "WARNING: no close method defined" def __parseElems(self, node, parent): """Parse elements inside a <layout> 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': hpanel = HorizontalPanel() hpanel.add(Button('Submit',self.onFormSubmitted)) if not 'NO_CANCEL' in self.options: hpanel.add(Button('Cancel',self.onFormCancelled)) self.add(hpanel) elif self.node_type == 'param': assert(isinstance(self.children[0],TabPanel)) hpanel = HorizontalPanel() hpanel.add(Button('Cancel', lambda ignore: self.close())) 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()