comparison sat_frontends/tools/xmlui.py @ 2668:c274201cea94

core, frontends (xmlui): added "hidden" widget, to specify a value to be returned unmodified
author Goffi <goffi@goffi.org>
date Fri, 31 Aug 2018 15:57:11 +0200
parents 56f94936df1e
children bdb8276fd2da
comparison
equal deleted inserted replaced
2667:8dd9db785ac8 2668:c274201cea94
27 27
28 class_map = {} 28 class_map = {}
29 CLASS_PANEL = "panel" 29 CLASS_PANEL = "panel"
30 CLASS_DIALOG = "dialog" 30 CLASS_DIALOG = "dialog"
31 CURRENT_LABEL = "current_label" 31 CURRENT_LABEL = "current_label"
32 HIDDEN = "hidden"
32 33
33 34
34 class InvalidXMLUI(Exception): 35 class InvalidXMLUI(Exception):
35 pass 36 pass
36 37
259 """Base class to construct SàT XML User Interface 260 """Base class to construct SàT XML User Interface
260 261
261 This class must not be instancied directly 262 This class must not be instancied directly
262 """ 263 """
263 264
264 def __init__( 265 def __init__(self, host, parsed_dom, title=None, flags=None, callback=None,
265 self, 266 profile=C.PROF_KEY_NONE):
266 host,
267 parsed_dom,
268 title=None,
269 flags=None,
270 callback=None,
271 profile=C.PROF_KEY_NONE,
272 ):
273 """Initialise the XMLUI instance 267 """Initialise the XMLUI instance
274 268
275 @param host: %(doc_host)s 269 @param host: %(doc_host)s
276 @param parsed_dom: main parsed dom 270 @param parsed_dom: main parsed dom
277 @param title: force the title, or use XMLUI one if None 271 @param title: force the title, or use XMLUI one if None
278 @param flags: list of string which can be: 272 @param flags: list of string which can be:
279 - NO_CANCEL: the UI can't be cancelled 273 - NO_CANCEL: the UI can't be cancelled
280 - FROM_BACKEND: the UI come from backend (i.e. it's not the direct result of user operation) 274 - FROM_BACKEND: the UI come from backend (i.e. it's not the direct result of
275 user operation)
281 @param callback(callable, None): if not None, will be used with launchAction: 276 @param callback(callable, None): if not None, will be used with launchAction:
282 - if None is used, default behaviour will be used (closing the dialog and calling host.actionManager) 277 - if None is used, default behaviour will be used (closing the dialog and
278 calling host.actionManager)
283 - if a callback is provided, it will be used instead, so you'll have to manage 279 - if a callback is provided, it will be used instead, so you'll have to manage
284 dialog closing or new xmlui to display, or other action (you can call host.actionManager) 280 dialog closing or new xmlui to display, or other action (you can call
281 host.actionManager)
282 The callback will have data, callback_id and profile as arguments
285 """ 283 """
286 self.host = host 284 self.host = host
287 top = parsed_dom.documentElement 285 top = parsed_dom.documentElement
288 self.session_id = top.getAttribute("session_id") or None 286 self.session_id = top.getAttribute("session_id") or None
289 self.submit_id = top.getAttribute("submit") or None 287 self.submit_id = top.getAttribute("submit") or None
290 self.xmlui_title = title or top.getAttribute("title") or u"" 288 self.xmlui_title = title or top.getAttribute("title") or u""
289 self.hidden = {}
291 if flags is None: 290 if flags is None:
292 flags = [] 291 flags = []
293 self.flags = flags 292 self.flags = flags
294 self.callback = callback or self._defaultCb 293 self.callback = callback or self._defaultCb
295 self.profile = profile 294 self.profile = profile
331 self._xmluiClose() 330 self._xmluiClose()
332 if self.submit_id is None: 331 if self.submit_id is None:
333 raise ValueError("Can't submit is self.submit_id is not set") 332 raise ValueError("Can't submit is self.submit_id is not set")
334 if "session_id" in data: 333 if "session_id" in data:
335 raise ValueError( 334 raise ValueError(
336 "session_id must no be used in data, it is automaticaly filled with self.session_id if present" 335 u"session_id must no be used in data, it is automaticaly filled with "
336 u"self.session_id if present"
337 ) 337 )
338 if self.session_id is not None: 338 if self.session_id is not None:
339 data["session_id"] = self.session_id 339 data["session_id"] = self.session_id
340 self._xmluiLaunchAction(self.submit_id, data) 340 self._xmluiLaunchAction(self.submit_id, data)
341 341
377 @property dialog_factory: factory to create frontend-specific dialogs 377 @property dialog_factory: factory to create frontend-specific dialogs
378 """ 378 """
379 379
380 widget_factory = None 380 widget_factory = None
381 381
382 def __init__( 382 def __init__(self, host, parsed_dom, title=None, flags=None, callback=None,
383 self, 383 ignore=None, whitelist=None, profile=C.PROF_KEY_NONE):
384 host,
385 parsed_dom,
386 title=None,
387 flags=None,
388 callback=None,
389 ignore=None,
390 whitelist=None,
391 profile=C.PROF_KEY_NONE,
392 ):
393 """ 384 """
394 385
395 @param title(unicode, None): title of the 386 @param title(unicode, None): title of the
396 @property widgets(dict): widget name => widget map 387 @property widgets(dict): widget name => widget map
397 @property widget_value(ValueGetter): retrieve widget value from it's name 388 @property widget_value(ValueGetter): retrieve widget value from it's name
414 self._whitelist = whitelist 405 self._whitelist = whitelist
415 else: 406 else:
416 self._whitelist = None 407 self._whitelist = None
417 self.constructUI(parsed_dom) 408 self.constructUI(parsed_dom)
418 409
419 def escape(self, name): 410 @staticmethod
411 def escape(name):
420 """Return escaped name for forms""" 412 """Return escaped name for forms"""
421 return u"%s%s" % (C.SAT_FORM_PREFIX, name) 413 return u"%s%s" % (C.SAT_FORM_PREFIX, name)
422 414
423 @property 415 @property
424 def main_cont(self): 416 def main_cont(self):
433 def _parseChilds(self, _xmlui_parent, current_node, wanted=("container",), data=None): 425 def _parseChilds(self, _xmlui_parent, current_node, wanted=("container",), data=None):
434 """Recursively parse childNodes of an element 426 """Recursively parse childNodes of an element
435 427
436 @param _xmlui_parent: widget container with '_xmluiAppend' method 428 @param _xmlui_parent: widget container with '_xmluiAppend' method
437 @param current_node: element from which childs will be parsed 429 @param current_node: element from which childs will be parsed
438 @param wanted: list of tag names that can be present in the childs to be SàT XMLUI compliant 430 @param wanted: list of tag names that can be present in the childs to be SàT XMLUI
431 compliant
439 @param data(None, dict): additionnal data which are needed in some cases 432 @param data(None, dict): additionnal data which are needed in some cases
440 """ 433 """
441 for node in current_node.childNodes: 434 for node in current_node.childNodes:
442 if data is None: 435 if data is None:
443 data = {} 436 data = {}
445 raise InvalidXMLUI("Unexpected node: [%s]" % node.nodeName) 438 raise InvalidXMLUI("Unexpected node: [%s]" % node.nodeName)
446 439
447 if node.nodeName == "container": 440 if node.nodeName == "container":
448 type_ = node.getAttribute("type") 441 type_ = node.getAttribute("type")
449 if _xmlui_parent is self and type_ != "vertical": 442 if _xmlui_parent is self and type_ != "vertical":
450 # main container is not a VerticalContainer and we want one, so we create one to wrap it 443 # main container is not a VerticalContainer and we want one,
444 # so we create one to wrap it
451 _xmlui_parent = self.widget_factory.createVerticalContainer(self) 445 _xmlui_parent = self.widget_factory.createVerticalContainer(self)
452 self.main_cont = _xmlui_parent 446 self.main_cont = _xmlui_parent
453 if type_ == "tabs": 447 if type_ == "tabs":
454 cont = self.widget_factory.createTabsContainer(_xmlui_parent) 448 cont = self.widget_factory.createTabsContainer(_xmlui_parent)
455 self._parseChilds(_xmlui_parent, node, ("tab",), {"tabs_cont": cont}) 449 self._parseChilds(_xmlui_parent, node, ("tab",), {"tabs_cont": cont})
555 elif type_ == "text": 549 elif type_ == "text":
556 ctrl = self.widget_factory.createTextWidget(_xmlui_parent, value) 550 ctrl = self.widget_factory.createTextWidget(_xmlui_parent, value)
557 elif type_ == "label": 551 elif type_ == "label":
558 ctrl = self.widget_factory.createLabelWidget(_xmlui_parent, value) 552 ctrl = self.widget_factory.createLabelWidget(_xmlui_parent, value)
559 data[CURRENT_LABEL] = ctrl 553 data[CURRENT_LABEL] = ctrl
554 elif type_ == "hidden":
555 if name in self.hidden:
556 raise exceptions.ConflictError(u"Conflict on hidden value with "
557 u"name {name}".format(name=name))
558 self.hidden[name] = value
559 continue
560 elif type_ == "jid": 560 elif type_ == "jid":
561 ctrl = self.widget_factory.createJidWidget(_xmlui_parent, value) 561 ctrl = self.widget_factory.createJidWidget(_xmlui_parent, value)
562 elif type_ == "divider": 562 elif type_ == "divider":
563 style = node.getAttribute("style") or "line" 563 style = node.getAttribute("style") or "line"
564 ctrl = self.widget_factory.createDividerWidget(_xmlui_parent, style) 564 ctrl = self.widget_factory.createDividerWidget(_xmlui_parent, style)
648 ctrl._xmluiOnChange(self.onParamChange) 648 ctrl._xmluiOnChange(self.onParamChange)
649 ctrl._param_category = self._current_category 649 ctrl._param_category = self._current_category
650 except ( 650 except (
651 AttributeError, 651 AttributeError,
652 TypeError, 652 TypeError,
653 ): # XXX: TypeError is here because pyjamas raise a TypeError instead of an AttributeError 653 ): # XXX: TypeError is here because pyjamas raise a TypeError instead
654 # of an AttributeError
654 if not isinstance( 655 if not isinstance(
655 ctrl, (EmptyWidget, TextWidget, LabelWidget, JidWidget) 656 ctrl, (EmptyWidget, TextWidget, LabelWidget, JidWidget)
656 ): 657 ):
657 log.warning(_("No change listener on [%s]") % ctrl) 658 log.warning(_("No change listener on [%s]") % ctrl)
658 659
855 selected_values.append( 856 selected_values.append(
856 (escaped, u"\t".join(ctrl["control"]._xmluiGetSelectedValues())) 857 (escaped, u"\t".join(ctrl["control"]._xmluiGetSelectedValues()))
857 ) 858 )
858 else: 859 else:
859 selected_values.append((escaped, ctrl["control"]._xmluiGetValue())) 860 selected_values.append((escaped, ctrl["control"]._xmluiGetValue()))
861 data = dict(selected_values)
862 for key, value in self.hidden.iteritems():
863 data[self.escape(key)] = value
864
860 if self.submit_id is not None: 865 if self.submit_id is not None:
861 data = dict(selected_values)
862 self.submit(data) 866 self.submit(data)
863 else: 867 else:
864 log.warning( 868 log.warning(
865 _("The form data is not sent back, the type is not managed properly") 869 _("The form data is not sent back, the type is not managed properly")
866 ) 870 )