From d52b03d69a916117cc5102d508f2ec3b8a6d765c Mon Sep 17 00:00:00 2001 From: Mark Mine Date: Tue, 17 Oct 2000 18:39:44 +0000 Subject: [PATCH] *** empty log message *** --- direct/src/tkpanels/AnimPanel.py | 345 ++++++++++++++++++++ direct/src/tkpanels/FSMInspector.py | 469 ++++++++++++++++++++++++++++ 2 files changed, 814 insertions(+) create mode 100644 direct/src/tkpanels/AnimPanel.py create mode 100644 direct/src/tkpanels/FSMInspector.py diff --git a/direct/src/tkpanels/AnimPanel.py b/direct/src/tkpanels/AnimPanel.py new file mode 100644 index 0000000000..1634b14554 --- /dev/null +++ b/direct/src/tkpanels/AnimPanel.py @@ -0,0 +1,345 @@ +"DIRECT Animation Control Panel" + +# Import Tkinter, Pmw, and the floater code from this directory tree. +from Tkinter import * +from tkSimpleDialog import askfloat +import Pmw +import string +import math + +FRAMES = 0 +SECONDS = 1 + +class AnimPanel(Pmw.MegaToplevel): + def __init__(self, parent = None, **kw): + + INITOPT = Pmw.INITOPT + optiondefs = ( + ('title', 'Anim Panel', None), + ('actorList', (), None), + ('Actor_label_width', 12, None), + ) + self.defineoptions(kw, optiondefs) + + # Initialize the superclass + Pmw.MegaToplevel.__init__(self, parent) + + # Handle to the toplevels hull + hull = self.component('hull') + + # A handy little help balloon + balloon = self.balloon = Pmw.Balloon() + # Start with balloon help disabled + self.balloon.configure(state = 'none') + + menuFrame = Frame(hull, relief = GROOVE, bd = 2) + + menuBar = Pmw.MenuBar(menuFrame, hotkeys = 1, balloon = balloon) + menuBar.pack(side = LEFT, expand = 1, fill = X) + menuBar.addmenu('AnimPanel', 'Anim Panel Operations') + # Actor control status + menuBar.addcascademenu('AnimPanel', 'Control Status', + 'Enable/disable actor control panels') + menuBar.addmenuitem('Control Status', 'command', + 'Enable all actor controls', + label = 'Enable all', + command = self.enableActorControls) + menuBar.addmenuitem('Control Status', 'command', + 'Disable all actor controls', + label = 'Disable all', + command = self.disableActorControls) + # Frame Slider units + menuBar.addcascademenu('AnimPanel', 'Display Units', + 'Select display units') + menuBar.addmenuitem('Display Units', 'command', + 'Display frame counts', label = 'Frame count', + command = self.displayFrameCounts) + menuBar.addmenuitem('Display Units', 'command', + 'Display seconds', label = 'Seconds', + command = self.displaySeconds) + # Reset all actor controls + menuBar.addmenuitem('AnimPanel', 'command', + 'Reset Actor controls', + label = 'Reset all', + command = self.resetAll) + # Exit panel + menuBar.addmenuitem('AnimPanel', 'command', + 'Exit Anim Panel', + label = 'Exit', + command = self.destroy) + + menuBar.addmenu('Help', 'Anim Panel Help Operations') + self.toggleBalloonVar = IntVar() + self.toggleBalloonVar.set(0) + menuBar.addmenuitem('Help', 'checkbutton', + 'Toggle balloon help', + label = 'Balloon Help', + variable = self.toggleBalloonVar, + command = self.toggleBalloon) + menuFrame.pack(fill = X) + + # Create a frame to hold all the actor controls + actorFrame = Frame(hull) + + # Create a control for each actor + index = 0 + self.actorControlList = [] + for actor in self['actorList']: + ac = self.createcomponent( + 'actorControl%d' % index, (), 'Actor', + ActorControl, (actorFrame,)) + ac.pack(expand = 1, fill = X) + self.actorControlList.append(ac) + index = index + 1 + + # Now pack the actor frame + actorFrame.pack(expand = 1, fill = BOTH) + + # Create a frame to hold the playback controls + controlFrame = Frame(hull) + self.playPauseVar = IntVar() + self.playPauseVar.set(0) + self.playPauseButton = self.createcomponent( + 'playPause', (), None, + Checkbutton, (controlFrame,), + text = 'Play', width = 8, + variable = self.playPauseVar, + indicatoron = FALSE) + self.playPauseButton.pack(side = LEFT, expand = 1, fill = X) + + self.resetButton = self.createcomponent( + 'reset', (), None, + Button, (controlFrame,), + text = 'Reset All', + width = 8, + command = self.resetAll) + self.resetButton.pack(side = LEFT, expand = 1, fill = X) + + self.loopVar = IntVar() + self.loopVar.set(0) + self.loopButton = self.createcomponent( + 'loopButton', (), None, + Checkbutton, (controlFrame,), + text = 'Loop', width = 8, + variable = self.loopVar) + self.loopButton.pack(side = LEFT, expand = 1, fill = X) + + controlFrame.pack(fill = X) + + # Execute option callbacks + self.initialiseoptions(AnimPanel) + + def getActorControlAt(self, index): + return self.actorControlList[index] + + def enableActorControlAt(self,index): + self.getActorControlAt(index).enableControl() + + def enableActorControls(self): + for actorControl in self.actorControlList: + actorControl.enableControl() + + def disableActorControls(self): + for actorControl in self.actorControlList: + actorControl.disableControl() + + def disableActorControlAt(self,index): + self.getActorControlAt(index).disableControl() + + def displayFrameCounts(self): + for actorControl in self.actorControlList: + actorControl.displayFrameCounts() + + def displaySeconds(self): + for actorControl in self.actorControlList: + actorControl.displaySeconds() + + def resetAll(self): + for actorControl in self.actorControlList: + actorControl.reset() + + def toggleBalloon(self): + if self.toggleBalloonVar.get(): + self.balloon.configure(state = 'balloon') + else: + self.balloon.configure(state = 'none') + +class ActorControl(Pmw.MegaWidget): + def __init__(self, parent = None, **kw): + + INITOPT = Pmw.INITOPT + DEFAULT_FONT = (('MS', 'Sans', 'Serif'), 12, 'bold') + DEFAULT_ANIMS = ('neutral', 'run', 'walk') + optiondefs = ( + ('text', 'Actor', self._updateLabelText), + ('actor', None, None), + ('animList', DEFAULT_ANIMS, None), + ('sLabel_width', 5, None), + ('sLabel_font', DEFAULT_FONT, None), + ) + self.defineoptions(kw, optiondefs) + self.addoptions( + (('active', self['animList'][0], None),) + ) + + # Initialize the superclass + Pmw.MegaWidget.__init__(self, parent) + + # Handle to the toplevels hull + interior = self.interior() + interior.configure(relief = RAISED, bd = 2) + + # Instance variables + self.offset = 0.0 + self.fps = 24.0 + self.maxFrame = 120 + self.maxSeconds = self.maxFrame / self.fps + + # Create component widgets + self._label = self.createcomponent( + 'label', (), None, + Menubutton, (interior,), + font=('MSSansSerif', 14, 'bold'), + relief = RAISED, bd = 1, + activebackground = '#909090', + text = self['text']) + # Top level menu + labelMenu = Menu(self._label, tearoff = 0 ) + + # Menu to select display mode + self.unitsVar = IntVar() + self.unitsVar.set(FRAMES) + displayMenu = Menu(labelMenu, tearoff = 0 ) + displayMenu.add_radiobutton(label = 'Frame count', + value = FRAMES, + variable = self.unitsVar, + command = self.updateDisplay) + displayMenu.add_radiobutton(label = 'Seconds', + value = SECONDS, + variable = self.unitsVar, + command = self.updateDisplay) + # Items for top level menu + labelMenu.add_cascade(label = 'Display Units', menu = displayMenu) + labelMenu.add_command(label = 'Set Offset', command = self.setOffset) + labelMenu.add_command(label = 'Reset', command = self.reset) + # Now associate menu with menubutton + self._label['menu'] = labelMenu + self._label.pack(side = LEFT, fill = X) + + # Combo box to select current animation + animMenu = self.createcomponent( + 'animMenu', (), None, + Pmw.ComboBox, (interior,), + labelpos = W, label_text = 'Anim:', + entry_width = 12, selectioncommand = self.selectAnimNamed, + scrolledlist_items = self['animList']) + animMenu.selectitem(self['active']) + animMenu.pack(side = 'left', padx = 5, expand = 0) + + # Combo box to select frame rate + fpsList = (1,2,4,8,12,15,24,30) + fpsMenu = self.createcomponent( + 'fpsMenu', (), None, + Pmw.ComboBox, (interior,), + labelpos = W, label_text = 'at:', + entry_width = 4, selectioncommand = self.setFrameRate, + scrolledlist_items = fpsList) + fpsMenu.selectitem('24') + fpsMenu.pack(side = LEFT, padx = 5, expand = 0) + + # A label + fpsLabel = Label(interior, text = "fps") + fpsLabel.pack(side = LEFT) + + # Scale to control animation + frameFrame = Frame(interior, relief = SUNKEN, bd = 1) + self.minLabel = self.createcomponent( + 'minLabel', (), 'sLabel', + Label, (frameFrame,), + text = 0) + self.minLabel.pack(side = LEFT) + + self.frameControl = self.createcomponent( + 'scale', (), None, + Scale, (frameFrame,), + from_ = 0.0, to = self.maxFrame, resolution = 1.0, + orient = HORIZONTAL, showvalue = 1) + self.frameControl.pack(side = LEFT, expand = 1) + + self.maxLabel = self.createcomponent( + 'maxLabel', (), 'sLabel', + Label, (frameFrame,), + text = self.maxFrame) + self.maxLabel.pack(side = LEFT) + frameFrame.pack(side = LEFT, expand = 1, fill = X) + + # Checkbutton to enable/disable control + self.frameActiveVar = IntVar() + self.frameActiveVar.set(1) + frameActive = self.createcomponent( + 'checkbutton', (), None, + Checkbutton, (interior,), + variable = self.frameActiveVar) + frameActive.pack(side = LEFT, expand = 1) + + # Execute option callbacks + self.initialiseoptions(ActorControl) + + def _updateLabelText(self): + self._label['text'] = self['text'] + + def updateDisplay(self): + # Switch between showing frame counts and seconds + if self.unitsVar.get() == FRAMES: + newMin = int(math.floor(self.offset * self.fps)) + newMax = int(math.ceil(self.offset * self.fps)) + self.maxFrame + self.minLabel['text'] = newMin + self.maxLabel['text'] = newMax + self.frameControl.configure(to = newMax, resolution = 1.0) + else: + newMin = self.offset + newMax = self.offset + self.maxSeconds + self.minLabel['text'] = "%.1f" % newMin + self.maxLabel['text'] = "%.1f" % newMax + print newMin, newMax + self.frameControl.configure(to = newMax, resolution = 0.1) + + def selectAnimNamed(self, name): + print 'Selected Anim: ' + name + + def setFrameRate(self, rate): + self.fps = string.atof(rate) + self.maxSeconds = self.maxFrame / self.fps + self.updateDisplay() + + def setOffset(self): + newOffset = askfloat(title = self['text'], + prompt = 'Start offset (seconds):') + if newOffset: + self.offset = newOffset + self.updateDisplay() + + def enableControl(self): + self.frameActiveVar.set(1) + + def disableControl(self): + self.frameActiveVar.set(0) + + def displayFrameCounts(self): + self.unitsVar.set(FRAMES) + self.updateDisplay() + + def displaySeconds(self): + self.unitsVar.set(SECONDS) + self.updateDisplay() + + def reset(self): + self.offset = 0.0 + self.frameControl.set(0.0) + self.updateDisplay() +###################################################################### + +# Create demo in root window for testing. +if __name__ == '__main__': + widget = AnimPanel(actorList = (1,2,3)) + diff --git a/direct/src/tkpanels/FSMInspector.py b/direct/src/tkpanels/FSMInspector.py new file mode 100644 index 0000000000..5276d572c1 --- /dev/null +++ b/direct/src/tkpanels/FSMInspector.py @@ -0,0 +1,469 @@ +from Tkinter import * +from tkSimpleDialog import askstring +import Pmw +import math +import operator + +DELTA = (5.0 / 360.) * 2.0 * math.pi + +class FSMInspector(Pmw.MegaToplevel): + def __init__(self, parent = None, **kw): + # Initialize instance variables + self.stateInspectorDict = {} + + #define the megawidget options + INITOPT = Pmw.INITOPT + optiondefs = ( + ('title', 'FSM Viewer', None), + ('currentFSM', (), None), + ('gridSize', '0.25i', self._setGridSize), + ) + self.defineoptions(kw, optiondefs) + + # Initialize the toplevel widget + Pmw.MegaToplevel.__init__(self, parent) + + # Create the components + oldInterior = Pmw.MegaToplevel.interior(self) + # The Menu Bar + balloon = self.balloon = Pmw.Balloon() + # Start with balloon help disabled + balloon.configure(state = 'none') + menubar = self._menubar = self.createcomponent('menubar', + (), None, + Pmw.MenuBar, (oldInterior,), + balloon = balloon) + menubar.pack(fill=X) + # FSM Menu + menubar.addmenu('FSM', 'FSM Operations') + menubar.addmenuitem('FSM', 'command', + 'Input grid spacing', + label = 'Grid spacing...', + command = self.popupGridDialog) + # Create the checkbutton variable + self._fGridSnap = IntVar() + self._fGridSnap.set(1) + menubar.addmenuitem('FSM', 'checkbutton', + 'Enable/disable grid', + label = 'Snap to grid', + variable = self._fGridSnap, + command = self.toggleGridSnap) + menubar.addmenuitem('FSM', 'command', + 'Print out FSM layout', + label = 'Print FSM layout', + command = self.printLayout) + menubar.addmenuitem('FSM', 'command', + 'Exit the FSM Inspector', + label = 'Exit', + command = self._exit) + + # States Menu + menubar.addmenu('States', 'State Inspector Operations') + menubar.addcascademenu('States', 'Font Size', + 'Set state label size', tearoff = 1) + for size in (8, 10, 12, 14, 18, 24): + menubar.addmenuitem('Font Size', 'command', + 'Set font to: ' + `size` + ' Pts', label = `size` + ' Pts', + command = lambda s = self, sz = size: s.setFontSize(sz)) + menubar.addcascademenu('States', 'Marker Size', + 'Set state marker size', tearoff = 1) + for size in ('Small', 'Medium', 'Large'): + sizeDict = {'Small': '0.25i', 'Medium': '0.375i', 'Large' : '0.5i'} + menubar.addmenuitem('Marker Size', 'command', + size + ' markers', label = size + ' Markers', + command = lambda s = self, sz = size, d = sizeDict: + s.setMarkerSize(d[sz])) + + # The Help menu + menubar.addmenu('Help', 'FSM Panel Help Operations') + self.toggleBalloonVar = IntVar() + self.toggleBalloonVar.set(0) + menubar.addmenuitem('Help', 'checkbutton', + 'Toggle balloon help', + label = 'Balloon Help', + variable = self.toggleBalloonVar, + command = self.toggleBalloon) + + # The Scrolled Canvas + self._scrolledCanvas = self.createcomponent('scrolledCanvas', + (), None, + Pmw.ScrolledCanvas, (oldInterior,), + hull_width = 400, hull_height = 400, + usehullsize = 1) + self._canvas = self._scrolledCanvas.component('canvas') + self._canvas['scrollregion'] = ('-2i', '-2i', '2i', '2i') + self._scrolledCanvas.resizescrollregion() + self._scrolledCanvas.pack(padx = 5, pady = 5, expand=1, fill = BOTH) + + """ + # The Buttons + buttonBox = Frame(oldInterior) + #Button(buttonBox, text = 'Inspect Target').pack(side=LEFT,expand=1,fill=X) + Button(buttonBox, text = 'Print Layout', + command = self.printLayout).pack(side=LEFT,expand=1,fill=X) + Button(buttonBox, text = 'Quit', + command = self._exit).pack(side=LEFT,expand=1,fill=X) + buttonBox.pack(fill=X) + """ + + # Update lines + self._canvas.bind('', self.drawConnections) + self._canvas.bind('', self.mouse2Down) + self._canvas.bind('', self.mouse2Motion) + self._canvas.bind('', + lambda e, sc = self._scrolledCanvas: + sc.resizescrollregion()) + + self.createStateInspectors() + + self.initialiseoptions(FSMInspector) + + def scrolledCanvas(self): + return self._scrolledCanvas + + def canvas(self): + return self._canvas + + def setFontSize(self, size): + self._canvas.itemconfigure('labels', font = ('MS Sans Serif', size)) + + def setMarkerSize(self, size): + for key in self.stateInspectorDict.keys(): + self.stateInspectorDict[key].setRadius(size) + self.drawConnections() + + def drawConnections(self, event = None): + # Get rid of existing arrows + self._canvas.delete('arrow') + for key in self.stateInspectorDict.keys(): + si = self.stateInspectorDict[key] + state = si.state + if state.transitionArray: + for name in state.transitionArray: + self.connectStates(si, self.getStateInspector(name)) + + def connectStates(self, fromState, toState): + endpts = self.computeEndpoints(fromState, toState) + line = self._canvas.create_line(endpts, tags = ('arrow',), + arrow = 'last') + + def computeEndpoints(self, fromState, toState): + # Compute angle between two points + fromCenter = fromState.center() + toCenter = toState.center() + angle = self.findAngle(fromCenter, toCenter) + + # Compute offset fromState point + newFromPt = map(operator.__add__, + fromCenter, + self.computePoint( fromState.radius, + angle + DELTA)) + + # Compute offset toState point + newToPt = map(operator.__sub__, + toCenter, + self.computePoint( toState.radius, + angle - DELTA)) + return newFromPt + newToPt + + def computePoint(self, radius, angle): + x = radius * math.cos(angle) + y = radius * math.sin(angle) + return (x, y) + + def findAngle(self, fromPoint, toPoint): + dx = toPoint[0] - fromPoint[0] + dy = toPoint[1] - fromPoint[1] + return math.atan2(dy, dx) + + def mouse2Down(self, event): + self._width = 1.0 * self._canvas.winfo_width() + self._height = 1.0 * self._canvas.winfo_height() + xview = self._canvas.xview() + yview = self._canvas.yview() + self._left = xview[0] + self._top = yview[0] + self._dxview = xview[1] - xview[0] + self._dyview = yview[1] - yview[0] + self._2lx = event.x + self._2ly = event.y + + def mouse2Motion(self,event): + newx = self._left - ((event.x - self._2lx)/self._width) * self._dxview + self._canvas.xview_moveto(newx) + newy = self._top - ((event.y - self._2ly)/self._height) * self._dyview + self._canvas.yview_moveto(newy) + self._2lx = event.x + self._2ly = event.y + self._left = self._canvas.xview()[0] + self._top = self._canvas.yview()[0] + + def createStateInspectors(self): + fsm = self['currentFSM'] + # Number of rows/cols needed to fit inspectors in a grid + dim = int(math.ceil(math.sqrt(len(fsm)))) + # Separation between nodes + spacing = 2.5 * self._canvas.canvasx('0.375i') + count = 0 + for state in self['currentFSM']: + si = self.addState(state) + if state.defaultPosition: + si.setPos(state.defaultPosition[0], state.defaultPosition[1]) + else: + row = int(math.floor(count / dim)) + col = count % dim + si.setPos(col * spacing, row * spacing + + 0.5 * (0, spacing)[col % 2]) + count = count + 1 + self.drawConnections() + + def getStateInspector(self, name): + return self.stateInspectorDict.get(name, None) + + def addState(self, state): + si = self.stateInspectorDict[state.name] = StateInspector(self, state) + return si + + def enteredState(self, stateName): + si = self.stateInspectorDict.get(stateName,None) + if si: + si.enteredState() + + def exitedState(self, stateName): + si = self.stateInspectorDict.get(stateName,None) + if si: + si.exitedState() + + def _setGridSize(self): + self._gridSize = self['gridSize'] + self.setGridSize(self._gridSize) + + def setGridSize(self, size): + for key in self.stateInspectorDict.keys(): + self.stateInspectorDict[key].setGridSize(size) + + def popupGridDialog(self): + spacing = askstring('FSM Grid Spacing', 'Grid Spacing:') + if spacing: + self.setGridSize(spacing) + self._gridSize = spacing + + def toggleGridSnap(self): + if self._fGridSnap.get(): + self.setGridSize(self._gridSize) + else: + self.setGridSize(0) + + def printLayout(self): + dict = self.stateInspectorDict + keys = dict.keys() + keys.sort + print '{ ' + for key in keys[:-1]: + si = dict[key] + center = si.center() + print "'%s' : (%.3f, %.3f)," % \ + (si.state.name, center[0], center[1]) + for key in keys[-1:]: + si = dict[key] + center = si.center() + print "'%s' : (%.3f, %.3f)," % \ + (si.state.name, center[0], center[1]) + print '}' + + def toggleBalloon(self): + if self.toggleBalloonVar.get(): + self.balloon.configure(state = 'balloon') + else: + self.balloon.configure(state = 'none') + + def _exit(self): + self.destroy() + +class StateInspector(Pmw.MegaArchetype): + def __init__(self, inspector, state, **kw): + + # Record state + self.state = state + # Create a unique tag which you can use to move a marker and + # and its corresponding text around together + self.tag = state.name + + # Pointers to the inspector's components + self.scrolledCanvas = inspector.component('scrolledCanvas') + self._canvas = self.scrolledCanvas.component('canvas') + + #define the megawidget options + optiondefs = ( + ('radius', '0.375i', self._setRadius), + ('gridSize', '0.25i', self._setGridSize), + ) + self.defineoptions(kw, optiondefs) + + # Initialize the parent class + Pmw.MegaArchetype.__init__(self) + + # Draw the oval + self.x = 0 + self.y = 0 + half = self._canvas.winfo_fpixels(self['radius']) + self.marker = self._canvas.create_oval((self.x - half), + (self.y - half), + (self.x + half), + (self.y + half), + fill = 'CornflowerBlue', + tags = (self.tag,'markers')) + self.text = self._canvas.create_text(0, 0, text = state.name, + justify = CENTER, + tags = (self.tag,'labels')) + # Is this state contain a sub machine? + if state.fsmArray: + # reduce half by sqrt of 2.0 + half = half * 0.707106 + self.rect = self._canvas.create_rectangle((- half), (- half), + half, half, + tags = (self.tag,)) + + + # The Popup State Menu + self._popupMenu = Menu(self._canvas, tearoff = 0) + self._popupMenu.add_command(label = 'Request transistion to ' + + state.name, + command = self.transitionTo) + if state.fsmArray: + self._popupMenu.add_command(label = 'Inspect ' + state.name + + ' submachine', + command = self.inspectSubMachine) + + self.scrolledCanvas.resizescrollregion() + + # Add bindings + self._canvas.tag_bind(self.tag, '', self.mouseEnter) + self._canvas.tag_bind(self.tag, '', self.mouseLeave) + self._canvas.tag_bind(self.tag, '', self.mouseDown) + self._canvas.tag_bind(self.tag, '', self.mouseMotion) + self._canvas.tag_bind(self.tag, '', self.mouseRelease) + self._canvas.tag_bind(self.tag, '', self.popupStateMenu) + + self.initialiseoptions(StateInspector) + + # Utility methods + def _setRadius(self): + self.setRadius(self['radius']) + + def setRadius(self, size): + half = self.radius = self._canvas.winfo_fpixels(size) + c = self.center() + self._canvas.coords(self.marker, + c[0] - half, c[1] - half, c[0] + half, c[1] + half) + if self.state.fsmArray: + half = self.radius * 0.707106 + self._canvas.coords(self.rect, + c[0] - half, c[1] - half, c[0] + half, c[1] + half) + + def _setGridSize(self): + self.setGridSize(self['gridSize']) + + def setGridSize(self, size): + self.gridSize = self._canvas.winfo_fpixels(size) + if self.gridSize == 0: + self.fGridSnap = 0 + else: + self.fGridSnap = 1 + + def setText(self, text = None): + self._canvas.itemconfigure(self.text, text = text) + + def setPos(self, x, y, snapToGrid = 0): + if self.fGridSnap: + self.x = round(x / self.gridSize) * self.gridSize + self.y = round(y / self.gridSize) * self.gridSize + else: + self.x = x + self.y = y + # How far do we have to move? + cx, cy = self.center() + self._canvas.move(self.tag, self.x - cx, self.y - cy) + + def center(self): + c = self._canvas.coords(self.marker) + return (c[0] + c[2])/2.0, (c[1] + c[3])/2.0 + + # Event Handlers + def mouseEnter(self, event): + self._canvas.itemconfig(self.marker, width = 2) + + def mouseLeave(self, event): + self._canvas.itemconfig(self.marker, width = 1) + + def mouseDown(self, event): + self._canvas.lift(self.tag) + self.startx, self.starty = self.center() + self.lastx = self._canvas.canvasx(event.x) + self.lasty = self._canvas.canvasy(event.y) + + def mouseMotion(self, event): + dx = self._canvas.canvasx(event.x) - self.lastx + dy = self._canvas.canvasy(event.y) - self.lasty + newx, newy = map(operator.__add__,(self.startx, self.starty), (dx, dy)) + self.setPos(newx, newy) + + def mouseRelease(self,event): + self.scrolledCanvas.resizescrollregion() + + def popupStateMenu(self, event): + self._popupMenu.post(event.widget.winfo_pointerx(), + event.widget.winfo_pointery()) + + def transitionTo(self): + print 'transition to ' + self.tag + + def inspectSubMachine(self): + print 'inspect ' + self.tag + ' subMachine' + + def enteredState(self): + self._canvas.itemconfigure(self.marker, fill = 'Red') + + def exitedState(self): + self._canvas.itemconfigure(self.marker, fill = 'CornflowerBlue') + +class dummyState: + def __init__(self, name = None, transitionArray = None, fsmArray = 0): + self.name = name + self.transitionArray = transitionArray + self.fsmArray = fsmArray + self.defaultPosition = None + def hasChildFSMs(self): + return fsmArray + +class dummyFSM: + def __init__(self, stateCollection = (), layout = {}): + self.stateCollection = stateCollection + if layout: + for state in self.stateCollection: + pos = layout.get(state.name, None) + if pos: + state.defaultPosition= pos + def __getitem__(self, item): + return self.stateCollection[item] + def __len__(self): + return len(self.stateCollection) + +if __name__ == '__main__': + s0 = dummyState('state-0', ('state-1',)) + s1 = dummyState('state-1', ('state-2', 'state-3')) + s2 = dummyState('state-2', ('state-0', 'state-4', 'state-5'), fsmArray = 1) + s3 = dummyState('state-3', ('state-6',)) + s4 = dummyState('state-4', ('state-2','state-0')) + s5 = dummyState('state-5', ('state-0',), fsmArray = 1) + s6 = dummyState('state-6', ('state-3', 'state-0')) + fsm = dummyFSM((s0, s1, s2, s3, s4, s5, s6), + layout = {'state-0' : (167.83, 0.0), + 'state-1' : (95.91, 143.86), + 'state-2' : (167.83, 287.72), + 'state-3' : (23.98, 263.74), + 'state-4' : (335.67, 143.86), + 'state-5' : (239.76, 143.86), + 'state-6' : (23.98, 71.93)}) + fsmi = FSMInspector(title = 'My Little Viewer', currentFSM = fsm) + mainloop()