1.1 --- a/XSLForms/Resources/PyQtWebResources.py Sun Oct 12 19:28:30 2008 +0200
1.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
1.3 @@ -1,313 +0,0 @@
1.4 -#!/usr/bin/env python
1.5 -
1.6 -"""
1.7 -PyQt-compatible resources for use with WebStack.
1.8 -
1.9 -Copyright (C) 2005, 2007 Paul Boddie <paul@boddie.org.uk>
1.10 -
1.11 -This program is free software; you can redistribute it and/or modify it under
1.12 -the terms of the GNU Lesser General Public License as published by the Free
1.13 -Software Foundation; either version 3 of the License, or (at your option) any
1.14 -later version.
1.15 -
1.16 -This program is distributed in the hope that it will be useful, but WITHOUT
1.17 -ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
1.18 -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
1.19 -details.
1.20 -
1.21 -You should have received a copy of the GNU Lesser General Public License along
1.22 -with this program. If not, see <http://www.gnu.org/licenses/>.
1.23 -"""
1.24 -
1.25 -import XSLForms.Prepare
1.26 -import XSLForms.Resources.PyQtCommon
1.27 -import XSLForms.Resources.WebResources
1.28 -import WebStack.Generic
1.29 -import os
1.30 -import libxml2dom
1.31 -
1.32 -class XSLFormsResource(XSLForms.Resources.WebResources.XSLFormsResource,
1.33 - XSLForms.Resources.PyQtCommon.PyQtCommonResource):
1.34 -
1.35 - """
1.36 - An XSLForms resource supporting PyQt-compatible Web applications for use
1.37 - with WebStack.
1.38 - """
1.39 -
1.40 - encoding = "utf-8"
1.41 - widget_resources = {}
1.42 -
1.43 - def __init__(self, design_identifier):
1.44 - self.factory = Factory()
1.45 - self.default_design = design_identifier
1.46 -
1.47 - # NOTE: Filenames extended by string concatenation.
1.48 -
1.49 - self.template_resources = {}
1.50 - self.init_resources = {}
1.51 - for design_identifier, design_name in self.design_resources.items():
1.52 - self.template_resources[design_identifier] = (design_name + "_template.xhtml", design_name + "_output.xsl")
1.53 - self.init_resources[design_identifier] = (design_name + "_template.xhtml", design_name + "_input.xsl")
1.54 -
1.55 - # Initialisation of connections - just a mapping from field names to
1.56 - # methods in the Web version.
1.57 -
1.58 - self.method_resources = {}
1.59 - for design_identifier, design_name in self.design_resources.items():
1.60 - design_path = self.prepare_design(design_identifier)
1.61 - design_doc = libxml2dom.parse(design_path)
1.62 - connections = {}
1.63 - for connection in design_doc.xpath("UI/connections/connection"):
1.64 - receiver = "".join([n.nodeValue for n in connection.xpath("receiver/text()")])
1.65 - if receiver == design_identifier:
1.66 - sender = "".join([n.nodeValue for n in connection.xpath("sender/text()")])
1.67 - slot = "".join([n.nodeValue for n in connection.xpath("slot/text()")])
1.68 - slot = slot.split("(")[0]
1.69 - connections[sender] = slot
1.70 - self.method_resources[design_identifier] = connections
1.71 -
1.72 - # Initialisation of template fragments.
1.73 -
1.74 - self.in_page_resources = {}
1.75 - for widget_identifier, (design_name, fragment_id) in self.widget_resources.items():
1.76 - self.in_page_resources[widget_identifier] = (design_name + "_output.xsl", fragment_id)
1.77 -
1.78 - # Refresh status - avoiding multiple refresh calls.
1.79 -
1.80 - self._refreshed = 0
1.81 -
1.82 - # Resource methods.
1.83 -
1.84 - def prepare_output(self, design_identifier):
1.85 -
1.86 - """
1.87 - Prepare the output stylesheets using the given 'design_identifier' to
1.88 - indicate which templates and stylesheets are to be employed in the
1.89 - production of output from the resource.
1.90 -
1.91 - The 'design_identifier' is used as a key to the 'design_resources' and
1.92 - 'template_resources' dictionary attributes.
1.93 -
1.94 - Return the full path to the output stylesheet for use with 'send_output'
1.95 - or 'get_result'.
1.96 - """
1.97 -
1.98 - design_path = self.prepare_design(design_identifier)
1.99 - template_filename, output_filename = self.template_resources[design_identifier]
1.100 - output_path = os.path.abspath(os.path.join(self.resource_dir, output_filename))
1.101 - template_path = os.path.abspath(os.path.join(self.resource_dir, template_filename))
1.102 - XSLForms.Prepare.ensure_qt_template(design_path, template_path)
1.103 - XSLForms.Prepare.ensure_stylesheet(template_path, output_path)
1.104 - return output_path
1.105 -
1.106 - # PyQt compatibility methods.
1.107 -
1.108 - def get_document(self, document_identifier):
1.109 - return libxml2dom.parse(self.prepare_document(document_identifier))
1.110 -
1.111 - def prepare_widget(self, design_identifier, widget_identifier, parent=None):
1.112 - fragment_name, widget_name = self.widget_resources[widget_identifier]
1.113 - element = UINode(self.doc._node.ownerDocument.createElement(widget_name))
1.114 -
1.115 - # NOTE: Creating an element which may not be appropriate.
1.116 -
1.117 - element._node.appendChild(self.doc._node.ownerDocument.createElement(widget_name + "_value"))
1.118 - return element
1.119 -
1.120 - def child(self, name):
1.121 - return self.doc.child(name)
1.122 -
1.123 - def sender(self):
1.124 - return self._sender
1.125 -
1.126 - # PyQt structural methods.
1.127 -
1.128 - def form_init(self):
1.129 -
1.130 - "Initialise a newly-created form."
1.131 -
1.132 - raise NotImplementedError, "form_init"
1.133 -
1.134 - def form_populate(self):
1.135 -
1.136 - "Populate the values in a form."
1.137 -
1.138 - raise NotImplementedError, "form_populate"
1.139 -
1.140 - def form_refresh(self):
1.141 -
1.142 - "Refresh the form."
1.143 -
1.144 - raise NotImplementedError, "form_refresh"
1.145 -
1.146 - def request_refresh(self, *args, **kw):
1.147 -
1.148 - "Request a refresh of the form."
1.149 -
1.150 - if not self._refreshed:
1.151 - self._refreshed = 1
1.152 - self.form_refresh(*args, **kw)
1.153 -
1.154 - # Standard XSLFormsResource method, overridden to handle presentation.
1.155 -
1.156 - def respond_to_form(self, trans, form):
1.157 -
1.158 - """
1.159 - Respond to the request described by the given transaction 'trans', using
1.160 - the given 'form' object to conveniently retrieve field (request
1.161 - parameter) information and structured form information (as DOM-style XML
1.162 - documents).
1.163 - """
1.164 -
1.165 - self._refreshed = 0
1.166 -
1.167 - # Ensure the presence of the template.
1.168 -
1.169 - self.prepare_output(self.default_design)
1.170 -
1.171 - # Remember the document since it is accessed independently elsewhere.
1.172 -
1.173 - doc = form.get_document(self.default_design)
1.174 - if doc is None:
1.175 - doc = form.new_document(self.default_design)
1.176 - doc = self._get_initialised_form(doc)
1.177 - self.doc = UINode(doc.xpath("*")[0])
1.178 - self.form_init()
1.179 - else:
1.180 - doc = self._get_initialised_form(doc)
1.181 - self.doc = UINode(doc.xpath("*")[0])
1.182 -
1.183 - self.form_populate()
1.184 -
1.185 - # Updates happen here.
1.186 -
1.187 - form.set_document(self.default_design, doc)
1.188 - selectors = form.get_selectors()
1.189 - connections = self.method_resources[self.default_design]
1.190 - for selector_name, selector_values in selectors.items():
1.191 - if connections.has_key(selector_name):
1.192 - slot = connections[selector_name]
1.193 - if hasattr(self, slot):
1.194 -
1.195 - # Initialise the sender.
1.196 -
1.197 - for selector_value in selector_values:
1.198 - # NOTE: Fake a special element to simulate the Qt widget hierarchy.
1.199 - # NOTE: We could instead have set the underlying annotations for
1.200 - # NOTE: selector-field differently, but that would be more work.
1.201 - # NOTE: An alternative which works in certain cases is a new
1.202 - # NOTE: attribute whose node is retained.
1.203 - _sender = self.doc._node.ownerDocument.createElement("_sender")
1.204 - self._sender = UINode(selector_value.appendChild(_sender))
1.205 - getattr(self, slot)()
1.206 -
1.207 - # Consistency is ensured and filtering enforced.
1.208 -
1.209 - self.request_refresh()
1.210 - #print self.doc._node.toString("iso-8859-1")
1.211 -
1.212 - # Output is produced.
1.213 -
1.214 - attributes = trans.get_attributes()
1.215 - encoding = attributes.get("encoding") or self.encoding or trans.default_charset
1.216 -
1.217 - trans.set_content_type(WebStack.Generic.ContentType("application/xhtml+xml", encoding))
1.218 - design_xsl = self.prepare_output(self.default_design)
1.219 - self.send_output(trans, [design_xsl], doc._node)
1.220 -
1.221 - def _get_initialised_form(self, doc):
1.222 - input_xsl = self.prepare_initialiser(self.default_design, init_enumerations=0)
1.223 - return self.get_result([input_xsl], doc)
1.224 -
1.225 -class UINode:
1.226 -
1.227 - "A PyQt widget tree emulation node."
1.228 -
1.229 - def __init__(self, node):
1.230 - self._node = node
1.231 -
1.232 - def add(self, node):
1.233 - self._node.appendChild(node._node)
1.234 -
1.235 - def child(self, name):
1.236 - nodes = self._node.xpath(name)
1.237 - if len(nodes) > 0:
1.238 - return UINode(nodes[0])
1.239 - else:
1.240 - return None
1.241 -
1.242 - def children(self):
1.243 - return [UINode(node) for node in self._node.childNodes]
1.244 -
1.245 - def count(self):
1.246 - return len(self._node.childNodes)
1.247 -
1.248 - def currentText(self):
1.249 - return self._node.getAttribute("value")
1.250 -
1.251 - def currentItem(self):
1.252 - found = self._node.xpath("*[@value=current()/@value]")
1.253 - if found:
1.254 - return int(found.xpath("count(preceding-sibling::*)"))
1.255 - else:
1.256 - return 0
1.257 -
1.258 - def deleteLater(self):
1.259 - pass
1.260 -
1.261 - def insertItem(self, item, position=-1):
1.262 - # NOTE: Names invented rather than being extracted from the schema.
1.263 - new_element = self._node.ownerDocument.createElement(self._node.localName + "_enum")
1.264 - new_element.setAttribute("value", item)
1.265 - if position == -1:
1.266 - self._node.appendChild(new_element)
1.267 - else:
1.268 - elements = self._node.xpath("*")
1.269 - if position < len(elements) - 1:
1.270 - self._node.insertBefore(new_element, elements[position])
1.271 - else:
1.272 - self._node.appendChild(new_element)
1.273 -
1.274 - def layout(self):
1.275 - return self
1.276 -
1.277 - def parent(self):
1.278 - return UINode(self._node.parentNode)
1.279 -
1.280 - def removeItem(self, item):
1.281 - elements = self._node.xpath("*")
1.282 - if item < len(elements):
1.283 - self._node.removeChild(elements[item])
1.284 -
1.285 - def remove(self, item):
1.286 - self._node.removeChild(item._node)
1.287 -
1.288 - def setCurrentItem(self, index):
1.289 - pass # NOTE: Not implemented yet!
1.290 -
1.291 - def show(self):
1.292 - pass
1.293 -
1.294 -class Factory:
1.295 -
1.296 - "A widget factory helper class."
1.297 -
1.298 - def connect(self, widget, obj):
1.299 -
1.300 - """
1.301 - Connection is done all at once by mapping field names to method names in
1.302 - the resource object.
1.303 - """
1.304 -
1.305 - pass
1.306 -
1.307 - def find_widgets(self, widget, name):
1.308 -
1.309 - """
1.310 - Find within the given 'widget' (a DOM node) the widget with the given
1.311 - 'name'.
1.312 - """
1.313 -
1.314 - return [UINode(node) for node in widget.doc._node.getElementsByTagName(name)]
1.315 -
1.316 -# vim: tabstop=4 expandtab shiftwidth=4