Browse Source

*** empty log message ***

Mark Mine 24 years ago
parent
commit
c6178a4d81

+ 2 - 2
direct/src/directtools/DirectSession.py

@@ -12,7 +12,7 @@ from ClusterServer import *
 from ClusterConfig import *
 from tkSimpleDialog import askstring
 import Placer
-import EntryScale
+import Slider
 import SceneGraphExplorer
 import OnscreenText
 import types
@@ -149,7 +149,7 @@ class DirectSession(PandaObject):
             ['SGE_Show All', self.showAllDescendants],
             ['SGE_Fit', self.fitOnNodePath],
             ['SGE_Place', Placer.place],
-            ['SGE_Set Color', EntryScale.rgbPanel],
+            ['SGE_Set Color', Slider.rgbPanel],
             ['SGE_Explore', SceneGraphExplorer.explore],
             ['SGE_Delete', self.removeNodePath],
             ['SGE_Set Name', self.getAndSetName],

+ 2 - 2
direct/src/extensions/NodePath-extensions.py

@@ -803,8 +803,8 @@
         base.wantTk = 1
         base.wantDIRECT = 1
         import TkGlobal
-        import EntryScale
-        return EntryScale.rgbPanel(self, cb)
+        import Slider
+        return Slider.rgbPanel(self, cb)
 
     def select(self):
         base.wantTk = 1

+ 0 - 1
direct/src/leveleditor/LevelEditor.py

@@ -10,7 +10,6 @@ from tkFileDialog import *
 from whrandom import *
 import Pmw
 import Floater
-import EntryScale
 import VectorWidgets
 import string
 import os

+ 3 - 3
direct/src/showbase/PythonUtil.py

@@ -276,7 +276,7 @@ def adjust(command = None, parent = None, **kw):
     adjust(command = None, parent = None, **kw)
     Popup and entry scale to adjust a parameter
     
-    Accepts any EntryScale keyword argument.  Typical arguments include:
+    Accepts any Slider keyword argument.  Typical arguments include:
     command: The one argument command to execute
     min: The min value of the slider
     max: The max value of the slider
@@ -294,7 +294,7 @@ def adjust(command = None, parent = None, **kw):
     # Make sure we enable Tk
     import TkGlobal
     import Tkinter
-    import EntryScale
+    import Slider
     import Pmw
     # Create toplevel if needed
     if not parent:
@@ -303,7 +303,7 @@ def adjust(command = None, parent = None, **kw):
     # Set command if specified
     if command:
         kw['command'] = command
-    es = apply(EntryScale.EntryScale, (parent,), kw)
+    es = apply(Slider.Slider, (parent,), kw)
     es.pack(expand = 1, fill = 'x')
     es.parent = parent
     return es

+ 28 - 35
direct/src/tkpanels/DirectSessionPanel.py

@@ -8,7 +8,7 @@ import string
 import Pmw
 import Dial
 import Floater
-import EntryScale
+import Slider
 import VectorWidgets
 import SceneGraphExplorer
 from TaskManagerPanel import TaskManagerWidget
@@ -24,8 +24,8 @@ class DirectSessionPanel(AppShell):
     # Override class variables here
     appname = 'Direct Session Panel'
     frameWidth      = 600
-    frameHeight     = 502
-    usecommandarea = 1
+    frameHeight     = 365
+    usecommandarea = 0
     usestatusarea  = 0
 
     def __init__(self, parent = None, **kw):
@@ -102,6 +102,17 @@ class DirectSessionPanel(AppShell):
                                  label = 'Enable Grid',
                                  variable = self.directGridEnabled,
                                  command = self.toggleDirectGrid)
+
+        self.menuBar.addmenuitem('DIRECT', 'command',
+                                 'Toggle Object Handles Visability',
+                                 label = 'Toggle Widget Viz',
+                                 command = direct.toggleWidgetVis)
+
+        self.menuBar.addmenuitem(
+            'DIRECT', 'command',
+            'Toggle Widget Move/COA Mode',
+            label = 'Toggle Widget Mode',
+            command = direct.manipulationControl.toggleObjectHandlesMode)
         
         # Get a handle to the menu frame
         menuFrame = self.menuFrame
@@ -150,7 +161,7 @@ class DirectSessionPanel(AppShell):
         self.SGE = SceneGraphExplorer.SceneGraphExplorer(
             sgeFrame, nodePath = render,
             scrolledCanvas_hull_width = 250,
-            scrolledCanvas_hull_height = 400)
+            scrolledCanvas_hull_height = 300)
         self.SGE.pack(fill = BOTH, expand = 0)
         sgeFrame.pack(side = LEFT, fill = 'both', expand = 0)
 
@@ -212,7 +223,7 @@ class DirectSessionPanel(AppShell):
 
         fovFrame = Frame(drFrame)
         fovFloaterFrame = Frame(fovFrame)
-        self.hFov = EntryScale.EntryScale(
+        self.hFov = Slider.Slider(
             fovFloaterFrame,
             text = 'Horizontal FOV',
             min = 0.01, max = 170.0)
@@ -220,7 +231,7 @@ class DirectSessionPanel(AppShell):
         self.hFov.pack(fill = X, expand = 0)
         self.bind(self.hFov, 'Set horizontal field of view')
            
-        self.vFov = EntryScale.EntryScale(
+        self.vFov = Slider.Slider(
             fovFloaterFrame,
             text = 'Vertical FOV',
             min = 0.01, max = 170.0)
@@ -366,7 +377,7 @@ class DirectSessionPanel(AppShell):
         self.bind(self.pSpecularColor,
                   'Set point light specular color')
 
-        self.pConstantAttenuation = EntryScale.EntryScale(
+        self.pConstantAttenuation = Slider.Slider(
             pointPage,
             text = 'Constant Attenuation',
             min = 0.0, max = 1.0, value = 1.0)
@@ -375,7 +386,7 @@ class DirectSessionPanel(AppShell):
         self.bind(self.pConstantAttenuation,
                   'Set point light constant attenuation')
            
-        self.pLinearAttenuation = EntryScale.EntryScale(
+        self.pLinearAttenuation = Slider.Slider(
             pointPage,
             text = 'Linear Attenuation',
             min = 0.0, max = 1.0, value = 0.0)
@@ -384,7 +395,7 @@ class DirectSessionPanel(AppShell):
         self.bind(self.pLinearAttenuation,
                   'Set point light linear attenuation')
            
-        self.pQuadraticAttenuation = EntryScale.EntryScale(
+        self.pQuadraticAttenuation = Slider.Slider(
             pointPage,
             text = 'Quadratic Attenuation',
             min = 0.0, max = 1.0, value = 0.0)
@@ -401,7 +412,7 @@ class DirectSessionPanel(AppShell):
         self.bind(self.sSpecularColor,
                   'Set spot light specular color')
 
-        self.sConstantAttenuation = EntryScale.EntryScale(
+        self.sConstantAttenuation = Slider.Slider(
             spotPage,
             text = 'Constant Attenuation',
             min = 0.0, max = 1.0, value = 1.0)
@@ -410,7 +421,7 @@ class DirectSessionPanel(AppShell):
         self.bind(self.sConstantAttenuation,
                   'Set spot light constant attenuation')
            
-        self.sLinearAttenuation = EntryScale.EntryScale(
+        self.sLinearAttenuation = Slider.Slider(
             spotPage,
             text = 'Linear Attenuation',
             min = 0.0, max = 1.0, value = 0.0)
@@ -419,7 +430,7 @@ class DirectSessionPanel(AppShell):
         self.bind(self.sLinearAttenuation,
                   'Set spot light linear attenuation')
            
-        self.sQuadraticAttenuation = EntryScale.EntryScale(
+        self.sQuadraticAttenuation = Slider.Slider(
             spotPage,
             text = 'Quadratic Attenuation',
             min = 0.0, max = 1.0, value = 0.0)
@@ -428,7 +439,7 @@ class DirectSessionPanel(AppShell):
         self.bind(self.sQuadraticAttenuation,
                   'Set spot light quadratic attenuation')
            
-        self.sExponent = EntryScale.EntryScale(
+        self.sExponent = Slider.Slider(
             spotPage,
             text = 'Exponent',
             min = 0.0, max = 1.0, value = 0.0)
@@ -493,7 +504,7 @@ class DirectSessionPanel(AppShell):
         self.gridSnapAngle = Dial.AngleDial(
             gridPage,
             text = 'Snap Angle',
-            style = Dial.DIAL_MINI,
+            style = 'mini',
             value = direct.grid.getSnapAngle())
         self.gridSnapAngle['command'] = direct.grid.setSnapAngle
         self.gridSnapAngle.pack(fill = X, expand = 0)
@@ -542,7 +553,7 @@ class DirectSessionPanel(AppShell):
             self.bind(self.jbNodePathMenu,
                       'Select node path to manipulate using the joybox')
 
-            self.jbXyzSF = EntryScale.EntryScale(
+            self.jbXyzSF = Slider.Slider(
                 joyboxFrame,
                 text = 'XYZ Scale Factor',
                 value = 1.0,
@@ -553,7 +564,7 @@ class DirectSessionPanel(AppShell):
             self.jbXyzSF.pack(fill = X, expand = 0)
             self.bind(self.jbXyzSF, 'Set joybox XYZ speed multiplier')
 
-            self.jbHprSF = EntryScale.EntryScale(
+            self.jbHprSF = Slider.Slider(
                 joyboxFrame,
                 text = 'HPR Scale Factor',
                 value = 1.0,
@@ -568,31 +579,13 @@ class DirectSessionPanel(AppShell):
         Label(tasksPage, text = 'TASKS',
               font=('MSSansSerif', 14, 'bold')).pack(expand = 0)
         self.taskMgrPanel = TaskManagerWidget(tasksPage, taskMgr)
+        self.taskMgrPanel.taskListBox['listbox_height'] = 10
 
         notebook.setnaturalsize()
 
         framePane.pack(expand = 1, fill = BOTH)
         mainFrame.pack(fill = 'both', expand = 1)
 
-        # Create some buttons in the bottom tray
-        self.createButtons()
-
-    def createButtons(self):
-        # Grid: enable/disable, xyz/hpr snap, snap to plane
-        # Render mode: wireframe, lights, texture
-        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()
-
     def toggleDirect(self):
         if self.directEnabled.get():
             direct.enable()

+ 32 - 13
direct/src/tkpanels/MopathRecorder.py

@@ -13,6 +13,7 @@ import string
 import Pmw
 import Dial
 import Floater
+import Slider
 import EntryScale
 import VectorWidgets
 import __builtin__
@@ -365,7 +366,7 @@ class MopathRecorder(AppShell, PandaObject):
             self.resamplePage, relief = SUNKEN, borderwidth = 2)
         label = Label(resampleFrame, text = 'RESAMPLE CURVE',
                       font=('MSSansSerif', 12, 'bold')).pack()
-        widget = self.createEntryScale(
+        widget = self.createSlider(
             resampleFrame, 'Resample', 'Num. Samples',
             'Number of samples in resampled curve',
             resolution = 1, min = 2, max = 1000, command = self.setNumSamples)
@@ -389,7 +390,7 @@ class MopathRecorder(AppShell, PandaObject):
             self.resamplePage, relief = SUNKEN, borderwidth = 2)
         Label(desampleFrame, text = 'DESAMPLE CURVE',
               font=('MSSansSerif', 12, 'bold')).pack()
-        widget = self.createEntryScale(
+        widget = self.createSlider(
             desampleFrame, 'Resample', 'Points Between Samples',
             'Specify number of points to skip between samples',
             min = 1, max = 100, resolution = 1,
@@ -405,28 +406,28 @@ class MopathRecorder(AppShell, PandaObject):
                       font=('MSSansSerif', 12, 'bold'))
         label.pack(fill = X)
 
-        widget = self.createEntryScale(refineFrame,
+        widget = self.createSlider(refineFrame,
                                        'Refine Page', 'Refine From',
                                        'Begin time of refine pass',
                                        resolution = 0.01,
                                        command = self.setRecordStart)
         widget['preCallback'] = self.setRefineMode
         widget['postCallback'] = lambda s = self: s.getPrePoints('Refine')
-        widget = self.createEntryScale(
+        widget = self.createSlider(
             refineFrame, 'Refine Page',
             'Control Start',
             'Time when full control of node path is given during refine pass',
             resolution = 0.01,
             command = self.setControlStart)
         widget['preCallback'] = self.setRefineMode
-        widget = self.createEntryScale(
+        widget = self.createSlider(
             refineFrame, 'Refine Page',
             'Control Stop',
             'Time when node path begins transition back to original curve',
             resolution = 0.01,
             command = self.setControlStop)
         widget['preCallback'] = self.setRefineMode
-        widget = self.createEntryScale(refineFrame, 'Refine Page', 'Refine To',
+        widget = self.createSlider(refineFrame, 'Refine Page', 'Refine To',
                                        'Stop time of refine pass',
                                        resolution = 0.01,
                                        command = self.setRefineStop)
@@ -441,14 +442,14 @@ class MopathRecorder(AppShell, PandaObject):
                       font=('MSSansSerif', 12, 'bold'))
         label.pack(fill = X)
 
-        widget = self.createEntryScale(extendFrame,
+        widget = self.createSlider(extendFrame,
                                        'Extend Page', 'Extend From',
                                        'Begin time of extend pass',
                                        resolution = 0.01,
                                        command = self.setRecordStart)
         widget['preCallback'] = self.setExtendMode
         widget['postCallback'] = lambda s = self: s.getPrePoints('Extend')
-        widget = self.createEntryScale(
+        widget = self.createSlider(
             extendFrame, 'Extend Page',
             'Control Start',
             'Time when full control of node path is given during extend pass',
@@ -464,14 +465,14 @@ class MopathRecorder(AppShell, PandaObject):
                       font=('MSSansSerif', 12, 'bold'))
         label.pack(fill = X)
 
-        widget = self.createEntryScale(
+        widget = self.createSlider(
             cropFrame,
             'Crop Page', 'Crop From',
             'Delete all curve points before this time',
             resolution = 0.01,
             command = self.setCropFrom)
 
-        widget = self.createEntryScale(
+        widget = self.createSlider(
             cropFrame,
             'Crop Page', 'Crop To',
             'Delete all curve points after this time',
@@ -523,21 +524,21 @@ class MopathRecorder(AppShell, PandaObject):
             side = LEFT, fill = X, expand = 1)
         frame.pack(fill = X, expand = 1)
         # Sliders
-        widget = self.createEntryScale(
+        widget = self.createSlider(
             sfFrame, 'Style', 'Num Segs',
             'Set number of segments used to approximate each parametric unit',
             min = 1.0, max = 400, resolution = 1.0,
             value = 40, 
             command = self.setNumSegs, side = TOP)
         widget.component('hull')['relief'] = RIDGE
-        widget = self.createEntryScale(
+        widget = self.createSlider(
             sfFrame, 'Style', 'Num Ticks',
             'Set number of tick marks drawn for each unit of time',
             min = 0.0, max = 10.0, resolution = 1.0,
             value = 0.0,
             command = self.setNumTicks, side = TOP)
         widget.component('hull')['relief'] = RIDGE
-        widget = self.createEntryScale(
+        widget = self.createSlider(
             sfFrame, 'Style', 'Tick Scale',
             'Set visible size of time tick marks',
             min = 0.01, max = 100.0, resolution = 0.01,
@@ -1760,6 +1761,24 @@ class MopathRecorder(AppShell, PandaObject):
         self.widgetDict[category + '-' + text] = widget
         return widget
 
+    def createSlider(self, parent, category, text, balloonHelp,
+                         command = None, min = 0.0, max = 1.0,
+                         resolution = None,
+                         side = TOP, fill = X, expand = 1, **kw):
+        kw['text'] = text
+        kw['min'] = min
+        kw['max'] = max
+        kw['resolution'] = resolution
+        #widget = apply(EntryScale.EntryScale, (parent,), kw)
+        import Slider
+        widget = apply(Slider.Slider, (parent,), kw)
+        # Do this after the widget so command isn't called on creation
+        widget['command'] = command
+        widget.pack(side = side, fill = fill, expand = expand)
+        self.bind(widget, balloonHelp)
+        self.widgetDict[category + '-' + text] = widget
+        return widget
+
     def createEntryScale(self, parent, category, text, balloonHelp,
                          command = None, min = 0.0, max = 1.0,
                          resolution = None,

+ 12 - 12
direct/src/tkpanels/ParticlePanel.py

@@ -9,7 +9,7 @@ import os
 import Pmw
 import Dial
 import Floater
-import EntryScale
+import Slider
 import VectorWidgets
 import Placer
 import ForceGroup
@@ -279,23 +279,23 @@ class ParticlePanel(AppShell):
         zSpinPage = self.factoryNotebook.add('ZSpinParticleFactory')
         self.createAngleDial(zSpinPage, 'Z Spin Factory', 'Initial Angle',
                              'Starting angle in degrees',
-                             dial_fRollover = 1,
+                             fRollover = 1,
                              command = self.setFactoryZSpinInitialAngle)
         self.createAngleDial(
             zSpinPage, 'Z Spin Factory',
             'Initial Angle Spread',
             'Spread of the initial angle',
-            dial_fRollover = 1,
+            fRollover = 1,
             command = self.setFactoryZSpinInitialAngleSpread)
         self.createAngleDial(zSpinPage, 'Z Spin Factory', 'Final Angle',
                              'Final angle in degrees',
-                             dial_fRollover = 1,
+                             fRollover = 1,
                              command = self.setFactoryZSpinFinalAngle)
         self.createAngleDial(
             zSpinPage, 'Z Spin Factory',
             'Final Angle Spread',
             'Spread of the final angle',
-            dial_fRollover = 1,
+            fRollover = 1,
             command = self.setFactoryZSpinFinalAngleSpread)
         # Oriented page #
         orientedPage = self.factoryNotebook.add('OrientedParticleFactory')
@@ -472,7 +472,7 @@ class ParticlePanel(AppShell):
                               ('NO_ALPHA','ALPHA_OUT','ALPHA_IN','ALPHA_USER'),
                               self.setRendererAlphaMode)
         
-        self.createEntryScale(
+        self.createSlider(
             rendererPage, 'Renderer', 'User Alpha',
             'alpha value for ALPHA_USER alpha mode',
             command = self.setRendererUserAlpha)
@@ -739,7 +739,7 @@ class ParticlePanel(AppShell):
                       numDigits = 3, **kw):
         kw['text'] = text
         kw['min'] = min
-        kw['floater_resolution'] = resolution
+        kw['resolution'] = resolution
         kw['numDigits'] = numDigits
         widget = apply(Floater.Floater, (parent,), kw)
         # Do this after the widget so command isn't called on creation
@@ -752,7 +752,7 @@ class ParticlePanel(AppShell):
     def createAngleDial(self, parent, category, text, balloonHelp,
                         command = None, **kw):
         kw['text'] = text
-        kw['style'] = Dial.DIAL_MINI
+        kw['style'] = 'mini'
         widget = apply(Dial.AngleDial,(parent,), kw)
         # Do this after the widget so command isn't called on creation
         widget['command'] = command
@@ -761,14 +761,14 @@ class ParticlePanel(AppShell):
         self.widgetDict[category + '-' + text] = widget
         return widget
 
-    def createEntryScale(self, parent, category, text, balloonHelp,
-                         command = None, min = 0.0, max = 1.0,
-                         resolution = 0.001, **kw):
+    def createSlider(self, parent, category, text, balloonHelp,
+                     command = None, min = 0.0, max = 1.0,
+                     resolution = 0.001, **kw):
         kw['text'] = text
         kw['min'] = min
         kw['max'] = max
         kw['resolution'] = resolution
-        widget = apply(EntryScale.EntryScale, (parent,), kw)
+        widget = apply(Slider.Slider, (parent,), kw)
         # Do this after the widget so command isn't called on creation
         widget['command'] = command
         widget.pack(fill = X)

+ 3 - 3
direct/src/tkpanels/Placer.py

@@ -233,7 +233,7 @@ class Placer(AppShell):
         # Create the dials
         self.hprH = self.createcomponent('hprH', (), None,
                                          Dial.AngleDial, (hprInterior,),
-                                         style = Dial.DIAL_MINI,
+                                         style = 'mini',
                                          text = 'H', value = 0.0,
                                          relief = FLAT,
                                          label_foreground = 'blue')
@@ -246,7 +246,7 @@ class Placer(AppShell):
         
         self.hprP = self.createcomponent('hprP', (), None,
                                          Dial.AngleDial, (hprInterior,),
-                                         style = Dial.DIAL_MINI,
+                                         style = 'mini',
                                          text = 'P', value = 0.0,
                                          relief = FLAT,
                                          label_foreground = 'red')
@@ -259,7 +259,7 @@ class Placer(AppShell):
         
         self.hprR = self.createcomponent('hprR', (), None,
                                          Dial.AngleDial, (hprInterior,),
-                                         style = Dial.DIAL_MINI,
+                                         style = 'mini',
                                          text = 'R', value = 0.0,
                                          relief = FLAT,
                                          label_foreground = '#00A000')

+ 10 - 0
direct/src/tkwidgets/AppShell.py

@@ -10,6 +10,7 @@ from tkFileDialog import *
 import Pmw
 import Dial
 import Floater
+import Slider
 import EntryScale
 import VectorWidgets
 import sys, string
@@ -411,6 +412,15 @@ class AppShell(Pmw.MegaWidget, PandaObject):
                                    help, command, side, fill, expand, kw)
         return widget
 
+    def newCreateSider(self, parent, category, text,
+                       help = '', command = None,
+                       side = LEFT, fill = X, expand = 0, **kw):
+        # Create the widget
+        widget = self.createWidget(parent, category, text,
+                                   Slider.Slider,
+                                   help, command, side, fill, expand, kw)
+        return widget
+
     def newCreateEntryScale(self, parent, category, text,
                             help = '', command = None,
                             side = LEFT, fill = X, expand = 0, **kw):

+ 4 - 8
direct/src/tkwidgets/Dial.py

@@ -16,15 +16,11 @@ ONEPOINTFIVE_PI = 1.5 * math.pi
 POINTFIVE_PI = 0.5 * math.pi
 INNER_SF = 0.2
 
-DIAL_FULL = 'full'
-DIAL_MINI = 'mini'
-
 DIAL_FULL_SIZE = 45
 DIAL_MINI_SIZE = 30
 
 globalClock = ClockObject.getGlobalClock()
 
-
 class Dial(Valuator):
     """
     Valuator widget which includes an angle dial and an entry for setting
@@ -33,7 +29,7 @@ class Dial(Valuator):
     def __init__(self, parent = None, **kw):
         INITOPT = Pmw.INITOPT
         optiondefs = (
-            ('style',             DIAL_FULL,      INITOPT),
+            ('style',             VALUATOR_FULL,  INITOPT),
             ('base',              0.0,            self.setBase),
             ('delta',             1.0,            self.setDelta),
             ('fSnap',             0,              self.setSnap),
@@ -56,7 +52,7 @@ class Dial(Valuator):
         self._valuator._canvas.bind('<Double-ButtonPress-1>', self.mouseReset)
 
     def packValuator(self):
-        if self['style'] == DIAL_FULL:
+        if self['style'] == VALUATOR_FULL:
             self._valuator.grid(rowspan = 2, columnspan = 2,
                                 padx = 2, pady = 2)
             if self._label:
@@ -155,7 +151,7 @@ class DialWidget(Pmw.MegaWidget):
         INITOPT = Pmw.INITOPT
         optiondefs = (
             # Appearance
-            ('style',           DIAL_FULL,      INITOPT),
+            ('style',           VALUATOR_FULL,      INITOPT),
             ('size',            None,           INITOPT),
             ('relief',          SUNKEN,         self.setRelief),
             ('borderwidth',     2,              self.setBorderwidth),
@@ -200,7 +196,7 @@ class DialWidget(Pmw.MegaWidget):
 
         # Base dial size on style, if size not specified, 
         if not self['size']:
-            if self['style'] == DIAL_FULL:
+            if self['style'] == VALUATOR_FULL:
                 size = DIAL_FULL_SIZE
             else:
                 size = DIAL_MINI_SIZE

+ 4 - 1
direct/src/tkwidgets/Floater.py

@@ -17,7 +17,10 @@ FLOATER_HEIGHT = 18
 
 class Floater(Valuator):
     def __init__(self, parent = None, **kw):
-        optiondefs = ()
+        INITOPT = Pmw.INITOPT
+        optiondefs = (
+            ('style',  VALUATOR_MINI,   INITOPT),
+            )
         self.defineoptions(kw, optiondefs)
         # Initialize the superclass
         Valuator.__init__(self, parent)

+ 235 - 495
direct/src/tkwidgets/Slider.py

@@ -11,18 +11,8 @@ import string
 import operator
 from PandaModules import ClockObject
 
-SLIDER_FULL = 'full'
-SLIDER_MINI = 'mini'
-
-SLIDER_FULL_WIDTH = 50
-SLIDER_FULL_HEIGHT = 25
-
-SLIDER_MINI_WIDTH = 16
-SLIDER_MINI_HEIGHT = 16
-
 globalClock = ClockObject.getGlobalClock()
 
-
 class Slider(Valuator):
     """
     Valuator widget which includes an min/max slider and an entry for setting
@@ -31,10 +21,17 @@ class Slider(Valuator):
     def __init__(self, parent = None, **kw):
         INITOPT = Pmw.INITOPT
         optiondefs = (
-            ('style',             SLIDER_FULL,    INITOPT),
+            ('min',        0.0,           self.setMin),
+            ('max',        100.0,         self.setMax),
+            ('style',      VALUATOR_MINI,   INITOPT),
             )
         self.defineoptions(kw, optiondefs)
         Valuator.__init__(self, parent)
+        # Can not enter none for min or max
+        self.propertyDict['min']['fNone'] = 0
+        self.propertyDict['min']['help'] = 'Minimum allowable value.'
+        self.propertyDict['max']['fNone'] = 0
+        self.propertyDict['max']['help'] = 'Maximum allowable value.'
         self.initialiseoptions(Slider)
 
     def createValuator(self):
@@ -49,13 +46,24 @@ class Slider(Valuator):
             value = self['value'])
         self._valuator._canvas.bind('<Double-ButtonPress-1>', self.mouseReset)
 
+        # Add popup bindings to slider widget
+        try:
+            self._valuator._arrowBtn.bind(
+                '<ButtonPress-3>', self._popupValuatorMenu)
+        except AttributeError:
+            pass
+        self._valuator._minLabel.bind(
+            '<ButtonPress-3>', self._popupValuatorMenu)
+        self._valuator._maxLabel.bind(
+            '<ButtonPress-3>', self._popupValuatorMenu)
+
     def packValuator(self):
-        if self['style'] == SLIDER_FULL:
+        if self['style'] == VALUATOR_FULL:
             if self._label:
                 self._label.grid(row = 0, col = 0, sticky = EW)
             self._entry.grid(row = 0, col = 1, sticky = EW)
             self._valuator.grid(row = 1, columnspan = 2,
-                                padx = 2, pady = 2)
+                                padx = 2, pady = 2, sticky = 'ew')
             self.interior().columnconfigure(0, weight = 1)
         else:
             if self._label:
@@ -64,351 +72,35 @@ class Slider(Valuator):
             self._valuator.grid(row=0,col=2, padx = 2, pady = 2)
             self.interior().columnconfigure(0, weight = 1)
 
+    def setMin(self):
+        if self['min'] is not None:
+            self._valuator['min'] = self['min']
 
-class SliderWidget(Pmw.MegaWidget):
-    def __init__(self, parent = None, **kw):
-        #define the megawidget options
-        INITOPT = Pmw.INITOPT
-        optiondefs = (
-            # Appearance
-            ('style',           SLIDER_MINI,    INITOPT),
-            ('width',           SLIDER_MINI_WIDTH,   INITOPT),
-            ('height',          SLIDER_MINI_HEIGHT,  INITOPT),
-            ('relief',          SUNKEN,         self.setRelief),
-            ('borderwidth',     2,              self.setBorderwidth),
-            ('background',      'white',        self.setBackground),
-            # Behavior
-            # Initial value of slider, use self.set to change value
-            ('value',           0.0,            INITOPT),
-            ('numDigits',       2,              self.setNumDigits),
-            # Command to execute on slider 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)
-        
-        # Initialize the superclass
-        Pmw.MegaWidget.__init__(self, parent)
-
-        # Set up some local and instance variables        
-        # Create the components
-        interior = self.interior()
-
-        # Current value
-        self.value = self['value']
-
-        # Base slider size on style, if size not specified, 
-        if not self['width']:
-            if self['style'] == SLIDER_FULL_SIZE:
-                width = SLIDER_FULL_WIDTH
-            else:
-                width = SLIDER_MINI_WIDTH
-        else:
-            width = self['width']
-
-        if not self['height']:
-            if self['style'] == SLIDER_FULL_SIZE:
-                height = SLIDER_FULL_HEIGHT
-            else:
-                height = SLIDER_MINI_HEIGHT
-        else:
-            height = self['height']
-
-        halfWidth = width/2.0
-        halfHeight = height/2.0
-        left = -(halfWidth - 2)
-        right = halfWidth - 2
-        top = -(halfHeight - 2)
-        bottom = halfHeight - 2
-
-        # The canvas 
-        self._canvas = self.createcomponent('canvas', (), None,
-                                            Canvas, (interior,),
-                                            width = width,
-                                            height = height,
-                                            background = self['background'],
-                                            highlightthickness = 0,
-                                            scrollregion = (-halfWidth,
-                                                            -halfHeight,
-                                                            halfWidth,
-                                                            halfHeight))
-        self._canvas.pack(expand = 1, fill = BOTH)
-
-        self._canvas.create_polygon(left,top,
-                                    0, bottom,
-                                    right, top,
-                                    fill = '#A0A0A0',
-                                    tags = ('slider',))
-
-        # The indicator
-        self._canvas.create_line(left, bottom,
-                                 right, bottom,
-                                 width = 2)
-
-        # Add event bindings
-        self._canvas.bind('<ButtonPress-1>', self.mouseDown)
-        self._canvas.bind('<B1-Motion>', self.updateSliderSF)
-        self._canvas.bind('<ButtonRelease-1>', self.mouseUp)
-        self._canvas.bind('<Enter>', self.highlightWidget)
-        self._canvas.bind('<Leave>', self.restoreWidget)
-
-        # Make sure input variables processed 
-        self.initialiseoptions(SliderWidget)
-
-    def set(self, value, fCommand = 1):
-        """
-        self.set(value, fCommand = 1)
-        Set slider to new value, execute command if fCommand == 1
-        """
-        # Send command if any
-        if fCommand and (self['command'] != None):
-            apply(self['command'], [value] + self['commandData'])
-        # Record value
-        self.value = value
-
-    def updateIndicator(self, value):
-        # Nothing visible to update on this type of widget
-        pass
-    
-    def get(self):
-        """
-        self.get()
-        Get current slider value
-        """
-        return self.value
-
-    ## Canvas callback functions
-    # Slider velocity controller
-    def mouseDown(self,event):
-        """ Begin mouse interaction """
-        # Exectute user redefinable callback function (if any)
-        if self['preCallback']:
-            apply(self['preCallback'], self['callbackData'])
-        self.velocitySF = 0.0
-        self.updateTask = taskMgr.add(self.updateSliderTask,
-                                        'updateSlider')
-        self.updateTask.lastTime = globalClock.getFrameTime()
-
-    def updateSliderTask(self, state):
-        """
-        Update sliderWidget value based on current scaleFactor
-        Adjust for time to compensate for fluctuating frame rates
-        """
-        currT = globalClock.getFrameTime()
-        dt = currT - state.lastTime
-        self.set(self.value + self.velocitySF * dt)
-        state.lastTime = currT
-        return Task.cont
-
-    def updateSliderSF(self, event):
-        """
-        Update velocity scale factor based of mouse distance from origin
-        """
-        x = self._canvas.canvasx(event.x)
-        y = self._canvas.canvasy(event.y)
-        offset = max(0, abs(x) - Valuator.deadband)
-        if offset == 0:
-            return 0
-        sf = math.pow(Valuator.sfBase,
-                      self.minExp + offset/Valuator.sfDist)
-        if x > 0:
-            self.velocitySF = sf
-        else:
-            self.velocitySF = -sf
-
-    def mouseUp(self, event):
-        taskMgr.remove(self.updateTask)
-        self.velocitySF = 0.0
-        # Execute user redefinable callback function (if any)
-        if self['postCallback']:
-            apply(self['postCallback'], self['callbackData'])
-
-    def setNumDigits(self):
-        """
-        Adjust minimum exponent to use in velocity task based
-        upon the number of digits to be displayed in the result
-        """
-        self.minExp = math.floor(-self['numDigits']/
-                                 math.log10(Valuator.sfBase))        
-
-    # Methods to modify slider characteristics    
-    def setRelief(self):
-        self.interior()['relief'] = self['relief']
-
-    def setBorderwidth(self):
-        self.interior()['borderwidth'] = self['borderwidth']
-
-    def setBackground(self):
-        self._canvas['background'] = self['background']
-
-    def highlightWidget(self, event):
-        self._canvas.itemconfigure('slider', fill = 'black')
-
-    def restoreWidget(self, event):
-        self._canvas.itemconfigure('slider', fill = '#A0A0A0')
-  
-if __name__ == '__main__':
-    tl = Toplevel()
-    d = Slider(tl)
-    d2 = Slider(tl, slider_numSegments = 12, max = 360,
-              slider_fRollover = 0, value = 180)
-    d3 = Slider(tl, slider_numSegments = 12, max = 90, min = -90,
-              slider_fRollover = 0)
-    d4 = Slider(tl, slider_numSegments = 16, max = 256,
-              slider_fRollover = 0)
-    d.pack(expand = 1, fill = X)
-    d2.pack(expand = 1, fill = X)
-    d3.pack(expand = 1, fill = X)
-    d4.pack(expand = 1, fill = X)
-
-
-
-class PopupSliderWidget(Pmw.MegaToplevel):
-    def __init__(self, parent = None, **kw):
-        optiondefs = (
-            ('width',                    150,            None),
-            ('height',                   25,             None),
-            ('xoffset',                  0,              None), # pixels
-            ('yoffset',                  1,              None), # pixels
-            )
-        self.defineoptions(kw, optiondefs)
-        Pmw.MegaToplevel.__init__(self, parent)
-        interior = self.interior()
-        self.withdraw()
-        self.overrideredirect(1)
-        interior['relief'] = RAISED
-        interior['borderwidth'] = 2
-        left = -self['width']/2.0
-        right = self['width']/2.0
-        top = -10
-        bottom = top + self['height']
-        self._canvas = self.createcomponent('canvas', (), None,
-                                            Canvas, (interior,),
-                                            relief = FLAT,
-                                            width = self['width'],
-                                            height = self['height'],
-                                            highlightthickness = 0,
-                                            scrollregion = (left, top,
-                                                            right, bottom)
-                                            )
-        self._canvas.pack(expand = 1, fill = BOTH)
-        self.marker = self._canvas.create_polygon(-6.9 + 1,12,
-                                    6.9+1,12,
-                                    1, 0,
-                                    fill = 'black',
-                                    tags = ('slider',))
-        self._canvas.create_polygon(-5.75,10,
-                                    5.75,10,
-                                    0, 0,
-                                    fill = 'grey85',
-                                    outline = 'black',
-                                    tags = ('slider',))
-        # The indicator
-        self.lineLeft = lineLeft = left + 10
-        self.lineRight = lineRight = right -10
-        self._canvas.create_line(lineLeft, 1,
-                                 lineRight, 1,
-                                 width = 2)
-
-        self.b = Button(interior, text = 'hello')
-        self.b['command'] = self.withdraw
-        self.b.pack()
-        self.initialiseoptions(PopupSliderWidget)
-        
-    def showPopup(self, widget):
-        x = widget.winfo_rootx() + widget.winfo_width() - self['width']
-        y = widget.winfo_rooty() + widget.winfo_height()
-        Pmw.setgeometryanddeiconify(self, '%dx%d+%d+%d' %
-                                    (self['width'], self['height'],x,y))
-
-"""
-
-pw = PopupSliderWidget()
-tl = Toplevel()
-b = Button(tl, text = 'V')
-b.pack()
-
-fCrossedLine = 0
-def move(event):
-    global fCrossedLine
-    if fCrossedLine:
-        newX = pw._canvas.canvasx(event.x_root) - pw.winfo_rootx()
-        if newX < pw.lineLeft:
-            newX = pw.lineLeft
-        elif newX > pw.lineRight:
-            newX = pw.lineRight
-        print (newX - pw.lineLeft)/(pw.lineRight - pw.lineLeft)
-        startX = getX()
-        dx = newX - startX
-        pw._canvas.move('slider', dx, 0)
-    else:
-        if event.y_root >= pw.winfo_rooty() + 10:
-            fCrossedLine = 1
-
-def getX():
-    c = pw._canvas.coords(pw.marker)
-    return c[4]
-
-def press(event):
-    global fCrossedLine
-    print 'press'
-    fCrossedLine = 0
-    pw.showPopup(b)
-    b.bind('<Motion>', move)
-
-def unpostCanvas(event):
-    print 'unpostCanvas', event.x_root, pw._canvas.winfo_rootx()
-    if event.x_root < pw._canvas.winfo_rootx():
-        print 'blah'
-    Pmw.popgrab(pw._canvas)
-    pw._canvas.withdraw()
-
-def popupPress(event):
-    global fCrossedLine
-    print 'popupPress'
-    fCrossedLine = 1
-    pw._canvas.bind('<Motion>', move)
-
-def release(event):
-    if fCrossedLine:
-        pw.withdraw()
-        b.unbind('<Motion>')
-        pw._canvas.unbind('<Motion>')
-    else:
-        Pmw.pushgrab(pw._canvas, 1, unpostCanvas)
-        pw._canvas.focus_set()
-
-b.bind('<ButtonPress-1>', press)
-b.bind('<ButtonRelease-1>', release)
-pw._canvas.bind('<ButtonPress-1>', popupPress)
-pw._canvas.bind('<ButtonRelease-1>', release)
-
-
-"""
+    def setMax(self):
+        if self['max'] is not None:
+            self._valuator['max'] = self['max']
 
 
 # Based on Pmw ComboBox code.
-class PopupSlider(Pmw.MegaWidget):
+class SliderWidget(Pmw.MegaWidget):
     def __init__(self, parent = None, **kw):
 
 	# Define the megawidget options.
 	INITOPT = Pmw.INITOPT
 	optiondefs = (
-	    ('dropdown',           1,          INITOPT),
-	    ('buttonaspect',       1.0,        INITOPT),
-	    ('fliparrow',          0,          INITOPT),
-	    ('labelmargin',        0,          INITOPT),
-	    ('labelpos',           None,       INITOPT),
+            # Appearance
+            ('relief',          RAISED,             self.setRelief),
+            ('borderwidth',     2,                  self.setBorderwidth),
+            ('background',      'SystemButtonFace', self.setBackground),
+	    ('fliparrow',       0,                  INITOPT),
             # Behavior
+	    ('style',           VALUATOR_MINI,        INITOPT),
+            # Bounds
+            ('min',             0.0,            self.setMin),
+            ('max',             100.0,          self.setMax),
             # Initial value of slider, use self.set to change value
             ('value',           0.0,            INITOPT),
-            ('numDigits',       2,              self._setNumDigits),
+            ('numDigits',       2,              self.setNumDigits),
             # Command to execute on slider updates
             ('command',         None,           None),
             # Extra data to be passed to command function
@@ -429,47 +121,95 @@ class PopupSlider(Pmw.MegaWidget):
 
         # Current value
         self.value = self['value']
+        self.formatString = '%2f'
+        self.increment = 0.01
 
         # Interaction flags
         self._fUpdate = 0
         self._fUnpost = 0
-        self._firstPress = 1
         self._fPressInsde = 0
-
-	self._entryfield = self.createcomponent('entryfield',
-		(('entry', 'entryfield_entry'),), None,
-		Pmw.EntryField, (interior,))
-	self._entryfield.grid(column=2, row=2, sticky='nsew')
-	interior.grid_columnconfigure(2, weight = 1)
-	self._entryWidget = self._entryfield.component('entry')
+        self._isPosted = 0
+        if self['style'] == VALUATOR_MINI:
+            self._firstPress = 1
+        else:
+            self._firstPress = 0
 
         # Slider dimensions
         width = 100
-        xPad = 10
+        self.xPad = xPad = 10
         canvasWidth = width + 2 * xPad
         height = 20
         self.left = left = -(width/2.0)
         self.right = right = (width/2.0)
-        top = -5
-        bottom = top + height
+        self.top = top = -5
+        self.bottom = bottom = top + height
+
+        def _createSlider(parent):
+            # Create the canvas inside the dropdown window.
+            # Min label
+            self._minLabel = Label(parent, text = self['min'], width = 8,
+                                   anchor = E)
+            self._minLabel.pack(side = LEFT)
+            # Slider
+            self._canvas = self.createcomponent(
+                'canvas', (), None,
+                Canvas, (parent,),
+                width = canvasWidth,
+                height = height,
+                bd = 2,
+                highlightthickness = 0,
+                scrollregion = (left - xPad, top, right + xPad, bottom))
+            self._canvas.pack(side = LEFT, expand=1, fill=X)
+            if self['style'] == VALUATOR_FULL:
+                self._canvas.configure(relief = SUNKEN, bd = 2)
+            # Max label
+            self._maxLabel = Label(parent, text = self['max'], width = 8,
+                                   anchor = W)
+            self._maxLabel.pack(side = LEFT)
+
+            # Interaction marker
+            xShift = 1
+            # Shadow arrow
+            self._marker = self._canvas.create_polygon(-7 + xShift, 12,
+                                                       7 + xShift, 12,
+                                                       xShift, 0,
+                                                       fill = 'black',
+                                                       tags = ('slider',))
+            # Arrow
+            self._canvas.create_polygon(-6.0, 10,
+                                        6.0, 10,
+                                        0, 0,
+                                        fill = 'grey85',
+                                        outline = 'black',
+                                        tags = ('slider',))
+            # The indicator
+            self._canvas.create_line(left, 0,
+                                     right, 0,
+                                     width = 2,
+                                     tags = ('line',))
+            
+            self._canvas.bind('<Left>', self._decrementValue)
+            self._canvas.bind('<Right>', self._incrementValue)
+            self._canvas.bind('<Shift-Left>', self._bigDecrementValue)
+            self._canvas.bind('<Shift-Right>', self._bigIncrementValue)
+            self._canvas.bind('<Home>', self._goToMin)
+            self._canvas.bind('<End>', self._goToMax)
+            
 
         # Create slider
-	if self['dropdown']:
+	if self['style'] == VALUATOR_MINI:
 	    self._isPosted = 0
-            interior.grid_rowconfigure(2, weight = 1)
 
 	    # Create the arrow button.
 	    self._arrowBtn = self.createcomponent('arrowbutton',
 		    (), None,
-		    Canvas, (interior,), borderwidth = 2,
-		    relief = 'raised',
-		    width = 16, height = 16)
-	    self._arrowBtn.grid(column=3, row=2)
+		    Canvas, (interior,), borderwidth = 0,
+		    relief = FLAT, width = 14, height = 14)
+	    self._arrowBtn.pack(expand = 1, fill = BOTH)
+            self._arrowBtn.create_polygon(2.5, 4.5, 12.5, 4.5, 7.5, 12.5,
+                                          tags = 'arrow')
 	    self._arrowRelief = self._arrowBtn.cget('relief')
 
-	    # Create the label.
-	    self.createlabel(interior, childCols=2)
-
 	    # Create the dropdown window.
 	    self._popup = self.createcomponent(
                 'popup',
@@ -479,87 +219,41 @@ class PopupSlider(Pmw.MegaWidget):
 	    self._popup.withdraw()
 	    self._popup.overrideredirect(1)
 
-	    # Create the canvas inside the dropdown window.
-            # Min label
-            self._minLabel = Label(self._popup, text = 'MINAAAAAA')
-            self._minLabel.pack(side = LEFT)
-            # Slider
-            self._canvas = self.createcomponent(
-                'canvas', (), None,
-                Canvas, (self._popup,),
-                width = canvasWidth,
-                height = height,
-                bd = 3,
-                highlightthickness = 0,
-                scrollregion = (left - xPad, top, right + xPad, bottom))
-	    self._canvas.pack(side = LEFT, expand=1, fill='both')
-            # Max label
-            self._maxLabel = Label(self._popup, text = 'MAX')
-            self._maxLabel.pack(side = LEFT)
-            
+            _createSlider(self._popup)
+
 	    # Bind events to the arrow button.
 	    self._arrowBtn.bind('<1>', self._postCanvas)
-	    self._arrowBtn.bind('<Configure>', self._drawArrow)
-
-	    # Bind events to the dropdown window.
-	    self._popup.bind('<Escape>', self._unpostCanvas)
-	    self._popup.bind('<ButtonRelease-1>', self._dropdownBtnRelease)
-	    self._popup.bind('<ButtonPress-1>', self._dropdownBtnPress)
-            self._popup.bind('<Motion>', self._dropdownMove)
-
-	    # Bind events to the Tk listbox.
-	    #self._canvas.bind('<Enter>', self._unpostOnNextRelease)
-
-	    # Bind events to the Tk entry widget.
-	    self._entryWidget.bind('<Configure>', self._resizeArrow)
-
-            # Need to unpost the popup if the entryfield is unmapped (eg: 
+            self._arrowBtn.bind('<Enter>', self.highlightWidget)
+            self._arrowBtn.bind('<Leave>', self.restoreWidget)
+            # Need to unpost the popup if the arrow Button is unmapped (eg: 
             # its toplevel window is withdrawn) while the popup canvas is
             # displayed.
-            self._entryWidget.bind('<Unmap>', self._unpostCanvas)
+            self._arrowBtn.bind('<Unmap>', self._unpostCanvas)
+            
+	    # Bind events to the dropdown window.
+	    self._popup.bind('<Escape>', self._unpostCanvas)
+	    self._popup.bind('<ButtonRelease-1>', self._sliderBtnRelease)
+	    self._popup.bind('<ButtonPress-1>', self._sliderBtnPress)
+            self._popup.bind('<Motion>', self._sliderMove)
 	else:
-	    # Create the slider below the entry field.
-            self._canvas = self.createcomponent(
-                'canvas', (), None,
-                Canvas, (interior,),
-                width = canvasWidth,
-                height = height,
-                highlightthickness = 0,
-                scrollregion = (left - xPad, top, right + xPad, bottom))
-	    self._canvas.grid(column=2, row=3, sticky='nsew')
-
-	    # The scrolled canvas should expand vertically.
-	    interior.grid_rowconfigure(3, weight = 1)
-
-	    # Create the label.
-	    self.createlabel(interior, childRows=2)
-
-        # Interaction marker
-        xShift = 1
-        # Shadow arrow
-        self._marker = self._canvas.create_polygon(-7 + xShift, 12,
-                                                   7 + xShift, 12,
-                                                   xShift, 0,
-                                                   fill = 'black',
-                                                   tags = ('slider',))
-        # Arrow
-        self._canvas.create_polygon(-6.0, 10,
-                                    6.0, 10,
-                                    0, 0,
-                                    fill = 'grey85',
-                                    outline = 'black',
-                                    tags = ('slider',))
-        # The indicator
-        self._canvas.create_line(left, 0,
-                                 right, 0,
-                                 width = 2)
+	    # Create the slider directly in the interior
+            _createSlider(interior)
+	    self._canvas.bind('<ButtonRelease-1>', self._sliderBtnRelease)
+	    self._canvas.bind('<ButtonPress-1>', self._sliderBtnPress)
+            self._canvas.bind('<Motion>', self._sliderMove)
+            self._canvas.bind('<Configure>', self._changeConfiguration)
 
         
 	# Check keywords and initialise options.
-	self.initialiseoptions(PopupSlider)
+	self.initialiseoptions(SliderWidget)
+
+        # Adjust relief
+        if not kw.has_key('relief'):
+            if self['style'] == VALUATOR_FULL:
+                self['relief'] = FLAT
 
     def destroy(self):
-	if self['dropdown'] and self._isPosted:
+	if (self['style'] == VALUATOR_MINI) and self._isPosted:
             Pmw.popgrab(self._popup)
         Pmw.MegaWidget.destroy(self)
 
@@ -578,10 +272,6 @@ class PopupSlider(Pmw.MegaWidget):
         # Record value
         self.value = value
 
-    def updateIndicator(self, value):
-        # Nothing visible to update on this type of widget
-        pass
-    
     def get(self):
         """
         self.get()
@@ -589,50 +279,42 @@ class PopupSlider(Pmw.MegaWidget):
         """
         return self.value
 
+    def updateIndicator(self, value):
+        # Get current marker position
+        markerX = self._getMarkerX()
+        percentX = (value - self['min'])/(self['max'] - self['min'])
+        newX = percentX * (self.right - self.left) + self.left
+        dx = newX - markerX
+        self._canvas.move('slider', dx, 0)
+    
     #======================================================================
 
-    # Private methods for dropdown canvas.
-
-    def _setNumDigits(self):
-        pass
-
-    def _drawArrow(self, event=None, sunken=0):
-        arrow = self._arrowBtn
-	if sunken:
-	    self._arrowRelief = arrow.cget('relief')
-	    arrow.configure(relief = 'sunken')
-	else:
-	    arrow.configure(relief = self._arrowRelief)
-
-	if self._isPosted and self['fliparrow']:
-            direction = 'up'
-        else:
-            direction = 'down'
-        Pmw.drawarrow(arrow, self['entry_foreground'], direction, 'arrow')
+    # Private methods for slider.
 
     def _postCanvas(self, event = None):
         self._isPosted = 1
         self._fUpdate = 0
-        self._drawArrow(sunken=1)
+        if self['style'] == VALUATOR_MINI:
+            self.interior()['relief'] = SUNKEN
 
         # Make sure that the arrow is displayed sunken.
         self.update_idletasks()
 
-        x = self._entryfield.winfo_rootx()
-        y = self._entryfield.winfo_rooty() + self._entryfield.winfo_height()
-        w = self._entryfield.winfo_width() + self._arrowBtn.winfo_width()
+        x = self._arrowBtn.winfo_rootx() + self._arrowBtn.winfo_width()/2.0
+        y = self._arrowBtn.winfo_rooty() + self._arrowBtn.winfo_height()
         minW = self._minLabel.winfo_width()
         cw =  self._canvas.winfo_width()
         maxW = self._maxLabel.winfo_width()
-        pw = minW + cw + maxW
+        #pw = minW + cw + maxW
+        pw = maxW + cw/2.0
         ch =  self._canvas.winfo_height()
         sh = self.winfo_screenheight()
 
         # Compensate if too close to edge of screen
         if y + ch > sh and y > sh / 2:
-            y = self._entryfield.winfo_rooty() - ch
+            y = self._arrowBtn.winfo_rooty() - ch
 
-        Pmw.setgeometryanddeiconify(self._popup, '+%d+%d' % (x + w - pw, y))
+        Pmw.setgeometryanddeiconify(self._popup, '+%d+%d' % (x - pw, y))
 
         # Grab the popup, so that all events are delivered to it, and
         # set focus to the canvas, to make keyboard navigation
@@ -640,8 +322,6 @@ class PopupSlider(Pmw.MegaWidget):
         Pmw.pushgrab(self._popup, 1, self._unpostCanvas)
         self._canvas.focus_set()
 
-        self._drawArrow()
-
         # Ignore the first release of the mouse button after posting the
         # dropdown canvas, unless the mouse enters the dropdown canvas.
         self._fUpdate = 0
@@ -650,23 +330,37 @@ class PopupSlider(Pmw.MegaWidget):
         self._fPressInsde = 0
 
     def _updateValue(self,event):
-        canvasX = self._canvas.canvasx(
+        mouseX = self._canvas.canvasx(
             event.x_root - self._canvas.winfo_rootx())
-        if canvasX < self.left:
-            canvasX = self.left
-        if canvasX > self.right:
-            canvasX = self.right
-        # Get current marker position
-        currX = self._getMarkerX()
-        dx = canvasX - currX
-        self._canvas.move('slider', dx, 0)
-
-    def _dropdownBtnPress(self, event):
-        self._fUpdate = 1
-        self._fPressInside = 1
-        self._updateValue(event)
+        if mouseX < self.left:
+            mouseX = self.left
+        if mouseX > self.right:
+            mouseX = self.right
+        # Update value
+        sf = (mouseX - self.left)/(self.right - self.left)
+        newVal = sf * (self['max'] - self['min']) + self['min']
+        self.set(newVal)
+
+    def _sliderBtnPress(self, event):
+        # Check behavior for this button press
+        if self['style'] == VALUATOR_MINI:
+            widget = self._popup
+            xPos = event.x_root - widget.winfo_rootx()
+            yPos = event.y_root - widget.winfo_rooty()
+            fInside = ((xPos > 0) and (xPos < widget.winfo_width()) and
+                       (yPos > 0) and (yPos < widget.winfo_height()))
+        else:
+            fInside = 1
+        # Set flags based upon result
+        if fInside:
+            self._fPressInside = 1
+            self._fUpdate = 1
+            self._updateValue(event)
+        else:
+            self._fPressInside = 0
+            self._fUpdate = 0
             
-    def _dropdownMove(self, event):
+    def _sliderMove(self, event):
         if self._firstPress and not self._fUpdate:
             canvasY = self._canvas.canvasy(
                 event.y_root - self._canvas.winfo_rooty())
@@ -676,7 +370,7 @@ class PopupSlider(Pmw.MegaWidget):
         elif self._fUpdate:
             self._updateValue(event)
 
-    def _dropdownBtnRelease(self, event):
+    def _sliderBtnRelease(self, event):
         if (self._fUnpost or
             (not (self._firstPress or self._fPressInside))):
             self._unpostCanvas()
@@ -688,14 +382,6 @@ class PopupSlider(Pmw.MegaWidget):
     def _unpostOnNextRelease(self, event = None):
 	self._fUnpost = 1
 
-    def _resizeArrow(self, event):
-	bw = (string.atoi(self._arrowBtn['borderwidth']) + 
-		string.atoi(self._arrowBtn['highlightthickness']))
-	newHeight = self._entryfield.winfo_reqheight() - 2 * bw
-	newWidth = int(newHeight * self['buttonaspect'])
-	self._arrowBtn.configure(width=newWidth, height=newHeight)
-	self._drawArrow()
-
     def _unpostCanvas(self, event=None):
 	if not self._isPosted:
             # It is possible to get events on an unposted popup.  For
@@ -714,12 +400,66 @@ class PopupSlider(Pmw.MegaWidget):
 	self._popup.withdraw()
 
 	self._isPosted = 0
-	self._drawArrow()
+
+        if self['style'] == VALUATOR_MINI:
+            self.interior()['relief'] = RAISED
+
+    def _incrementValue(self, event):
+        self.set(self.value + self.increment)
+    def _bigIncrementValue(self, event):
+        self.set(self.value + self.increment * 10.0)
+    def _decrementValue(self, event):
+        self.set(self.value - self.increment)
+    def _bigDecrementValue(self, event):
+        self.set(self.value - self.increment * 10.0)
+    def _goToMin(self, event):
+        self.set(self['min'])
+    def _goToMax(self, event):
+        self.set(self['max'])
+
+    # Methods to modify floater characteristics    
+    def setMin(self):
+        self._minLabel['text'] = self.formatString % self['min']
+        self.updateIndicator(self.value)
+
+    def setMax(self):
+        self._maxLabel['text'] = self.formatString % self['max']
+        self.updateIndicator(self.value)
+
+    def setNumDigits(self):
+        self.formatString = '%0.' + ('%d' % self['numDigits']) + 'f'
+        self._minLabel['text'] = self.formatString % self['min']
+        self._maxLabel['text'] = self.formatString % self['max']
+        self.updateIndicator(self.value)
+        self.increment = pow(10, -self['numDigits'])
+
+    def _changeConfiguration(self, event):
+        newWidth = self._canvas.winfo_width()
+        self.left = -newWidth/2.0 + self.xPad
+        self.right = newWidth/2.0 - self.xPad
+        self._canvas.configure(scrollregion = (-newWidth/2.0, self.top,
+                                               newWidth/2.0, self.bottom))
+        self._canvas.coords('line', self.left, 0, self.right, 0)
+        self.updateIndicator(self.value)
 
     def _getMarkerX(self):
         # Get marker triangle coordinates
         c = self._canvas.coords(self._marker)
         # Marker postion defined as X position of third vertex
         return c[4]
-        
-Pmw.forwardmethods(PopupSlider, Pmw.EntryField, '_entryfield')
+
+    def setRelief(self):
+        self.interior()['relief'] = self['relief']
+
+    def setBorderwidth(self):
+        self.interior()['borderwidth'] = self['borderwidth']
+
+    def setBackground(self):
+        self._canvas['background'] = self['background']
+
+    def highlightWidget(self, event):
+        self._arrowBtn.itemconfigure('arrow', fill = 'black')
+
+    def restoreWidget(self, event):
+        self._arrowBtn.itemconfigure('arrow', fill = '#A0A0A0')
+

+ 117 - 2
direct/src/tkwidgets/Valuator.py

@@ -4,6 +4,9 @@ import Pmw
 import WidgetPropertiesDialog
 import string
 
+VALUATOR_MINI = 'mini'
+VALUATOR_FULL = 'full'
+
 class Valuator(Pmw.MegaWidget):
     sfBase = 3.0
     sfDist = 7
@@ -357,6 +360,11 @@ class ValuatorGroup(Pmw.MegaWidget):
             ('labels',          DEFAULT_LABELS,         self._updateLabels),
             # The command to be executed when one of the valuators is updated
             ('command',         None,                   None),
+            # Callbacks to execute when updating widget's value
+            ('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)
 
@@ -376,6 +384,9 @@ class ValuatorGroup(Pmw.MegaWidget):
             if self['type'] == DIAL:
                 import Dial
                 valuatorType = Dial.Dial
+            elif self['type'] == SLIDER:
+                import Slider
+                valuatorType = Slider.Slider
             else:
                 import Floater
                 valuatorType = Floater.Floater
@@ -383,7 +394,10 @@ class ValuatorGroup(Pmw.MegaWidget):
                 'valuator%d' % index, (), 'valuator', valuatorType,
                 (interior,), value = self._value[index],
                 text = self['labels'][index],
-                command = lambda val, i = index: self._valuatorSetAt(i, val)
+                command = lambda val, i = index: self._valuatorSetAt(i, val),
+                preCallback = self._preCallback,
+                postCallback = self._postCallback,
+                callbackData = [self],
                 )
             f.pack(side = self['side'], expand = 1, fill = X)
             self._valuatorList.append(f)
@@ -428,6 +442,16 @@ class ValuatorGroup(Pmw.MegaWidget):
             for index in range(self['dim']):
                 self._valuatorList[index]['text'] = self['labels'][index]
 
+    def _preCallback(self, valGroup):
+        # Execute pre callback
+        if self['preCallback']:
+            apply(self['preCallback'], valGroup.get())
+        
+    def _postCallback(self, valGroup):
+        # Execute post callback
+        if self['postCallback']:
+            apply(self['postCallback'], valGroup.get())
+
     def __len__(self):
         return self['dim']
     
@@ -464,6 +488,13 @@ class ValuatorGroupPanel(Pmw.MegaToplevel):
             ('numDigits',       2,                      self._setNumDigits),
             # The command to be executed when one of the floaters is updated
             ('command',         None,                   self._setCommand),
+            # Callbacks to execute when updating widget's value
+            ('preCallback',       None,                 self._setPreCallback),
+            ('postCallback',      None,                 self._setPostCallback),
+            # Extra data to be passed to callback function, needs to be a list
+            ('callbackData',      [],                   self._setCallbackData),
+            # Destroy or withdraw
+            ('fDestroy',        0,                      INITOPT)
             )
         self.defineoptions(kw, optiondefs)
 
@@ -486,9 +517,15 @@ class ValuatorGroupPanel(Pmw.MegaToplevel):
             'Valuator Group', 'command', 'Reset the Valuator Group panel',
             label = 'Reset',
             command = lambda s = self: s.reset())
+
+        if self['fDestroy']:
+            dismissCommand = self.destroy
+        else:
+            dismissCommand = self.withdraw
+
         menubar.addmenuitem(
             'Valuator Group', 'command', 'Dismiss Valuator Group panel',
-            label = 'Dismiss', command = self.withdraw)
+            label = 'Dismiss', command = dismissCommand)
         
         menubar.addmenu('Help', 'Valuator Group Help Operations')
         self.toggleBalloonVar = IntVar()
@@ -530,7 +567,85 @@ class ValuatorGroupPanel(Pmw.MegaToplevel):
     def _setCommand(self):
         self.valuatorGroup['command'] = self['command']
 
+    def _setPreCallback(self):
+        self.valuatorGroup['preCallback'] = self['preCallback']
+
+    def _setPostCallback(self):
+        self.valuatorGroup['postCallback'] = self['postCallback']
+
+    def _setCallbackData(self):
+        self.valuatorGroup['callbackData'] = self['callbackData']
+
     def reset(self):
         self.set(self['value'])
 
 Pmw.forwardmethods(ValuatorGroupPanel, ValuatorGroup, 'valuatorGroup')
+
+
+def rgbPanel(nodePath, callback = None, style = 'full'):
+    def setNodePathColor(color, np = nodePath, cb = callback):
+        np.setColor(color[0]/255.0, color[1]/255.0,
+                    color[2]/255.0, color[3]/255.0)
+        # Execute callback to pass along color info
+        if cb:
+            cb(color)
+    # Check init color
+    if nodePath.hasColor():
+        initColor = nodePath.getColor() * 255.0
+    else:
+        initColor = Vec4(255)
+    # Create entry scale group
+    esg = ValuatorGroupPanel(title = 'RGBA Panel: ' + nodePath.getName(),
+                             dim = 4,
+                             labels = ['R','G','B','A'],
+                             value = [int(initColor[0]),
+                                      int(initColor[1]),
+                                      int(initColor[2]),
+                                      int(initColor[3])],
+                             type = 'slider',
+                             valuator_style = style,
+                             valuator_min = 0,
+                             valuator_max = 255,
+                             valuator_resolution = 1,
+                             # Destroy not withdraw panel on dismiss
+                             fDestroy = 1,
+                             command = setNodePathColor)
+    # Update menu button
+    esg.component('menubar').component('Valuator Group-button')['text'] = (
+        'RGBA Panel')
+    # Update menu
+    menu = esg.component('menubar').component('Valuator Group-menu')
+    # Some helper functions
+    # Clear color
+    menu.insert_command(index = 1, label = 'Clear Color',
+                        command = lambda np = nodePath: np.clearColor())
+    # Set Clear Transparency
+    menu.insert_command(index = 2, label = 'Set Transparency',
+                        command = lambda np = nodePath: np.setTransparency(1))
+    menu.insert_command(
+        index = 3, label = 'Clear Transparency',
+        command = lambda np = nodePath: np.clearTransparency())
+
+    # System color picker
+    def popupColorPicker(esg = esg):
+        # Can pass in current color with: color = (255, 0, 0)
+        color = tkColorChooser.askcolor(
+            parent = esg.interior(),
+            # Initialize it to current color
+            initialcolor = tuple(esg.get()[:3]))[0]
+        if color:
+            esg.set((color[0], color[1], color[2], esg.getAt(3)))
+    menu.insert_command(index = 4, label = 'Popup Color Picker',
+                        command = popupColorPicker)
+    def printToLog(nodePath=nodePath):
+        c=nodePath.getColor()
+        print "Vec4(%.3f, %.3f, %.3f, %.3f)"%(c[0], c[1], c[2], c[3])
+    menu.insert_command(index = 5, label = 'Print to log',
+                        command = printToLog)
+    
+    # Set callback
+    def onRelease(r,g,b,a, nodePath = nodePath):
+        messenger.send('RGBPanel_setColor', [nodePath, r,g,b,a])
+    esg['postCallback'] = onRelease
+    return esg
+

+ 22 - 22
direct/src/tkwidgets/VectorWidgets.py

@@ -2,7 +2,7 @@ from Tkinter import *
 import Pmw
 import Valuator
 import Floater
-import EntryScale
+import Slider
 import string
 import tkColorChooser
 
@@ -15,7 +15,6 @@ class VectorEntry(Pmw.MegaWidget):
         DEFAULT_VALUE = [0.0] * kw.get('dim', DEFAULT_DIM)
         DEFAULT_LABELS = map(lambda x: 'v[%d]' % x,
                              range(kw.get('dim', DEFAULT_DIM)))
-        VALUATOR = kw.get('valuatorType', Floater.FloaterGroup)
 
         # Process options
         INITOPT = Pmw.INITOPT
@@ -31,8 +30,8 @@ class VectorEntry(Pmw.MegaWidget):
             ('text',                'Vector:',      self._updateText),
             ('min',                 None,           self._updateValidate),
             ('max',                 None,           self._updateValidate),
-            ('numDigits',   2,              self._setSigDigits),
-            ('valuatorType',        VALUATOR,       None),
+            ('numDigits',           2,              self._setSigDigits),
+            ('type',                'floater',      None),
             ('state',               'normal',       self._setState),
             )
         self.defineoptions(kw, optiondefs)
@@ -92,9 +91,11 @@ class VectorEntry(Pmw.MegaWidget):
         self._floaters = self.createcomponent(
             'floaterGroup',
             (('fGroup', 'floaterGroup'),
-             ('Valuator', 'floaterGroup_Valuator'),), None,
-            self['valuatorType'], (self.interior(),),
-            dim = self['dim'], title = self['text'],
+             ('valuator', 'floaterGroup_valuator'),), None,
+            Valuator.ValuatorGroupPanel, (self.interior(),),
+            dim = self['dim'],
+            #title = self['text'],
+            type = self['type'],
             command = self.set)
         # Note: This means the 'X' on the menu bar doesn't really destroy
         # the panel, just withdraws it.  This is to avoid problems which occur
@@ -145,7 +146,7 @@ class VectorEntry(Pmw.MegaWidget):
     def _setSigDigits(self):
         sd = self['numDigits']
         self.entryFormat = '%.' + '%d' % sd + 'f'
-        self.configure(Valuator_numDigits = sd)
+        self.configure(valuator_numDigits = sd)
         # And refresh value to reflect change
         for index in range(self['dim']):
             self._refreshEntry(index)
@@ -159,8 +160,8 @@ class VectorEntry(Pmw.MegaWidget):
             'minstrict' : 0,
             'maxstrict' : 0})
         # Reflect changes in floaters
-        self.configure(Valuator_min = self['min'],
-                       Valuator_max = self['max'])            
+        self.configure(valuator_min = self['min'],
+                       valuator_max = self['max'])            
 
     def get(self):
         return self._value
@@ -234,24 +235,24 @@ class VectorEntry(Pmw.MegaWidget):
             self.configure(Entry_entry_background = '#C0C0C0')
             # Disable floater Group scale
             self.component('fGroup').configure(
-                Valuator_state = 'disabled')
+                valuator_state = 'disabled')
             # Disable floater group entry
             self.component('fGroup').configure(
-                Valuator_entry_state = 'disabled')
+                valuator_entry_state = 'disabled')
             self.component('fGroup').configure(
-                Valuator_entry_background = '#C0C0C0')
+                valuator_entry_background = '#C0C0C0')
         else:
             # Disable entry
             self.configure(Entry_entry_state = 'normal')
             self.configure(Entry_entry_background = self.entryBackground)
             # Disable floater Group scale
             self.component('fGroup').configure(
-                Valuator_state = 'normal')
+                valuator_state = 'normal')
             # Disable floater group entry
             self.component('fGroup').configure(
-                Valuator_entry_state = 'normal')
+                valuator_entry_state = 'normal')
             self.component('fGroup').configure(
-                Valuator_entry_background = self.entryBackground)
+                valuator_entry_background = self.entryBackground)
 
 class Vector2Entry(VectorEntry):
     def __init__(self, parent = None, **kw):
@@ -300,18 +301,17 @@ class ColorEntry(VectorEntry):
         # Initialize options for the class (overriding some superclass options)
         optiondefs = (
             ('dim',                     4,                  Pmw.INITOPT),
+            ('type',                    'slider',           Pmw.INITOPT),
             ('fGroup_labels',           ('R','G','B','A'),  None),
             ('min',                     0.0,                None),
             ('max',                     255.0,              None),
-            ('nuDigits',       0,                  None),
-            ('Valuator_resolution',     1.0,                None),
+            ('nuDigits',                0,                  None),
+            ('valuator_resolution',     1.0,                None),
             )
         self.defineoptions(kw, optiondefs)
-        #kw['valuatorType'] = EntryScale.EntryScaleGroup
-        #kw['dim'] = self['dim']
+
         # Initialize the superclass, make sure dim makes it to superclass
-        VectorEntry.__init__(self, parent, dim = self['dim'],
-                             valuatorType = EntryScale.EntryScaleGroup)
+        VectorEntry.__init__(self, parent, dim = self['dim'])
         # Add menu item to popup color picker
         self.addMenuItem(
             'Popup color picker',

+ 8 - 2
direct/src/tkwidgets/WidgetPropertiesDialog.py

@@ -111,12 +111,18 @@ class WidgetPropertiesDialog(Toplevel):
             # Set up help string and validator based upon type
             if entryType == 'real':
                 # Only allow real numbers
-                entry['validate'] = { 'validator' : self.realOrNone }
+                if fAllowNone:
+                    entry['validate'] = { 'validator' : self.realOrNone }
+                else:
+                    entry['validate'] = { 'validator' : 'real' }
                 if helpString is None:
                     helpString = 'Enter a floating point number' + extra + '.'
             elif entryType == 'integer':
                 # Only allow integer values
-                entry['validate'] = { 'validator' : self.intOrNone }
+                if fAllowNone:
+                    entry['validate'] = { 'validator' : self.intOrNone }
+                else:
+                    entry['validate'] = { 'validator' : 'integer' }
                 if helpString is None:
                     helpString = 'Enter an integer' + extra + '.'
             else: