Mercurial > libervia-backend
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 """ |