Mercurial > libervia-web
annotate browser_side/xmlui.py @ 239:b911f2b43fd4
browser_side: added input history in the unibox:
This functionality uses a file from the sat project:
use the -I parameter of pyjsbuild to add sat library
to your PYJSPATH. To ease also possible to use your
sat source directory instead of the library, you just
need to trick pyjsbuild with a symbolic link:
SAT=~/workspace/sat
if [[ ! -e $SAT/sat ]]; then ln -sf $SAT/src $SAT/sat; fi
This will allow you to import like that in libervia.py:
from sat.tools.frontend.misc import InputHistory
And then you can build with:
$PYJS/bin/pyjsbuild libervia --no-compile-inplace -m -I $SAT
author | souliane <souliane@mailoo.org> |
---|---|
date | Mon, 14 Oct 2013 20:54:13 +0200 |
parents | e632f77c4219 |
children | fe83837d3491 |
rev | line source |
---|---|
143 | 1 #!/usr/bin/python |
2 # -*- coding: utf-8 -*- | |
3 | |
4 """ | |
5 Libervia: a Salut à Toi frontend | |
165 | 6 Copyright (C) 2011, 2012, 2013 Jérôme Poisson <goffi@goffi.org> |
143 | 7 |
8 This program is free software: you can redistribute it and/or modify | |
9 it under the terms of the GNU Affero General Public License as published by | |
10 the Free Software Foundation, either version 3 of the License, or | |
11 (at your option) any later version. | |
12 | |
13 This program is distributed in the hope that it will be useful, | |
14 but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 GNU Affero General Public License for more details. | |
17 | |
18 You should have received a copy of the GNU Affero General Public License | |
19 along with this program. If not, see <http://www.gnu.org/licenses/>. | |
20 """ | |
21 | |
22 from pyjamas.ui.VerticalPanel import VerticalPanel | |
23 from pyjamas.ui.HorizontalPanel import HorizontalPanel | |
24 from pyjamas.ui.CellPanel import CellPanel | |
25 from pyjamas.ui.TabPanel import TabPanel | |
26 from pyjamas.ui.Grid import Grid | |
27 from pyjamas.ui.Label import Label | |
28 from pyjamas.ui.TextBoxBase import TextBoxBase | |
29 from pyjamas.ui.TextBox import TextBox | |
30 from pyjamas.ui.PasswordTextBox import PasswordTextBox | |
31 from pyjamas.ui.TextArea import TextArea | |
32 from pyjamas.ui.CheckBox import CheckBox | |
33 from pyjamas.ui.ListBox import ListBox | |
34 from pyjamas.ui.Button import Button | |
35 from nativedom import NativeDOM | |
36 | |
37 | |
38 class InvalidXMLUI(Exception): | |
39 pass | |
40 | |
41 class Pairs(Grid): | |
42 | |
43 def __init__(self): | |
44 Grid.__init__(self, 0, 0) | |
45 self.row = 0 | |
46 self.col = 0 | |
47 | |
48 def append(self, widget): | |
49 if self.col == 0: | |
50 self.resize(self.row+1, 2) | |
51 self.setWidget(self.row, self.col, widget) | |
52 self.col += 1 | |
53 if self.col == 2: | |
54 self.row +=1 | |
55 self.col = 0 | |
56 | |
57 class XMLUI(VerticalPanel): | |
58 | |
59 def __init__(self, host, xml_data, title = None, options = None, misc = None, close_cb = None): | |
60 print "XMLUI init" | |
61 VerticalPanel.__init__(self) | |
62 self.dom = NativeDOM() | |
63 self.host = host | |
64 self.title = title | |
65 self.options = options or [] | |
66 self.misc = misc or {} | |
67 self.close_cb = close_cb | |
68 self.__dest = "window" | |
69 self.ctrl_list = {} # usefull to access ctrl | |
70 self.constructUI(xml_data) | |
71 self.setSize('100%', '100%') | |
72 | |
73 def setCloseCb(self, close_cb): | |
74 self.close_cb = close_cb | |
75 | |
76 def close(self): | |
77 if self.close_cb: | |
78 self.close_cb() | |
79 else: | |
80 print "WARNING: no close method defined" | |
81 | |
82 def __parseElems(self, node, parent): | |
83 """Parse elements inside a <layout> tags, and add them to the parent""" | |
84 for elem in node.childNodes: | |
85 if elem.nodeName != "elem": | |
86 raise Exception("Unmanaged tag [%s]" % (elem.nodeName)) | |
87 node_id = elem.getAttribute("node_id") | |
88 name = elem.getAttribute("name") | |
89 node_type = elem.getAttribute("type") | |
90 value = elem.getAttribute("value") if elem.hasAttribute('value') else u'' | |
91 if node_type=="empty": | |
92 ctrl = Label('') | |
93 elif node_type=="text": | |
94 try: | |
95 value = elem.childNodes[0].wholeText | |
96 except IndexError: | |
97 print ("WARNING: text node has no child !") | |
98 ctrl = Label(value) | |
99 elif node_type=="label": | |
100 ctrl = Label(value+": ") | |
101 elif node_type=="string": | |
102 ctrl = TextBox() | |
103 ctrl.setText(value) | |
104 self.ctrl_list[name] = ({'node_type':node_type, 'control':ctrl}) | |
105 elif node_type=="password": | |
106 ctrl = PasswordTextBox() | |
107 ctrl.setText(value) | |
108 self.ctrl_list[name] = ({'node_type':node_type, 'control':ctrl}) | |
109 elif node_type=="textbox": | |
110 ctrl = TextArea() | |
111 ctrl.setText(value) | |
112 self.ctrl_list[name] = ({'node_type':node_type, 'control':ctrl}) | |
113 elif node_type=="bool": | |
114 ctrl = CheckBox() | |
115 ctrl.setChecked(value=="true") | |
116 self.ctrl_list[name] = ({'node_type':node_type, 'control':ctrl}) | |
117 elif node_type=="list": | |
118 ctrl = ListBox() | |
119 ctrl.setMultipleSelect(elem.getAttribute("multi")=='yes') | |
120 for option in elem.getElementsByTagName("option"): | |
121 ctrl.addItem(option.getAttribute("value")) | |
229
e632f77c4219
bridge: asyncGetParamA takes a security_limit argument
souliane <souliane@mailoo.org>
parents:
218
diff
changeset
|
122 ctrl.selectItem(value) |
143 | 123 self.ctrl_list[name] = ({'node_type':node_type, 'control':ctrl}) |
124 elif node_type=="button": | |
125 callback_id = elem.getAttribute("callback_id") | |
126 ctrl = Button(value, self.onButtonPress) | |
127 ctrl.param_id = (callback_id,[field.getAttribute('name') for field in elem.getElementsByTagName("field_back")]) | |
128 else: | |
129 print("FIXME FIXME FIXME: type [%s] is not implemented" % node_type) #FIXME ! | |
130 raise NotImplementedError | |
131 if self.node_type == 'param': | |
229
e632f77c4219
bridge: asyncGetParamA takes a security_limit argument
souliane <souliane@mailoo.org>
parents:
218
diff
changeset
|
132 if isinstance(ctrl, TextBoxBase): |
143 | 133 ctrl.addChangeListener(self.onParamChange) |
134 elif isinstance(ctrl, CheckBox): | |
135 ctrl.addClickListener(self.onParamChange) | |
229
e632f77c4219
bridge: asyncGetParamA takes a security_limit argument
souliane <souliane@mailoo.org>
parents:
218
diff
changeset
|
136 elif isinstance(ctrl, ListBox): |
e632f77c4219
bridge: asyncGetParamA takes a security_limit argument
souliane <souliane@mailoo.org>
parents:
218
diff
changeset
|
137 ctrl.addChangeListener(self.onParamChange) |
143 | 138 ctrl._param_category = self._current_category |
139 ctrl._param_name = name | |
140 parent.append(ctrl) | |
141 | |
142 def __parseChilds(self, current, elem, wanted = ['layout'], data = None): | |
143 """Recursively parse childNodes of an elemen | |
144 @param current: widget container with 'append' method | |
145 @param elem: element from which childs will be parsed | |
146 @param wanted: list of tag names that can be present in the childs to be SàT XMLUI compliant""" | |
147 for node in elem.childNodes: | |
148 if wanted and not node.nodeName in wanted: | |
149 raise InvalidXMLUI("ERROR: unexpected nodeName") | |
150 if node.nodeName == "layout": | |
151 node_type = node.getAttribute('type') | |
152 if node_type == "tabs": | |
218
4e6467efd6bf
browser_side: small improvements for parameters panel
souliane <souliane@mailoo.org>
parents:
165
diff
changeset
|
153 tab_cont = TabPanel() |
143 | 154 tab_cont.setStyleName('liberviaTabPanel') |
155 tab_cont.setHeight('100%') | |
156 self.__parseChilds(current, node, ['category'], tab_cont) | |
157 current.append(tab_cont) | |
158 if isinstance(current, CellPanel): | |
159 current.setCellHeight(tab_cont, '100%') | |
218
4e6467efd6bf
browser_side: small improvements for parameters panel
souliane <souliane@mailoo.org>
parents:
165
diff
changeset
|
160 if len(tab_cont.getChildren()) > 0: |
4e6467efd6bf
browser_side: small improvements for parameters panel
souliane <souliane@mailoo.org>
parents:
165
diff
changeset
|
161 tab_cont.selectTab(0) |
143 | 162 elif node_type == "vertical": |
163 self.__parseElems(node, current) | |
164 elif node_type == "pairs": | |
165 pairs = Pairs() | |
166 self.__parseElems(node, pairs) | |
167 current.append(pairs) | |
168 else: | |
169 print("WARNING: Unknown layout [%s], using default one" % (node_type,)) | |
170 self.__parseElems(node, current) | |
171 elif node.nodeName == "category": | |
172 name = node.getAttribute('name') | |
173 label = node.getAttribute('label') | |
174 if not name or not isinstance(data,TabPanel): | |
175 raise InvalidXMLUI | |
176 if self.node_type == 'param': | |
177 self._current_category = name #XXX: awful hack because params need category and we don't keep parent | |
178 tab_cont = data | |
179 tab_body = VerticalPanel() | |
180 tab_cont.add(tab_body, label or name) | |
181 self.__parseChilds(tab_body, node, ['layout']) | |
182 else: | |
183 message=_("Unknown tag") | |
184 raise NotImplementedError(message) | |
185 | |
186 def constructUI(self, xml_data): | |
187 | |
188 cat_dom = self.dom.parseString(xml_data) | |
189 | |
190 top=cat_dom.documentElement | |
191 self.node_type = top.getAttribute("type") | |
192 self.title = top.getAttribute("title") or self.title | |
193 if top.nodeName != "sat_xmlui" or not self.node_type in ['form', 'param', 'window']: | |
194 raise InvalidXMLUI | |
195 | |
196 if self.node_type == 'param': | |
197 self.param_changed = set() | |
198 | |
199 self.__parseChilds(self, cat_dom.documentElement) | |
200 | |
201 if self.node_type == 'form': | |
202 hpanel = HorizontalPanel() | |
203 hpanel.add(Button('Submit',self.onFormSubmitted)) | |
204 if not 'NO_CANCEL' in self.options: | |
205 hpanel.add(Button('Cancel',self.onFormCancelled)) | |
206 self.add(hpanel) | |
207 elif self.node_type == 'param': | |
208 assert(isinstance(self.children[0],TabPanel)) | |
209 hpanel = HorizontalPanel() | |
210 hpanel.add(Button('Cancel', lambda ignore: self.close())) | |
211 hpanel.add(Button('Save', self.onSaveParams)) | |
212 self.add(hpanel) | |
213 | |
214 ##EVENTS## | |
215 | |
216 def onButtonPress(self, button): | |
217 print "onButtonPress (%s)" % (button,) | |
218 callback_id, fields = button.param_id | |
219 data = {"callback_id":callback_id} | |
220 for field in fields: | |
221 ctrl = self.ctrl_list[field] | |
222 if isinstance(ctrl['control'],ListBox): | |
229
e632f77c4219
bridge: asyncGetParamA takes a security_limit argument
souliane <souliane@mailoo.org>
parents:
218
diff
changeset
|
223 data[field] = '\t'.join(ctrl['control'].getSelectedItemText()) |
143 | 224 elif isinstance(ctrl['control'],CheckBox): |
225 data[field] = "true" if ctrl['control'].isChecked() else "false" | |
226 else: | |
227 data[field] = ctrl['control'].getText() | |
228 | |
229 self.host.bridge.call('launchAction', None, "button", data) | |
230 self.host.current_action_ids.add(id) | |
231 | |
232 def onParamChange(self, widget): | |
233 """Called when type is param and a widget to save is modified""" | |
234 assert(self.node_type == "param") | |
235 print "onParamChange:", widget | |
236 self.param_changed.add(widget) | |
237 | |
238 def onFormSubmitted(self, button): | |
239 print "onFormSubmitted" | |
240 # FIXME: untested | |
241 print "FIXME FIXME FIXME: Form submitting not managed yet" | |
242 data = [] | |
243 for ctrl_name in self.ctrl_list: | |
244 ctrl = self.ctrl_list[ctrl_name] | |
245 if isinstance(ctrl['control'], ListBox): | |
229
e632f77c4219
bridge: asyncGetParamA takes a security_limit argument
souliane <souliane@mailoo.org>
parents:
218
diff
changeset
|
246 data.append((ctrl_name, '\t'.join(ctrl['control'].getSelectedItemText()))) |
143 | 247 elif isinstance(ctrl['control'], CheckBox): |
248 data.append((ctrl_name, "true" if ctrl['control'].isChecked() else "false")) | |
249 else: | |
250 data.append((ctrl_name, ctrl['control'].getText())) | |
251 if 'action_back' in self.misc: #FIXME FIXME FIXME: WTF ! Must be cleaned | |
252 raise NotImplementedError | |
253 elif 'callback' in self.misc: | |
254 self.misc['callback'](data) | |
255 else: | |
256 print ("WARNING: The form data is not sent back, the type is not managed properly") | |
257 | |
258 self.close() | |
259 | |
260 def onFormCancelled(self, button): | |
261 self.close() | |
262 | |
263 def onSaveParams(self, button): | |
264 print "onSaveParams" | |
265 for ctrl in self.param_changed: | |
266 if isinstance(ctrl, CheckBox): | |
267 value = "true" if ctrl.isChecked() else "false" | |
229
e632f77c4219
bridge: asyncGetParamA takes a security_limit argument
souliane <souliane@mailoo.org>
parents:
218
diff
changeset
|
268 elif isinstance(ctrl, ListBox): |
e632f77c4219
bridge: asyncGetParamA takes a security_limit argument
souliane <souliane@mailoo.org>
parents:
218
diff
changeset
|
269 value = '\t'.join(ctrl.getSelectedItemText()) |
143 | 270 else: |
271 value = ctrl.getText() | |
272 self.host.bridge.call('setParam', None, ctrl._param_name, value, ctrl._param_category) | |
273 self.close() |