Mercurial > MadButterfly
view pyink/html5css3.py @ 1532:4a92b639a1cd
Clear selection set when switching current scene.
To clear selection set after switching away from current to another scene.
It avoids Inkscape select on nodes they are not saw after switching.
author | Thinker K.F. Li <thinker@codemud.net> |
---|---|
date | Fri, 30 Sep 2011 12:31:33 +0800 |
parents | 9e7ec3b96968 |
children |
line wrap: on
line source
import pybExtension from domview import component_manager, layers_parser, scenes_parser from trait import composite, trait, require @composite class dom_parser(object): use_traits = (component_manager, layers_parser, scenes_parser) provide_traits = {layers_parser.get_scene: '_scenes_get_scene'} method_map_traits = { scenes_parser._find_maxframe: '_find_maxframe', scenes_parser._collect_all_scenes: '_collect_all_scenes', scenes_parser._collect_node_ids: '_collect_node_ids', component_manager._start_component_manager: '_start_component_manager', scenes_parser.get_scene: '_scenes_get_scene' } def __init__(self): self._metadata_node = None self._scenes_node = None self._doc = None self._root = None self._layers = [] self._layers_parent = None self._maxframe = 0 self.current = 0 self._id2node = {} self._group2scene = {} pass def _find_metadata(self): for child in self._root.childList(): if child.name() == 'svg:metadata': self._metadata_node = child break pass else: raise RuntimeError, 'can not find \'svg:metadata\' node' for child in self._metadata_node.childList(): if child.name() == 'ns0:scenes': self._scenes_node = child break pass else: raise RuntimeError, \ 'can not find \'ns0:scenes\' node under \'svg:metadata\'' pass ## \brief Return the range of a scene node. # @staticmethod def _scene_node_range(node): start = node.getAttribute('start') try: end = node.getAttribute('end') except: end = start pass try: scene_type = node.getAttribute('type') except: scene_type = 'normal' pass return int(start), int(end), scene_type ## \brief To handle the parsing for the given document. # def start_handle(self, doc): self._doc = doc self._root = doc.root() self._layers_parent = self._root self._find_metadata() self._collect_node_ids() self._collect_all_scenes() self.parse_all_layers() self._start_component_manager() pass ## \brief Reset the content of the parser before next parsing. # def reset(self): self.reset_layers() pass def get_maxframe(self): return self._maxframe def _find_scene_node(self, frame_idx, layer_idx): layer = self._layers[layer_idx] for scene_node in layer.scenes: start, end, scene_type = self._scene_node_range(scene_node) if start <= frame_idx and frame_idx <= end: return scene_node pass pass def get_scene(self, frame_idx, layer_idx): scene_node = self._find_scene_node(frame_idx, layer_idx) if scene_node: start, end, scene_type = self._scene_node_range(scene_node) return start, end, scene_type return None def get_scene_group(self, frame_idx, layer_idx): scene_node = self._find_scene_node(frame_idx, layer_idx) if scene_node: gid = scene_node.getAttribute('ref') scene_group = self.get_node(gid) return scene_group return None pass def _print_subtree(node, lvl, out): ## \brief Determize whether to skip a node and its descendants. # # Some part of the tree is useless in the result document. # def skip(node): node_name = node.name() if node_name == 'svg:metadata': return True try: label = node.getAttribute('inkscape:label') except: return False if label == 'dup': return True return False ## \brief Last modify style properties before generate output. # # This is function return a string that will be written out as # value of style attribute. # def filter_style(node): from tween import _parse_style_ani try: style = node.getAttribute('style') except: style = '' pass # # Make scene groups hidden # try: scene_group = node.getAttribute('scene_group') except: return style if scene_group != 'true': return style props = {} _parse_style_ani(node, props) props['display'] = 'none' return ';'.join([key + ':' + value for key, value in props.items()]) def _print_level(txt, lvl, out): indent = ' ' * lvl print >> out, '%s%s' % (indent, txt) pass def _print_node_open(node, lvl, out): node_name = node.name() if node_name.startswith('svg:'): node_name = node_name[4:] pass attrs = [] for attrname in node.allAttributes(): if attrname == 'style': attrvalue = filter_style(node) if not attrvalue: continue pass else: attrvalue = node.getAttribute(attrname) pass attr = '%s="%s"' % (attrname, attrvalue) attrs.append(attr) pass if attrs: attrs_str = ' '.join(attrs) line = '<%s %s>' % (node_name, attrs_str) else: line = '<%s>' % (node_name) pass _print_level(line, lvl, out) pass def _print_node_close(node, lvl, out): node_name = node.name() if node_name.startswith('svg:'): node_name = node_name[4:] pass line = '</%s>' % (node_name) _print_level(line, lvl, out) pass def _print_node_single(node, lvl, out): node_name = node.name() if node_name.startswith('svg:'): node_name = node_name[4:] pass attrs = [] for attrname in node.allAttributes(): attrvalue = node.getAttribute(attrname) attr = '%s="%s"' % (attrname, attrvalue) attrs.append(attr) pass if attrs: attrs_str = ' '.join(attrs) line = '<%s %s/>' % (node_name, attrs_str) else: line = '<%s/>' % (node_name) pass _print_level(line, lvl, out) pass def _print_node_content(node, lvl, out): line = node.content() _print_level(line, lvl, out) pass children = node.childList() if not children: if node.name() != 'string': _print_node_single(node, lvl, out) else: _print_node_content(node, lvl, out) pass return _print_node_open(node, lvl, out) for child in children: if skip(child): continue _print_subtree(child, lvl + 1, out) pass _print_node_close(node, lvl, out) pass ## \brief CSS3 animation code generator. # # This trait parses and generate transition code, in CSS3, for two # nodes. # @trait class css3_ani_gen(object): _parse_ani_attrs = require _passing = lambda name, value: (name, str(value)) _trans_transform = lambda name, value: \ (name, 'matrix(' + ','.join([str(e) for e in value[0]]) + ')') _translators = {'x': _passing, 'y': _passing, 'width': _passing, 'height': _passing, 'opacity': _passing, 'transform': _trans_transform } del _trans_transform del _passing @staticmethod def _ani_attr_to_css3(attrname, attrvalue, css): translator = css3_ani_gen._translators[attrname] cssname, cssvalue = translator(attrname, attrvalue) css[cssname] = cssvalue pass def _make_css3_transition(self, node1, node2, duration): from tween import _normalize_attrs ani_attrs1 = self._parse_ani_attrs(node1) ani_attrs2 = self._parse_ani_attrs(node2) _normalize_attrs(node1, ani_attrs1, node2, ani_attrs2) css = {} attrnames = set(ani_attrs1.keys() + ani_attrs2.keys()) for attrname in attrnames: attrvalue = ani_attrs2[attrname] self._ani_attr_to_css3(attrname, attrvalue, css) pass properties = css.keys() css['transition-property'] = ', '.join(properties) css['transition-duration'] = '%gs' % (duration) return css def _write_css(self, selector, css_props, out): print >> out, '%s {' % (selector) for prop_name, prop_value in css_props.items(): print >> out, ' %s: %s;' % (prop_name, prop_value) pass print >> out, '}' pass pass @composite class html5css3_ext(pybExtension.PYBindExtImp): use_traits = (css3_ani_gen,) method_map_traits = {css3_ani_gen._make_css3_transition: '_make_css3_transition', css3_ani_gen._write_css: '_write_css' } _frame_rate = 12 def __init__(self): self._stylesheet = {} pass @staticmethod def _parse_ani_attrs(node): from tween import _parse_attr_ani, _parse_style_ani attrs = {} _parse_attr_ani(node, attrs) _parse_style_ani(node, attrs) return attrs def save(self, module, doc, filename): import sys parser = dom_parser() self._parser = parser parser.start_handle(doc.rdoc) self._handle_transition_layers() out = file(filename, 'w+') print >> out, '''\ <html xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:ns0="http://madbutterfly.sourceforge.net/DTD/madbutterfly.dtd" > <head> <title>Scribboo Test Page</title> <!-- Style for animation transitions --> <style type='text/css'>''' for selector, style in self._stylesheet.items(): self._write_css(selector, style, out) pass print >> out, '''\ </style> <script>''' print >>out, 'var animation_info = ' import pprint pprint.pprint(self._stylesheet, stream=out, indent=4, depth=2) print >> out, '''\ </script> </head> <body>''' root = doc.rdoc.root() _print_subtree(root, 1, out) print >> out, '''\ </body> </html>''' out.close() pass ## \brief Find all animation pairs. # # An animation pair is two nodes, one from start scene group and # another from stop scene group. The node from start scene group # will transite to the state of the node from stop scene group. # This method is responsible for finding all pairs. # @staticmethod def _find_transition_pairs(start_scene_group, stop_scene_group): # Collect all nodes in stop scene stop_nodes = {} node = stop_scene_group.firstChild() while node: try: node_label = node.getAttribute("ns0:duplicate-src") stop_nodes[node_label] = node except: pass node = node.next() pass # Collect all nodes in start scene start_nodes = {} node = start_scene_group.firstChild() while node: try: node_label = node.getAttribute("id") start_nodes[node_label] = node except: pass node = node.next() pass pairs = [] for start_node in start_scene_group.childList(): start_node_id = start_node.getAttribute('id') try: stop_node = stop_nodes[start_node_id] except: stop_node = start_node pass pairs.append((start_node, stop_node)) pass return pairs def _make_scene_group_style(self, frame_idx, layer_idx): scene_group = self._parser.get_scene_group(frame_idx, layer_idx) gid = scene_group.getAttribute('id') selector = '.frame%04d #%s' % (frame_idx, gid) style = {'display': 'inline'} self._stylesheet[selector] = style pass def _handle_transition_layer(self, layer_idx): parser = self._parser maxframe = parser.get_maxframe() stylesheet = self._stylesheet frame_idx = 0 while frame_idx < maxframe: scene = parser.get_scene(frame_idx, layer_idx) if not scene: frame_idx = frame_idx + 1 continue self._make_scene_group_style(frame_idx, layer_idx) start, end, tween_type = scene if start == end: frame_idx = frame_idx + 1 continue next_start = end + 1 scene = parser.get_scene(next_start, layer_idx) if not scene: frame_idx = next_start + 1 continue scene_group = parser.get_scene_group(start, layer_idx) next_scene_group = parser.get_scene_group(next_start, layer_idx) duration = float(next_start - start) / self._frame_rate # # Generate style for every transition pair # transition_pairs = \ self._find_transition_pairs(scene_group, next_scene_group) for start_node, stop_node in transition_pairs: css_props = self._make_css3_transition(start_node, stop_node, duration) node_id = start_node.getAttribute('id') selector = '.frame%04d #%s' % (start, node_id) stylesheet[selector] = css_props pass frame_idx = next_start pass pass def _handle_transition_layers(self): num_layers = self._parser.get_layer_num() for layer_idx in range(num_layers): self._handle_transition_layer(layer_idx) pass pass pass extension = (html5css3_ext(), 'net.scribboo.html5css3', 'HTML5/CSS3 exporter', 'output', {'extension': '.html', 'mimetype': 'text/html', '_filetypename': 'HTML5/CSS3 (*.html)'})