| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546 |
- """
- EntryScale Class: Scale with a label, and a linked and validated entry
- """
- from PandaObject import *
- from Tkinter import *
- import Pmw
- import string
- import tkColorChooser
- from tkSimpleDialog import *
- """
- Change Min/Max buttons to labels, add highlight binding
- """
- class EntryScale(Pmw.MegaWidget):
- "Scale with linked and validated entry"
-
- def __init__(self, parent = None, **kw):
- # Define the megawidget options.
- optiondefs = (
- ('initialValue', 0.0, Pmw.INITOPT),
- ('resolution', 0.001, None),
- ('command', None, None),
- ('callbackData', [], None),
- ('min', 0.0, self._updateValidate),
- ('max', 100.0, self._updateValidate),
- ('text', 'EntryScale', self._updateLabelText),
- ('significantDigits', 2, self._setSigDigits),
- )
- self.defineoptions(kw, optiondefs)
-
- # Initialise superclass
- Pmw.MegaWidget.__init__(self, parent)
- # Initialize some class variables
- self.value = self['initialValue']
- self.entryFormat = '%.2f'
- self.fScaleCommand = 0
- # Create the components.
- # Setup up container
- interior = self.interior()
- interior.configure(relief = GROOVE, borderwidth = 2)
- # Create a label and an entry
- self.labelFrame = self.createcomponent('frame', (), None,
- Frame, interior)
- # Create an entry field to display and validate the entryScale's value
- self.entryValue = StringVar()
- self.entryValue.set(self['initialValue'])
- self.entry = self.createcomponent('entryField',
- # Access widget's entry using "entry"
- (('entry', 'entryField_entry'),),
- None,
- Pmw.EntryField, self.labelFrame,
- entry_width = 10,
- validate = { 'validator' : 'real',
- 'min' : self['min'],
- 'max' : self['max'],
- 'minstrict' : 0,
- 'maxstrict' : 0},
- entry_justify = 'right',
- entry_textvar = self.entryValue,
- command = self._entryCommand)
- self.entry.pack(side='left',padx = 4)
-
- # Create the EntryScale's label
- self.label = self.createcomponent('label', (), None,
- Label, self.labelFrame,
- text = self['text'],
- width = 12,
- anchor = 'center',
- font = "Arial 12 bold")
- self.label.pack(side='left', expand = 1, fill = 'x')
- self.label.bind('<Button-3>', self.askForLabel)
- # Now pack the frame
- self.labelFrame.pack(expand = 1, fill = 'both')
- # Create a label and an entry
- self.minMaxFrame = self.createcomponent('frame', (), None,
- Frame, interior)
- # Create the EntryScale's min max labels
- self.minLabel = self.createcomponent('minLabel', (), None,
- Label, self.minMaxFrame,
- text = `self['min']`,
- relief = FLAT,
- width = 5,
- anchor = W,
- font = "Arial 8")
- self.minLabel.pack(side='left', fill = 'x')
- self.minLabel.bind('<Button-3>', self.askForMin)
- # Create the scale component.
- self.scale = self.createcomponent('scale', (), None,
- Scale, self.minMaxFrame,
- command = self._scaleCommand,
- orient = 'horizontal',
- length = 150,
- from_ = self['min'],
- to = self['max'],
- resolution = self['resolution'],
- showvalue = 0)
- self.scale.pack(side = 'left', expand = 1, fill = 'x')
- # Set scale to the middle of its range
- self.scale.set(self['initialValue'])
- self.scale.bind('<Button-1>', self.__onPress)
- self.scale.bind('<ButtonRelease-1>', self.__onRelease)
- self.scale.bind('<Button-3>', self.askForResolution)
- self.maxLabel = self.createcomponent('maxLabel', (), None,
- Label, self.minMaxFrame,
- text = `self['max']`,
- relief = FLAT,
- width = 5,
- anchor = E,
- font = "Arial 8")
- self.maxLabel.bind('<Button-3>', self.askForMax)
- self.maxLabel.pack(side='left', fill = 'x')
- self.minMaxFrame.pack(expand = 1, fill = 'both')
-
- # Check keywords and initialise options based on input values.
- self.initialiseoptions(EntryScale)
- def label(self):
- return self.label
- def scale(self):
- return self.scale
- def entry(self):
- return self.entry
- def askForLabel(self, event = None):
- newLabel = askstring(title = self['text'],
- prompt = 'New label:',
- initialvalue = `self['text']`,
- parent = self.interior())
- if newLabel:
- self['text'] = newLabel
-
- def askForMin(self, event = None):
- newMin = askfloat(title = self['text'],
- prompt = 'New min val:',
- initialvalue = `self['min']`,
- parent = self.interior())
- if newMin:
- self.setMin(newMin)
-
- def setMin(self, newMin):
- self['min'] = newMin
- self.scale['from_'] = newMin
- self.minLabel['text'] = newMin
- self.entry.checkentry()
-
- def askForMax(self, event = None):
- newMax = askfloat(title = self['text'],
- parent = self.interior(),
- initialvalue = self['max'],
- prompt = 'New max val:')
- if newMax:
- self.setMax(newMax)
- def setMax(self, newMax):
- self['max'] = newMax
- self.scale['to'] = newMax
- self.maxLabel['text'] = newMax
- self.entry.checkentry()
-
- def askForResolution(self, event = None):
- newResolution = askfloat(title = self['text'],
- parent = self.interior(),
- initialvalue = self['resolution'],
- prompt = 'New resolution:')
- if newResolution:
- self.setResolution(newResolution)
- def setResolution(self, newResolution):
- self['resolution'] = newResolution
- self.scale['resolution'] = newResolution
- self.entry.checkentry()
-
- def _updateLabelText(self):
- self.label['text'] = self['text']
- def _updateValidate(self):
- self.configure(entryField_validate = {
- 'validator' : 'real',
- 'min' : self['min'],
- 'max' : self['max'],
- 'minstrict' : 0,
- 'maxstrict' : 0})
- self.minLabel['text'] = self['min']
- self.scale['from_'] = self['min']
- self.scale['to'] = self['max']
- self.maxLabel['text'] = self['max']
- def _scaleCommand(self, strVal):
- if not self.fScaleCommand:
- return
- # convert scale val to float
- self.set(string.atof(strVal))
- """
- # Update entry to reflect formatted value
- self.entryValue.set( self.entryFormat % self.value )
- self.entry.checkentry()
- if self['command']:
- self['command'](self.value)
- """
- def _entryCommand(self, event = None):
- try:
- val = string.atof( self.entryValue.get() )
- apply(self.onReturn,self['callbackData'])
- self.set( val )
- apply(self.onReturnRelease,self['callbackData'])
- except ValueError:
- pass
- def _setSigDigits(self):
- sd = self['significantDigits']
- self.entryFormat = '%.' + '%d' % sd + 'f'
- # And reset value to reflect change
- self.entryValue.set( self.entryFormat % self.value )
- def get(self):
- return self.value
-
- def set(self, newVal, fCommand = 1):
- # Clamp value
- if self['min'] is not None:
- if newVal < self['min']:
- newVal = self['min']
- if self['max'] is not None:
- if newVal > self['max']:
- newVal = self['max']
- # Round by resolution
- if self['resolution'] is not None:
- newVal = round(newVal / self['resolution']) * self['resolution']
- # Record updated value
- self.value = newVal
- # Update scale's position
- self.scale.set(newVal)
- # Update entry to reflect formatted value
- self.entryValue.set( self.entryFormat % self.value )
- self.entry.checkentry()
-
- # execute command
- if fCommand and (self['command'] is not None):
- self['command']( newVal )
- def onReturn(self, *args):
- """ User redefinable callback executed on <Return> in entry """
- pass
- def onReturnRelease(self, *args):
- """ User redefinable callback executed on <Return> release in entry """
- pass
- def __onPress(self, event):
- # First execute onpress callback
- apply(self.onPress, self['callbackData'])
- # Now enable slider command
- self.fScaleCommand = 1
- def onPress(self, *args):
- """ User redefinable callback executed on button press """
- pass
- def __onRelease(self, event):
- # Now disable slider command
- self.fScaleCommand = 0
- # First execute onpress callback
- apply(self.onRelease, self['callbackData'])
- def onRelease(self, *args):
- """ User redefinable callback executed on button release """
- pass
- class EntryScaleGroup(Pmw.MegaToplevel):
- def __init__(self, parent = None, **kw):
- # Default group size
- DEFAULT_DIM = 1
- # Default value depends on *actual* group size, test for user input
- DEFAULT_VALUE = [0.0] * kw.get('dim', DEFAULT_DIM)
- DEFAULT_LABELS = map(lambda x: 'v[%d]' % x,
- range(kw.get('dim', DEFAULT_DIM)))
- #define the megawidget options
- INITOPT = Pmw.INITOPT
- optiondefs = (
- ('dim', DEFAULT_DIM, INITOPT),
- ('side', TOP, INITOPT),
- ('title', 'EntryScale Group', None),
- # A tuple of initial values, one for each entryScale
- ('initialValue', DEFAULT_VALUE, INITOPT),
- # The command to be executed any time one of the entryScales is updated
- ('command', None, None),
- # A tuple of labels, one for each entryScale
- ('labels', DEFAULT_LABELS, self._updateLabels),
- # Destroy or withdraw
- ('fDestroy', 0, INITOPT)
- )
- self.defineoptions(kw, optiondefs)
- # Initialize the toplevel widget
- Pmw.MegaToplevel.__init__(self, parent)
-
- # Create the components
- interior = self.interior()
- # Get a copy of the initial value (making sure its a list)
- self._value = list(self['initialValue'])
- # The Menu Bar
- self.balloon = Pmw.Balloon()
- menubar = self.createcomponent('menubar',(), None,
- Pmw.MenuBar, (interior,),
- balloon = self.balloon)
- menubar.pack(fill=X)
-
- # EntryScaleGroup Menu
- menubar.addmenu('EntryScale Group', 'EntryScale Group Operations')
- menubar.addmenuitem(
- 'EntryScale Group', 'command', 'Reset the EntryScale Group panel',
- label = 'Reset',
- command = lambda s = self: s.reset())
- if self['fDestroy']:
- dismissCommand = self.destroy
- else:
- dismissCommand = self.withdraw
- menubar.addmenuitem(
- 'EntryScale Group', 'command', 'Dismiss EntryScale Group panel',
- label = 'Dismiss', command = dismissCommand)
-
- menubar.addmenu('Help', 'EntryScale Group Help Operations')
- self.toggleBalloonVar = IntVar()
- self.toggleBalloonVar.set(0)
- menubar.addmenuitem('Help', 'checkbutton',
- 'Toggle balloon help',
- label = 'Balloon Help',
- variable = self.toggleBalloonVar,
- command = self.toggleBalloon)
- self.entryScaleList = []
- for index in range(self['dim']):
- # Add a group alias so you can configure the entryScales via:
- # fg.configure(Valuator_XXX = YYY)
- f = self.createcomponent(
- 'entryScale%d' % index, (), 'Valuator', EntryScale,
- (interior,), initialValue = self._value[index],
- text = self['labels'][index])
- # Do this separately so command doesn't get executed during construction
- f['command'] = lambda val, s=self, i=index: s._entryScaleSetAt(i, val)
- f['callbackData'] = [self]
- # Callbacks
- f.onReturn = self.__onReturn
- f.onReturnRelease = self.__onReturnRelease
- f.onPress = self.__onPress
- f.onRelease = self.__onRelease
- f.pack(side = self['side'], expand = 1, fill = X)
- self.entryScaleList.append(f)
- # Make sure entryScales are initialized
- self.set(self['initialValue'])
-
- # Make sure input variables processed
- self.initialiseoptions(EntryScaleGroup)
- def _updateLabels(self):
- if self['labels']:
- for index in range(self['dim']):
- self.entryScaleList[index]['text'] = self['labels'][index]
- def toggleBalloon(self):
- if self.toggleBalloonVar.get():
- self.balloon.configure(state = 'balloon')
- else:
- self.balloon.configure(state = 'none')
- def get(self):
- return self._value
- def getAt(self,index):
- return self._value[index]
- # This is the command is used to set the groups value
- def set(self, value, fCommand = 1):
- for i in range(self['dim']):
- self._value[i] = value[i]
- # Update entryScale, but don't execute its command
- self.entryScaleList[i].set(value[i], 0)
- if fCommand and (self['command'] is not None):
- self['command'](self._value)
- def setAt(self, index, value):
- # Update entryScale and execute its command
- self.entryScaleList[index].set(value)
- # This is the command used by the entryScale
- def _entryScaleSetAt(self, index, value):
- self._value[index] = value
- if self['command']:
- self['command'](self._value)
- def reset(self):
- self.set(self['initialValue'])
- def __onReturn(self, esg):
- # Execute onReturn callback
- apply(self.onReturn, esg.get())
- def onReturn(self, *args):
- """ User redefinable callback executed on button press """
- pass
- def __onReturnRelease(self, esg):
- # Execute onReturnRelease callback
- apply(self.onReturnRelease, esg.get())
- def onReturnRelease(self, *args):
- """ User redefinable callback executed on button press """
- pass
- def __onPress(self, esg):
- # Execute onPress callback
- apply(self.onPress, esg.get())
- def onPress(self, *args):
- """ User redefinable callback executed on button press """
- pass
- def __onRelease(self, esg):
- # Execute onRelease callback
- apply(self.onRelease, esg.get())
- def onRelease(self, *args):
- """ User redefinable callback executed on button release """
- pass
- def rgbPanel(nodePath, callback = None):
- 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 = EntryScaleGroup(title = 'RGBA Panel: ' + nodePath.getName(),
- dim = 4,
- labels = ['R','G','B','A'],
- initialValue = [int(initColor[0]),
- int(initColor[1]),
- int(initColor[2]),
- int(initColor[3])],
- Valuator_max = 255,
- Valuator_resolution = 1,
- # Destroy not withdraw panel on dismiss
- fDestroy = 1,
- command = setNodePathColor)
- # Update menu button
- esg.component('menubar').component('EntryScale Group-button')['text'] = (
- 'RGBA Panel')
- # Update menu
- menu = esg.component('menubar').component('EntryScale 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.onRelease = onRelease
- return esg
- ## SAMPLE CODE
- if __name__ == '__main__':
- # Initialise Tkinter and Pmw.
- root = Toplevel()
- root.title('Pmw EntryScale demonstration')
- # Dummy command
- def printVal(val):
- print val
-
- # Create and pack a EntryScale megawidget.
- mega1 = EntryScale(root, command = printVal)
- mega1.pack(side = 'left', expand = 1, fill = 'x')
- """
- # These are things you can set/configure
- # Starting value for entryScale
- mega1['initialValue'] = 123.456
- mega1['text'] = 'Drive delta X'
- mega1['min'] = 0.0
- mega1['max'] = 1000.0
- mega1['resolution'] = 1.0
- # To change the color of the label:
- mega1.label['foreground'] = 'Red'
- # Max change/update, default is 100
- # To have really fine control, for example
- # mega1['maxVelocity'] = 0.1
- # Number of digits to the right of the decimal point, default = 2
- # mega1['significantDigits'] = 5
- """
- # To create a entryScale group to set an RGBA value:
- group1 = EntryScaleGroup(root, dim = 4,
- title = 'Simple RGBA Panel',
- labels = ('R', 'G', 'B', 'A'),
- EntryScale_min = 0.0,
- EntryScale_max = 255.0,
- EntryScale_resolution = 1.0,
- command = printVal)
-
- # Uncomment this if you aren't running in IDLE
- #root.mainloop()
|