Mercurial > MadButterfly
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pyink/MBScene.py Wed Dec 01 12:25:56 2010 +0800 @@ -0,0 +1,598 @@ +#!/usr/bin/python +# -*- indent-tabs-mode: t; tab-width: 8; python-indent: 4; -*- +# vim: sw=4:ts=8:sts=4 +import pygtk +import gtk +import glib +from copy import deepcopy +from lxml import etree +import random +import traceback +import time +import pybInkscape + +# Please refer to +# http://www.assembla.com/wiki/show/MadButterfly/Inkscape_extention +# for the designed document. + + +# Algorithm: +# +# We will parse the first two level of the SVG DOM. collect a table of +# layer and scene. +# - 1. Collect the layer table which will be displayed as the first +# column of the grid. +# - 2. Get the maximum scene number. This will decide the size of the +# grid. +# - 3. When F6 is pressed, we will check if this scene has been +# defined. This can be done by scan all second level group and +# check if the current scene number is within the range specified +# by scene field. The function IsSceneDefined(scene) can be used +# for this purpose. +# - 4. If this is a new scene, we will append a new group which +# duplication the content of the last scene in the same +# group. The scene field will contain the number from the last +# scene number of the last scene to the current scenen +# number. For example, if the last scene is from 4-7 and the new +# scene is 10, we will set the scene field as "8-10". +# - 5. If this scene are filled screne, we will split the existing +# scene into two scenes with the same content. +# + +class Layer: + def __init__(self,node): + self.scenes = [] + self.node = node + self.nodes=[] + pass + pass + +class Scene: + def __init__(self, node, start,end): + self.node = node + self.start = int(start) + self.end = int(end) + pass + pass + +_scenes = '{http://madbutterfly.sourceforge.net/DTD/madbutterfly.dtd}scenes' +_scene = '{http://madbutterfly.sourceforge.net/DTD/madbutterfly.dtd}scene' +class LayerAttributeWatcher(pybInkscape.PYNodeObserver): + def __init__(self,ui): + self.ui = ui + def notifyChildAdded(self,node,child,prev): + pass + def notifyChildRemoved(self,node,child,prev): + pass + def notifyChildOrderChanged(self,node,child,prev): + pass + def notifyContentChanged(self,node,old_content,new_content): + pass + def notifyAttributeChanged(self,node, name, old_value, new_value): + self.ui.updateUI() +class LayerAddRemoveWatcher(pybInkscape.PYNodeObserver): + def __init__(self,ui): + self.ui = ui + def notifyChildAdded(self,node,child,prev): + self.ui.updateUI() + def notifyChildRemoved(self,node,child,prev): + self.ui.updateUI() + def notifyChildOrderChanged(self,node,child,prev): + self.ui.updateUI() + def notifyContentChanged(self,node,old_content,new_content): + self.ui.updateUI() + def notifyAttributeChanged(self,node, name, old_value, new_value): + self.ui.updateUI() +class MBScene(): + def __init__(self,desktop,win): + self.desktop = desktop + self.window = win + self.layers = [] + self.layers.append(Layer(None)) + self.scenemap = None + self.top = None + self.last_update = None + pass + + def confirm(self,msg): + vbox = gtk.VBox() + vbox.pack_start(gtk.Label(msg)) + self.button = gtk.Button('OK') + vbox.pack_start(self.button) + self.button.connect("clicked", self.onQuit) + self.window.add(vbox) + pass + + def dumpattr(self,n): + s = "" + for a,v in n.attrib.items(): + s = s + ("%s=%s" % (a,v)) + pass + return s + + def dump(self,node,l=0): + print " " * l*2,"<", node.tag, self.dumpattr(node),">" + for n in node: + self.dump(n,l+1) + pass + print " " * l * 2,"/>" + pass + + def parseMetadata(self,node): + self.current = 1 + for n in node.childList(): + if n.repr.name() == 'ns0:scenes': + self.scenemap={} + try: + cur = int(n.repr.attribute("current")) + except: + cur = 1 + self.current = cur + + for s in n.childList(): + if s.repr.name() == 'ns0:scene': + try: + start = int(s.repr.attribute("start")) + except: + traceback.print_exc() + continue + try: + end = s.repr.attribute("end") + if end == None: + end = start + pass + except: + end = start + pass + link = s.repr.attribute("ref") + self.scenemap[link] = [int(start),int(end)] + print "scene %d to %d" % (self.scenemap[link][0], + self.scenemap[link][1]) + if cur >= start and cur <= end: + self.currentscene = link + pass + pass + pass + pass + pass + pass + if self.scenemap==None: + self.desktop.doc().root().repr.setAttribute("xmlns:ns0","http://madbutterfly.sourceforge.net/DTD/madbutterfly.dtd",True) + scenes = self.desktop.doc().rdoc.createElement("ns0:scenes") + node.repr.appendChild(scenes) + def update(self): + doc = self.desktop.doc().root() + rdoc = self.desktop.doc().rdoc + for node in doc.childList(): + if node.repr.name() == 'svg:metadata': + for t in node.childList(): + if t.repr.name() == "ns0:scenes": + node.repr.removeChild(t.repr) + ns = rdoc.createElement("ns0:scenes") + node.repr.appendChild(ns) + for layer in range(0,len(self._framelines)): + lobj = self._framelines[layer] + lobj.addScenes(rdoc,ns) + + + def parseScene(self): + """ + In this function, we will collect all items for the current + scene and then relocate them back to the appropriate scene + object. + """ + self.layers = [] + self.scenemap = None + doc = self.desktop.doc().root() + + #obs = pybInkscape.PYNodeObserver() + obs = LayerAddRemoveWatcher(self) + doc.repr.addObserver(obs) + for node in doc.childList(): + if node.repr.name() == 'svg:metadata': + self.parseMetadata(node) + pass + elif node.repr.name() == 'svg:g': + oldscene = None + obs = LayerAttributeWatcher(self) + node.repr.addObserver(obs) + lyobj = Layer(node) + self.layers.append(lyobj) + lyobj.current_scene = [] + for scene in node.childList(): + if scene.repr.name() == 'svg:g': + try: + scmap = self.scenemap[scene.getId()] + if scmap == None: + lyobj.current_scene.append(scene) + continue + except: + lyobj.current_scene.append(scene) + continue + + lyobj.scenes.append(Scene(scene,scmap[0],scmap[1])) + pass + else: + lyobj.current_scene.append(scene) + pass + pass + pass + pass + + + self.collectID() + self.dumpID() + pass + + def collectID(self): + self.ID = {} + root = self.desktop.doc().root() + for n in root.childList(): + self.collectID_recursive(n) + pass + pass + + def collectID_recursive(self,node): + self.ID[node.getId()] = 1 + for n in node.childList(): + self.collectID_recursive(n) + pass + pass + + def newID(self): + while True: + n = 's%d' % int(random.random()*10000) + #print "try %s" % n + if self.ID.has_key(n) == False: + return n + pass + pass + + def dumpID(self): + for a,v in self.ID.items(): + pass + pass + + def getLayer(self, layer): + for l in self.layers: + if l.node.getId() == layer: + return l + pass + return None + + + def insertKeyScene(self): + """ + Insert a new key scene into the stage. If the nth is always a + key scene, we will return without changing anything. If the + nth is a filled scene, we will break the original scene into + two parts. If the nth is out of any scene, we will append a + new scene. + + """ + x = self.last_frame + y = self.last_line + rdoc = self.desktop.doc().rdoc + ns = rdoc.createElement("svg:g") + txt = rdoc.createElement("svg:rect") + txt.setAttribute("x","0",True) + txt.setAttribute("y","0",True) + txt.setAttribute("width","100",True) + txt.setAttribute("height","100",True) + txt.setAttribute("style","fill:#ff00",True) + ns.appendChild(txt) + gid = self.last_line.node.label()+self.newID() + self.ID[gid]=1 + ns.setAttribute("id",gid,True) + ns.setAttribute("inkscape:groupmode","layer",True) + self.last_line.node.repr.appendChild(ns) + print 'Add key ', x + self.last_line.add_keyframe(x,ns) + self.update() + self.last_line.update() + + + def removeKeyScene(self): + nth = self.last_frame + y = self.last_line + rdoc = self.desktop.doc().rdoc + i = 0 + layer = self.last_line + while i < len(layer._keys): + s = layer._keys[i] + print "nth:%d idx %d" % (nth,s.idx) + if nth > s.idx: + if i == len(layer._keys)-1: + return + if nth == s.idx: + if s.left_tween: + # This is left tween, we move the keyframe one frame ahead + if s.idx == layer._keys[i-1].idx: + layer._keys[i].ref.parent().removeChild(layer._keys[i].ref) + self.last_line.rm_keyframe(nth) + self.last_line.rm_keyframe(nth-1) + else: + s.idx = s.idx-1 + else: + layer._keys[i].ref.parent().removeChild(layer._keys[i].ref) + if s.right_tween: + self.last_line.rm_keyframe(layer._keys[i+1].idx) + self.last_line.rm_keyframe(nth) + else: + self.last_line.rm_keyframe(nth) + + self.update() + self.last_line._draw_all_frames() + self.last_line.update() + return + i = i + 1 + def extendScene(self): + nth = self.last_frame + layer = self.last_line + i = 0 + while i < len(layer._keys): + s = layer._keys[i] + if s.right_tween: + if nth > s.idx: + if nth <= layer._keys[i+1].idx: + return + try: + if nth <= layer._keys[i+2].idx: + layer._keys[i+1].idx = nth + layer.draw_all_frames() + self.update() + self.setCurrentScene(nth) + self.last_line.update() + return + else: + # We may in the next scene + i = i + 2 + pass + except: + # This is the last keyframe, extend the keyframe by + # relocate the location of the keyframe + layer._keys[i+1].idx = nth + layer._draw_all_frames() + self.update() + self.last_line.update() + self.setCurrentScene(nth) + return + else: + # We are in the front of all keyframes + return + else: + # This is a single keyframe + if nth < s.idx: + return + if nth == s.idx: + return + try: + if nth < layer._keys[i+1].idx: + # We are after a single keyframe and no scene is + # available here. Create a new tween here + idx = layer._keys[i].idx + layer.add_keyframe(nth,layer._keys[i].ref) + layer.tween(idx) + layer._draw_all_frames() + self.update() + self.setCurrentScene(nth) + self.last_line.update() + return + else: + # We may in the next scene + i = i + 1 + pass + pass + except: + # This is the last scene, create a new one + idx = layer._keys[i].idx + layer.add_keyframe(nth,layer._keys[i].ref) + layer.tween(idx) + layer._draw_all_frames() + self.update() + self.setCurrentScene(nth) + self.last_line.update() + return + pass + pass + pass + + + def setCurrentScene(self,nth): + self.current = nth + for layer in self._framelines: + i=0 + while i < len(layer._keys): + s = layer._keys[i] + print s.ref.attribute("id"),s.idx,s.left_tween,s.right_tween + if s.right_tween is False: + if nth == s.idx+1: + s.ref.setAttribute("style","",True) + else: + s.ref.setAttribute("style","display:none",True) + i = i + 1 + continue + + if nth >= (s.idx+1) and nth <= (layer._keys[i+1].idx+1): + s.ref.setAttribute("style","",True) + else: + s.ref.setAttribute("style","display:none",True) + i = i + 2 + pass + pass + pass + + + def newCell(self,file): + img = gtk.Image() + img.set_from_file(file) + btn = gtk.EventBox() + btn.add(img) + btn.connect("button_press_event", self.cellSelect) + btn.modify_bg(gtk.STATE_NORMAL, btn.get_colormap().alloc_color("gray")) + return btn + + def onCellClick(self,line,frame,but): + self.last_line = line + self.last_frame = frame + self.last_line.active_frame(frame) + self.doEditScene(frame) + + + def _remove_active_frame(self,widget,event): + """ + Hide all hover frames. This is a hack. We should use the lost focus event + instead in the future to reduce the overhead. + """ + for f in self._framelines: + if f != widget: + f.hide_hover() + + def _create_framelines(self): + import frameline + self.scrollwin = gtk.ScrolledWindow() + self.scrollwin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) + self.scrollwin.set_size_request(-1,150) + + nframes = 100 + + vbox = gtk.VBox() + vbox.show() + self.scrollwin.add_with_viewport(vbox) + + ruler = frameline.frameruler(nframes) + ruler.set_size_request(nframes * 10, 20) + ruler.show() + hbox = gtk.HBox() + label=gtk.Label('') + label.set_size_request(100,0) + hbox.pack_start(label,expand=False,fill=True) + hbox.pack_start(ruler) + vbox.pack_start(hbox, False) + + # + # Add a frameline for each layer + # + self._framelines = [] + for i in range(len(self.layers)-1,-1,-1): + line = frameline.frameline(nframes) + hbox = gtk.HBox() + label = gtk.Label(self.layers[i].node.label()) + label.set_size_request(100,0) + hbox.pack_start(label,expand=False,fill=True) + hbox.pack_start(line) + line.set_size_request(nframes * 10, 20) + vbox.pack_start(hbox, False) + line.label = label + self._framelines.append(line) + line.connect(line.FRAME_BUT_PRESS, self.onCellClick) + line.nLayer = i + line.node = self.layers[i].node + line.layer = self.layers[i] + line.connect('motion-notify-event', self._remove_active_frame) + pass + pass + + ## \brief Update conetent of frameliens according layers. + # + def _update_framelines(self): + for frameline in self._framelines: + layer = frameline.layer + if frameline.node.label()==None: + frameline.label.set_text('???') + else: + frameline.label.set_text(frameline.node.label()) + for scene in layer.scenes: + frameline.add_keyframe(scene.start-1,scene.node.repr) + if scene.start != scene.end: + frameline.add_keyframe(scene.end-1,scene.node.repr) + frameline.tween(scene.start-1) + pass + pass + pass + + def cellSelect(self, cell, data): + if self.last_cell: + color = self.last_cell.get_colormap().alloc_color("gray") + self.last_cell.modify_bg(gtk.STATE_NORMAL, color) + pass + + self.last_cell = cell + color = cell.get_colormap().alloc_color("green") + cell.modify_bg(gtk.STATE_NORMAL, color) + pass + + def doEditScene(self,w): + self.setCurrentScene(self.last_frame+1) + pass + + def doInsertKeyScene(self,w): + self.insertKeyScene() + # self.grid.show_all() + return + + def doRemoveScene(self,w): + self.removeKeyScene() + return + + + def doExtendScene(self,w): + self.extendScene() + #self.grid.show_all() + pass + + def addButtons(self,hbox): + #btn = gtk.Button('Edit') + #btn.connect('clicked', self.doEditScene) + #hbox.pack_start(btn,expand=False,fill=False) + btn = gtk.Button('Insert Key') + btn.connect('clicked',self.doInsertKeyScene) + hbox.pack_start(btn,expand=False,fill=False) + btn=gtk.Button('Remove Key') + btn.connect('clicked', self.doRemoveScene) + hbox.pack_start(btn,expand=False,fill=False) + btn=gtk.Button('Extend scene') + btn.connect('clicked', self.doExtendScene) + hbox.pack_start(btn,expand=False,fill=False) + pass + + def onQuit(self, event): + self.OK = False + gtk.main_quit() + pass + + def onOK(self,event): + self.OK = True + gtk.main_quit() + pass + + def updateUI(self): + if self.last_update!= None: + glib.source_remove(self.last_update) + self.last_update = glib.timeout_add(300,self.show) + def show(self): + self.OK = True + self.parseScene() + self._create_framelines() + self._update_framelines() + if self.top == None: + self.top = gtk.VBox(False,0) + self.desktop.getToplevel().child.child.pack_end(self.top,expand=False) + else: + self.top.remove(self.startWindow) + vbox = gtk.VBox(False,0) + self.startWindow = vbox + self.top.pack_start(vbox,expand=False) + vbox.pack_start(self.scrollwin,expand=False) + hbox=gtk.HBox(False,0) + self.addButtons(hbox) + vbox.pack_start(hbox,expand=False) + + # self.window = gtk.Window(gtk.WINDOW_TOPLEVEL) + # self.window.connect("destroy", gtk.main_quit) + # self.window.set_position(gtk.WIN_POS_MOUSE) + + self.top.show_all() + self.last_update = None + return False + pass