Mercurial > MadButterfly
comparison pyink/domview.py @ 1243:d5f70928e9f1
Move MBScene_domview_ui and MBScene_domview to separated modules.
- domview.py is where MBScene_domview and MBScene_domview_monitor are.
- domview_ui.py is where MBScene_domview_ui and MBScene_frameline_stack are.
- keep modules small to make better collaberation.
author | Thinker K.F. Li <thinker@codemud.net> |
---|---|
date | Mon, 10 Jan 2011 16:32:16 +0800 |
parents | |
children | b241f9768833 |
comparison
equal
deleted
inserted
replaced
1242:1b1eb8f9a866 | 1243:d5f70928e9f1 |
---|---|
1 import random | |
2 import pybInkscape | |
3 from tween import TweenObject | |
4 | |
5 class Layer: | |
6 def __init__(self, node): | |
7 self.scenes = [] | |
8 self.group = node | |
9 pass | |
10 pass | |
11 | |
12 class ObjectWatcher(pybInkscape.PYNodeObserver): | |
13 def __init__(self, obj, type, func, arg): | |
14 self.obj = obj | |
15 self.type = type | |
16 self.func = func | |
17 self.arg = arg | |
18 | |
19 def notifyChildAdded(self, node, child, prev): | |
20 if self.type == 'DOMNodeInserted': | |
21 self.func(node, child) | |
22 def notifyChildRemoved(self, node, child, prev): | |
23 if self.type == 'DOMNodeRemoved': | |
24 self.func(node, child) | |
25 def notifyChildOrderChanged(self,node,child,prev): | |
26 pass | |
27 def notifyContentChanged(self,node,old_content,new_content): | |
28 if self.type == 'DOMSubtreeModified': | |
29 self.func(node) | |
30 def notifyAttributeChanged(self,node, name, old_value, new_value): | |
31 if self.type == 'DOMAttrModified': | |
32 self.func(node, name, old_value, new_value) | |
33 | |
34 def addEventListener(obj, type, func, arg): | |
35 obs = ObjectWatcher(obj, type, func, arg) | |
36 obj.addSubtreeObserver(obs) | |
37 pass | |
38 | |
39 | |
40 ## \brief Monitor changes of DOM-tree. | |
41 # | |
42 # This class monitors DOM-tree to maintain _maxframe and maps for node ID to | |
43 # node and scene group ID to scene node. | |
44 class MBScene_domview_monitor(object): | |
45 def __init__(self, *args, **kws): | |
46 super(MBScene_domview_monitor, self).__init__() | |
47 | |
48 self._maxframe = 0 | |
49 self._id2node = {} # map ID to the node in the DOM tree. | |
50 self._group2scene = {} # map ID of a group to associated scene node. | |
51 pass | |
52 | |
53 def _start_monitor(self): | |
54 self._collect_node_ids() | |
55 self._collect_all_scenes() | |
56 | |
57 doc = self._doc | |
58 addEventListener(doc, 'DOMNodeInserted', self._on_insert_node, None) | |
59 addEventListener(doc, 'DOMNodeRemoved', self._on_remove_node, None) | |
60 addEventListener(doc, 'DOMAttrModified', self._on_attr_modified, None) | |
61 pass | |
62 | |
63 def _on_insert_node(self, node, child): | |
64 for cchild in child.childList(): | |
65 self._on_insert_node(child, cchild) | |
66 pass | |
67 | |
68 try: | |
69 child_id = child.getAttribute('id') | |
70 except: | |
71 pass | |
72 else: | |
73 if child_id not in self._id2node: | |
74 self._id2node[child_id] = child | |
75 pass | |
76 pass | |
77 | |
78 if child.name() == 'ns0:scene': | |
79 try: | |
80 ref = child.getAttribute('ref') | |
81 except: | |
82 pass | |
83 else: | |
84 if ref not in self._group2scene: | |
85 self._group2scene[ref] = child | |
86 pass | |
87 pass | |
88 | |
89 try: | |
90 start = child.getAttribute('start') | |
91 self._maxframe = max(int(start), self._maxframe) | |
92 except: | |
93 pass | |
94 try: | |
95 start = child.getAttribute('end') | |
96 self._maxframe = max(int(start), self._maxframe) | |
97 except: | |
98 pass | |
99 pass | |
100 pass | |
101 | |
102 def _find_maxframe(self, scenes_node): | |
103 maxframe = 0 | |
104 for child in scenes_node.childList(): | |
105 if child.name() != 'ns0:scene': | |
106 continue | |
107 | |
108 try: | |
109 start = child.getAttribute('start') | |
110 maxframe = max(int(start), maxframe) | |
111 except: | |
112 pass | |
113 try: | |
114 end = child.getAttribute('end') | |
115 maxframe = max(int(end), maxframe) | |
116 except: | |
117 pass | |
118 pass | |
119 return maxframe | |
120 | |
121 def _on_remove_node(self, node, child): | |
122 for cchild in child.childList(): | |
123 self._on_remove_node(child, cchild) | |
124 pass | |
125 | |
126 try: | |
127 child_id = child.getAttribute('id') | |
128 except: | |
129 pass | |
130 else: | |
131 if child_id not in self._id2node: | |
132 raise ValueError, \ | |
133 'remove a node that is never known (%s)' % (child_id) | |
134 del self._id2node[child_id] | |
135 pass | |
136 | |
137 if child.name() == 'ns0:scene': | |
138 try: | |
139 ref = child.getAttribute('ref') | |
140 except: | |
141 pass | |
142 else: | |
143 del self._group2scene[ref] | |
144 pass | |
145 | |
146 try: | |
147 if node.name() == 'ns0:scenes' and \ | |
148 (int(child.getAttribute('start')) == self._maxframe or | |
149 int(child.getAttribute('end')) == self._maxframe): | |
150 self._maxframe = self._find_maxframe(node) | |
151 pass | |
152 except: | |
153 pass | |
154 pass | |
155 pass | |
156 | |
157 def _on_attr_modified(self, node, name, old_value, new_value): | |
158 if name == 'id' and old_value != new_value: | |
159 if old_value and (old_value not in self._id2node): | |
160 raise ValueError, \ | |
161 'old ID value of passed node is invalid one (%s)' % \ | |
162 (old_value) | |
163 if (new_value in self._id2node): | |
164 raise ValueError, \ | |
165 'new ID value of passed node is invalid one (%s)' % \ | |
166 (new_value) | |
167 | |
168 if old_value: | |
169 del self._id2node[old_value] | |
170 pass | |
171 self._id2node[new_value] = node | |
172 pass | |
173 elif name == 'ref' and node.name() == 'ns0:scene': | |
174 if old_value == new_value: | |
175 return | |
176 if old_value: | |
177 node = self._group2scene[old_value] # use old node. Binding | |
178 # may generate a new | |
179 # wrapper. | |
180 del self._group2scene[old_value] | |
181 pass | |
182 if new_value: | |
183 self._group2scene[new_value] = node | |
184 pass | |
185 pass | |
186 elif (name in ('start', 'end')) and node.name() == 'ns0:scene': | |
187 self._maxframe = max(int(new_value), self._maxframe) | |
188 pass | |
189 pass | |
190 | |
191 ## \brief Collect ID of nodes in the document. | |
192 # | |
193 # It is used to implement a fast mapping from an ID to the respective node. | |
194 # | |
195 def _collect_node_ids(self): | |
196 self._id2node = {} | |
197 root = self._root | |
198 for n in root.childList(): | |
199 self._collect_node_ids_recursive(n) | |
200 pass | |
201 pass | |
202 | |
203 def _collect_node_ids_recursive(self, node): | |
204 try: | |
205 node_id = node.getAttribute('id') | |
206 except: | |
207 return | |
208 | |
209 self._id2node[node_id] = node | |
210 for n in node.childList(): | |
211 self._collect_node_ids_recursive(n) | |
212 pass | |
213 pass | |
214 | |
215 def _parse_one_scene(self, scene_node): | |
216 assert scene_node.name() == 'ns0:scene' | |
217 | |
218 start = int(scene_node.getAttribute("start")) | |
219 try: | |
220 end = int(scene_node.getAttribute("end")) | |
221 except: | |
222 end = start | |
223 pass | |
224 | |
225 try: | |
226 scene_type = scene_node.getAttribute('type') | |
227 if scene_type == None: | |
228 scene_type = 'normal' | |
229 pass | |
230 except: | |
231 scene_type = 'normal' | |
232 pass | |
233 | |
234 return start, end, scene_type | |
235 | |
236 def _parse_one_scenes(self, scenes_node): | |
237 try: | |
238 cur = int(n.getAttribute("current")) | |
239 except: | |
240 cur = 0 | |
241 pass | |
242 self.current = cur | |
243 | |
244 for scene_node in scenes_node.childList(): | |
245 if scene_node.name() != 'ns0:scene': | |
246 continue | |
247 | |
248 try: | |
249 start, end, scene_type = self._parse_one_scene(scene_node) | |
250 except: | |
251 continue | |
252 | |
253 group_id = scene_node.getAttribute("ref") | |
254 self._group2scene[group_id] = scene_node | |
255 pass | |
256 pass | |
257 | |
258 ## \brief Parse all scenes node in svg:metadata subtree. | |
259 # | |
260 def _collect_all_scenes(self): | |
261 root = self._root | |
262 for child in root.childList(): | |
263 if child.name() != 'svg:metadata': | |
264 continue | |
265 | |
266 metadata_node = child | |
267 for metachild in metadata_node.childList(): | |
268 if metachild.name() == 'ns0:scenes': | |
269 self._parse_one_scenes(metachild) | |
270 self._maxframe = self._find_maxframe(metachild) | |
271 pass | |
272 pass | |
273 pass | |
274 pass | |
275 | |
276 ## \brief Return the node with given ID. | |
277 # | |
278 def get_node(self, node_id): | |
279 return self._id2node[node_id] | |
280 | |
281 ## \brief Return a scene node corresponding to a scene group of given ID. | |
282 # | |
283 def get_scene(self, group_id): | |
284 return self._group2scene[group_id] | |
285 | |
286 def new_id(self): | |
287 while True: | |
288 candidate = 's%d' % int(random.random()*100000) | |
289 if candidate not in self._id2node: | |
290 return candidate | |
291 pass | |
292 pass | |
293 pass | |
294 | |
295 | |
296 ## \brief This layer provide a data view to the DOM-tree. | |
297 # | |
298 # This class maintains layers information, and provides functions to create, | |
299 # change and destroy scene node and scene group. A scene node is a 'ns0:scene' | |
300 # in 'ns0:scenes' tag. A scene group is respective 'svg:g' for a scene. | |
301 # | |
302 class MBScene_domview(MBScene_domview_monitor): | |
303 # Declare variables, here, for keeping tracking | |
304 _doc = None | |
305 _root = None | |
306 | |
307 def __init__(self, *args, **kws): | |
308 super(MBScene_domview, self).__init__() | |
309 pass | |
310 | |
311 ## \brief Create a scenes node if not existed. | |
312 # | |
313 def _init_metadata(self): | |
314 for node in self._root.childList(): | |
315 if node.name() == 'svg:metadata': | |
316 break | |
317 pass | |
318 else: | |
319 raise RuntimeError, \ | |
320 'can not find <svg:metadata> node in the document' | |
321 | |
322 for n in node.childList(): | |
323 if n.name() == 'ns0:scenes': | |
324 self._scenes_node = n | |
325 break | |
326 pass | |
327 else: | |
328 ns = "http://madbutterfly.sourceforge.net/DTD/madbutterfly.dtd" | |
329 self._root.setAttribute("xmlns:ns0", ns) | |
330 scenes_node = self._doc.createElement("ns0:scenes") | |
331 node.appendChild(scenes_node) | |
332 self._scenes_node = scenes_node | |
333 pass | |
334 pass | |
335 | |
336 def _parse_all_layers(self): | |
337 root = self._root | |
338 layers = self._layers | |
339 | |
340 for child in root.childList(): | |
341 if child.name() != 'svg:g': | |
342 continue | |
343 | |
344 layer_group = child | |
345 layer = Layer(layer_group) | |
346 layer.idx = len(layers) | |
347 layers.append(layer) | |
348 self.parse_layer(layer.idx) | |
349 pass | |
350 pass | |
351 | |
352 def handle_doc_root(self, doc, root): | |
353 self._doc = doc | |
354 self._root = root | |
355 self._layers = [] | |
356 | |
357 self._start_monitor() # start MBScene_domview_monitor | |
358 self._init_metadata() | |
359 self._parse_all_layers() | |
360 pass | |
361 | |
362 def dumpattr(self, n): | |
363 s = "" | |
364 for a,v in n.attrib.items(): | |
365 s = s + ("%s=%s" % (a,v)) | |
366 pass | |
367 return s | |
368 | |
369 def dump(self, node, l=0): | |
370 print " " * l*2,"<", node.tag, self.dumpattr(node),">" | |
371 for n in node: | |
372 self.dump(n, l+1) | |
373 pass | |
374 print " " * l * 2,"/>" | |
375 pass | |
376 | |
377 ## \brief Create and add a ns0:scene node under ns0:scenes subtree. | |
378 # | |
379 def add_scene_node(self, start, end, | |
380 frame_type=TweenObject.TWEEN_TYPE_NORMAL, | |
381 ref=None): | |
382 type_names = ('normal', 'scale') | |
383 scenes_node = self._scenes_node | |
384 doc = self._doc | |
385 | |
386 scene_node = doc.createElement('ns0:scene') | |
387 self.chg_scene_node(scene_node, start=start) | |
388 if start != end: | |
389 self.chg_scene_node(scene_node, end=end) | |
390 pass | |
391 type_name = type_names[frame_type] | |
392 self.chg_scene_node(scene_node, tween_type=type_name) | |
393 if ref: | |
394 self.chg_scene_node(scene_node, ref=ref) | |
395 pass | |
396 | |
397 scenes_node.appendChild(scene_node) | |
398 | |
399 return scene_node | |
400 | |
401 ## \brief Change attributes of a scene node. | |
402 # | |
403 # This is here to monitor changes of scene node. | |
404 def chg_scene_node(self, scene_node, start=None, end=None, | |
405 tween_type=None, ref=None): | |
406 if start is not None: | |
407 scene_node.setAttribute('start', str(start)) | |
408 pass | |
409 if end is not None: | |
410 scene_node.setAttribute('end', str(end)) | |
411 pass | |
412 if tween_type is not None: | |
413 scene_node.setAttribute('type', tween_type) | |
414 pass | |
415 if ref is not None: | |
416 scene_node.setAttribute('ref', ref) | |
417 pass | |
418 pass | |
419 | |
420 def rm_scene_node(self, scene_node): | |
421 self._scenes_node.removeChild(scene_node) | |
422 pass | |
423 | |
424 def rm_scene_node_n_group(self, scene_node): | |
425 scene_group_id = scene_node.getAttribute('ref') | |
426 scene_group_node = self.get_node(scene_group_id) | |
427 scene_group_node.parent().removeChild(scene_group_node) | |
428 | |
429 self._scenes_node.removeChild(scene_node) | |
430 pass | |
431 | |
432 ## \brief Create and add a svg:g for a scene under a group for a layer. | |
433 # | |
434 def add_scene_group(self, layer_idx): | |
435 layer = self._layers[layer_idx] | |
436 doc = self._doc | |
437 | |
438 scene_group = doc.createElement('svg:g') | |
439 gid = self.new_id() | |
440 scene_group.setAttribute("id", gid) | |
441 scene_group.setAttribute("inkscape:groupmode", "layer") | |
442 | |
443 layer.group.appendChild(scene_group) | |
444 | |
445 return scene_group | |
446 | |
447 def parse_layer(self, layer_idx): | |
448 layer = self._layers[layer_idx] | |
449 layer_group = layer.group | |
450 | |
451 for child in layer_group.childList(): | |
452 if child.name() != 'svg:g': | |
453 continue | |
454 try: | |
455 child_id = child.getAttribute('id') | |
456 scene_node = self.get_scene(child_id) | |
457 except: | |
458 continue | |
459 | |
460 layer.scenes.append(scene_node) | |
461 pass | |
462 pass | |
463 | |
464 ## \brief Add/insert a layer at given position. | |
465 # | |
466 # \param layer_idx is the position in the layer list. | |
467 # | |
468 def insert_layer(self, layer_idx, layer_group): | |
469 layers = self._layers | |
470 | |
471 layer = Layer(layer_group) | |
472 if layer_idx >= len(layers): | |
473 layers.append(layer) | |
474 else: | |
475 layers.insert(layer_idx, layer) | |
476 for idx in range(layer_idx, len(layers)): | |
477 layers[idx].idx = idx | |
478 pass | |
479 pass | |
480 pass | |
481 | |
482 ## \brief Remove layer and associated scene nodes and scene groups. | |
483 # | |
484 def rm_layer(self, layer_idx): | |
485 layers = self._layers | |
486 | |
487 for layer in layers: | |
488 for scene_node in layer.scenes: | |
489 scene_group_id = scene_node.getAttribute('ref') | |
490 scene_group_node = self.get_node(scene_group_id) | |
491 scene_group_node.parent().removeChild(scene_group_node) | |
492 | |
493 scene_node.parent().removeChild(scene_node) | |
494 pass | |
495 pass | |
496 | |
497 del layers[layer_idx] | |
498 | |
499 for idx in range(layer_idx, len(layers)): | |
500 layers[idx].idx = idx | |
501 pass | |
502 pass | |
503 | |
504 def get_layer_num(self): | |
505 return len(self._layers) | |
506 | |
507 def find_layer_n_scene_of_node(self, node_id): | |
508 for layer_idx, layer in enumerate(self._layers): | |
509 for scene_node in layer.scenes: | |
510 scene_group_id = scene_node.getAttribute('ref') | |
511 if scene_group_id == node_id: | |
512 return layer_idx, scene_node | |
513 pass | |
514 pass | |
515 return -1, None | |
516 | |
517 def get_layer_group(self, layer_idx): | |
518 layer = self._layers[layer_idx] | |
519 return layer.group | |
520 | |
521 def get_all_scene_node_of_layer(self, layer_idx): | |
522 layer = self._layers[layer_idx] | |
523 return layer.scenes | |
524 | |
525 def get_layer_data(self, layer_idx): | |
526 layer = self._layers[layer_idx] | |
527 try: | |
528 data = layer.data | |
529 except: | |
530 return None | |
531 return data | |
532 | |
533 def set_layer_data(self, layer_idx, data): | |
534 layer = self._layers[layer_idx] | |
535 layer.data = data | |
536 pass | |
537 | |
538 def create_layer_dup_group(self, layer_idx): | |
539 layer = self._layers[layer_idx] | |
540 | |
541 dup_group = self._doc.createElement('svg:g') | |
542 gid = self.new_id() | |
543 dup_group.setAttribute('id', gid) | |
544 dup_group.setAttribute('inkscape:label', 'dup') | |
545 dup_group.setAttribute('sodipodi:insensitive', '1') | |
546 dup_group.setAttribute('style', '') | |
547 | |
548 layer.group.appendChild(dup_group) | |
549 | |
550 return dup_group | |
551 | |
552 def insert_frames(self, layer_idx, frame_idx, num): | |
553 layer = self._layers[layer_idx] | |
554 for scene_node in layer.scenes: | |
555 start, end, tween_type = self._parse_one_scene(scene_node) | |
556 if start >= frame_idx: | |
557 self.chg_scene_node(scene_node, start=(start + num)) | |
558 pass | |
559 if end >= frame_idx: | |
560 self.chg_scene_node(scene_node, end=(end + num)) | |
561 pass | |
562 pass | |
563 pass | |
564 | |
565 ## \brief Remove frames | |
566 # | |
567 # - Scenes covered by removing range were removed. | |
568 # - Scenes after removing range were shifted left. | |
569 # | |
570 def rm_frames(self, layer_idx, frame_idx, num): | |
571 layer = self._layers[layer_idx] | |
572 | |
573 last_rm = frame_idx + num - 1 # last removed frame | |
574 for scene_node in layer.scenes: | |
575 start, end, tween_type = \ | |
576 self._dom._parse_one_scene(scene_node) | |
577 | |
578 if end < frame_idx: | |
579 continue | |
580 | |
581 if start > last_rm: # this scene is at right side | |
582 self.chg_scene_node(scene_node, | |
583 start=(start - num), | |
584 end=(end - num)) | |
585 else: # this scene is covered by removing range | |
586 self.rm_scene_node_n_group(scene_node) | |
587 pass | |
588 pass | |
589 pass | |
590 | |
591 def get_max_frame(self): | |
592 return self._maxframe | |
593 pass | |
594 |