*** empty log message ***

This commit is contained in:
Mark Mine 2000-10-17 18:31:39 +00:00
parent f16d63d6ea
commit 576942ca88
8 changed files with 2039 additions and 0 deletions

View File

@ -0,0 +1 @@
123

View File

@ -0,0 +1 @@
123

View File

@ -0,0 +1,350 @@
"PANDA3D Particle Panel"
# Import Tkinter, Pmw, and the floater code from this directory tree.
from Tkinter import *
import Pmw
import dial
import floater
import vectorWidgets
class ParticlePanel(Pmw.MegaToplevel):
def __init__(self, parent = None, **kw):
INITOPT = Pmw.INITOPT
optiondefs = (
('title', 'Particle Panel', None),
)
self.defineoptions(kw, optiondefs)
Pmw.MegaToplevel.__init__(self, parent, title = self['title'])
# Handle to the toplevels hull
hull = self.component('hull')
balloon = self.balloon = Pmw.Balloon(hull)
# Start with balloon help disabled
self.balloon.configure(state = 'none')
menuFrame = Frame(hull, relief = GROOVE, bd = 2)
menuFrame.pack(fill = X, expand = 1)
menuBar = Pmw.MenuBar(menuFrame, hotkeys = 1, balloon = balloon)
menuBar.pack(side = LEFT, expand = 1, fill = X)
menuBar.addmenu('Particles', 'Particle Panel Operations')
menuBar.addmenuitem('Particles', 'command',
'Exit Particles Panel',
label = 'Exit',
command = self.destroy)
menuBar.addmenu('Help', 'Particle 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)
self.systemSelector = Pmw.ComboBox(menuFrame,
labelpos = W,
label_text = 'Particle System:',
entry_width = 16,
selectioncommand = self.selectSystemNamed,
scrolledlist_items = ('system 0',))
self.systemSelector.selectitem('system 0')
self.systemSelector.pack(side = 'left', expand = 0)
# Create the notebook pages
notebook = Pmw.NoteBook(hull)
notebook.pack(fill = BOTH, expand = 1)
systemPage = notebook.add('System')
factoryPage = notebook.add('Factory')
emitterPage = notebook.add('Emitter')
rendererPage = notebook.add('Renderer')
# System page
systemWidgets = (
('Pool size', 'Size of particle pool', 0.0, 1.0),
('Birth rate', 'Seconds between particle births', 0.0, None),
('Litter size', 'Number of particle created at each birth', 1.0, 1.0),
('Litter spread', 'Variation in litter size', 0.0, 1.0),
('System lifespan', 'Age in seconds at which system should die', 0.0, None)
)
self.createFloaters(systemPage, systemWidgets)
Checkbutton(systemPage, text = 'Local velocity',anchor = W).pack(
fill = X)
Checkbutton(systemPage, text = 'System grows older',anchor = W).pack(
fill = X)
pos = self.createVector3Entry(systemPage, 'Pos',
'Particle system position')
pos.addMenuItem('Popup 3DoF Panel')
hpr = self.createVector3Entry(systemPage, 'Hpr',
'Particle system orientation',
floaterGroup_labels = ('H', 'P', 'R'))
hpr.addMenuItem('Popup 3DoF Panel')
# FACTORY PAGE
self.createOptionMenu(factoryPage, 'Factory type:',
'Select type of particle factory',
('Point', 'Z Spin', 'Oriented'),
self.selectFactoryType)
factoryWidgets = (
('Life span', 'Average lifespan in seconds', 0.0, None),
('Life span spread', 'Variation in lifespan', 0.0, None),
('Mass', 'Average particle mass', 0.0, None),
('Mass spread', 'Variation in particle mass', 0.0, None),
('Terminal velocity', 'Average particle terminal velocity', 0.0, None),
('Terminal vel. spread', 'Variation in terminal velocity', 0.0, None))
self.createFloaters(factoryPage, factoryWidgets)
self.factoryNotebook = Pmw.NoteBook(factoryPage, tabpos = None)
pointPage = self.factoryNotebook.add('Point')
zSpinPage = self.factoryNotebook.add('Z Spin')
self.createAngleDial(zSpinPage, 'Initial angle',
'Starting angle in degrees')
self.createAngleDial(zSpinPage, 'Final angle',
'Final angle in degrees')
self.createAngleDial(zSpinPage, 'Angle spread',
'Spread of the final angle')
orientedPage = self.factoryNotebook.add('Oriented')
Label(orientedPage, text = 'Not implemented').pack(expand = 1,
fill = BOTH)
self.factoryNotebook.pack(expand = 1, fill = BOTH)
# EMITTER PAGE
self.createOptionMenu(emitterPage, 'Emitter type:',
'Select type of particle emitter',
('Box', 'Disc', 'Line', 'Point', 'Rectangle',
'Ring', 'Sphere Surface', 'Sphere Volume',
'Tangent Ring'),
self.selectEmitterType)
self.emitterNotebook = Pmw.NoteBook(emitterPage, tabpos = None)
pointPage = self.emitterNotebook.add('Box')
self.createVector3Entry(pointPage, 'Point 1',
'Point defining emitter box')
self.createVector3Entry(pointPage, 'Point 2',
'Point defining emitter box',
initialValue = (1.0, 1.0, 1.0))
self.createVector3Entry(pointPage, 'Launch vector',
'Initial particle velocity vector')
discPage = self.emitterNotebook.add('Disc')
self.createFloater(discPage, 'Radius', 'Radius of disc')
self.createAngleDial(discPage, 'Inner angle',
'Particle launch angle at center of disc')
self.createFloater(discPage, 'Inner magnitude',
'Launch velocity multiplier at center of disc')
self.createAngleDial(discPage, 'Outer angle',
'Particle launch angle at outer edge of disc')
self.createFloater(discPage, 'Outer magnitude',
'Launch velocity multiplier at edge of disc')
Checkbutton(discPage, text = 'Cubic Lerping').pack(
side = LEFT, expand = 1, fill = X)
linePage = self.emitterNotebook.add('Line')
self.createVector3Entry(linePage, 'Point 1',
'Point defining emitter line')
self.createVector3Entry(linePage, 'Point 2',
'Point defining emitter line',
initialValue = (1.0, 0.0, 0.0))
self.createVector3Entry(linePage, 'Launch Vector',
'Initial particle velocity vector',
initialValue = (0.0, 0.0, 1.0))
pointPage = self.emitterNotebook.add('Point')
self.createVector3Entry(pointPage, 'Location',
'Location of emitter point')
self.createVector3Entry(pointPage, 'Launch vector',
'Initial particle velocity vector',
initialValue = (0.0, 0.0, 1.0))
rectanglePage = self.emitterNotebook.add('Rectangle')
self.createVector3Entry(rectanglePage, 'Point 1',
'Point defining rectangle')
self.createVector3Entry(rectanglePage, 'Point 2',
'Point defining rectangle')
self.createVector3Entry(rectanglePage, 'Launch vector',
'Initial particle velocity vector',
initialValue = (0.0, 0.0, 1.0))
ringPage = self.emitterNotebook.add('Ring')
self.createFloater(ringPage, 'Radius', 'Radius of ring')
self.createAngleDial(ringPage, 'Angle', 'Particle launch angle')
self.createFloater(ringPage, 'Magnitude',
'Launch velocity multiplier at outer edge of ring')
sphereSurfacePage = self.emitterNotebook.add('Sphere Surface')
self.createFloater(sphereSurfacePage, 'Radius',
'Radius of sphere')
sphereVolumePage = self.emitterNotebook.add('Sphere Volume')
self.createFloater(sphereVolumePage, 'Radius',
'Radius of sphere')
tangentRingPage = self.emitterNotebook.add('Tangent Ring')
self.createFloater(tangentRingPage, 'Radius',
'Radius of ring')
self.emitterNotebook.pack(fill = X)
# RENDERER PAGE
self.createOptionMenu(rendererPage, 'Renderer type:',
'Select type of particle renderer',
('Geom', 'Point', 'Sparkle', 'Sprite'),
self.selectRendererType)
self.rendererNotebook = Pmw.NoteBook(rendererPage, tabpos = None)
geomPage = self.rendererNotebook.add('Geom')
f = Frame(geomPage)
f.pack(fill = X)
Label(f, width = 12, text = 'Geom node:').pack(side = LEFT)
Entry(f, width = 12).pack(side = LEFT, expand = 1, fill = X)
pointPage = self.rendererNotebook.add('Point')
self.createFloater(pointPage, 'Point size',
'Width and height of points in pixels')
self.createColorEntry(pointPage, 'Start color',
'Starting color of point')
self.createColorEntry(pointPage, 'End color',
'Ending color of point')
self.createOptionMenu(pointPage, 'Blend type:',
'Type of color blending used for particle',
('ONE_COLOR', 'BLEND_LIFE', 'BLEND_VEL'),
self.selectBlendType)
self.createOptionMenu(pointPage, 'Blend method:',
'Interpolation method between colors',
('LINEAR', 'CUBIC'),
self.selectBlendMethod)
sparklePage = self.rendererNotebook.add('Sparkle')
self.createColorEntry(sparklePage, 'Center color',
'Color of sparkle center')
self.createColorEntry(sparklePage, 'Edge color',
'Color of sparkle line endpoints')
self.createFloater(sparklePage, 'Birth radius',
'Initial sparkle radius')
self.createFloater(sparklePage, 'Death radius',
'Final sparkle radius')
self.createOptionMenu(pointPage, 'Life scale:',
'Does particle scale over its lifetime?',
('NO_SCALE', 'SCALE'),
self.selectBlendMethod)
spritePage = self.rendererNotebook.add('Sprite')
f = Frame(spritePage)
f.pack(fill = X)
Label(f, width = 12, text = 'Texture:').pack(side = LEFT)
Entry(f, width = 12).pack(side = LEFT, expand = 1, fill = X)
Checkbutton(spritePage, text = 'xScale',anchor = W).pack(fill = X)
Checkbutton(spritePage, text = 'yScale',anchor = W).pack(fill = X)
Checkbutton(spritePage, text = 'animAngle',anchor = W).pack(fill = X)
self.createFloater(spritePage, 'Initial X Scale',
'Initial X scaling factor')
self.createFloater(spritePage, 'Final X Scale',
'Final X scaling factor')
self.createFloater(spritePage, 'Initial Y Scale',
'Initial Y scaling factor')
self.createFloater(spritePage, 'Final Y Scale',
'Final Y scaling factor')
self.createAngleDial(spritePage, 'Non Animated Theta',
'Counter clockwise Z rotation of all sprites')
self.createOptionMenu(spritePage, 'Blend Type',
'Interpolation blend type for X and Y scaling',
('LINEAR', 'CUBIC'),
self.selectBlendMethod)
Checkbutton(spritePage, text = 'alphaDisable',anchor = W).pack(fill = X)
self.rendererNotebook.pack(fill = X)
self.factoryNotebook.setnaturalsize()
self.emitterNotebook.setnaturalsize()
self.rendererNotebook.setnaturalsize()
notebook.setnaturalsize()
# Make sure input variables processed
self.initialiseoptions(ParticlePanel)
def createFloaters(self, parent, widgetDefinitions):
for label, balloonHelp, min, resolution in widgetDefinitions:
self.createFloater(parent, label, balloonHelp, min, resolution)
def createFloater(self, parent, text, balloonHelp,
min = 0.0, resolution = None, **kw):
kw['text'] = text
kw['min'] = min
kw['initialValue'] = min
kw['resolution'] = resolution
widget = apply(floater.Floater, (parent,), kw)
widget.pack(fill = X)
self.balloon.bind(widget, balloonHelp)
return widget
def createAngleDial(self, parent, text, balloonHelp, **kw):
kw['text'] = text
widget = apply(dial.AngleDial,(parent,), kw)
widget.pack(fill = X)
self.balloon.bind(widget, balloonHelp)
return widget
def createVector3Entry(self, parent, text, balloonHelp, **kw):
# Set label's text
kw['text'] = text
widget = apply(vectorWidgets.Vector3Entry, (parent,), kw)
widget.pack(fill = X)
self.balloon.bind(widget, balloonHelp)
return widget
def createColorEntry(self, parent, text, balloonHelp, **kw):
# Set label's text
kw['text'] = text
widget = apply(vectorWidgets.ColorEntry, (parent,) ,kw)
widget.pack(fill = X)
self.balloon.bind(widget, balloonHelp)
return widget
def createOptionMenu(self, parent, text, balloonHelp, items, command):
optionVar = StringVar()
optionVar.set(items[0])
widget = Pmw.OptionMenu(parent, labelpos = W, label_text = text,
label_width = 12, menu_tearoff = 1,
menubutton_textvariable = optionVar,
items = items,
command = command)
widget.pack(fill = X)
self.balloon.bind(widget.component('menubutton'), balloonHelp)
return optionVar
def selectFactoryType(self, type):
self.factoryNotebook.selectpage(type)
def selectEmitterType(self, type):
self.emitterNotebook.selectpage(type)
def selectRendererType(self, type):
self.rendererNotebook.selectpage(type)
def selectBlendType(self, type):
print type
def selectBlendMethod(self, method):
print method
def selectSystemNamed(self, name):
print name
def toggleBalloon(self):
if self.toggleBalloonVar.get():
self.balloon.configure(state = 'balloon')
else:
self.balloon.configure(state = 'none')
######################################################################
# Create demo in root window for testing.
if __name__ == '__main__':
root = Pmw.initialise()
pp = ParticlePanel()
#ve = VectorEntry(Toplevel(), relief = GROOVE)
#ve.pack()

View File

@ -0,0 +1,389 @@
"DIRECT Nine DoF Placer demonstration"
# Import Tkinter, Pmw, and the floater code from this directory tree.
from Tkinter import *
import Pmw
import floater
import dial
class Placer(Pmw.MegaToplevel):
def __init__(self, parent = None, **kw):
INITOPT = Pmw.INITOPT
optiondefs = (
('title', 'Placer Panel', None),
('nodePath', None, None),
)
self.defineoptions(kw, optiondefs)
# Initialize the superclass
Pmw.MegaToplevel.__init__(self, parent)
# Handle to the toplevels hull
hull = self.component('hull')
menuFrame = Frame(hull, relief = GROOVE, bd = 2)
menuFrame.pack(fill = X, expand = 1)
balloon = self.balloon = Pmw.Balloon()
# Start with balloon help disabled
self.balloon.configure(state = 'none')
menuBar = Pmw.MenuBar(menuFrame, hotkeys = 1, balloon = balloon)
menuBar.pack(side = LEFT, expand = 1, fill = X)
menuBar.addmenu('Placer', 'Placer Panel Operations')
menuBar.addcascademenu('Placer', 'Axis',
'Control axis visibility',
tearoff = 1)
self.axisViz = StringVar()
self.axisViz.set('Show Axis')
menuBar.component('Axis-menu').add_radiobutton(
label = 'Show Axis',
variable = self.axisViz,
value = 'Show Axis',
command = lambda s = self: s._updateAxisViz())
menuBar.component('Axis-menu').add_radiobutton(
label = 'Hide Axis',
variable = self.axisViz,
value = 'Hide Axis',
command = lambda s = self: s._updateAxisViz())
menuBar.component('Axis-menu').add_radiobutton(
label = 'Auto Axis',
variable = self.axisViz,
value = 'Auto Axis',
command = lambda s = self: s._updateAxisViz())
menuBar.addmenuitem('Placer', 'command',
'Exit Placer Panel',
label = 'Exit',
command = self.destroy)
menuBar.addmenu('NodePath', 'Node Path Operations')
menuBar.addmenuitem('NodePath', 'command',
'Undo Pos/Hpr/Scale',
label = 'Undo All',
command = self._undoAll)
menuBar.addmenuitem('NodePath', 'command',
'Redo Pos/Hpr/Scale',
label = 'Redo All',
command = self._redoAll)
menuBar.addmenuitem('NodePath', 'command',
'Reset Node Path',
label = 'Reset All',
command = self._resetAll)
menuBar.addmenuitem('NodePath', 'command',
'Print Node Path Info',
label = 'Print Info',
command = self.printNodePathInfo)
menuBar.addmenu('Help', 'Placer 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)
nodePathMenu = Pmw.ComboBox(menuFrame,
labelpos = W,
label_text = 'Node Path:',
entry_width = 12,
selectioncommand = self.selectNodePathNamed,
scrolledlist_items = ('selected',
'hot point',
'camera'))
nodePathMenu.selectitem('selected')
nodePathMenu.pack(side = 'left', expand = 0)
mode = StringVar()
mode.set('Drive')
modeMenu = Pmw.OptionMenu(menuFrame,
menubutton_textvariable=mode,
items = ('Drive', 'Orbit',
'Absolute', 'Relative'),
command = self._updateFloaterLabels,
menubutton_width = 8)
modeMenu.pack(side = 'left', expand = 0)
self.wrtMenu = Pmw.ComboBox(menuFrame,
labelpos = W,
label_text = 'WRT:',
entry_width = 12,
selectioncommand = self.selectNodePathNamed,
scrolledlist_items = ('render',
'selected',
'camera'))
self.wrtMenu.selectitem('render')
self.wrtMenu.pack(side = 'left', expand = 0)
# The master frame for the dials
dialFrame = Frame(hull)
dialFrame.pack(fill = 'both', expand = 1)
# Create and pack the Pos Controls
posGroup = Pmw.Group(dialFrame,
tag_pyclass = Menubutton,
tag_text = 'Position',
tag_font=('MSSansSerif', 14, 'bold'),
tag_activebackground = '#909090',
ring_relief = 'raised')
posMenubutton = posGroup.component('tag')
posMenu = Menu(posMenubutton)
posMenu.add_command(label = 'Undo', command = self._undoPos)
posMenu.add_command(label = 'Redo', command = self._redoPos)
posMenu.add_command(label = 'Set to zero', command = self._zeroPos)
posMenu.add_command(label = 'Restore initial', command = self._resetPos)
posMenubutton['menu'] = posMenu
posGroup.pack(side='left',fill = 'both', expand = 1)
posInterior = posGroup.interior()
# Create the floaters
self.posX = self.createcomponent('posX', (), None,
dial.Dial, (posInterior,),
text = 'X',
label_foreground = 'Red')
self.posX['command'] = self.printCommand
self.posX.pack(expand=1,fill='x')
self.posY = self.createcomponent('posY', (), None,
dial.Dial, (posInterior,),
text = 'Y',
label_foreground = '#00A000')
self.posY['command'] = self.printCommand
self.posY.pack(expand=1,fill='x')
self.posZ = self.createcomponent('posZ', (), None,
dial.Dial, (posInterior,),
text = 'Z',
label_foreground = 'Blue')
self.posZ['command'] = self.printCommand
self.posZ.pack(expand=1,fill='x')
# Create and pack the Hpr Controls
hprGroup = Pmw.Group(dialFrame,
tag_pyclass = Menubutton,
tag_text = 'Orientation',
tag_font=('MSSansSerif', 14, 'bold'),
tag_activebackground = '#909090',
ring_relief = 'raised')
hprMenubutton = hprGroup.component('tag')
hprMenu = Menu(hprMenubutton)
hprMenu.add_command(label = 'Undo', command = self._undoHpr)
hprMenu.add_command(label = 'Redo', command = self._redoHpr)
hprMenu.add_command(label = 'Set to zero', command = self._zeroHpr)
hprMenu.add_command(label = 'Restore initial', command = self._resetHpr)
hprMenubutton['menu'] = hprMenu
hprGroup.pack(side='left',fill = 'both', expand = 1)
hprInterior = hprGroup.interior()
# Create the floaters
self.hprH = self.createcomponent('hprH', (), None,
dial.Dial, (hprInterior,),
text = 'H', fRollover = 0,
max = 360.0, numTicks = 12,
label_foreground = 'blue')
self.hprH['command'] = self.printCommand
self.hprH.pack(expand=1,fill='x')
self.hprP = self.createcomponent('hprP', (), None,
dial.Dial, (hprInterior,),
text = 'P', fRollover = 0,
max = 360.0, numTicks = 12,
label_foreground = 'red')
self.hprP['command'] = self.printCommand
self.hprP.pack(expand=1,fill='x')
self.hprR = self.createcomponent('hprR', (), None,
dial.Dial, (hprInterior,),
text = 'R', fRollover = 0,
max = 360.0, numTicks = 12,
label_foreground = '#00A000')
self.hprR['command'] = self.printCommand
self.hprR.pack(expand=1,fill='x')
# Create and pack the Scale Controls
scaleGroup = Pmw.Group(dialFrame,
tag_text = 'Scale',
tag_pyclass = Menubutton,
tag_font=('MSSansSerif', 14, 'bold'),
tag_activebackground = '#909090',
ring_relief = 'raised')
scaleMenubutton = scaleGroup.component('tag')
scaleMenu = Menu(scaleMenubutton)
scaleModeMenu = Menu(scaleMenu)
# The available scaling modes
self.scalingMode = StringVar()
self.scalingMode.set('Free')
scaleModeMenu.add_radiobutton(label = 'Free',
variable = self.scalingMode)
scaleModeMenu.add_radiobutton(label = 'Uniform',
variable = self.scalingMode)
scaleModeMenu.add_radiobutton(label = 'Proportional',
variable = self.scalingMode)
# First level scaling menu
scaleMenu.add_command(label = 'Undo', command = self._undoScale)
scaleMenu.add_command(label = 'Redo', command = self._redoScale)
scaleMenu.add_command(label = 'Set to unity',
command = self._unitScale)
scaleMenu.add_command(label = 'Restore initial',
command = self._resetScale)
scaleMenu.add_cascade(label = 'Scaling mode...',
menu = scaleModeMenu)
scaleMenubutton['menu'] = scaleMenu
scaleGroup.pack(side='left',fill = 'both', expand = 1)
scaleInterior = scaleGroup.interior()
# Create the floaters
self.scaleX = self.createcomponent('scaleX', (), None,
dial.Dial, (scaleInterior,),
text = 'X Scale',
initialValue = 1.0,
label_foreground = 'Red')
self.scaleX['command'] = self.printCommand
self.scaleX.pack(expand=1,fill='x')
self.scaleY = self.createcomponent('scaleY', (), None,
dial.Dial, (scaleInterior,),
text = 'Y Scale',
initialValue = 1.0,
label_foreground = '#00A000')
self.scaleY['command'] = self.printCommand
self.scaleY.pack(expand=1,fill='x')
self.scaleZ = self.createcomponent('scaleZ', (), None,
dial.Dial, (scaleInterior,),
text = 'Z Scale',
initialValue = 1.0,
label_foreground = 'Blue')
self.scaleZ['command'] = self.printCommand
self.scaleZ.pack(expand=1,fill='x')
# Make sure appropriate labels are showing
self._updateFloaterLabels('Drive')
# Make sure input variables processed
self.initialiseoptions(Placer)
def printCommand(self, val):
print 'Current value: %s' % val
def selectNodePathNamed(self, name):
print 'Selected Node Path: ' + name
def printNodePathInfo(self):
print 'Print Node Path info here'
def _updateAxisViz(self):
self.updateAxisViz(self.axisViz.get())
def updateAxisViz(self, mode):
print mode
def _undoPos(self):
print 'undo pos'
def _redoPos(self):
print 'redo pos'
def _resetPos(self):
self.posX.reset()
self.posY.reset()
self.posZ.reset()
def _zeroPos(self):
self.posX.set(0.0)
self.posY.set(0.0)
self.posZ.set(0.0)
def _undoHpr(self):
print 'undo hpr'
def _redoHpr(self):
print 'redo hpr'
def _resetHpr(self):
self.hprH.reset()
self.hprP.reset()
self.hprR.reset()
def _zeroHpr(self):
self.hprH.set(0.0)
self.hprP.set(0.0)
self.hprR.set(0.0)
def _resetScale(self):
self.scaleX.reset()
self.scaleY.reset()
self.scaleZ.reset()
def _undoScale(self):
print 'undo scale'
def _redoScale(self):
print 'redo scale'
def _unitScale(self):
self.scaleX.set(1.0)
self.scaleY.set(1.0)
self.scaleZ.set(1.0)
def _undoAll(self):
self._undoPos()
self._undoHpr()
self._undoScale()
def _redoAll(self):
self._redoPos()
self._redoHpr()
self._redoScale()
def _resetAll(self):
self._resetPos()
self._resetHpr()
self._resetScale()
def _updateFloaterLabels(self, movementMode):
namePrefix = ''
self.movementMode = movementMode
if (movementMode == 'Drive'):
namePrefix = 'Drive delta '
elif (movementMode == 'Orbit'):
namePrefix = 'Orbit '
elif (movementMode == 'Absolute'):
namePrefix = 'Absolute '
elif (movementMode == 'Relative'):
namePrefix = 'Relative '
if(movementMode == 'Relative'):
self.wrtMenu.configure(entry_foreground = 'Black')
self.wrtMenu.configure(entry_background = 'SystemWindow')
else:
self.wrtMenu.configure(entry_foreground = 'gray50')
self.wrtMenu.configure(entry_background = '#E0E0E0')
self.posX['text'] = namePrefix + 'X'
self.posY['text'] = namePrefix + 'Y'
self.posZ['text'] = namePrefix + 'Z'
if (movementMode == 'Orbit'):
namePrefix = 'Orbit delta '
self.hprH['text'] = namePrefix + 'H'
self.hprP['text'] = namePrefix + 'P'
self.hprR['text'] = namePrefix + 'R'
def toggleBalloon(self):
if self.toggleBalloonVar.get():
self.balloon.configure(state = 'balloon')
else:
self.balloon.configure(state = 'none')
######################################################################
# Create demo in root window for testing.
if __name__ == '__main__':
root = Pmw.initialise()
widget = Placer()

View File

@ -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))

View File

@ -0,0 +1,458 @@
from Tkinter import *
from tkSimpleDialog import askfloat
import Pmw
import math
import string
import operator
# TODO:
# More standardized use of 'max' and 'min'
# Better floater style action
# New option? 'delta'? 'repeatVal'? 'modulus'
TWO_PI = 2.0 * math.pi
ONEPOINTFIVE_PI = 1.5 * math.pi
POINTFIVE_PI = 0.5 * math.pi
INNER_SF = 0.175
MAX_EXP = 5
class Dial(Pmw.MegaWidget):
def __init__(self, parent = None, **kw):
#define the megawidget options
INITOPT = Pmw.INITOPT
optiondefs = (
# Widget relief
('relief', GROOVE, INITOPT),
# Widget borderwidth
('borderwidth', 2, INITOPT),
# Relief of dial inset
('canvas_relief', GROOVE, INITOPT),
# Borderwidth of dial inset
('canvas_bd', 2, INITOPT),
# Size of edge of dial inset
('edgeLength', 50, INITOPT),
('initialValue', 0.0, INITOPT),
# Snap to angle on/off
('fSnap', 0, None),
# Do values rollover (i.e. accumulate) with multiple revolutions
('fRollover', 1, None),
('command', None, None),
('text', 'Dial Widget', self.updateLabel),
('numTicks', 10, self.createTicks),
('numDigits', 2, self.updateEntryFormat),
('min', 0.0, self.setScaleFactor),
('max', 1.0, self.setScaleFactor),
)
self.defineoptions(kw, optiondefs)
# Initialize the superclass
Pmw.MegaWidget.__init__(self, parent)
# Set up some local and instance variables
dim = self['edgeLength']
self.sfGridDelta = dim / 10
half = self.half = int(dim/2.0)
radius = self.radius = half - 2
# Running total which increments/decrements every time around dial
self.baseVal = 0.0
# Determines value of one dial revolution
self.scaleFactor = 1.0
self.dialAngle = None
# Current value
self.value = self['initialValue']
# Create the components
interior = self.interior()
interior.configure(relief = self['relief'], bd = self['borderwidth'])
# The canvas
self._canvas = self.createcomponent('canvas', (), None,
Canvas, (interior,),
width = dim + 12, height = dim,
scrollregion = ((- half),(- half),
half, half))
self._canvas.grid(rowspan = 2, columnspan = 2)
# The dial face
self._canvas.create_oval(-radius, -radius, radius, radius,
fill = 'white', tags = ('dial',))
self.createTicks()
# The velocity knob
self._canvas.create_oval(-radius * INNER_SF, -radius * INNER_SF,
radius * INNER_SF, radius * INNER_SF,
fill = '#909090', tags = ('velocityKnob',))
# The indicator
self._canvas.create_line(0, 0, 0, (- radius), width = 2,
tags = ('indicator', 'dial'))
# The Scale Factor marker
self._canvas.create_polygon( half + 4, - 4, half + 12, 0,
half + 4, + 4, fill = '#A0A0A0',
tags = ('sfMarker',))
self.sfy = 0
# The Dial's label
self._label = self.createcomponent('label', (), None,
Label, (interior,),
text = self['text'],
font = ('MS Sans Serif', 12, 'bold'),
anchor = CENTER)
self._label.grid(row = 0, col = 2, sticky = EW)
# The entry
self._entryVal = StringVar()
self._entry = self.createcomponent('entry', (), None,
Entry, (interior,),
justify = RIGHT,
textvariable = self._entryVal)
self._entry.grid(row = 1, col = 2, sticky = EW)
self._entry.bind('<Return>', self.validateEntryInput)
self._entryBackground = self._entry.cget('background')
interior.columnconfigure(2, weight = 1)
# The popup menu
self._popupMenu = Menu(interior, tearoff = 0)
self._fAngleSnap = IntVar()
self._fAngleSnap.set(self['fSnap'])
self._popupMenu.add_checkbutton(label = 'Angle snap',
variable = self._fAngleSnap,
command = self.setAngleSnap)
self._fRollover = IntVar()
self._fRollover.set(self['fRollover'])
self._popupMenu.add_checkbutton(label = 'Rollover',
variable = self._fRollover,
command = self.setRollover)
sfMenu = Menu(interior, tearoff = 1)
self.expVar = DoubleVar()
self.expVar.set(0)
for exp in range (MAX_EXP, -(MAX_EXP + 1), -1):
sf = "%g" % math.pow(10, exp)
sfMenu.add_radiobutton(label = sf, value = exp,
variable = self.expVar,
command = self.setScaleFactor)
sfMenu.add_command(label = 'Scale Factor...',
command = self.getScaleFactor)
self._popupMenu.add_cascade(label = 'Scale Factor',
menu = sfMenu)
self._popupMenu.add_command(label = 'Reset Dial',
command = self.reset)
# Add event bindings
self._canvas.tag_bind('dial', '<ButtonPress-1>', self.mouseDown)
self._canvas.tag_bind('dial', '<B1-Motion>', self.mouseMotion)
self._canvas.tag_bind('dial', '<Shift-B1-Motion>', self.shiftMouseMotion)
self._canvas.tag_bind('sfMarker', '<Enter>', self.highlightSFMarker)
self._canvas.tag_bind('sfMarker', '<Leave>', self.restoreSFMarker)
self._canvas.tag_bind('velocityKnob', '<Enter>', self.highlightKnob)
self._canvas.tag_bind('velocityKnob', '<Leave>', self.restoreKnob)
self._canvas.tag_bind('sfMarker', '<ButtonPress-1>', self.sfMouseDown)
self._canvas.tag_bind('sfMarker', '<B1-Motion>', self.sfMouseMotion)
self._canvas.tag_bind('sfMarker', '<ButtonRelease-1>', self.sfMouseUp)
self._canvas.tag_bind('velocityKnob', '<ButtonPress-1>', self.knobMouseDown)
self._canvas.tag_bind('velocityKnob', '<B1-Motion>', self.knobMouseMotion)
self._canvas.tag_bind('velocityKnob', '<ButtonRelease-1>', self.knobMouseUp)
self._canvas.bind('<ButtonPress-3>', self.popupDialMenu)
self._canvas.bind('<Double-ButtonPress-1>', self.mouseReset)
self._canvas.bind('<Up>', self.expUp)
self._canvas.bind('<Down>', self.expDown)
# Make sure input variables processed
self.initialiseoptions(Dial)
def updateLabel(self):
self._label['text'] = self['text']
def createTicks(self):
self._canvas.delete('ticks')
# Based upon input snap angle, how many ticks
numTicks = self['numTicks']
# Compute snapAngle (radians)
self.snapAngle = snapAngle = TWO_PI / numTicks
# Create the ticks at the snap angles
for ticknum in range(numTicks):
angle = snapAngle * ticknum
# convert to canvas coords
angle = angle - POINTFIVE_PI
# Compute tick endpoints
startx = math.cos(angle) * self.radius
starty = math.sin(angle) * self.radius
# Elongate ticks at 90 degree points
if (angle % POINTFIVE_PI) == 0.0:
sf = 0.6
else:
sf = 0.8
endx = startx * sf
endy = starty * sf
self._canvas.create_line(startx, starty, endx, endy,
tags = ('ticks','dial'))
def mouseDown(self,event):
self.lastAngle = dialAngle = self.computeDialAngle(event)
self.computeValueFromAngle(dialAngle)
def shiftMouseMotion(self,event):
self.mouseMotion(event, 1)
def mouseMotion(self, event, fShift = 0):
dialAngle = self.computeDialAngle(event, fShift)
self.computeValueFromAngle(dialAngle)
def computeDialAngle(self,event, fShift = 0):
x = self._canvas.canvasx(event.x)
y = self._canvas.canvasy(event.y)
rawAngle = math.atan2(y,x)
# Snap to grid
# Convert to dial coords to do snapping
dialAngle = rawAngle + POINTFIVE_PI
if operator.xor(self['fSnap'], fShift):
dialAngle = round(dialAngle / self.snapAngle) * self.snapAngle
return dialAngle
def computeValueFromAngle(self, dialAngle):
delta = self.delta
dialAngle = dialAngle % TWO_PI
# Check for rollover, if necessary
if (self.lastAngle > ONEPOINTFIVE_PI) & (dialAngle < POINTFIVE_PI):
self.baseVal = self.baseVal + delta
elif (self.lastAngle < POINTFIVE_PI) & (dialAngle > ONEPOINTFIVE_PI):
self.baseVal = self.baseVal - delta
self.lastAngle = dialAngle
# Update value and entry
newValue = self['min'] + self.baseVal + delta * (dialAngle / TWO_PI)
self.dialAngle = dialAngle
self.set(newValue)
def get(self):
return self.value
def set(self, value):
if not self['fRollover']:
if value > self['max']:
self.baseVal = 0.0
value = ((value - self['min']) %
(self['max'] - self['min'])) + self['min']
self.updateEntry(value)
if self.dialAngle:
self.updateIndicatorRadians(self.dialAngle)
self.dialAngle = None
else:
self.updateIndicator(value)
if self['command']:
self['command'](value)
def updateIndicator(self, value):
# compute new indicator angle
delta = self.delta
factors = divmod(value - self['min'], delta)
self.baseVal = factors[0] * delta
self.updateIndicatorRadians( (factors[1]/delta) * TWO_PI )
def updateIndicatorDegrees(self, degAngle):
self.updateIndicatorRadians(degAngle * (math.pi/180.0))
def updateIndicatorRadians(self,dialAngle):
rawAngle = dialAngle - POINTFIVE_PI
# Compute end points
endx = math.cos(rawAngle) * self.radius
endy = math.sin(rawAngle) * self.radius
# Draw new indicator
self._canvas.coords('indicator', endx * INNER_SF, endy * INNER_SF,
endx, endy)
def updateEntry(self, value):
self._entryVal.set(self.entryFormat % value)
def updateEntryFormat(self):
self.entryFormat = "%." + "%df" % self['numDigits']
self.updateEntry(self.value)
def validateEntryInput(self, event):
input = self._entryVal.get()
try:
newValue = string.atof(input)
self.set(newValue)
self._entry.configure(background = self._entryBackground)
except ValueError:
self._entry.configure(background = 'Pink')
def sfMouseDown(self, event):
# Record marker starting position
self.starty = self.sfy
# Record mouse starting position (convert to canvas coords)
self.lasty = self._canvas.canvasy(event.y)
def sfMouseMotion(self, event):
# How far did the mouse move?
dy = self._canvas.canvasy(event.y) - self.lasty
# Apply this delta to the marker
newy = self.starty + dy
# Compute new exponent based upon current position
exp = self.sfComputeExp(newy)
# Set resulting scale factor
self.setScaleFactorExp(exp)
def sfMouseUp(self, event):
self._canvas.delete('sfText')
# Compute exponent based on current marker position
def sfComputeExp(self, y, fSnap = 1):
# Force marker to stay visible
newy = max( -self.half, min( self.half, y ) )
# Snap it
gridDelta = self.sfGridDelta
if fSnap:
newy = round( newy / gridDelta ) * gridDelta
# Compute resulting exponent
return (-(newy / gridDelta))
def setScaleFactorExp(self, exp, showText = 1, fUpdateIndicator = 1):
self.exp = exp
# Update popup scale factor menu to nearest exponent
self.expVar.set(int(round(exp)))
# Compute new scale factor
self.scaleFactor = math.pow(10, exp)
# Compute resulting delta
self.delta = self.scaleFactor * (self['max'] - self['min'])
# Update indicator to reflect new scale factor
if fUpdateIndicator:
self.updateIndicator(self.value)
# Move marker to correct position
self.updateScaleFactorMarker(-exp*self.sfGridDelta, showText)
def expUp(self,event):
self.setScaleFactorExp(min(MAX_EXP, self.exp + 1), 0)
def expDown(self,event):
self.setScaleFactorExp(max(-MAX_EXP, self.exp - 1), 0)
def knobMouseDown(self,event):
self.lasty = self._canvas.canvasy(event.y)
self.updateIndicatorRadians(0.0)
self.velocityTask = self.after(100, self.computeVelocity)
def knobMouseMotion(self, event):
# How far is the mouse from the origin?
dx = self._canvas.canvasx(event.x)
self.lasty = self._canvas.canvasy(event.y)
exp = -5 + dx/20.0
exp = max( -5, min( 5, exp ) )
# Set resulting scale factor
self.setScaleFactorExp(exp, 0, fUpdateIndicator = 0)
def knobMouseUp(self, event):
self.after_cancel(self.velocityTask)
# reset indicator
self.updateIndicator(self.value)
def computeVelocity(self):
if self.lasty < 0:
sign = -1.0
else:
sign = 1.0
lasty = abs(self.lasty)
if lasty > 5:
lasty = lasty - 5
sf = min(100, lasty)/100.0
sf = pow(sf, 3.0)
newVal = self.value - sign * sf * self.delta
self.dialAngle = - sign * sf * POINTFIVE_PI
self.set(newVal)
self.velocityTask = self.after(100, self.computeVelocity)
def updateScaleFactorMarker(self, newy, showText = 1):
# Move marker
self._canvas.move('sfMarker', 0, newy - self.sfy)
self.sfy = newy
# Show current scaling factor
if showText:
sfText = '%g' % (self.delta / 10.0,)
self._canvas.delete('sfText')
self._canvas.create_rectangle( self.half - 40, newy - 6,
self.half, newy + 7,
fill = 'white',
tags = ('sfText',))
self._canvas.create_text( self.half, newy,
justify = RIGHT,
anchor = E,
text = sfText,
fill = 'Red',
tags = ('sfText',))
# The following routines are used to handle the popup menu
def popupDialMenu(self,event):
self._popupMenu.post(event.widget.winfo_pointerx(),
event.widget.winfo_pointery())
# This is called by the scale factor popup menu and when the user
# changes the dial 'delta' value
def setScaleFactor(self):
exp = self.expVar.get()
self.setScaleFactorExp(exp, showText = 0)
# This handles the popup scale factor dialog
def getScaleFactor(self):
sf = askfloat('Dial Scale Factor', 'Scale Factor:',
parent = self.interior())
if sf:
self.setScaleFactorExp(math.log10(sf), showText = 0)
# Turn angle snap on/off
def setAngleSnap(self):
self['fSnap'] = self._fAngleSnap.get()
# Turn rollover (accumulation of a sum) on/off
def setRollover(self):
self['fRollover'] = self._fRollover.get()
def highlightSFMarker(self, event):
self._canvas.itemconfigure('sfMarker', fill = '#252525')
def restoreSFMarker(self, event):
self._canvas.itemconfigure('sfMarker', fill = '#A0A0A0')
def highlightKnob(self, event):
self._canvas.itemconfigure('velocityKnob', fill = '#252525')
def restoreKnob(self, event):
self._canvas.itemconfigure('velocityKnob', fill = '#A0A0A0')
# Reset dial to zero
def mouseReset(self,event):
if not self._canvas.find_withtag(CURRENT):
self.reset()
def reset(self):
self.set(self['initialValue'])
# Should we do this?
self.setScaleFactorExp(0, showText = 0)
class AngleDial(Dial):
def __init__(self, parent = None, **kw):
# Set the typical defaults for a 360 degree angle dial
optiondefs = (
('fRollover', 0, None),
('numTicks', 12, None),
('max', 360.0, None),
)
self.defineoptions(kw, optiondefs)
# Initialize the superclass
Dial.__init__(self, parent)
# Needed because this method checks if self.__class__ is myClass
# where myClass is the argument passed into inialiseoptions
self.initialiseoptions(AngleDial)
if __name__ == '__main__':
tl = Toplevel()
d = Dial(tl)
d2 = Dial(tl, numTicks = 12, max = 360, fRollover = 0, initialValue = 180)
d3 = Dial(tl, numTicks = 12, max = 90, min = -90, fRollover = 0)
d4 = Dial(tl, numTicks = 16, max = 256, fRollover = 0)
d.pack(expand = 1, fill = X)
d2.pack(expand = 1, fill = X)
d3.pack(expand = 1, fill = X)
d4.pack(expand = 1, fill = X)

View File

@ -0,0 +1,209 @@
title = 'DIRECT Floater megawidget'
import string
import Tkinter
import Pmw
OK = 1
ERROR = 0
class Floater(Pmw.MegaWidget):
""" Megawidget containing a label, an entry, and a scale.
Used as a velocity style controller for floating point values
"""
def __init__(self, parent = None, **kw):
# Define the megawidget options.
optiondefs = (
('command', None, None),
('value', 0.0, self._updateValue),
('text', '', self._updateLabelText),
('min', None, None),
('max', None, None),
('resolution', None, None),
('maxVelocity', 100.0, None),
('errorbackground', 'pink', None),
('significantDigits', 2, self._setSigDigits),
)
self.defineoptions(kw, optiondefs)
# Initialise base class (after defining options).
Pmw.MegaWidget.__init__(self, parent)
# Initialize some variables
self.value = 0.0
self.velocity = 0.0
self.entryValue = Tkinter.StringVar()
self.entryFormat = '%.2f'
self.normalBackground = None
# Create the components.
interior = self.interior()
interior['relief'] = 'groove'
interior['borderwidth'] = 2
self.infoFrame = self.createcomponent('frame',
(), None,
Tkinter.Frame, interior)
self.infoFrame.pack(expand = 1, fill = 'both')
# Create the Floater's label
self.label = self.createcomponent('label',
(), None,
Tkinter.Label, self.infoFrame,
text = self['text'],
anchor = 'center',
width = 12,
font = "Arial 12 bold")
self.label.pack(side='left', expand = 1, fill = 'x')
# Create an entry field to display and validate the floater's value
self.entry = self.createcomponent('entry',
(), None,
Tkinter.Entry, self.infoFrame,
width = 10,
justify = 'right',
textvar = self.entryValue)
self.entry.pack(side='left',padx = 4)
self.entry.bind('<Return>', self._entryCommand)
# Create the scale component.
self.scale = self.createcomponent('scale',
(), None,
Tkinter.Scale, interior,
command = self._scaleToVelocity,
orient = 'horizontal',
length = 150,
from_ = -1.0,
to = 1.0,
resolution = 0.01,
showvalue = 0)
self.scale.pack(expand = 1, fill = 'x')
self.scale.set(0.0)
# When interacting with mouse:
self.scale.bind('<Button-1>', self._startFloaterTask)
self.scale.bind('<ButtonRelease-1>', self._floaterReset)
# In case you wish to interact using keys
self.scale.bind('<KeyPress-Right>', self._floaterKeyCommand)
self.scale.bind('<KeyRelease-Right>', self._floaterReset)
self.scale.bind('<KeyPress-Left>', self._floaterKeyCommand)
self.scale.bind('<KeyRelease-Left>', self._floaterReset)
# Check keywords and initialise options.
self.initialiseoptions(Floater)
# Now that the widgets have been created, update significant digits
self._setSigDigits()
self._updateValue()
def _scaleToVelocity(self, strVal):
# convert scale val to float
val = string.atof(strVal)
# retain sign of velocity by only calling abs once
self.velocity = self['maxVelocity'] * val * abs(val)
def _startFloaterTask(self,event):
self._fFloaterTask = 1
self._floaterTask()
def _floaterTask(self):
if self.velocity != 0.0:
self.setValue( self.value + self.velocity )
if self._fFloaterTask:
self.after(50, self._floaterTask)
def _floaterReset(self, event):
self._fFloaterTask = 0
self.velocity = 0.0
self.scale.set(0.0)
def _floaterKeyCommand(self, event):
if self.velocity != 0.0:
self.setValue( self.value + self.velocity )
def _entryCommand(self, event = None):
try:
val = string.atof( self.entryValue.get() )
self.setValue( val )
except ValueError:
# invalid entry, ring bell set background to warning color
self.entry.bell()
if self.normalBackground is None:
self.normalBackground = self.entry.cget('background')
self.entry.configure( background = self['errorbackground'] )
def _updateValue(self):
self.setValue(self['value'])
def setValue(self, newVal):
if self['min'] is not None:
if newVal < self['min']:
newVal = self['min']
if self['max'] is not None:
if newVal > self['max']:
newVal = self['max']
if self['resolution'] is not None:
newVal = round(newVal / self['resolution']) * self['resolution']
# Update floater's value
self.value = newVal
# Update entry to reflect formatted value
self.entryValue.set( self.entryFormat % self.value )
# Reset background
if self.normalBackground is not None:
self.entry.configure(background = self.normalBackground)
self.normalBackground = None
# execute command
command = self['command']
if command is not None:
command( newVal )
def _setSigDigits(self):
sd = self['significantDigits']
self.entryFormat = '%.' + '%d' % sd + 'f'
self.scale['resolution'] = 10 ** (-1.0 * sd)
# And reset value to reflect change
self.entryValue.set( self.entryFormat % self.value )
def _updateLabelText(self):
self.label['text'] = self['text']
Pmw.forwardmethods(Floater, Tkinter.Scale, 'scale')
## SAMPLE CODE
if __name__ == '__main__':
# Initialise Tkinter and Pmw.
root = Pmw.initialise()
root.title('Pmw Floater demonstration')
# Dummy command
def printVal(val):
print `val`
# Create and pack a Floater megawidget.
mega1 = Floater(root)
mega1.pack(side = 'left', expand = 1, fill = 'x')
# These are things you can set/configure
mega1['command'] = printVal
mega1['text'] = 'Drive delta X'
mega1['min'] = 0.0
#mega1['max'] = 1000.0
mega1['resolution'] = 1.0
# UNCOMMENT THESE TO CUSTOMIZE THE FLOATER
# To change the color of the label:
# mega1.label['foreground'] = 'Red'
# Max change/update, default is 100
# To have really fine control, for example
# mega1['maxVelocity'] = 0.1
# Number of digits to the right of the decimal point, default = 2
# mega1['significantDigits'] = 5
# Starting value for floater
# mega1['value'] = 123.4557
# Let's go.
#root.mainloop()

View File

@ -0,0 +1,286 @@
from Tkinter import *
import Pmw
import floater
import string
import tkColorChooser
class VectorEntry(Pmw.MegaWidget):
def __init__(self, parent = None, **kw):
# Default vector size
DEFAULT_DIM = 3
# Default value depends on *actual* vector size, test for user input
DEFAULT_VALUE = [0.0] * kw.get('dim', DEFAULT_DIM)
DEFAULT_LABELS = map(lambda x: 'v[%d]' % x,
range(kw.get('dim', DEFAULT_DIM)))
# Process options
INITOPT = Pmw.INITOPT
optiondefs = (
('dim', DEFAULT_DIM, INITOPT),
('initialValue', DEFAULT_VALUE, INITOPT),
('label_width', 12, None),
('command', None, None),
('entryWidth', 8, self._updateEntryWidth),
('relief', GROOVE, self._updateRelief),
('bd', 2, self._updateBorderWidth),
('text', 'Vector:', self._updateText),
('min', None, self._updateValidate),
('max', None, self._updateValidate),
('significantDigits', 2, self._setSigDigits),
)
self.defineoptions(kw, optiondefs)
# Initialize superclass
Pmw.MegaWidget.__init__(self, parent)
# Initialize value
# Make sure its a list (and as a byproduct, make a distinct copy)
self._value = list(self['initialValue'])
self._floaters = None
self.entryFormat = '%.2f'
# Get a handle on the parent container
interior = self.interior()
# This does double duty as a menu button
self._label = self.createcomponent('label', (), None,
Menubutton, (interior,),
text = self['text'],
activebackground = '#909090')
self.menu = self._label['menu'] = Menu(self._label)
self.menu.add_command(label = 'Reset', command = self.reset)
self.menu.add_command(label = 'Popup sliders', command = self.popupSliders)
self._label.pack(side = LEFT, fill = X, ipadx = 2)
self.variableList = []
self.entryList = []
for index in range(self['dim']):
var = StringVar()
self.variableList.append(var)
# To set the configuration of all entrys in a vector use:
# ve.configure(Entry_XXX = YYY)
# To configure an individual entryfield's entry use:
# ve.configure(entry0_XXX = YYY)
entry = self.createcomponent(
'entryField%d' % index,
(('entry%d' % index,
'entryField%d_entry' % index),),
'Entry',
Pmw.EntryField, (interior,),
entry_justify = RIGHT,
entry_textvariable = var,
command = lambda s = self, i = index: s._entryUpdateAt(i))
entry.pack(side = LEFT, expand = 1, fill = X)
self.entryList.append(entry)
# To configure the floaterGroup use:
# ve.configure(floaterGroup_XXX = YYY)
# ve.configure(fGroup_XXX = YYY) or
# To set the configuration all floaters in a group use:
# ve.configure(Floater_XXX = YYY)
# To configure an individual floater in a group use:
# ve.configure(floaterGroup_floater0_XXX = YYY) or
# ve.configure(fGroup_floater0_XXX = YYY)
self._floaters = self.createcomponent(
'floaterGroup',
(('fGroup', 'floaterGroup'),
('Floater', 'floaterGroup_Floater'),), None,
floater.FloaterGroup, (self.interior(),),
dim = self['dim'], title = self['text'],
command = self.set)
# Note: This means the 'X' on the menu bar doesn't really destroy
# the panel, just withdraws it. This is to avoid problems which occur
# if the user kills the floaterGroup and then tries to pop it open again
self._floaters.userdeletefunc(self._floaters.withdraw)
self._floaters.withdraw()
# Make sure entries are updated
self.set(self['initialValue'])
# Make sure input variables processed
self.initialiseoptions(VectorEntry)
def menu(self):
return self.menu
def label(self):
return self._label
def entry(self, index):
return self.entryList[index]
def entryList(self):
return self.entryList
def floaters(self):
return self._floaters
def _clearFloaters(self):
self._floaters.withdraw()
def _updateText(self):
self._label['text'] = self['text']
def _updateRelief(self):
self.interior()['relief'] = self['relief']
def _updateBorderWidth(self):
self.interior()['bd'] = self['bd']
def _updateEntryWidth(self):
self['Entry_entry_width'] = self['entryWidth']
def _setSigDigits(self):
sd = self['significantDigits']
self.entryFormat = '%.' + '%d' % sd + 'f'
self.configure(Floater_significantDigits = sd)
# And refresh value to reflect change
for index in range(self['dim']):
self._refreshEntry(index)
def _updateValidate(self):
# Update entry field to respect new limits
self.configure(Entry_validate = {
'validator' : 'real',
'min' : self['min'],
'max' : self['max'],
'minstrict' : 0,
'maxstrict' : 0})
# Reflect changes in floaters
self.configure(Floater_min = self['min'],
Floater_max = self['max'])
def get(self):
return self._value
def getAt(self,index):
return self._value[index]
def set(self, value):
for i in range(self['dim']):
self._value[i] = value[i]
self.variableList[i].set(self.entryFormat % value[i])
self.action()
def setAt(self, index, value):
self.variableList[index].set(self.entryFormat % value)
self._value[index] = value
self.action()
def _entryUpdateAt(self, index):
entryVar = self.variableList[index]
# Did we get a valid float?
try:
newVal = string.atof(entryVar.get())
except ValueError:
return
# Clamp value
if self['min'] is not None:
if newVal < self['min']:
newVal = self['min']
if self['max'] is not None:
if newVal > self['max']:
newVal = self['max']
# Update vector's value
self._value[index] = newVal
# refresh entry to reflect formatted value
self._refreshEntry(index)
# Update the floaters and call the command
self.action()
def _refreshEntry(self,index):
self.variableList[index].set( self.entryFormat % self._value[index] )
self.entryList[index].checkentry()
def _refreshFloaters(self):
if self._floaters:
self._floaters.set(self._value, 0)
def action(self):
self._refreshFloaters()
if self['command']:
self['command'](self._value)
def reset(self):
self.set(self['initialValue'])
def addMenuItem(self, label = '', command = None):
self.menu.add_command(label = label, command = command)
def popupSliders(self):
self._floaters.set(self.get()[:])
self._floaters.show()
class Vector3Entry(VectorEntry):
def __init__(self, parent = None, **kw):
# Initialize options for the class
optiondefs = (
('dim', 3, Pmw.INITOPT),
('fGroup_labels', ('X','Y','Z'), None),
)
self.defineoptions(kw, optiondefs)
# Initialize the superclass, make sure dim makes it to superclass
VectorEntry.__init__(self, parent, dim = self['dim'])
# Needed because this method checks if self.__class__ is myClass
# where myClass is the argument passed into inialiseoptions
self.initialiseoptions(Vector3Entry)
class Vector4Entry(VectorEntry):
def __init__(self, parent = None, **kw):
# Initialize options for the class
optiondefs = (
('dim', 4, Pmw.INITOPT),
('fGroup_labels', ('X','Y','Z','W'), None),
)
self.defineoptions(kw, optiondefs)
# Initialize the superclass, make sure dim makes it to superclass
VectorEntry.__init__(self, parent, dim = self['dim'])
# Needed because this method checks if self.__class__ is myClass
# where myClass is the argument passed into inialiseoptions
self.initialiseoptions(Vector4Entry)
class ColorEntry(VectorEntry):
def __init__(self, parent = None, **kw):
# Initialize options for the class (overriding some superclass options)
optiondefs = (
('dim', 4, Pmw.INITOPT),
('fGroup_labels', ('R','G','B','A'), None),
('min', 0.0, None),
('max', 255.0, None),
('significantDigits', 0, None),
('Floater_resolution', 1.0, None),
)
self.defineoptions(kw, optiondefs)
# Initialize the superclass, make sure dim makes it to superclass
VectorEntry.__init__(self, parent, dim = self['dim'])
# Add menu item to popup color picker
self.addMenuItem(
'Popup color picker',
command = lambda s = self: s.popupColorPicker())
# Needed because this method checks if self.__class__ is myClass
# where myClass is the argument passed into inialiseoptions
self.initialiseoptions(ColorEntry)
def popupColorPicker(self):
# Can pass in current color with: color = (255, 0, 0)
color = tkColorChooser.askcolor(
parent = self.interior(),
# Initialize it to current color
initialcolor = tuple(self.get()[:3]))[0]
if color:
self.set((color[0], color[1], color[2], self.getAt(3)))
if __name__ == '__main__':
root = Toplevel()
root.title('Vector Widget demo')
ve = VectorEntry(root); ve.pack()
v3e = Vector3Entry(root); v3e.pack()
v4e = Vector4Entry(root); v4e.pack()
ce = ColorEntry(root); ce.pack()