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