comparison src/tools/xml_tools.py @ 1105:018bdd687747

core (XMLUI): Dialogs are now managemed in XMLUI: - currents dialogs are available: - Message: a classical informational/error message, usually a popup that user has to close after reading - Note: more discreet message, usually a notification with a timeout (user don't need to close manually) - Confirm: a Ok/Cancel or Yes/No dialog - File: a dialog to get a file or dir path - dialog_opt parameter is used to customise dialogs - submit_id is used in a similar way as for forms
author Goffi <goffi@goffi.org>
date Mon, 11 Aug 2014 19:10:24 +0200
parents 2cb30f46e560
children 7fcafc3206b1
comparison
equal deleted inserted replaced
1104:490a8a4536b6 1105:018bdd687747
16 16
17 # You should have received a copy of the GNU Affero General Public License 17 # You should have received a copy of the GNU Affero General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>. 18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
19 19
20 from sat.core.i18n import _ 20 from sat.core.i18n import _
21 from sat.core.constants import Const as C
21 from sat.core.log import getLogger 22 from sat.core.log import getLogger
22 log = getLogger(__name__) 23 log = getLogger(__name__)
23 from xml.dom import minidom, NotFoundErr 24 from xml.dom import minidom, NotFoundErr
24 from wokkel import data_form 25 from wokkel import data_form
25 from twisted.words.xish import domish 26 from twisted.words.xish import domish
738 def __init__(self, xmlui, options, selected=None, style=None, name=None, parent=None): 739 def __init__(self, xmlui, options, selected=None, style=None, name=None, parent=None):
739 """ 740 """
740 741
741 @param xmlui 742 @param xmlui
742 @param options (list[string, tuple]): each option can be given as: 743 @param options (list[string, tuple]): each option can be given as:
743 - a string if the label and the value are the same 744 - a string if the label and the value are the same
744 - a couple if the label and the value differ 745 - a couple if the label and the value differ
745 @param selected (list[string]): list of the selected values 746 @param selected (list[string]): list of the selected values
746 @param style (string) 747 @param style (string)
747 @param name (string) 748 @param name (string)
748 @param parent 749 @param parent
749 """ 750 """
770 assert(isinstance(option, basestring) or isinstance(option, tuple)) 771 assert(isinstance(option, basestring) or isinstance(option, tuple))
771 value = option if isinstance(option, basestring) else option[0] 772 value = option if isinstance(option, basestring) else option[0]
772 OptionElement(self, option, value in selected) 773 OptionElement(self, option, value in selected)
773 774
774 775
776 ## Dialog Elements ##
777
778
779 class DialogElement(Element):
780 """Main dialog element """
781 type = 'dialog'
782
783 def __init__(self, parent, type_, level=None):
784 if not isinstance(parent, TopElement):
785 raise exceptions.DataError(_("DialogElement must be a direct child of TopElement"))
786 super(DialogElement, self).__init__(parent.xmlui, parent)
787 self.elem.setAttribute(C.XMLUI_DATA_TYPE, type_)
788 self.elem.setAttribute(C.XMLUI_DATA_LVL, level or C.XMLUI_DATA_LVL_DEFAULT)
789
790
791 class MessageElement(Element):
792 """Element with the instruction message"""
793 type = C.XMLUI_DATA_MESS
794
795 def __init__(self, parent, message):
796 if not isinstance(parent, DialogElement):
797 raise exceptions.DataError(_("MessageElement must be a direct child of DialogElement"))
798 super(MessageElement, self).__init__(parent.xmlui, parent)
799 message_txt = self.xmlui.doc.createTextNode(message)
800 self.elem.appendChild(message_txt)
801
802
803 class ButtonsElement(Element):
804 """Buttons element which indicate which set to use"""
805 type = 'buttons'
806
807 def __init__(self, parent, set_):
808 if not isinstance(parent, DialogElement):
809 raise exceptions.DataError(_("ButtonsElement must be a direct child of DialogElement"))
810 super(ButtonsElement, self).__init__(parent.xmlui, parent)
811 self.elem.setAttribute('set', set_)
812
813
814 class FileElement(Element):
815 """File element used for FileDialog"""
816 type = 'file'
817
818 def __init__(self, parent, type_):
819 if not isinstance(parent, DialogElement):
820 raise exceptions.DataError(_("FileElement must be a direct child of DialogElement"))
821 super(FileElement, self).__init__(parent.xmlui, parent)
822 self.elem.setAttribute('type', type_)
823
824
775 ## XMLUI main class 825 ## XMLUI main class
776 826
777 827
778 class XMLUI(object): 828 class XMLUI(object):
779 """This class is used to create a user interface (form/window/parameters/etc) using SàT XML""" 829 """This class is used to create a user interface (form/window/parameters/etc) using SàT XML"""
780 830
781 def __init__(self, panel_type="window", container="vertical", title=None, submit_id=None, session_id=None): 831 def __init__(self, panel_type="window", container="vertical", dialog_opt=None, title=None, submit_id=None, session_id=None):
782 """Init SàT XML Panel 832 """Init SàT XML Panel
833
783 @param panel_type: one of 834 @param panel_type: one of
784 - window (new window) 835 - C.XMLUI_WINDOW (new window)
785 - popup 836 - C.XMLUI_POPUP
786 - form (formulaire, depend of the frontend, usually a panel with cancel/submit buttons) 837 - C.XMLUI_FORM (formulaire, depend of the frontend, usually a panel with cancel/submit buttons)
787 - param (parameters, presentation depend of the frontend) 838 - C.XMLUI_PARAM (parameters, presentation depend of the frontend)
839 - C.XMLUI_DIALOG (one common dialog, presentation depend of frontend)
788 @param container: disposition of elements, one of: 840 @param container: disposition of elements, one of:
789 - vertical: elements are disposed up to bottom 841 - vertical: elements are disposed up to bottom
790 - horizontal: elements are disposed left to right 842 - horizontal: elements are disposed left to right
791 - pairs: elements come on two aligned columns 843 - pairs: elements come on two aligned columns
792 (usually one for a label, the next for the element) 844 (usually one for a label, the next for the element)
793 - tabs: elemens are in categories with tabs (notebook) 845 - tabs: elemens are in categories with tabs (notebook)
846 @param dialog_opt: only used if panel_type == C.XMLUI_DIALOG. Dictionnary (string/string) where key can be:
847 - C.XMLUI_DATA_TYPE: type of dialog, value can be:
848 - C.XMLUI_DIALOG_MESSAGE (default): an information/error message. Action of user is necessary to close the dialog. Usually the frontend display a classic popup
849 - C.XMLUI_DIALOG_NOTE: like a C.XMLUI_DIALOG_MESSAGE, but action of user is not necessary to close, at frontend choice (it can be closed after a timeout). Usually the frontend display as a timed out notification
850 - C.XMLUI_DIALOG_CONFIRM: dialog with 2 choices (usualy "Ok"/"Cancel").
851 returned data can contain:
852 - "answer": "true" if answer is "ok", "yes" or equivalent, "false" else
853 - C.XLMUI_DIALOG_FILE: a file selection dialog
854 returned data can contain:
855 - "cancelled": "true" if dialog has been cancelled, not present or "false" else
856 - "path": path of the choosed file/dir
857 - C.XMLUI_DATA_MESS: message shown in dialog
858 - C.XMLUI_DATA_LVL: one of:
859 - C.XMLUI_DATA_LVL_INFO (default): normal message
860 - C.XMLUI_DATA_LVL_WARNING: attention of user is important
861 - C.XMLUI_DATA_LVL_ERROR: something went wrong
862 - C.XMLUI_DATA_BTNS_SET: one of:
863 - C.XMLUI_DATA_BTNS_SET_OKCANCEL (default): classical "OK" and "Cancel" set
864 - C.XMLUI_DATA_BTNS_SET_YESNO: a translated "yes" for OK, and "no" for Cancel
794 @param title: title or default if None 865 @param title: title or default if None
795 @param submit_id: callback id to call for panel_type we can submit (form, param) 866 @param submit_id: callback id to call for panel_type we can submit (form, param, dialog)
867 @param session_id: use to keep a session attached to the dialog, must be returned by frontends
796 """ 868 """
797 self._introspect() 869 self._introspect()
798 if panel_type not in ['window', 'form', 'param', 'popup']: 870 if panel_type not in [C.XMLUI_WINDOW, C.XMLUI_FORM, C.XMLUI_PARAM, C.XMLUI_POPUP, C.XMLUI_DIALOG]:
799 raise exceptions.DataError(_("Unknown panel type [%s]") % panel_type) 871 raise exceptions.DataError(_("Unknown panel type [%s]") % panel_type)
800 if panel_type == 'form' and submit_id is None: 872 if panel_type == C.XMLUI_FORM and submit_id is None:
801 raise exceptions.DataError(_("form XMLUI need a submit_id")) 873 raise exceptions.DataError(_("form XMLUI need a submit_id"))
802 if not isinstance(container, basestring): 874 if not isinstance(container, basestring):
803 raise exceptions.DataError(_("container argument must be a string")) 875 raise exceptions.DataError(_("container argument must be a string"))
876 if dialog_opt is not None and panel_type != C.XMLUI_DIALOG:
877 raise exceptions.DataError(_("dialog_opt can only be used with dialog panels"))
804 self.type = panel_type 878 self.type = panel_type
805 impl = minidom.getDOMImplementation() 879 impl = minidom.getDOMImplementation()
806 880
807 self.doc = impl.createDocument(None, "sat_xmlui", None) 881 self.doc = impl.createDocument(None, "sat_xmlui", None)
808 top_element = self.doc.documentElement 882 top_element = self.doc.documentElement
809 top_element.setAttribute("type", panel_type) 883 top_element.setAttribute("type", panel_type)
810 if title: 884 if title:
811 top_element.setAttribute("title", title) 885 top_element.setAttribute("title", title)
812 self.submit_id = submit_id 886 self.submit_id = submit_id
813 self.session_id = session_id 887 self.session_id = session_id
888 if panel_type == C.XMLUI_DIALOG:
889 if dialog_opt is None:
890 dialog_opt = {}
891 self._createDialog(dialog_opt)
892 return
814 self.main_container = self._createContainer(container, TopElement(self)) 893 self.main_container = self._createContainer(container, TopElement(self))
815 self.current_container = self.main_container 894 self.current_container = self.main_container
816 895
817 def _introspect(self): 896 def _introspect(self):
818 """ Introspect module to find Widgets and Containers """ 897 """ Introspect module to find Widgets and Containers """
834 def __del__(self): 913 def __del__(self):
835 self.doc.unlink() 914 self.doc.unlink()
836 915
837 def __getattr__(self, name): 916 def __getattr__(self, name):
838 if name.startswith("add") and name not in ('addWidget',): # addWidgetName(...) create an instance of WidgetName 917 if name.startswith("add") and name not in ('addWidget',): # addWidgetName(...) create an instance of WidgetName
918 if self.type == C.XMLUI_DIALOG:
919 raise exceptions.InternalError(_("addXXX can't be used with dialogs"))
839 class_name = name[3:]+"Widget" 920 class_name = name[3:]+"Widget"
840 if class_name in globals(): 921 if class_name in globals():
841 cls = globals()[class_name] 922 cls = globals()[class_name]
842 if issubclass(cls, Widget): 923 if issubclass(cls, Widget):
843 def createWidget(*args, **kwargs): 924 def createWidget(*args, **kwargs):
884 elif value: 965 elif value:
885 top_element.setAttribute("session_id", value) 966 top_element.setAttribute("session_id", value)
886 else: 967 else:
887 raise exceptions.DataError("session_id can't be empty") 968 raise exceptions.DataError("session_id can't be empty")
888 969
970 def _createDialog(self, dialog_opt):
971 dialog_type = dialog_opt.setdefault(C.XMLUI_DATA_TYPE, C.XMLUI_DIALOG_MESSAGE)
972 if dialog_type in [C.XMLUI_DIALOG_CONFIRM, C.XMLUI_DIALOG_FILE] and self.submit_id is None:
973 raise exceptions.InternalError(_("Submit ID must be filled for this kind of dialog"))
974 top_element = TopElement(self)
975 level = dialog_opt.get(C.XMLUI_DATA_LVL)
976 dialog_elt = DialogElement(top_element, dialog_type, level)
977
978 try:
979 MessageElement(dialog_elt, dialog_opt[C.XMLUI_DATA_MESS])
980 except KeyError:
981 pass
982
983 try:
984 ButtonsElement(dialog_elt, dialog_opt[C.XMLUI_DATA_BTNS_SET])
985 except KeyError:
986 pass
987
988 try:
989 FileElement(dialog_elt, dialog_opt[C.XMLUI_DATA_FILETYPE])
990 except KeyError:
991 pass
992
889 def _createContainer(self, container, parent=None, **kwargs): 993 def _createContainer(self, container, parent=None, **kwargs):
890 """Create a container element 994 """Create a container element
891 @param type: container type (cf init doc) 995 @param type: container type (cf init doc)
892 @parent: parent element or None 996 @parent: parent element or None
893 """ 997 """