From c6178a4d81579cf30e2492f2a5f81e787b85af89 Mon Sep 17 00:00:00 2001 From: Mark Mine Date: Wed, 20 Feb 2002 02:55:46 +0000 Subject: [PATCH] *** empty log message *** --- direct/src/directtools/DirectSession.py | 4 +- direct/src/extensions/NodePath-extensions.py | 4 +- direct/src/leveleditor/LevelEditor.py | 1 - direct/src/showbase/PythonUtil.py | 6 +- direct/src/tkpanels/DirectSessionPanel.py | 63 +- direct/src/tkpanels/MopathRecorder.py | 45 +- direct/src/tkpanels/ParticlePanel.py | 24 +- direct/src/tkpanels/Placer.py | 6 +- direct/src/tkwidgets/AppShell.py | 10 + direct/src/tkwidgets/Dial.py | 12 +- direct/src/tkwidgets/Floater.py | 5 +- direct/src/tkwidgets/Slider.py | 728 ++++++------------ direct/src/tkwidgets/Valuator.py | 119 ++- direct/src/tkwidgets/VectorWidgets.py | 44 +- .../src/tkwidgets/WidgetPropertiesDialog.py | 10 +- 15 files changed, 481 insertions(+), 600 deletions(-) diff --git a/direct/src/directtools/DirectSession.py b/direct/src/directtools/DirectSession.py index acf75bfcc2..3e7376b87d 100644 --- a/direct/src/directtools/DirectSession.py +++ b/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], diff --git a/direct/src/extensions/NodePath-extensions.py b/direct/src/extensions/NodePath-extensions.py index c98c5b4996..bf9a07d8eb 100644 --- a/direct/src/extensions/NodePath-extensions.py +++ b/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 diff --git a/direct/src/leveleditor/LevelEditor.py b/direct/src/leveleditor/LevelEditor.py index 95c5a3f72d..11cc56cdf3 100644 --- a/direct/src/leveleditor/LevelEditor.py +++ b/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 diff --git a/direct/src/showbase/PythonUtil.py b/direct/src/showbase/PythonUtil.py index c9be889d97..58c52f17cc 100644 --- a/direct/src/showbase/PythonUtil.py +++ b/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 diff --git a/direct/src/tkpanels/DirectSessionPanel.py b/direct/src/tkpanels/DirectSessionPanel.py index 99de95073c..f5b19625e7 100644 --- a/direct/src/tkpanels/DirectSessionPanel.py +++ b/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() diff --git a/direct/src/tkpanels/MopathRecorder.py b/direct/src/tkpanels/MopathRecorder.py index d82fe5c1e5..b3af9b9fc8 100644 --- a/direct/src/tkpanels/MopathRecorder.py +++ b/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, diff --git a/direct/src/tkpanels/ParticlePanel.py b/direct/src/tkpanels/ParticlePanel.py index 32c8d2c6e9..ec4ad24f64 100644 --- a/direct/src/tkpanels/ParticlePanel.py +++ b/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) diff --git a/direct/src/tkpanels/Placer.py b/direct/src/tkpanels/Placer.py index 803126907b..d3f0d54633 100644 --- a/direct/src/tkpanels/Placer.py +++ b/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') diff --git a/direct/src/tkwidgets/AppShell.py b/direct/src/tkwidgets/AppShell.py index 3bd1fcdb73..07409201b8 100644 --- a/direct/src/tkwidgets/AppShell.py +++ b/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): diff --git a/direct/src/tkwidgets/Dial.py b/direct/src/tkwidgets/Dial.py index b8a1cf042b..aa5377a8a0 100644 --- a/direct/src/tkwidgets/Dial.py +++ b/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('', 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 diff --git a/direct/src/tkwidgets/Floater.py b/direct/src/tkwidgets/Floater.py index 8ea502c7b8..b2c55b45fc 100644 --- a/direct/src/tkwidgets/Floater.py +++ b/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) diff --git a/direct/src/tkwidgets/Slider.py b/direct/src/tkwidgets/Slider.py index 29d4e231e2..54496809f6 100644 --- a/direct/src/tkwidgets/Slider.py +++ b/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('', self.mouseReset) + # Add popup bindings to slider widget + try: + self._valuator._arrowBtn.bind( + '', self._popupValuatorMenu) + except AttributeError: + pass + self._valuator._minLabel.bind( + '', self._popupValuatorMenu) + self._valuator._maxLabel.bind( + '', 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('', self.mouseDown) - self._canvas.bind('', self.updateSliderSF) - self._canvas.bind('', self.mouseUp) - self._canvas.bind('', self.highlightWidget) - self._canvas.bind('', 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('', 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('', move) - -def release(event): - if fCrossedLine: - pw.withdraw() - b.unbind('') - pw._canvas.unbind('') - else: - Pmw.pushgrab(pw._canvas, 1, unpostCanvas) - pw._canvas.focus_set() - -b.bind('', press) -b.bind('', release) -pw._canvas.bind('', popupPress) -pw._canvas.bind('', 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('', self._decrementValue) + self._canvas.bind('', self._incrementValue) + self._canvas.bind('', self._bigDecrementValue) + self._canvas.bind('', self._bigIncrementValue) + self._canvas.bind('', self._goToMin) + self._canvas.bind('', 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('', self._drawArrow) - - # Bind events to the dropdown window. - self._popup.bind('', self._unpostCanvas) - self._popup.bind('', self._dropdownBtnRelease) - self._popup.bind('', self._dropdownBtnPress) - self._popup.bind('', self._dropdownMove) - - # Bind events to the Tk listbox. - #self._canvas.bind('', self._unpostOnNextRelease) - - # Bind events to the Tk entry widget. - self._entryWidget.bind('', self._resizeArrow) - - # Need to unpost the popup if the entryfield is unmapped (eg: + self._arrowBtn.bind('', self.highlightWidget) + self._arrowBtn.bind('', 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('', self._unpostCanvas) + self._arrowBtn.bind('', self._unpostCanvas) + + # Bind events to the dropdown window. + self._popup.bind('', self._unpostCanvas) + self._popup.bind('', self._sliderBtnRelease) + self._popup.bind('', self._sliderBtnPress) + self._popup.bind('', 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('', self._sliderBtnRelease) + self._canvas.bind('', self._sliderBtnPress) + self._canvas.bind('', self._sliderMove) + self._canvas.bind('', 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) + 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 _dropdownBtnPress(self, event): - self._fUpdate = 1 - self._fPressInside = 1 - self._updateValue(event) + 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') + diff --git a/direct/src/tkwidgets/Valuator.py b/direct/src/tkwidgets/Valuator.py index 2b369e44d5..f732bdab02 100644 --- a/direct/src/tkwidgets/Valuator.py +++ b/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 + diff --git a/direct/src/tkwidgets/VectorWidgets.py b/direct/src/tkwidgets/VectorWidgets.py index 173b6d58a0..9fa0ce2126 100644 --- a/direct/src/tkwidgets/VectorWidgets.py +++ b/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', diff --git a/direct/src/tkwidgets/WidgetPropertiesDialog.py b/direct/src/tkwidgets/WidgetPropertiesDialog.py index f897fa46b2..9ea59a81f6 100644 --- a/direct/src/tkwidgets/WidgetPropertiesDialog.py +++ b/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: