Browse Source

*** empty log message ***

Mark Mine 24 years ago
parent
commit
235cd02cb7

+ 31 - 21
direct/src/leveleditor/LevelEditor.py

@@ -4435,7 +4435,7 @@ class LevelEditorPanel(Pmw.MegaToplevel):
         self.fUpdateSelected = 1
         # Handle to the toplevels hull
         hull = self.component('hull')
-        hull.geometry('400x550')
+        hull.geometry('400x515')
         
         balloon = self.balloon = Pmw.Balloon(hull)
         # Start with balloon help disabled
@@ -4737,63 +4737,73 @@ class LevelEditorPanel(Pmw.MegaToplevel):
         self.addKernFloater = Floater.Floater(
             gridFrame, 
             text='Kern',
-            maxVelocity=1.0,
+            #maxVelocity=1.0,
             command=self.setSignBaselineKern)
-        self.addKernFloater.grid(row=4, column=0, rowspan=2, columnspan=3)
+        self.addKernFloater.grid(row=4, column=0, rowspan=2, columnspan=3,
+                                 sticky = EW)
         self.addWiggleFloater = Floater.Floater(
             gridFrame, 
             text='Wiggle',
-            maxVelocity=10.0,
+            #maxVelocity=10.0,
             command=self.setSignBaselineWiggle)
-        self.addWiggleFloater.grid(row=6, column=0, rowspan=2, columnspan=3)
+        self.addWiggleFloater.grid(row=6, column=0, rowspan=2, columnspan=3,
+                                 sticky = EW)
         self.addStumbleFloater = Floater.Floater(
             gridFrame, 
             text='Stumble',
-            maxVelocity=1.0,
+            #maxVelocity=1.0,
             command=self.setSignBaselineStumble)
-        self.addStumbleFloater.grid(row=8, column=0, rowspan=2, columnspan=3)
+        self.addStumbleFloater.grid(row=8, column=0, rowspan=2, columnspan=3,
+                                 sticky = EW)
         self.addStompFloater = Floater.Floater(
             gridFrame, 
             text='Stomp',
-            maxVelocity=1.0,
+            #maxVelocity=1.0,
             command=self.setSignBaselineStomp)
-        self.addStompFloater.grid(row=10, column=0, rowspan=2, columnspan=3)
+        self.addStompFloater.grid(row=10, column=0, rowspan=2, columnspan=3,
+                                 sticky = EW)
         self.addCurveFloater = Floater.Floater(
             gridFrame, 
             text='Curve',
-            maxVelocity = 1.0,
+            #maxVelocity = 1.0,
             command=self.setSignBaselineCurve)
-        self.addCurveFloater.grid(row=12, column=0, rowspan=2, columnspan=3)
+        self.addCurveFloater.grid(row=12, column=0, rowspan=2, columnspan=3,
+                                 sticky = EW)
         self.addXFloater = Floater.Floater(
             gridFrame, 
             text='X',
-            maxVelocity=1.0,
+            #maxVelocity=1.0,
             command=self.setDNATargetX)
-        self.addXFloater.grid(row=4, column=3, rowspan=2, columnspan=3)
+        self.addXFloater.grid(row=4, column=3, rowspan=2, columnspan=3,
+                                 sticky = EW)
         self.addZFloater = Floater.Floater(
             gridFrame, 
             text='Z',
-            maxVelocity=1.0,
+            #maxVelocity=1.0,
             command=self.setDNATargetZ)
-        self.addZFloater.grid(row=6, column=3, rowspan=2, columnspan=3)
+        self.addZFloater.grid(row=6, column=3, rowspan=2, columnspan=3,
+                                 sticky = EW)
         self.addScaleXFloater = Floater.Floater(
             gridFrame, 
             text='Scale X',
-            maxVelocity=1.0,
+            #maxVelocity=1.0,
             command=self.setDNATargetScaleX)
-        self.addScaleXFloater.grid(row=8, column=3, rowspan=2, columnspan=3)
+        self.addScaleXFloater.grid(row=8, column=3, rowspan=2, columnspan=3,
+                                 sticky = EW)
         self.addScaleZFloater = Floater.Floater(
             gridFrame, 
             text='Scale Z',
-            maxVelocity=1.0,
+            #maxVelocity=1.0,
             command=self.setDNATargetScaleZ)
-        self.addScaleZFloater.grid(row=10, column=3, rowspan=2, columnspan=3)
+        self.addScaleZFloater.grid(row=10, column=3, rowspan=2, columnspan=3,
+                                 sticky = EW)
         self.addRollFloater = Floater.Floater(
             gridFrame, 
             text='Roll',
-            maxVelocity=10.0,
+            #maxVelocity=10.0,
             command=self.setDNATargetRoll)
-        self.addRollFloater.grid(row=12, column=3, rowspan=2, columnspan=3)
+        self.addRollFloater.grid(row=12, column=3, rowspan=2, columnspan=3,
+                                 sticky = EW)
 
         gridFrame.pack(fill=BOTH)
 

+ 12 - 11
direct/src/tkpanels/DirectSessionPanel.py

@@ -369,7 +369,7 @@ class DirectSessionPanel(AppShell):
         self.pConstantAttenuation = EntryScale.EntryScale(
             pointPage,
             text = 'Constant Attenuation',
-            min = 0.0, max = 1.0, initialValue = 1.0)
+            min = 0.0, max = 1.0, value = 1.0)
         self.pConstantAttenuation['command'] = self.setConstantAttenuation
         self.pConstantAttenuation.pack(fill = X, expand = 0)
         self.bind(self.pConstantAttenuation,
@@ -378,7 +378,7 @@ class DirectSessionPanel(AppShell):
         self.pLinearAttenuation = EntryScale.EntryScale(
             pointPage,
             text = 'Linear Attenuation',
-            min = 0.0, max = 1.0, initialValue = 0.0)
+            min = 0.0, max = 1.0, value = 0.0)
         self.pLinearAttenuation['command'] = self.setLinearAttenuation
         self.pLinearAttenuation.pack(fill = X, expand = 0)
         self.bind(self.pLinearAttenuation,
@@ -387,7 +387,7 @@ class DirectSessionPanel(AppShell):
         self.pQuadraticAttenuation = EntryScale.EntryScale(
             pointPage,
             text = 'Quadratic Attenuation',
-            min = 0.0, max = 1.0, initialValue = 0.0)
+            min = 0.0, max = 1.0, value = 0.0)
         self.pQuadraticAttenuation['command'] = self.setQuadraticAttenuation
         self.pQuadraticAttenuation.pack(fill = X, expand = 0)
         self.bind(self.pQuadraticAttenuation,
@@ -404,7 +404,7 @@ class DirectSessionPanel(AppShell):
         self.sConstantAttenuation = EntryScale.EntryScale(
             spotPage,
             text = 'Constant Attenuation',
-            min = 0.0, max = 1.0, initialValue = 1.0)
+            min = 0.0, max = 1.0, value = 1.0)
         self.sConstantAttenuation['command'] = self.setConstantAttenuation
         self.sConstantAttenuation.pack(fill = X, expand = 0)
         self.bind(self.sConstantAttenuation,
@@ -413,7 +413,7 @@ class DirectSessionPanel(AppShell):
         self.sLinearAttenuation = EntryScale.EntryScale(
             spotPage,
             text = 'Linear Attenuation',
-            min = 0.0, max = 1.0, initialValue = 0.0)
+            min = 0.0, max = 1.0, value = 0.0)
         self.sLinearAttenuation['command'] = self.setLinearAttenuation
         self.sLinearAttenuation.pack(fill = X, expand = 0)
         self.bind(self.sLinearAttenuation,
@@ -422,7 +422,7 @@ class DirectSessionPanel(AppShell):
         self.sQuadraticAttenuation = EntryScale.EntryScale(
             spotPage,
             text = 'Quadratic Attenuation',
-            min = 0.0, max = 1.0, initialValue = 0.0)
+            min = 0.0, max = 1.0, value = 0.0)
         self.sQuadraticAttenuation['command'] = self.setQuadraticAttenuation
         self.sQuadraticAttenuation.pack(fill = X, expand = 0)
         self.bind(self.sQuadraticAttenuation,
@@ -431,7 +431,7 @@ class DirectSessionPanel(AppShell):
         self.sExponent = EntryScale.EntryScale(
             spotPage,
             text = 'Exponent',
-            min = 0.0, max = 1.0, initialValue = 0.0)
+            min = 0.0, max = 1.0, value = 0.0)
         self.sExponent['command'] = self.setExponent
         self.sExponent.pack(fill = X, expand = 0)
         self.bind(self.sExponent,
@@ -478,7 +478,7 @@ class DirectSessionPanel(AppShell):
             gridPage,
             text = 'Grid Spacing',
             min = 0.1,
-            initialValue = direct.grid.getGridSpacing())
+            value = direct.grid.getGridSpacing())
         self.gridSpacing['command'] = direct.grid.setGridSpacing
         self.gridSpacing.pack(fill = X, expand = 0)
         
@@ -486,13 +486,14 @@ class DirectSessionPanel(AppShell):
             gridPage,
             text = 'Grid Size',
             min = 1.0,
-            initialValue = direct.grid.getGridSize())
+            value = direct.grid.getGridSize())
         self.gridSize['command'] = direct.grid.setGridSize
         self.gridSize.pack(fill = X, expand = 0)
 
         self.gridSnapAngle = Dial.AngleDial(
             gridPage,
             text = 'Snap Angle',
+            style = Dial.DIAL_MINI,
             value = direct.grid.getSnapAngle())
         self.gridSnapAngle['command'] = direct.grid.setSnapAngle
         self.gridSnapAngle.pack(fill = X, expand = 0)
@@ -544,7 +545,7 @@ class DirectSessionPanel(AppShell):
             self.jbXyzSF = EntryScale.EntryScale(
                 joyboxFrame,
                 text = 'XYZ Scale Factor',
-                initialValue = 1.0,
+                value = 1.0,
                 hull_relief = RIDGE, hull_borderwidth = 2,
                 min = 1.0, max = 100.0)
             self.jbXyzSF['command'] = (
@@ -555,7 +556,7 @@ class DirectSessionPanel(AppShell):
             self.jbHprSF = EntryScale.EntryScale(
                 joyboxFrame,
                 text = 'HPR Scale Factor',
-                initialValue = 1.0,
+                value = 1.0,
                 hull_relief = RIDGE, hull_borderwidth = 2,
                 min = 1.0, max = 100.0)
             self.jbHprSF['command'] = (

+ 23 - 25
direct/src/tkpanels/MopathRecorder.py

@@ -370,7 +370,7 @@ class MopathRecorder(AppShell, PandaObject):
             'Number of samples in resampled curve',
             resolution = 1, min = 2, max = 1000, command = self.setNumSamples)
         widget.component('hull')['relief'] = RIDGE
-        widget.onRelease = widget.onReturnRelease = self.sampleCurve
+        widget['preCallback'] = widget['postCallback'] = self.sampleCurve
 
         frame = Frame(resampleFrame)
         self.createButton(
@@ -395,7 +395,7 @@ class MopathRecorder(AppShell, PandaObject):
             min = 1, max = 100, resolution = 1,
             command = self.setDesampleFrequency)
         widget.component('hull')['relief'] = RIDGE
-        widget.onRelease = widget.onReturnRelease = self.desampleCurve
+        widget['postCallback'] = self.desampleCurve
         desampleFrame.pack(fill = X, expand = 0, pady = 2)
 
         ## REFINE PAGE ##
@@ -410,29 +410,28 @@ class MopathRecorder(AppShell, PandaObject):
                                        'Begin time of refine pass',
                                        resolution = 0.01,
                                        command = self.setRecordStart)
-        widget.onPress = self.setRefineMode
-        widget.onRelease = widget.onReturnRelease = (
-            lambda s = self: s.getPrePoints('Refine'))
+        widget['preCallback'] = self.setRefineMode
+        widget['postCallback'] = lambda s = self: s.getPrePoints('Refine')
         widget = self.createEntryScale(
             refineFrame, 'Refine Page',
             'Control Start',
             'Time when full control of node path is given during refine pass',
             resolution = 0.01,
             command = self.setControlStart)
-        widget.onPress = widget.onReturn = self.setRefineMode
+        widget['preCallback'] = self.setRefineMode
         widget = self.createEntryScale(
             refineFrame, 'Refine Page',
             'Control Stop',
             'Time when node path begins transition back to original curve',
             resolution = 0.01,
             command = self.setControlStop)
-        widget.onPress = widget.onReturn = self.setRefineMode
+        widget['preCallback'] = self.setRefineMode
         widget = self.createEntryScale(refineFrame, 'Refine Page', 'Refine To',
                                        'Stop time of refine pass',
                                        resolution = 0.01,
                                        command = self.setRefineStop)
-        widget.onPress = self.setRefineMode
-        widget.onRelease = widget.onReturnRelease = self.getPostPoints
+        widget['preCallback'] = self.setRefineMode
+        widget['postCallback'] = self.getPostPoints
         refineFrame.pack(fill = X)
 
         ## EXTEND PAGE ##
@@ -447,16 +446,15 @@ class MopathRecorder(AppShell, PandaObject):
                                        'Begin time of extend pass',
                                        resolution = 0.01,
                                        command = self.setRecordStart)
-        widget.onPress = self.setExtendMode
-        widget.onRelease = widget.onReturnRelease = (
-            lambda s = self: s.getPrePoints('Extend'))
+        widget['preCallback'] = self.setExtendMode
+        widget['postCallback'] = lambda s = self: s.getPrePoints('Extend')
         widget = self.createEntryScale(
             extendFrame, 'Extend Page',
             'Control Start',
             'Time when full control of node path is given during extend pass',
             resolution = 0.01,
             command = self.setControlStart)
-        widget.onPress = widget.onReturn = self.setExtendMode
+        widget['preCallback'] = self.setExtendMode
         extendFrame.pack(fill = X)
 
         ## CROP PAGE ##
@@ -529,48 +527,48 @@ class MopathRecorder(AppShell, PandaObject):
             sfFrame, 'Style', 'Num Segs',
             'Set number of segments used to approximate each parametric unit',
             min = 1.0, max = 400, resolution = 1.0,
-            initialValue = 40, 
+            value = 40, 
             command = self.setNumSegs, side = TOP)
         widget.component('hull')['relief'] = RIDGE
         widget = self.createEntryScale(
             sfFrame, 'Style', 'Num Ticks',
             'Set number of tick marks drawn for each unit of time',
             min = 0.0, max = 10.0, resolution = 1.0,
-            initialValue = 0.0,
+            value = 0.0,
             command = self.setNumTicks, side = TOP)
         widget.component('hull')['relief'] = RIDGE
         widget = self.createEntryScale(
             sfFrame, 'Style', 'Tick Scale',
             'Set visible size of time tick marks',
             min = 0.01, max = 100.0, resolution = 0.01,
-            initialValue = 5.0,
+            value = 5.0,
             command = self.setTickScale, side = TOP)
         widget.component('hull')['relief'] = RIDGE
         self.createColorEntry(
             sfFrame, 'Style', 'Path Color',
             'Color of curve',
             command = self.setPathColor,
-            initialValue = [255.0,255.0,255.0,255.0])
+            value = [255.0,255.0,255.0,255.0])
         self.createColorEntry(
             sfFrame, 'Style', 'Knot Color',
             'Color of knots',
             command = self.setKnotColor,
-            initialValue = [0,0,255.0,255.0])
+            value = [0,0,255.0,255.0])
         self.createColorEntry(
             sfFrame, 'Style', 'CV Color',
             'Color of CVs',
             command = self.setCvColor,
-            initialValue = [255.0,0,0,255.0])
+            value = [255.0,0,0,255.0])
         self.createColorEntry(
             sfFrame, 'Style', 'Tick Color',
             'Color of Ticks',
             command = self.setTickColor,
-            initialValue = [255.0,0,0,255.0])
+            value = [255.0,0,0,255.0])
         self.createColorEntry(
             sfFrame, 'Style', 'Hull Color',
             'Color of Hull',
             command = self.setHullColor,
-            initialValue = [255.0,128.0,128.0,255.0])
+            value = [255.0,128.0,128.0,255.0])
 
         #drawFrame.pack(fill = X)
 
@@ -585,7 +583,7 @@ class MopathRecorder(AppShell, PandaObject):
         widget = self.createLabeledEntry(
             frame, 'Recording', 'Record Hook',
             'Hook used to start/stop recording',
-            initialValue = self.startStopHook,
+            value = self.startStopHook,
             command = self.setStartStopHook)[0]
         label = self.getWidget('Recording', 'Record Hook-Label')
         label.configure(width = 16, anchor = W)
@@ -593,7 +591,7 @@ class MopathRecorder(AppShell, PandaObject):
         widget = self.createLabeledEntry(
             frame, 'Recording', 'Keyframe Hook',
             'Hook used to add a new keyframe',
-            initialValue = self.keyframeHook,
+            value = self.keyframeHook,
             command = self.setKeyframeHook)[0]
         label = self.getWidget('Recording', 'Keyframe Hook-Label')
         label.configure(width = 16, anchor = W)
@@ -1678,12 +1676,12 @@ class MopathRecorder(AppShell, PandaObject):
         return self.variableDict[category + '-' + text]
 
     def createLabeledEntry(self, parent, category, text, balloonHelp,
-                           initialValue = '', command = None,
+                           value = '', command = None,
                            relief = 'sunken', side = LEFT,
                            expand = 1, width = 12):
         frame = Frame(parent)
         variable = StringVar()
-        variable.set(initialValue)
+        variable.set(value)
         label = Label(frame, text = text)
         label.pack(side = LEFT, fill = X)
         self.bind(label, balloonHelp)

+ 15 - 16
direct/src/tkpanels/ParticlePanel.py

@@ -20,7 +20,7 @@ class ParticlePanel(AppShell):
     # Override class variables
     appname = 'Particle Panel'
     frameWidth  = 375
-    frameHeight = 775
+    frameHeight = 575
     usecommandarea = 0
     usestatusarea  = 0
     balloonState = 'both'
@@ -374,7 +374,7 @@ class ParticlePanel(AppShell):
         self.createVector3Entry(boxPage, 'Box Emitter', 'Max',
                                 'Max point defining emitter box',
                                 command = self.setEmitterBoxPoint2,
-                                initialValue = (1.0, 1.0, 1.0))
+                                value = (1.0, 1.0, 1.0))
         # Disc page #
         discPage = self.emitterNotebook.add('DiscEmitter')
         self.createFloater(discPage, 'Disc Emitter', 'Radius',
@@ -408,7 +408,7 @@ class ParticlePanel(AppShell):
         self.createVector3Entry(linePage, 'Line Emitter', 'Max',
                                 'Max point defining emitter line',
                                 command = self.setEmitterLinePoint2,
-                                initialValue = (1.0, 0.0, 0.0))
+                                value = (1.0, 0.0, 0.0))
         # Point page #
         emitterPointPage = self.emitterNotebook.add('PointEmitter')
         self.createVector3Entry(emitterPointPage, 'Point Emitter', 'Position',
@@ -736,13 +736,11 @@ class ParticlePanel(AppShell):
 
     def createFloater(self, parent, category, text, balloonHelp,
                       command = None, min = 0.0, resolution = None,
-                      significantDigits = 3,
-                      maxVelocity = 10.0, **kw):
+                      numDigits = 3, **kw):
         kw['text'] = text
         kw['min'] = min
-        kw['maxVelocity'] = maxVelocity
-        kw['resolution'] = resolution
-        kw['significantDigits'] = significantDigits
+        kw['floater_resolution'] = resolution
+        kw['numDigits'] = numDigits
         widget = apply(Floater.Floater, (parent,), kw)
         # Do this after the widget so command isn't called on creation
         widget['command'] = command
@@ -754,6 +752,7 @@ class ParticlePanel(AppShell):
     def createAngleDial(self, parent, category, text, balloonHelp,
                         command = None, **kw):
         kw['text'] = text
+        kw['style'] = Dial.DIAL_MINI
         widget = apply(Dial.AngleDial,(parent,), kw)
         # Do this after the widget so command isn't called on creation
         widget['command'] = command
@@ -1771,7 +1770,7 @@ class ParticlePanel(AppShell):
         self.createFloater(frame, pageName, forceName + ' Amplitude',
                            'Force amplitude multiplier',
                            command = setAmplitude,
-                           initialValue = force.getAmplitude())
+                           value = force.getAmplitude())
         cbf = Frame(frame, relief = FLAT)
         self.createCheckbutton(cbf, pageName, forceName + ' Mass Dependent',
                                ('On: force depends on mass; ' +
@@ -1808,7 +1807,7 @@ class ParticlePanel(AppShell):
         self.createVector3Entry(frame, pageName, forceName,
                                 'Set force direction and magnitude',
                                 command = setVec,
-                                initialValue = [vec[0], vec[1], vec[2]])
+                                value = [vec[0], vec[1], vec[2]])
         self.createForceActiveWidget(frame, pageName, forceName, force)
 
     def createLinearRandomForceWidget(self, forcePage, pageName, count,
@@ -1828,7 +1827,7 @@ class ParticlePanel(AppShell):
         self.createFloater(frame, pageName, forceName + ' Coef',
                            'Set linear friction force',
                            command = setCoef, min = None,
-                           initialValue = force.getCoef())
+                           value = force.getCoef())
         self.createForceActiveWidget(frame, pageName, forceName, force)
 
     def createLinearCylinderVortexForceWidget(self, forcePage, pageName,
@@ -1845,15 +1844,15 @@ class ParticlePanel(AppShell):
         self.createFloater(frame, pageName, forceName + ' Coef',
                            'Set linear cylinder vortex coefficient',
                            command = setCoef,
-                           initialValue = force.getCoef())
+                           value = force.getCoef())
         self.createFloater(frame, pageName, forceName + ' Length',
                            'Set linear cylinder vortex length',
                            command = setLength,
-                           initialValue = force.getLength())
+                           value = force.getLength())
         self.createFloater(frame, pageName, forceName + ' Radius',
                            'Set linear cylinder vortex radius',
                            command = setRadius,
-                           initialValue = force.getRadius())
+                           value = force.getRadius())
         self.createForceActiveWidget(frame, pageName, forceName, force)
 
     def createLinearDistanceForceWidget(self, forcePage, pageName,
@@ -1896,12 +1895,12 @@ class ParticlePanel(AppShell):
                                 'Set center of force',
                                 command = setForceCenter,
                                 label_width = 16,
-                                initialValue = [vec[0], vec[1], vec[2]])
+                                value = [vec[0], vec[1], vec[2]])
         self.createFloater(frame, pageName, forceName + ' Radius',
                            'Set falloff radius',
                            command = setRadius,
                            min = 0.01,
-                           initialValue = force.getRadius())
+                           value = force.getRadius())
         self.createForceActiveWidget(frame, pageName, forceName, force)
 
 ######################################################################

+ 60 - 90
direct/src/tkpanels/Placer.py

@@ -18,8 +18,8 @@ class Placer(AppShell):
     # Override class variables here
     appname = 'Placer Panel'
     frameWidth      = 625
-    frameHeight     = 290
-    usecommandarea = 1
+    frameHeight     = 215
+    usecommandarea = 0
     usestatusarea  = 0
 
     def __init__(self, parent = None, **kw):
@@ -82,6 +82,7 @@ class Placer(AppShell):
     def createInterface(self):
         # The interior of the toplevel panel
         interior = self.interior()
+        interior['relief'] = FLAT
         # Add placer commands to menubar
         self.menuBar.addmenu('Placer', 'Placer Panel Operations')
         self.menuBar.addmenuitem('Placer', 'command',
@@ -159,20 +160,16 @@ class Placer(AppShell):
         self.redoButton.pack(side = 'left', expand = 0)
         self.bind(self.redoButton, 'Redo last operation')
 
-        # The master frame for the dials
-        dialFrame = Frame(interior)
-        dialFrame.pack(fill = 'both', expand = 1)
-        
         # Create and pack the Pos Controls
-        posGroup = Pmw.Group(dialFrame,
+        posGroup = Pmw.Group(interior,
                              tag_pyclass = Menubutton,
                              tag_text = 'Position',
-                             tag_font=('MSSansSerif', 14, 'bold'),
+                             tag_font=('MSSansSerif', 14),
                              tag_activebackground = '#909090',
-                             ring_relief = 'flat')
+                             ring_relief = RIDGE)
         posMenubutton = posGroup.component('tag')
         self.bind(posMenubutton, 'Position menu operations')
-        posMenu = Menu(posMenubutton)
+        posMenu = Menu(posMenubutton, tearoff = 0)
         posMenu.add_command(label = 'Set to zero', command = self.zeroPos)
         posMenu.add_command(label = 'Reset initial',
                             command = self.resetPos)
@@ -183,56 +180,50 @@ class Placer(AppShell):
         # Create the dials
         self.posX = self.createcomponent('posX', (), None,
                                          Floater.Floater, (posInterior,),
-                                         text = 'X',
-                                         initialValue = 0.0,
+                                         text = 'X', relief = FLAT,
+                                         value = 0.0,
                                          label_foreground = 'Red')
         self.posX['command'] = self.xform
         self.posX['commandData'] = ['x']
+        self.posX['preCallback'] = self.xformStart
+        self.posX['postCallback'] = self.xformStop
         self.posX['callbackData'] = ['x']
-        self.posX.onReturn = self.xformStart
-        self.posX.onReturnRelease = self.xformStop
-        self.posX.onPress = self.xformStart
-        self.posX.onRelease = self.xformStop
         self.posX.pack(expand=1,fill='both')
         
         self.posY = self.createcomponent('posY', (), None,
                                          Floater.Floater, (posInterior,),
-                                         text = 'Y',
-                                         initialValue = 0.0,
+                                         text = 'Y', relief = FLAT,
+                                         value = 0.0,
                                          label_foreground = '#00A000')
         self.posY['command'] = self.xform
         self.posY['commandData'] = ['y']
+        self.posY['preCallback'] = self.xformStart
+        self.posY['postCallback'] = self.xformStop
         self.posY['callbackData'] = ['y']
-        self.posY.onReturn = self.xformStart
-        self.posY.onReturnRelease = self.xformStop
-        self.posY.onPress = self.xformStart
-        self.posY.onRelease = self.xformStop
         self.posY.pack(expand=1,fill='both')
         
         self.posZ = self.createcomponent('posZ', (), None,
                                          Floater.Floater, (posInterior,),
-                                         text = 'Z',
-                                         initialValue = 0.0,
+                                         text = 'Z', relief = FLAT,
+                                         value = 0.0,
                                          label_foreground = 'Blue')
         self.posZ['command'] = self.xform
         self.posZ['commandData'] = ['z']
+        self.posZ['preCallback'] = self.xformStart
+        self.posZ['postCallback'] = self.xformStop
         self.posZ['callbackData'] = ['z']
-        self.posZ.onReturn = self.xformStart
-        self.posZ.onReturnRelease = self.xformStop
-        self.posZ.onPress = self.xformStart
-        self.posZ.onRelease = self.xformStop
         self.posZ.pack(expand=1,fill='both')
 
         # Create and pack the Hpr Controls
-        hprGroup = Pmw.Group(dialFrame,
+        hprGroup = Pmw.Group(interior,
                              tag_pyclass = Menubutton,
                              tag_text = 'Orientation',
-                             tag_font=('MSSansSerif', 14, 'bold'),
+                             tag_font=('MSSansSerif', 14),
                              tag_activebackground = '#909090',
-                             ring_relief = 'flat')
+                             ring_relief = RIDGE)
         hprMenubutton = hprGroup.component('tag')
         self.bind(hprMenubutton, 'Orientation menu operations')
-        hprMenu = Menu(hprMenubutton)
+        hprMenu = Menu(hprMenubutton, tearoff = 0)
         hprMenu.add_command(label = 'Set to zero', command = self.zeroHpr)
         hprMenu.add_command(label = 'Reset initial', command = self.resetHpr)
         hprMenubutton['menu'] = hprMenu
@@ -242,41 +233,41 @@ class Placer(AppShell):
         # Create the dials
         self.hprH = self.createcomponent('hprH', (), None,
                                          Dial.AngleDial, (hprInterior,),
+                                         style = Dial.DIAL_MINI,
                                          text = 'H', value = 0.0,
+                                         relief = FLAT,
                                          label_foreground = 'blue')
         self.hprH['command'] = self.xform
         self.hprH['commandData'] = ['h']
+        self.hprH['preCallback'] = self.xformStart
+        self.hprH['postCallback'] = self.xformStop
         self.hprH['callbackData'] = ['h']
-        self.hprH['onReturnPress'] = self.xformStart
-        self.hprH['onReturnRelease'] = self.xformStop
-        self.hprH['onButtonPress'] = self.xformStart
-        self.hprH['onButtonRelease'] = self.xformStop
         self.hprH.pack(expand=1,fill='both')
         
         self.hprP = self.createcomponent('hprP', (), None,
                                          Dial.AngleDial, (hprInterior,),
+                                         style = Dial.DIAL_MINI,
                                          text = 'P', value = 0.0,
+                                         relief = FLAT,
                                          label_foreground = 'red')
         self.hprP['command'] = self.xform
         self.hprP['commandData'] = ['p']
+        self.hprP['preCallback'] = self.xformStart
+        self.hprP['postCallback'] = self.xformStop
         self.hprP['callbackData'] = ['p']
-        self.hprP['onReturnPress'] = self.xformStart
-        self.hprP['onReturnRelease'] = self.xformStop
-        self.hprP['onButtonPress'] = self.xformStart
-        self.hprP['onButtonRelease'] = self.xformStop
         self.hprP.pack(expand=1,fill='both')
         
         self.hprR = self.createcomponent('hprR', (), None,
                                          Dial.AngleDial, (hprInterior,),
+                                         style = Dial.DIAL_MINI,
                                          text = 'R', value = 0.0,
+                                         relief = FLAT,
                                          label_foreground = '#00A000')
         self.hprR['command'] = self.xform
         self.hprR['commandData'] = ['r']
+        self.hprR['preCallback'] = self.xformStart
+        self.hprR['postCallback'] = self.xformStop
         self.hprR['callbackData'] = ['r']
-        self.hprR['onReturnPress'] = self.xformStart
-        self.hprR['onReturnRelease'] = self.xformStop
-        self.hprR['onButtonPress'] = self.xformStart
-        self.hprR['onButtonRelease'] = self.xformStop
         self.hprR.pack(expand=1,fill='both')
 
         # Create and pack the Scale Controls
@@ -284,18 +275,18 @@ class Placer(AppShell):
         self.scalingMode = StringVar()
         self.scalingMode.set('Scale Uniform')
         # The scaling widgets
-        scaleGroup = Pmw.Group(dialFrame,
+        scaleGroup = Pmw.Group(interior,
                                tag_text = 'Scale Uniform',
                                tag_pyclass = Menubutton,
-                               tag_font=('MSSansSerif', 14, 'bold'),
+                               tag_font=('MSSansSerif', 14),
                                tag_activebackground = '#909090',
-                               ring_relief = 'flat')
+                               ring_relief = RIDGE)
         self.scaleMenubutton = scaleGroup.component('tag')
         self.bind(self.scaleMenubutton, 'Scale menu operations')
         self.scaleMenubutton['textvariable'] = self.scalingMode
 
         # Scaling menu
-        scaleMenu = Menu(self.scaleMenubutton)
+        scaleMenu = Menu(self.scaleMenubutton, tearoff = 0)
         scaleMenu.add_command(label = 'Set to unity',
                               command = self.unitScale)
         scaleMenu.add_command(label = 'Reset initial',
@@ -313,48 +304,45 @@ class Placer(AppShell):
         
         # Create the dials
         self.scaleX = self.createcomponent('scaleX', (), None,
-                                           Dial.Dial, (scaleInterior,),
+                                           Floater.Floater, (scaleInterior,),
                                            text = 'X Scale',
+                                           relief = FLAT,
                                            min = 0.0001, value = 1.0,
                                            resetValue = 1.0,
                                            label_foreground = 'Red')
         self.scaleX['command'] = self.xform
         self.scaleX['commandData'] = ['sx']
         self.scaleX['callbackData'] = ['sx']
-        self.scaleX['onReturnPress'] = self.xformStart
-        self.scaleX['onReturnRelease'] = self.xformStop
-        self.scaleX['onButtonPress'] = self.xformStart
-        self.scaleX['onButtonRelease'] = self.xformStop
+        self.scaleX['preCallback'] = self.xformStart
+        self.scaleX['postCallback'] = self.xformStop
         self.scaleX.pack(expand=1,fill='both')
         
         self.scaleY = self.createcomponent('scaleY', (), None,
-                                           Dial.Dial, (scaleInterior,),
+                                           Floater.Floater, (scaleInterior,),
                                            text = 'Y Scale',
+                                           relief = FLAT,
                                            min = 0.0001, value = 1.0,
                                            resetValue = 1.0,
                                            label_foreground = '#00A000')
         self.scaleY['command'] = self.xform
         self.scaleY['commandData'] = ['sy']
         self.scaleY['callbackData'] = ['sy']
-        self.scaleY['onReturnPress'] = self.xformStart
-        self.scaleY['onReturnRelease'] = self.xformStop
-        self.scaleY['onButtonPress'] = self.xformStart
-        self.scaleY['onButtonRelease'] = self.xformStop
+        self.scaleY['preCallback'] = self.xformStart
+        self.scaleY['postCallback'] = self.xformStop
         self.scaleY.pack(expand=1,fill='both')
         
         self.scaleZ = self.createcomponent('scaleZ', (), None,
-                                           Dial.Dial, (scaleInterior,),
+                                           Floater.Floater, (scaleInterior,),
                                            text = 'Z Scale',
+                                           relief = FLAT,
                                            min = 0.0001, value = 1.0,
                                            resetValue = 1.0,
                                            label_foreground = 'Blue')
         self.scaleZ['command'] = self.xform
         self.scaleZ['commandData'] = ['sz']
         self.scaleZ['callbackData'] = ['sz']
-        self.scaleZ['onReturnPress'] = self.xformStart
-        self.scaleZ['onReturnRelease'] = self.xformStop
-        self.scaleZ['onButtonPress'] = self.xformStart
-        self.scaleZ['onButtonRelease'] = self.xformStop
+        self.scaleZ['preCallback'] = self.xformStart
+        self.scaleZ['postCallback'] = self.xformStop
         self.scaleZ.pack(expand=1,fill='both')
 
         # Make sure appropriate labels are showing
@@ -363,33 +351,6 @@ class Placer(AppShell):
         self.selectNodePathNamed('init')
         self.selectRefNodePathNamed('parent')
 
-        self.createButtons()
-
-    def createButtons(self):
-        self.buttonAdd('Zero All',
-                       helpMessage='Zero Node Path',
-                       statusMessage='Zero Node Path',
-                       command=self.zeroAll)
-        self.buttonAdd('Reset All',
-                       helpMessage='Reset Node Path',
-                       statusMessage='Reset Node Path',
-                       command=self.resetAll)
-        self.buttonAdd('Print Info',
-                       helpMessage='Print Node Path Info',
-                       statusMessage='Print Node Path Info',
-                       command=self.printNodePathInfo)
-        self.buttonAdd('Toggle Widget Viz',
-                       helpMessage='Toggle Object Handles Visability',
-                       statusMessage='Toggle Object Handles Visability',
-                       command=direct.toggleWidgetVis)
-        self.buttonAdd(
-            'Toggle Widget Mode',
-            helpMessage='Toggle Widget Move/COA Mode',
-            statusMessage='Toggle Widget Move/COA Mode',
-            command=direct.manipulationControl.toggleObjectHandlesMode)
-        
-        # Make all buttons as wide as widest
-        self.alignbuttons()
 
     ### WIDGET OPERATIONS ###
     def setMovementMode(self, movementMode):
@@ -715,8 +676,17 @@ class Placer(AppShell):
 
     def updateResetValues(self, nodePath):
         self.initPos.assign(nodePath.getPos())
+        self.posX['resetValue'] = self.initPos[0]
+        self.posY['resetValue'] = self.initPos[1]
+        self.posZ['resetValue'] = self.initPos[2]
         self.initHpr.assign(nodePath.getHpr())
+        self.hprH['resetValue'] = self.initHpr[0]
+        self.hprP['resetValue'] = self.initHpr[1]
+        self.hprR['resetValue'] = self.initHpr[2]
         self.initScale.assign(nodePath.getScale())
+        self.scaleX['resetValue'] = self.initScale[0]
+        self.scaleY['resetValue'] = self.initScale[1]
+        self.scaleZ['resetValue'] = self.initScale[2]
 
     def resetAll(self):
         if self['nodePath']:

+ 2 - 2
direct/src/tkwidgets/AppShell.py

@@ -328,14 +328,14 @@ class AppShell(Pmw.MegaWidget, PandaObject):
         return widget
 
     def newCreateLabeledEntry(self, parent, category, text, help = '', 
-                              command = None, initialValue = '',
+                              command = None, value = '',
                               width = 12, relief = SUNKEN,
                               side = LEFT, fill = X, expand = 0):
         """ createLabeledEntry(parent, category, text, [options]) """
         # Create labeled entry
         frame = Frame(parent)
         variable = StringVar()
-        variable.set(initialValue)
+        variable.set(value)
         label = Label(frame, text = text)
         label.pack(side = LEFT, fill = X, expand = 0)
         entry = Entry(frame, width = width, relief = relief,

+ 96 - 65
direct/src/tkwidgets/Dial.py

@@ -1,6 +1,6 @@
 from Tkinter import *
 from PandaModules import ClockObject
-from WidgetPropertiesDialog import *
+import WidgetPropertiesDialog
 import Pmw
 import Task
 import math
@@ -17,7 +17,7 @@ DIAL_FULL = 'full'
 DIAL_MINI = 'mini'
 
 DIAL_FULL_SIZE = 45
-DIAL_MINI_SIZE = 20
+DIAL_MINI_SIZE = 30
 
 globalClock = ClockObject.getGlobalClock()
 
@@ -29,13 +29,8 @@ class Dial(Pmw.MegaWidget):
     def __init__(self, parent = None, **kw):
         #define the megawidget options
         INITOPT = Pmw.INITOPT
-        if 'full' == kw.get('style', DIAL_FULL):
-            DIAL_SIZE = DIAL_FULL_SIZE
-        else:
-            DIAL_SIZE = DIAL_MINI_SIZE
         optiondefs = (
             ('style',             DIAL_FULL,      INITOPT),
-            ('dial_size',         DIAL_SIZE,      None),
             # Widget relief
             ('relief',            GROOVE,         None),
             # Widget borderwidth
@@ -46,21 +41,30 @@ class Dial(Pmw.MegaWidget):
             ('numDigits',         2,              self.setEntryFormat),
             ('command',           None,           None),
             ('commandData',       [],             None),
-            ('callbackData',      [],             self.setCallbackData),
             ('min',               None,           self.setMin),
             ('max',               None,           self.setMax),
             ('base',              0.0,            self.setBase),
             ('delta',             1.0,            self.setDelta),
-            ('onReturnPress',     None,           None),
-            ('onReturnRelease',   None,           None),
-            ('onButtonPress',     None,           self.setButtonPressCmd),
-            ('onButtonRelease',   None,           self.setButtonReleaseCmd),
+            # Callbacks to execute when updating widget's value
+            ('preCallback',       None,           self.setPreCallbackCmd),
+            ('postCallback',      None,           self.setPostCallbackCmd),
+            # Extra data to be passed to callback function, needs to be a list
+            ('callbackData',      [],             self.setCallbackData),
             )
         self.defineoptions(kw, optiondefs)
         
         # Initialize the superclass
         Pmw.MegaWidget.__init__(self, parent)
 
+        # Override size if style specified by size is not
+        if not kw.has_key('dial_size'):
+            if self['style'] == DIAL_FULL:
+                dialSize = DIAL_FULL_SIZE
+            else:
+                dialSize = DIAL_MINI_SIZE
+        else:
+            dialSize = DIAL_FULL_SIZE
+
         # Create the components
         interior = self.interior()
         interior.configure(relief = self['relief'], bd = self['borderwidth'])
@@ -68,15 +72,24 @@ class Dial(Pmw.MegaWidget):
         # The Dial 
         self._dial = self.createcomponent('dial', (), None,
                                           DialWidget, (interior,),
+                                          size = dialSize,
                                           command = self.setEntry,
                                           value = self['value'])
 
-        self._dial.propertyDict['numDigits'] = {
-            'widget' : self,
-            'type' : 'integer',
-            'help' : 'Enter number of digits after decimal point.'
-            }
-        self._dial.propertyList.append('numDigits')
+        self._dial.addPropertyToDialog(
+            'text',
+            {'widget' : self,
+             'type' : 'string',
+             'help' : 'Enter label text for Dial.'
+             }
+            )
+        self._dial.addPropertyToDialog(
+            'numDigits',
+            {'widget' : self,
+             'type' : 'integer',
+             'help' : 'Enter number of digits after decimal point.'
+             }
+            )
 
         # The Label
         self._label = self.createcomponent('label', (), None,
@@ -99,14 +112,14 @@ class Dial(Pmw.MegaWidget):
 
         if self['style'] == DIAL_FULL:
             # Attach dial to entry
-            self._dial.grid(rowspan = 2, columnspan = 2)
+            self._dial.grid(rowspan = 2, columnspan = 2, padx = 2, pady = 2)
             self._label.grid(row = 0, col = 2, sticky = EW)
             self._entry.grid(row = 1, col = 2, sticky = EW)
             interior.columnconfigure(2, weight = 1)
         else:
             self._label.grid(row=0,col=0, sticky = EW)
             self._entry.grid(row=0,col=1, sticky = EW)
-            self._dial.grid(row=0,col=2)
+            self._dial.grid(row=0,col=2, padx = 2, pady = 2)
             interior.columnconfigure(0, weight = 1)
 
         # Make sure input variables processed 
@@ -144,13 +157,13 @@ class Dial(Pmw.MegaWidget):
 
     def _onReturnPress(self, *args):
         """ User redefinable callback executed on <Return> in entry """
-        if self['onReturnPress']:
-            apply(self['onReturnPress'], self['callbackData'])
+        if self['preCallback']:
+            apply(self['preCallback'], self['callbackData'])
 
     def _onReturnRelease(self, *args):
         """ User redefinable callback executed on <Return> release in entry """
-        if self['onReturnRelease']:
-            apply(self['onReturnRelease'], self['callbackData'])
+        if self['postCallback']:
+            apply(self['postCallback'], self['callbackData'])
 
     # Pass settings down to dial
     def setCallbackData(self):
@@ -175,11 +188,12 @@ class Dial(Pmw.MegaWidget):
     def setLabel(self):
         self._label['text'] = self['text']
 
-    def setButtonPressCmd(self):
-        self._dial['onButtonPress'] = self['onButtonPress']
+    def setPreCallbackCmd(self):
+        self._dial['preCallback'] = self['preCallback']
+
+    def setPostCallbackCmd(self):
+        self._dial['postCallback'] = self['postCallback']
 
-    def setButtonReleaseCmd(self):
-        self._dial['onButtonRelease'] = self['onButtonRelease']
 
 class AngleDial(Dial):
     def __init__(self, parent = None, **kw):
@@ -237,10 +251,11 @@ class DialWidget(Pmw.MegaWidget):
             ('command',         None,           None),
             # Extra data to be passed to command function
             ('commandData',     [],             None),
-            # Extra data to be passed to callback function
+            # Callback's to execute during mouse interaction
+            ('preCallback',     None,           None),
+            ('postCallback',    None,           None),
+            # Extra data to be passed to callback function, needs to be a list
             ('callbackData',    [],             None),
-            ('onButtonPress',   None,           None),
-            ('onButtonRelease', None,           None),
             )
         self.defineoptions(kw, optiondefs)
         
@@ -262,31 +277,6 @@ class DialWidget(Pmw.MegaWidget):
         # Radius of the inner knob
         inner_radius = max(3,radius * INNER_SF)
 
-        # A Dictionary of dictionaries
-        self.propertyDict = {
-            'min' : { 'widget' : self,
-                      'type' : 'real',
-                      'fNone' : 1,
-                      'help' : 'Minimum allowable dial value, Enter None for no minimum'},
-            'max' : { 'widget' : self,
-                      'type' : 'real',
-                      'fNone' : 1,
-                      'help' : 'Maximum allowable dial value, Enter None for no maximum'},
-            'base' : { 'widget' : self,
-                       'type' : 'real',
-                       'help' : 'Dial value = base + delta * numRevs'},
-            'delta' : { 'widget' : self,
-                        'type' : 'real',
-                        'help' : 'Dial value = base + delta * numRevs'},
-            'numSegments' : { 'widget' : self,
-                              'type' : 'integer',
-                              'help' : 'Number of segments to divide dial into'},
-            'resetValue' : { 'widget' : self,
-                             'type' : 'real',
-                             'help' : 'Enter value to set dial to on reset.'}
-            }
-        self.propertyList = ['min', 'max', 'base', 'delta', 'numSegments', 'resetValue']
-
         # The canvas 
         self._canvas = self.createcomponent('canvas', (), None,
                                             Canvas, (interior,),
@@ -312,6 +302,32 @@ class DialWidget(Pmw.MegaWidget):
                                  fill = '#A0A0A0',
                                  tags = ('knob',))
 
+        # A Dictionary of dictionaries used for the popup property dialog
+        self.propertyDict = {
+            'min' : { 'widget' : self,
+                      'type' : 'real',
+                      'fNone' : 1,
+                      'help' : 'Minimum allowable dial value, Enter None for no minimum'},
+            'max' : { 'widget' : self,
+                      'type' : 'real',
+                      'fNone' : 1,
+                      'help' : 'Maximum allowable dial value, Enter None for no maximum'},
+            'base' : { 'widget' : self,
+                       'type' : 'real',
+                       'help' : 'Dial value = base + delta * numRevs'},
+            'delta' : { 'widget' : self,
+                        'type' : 'real',
+                        'help' : 'Dial value = base + delta * numRevs'},
+            'numSegments' : { 'widget' : self,
+                              'type' : 'integer',
+                              'help' : 'Number of segments to divide dial into'},
+            'resetValue' : { 'widget' : self,
+                             'type' : 'real',
+                             'help' : 'Enter value to set dial to on reset.'}
+            }
+        self.propertyList = ['min', 'max', 'base', 'delta',
+                             'resetValue', 'numSegments']
+
         # The popup menu
         self._popupMenu = Menu(interior, tearoff = 0)
         self._fSnap = IntVar()
@@ -325,8 +341,11 @@ class DialWidget(Pmw.MegaWidget):
             self._popupMenu.add_checkbutton(label = 'Rollover',
                                             variable = self._fRollover,
                                             command = self.setRollover)
-            self._popupMenu.add_command(label = 'Properties...',
-                                        command = self.getProperties)
+            self._popupMenu.add_command(
+                label = 'Properties...',
+                command = self.popupPropertiesDialog)
+        self._popupMenu.add_command(label = 'Zero Dial',
+                                    command = self.zero)
         self._popupMenu.add_command(label = 'Reset Dial',
                                     command = self.reset)
 
@@ -375,6 +394,14 @@ class DialWidget(Pmw.MegaWidget):
         # Record value
         self.value = value
     
+    # Set floater to zero
+    def zero(self):
+        """
+        self.reset()
+        Set dial to zero
+        """
+        self.set(0.0)
+
     # Reset dial to reset value
     def reset(self):
         """
@@ -384,6 +411,7 @@ class DialWidget(Pmw.MegaWidget):
         self.set(self['resetValue'])
 
     def mouseReset(self,event):
+        # If not over any canvas item
         if not self._canvas.find_withtag(CURRENT):
             self.reset()
         
@@ -465,7 +493,6 @@ class DialWidget(Pmw.MegaWidget):
         # Update value
         currT = globalClock.getFrameTime()
         dt = currT - state.lastTime
-        #self.set(self.value + self['delta'] * self.knobSF * dt)
         self.set(self.value + self.knobSF * dt)
         state.lastTime = currT
         return Task.cont
@@ -548,24 +575,28 @@ class DialWidget(Pmw.MegaWidget):
         self['fRollover'] = self._fRollover.get()
 
     # This handles the popup dial min dialog
-    def getProperties(self):
+    def popupPropertiesDialog(self):
         # Popup dialog to adjust widget properties
-        WidgetPropertiesDialog(
+        WidgetPropertiesDialog.WidgetPropertiesDialog(
             self.propertyDict,
             propertyList = self.propertyList,
             title = 'Dial Widget Properties',
             parent = self._canvas)
+
+    def addPropertyToDialog(self, property, pDict):
+        self.propertyDict[property] = pDict
+        self.propertyList.append(property)
             
     # User callbacks
     def _onButtonPress(self, *args):
         """ User redefinable callback executed on button press """
-        if self['onButtonPress']:
-            apply(self['onButtonPress'], self['callbackData'])
+        if self['preCallback']:
+            apply(self['preCallback'], self['callbackData'])
 
     def _onButtonRelease(self, *args):
         """ User redefinable callback executed on button release """
-        if self['onButtonRelease']:
-            apply(self['onButtonRelease'], self['callbackData'])
+        if self['postCallback']:
+            apply(self['postCallback'], self['callbackData'])
 
   
 if __name__ == '__main__':

+ 29 - 21
direct/src/tkwidgets/EntryScale.py

@@ -19,14 +19,16 @@ class EntryScale(Pmw.MegaWidget):
 
         # Define the megawidget options.
         optiondefs = (
-            ('initialValue',        0.0,           Pmw.INITOPT),
+            ('value',        0.0,           Pmw.INITOPT),
             ('resolution',          0.001,         None),
             ('command',             None,          None),
+            ('preCallback',         None,          None),
+            ('postCallback',        None,          None),
             ('callbackData',        [],            None),
             ('min',                 0.0,           self._updateValidate),
             ('max',                 100.0,         self._updateValidate),
             ('text',                'EntryScale',  self._updateLabelText),
-            ('significantDigits',   2,             self._setSigDigits),
+            ('numDigits',   2,             self._setSigDigits),
             )
         self.defineoptions(kw, optiondefs)
  
@@ -34,7 +36,7 @@ class EntryScale(Pmw.MegaWidget):
         Pmw.MegaWidget.__init__(self, parent)
 
         # Initialize some class variables
-        self.value = self['initialValue']
+        self.value = self['value']
         self.entryFormat = '%.2f'
         self.fScaleCommand = 0
 
@@ -49,7 +51,7 @@ class EntryScale(Pmw.MegaWidget):
                                                Frame, interior)
         # Create an entry field to display and validate the entryScale's value
         self.entryValue = StringVar()
-        self.entryValue.set(self['initialValue'])
+        self.entryValue.set(self['value'])
         self.entry = self.createcomponent('entryField',
                                           # Access widget's entry using "entry"
                                           (('entry', 'entryField_entry'),),
@@ -105,7 +107,7 @@ class EntryScale(Pmw.MegaWidget):
                                           showvalue = 0)
         self.scale.pack(side = 'left', expand = 1, fill = 'x')
         # Set scale to the middle of its range
-        self.scale.set(self['initialValue'])
+        self.scale.set(self['value'])
         self.scale.bind('<Button-1>', self.__onPress)
         self.scale.bind('<ButtonRelease-1>', self.__onRelease)
         self.scale.bind('<Button-3>', self.askForResolution)
@@ -218,7 +220,7 @@ class EntryScale(Pmw.MegaWidget):
             pass
 
     def _setSigDigits(self):
-        sd = self['significantDigits']
+        sd = self['numDigits']
         self.entryFormat = '%.' + '%d' % sd + 'f'
         # And reset value to reflect change
         self.entryValue.set( self.entryFormat % self.value )
@@ -260,7 +262,8 @@ class EntryScale(Pmw.MegaWidget):
 
     def __onPress(self, event):
         # First execute onpress callback
-        apply(self.onPress, self['callbackData'])
+        if self['preCallback']:
+            apply(self['preCallback'], self['callbackData'])
         # Now enable slider command
         self.fScaleCommand = 1
 
@@ -272,7 +275,8 @@ class EntryScale(Pmw.MegaWidget):
         # Now disable slider command
         self.fScaleCommand = 0
         # First execute onpress callback
-        apply(self.onRelease, self['callbackData'])
+        if self['postCallback']:
+            apply(self['postCallback'], self['callbackData'])
 
     def onRelease(self, *args):
         """ User redefinable callback executed on button release """
@@ -295,9 +299,11 @@ class EntryScaleGroup(Pmw.MegaToplevel):
             ('side',            TOP,                    INITOPT),
             ('title',           'Group',                None),
             # A tuple of initial values, one for each entryScale
-            ('initialValue',    DEFAULT_VALUE,          INITOPT),
+            ('value',    DEFAULT_VALUE,          INITOPT),
             # The command to be executed any time one of the entryScales is updated
             ('command',         None,                   None),
+            ('preCallback',     None,                   None),
+            ('postCallback',    None,                   None),
             # A tuple of labels, one for each entryScale
             ('labels',          DEFAULT_LABELS,         self._updateLabels),
             # Destroy or withdraw
@@ -311,7 +317,7 @@ class EntryScaleGroup(Pmw.MegaToplevel):
         # Create the components
         interior = self.interior()
         # Get a copy of the initial value (making sure its a list)
-        self._value = list(self['initialValue'])
+        self._value = list(self['value'])
 
         # The Menu Bar
         self.balloon = Pmw.Balloon()
@@ -349,7 +355,7 @@ class EntryScaleGroup(Pmw.MegaToplevel):
             #   fg.configure(Valuator_XXX = YYY)
             f = self.createcomponent(
                 'entryScale%d' % index, (), 'Valuator', EntryScale,
-                (interior,), initialValue = self._value[index],
+                (interior,), value = self._value[index],
                 text = self['labels'][index])
             # Do this separately so command doesn't get executed during construction
             f['command'] = lambda val, s=self, i=index: s._entryScaleSetAt(i, val)
@@ -357,13 +363,13 @@ class EntryScaleGroup(Pmw.MegaToplevel):
             # Callbacks
             f.onReturn = self.__onReturn
             f.onReturnRelease = self.__onReturnRelease
-            f.onPress = self.__onPress
-            f.onRelease = self.__onRelease
+            f['preCallback'] = self.__onPress
+            f['postCallback'] = self.__onRelease
             f.pack(side = self['side'], expand = 1, fill = X)
             self.entryScaleList.append(f)
 
         # Make sure entryScales are initialized
-        self.set(self['initialValue'])
+        self.set(self['value'])
         
         # Make sure input variables processed 
         self.initialiseoptions(EntryScaleGroup)
@@ -405,7 +411,7 @@ class EntryScaleGroup(Pmw.MegaToplevel):
             self['command'](self._value)
 
     def reset(self):
-        self.set(self['initialValue'])
+        self.set(self['value'])
 
     def __onReturn(self, esg):
         # Execute onReturn callback
@@ -425,7 +431,8 @@ class EntryScaleGroup(Pmw.MegaToplevel):
 
     def __onPress(self, esg):
         # Execute onPress callback
-        apply(self.onPress, esg.get())
+        if self['preCallback']:
+            apply(self['preCallback'], esg.get())
 
     def onPress(self, *args):
         """ User redefinable callback executed on button press """
@@ -433,7 +440,8 @@ class EntryScaleGroup(Pmw.MegaToplevel):
 
     def __onRelease(self, esg):
         # Execute onRelease callback
-        apply(self.onRelease, esg.get())
+        if self['postCallback']:
+            apply(self['postCallback'], esg.get())
 
     def onRelease(self, *args):
         """ User redefinable callback executed on button release """
@@ -455,7 +463,7 @@ def rgbPanel(nodePath, callback = None):
     esg = EntryScaleGroup(title = 'RGBA Panel: ' + nodePath.getName(),
                           dim = 4,
                           labels = ['R','G','B','A'],
-                          initialValue = [int(initColor[0]),
+                          value = [int(initColor[0]),
                                           int(initColor[1]),
                                           int(initColor[2]),
                                           int(initColor[3])],
@@ -499,7 +507,7 @@ def rgbPanel(nodePath, callback = None):
     # Set callback
     def onRelease(r,g,b,a, nodePath = nodePath):
         messenger.send('RGBPanel_setColor', [nodePath, r,g,b,a])
-    esg.onRelease = onRelease
+    esg['postCallback'] = onRelease
     return esg
 
 ## SAMPLE CODE
@@ -519,7 +527,7 @@ if __name__ == '__main__':
     """
     # These are things you can set/configure
     # Starting value for entryScale    
-    mega1['initialValue'] = 123.456
+    mega1['value'] = 123.456
     mega1['text'] = 'Drive delta X'
     mega1['min'] = 0.0
     mega1['max'] = 1000.0
@@ -530,7 +538,7 @@ if __name__ == '__main__':
     # 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
+    # mega1['numDigits'] = 5
     """
 
     # To create a entryScale group to set an RGBA value:

+ 404 - 187
direct/src/tkwidgets/Floater.py

@@ -5,214 +5,431 @@ Floater Class: Velocity style controller for floating point values with
 from PandaObject import *
 from Tkinter import *
 import Pmw
+import WidgetPropertiesDialog
 import string
 
-class Floater(Pmw.MegaWidget):
-    "Velocity style floating point controller"
- 
-    def __init__(self, parent = None, **kw):
+globalClock = ClockObject.getGlobalClock()
+
+
+FLOATER_FULL = 'full'
+FLOATER_MINI = 'mini'
+
+FLOATER_WIDTH = 25
+FLOATER_HEIGHT = 20
 
-        # Define the megawidget options.
+class FloaterWidget(Pmw.MegaWidget):
+    sfBase = 3.0
+    sfDist = 15
+    deadband = 10
+    def __init__(self, parent = None, **kw):
+        #define the megawidget options
+        INITOPT = Pmw.INITOPT
         optiondefs = (
-            ('initialValue',        0.0,        Pmw.INITOPT),
-            ('resolution',          None,       None),
-            ('command',             None,       None),
-            ('commandData',         [],         None),
-            ('callbackData',        [],         None),
-            ('maxVelocity',         10.0,       None),
-            ('min',                 None,       self._updateValidate),
-            ('max',                 None,       self._updateValidate),
-            ('text',                'Floater',  self._updateLabelText),
-            ('significantDigits',   2,          self._setSigDigits),
+            ## Appearance
+            # Edge size of the floater
+            ('width',           FLOATER_WIDTH,  INITOPT),
+            ('height',          FLOATER_HEIGHT, INITOPT),
+            # Color
+            ('background',      'white',        INITOPT),
+            # Widget relief
+            ('relief',          SUNKEN,         self.setRelief),
+            # Widget borderwidth
+            ('borderwidth',     2,              self.setBorderwidth),
+            ## Values
+            # Initial value of floater, use self.set to change value
+            ('value',           0.0,            INITOPT),
+            ('min',             None,           None),
+            ('max',             None,           None),
+            ('resolution',      None,           None),
+            ('numDigits',       2,              self.setNumDigits),
+            # Value floater jumps to on reset
+            ('resetValue',      0.0,            None),
+            ## Behavior
+            # Able to adjust max/min
+            ('fAdjustable',     1,              None),
+            # Command to execute on floater updates
+            ('command',         None,           None),
+            # Extra data to be passed to command function
+            ('commandData',     [],             None),
+            # Callback's to execute during mouse interaction
+            ('preCallback',   None,           None),
+            ('postCallback', None,           None),
+            # Extra data to be passed to callback function, needs to be a list
+            ('callbackData',    [],             None),
             )
         self.defineoptions(kw, optiondefs)
- 
-        # Initialise superclass
-        Pmw.MegaWidget.__init__(self, parent)
-
-        # Initialize some class variables
-        self.value = self['initialValue']
-        self.velocity = 0.0
-        self.entryFormat = '%.2f'
 
-        # Create the components.
-
-        # Setup up container
-        interior = self.interior()
-        interior.configure(relief = GROOVE, borderwidth = 2)
-
-        # Create a label and an entry
-        self.labelFrame = self.createcomponent('frame', (), None,
-                                               Frame, interior)
-        # Create an entry field to display and validate the floater's value
-        self.entryValue = StringVar()
-        self.entryValue.set(self['initialValue'])
-        self.entry = self.createcomponent('entryField',
-                                          # Access floaters entry using "entry"
-                                          (('entry', 'entryField_entry'),),
-                                          None,
-                                          Pmw.EntryField, self.labelFrame,
-                                          entry_width = 10,
-                                          validate = { 'validator' : 'real',
-                                                       'min' : self['min'],
-                                                       'max' : self['max'],
-                                                       'minstrict' : 0,
-                                                       'maxstrict' : 0},
-                                          entry_justify = 'right',
-                                          entry_textvar = self.entryValue,
-                                          command = self._entryCommand)
-        self.entry.pack(side='left',padx = 4)
-                                          
-        # Create the Floater's label
-        self.label = self.createcomponent('label', (), None,
-                                          Label, self.labelFrame,
-                                          text = self['text'],
-                                          width = 12,
-                                          anchor = 'center',
-                                          font = "Arial 10 bold")
-        self.label.pack(side='left', expand = 1, fill = 'x')
-
-        # Now pack the frame
-        self.labelFrame.pack(expand = 1, fill = 'both')
-
-        # Create the scale component.
-        self.scale = self.createcomponent('scale', (), None,
-                                          Scale, interior,
-                                          command = self._scaleToVelocity,
-                                          orient = 'horizontal',
-                                          length = 150,
-                                          from_ = -1.0,
-                                          to = 1.0,
-                                          resolution = 0.001,
-                                          showvalue = 0)
-        self.scale.pack(expand = 1, fill = 'x')
-        # Set scale to the middle of its range
-        self.scale.set(0.0)
+        #print 'FLOATER WIDGET', self['resetValue']
         
-        # Add scale bindings: 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 based on input values.
-        self.initialiseoptions(Floater)
+        # Initialize the superclass
+        Pmw.MegaWidget.__init__(self, parent)
 
-    def label(self):
-        return self.label
-    def scale(self):
-        return self.scale
-    def entry(self):
-        return self.entry
-    
-    def _updateLabelText(self):
-        self.label['text'] = self['text']
-
-    def _updateValidate(self):
-        self.configure(entryField_validate = {
-            'validator' : 'real',
-            'min' : self['min'],
-            'max' : self['max'],
-            'minstrict' : 0,
-            'maxstrict' : 0})
-
-    def _scaleToVelocity(self, strVal):
-        # convert scale val to float
-        val = string.atof(strVal)
-        # Square val, but retain sign of velocity by only calling abs once
-        self.velocity = self['maxVelocity'] * val * abs(val)
-
-    def _startFloaterTask(self,event):
-        self._fFloaterTask = 1
-        apply(self.onPress,self['callbackData'])
-        taskMgr.add(self._floaterTask, 'floaterTask')
-
-    def _floaterTask(self, state):
-        if self.velocity != 0.0:
-            self.set( self.value + self.velocity )
-        return Task.cont
+        # Set up some local and instance variables        
+        # Current value
+        self.value = self['value']
 
-    def _floaterReset(self, event):
-        taskMgr.remove('floaterTask')
-        self.velocity = 0.0
-        self.scale.set(0.0)
-        apply(self.onRelease, self['callbackData'])
+        # Create the components
+        interior = self.interior()
 
-    def _floaterKeyCommand(self, event):
-        if self.velocity != 0.0:
-            self.set( self.value + self.velocity )
+        # The canvas
+        width = self['width']
+        height = self['height']
+        self._canvas = self.createcomponent('canvas', (), None,
+                                            Canvas, (interior,),
+                                            width = self['width'],
+                                            height = self['height'],
+                                            background = self['background'],
+                                            highlightthickness = 0,
+                                            scrollregion = (-width/2.0,
+                                                            -height/2.0,
+                                                            width/2.0,
+                                                            height/2.0))
+        self._canvas.pack(expand = 1, fill = BOTH)
+
+        # The floater icon
+        self._canvas.create_polygon(-width/2.0, 0, -2.0, -height/2.0,
+                                    -2.0, height/2.0,
+                                    fill = '#A0A0A0',
+                                    tags = ('floater',))
+        self._canvas.create_polygon(width/2.0, 0, 2.0, height/2.0,
+                                    2.0, -height/2.0,
+                                    fill = '#A0A0A0',
+                                    tags = ('floater',))
+
+        # A Dictionary of dictionaries for the popup property dialog
+        self.propertyDict = {
+            'min' : { 'widget' : self,
+                      'type' : 'real',
+                      'fNone' : 1,
+                      'help' : 'Minimum allowable floater value, Enter None for no minimum'},
+            'max' : { 'widget' : self,
+                      'type' : 'real',
+                      'fNone' : 1,
+                      'help' : 'Maximum allowable floater value, Enter None for no maximum'},
+            'resetValue' : { 'widget' : self,
+                             'type' : 'real',
+                             'help' : 'Enter value to set floater to on reset.'}
+            }
+        self.propertyList = ['min', 'max', 'resetValue']
+
+        # The popup menu
+        self._popupMenu = Menu(interior, tearoff = 0)
+
+        if self['fAdjustable']:
+            self._popupMenu.add_command(
+                label = 'Properties...',
+                command = self.popupPropertiesDialog)
+        self._popupMenu.add_command(label = 'Zero Floater',
+                                    command = self.zero)
+        self._popupMenu.add_command(label = 'Reset Floater',
+                                    command = self.reset)
+
+        # Add event bindings
+        self._canvas.bind('<ButtonPress-1>', self.mouseDown)
+        self._canvas.bind('<B1-Motion>', self.mouseMotion)
+        self._canvas.bind('<ButtonRelease-1>', self.mouseUp)
+        self._canvas.bind('<ButtonPress-3>', self.popupFloaterMenu)
+        self._canvas.bind('<Double-ButtonPress-1>', self.mouseReset)
+        self._canvas.bind('<ButtonPress-3>', self.popupFloaterMenu)
+        self._canvas.bind('<Enter>', self.highlightIcon)
+        self._canvas.bind('<Leave>', self.restoreIcon)
+        self._canvas.tag_bind('floater', '<ButtonPress-1>', self.mouseDown)
+        self._canvas.tag_bind('floater', '<B1-Motion>', self.mouseMotion)
+        self._canvas.tag_bind('floater', '<ButtonRelease-1>', self.mouseUp)
 
-    def _entryCommand(self, event = None):
-        try:
-            val = string.atof( self.entryValue.get() )
-            apply(self.onReturn,self['callbackData'])
-            self.set( val )
-            apply(self.onReturnRelease,self['callbackData'])
-        except ValueError:
-            pass
-
-    def _setSigDigits(self):
-        sd = self['significantDigits']
-        self.entryFormat = '%.' + '%d' % sd + 'f'
-        # And reset value to reflect change
-        self.entryValue.set( self.entryFormat % self.value )
+        # Make sure input variables processed 
+        self.initialiseoptions(FloaterWidget)
 
-    def get(self):
-        return self.value
-    
-    def set(self, newVal, fCommand = 1):
+    def set(self, value, fCommand = 1):
+        """
+        self.set(value, fCommand = 1)
+        Set floater to new value, execute command if fCommand == 1
+        """
         # Clamp value
         if self['min'] is not None:
-            if newVal < self['min']:
-                newVal = self['min']
+            if value < self['min']:
+                value = self['min']
         if self['max'] is not None:
-            if newVal > self['max']:
-                newVal = self['max']
+            if value > self['max']:
+                value = self['max']
         # Round by resolution
         if self['resolution'] is not None:
-            newVal = round(newVal / self['resolution']) * self['resolution']
+            value = round(value / self['resolution']) * self['resolution']
+
+        # Send command if any
+        if fCommand and (self['command'] != None):
+            apply(self['command'], [value] + self['commandData'])
+        # Record value
+        self.value = value
+    
+    # Set floater to zero
+    def zero(self):
+        """
+        self.reset()
+        Set floater to zero
+        """
+        self.set(0.0)
+
+    # Reset floater to reset value
+    def reset(self):
+        """
+        self.reset()
+        Reset floater to reset value
+        """
+        self.set(self['resetValue'])
+
+    def mouseReset(self,event):
+        # If not over any canvas item
+        #if not self._canvas.find_withtag(CURRENT):
+        self.reset()
         
-        # Update floater's value
-        self.value = newVal
-        # Update entry to reflect formatted value
-        self.entryValue.set( self.entryFormat % self.value )
-        self.entry.checkentry()
+    def get(self):
+        """
+        self.get()
+        Get current floater value
+        """
+        return self.value
+
+    ## Canvas callback functions
+    # Floater velocity controller
+    def mouseDown(self,event):
+        self._onButtonPress()
+        self.velocitySF = 0.0
+        t = taskMgr.add(self.computeVelocity, 'cv')
+        t.lastTime = globalClock.getFrameTime()
+
+    def computeVelocity(self, state):
+        # Update value
+        currT = globalClock.getFrameTime()
+        dt = currT - state.lastTime
+        self.set(self.value + self.velocitySF * dt)
+        state.lastTime = currT
+        return Task.cont
+
+    def mouseMotion(self, event):
+        # What is the current knob angle
+        self.velocitySF = self.computeVelocitySF(event)
+
+    def computeVelocitySF(self, event):
+        x = self._canvas.canvasx(event.x)
+        y = self._canvas.canvasy(event.y)
+        offset = max(0, abs(x) - FloaterWidget.deadband)
+        if offset == 0:
+            return 0
+        sf = math.pow(FloaterWidget.sfBase,
+                      self.minExp + offset/FloaterWidget.sfDist)
+        if x > 0:
+            return sf
+        else:
+            return -sf
+
+    def mouseUp(self, event):
+        taskMgr.remove('cv')
+        self.velocitySF = 0.0
+        self._onButtonRelease()
+
+    def highlightIcon(self, event):
+        self._canvas.itemconfigure('floater', fill = 'black')
+
+    def restoreIcon(self, event):
+        self._canvas.itemconfigure('floater', fill = '#A0A0A0')
+
+    # Methods to modify floater characteristics    
+    def setRelief(self):
+        self.interior()['relief'] = self['relief']
+
+    def setBorderwidth(self):
+        self.interior()['borderwidth'] = self['borderwidth']
+
+    def setNumDigits(self):
+        # Set minimum exponent to use in velocity task
+        self.minExp = math.floor(-self['numDigits']/
+                                 math.log10(FloaterWidget.sfBase))        
+
+    # The following methods are used to handle the popup menu
+    def popupFloaterMenu(self,event):
+        self._popupMenu.post(event.widget.winfo_pointerx(),
+                             event.widget.winfo_pointery())
+
+    # Popup dialog to adjust widget properties
+    def popupPropertiesDialog(self):
+        WidgetPropertiesDialog.WidgetPropertiesDialog(
+            self.propertyDict,
+            propertyList = self.propertyList,
+            title = 'Floater Widget Properties',
+            parent = self._canvas)
+
+    def addPropertyToDialog(self, property, pDict):
+        self.propertyDict[property] = pDict
+        self.propertyList.append(property)
+            
+    # User callbacks
+    def _onButtonPress(self, *args):
+        """ User redefinable callback executed on button press """
+        if self['preCallback']:
+            apply(self['preCallback'], self['callbackData'])
+
+    def _onButtonRelease(self, *args):
+        """ User redefinable callback executed on button release """
+        if self['postCallback']:
+            apply(self['postCallback'], self['callbackData'])
+
+
+class Floater(Pmw.MegaWidget):
+    def __init__(self, parent = None, **kw):
+        #define the megawidget options
+        INITOPT = Pmw.INITOPT
+        optiondefs = (
+            # Widget relief
+            ('relief',            GROOVE,         None),
+            # Widget borderwidth
+            ('borderwidth',       2,              None),
+            ('value',             0.0,            INITOPT),
+            ('resetValue',        0.0,            self.setResetValue),
+            ('text',              'Floater',      self.setLabel),
+            ('numDigits',         2,              self.setEntryFormat),
+            ('command',           None,           None),
+            ('commandData',       [],             None),
+            ('min',               None,           self.setMin),
+            ('max',               None,           self.setMax),
+            # Callbacks to execute when updating widget's value
+            ('preCallback',     None,           self.setButtonPressCmd),
+            ('postCallback',   None,           self.setButtonReleaseCmd),
+            # Extra data to be passed to callback function, needs to be a list
+            ('callbackData',      [],             self.setCallbackData),
+            )
+        self.defineoptions(kw, optiondefs)
         
-        # execute command
-        if fCommand and (self['command'] is not None):
-            apply(self['command'], [newVal] + self['commandData'])
+        # Initialize the superclass
+        Pmw.MegaWidget.__init__(self, parent)
 
-    def reset(self):
-        self.set(self['initialValue'])
+        # Create the components
+        interior = self.interior()
+        interior.configure(relief = self['relief'], bd = self['borderwidth'])
+        
+        # The Floater
+        #print self['text'], self['value'], self['resetValue']
+        self._floater = self.createcomponent('floater', (), None,
+                                             FloaterWidget, (interior,),
+                                             command = self.setEntry,
+                                             resetValue = self['value'],
+                                             value = self['value'])
+
+        if not kw.has_key('resetValue'):
+            self['resetValue'] = self['value']
+        self._floater.addPropertyToDialog(
+            'text',
+            {'widget' : self,
+             'type' : 'string',
+             'help' : 'Enter label text for Floater.'
+             }
+            )
+        self._floater.addPropertyToDialog(
+            'numDigits',
+            {'widget' : self,
+             'type' : 'integer',
+             'help' : 'Enter number of digits after decimal point.'
+             }
+            )
 
-    def disable(self):
-        self.scale['state'] = 'disabled'
-        self.label['state'] = 'disabled'
-        self.component('entry')['state'] = 'disabled'
+        # The Label
+        self._label = self.createcomponent('label', (), None,
+                                           Label, (interior,),
+                                           text = self['text'],
+                                           font = ('MS Sans Serif',12,'bold'),
+                                           anchor = CENTER)
+        self._label.bind('<ButtonPress-3>', self._floater.popupFloaterMenu)
+
+        # The entry
+        self._entryVal = StringVar()
+        self._entry = self.createcomponent('entry', (), None,
+                                           Entry, (interior,),
+                                           justify = RIGHT,
+                                           width = 12,
+                                           textvariable = self._entryVal)
+        self._entry.bind('<Return>', self.validateEntryInput)
+        self._entry.bind('<ButtonPress-3>', self._floater.popupFloaterMenu)
+        self._entryBackground = self._entry.cget('background')
+
+        # Position components
+        self._label.grid(row=0,col=0, sticky = EW)
+        self._entry.grid(row=0,col=1, sticky = EW)
+        self._floater.grid(row=0,col=2, padx = 2, pady = 2)
+        interior.columnconfigure(0, weight = 1)
+
+        # Make sure input variables processed
+        self.fInit = 0
+        self.initialiseoptions(Floater)
+        self.fInit = 1
+
+    def set(self, value, fCommand = 1):
+        # Pass fCommand to user specified data (to control if command
+        # is executed or not) to floater which will return it to self.setEntry
+        self._floater['commandData'] = [fCommand]
+        self._floater.set(value)
+        # Restore commandData to 1 so that interaction via floater widget
+        # will result in command being executed, otherwise a set with
+        # commandData == 0 will stick and commands will not be executed
+        self._floater['commandData'] = [1]
+        
+    def get(self):
+        return self._floater.get()
 
-    def enable(self):
-        self.scale['state'] = 'normal'
-        self.label['state'] = 'normal'
-        self.component('entry')['state'] = 'normal'
+    def setEntry(self, value, fCommand = 1):
+        self._entryVal.set(self.entryFormat % value)
+        # Execute command
+        if self.fInit and fCommand and (self['command'] != None):
+            apply(self['command'], [value] + self['commandData'])
+
+    def setEntryFormat(self):
+        self.entryFormat = "%." + "%df" % self['numDigits']
+        self.setEntry(self.get())
+        self._floater['numDigits'] = self['numDigits']
+
+    def validateEntryInput(self, event):
+        input = self._entryVal.get()
+        try:
+            self._onReturnPress()
+            self._entry.configure(background = self._entryBackground)
+            newValue = string.atof(input)
+            self.set(newValue)
+            self._onReturnRelease()
+        except ValueError:
+            self._entry.configure(background = 'Pink')
 
-    def onReturn(self, *args):
+    def _onReturnPress(self, *args):
         """ User redefinable callback executed on <Return> in entry """
-        pass
+        if self['preCallback']:
+            apply(self['preCallback'], self['callbackData'])
 
-    def onReturnRelease(self, *args):
+    def _onReturnRelease(self, *args):
         """ User redefinable callback executed on <Return> release in entry """
-        pass
+        if self['postCallback']:
+            apply(self['postCallback'], self['callbackData'])
 
-    def onPress(self, *args):
-        """ User redefinable callback executed on button press """
-        pass
+    # Pass settings down to floater
+    def setCallbackData(self):
+        # Pass callback data down to floater
+        self._floater['callbackData'] = self['callbackData']
+
+    def setResetValue(self):
+        self._floater['resetValue'] = self['resetValue']
+
+    def setMin(self):
+        self._floater['min'] = self['min']
+
+    def setMax(self):
+        self._floater['max'] = self['max']
+
+    def setLabel(self):
+        self._label['text'] = self['text']
+
+    def setButtonPressCmd(self):
+        self._floater['preCallback'] = self['preCallback']
+
+    def setButtonReleaseCmd(self):
+        self._floater['postCallback'] = self['postCallback']
 
-    def onRelease(self, *args):
-        """ User redefinable callback executed on button release """
-        pass
 
 class FloaterGroup(Pmw.MegaToplevel):
     def __init__(self, parent = None, **kw):
@@ -231,7 +448,7 @@ class FloaterGroup(Pmw.MegaToplevel):
             ('side',            TOP,                    INITOPT),
             ('title',           'Floater Group',        None),
             # A tuple of initial values, one for each floater
-            ('initialValue',    DEFAULT_VALUE,          INITOPT),
+            ('value',    DEFAULT_VALUE,          INITOPT),
             # The command to be executed any time one of the floaters is updated
             ('command',         None,                   None),
             # A tuple of labels, one for each floater
@@ -245,7 +462,7 @@ class FloaterGroup(Pmw.MegaToplevel):
         # Create the components
         interior = self.interior()
         # Get a copy of the initial value (making sure its a list)
-        self._value = list(self['initialValue'])
+        self._value = list(self['value'])
 
         # The Menu Bar
         self.balloon = Pmw.Balloon()
@@ -279,7 +496,7 @@ class FloaterGroup(Pmw.MegaToplevel):
             #   fg.configure(Valuator_XXX = YYY)
             f = self.createcomponent(
                 'floater%d' % index, (), 'Valuator', Floater,
-                (interior,), initialValue = self._value[index],
+                (interior,), value = self._value[index],
                 text = self['labels'][index])
             # Do this separately so command doesn't get executed during construction
             f['command'] = lambda val, s=self, i=index: s._floaterSetAt(i, val)
@@ -287,7 +504,7 @@ class FloaterGroup(Pmw.MegaToplevel):
             self.floaterList.append(f)
 
         # Make sure floaters are initialized
-        self.set(self['initialValue'])
+        self.set(self['value'])
         
         # Make sure input variables processed 
         self.initialiseoptions(FloaterGroup)
@@ -329,9 +546,9 @@ class FloaterGroup(Pmw.MegaToplevel):
             self['command'](self._value)
 
     def reset(self):
-        self.set(self['initialValue'])
-
+        self.set(self['value'])
 
+  
 ## SAMPLE CODE
 if __name__ == '__main__':
     # Initialise Tkinter and Pmw.
@@ -349,7 +566,7 @@ if __name__ == '__main__':
     """
     # These are things you can set/configure
     # Starting value for floater    
-    mega1['initialValue'] = 123.456
+    mega1['value'] = 123.456
     mega1['text'] = 'Drive delta X'
     mega1['min'] = 0.0
     mega1['max'] = 1000.0
@@ -360,7 +577,7 @@ if __name__ == '__main__':
     # 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
+    # mega1['numDigits'] = 5
     """
 
     # To create a floater group to set an RGBA value:

+ 12 - 8
direct/src/tkwidgets/VectorWidgets.py

@@ -20,7 +20,7 @@ class VectorEntry(Pmw.MegaWidget):
         INITOPT = Pmw.INITOPT
         optiondefs = (
             ('dim',                 DEFAULT_DIM,    INITOPT),
-            ('initialValue',        DEFAULT_VALUE,  INITOPT),
+            ('value',        DEFAULT_VALUE,  INITOPT),
             ('resetValue',          DEFAULT_VALUE,  None),
             ('label_width',         12,             None),
             ('command',             None,           None),
@@ -30,7 +30,7 @@ class VectorEntry(Pmw.MegaWidget):
             ('text',                'Vector:',      self._updateText),
             ('min',                 None,           self._updateValidate),
             ('max',                 None,           self._updateValidate),
-            ('significantDigits',   2,              self._setSigDigits),
+            ('numDigits',   2,              self._setSigDigits),
             ('valuatorType',        VALUATOR,       None),
             ('state',               'normal',       self._setState),
             )
@@ -41,8 +41,8 @@ class VectorEntry(Pmw.MegaWidget):
 
         # Initialize value
         # Make sure its a list (and as a byproduct, make a distinct copy)
-        self._value = list(self['initialValue'])
-        self['resetValue'] = self['initialValue']
+        self._value = list(self['value'])
+        self['resetValue'] = self['value']
         self._floaters = None
         self.entryFormat = '%.2f'
 
@@ -103,7 +103,7 @@ class VectorEntry(Pmw.MegaWidget):
                                              
 
         # Make sure entries are updated
-        self.set(self['initialValue'])
+        self.set(self['value'])
 
         # Record entry color
         self.entryBackground = self.cget('Entry_entry_background')
@@ -142,9 +142,9 @@ class VectorEntry(Pmw.MegaWidget):
         self['Entry_entry_width'] = self['entryWidth']
 
     def _setSigDigits(self):
-        sd = self['significantDigits']
+        sd = self['numDigits']
         self.entryFormat = '%.' + '%d' % sd + 'f'
-        self.configure(Valuator_significantDigits = sd)
+        self.configure(Valuator_numDigits = sd)
         # And refresh value to reflect change
         for index in range(self['dim']):
             self._refreshEntry(index)
@@ -232,8 +232,10 @@ class VectorEntry(Pmw.MegaWidget):
             self.configure(Entry_entry_state = 'disabled')
             self.configure(Entry_entry_background = '#C0C0C0')
             # Disable floater Group scale
+            """
             self.component('fGroup').configure(
                 Valuator_scale_state = 'disabled')
+            """
             # Disable floater group entry
             self.component('fGroup').configure(
                 Valuator_entry_state = 'disabled')
@@ -244,8 +246,10 @@ class VectorEntry(Pmw.MegaWidget):
             self.configure(Entry_entry_state = 'normal')
             self.configure(Entry_entry_background = self.entryBackground)
             # Disable floater Group scale
+            """
             self.component('fGroup').configure(
                 Valuator_scale_state = 'normal')
+            """
             # Disable floater group entry
             self.component('fGroup').configure(
                 Valuator_entry_state = 'normal')
@@ -302,7 +306,7 @@ class ColorEntry(VectorEntry):
             ('fGroup_labels',           ('R','G','B','A'),  None),
             ('min',                     0.0,                None),
             ('max',                     255.0,              None),
-            ('significantDigits',       0,                  None),
+            ('nuDigits',       0,                  None),
             ('Valuator_resolution',     1.0,                None),
             )
         self.defineoptions(kw, optiondefs)

+ 3 - 1
direct/src/tkwidgets/WidgetPropertiesDialog.py

@@ -110,15 +110,17 @@ class WidgetPropertiesDialog(Toplevel):
                     extra = ''
             # Set up help string and validator based upon type
             if entryType == 'real':
+                # Only allow real numbers
                 entry['validate'] = { 'validator' : self.realOrNone }
                 if helpString is None:
                     helpString = 'Enter a floating point number' + extra + '.'
             elif entryType == 'integer':
+                # Only allow integer values
                 entry['validate'] = { 'validator' : self.intOrNone }
                 if helpString is None:
                     helpString = 'Enter an integer' + extra + '.'
             else:
-                entry['validate'] = { 'validator' : 'alphanumeric' }
+                # Anything goes with a string widget
                 if helpString is None:
                     helpString = 'Enter a string' + extra + '.'
             # Bind balloon with help string to entry