1 #!/usr/bin/env python 2 3 """ 4 PyQt-compatible resources for use with WebStack. 5 6 Copyright (C) 2005 Paul Boddie <paul@boddie.org.uk> 7 8 This library is free software; you can redistribute it and/or 9 modify it under the terms of the GNU Lesser General Public 10 License as published by the Free Software Foundation; either 11 version 2.1 of the License, or (at your option) any later version. 12 13 This library 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 GNU 16 Lesser General Public License for more details. 17 18 You should have received a copy of the GNU Lesser General Public 19 License along with this library; if not, write to the Free Software 20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 """ 22 23 import XSLForms.Prepare 24 import XSLForms.Resources.Common 25 import XSLForms.Resources.WebResources 26 import WebStack.Generic 27 import os 28 import libxml2dom 29 30 class XSLFormsResource(XSLForms.Resources.WebResources.XSLFormsResource, 31 XSLForms.Resources.Common.PyQtCommonResource): 32 33 """ 34 An XSLForms resource supporting PyQt-compatible Web applications for use 35 with WebStack. 36 """ 37 38 widget_resources = {} 39 40 def __init__(self, design_identifier): 41 self.factory = Factory() 42 self.default_design = design_identifier 43 44 # NOTE: Filename extended by string concatenation. 45 46 self.template_resources = {} 47 self.init_resources = {} 48 for design_identifier, design_name in self.design_resources.items(): 49 self.template_resources[design_identifier] = (design_name + "_template.xhtml", design_name + "_output.xsl") 50 self.init_resources[design_identifier] = (design_name + "_template.xhtml", design_name + "_input.xsl") 51 52 # Resource methods. 53 54 def prepare_output(self, design_identifier): 55 56 """ 57 Prepare the output stylesheets using the given 'design_identifier' to 58 indicate which templates and stylesheets are to be employed in the 59 production of output from the resource. 60 61 The 'design_identifier' is used as a key to the 'design_resources' and 62 'template_resources' dictionary attributes. 63 64 Return the full path to the output stylesheet for use with 'send_output' 65 or 'get_result'. 66 """ 67 68 design_path = self.prepare_design(design_identifier) 69 template_filename, output_filename = self.template_resources[design_identifier] 70 output_path = os.path.abspath(os.path.join(self.resource_dir, output_filename)) 71 template_path = os.path.abspath(os.path.join(self.resource_dir, template_filename)) 72 XSLForms.Prepare.ensure_qt_template(design_path, template_path) 73 XSLForms.Prepare.ensure_stylesheet(template_path, output_path) 74 return output_path 75 76 # PyQt compatibility methods. 77 78 def get_document(self, document_identifier): 79 return libxml2dom.parse(self.prepare_document(document_identifier)) 80 81 def prepare_widget(self, design_identifier, widget_identifier, parent=None): 82 design_path = self.prepare_design(design_identifier) 83 fragment_name, widget_name = self.widget_resources[widget_identifier] 84 fragment_path = os.path.abspath(os.path.join(self.resource_dir, fragment_name)) 85 XSLForms.Prepare.ensure_qt_fragment(design_path, fragment_path, widget_name) 86 # NOTE: Implement the equivalent here! 87 return qtui.QWidgetFactory.create(fragment_path, None, parent) 88 89 def child(self, name): 90 return self.doc.child(name) 91 92 # PyQt structural methods. 93 94 def form_init(self): 95 96 "Initialise a newly-created form." 97 98 raise NotImplementedError, "form_init" 99 100 def form_populate(self): 101 102 "Populate the values in a form." 103 104 raise NotImplementedError, "form_populate" 105 106 def form_refresh(self): 107 108 "Refresh the form." 109 110 raise NotImplementedError, "form_refresh" 111 112 # Standard XSLFormsResource method, overridden to handle presentation. 113 114 def respond_to_form(self, trans, form): 115 116 """ 117 Respond to the request described by the given transaction 'trans', using 118 the given 'form' object to conveniently retrieve field (request 119 parameter) information and structured form information (as DOM-style XML 120 documents). 121 """ 122 123 # Ensure the presence of the template. 124 125 self.prepare_output(self.default_design) 126 127 # Remember the document since it is accessed independently elsewhere. 128 129 doc = form.get_document(self.default_design) 130 if doc is None: 131 doc = form.new_document(self.default_design) 132 doc = self._form_init(doc) 133 self.doc = UINode(doc.xpath("*")[0]) 134 self.form_init() 135 else: 136 doc = self._form_init(doc) 137 self.doc = UINode(doc.xpath("*")[0]) 138 139 self.form_populate() 140 #print self.doc._node.toString("iso-8859-1") 141 142 # NOTE: Updates happen here. 143 144 self.form_refresh() 145 146 trans.set_content_type(WebStack.Generic.ContentType("application/xhtml+xml", self.encoding)) 147 design_xsl = self.prepare_output(self.default_design) 148 self.send_output(trans, [design_xsl], doc._node) 149 150 def _form_init(self, doc): 151 input_xsl = self.prepare_initialiser(self.default_design, init_enumerations=0) 152 return self.get_result([input_xsl], doc) 153 154 class UINode: 155 156 "A PyQt widget tree emulation node." 157 158 def __init__(self, node): 159 self._node = node 160 161 def child(self, name): 162 nodes = self._node.xpath(name) 163 if len(nodes) > 0: 164 return UINode(nodes[0]) 165 else: 166 return None 167 168 def children(self): 169 return [UINode(node) for node in self._node.childNodes] 170 171 def count(self): 172 return len(self._node.childNodes) 173 174 def currentText(self): 175 return self._node.getAttribute("value") 176 177 def currentItem(self): 178 found = self._node.xpath("*[@value=current()/@value]") 179 if found: 180 return int(found.xpath("count(preceding-sibling::*)")) 181 else: 182 return 0 183 184 def insertItem(self, item, position=-1): 185 # NOTE: Names invented rather than being extracted from the schema. 186 new_element = self._node.ownerDocument.createElement(self._node.localName + "_enum") 187 new_element.setAttribute("value", item) 188 if position == -1: 189 self._node.appendChild(new_element) 190 else: 191 elements = self._node.xpath("*") 192 if position < len(elements) - 1: 193 self._node.insertBefore(new_element, elements[position]) 194 else: 195 self._node.appendChild(new_element) 196 197 def parent(self): 198 return UINode(self._node.parentNode) 199 200 def removeItem(self, item): 201 pass # NOTE: Not implemented yet! 202 203 def remove(self, item): 204 pass # NOTE: Not implemented yet! 205 206 def layout(self): 207 return self 208 209 def setCurrentItem(self, index): 210 pass # NOTE: Not implemented yet! 211 212 def deleteLater(self): 213 self._node.parentNode.removeChild(self._node) 214 215 class Factory: 216 217 "A widget factory helper class." 218 219 def connect(self, widget, obj): 220 pass 221 222 def find_widgets(self, widget, name): 223 224 """ 225 Find within the given 'widget' (a DOM node) the widget with the given 226 'name'. 227 """ 228 229 return [UINode(node) for node in widget.doc._node.getElementsByTagName(name)] 230 231 # vim: tabstop=4 expandtab shiftwidth=4