*** empty log message ***

This commit is contained in:
Mark Mine 2002-02-20 02:55:46 +00:00
parent f2bd33cd7c
commit c6178a4d81
15 changed files with 481 additions and 600 deletions

View File

@ -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],

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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',

View File

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