Mercurial > MadButterfly
comparison pyink/MBScene.py @ 1067:7b4e80ab671a openvg
merge from default branch
author | Thinker K.F. Li <thinker@codemud.net> |
---|---|
date | Wed, 01 Dec 2010 12:25:56 +0800 |
parents | 16c69756ef5d |
children | afa42d5836cc |
comparison
equal
deleted
inserted
replaced
630:bd18951b51d5 | 1067:7b4e80ab671a |
---|---|
1 #!/usr/bin/python | |
2 # -*- indent-tabs-mode: t; tab-width: 8; python-indent: 4; -*- | |
3 # vim: sw=4:ts=8:sts=4 | |
4 import pygtk | |
5 import gtk | |
6 import glib | |
7 from copy import deepcopy | |
8 from lxml import etree | |
9 import random | |
10 import traceback | |
11 import time | |
12 import pybInkscape | |
13 | |
14 # Please refer to | |
15 # http://www.assembla.com/wiki/show/MadButterfly/Inkscape_extention | |
16 # for the designed document. | |
17 | |
18 | |
19 # Algorithm: | |
20 # | |
21 # We will parse the first two level of the SVG DOM. collect a table of | |
22 # layer and scene. | |
23 # - 1. Collect the layer table which will be displayed as the first | |
24 # column of the grid. | |
25 # - 2. Get the maximum scene number. This will decide the size of the | |
26 # grid. | |
27 # - 3. When F6 is pressed, we will check if this scene has been | |
28 # defined. This can be done by scan all second level group and | |
29 # check if the current scene number is within the range specified | |
30 # by scene field. The function IsSceneDefined(scene) can be used | |
31 # for this purpose. | |
32 # - 4. If this is a new scene, we will append a new group which | |
33 # duplication the content of the last scene in the same | |
34 # group. The scene field will contain the number from the last | |
35 # scene number of the last scene to the current scenen | |
36 # number. For example, if the last scene is from 4-7 and the new | |
37 # scene is 10, we will set the scene field as "8-10". | |
38 # - 5. If this scene are filled screne, we will split the existing | |
39 # scene into two scenes with the same content. | |
40 # | |
41 | |
42 class Layer: | |
43 def __init__(self,node): | |
44 self.scenes = [] | |
45 self.node = node | |
46 self.nodes=[] | |
47 pass | |
48 pass | |
49 | |
50 class Scene: | |
51 def __init__(self, node, start,end): | |
52 self.node = node | |
53 self.start = int(start) | |
54 self.end = int(end) | |
55 pass | |
56 pass | |
57 | |
58 _scenes = '{http://madbutterfly.sourceforge.net/DTD/madbutterfly.dtd}scenes' | |
59 _scene = '{http://madbutterfly.sourceforge.net/DTD/madbutterfly.dtd}scene' | |
60 class LayerAttributeWatcher(pybInkscape.PYNodeObserver): | |
61 def __init__(self,ui): | |
62 self.ui = ui | |
63 def notifyChildAdded(self,node,child,prev): | |
64 pass | |
65 def notifyChildRemoved(self,node,child,prev): | |
66 pass | |
67 def notifyChildOrderChanged(self,node,child,prev): | |
68 pass | |
69 def notifyContentChanged(self,node,old_content,new_content): | |
70 pass | |
71 def notifyAttributeChanged(self,node, name, old_value, new_value): | |
72 self.ui.updateUI() | |
73 class LayerAddRemoveWatcher(pybInkscape.PYNodeObserver): | |
74 def __init__(self,ui): | |
75 self.ui = ui | |
76 def notifyChildAdded(self,node,child,prev): | |
77 self.ui.updateUI() | |
78 def notifyChildRemoved(self,node,child,prev): | |
79 self.ui.updateUI() | |
80 def notifyChildOrderChanged(self,node,child,prev): | |
81 self.ui.updateUI() | |
82 def notifyContentChanged(self,node,old_content,new_content): | |
83 self.ui.updateUI() | |
84 def notifyAttributeChanged(self,node, name, old_value, new_value): | |
85 self.ui.updateUI() | |
86 class MBScene(): | |
87 def __init__(self,desktop,win): | |
88 self.desktop = desktop | |
89 self.window = win | |
90 self.layers = [] | |
91 self.layers.append(Layer(None)) | |
92 self.scenemap = None | |
93 self.top = None | |
94 self.last_update = None | |
95 pass | |
96 | |
97 def confirm(self,msg): | |
98 vbox = gtk.VBox() | |
99 vbox.pack_start(gtk.Label(msg)) | |
100 self.button = gtk.Button('OK') | |
101 vbox.pack_start(self.button) | |
102 self.button.connect("clicked", self.onQuit) | |
103 self.window.add(vbox) | |
104 pass | |
105 | |
106 def dumpattr(self,n): | |
107 s = "" | |
108 for a,v in n.attrib.items(): | |
109 s = s + ("%s=%s" % (a,v)) | |
110 pass | |
111 return s | |
112 | |
113 def dump(self,node,l=0): | |
114 print " " * l*2,"<", node.tag, self.dumpattr(node),">" | |
115 for n in node: | |
116 self.dump(n,l+1) | |
117 pass | |
118 print " " * l * 2,"/>" | |
119 pass | |
120 | |
121 def parseMetadata(self,node): | |
122 self.current = 1 | |
123 for n in node.childList(): | |
124 if n.repr.name() == 'ns0:scenes': | |
125 self.scenemap={} | |
126 try: | |
127 cur = int(n.repr.attribute("current")) | |
128 except: | |
129 cur = 1 | |
130 self.current = cur | |
131 | |
132 for s in n.childList(): | |
133 if s.repr.name() == 'ns0:scene': | |
134 try: | |
135 start = int(s.repr.attribute("start")) | |
136 except: | |
137 traceback.print_exc() | |
138 continue | |
139 try: | |
140 end = s.repr.attribute("end") | |
141 if end == None: | |
142 end = start | |
143 pass | |
144 except: | |
145 end = start | |
146 pass | |
147 link = s.repr.attribute("ref") | |
148 self.scenemap[link] = [int(start),int(end)] | |
149 print "scene %d to %d" % (self.scenemap[link][0], | |
150 self.scenemap[link][1]) | |
151 if cur >= start and cur <= end: | |
152 self.currentscene = link | |
153 pass | |
154 pass | |
155 pass | |
156 pass | |
157 pass | |
158 pass | |
159 if self.scenemap==None: | |
160 self.desktop.doc().root().repr.setAttribute("xmlns:ns0","http://madbutterfly.sourceforge.net/DTD/madbutterfly.dtd",True) | |
161 scenes = self.desktop.doc().rdoc.createElement("ns0:scenes") | |
162 node.repr.appendChild(scenes) | |
163 def update(self): | |
164 doc = self.desktop.doc().root() | |
165 rdoc = self.desktop.doc().rdoc | |
166 for node in doc.childList(): | |
167 if node.repr.name() == 'svg:metadata': | |
168 for t in node.childList(): | |
169 if t.repr.name() == "ns0:scenes": | |
170 node.repr.removeChild(t.repr) | |
171 ns = rdoc.createElement("ns0:scenes") | |
172 node.repr.appendChild(ns) | |
173 for layer in range(0,len(self._framelines)): | |
174 lobj = self._framelines[layer] | |
175 lobj.addScenes(rdoc,ns) | |
176 | |
177 | |
178 def parseScene(self): | |
179 """ | |
180 In this function, we will collect all items for the current | |
181 scene and then relocate them back to the appropriate scene | |
182 object. | |
183 """ | |
184 self.layers = [] | |
185 self.scenemap = None | |
186 doc = self.desktop.doc().root() | |
187 | |
188 #obs = pybInkscape.PYNodeObserver() | |
189 obs = LayerAddRemoveWatcher(self) | |
190 doc.repr.addObserver(obs) | |
191 for node in doc.childList(): | |
192 if node.repr.name() == 'svg:metadata': | |
193 self.parseMetadata(node) | |
194 pass | |
195 elif node.repr.name() == 'svg:g': | |
196 oldscene = None | |
197 obs = LayerAttributeWatcher(self) | |
198 node.repr.addObserver(obs) | |
199 lyobj = Layer(node) | |
200 self.layers.append(lyobj) | |
201 lyobj.current_scene = [] | |
202 for scene in node.childList(): | |
203 if scene.repr.name() == 'svg:g': | |
204 try: | |
205 scmap = self.scenemap[scene.getId()] | |
206 if scmap == None: | |
207 lyobj.current_scene.append(scene) | |
208 continue | |
209 except: | |
210 lyobj.current_scene.append(scene) | |
211 continue | |
212 | |
213 lyobj.scenes.append(Scene(scene,scmap[0],scmap[1])) | |
214 pass | |
215 else: | |
216 lyobj.current_scene.append(scene) | |
217 pass | |
218 pass | |
219 pass | |
220 pass | |
221 | |
222 | |
223 self.collectID() | |
224 self.dumpID() | |
225 pass | |
226 | |
227 def collectID(self): | |
228 self.ID = {} | |
229 root = self.desktop.doc().root() | |
230 for n in root.childList(): | |
231 self.collectID_recursive(n) | |
232 pass | |
233 pass | |
234 | |
235 def collectID_recursive(self,node): | |
236 self.ID[node.getId()] = 1 | |
237 for n in node.childList(): | |
238 self.collectID_recursive(n) | |
239 pass | |
240 pass | |
241 | |
242 def newID(self): | |
243 while True: | |
244 n = 's%d' % int(random.random()*10000) | |
245 #print "try %s" % n | |
246 if self.ID.has_key(n) == False: | |
247 return n | |
248 pass | |
249 pass | |
250 | |
251 def dumpID(self): | |
252 for a,v in self.ID.items(): | |
253 pass | |
254 pass | |
255 | |
256 def getLayer(self, layer): | |
257 for l in self.layers: | |
258 if l.node.getId() == layer: | |
259 return l | |
260 pass | |
261 return None | |
262 | |
263 | |
264 def insertKeyScene(self): | |
265 """ | |
266 Insert a new key scene into the stage. If the nth is always a | |
267 key scene, we will return without changing anything. If the | |
268 nth is a filled scene, we will break the original scene into | |
269 two parts. If the nth is out of any scene, we will append a | |
270 new scene. | |
271 | |
272 """ | |
273 x = self.last_frame | |
274 y = self.last_line | |
275 rdoc = self.desktop.doc().rdoc | |
276 ns = rdoc.createElement("svg:g") | |
277 txt = rdoc.createElement("svg:rect") | |
278 txt.setAttribute("x","0",True) | |
279 txt.setAttribute("y","0",True) | |
280 txt.setAttribute("width","100",True) | |
281 txt.setAttribute("height","100",True) | |
282 txt.setAttribute("style","fill:#ff00",True) | |
283 ns.appendChild(txt) | |
284 gid = self.last_line.node.label()+self.newID() | |
285 self.ID[gid]=1 | |
286 ns.setAttribute("id",gid,True) | |
287 ns.setAttribute("inkscape:groupmode","layer",True) | |
288 self.last_line.node.repr.appendChild(ns) | |
289 print 'Add key ', x | |
290 self.last_line.add_keyframe(x,ns) | |
291 self.update() | |
292 self.last_line.update() | |
293 | |
294 | |
295 def removeKeyScene(self): | |
296 nth = self.last_frame | |
297 y = self.last_line | |
298 rdoc = self.desktop.doc().rdoc | |
299 i = 0 | |
300 layer = self.last_line | |
301 while i < len(layer._keys): | |
302 s = layer._keys[i] | |
303 print "nth:%d idx %d" % (nth,s.idx) | |
304 if nth > s.idx: | |
305 if i == len(layer._keys)-1: | |
306 return | |
307 if nth == s.idx: | |
308 if s.left_tween: | |
309 # This is left tween, we move the keyframe one frame ahead | |
310 if s.idx == layer._keys[i-1].idx: | |
311 layer._keys[i].ref.parent().removeChild(layer._keys[i].ref) | |
312 self.last_line.rm_keyframe(nth) | |
313 self.last_line.rm_keyframe(nth-1) | |
314 else: | |
315 s.idx = s.idx-1 | |
316 else: | |
317 layer._keys[i].ref.parent().removeChild(layer._keys[i].ref) | |
318 if s.right_tween: | |
319 self.last_line.rm_keyframe(layer._keys[i+1].idx) | |
320 self.last_line.rm_keyframe(nth) | |
321 else: | |
322 self.last_line.rm_keyframe(nth) | |
323 | |
324 self.update() | |
325 self.last_line._draw_all_frames() | |
326 self.last_line.update() | |
327 return | |
328 i = i + 1 | |
329 def extendScene(self): | |
330 nth = self.last_frame | |
331 layer = self.last_line | |
332 i = 0 | |
333 while i < len(layer._keys): | |
334 s = layer._keys[i] | |
335 if s.right_tween: | |
336 if nth > s.idx: | |
337 if nth <= layer._keys[i+1].idx: | |
338 return | |
339 try: | |
340 if nth <= layer._keys[i+2].idx: | |
341 layer._keys[i+1].idx = nth | |
342 layer.draw_all_frames() | |
343 self.update() | |
344 self.setCurrentScene(nth) | |
345 self.last_line.update() | |
346 return | |
347 else: | |
348 # We may in the next scene | |
349 i = i + 2 | |
350 pass | |
351 except: | |
352 # This is the last keyframe, extend the keyframe by | |
353 # relocate the location of the keyframe | |
354 layer._keys[i+1].idx = nth | |
355 layer._draw_all_frames() | |
356 self.update() | |
357 self.last_line.update() | |
358 self.setCurrentScene(nth) | |
359 return | |
360 else: | |
361 # We are in the front of all keyframes | |
362 return | |
363 else: | |
364 # This is a single keyframe | |
365 if nth < s.idx: | |
366 return | |
367 if nth == s.idx: | |
368 return | |
369 try: | |
370 if nth < layer._keys[i+1].idx: | |
371 # We are after a single keyframe and no scene is | |
372 # available here. Create a new tween here | |
373 idx = layer._keys[i].idx | |
374 layer.add_keyframe(nth,layer._keys[i].ref) | |
375 layer.tween(idx) | |
376 layer._draw_all_frames() | |
377 self.update() | |
378 self.setCurrentScene(nth) | |
379 self.last_line.update() | |
380 return | |
381 else: | |
382 # We may in the next scene | |
383 i = i + 1 | |
384 pass | |
385 pass | |
386 except: | |
387 # This is the last scene, create a new one | |
388 idx = layer._keys[i].idx | |
389 layer.add_keyframe(nth,layer._keys[i].ref) | |
390 layer.tween(idx) | |
391 layer._draw_all_frames() | |
392 self.update() | |
393 self.setCurrentScene(nth) | |
394 self.last_line.update() | |
395 return | |
396 pass | |
397 pass | |
398 pass | |
399 | |
400 | |
401 def setCurrentScene(self,nth): | |
402 self.current = nth | |
403 for layer in self._framelines: | |
404 i=0 | |
405 while i < len(layer._keys): | |
406 s = layer._keys[i] | |
407 print s.ref.attribute("id"),s.idx,s.left_tween,s.right_tween | |
408 if s.right_tween is False: | |
409 if nth == s.idx+1: | |
410 s.ref.setAttribute("style","",True) | |
411 else: | |
412 s.ref.setAttribute("style","display:none",True) | |
413 i = i + 1 | |
414 continue | |
415 | |
416 if nth >= (s.idx+1) and nth <= (layer._keys[i+1].idx+1): | |
417 s.ref.setAttribute("style","",True) | |
418 else: | |
419 s.ref.setAttribute("style","display:none",True) | |
420 i = i + 2 | |
421 pass | |
422 pass | |
423 pass | |
424 | |
425 | |
426 def newCell(self,file): | |
427 img = gtk.Image() | |
428 img.set_from_file(file) | |
429 btn = gtk.EventBox() | |
430 btn.add(img) | |
431 btn.connect("button_press_event", self.cellSelect) | |
432 btn.modify_bg(gtk.STATE_NORMAL, btn.get_colormap().alloc_color("gray")) | |
433 return btn | |
434 | |
435 def onCellClick(self,line,frame,but): | |
436 self.last_line = line | |
437 self.last_frame = frame | |
438 self.last_line.active_frame(frame) | |
439 self.doEditScene(frame) | |
440 | |
441 | |
442 def _remove_active_frame(self,widget,event): | |
443 """ | |
444 Hide all hover frames. This is a hack. We should use the lost focus event | |
445 instead in the future to reduce the overhead. | |
446 """ | |
447 for f in self._framelines: | |
448 if f != widget: | |
449 f.hide_hover() | |
450 | |
451 def _create_framelines(self): | |
452 import frameline | |
453 self.scrollwin = gtk.ScrolledWindow() | |
454 self.scrollwin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) | |
455 self.scrollwin.set_size_request(-1,150) | |
456 | |
457 nframes = 100 | |
458 | |
459 vbox = gtk.VBox() | |
460 vbox.show() | |
461 self.scrollwin.add_with_viewport(vbox) | |
462 | |
463 ruler = frameline.frameruler(nframes) | |
464 ruler.set_size_request(nframes * 10, 20) | |
465 ruler.show() | |
466 hbox = gtk.HBox() | |
467 label=gtk.Label('') | |
468 label.set_size_request(100,0) | |
469 hbox.pack_start(label,expand=False,fill=True) | |
470 hbox.pack_start(ruler) | |
471 vbox.pack_start(hbox, False) | |
472 | |
473 # | |
474 # Add a frameline for each layer | |
475 # | |
476 self._framelines = [] | |
477 for i in range(len(self.layers)-1,-1,-1): | |
478 line = frameline.frameline(nframes) | |
479 hbox = gtk.HBox() | |
480 label = gtk.Label(self.layers[i].node.label()) | |
481 label.set_size_request(100,0) | |
482 hbox.pack_start(label,expand=False,fill=True) | |
483 hbox.pack_start(line) | |
484 line.set_size_request(nframes * 10, 20) | |
485 vbox.pack_start(hbox, False) | |
486 line.label = label | |
487 self._framelines.append(line) | |
488 line.connect(line.FRAME_BUT_PRESS, self.onCellClick) | |
489 line.nLayer = i | |
490 line.node = self.layers[i].node | |
491 line.layer = self.layers[i] | |
492 line.connect('motion-notify-event', self._remove_active_frame) | |
493 pass | |
494 pass | |
495 | |
496 ## \brief Update conetent of frameliens according layers. | |
497 # | |
498 def _update_framelines(self): | |
499 for frameline in self._framelines: | |
500 layer = frameline.layer | |
501 if frameline.node.label()==None: | |
502 frameline.label.set_text('???') | |
503 else: | |
504 frameline.label.set_text(frameline.node.label()) | |
505 for scene in layer.scenes: | |
506 frameline.add_keyframe(scene.start-1,scene.node.repr) | |
507 if scene.start != scene.end: | |
508 frameline.add_keyframe(scene.end-1,scene.node.repr) | |
509 frameline.tween(scene.start-1) | |
510 pass | |
511 pass | |
512 pass | |
513 | |
514 def cellSelect(self, cell, data): | |
515 if self.last_cell: | |
516 color = self.last_cell.get_colormap().alloc_color("gray") | |
517 self.last_cell.modify_bg(gtk.STATE_NORMAL, color) | |
518 pass | |
519 | |
520 self.last_cell = cell | |
521 color = cell.get_colormap().alloc_color("green") | |
522 cell.modify_bg(gtk.STATE_NORMAL, color) | |
523 pass | |
524 | |
525 def doEditScene(self,w): | |
526 self.setCurrentScene(self.last_frame+1) | |
527 pass | |
528 | |
529 def doInsertKeyScene(self,w): | |
530 self.insertKeyScene() | |
531 # self.grid.show_all() | |
532 return | |
533 | |
534 def doRemoveScene(self,w): | |
535 self.removeKeyScene() | |
536 return | |
537 | |
538 | |
539 def doExtendScene(self,w): | |
540 self.extendScene() | |
541 #self.grid.show_all() | |
542 pass | |
543 | |
544 def addButtons(self,hbox): | |
545 #btn = gtk.Button('Edit') | |
546 #btn.connect('clicked', self.doEditScene) | |
547 #hbox.pack_start(btn,expand=False,fill=False) | |
548 btn = gtk.Button('Insert Key') | |
549 btn.connect('clicked',self.doInsertKeyScene) | |
550 hbox.pack_start(btn,expand=False,fill=False) | |
551 btn=gtk.Button('Remove Key') | |
552 btn.connect('clicked', self.doRemoveScene) | |
553 hbox.pack_start(btn,expand=False,fill=False) | |
554 btn=gtk.Button('Extend scene') | |
555 btn.connect('clicked', self.doExtendScene) | |
556 hbox.pack_start(btn,expand=False,fill=False) | |
557 pass | |
558 | |
559 def onQuit(self, event): | |
560 self.OK = False | |
561 gtk.main_quit() | |
562 pass | |
563 | |
564 def onOK(self,event): | |
565 self.OK = True | |
566 gtk.main_quit() | |
567 pass | |
568 | |
569 def updateUI(self): | |
570 if self.last_update!= None: | |
571 glib.source_remove(self.last_update) | |
572 self.last_update = glib.timeout_add(300,self.show) | |
573 def show(self): | |
574 self.OK = True | |
575 self.parseScene() | |
576 self._create_framelines() | |
577 self._update_framelines() | |
578 if self.top == None: | |
579 self.top = gtk.VBox(False,0) | |
580 self.desktop.getToplevel().child.child.pack_end(self.top,expand=False) | |
581 else: | |
582 self.top.remove(self.startWindow) | |
583 vbox = gtk.VBox(False,0) | |
584 self.startWindow = vbox | |
585 self.top.pack_start(vbox,expand=False) | |
586 vbox.pack_start(self.scrollwin,expand=False) | |
587 hbox=gtk.HBox(False,0) | |
588 self.addButtons(hbox) | |
589 vbox.pack_start(hbox,expand=False) | |
590 | |
591 # self.window = gtk.Window(gtk.WINDOW_TOPLEVEL) | |
592 # self.window.connect("destroy", gtk.main_quit) | |
593 # self.window.set_position(gtk.WIN_POS_MOUSE) | |
594 | |
595 self.top.show_all() | |
596 self.last_update = None | |
597 return False | |
598 pass |