Poodletooth-iLand/panda/direct/tkwidgets/VectorWidgets.py

348 lines
13 KiB
Python
Raw Normal View History

2015-03-03 16:10:12 -06:00
"""Undocumented Module"""
__all__ = ['VectorEntry', 'Vector2Entry', 'Vector3Entry', 'Vector4Entry', 'ColorEntry']
from direct.showbase.TkGlobal import *
from Tkinter import *
import Valuator
import Floater
import Slider
import string
import tkColorChooser
import types
import Pmw
class VectorEntry(Pmw.MegaWidget):
def __init__(self, parent = None, **kw):
# Default vector size
DEFAULT_DIM = 3
# Default value depends on *actual* vector size, test for user input
DEFAULT_VALUE = [0.0] * kw.get('dim', DEFAULT_DIM)
DEFAULT_LABELS = ['v[%d]' % x for x in range(kw.get('dim', DEFAULT_DIM))]
# Process options
INITOPT = Pmw.INITOPT
optiondefs = (
('dim', DEFAULT_DIM, INITOPT),
('value', DEFAULT_VALUE, INITOPT),
('resetValue', DEFAULT_VALUE, None),
('label_width', 12, None),
('labelIpadx', 2, None),
('command', None, None),
('entryWidth', 8, self._updateEntryWidth),
('relief', GROOVE, self._updateRelief),
('bd', 2, self._updateBorderWidth),
('text', 'Vector:', self._updateText),
('min', None, self._updateValidate),
('max', None, self._updateValidate),
('numDigits', 2, self._setSigDigits),
('type', 'floater', None),
('state', 'normal', self._setState),
)
self.defineoptions(kw, optiondefs)
# Initialize superclass
Pmw.MegaWidget.__init__(self, parent)
# Initialize value
# Make sure its a list (and as a byproduct, make a distinct copy)
self._value = list(self['value'])
self['resetValue'] = self['value']
self._floaters = None
self.entryFormat = '%.2f'
# Get a handle on the parent container
interior = self.interior()
# This does double duty as a menu button
self._label = self.createcomponent('label', (), None,
Menubutton, (interior,),
text = self['text'],
activebackground = '#909090')
self.menu = self._label['menu'] = Menu(self._label)
self.menu.add_command(label = 'Reset', command = self.reset)
self.menu.add_command(label = 'Popup sliders', command = self.popupSliders)
self._label.pack(side = LEFT, fill = X, ipadx = self['labelIpadx'])
self.variableList = []
self.entryList = []
for index in range(self['dim']):
var = StringVar()
self.variableList.append(var)
# To set the configuration of all entrys in a vector use:
# ve.configure(Entry_XXX = YYY)
# To configure an individual entryfield's entry use:
# ve.configure(entry0_XXX = YYY)
entry = self.createcomponent(
'entryField%d' % index,
(('entry%d' % index,
'entryField%d_entry' % index),),
'Entry',
Pmw.EntryField, (interior,),
entry_justify = RIGHT,
entry_textvariable = var,
command = lambda s = self, i = index: s._entryUpdateAt(i))
entry.pack(side = LEFT, expand = 1, fill = X)
self.entryList.append(entry)
# To configure the floaterGroup use:
# ve.configure(floaterGroup_XXX = YYY)
# ve.configure(fGroup_XXX = YYY) or
# To set the configuration all floaters in a group use:
# ve.configure(Valuator_XXX = YYY)
# To configure an individual floater in a group use:
# ve.configure(floaterGroup_floater0_XXX = YYY) or
# ve.configure(fGroup_floater0_XXX = YYY)
self._floaters = self.createcomponent(
'floaterGroup',
(('fGroup', 'floaterGroup'),
('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
# if the user kills the floaterGroup and then tries to pop it open again
self._floaters.userdeletefunc(self._floaters.withdraw)
self._floaters.withdraw()
# Make sure entries are updated
self.set(self['value'])
# Record entry color
self.entryBackground = self.cget('Entry_entry_background')
# Make sure input variables processed
self.initialiseoptions(VectorEntry)
def menu(self):
return self.menu
def label(self):
return self._label
def entry(self, index):
return self.entryList[index]
def entryList(self):
return self.entryList
def floaters(self):
return self._floaters
def _clearFloaters(self):
self._floaters.withdraw()
def _updateText(self):
self._label['text'] = self['text']
def _updateRelief(self):
self.interior()['relief'] = self['relief']
def _updateBorderWidth(self):
self.interior()['bd'] = self['bd']
def _updateEntryWidth(self):
self['Entry_entry_width'] = self['entryWidth']
def _setSigDigits(self):
sd = self['numDigits']
self.entryFormat = '%.' + '%d' % sd + 'f'
self.configure(valuator_numDigits = sd)
# And refresh value to reflect change
for index in range(self['dim']):
self._refreshEntry(index)
def _updateValidate(self):
# Update entry field to respect new limits
self.configure(Entry_validate = {
'validator': 'real',
'min': self['min'],
'max': self['max'],
'minstrict': 0,
'maxstrict': 0})
# Reflect changes in floaters
self.configure(valuator_min = self['min'],
valuator_max = self['max'])
def get(self):
return self._value
def getAt(self, index):
return self._value[index]
def set(self, value, fCommand = 1):
if type(value) in (types.FloatType, types.IntType, types.LongType):
value = [value] * self['dim']
for i in range(self['dim']):
self._value[i] = value[i]
self.variableList[i].set(self.entryFormat % value[i])
self.action(fCommand)
def setAt(self, index, value, fCommand = 1):
self.variableList[index].set(self.entryFormat % value)
self._value[index] = value
self.action(fCommand)
def _entryUpdateAt(self, index):
entryVar = self.variableList[index]
# Did we get a valid float?
try:
newVal = string.atof(entryVar.get())
except ValueError:
return
# 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']
# Update vector's value
self._value[index] = newVal
# refresh entry to reflect formatted value
self._refreshEntry(index)
# Update the floaters and call the command
self.action()
def _refreshEntry(self, index):
self.variableList[index].set(self.entryFormat % self._value[index])
self.entryList[index].checkentry()
def _refreshFloaters(self):
if self._floaters:
self._floaters.set(self._value, 0)
def action(self, fCommand = 1):
self._refreshFloaters()
if fCommand and (self['command'] != None):
self['command'](self._value)
def reset(self):
self.set(self['resetValue'])
def addMenuItem(self, label = '', command = None):
self.menu.add_command(label = label, command = command)
def popupSliders(self):
self._floaters.set(self.get()[:])
self._floaters.show()
def _setState(self):
if self['state'] == 'disabled':
# Disable entry
self.configure(Entry_entry_state = 'disabled')
self.configure(Entry_entry_background = '#C0C0C0')
# Disable floater Group scale
self.component('fGroup').configure(
valuator_state = 'disabled')
# Disable floater group entry
self.component('fGroup').configure(
valuator_entry_state = 'disabled')
self.component('fGroup').configure(
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')
# Disable floater group entry
self.component('fGroup').configure(
valuator_entry_state = 'normal')
self.component('fGroup').configure(
valuator_entry_background = self.entryBackground)
class Vector2Entry(VectorEntry):
def __init__(self, parent = None, **kw):
# Initialize options for the class
optiondefs = (
('dim', 2, Pmw.INITOPT),
('fGroup_labels', ('X','Y','Z'), None),
)
self.defineoptions(kw, optiondefs)
# Initialize the superclass, make sure dim makes it to superclass
VectorEntry.__init__(self, parent, dim = self['dim'])
# Needed because this method checks if self.__class__ is myClass
# where myClass is the argument passed into inialiseoptions
self.initialiseoptions(Vector2Entry)
class Vector3Entry(VectorEntry):
def __init__(self, parent = None, **kw):
# Initialize options for the class
optiondefs = (
('dim', 3, Pmw.INITOPT),
('fGroup_labels', ('X','Y','Z'), None),
)
self.defineoptions(kw, optiondefs)
# Initialize the superclass, make sure dim makes it to superclass
VectorEntry.__init__(self, parent, dim = self['dim'])
# Needed because this method checks if self.__class__ is myClass
# where myClass is the argument passed into inialiseoptions
self.initialiseoptions(Vector3Entry)
class Vector4Entry(VectorEntry):
def __init__(self, parent = None, **kw):
# Initialize options for the class
optiondefs = (
('dim', 4, Pmw.INITOPT),
('fGroup_labels', ('X','Y','Z','W'), None),
)
self.defineoptions(kw, optiondefs)
# Initialize the superclass, make sure dim makes it to superclass
VectorEntry.__init__(self, parent, dim = self['dim'])
# Needed because this method checks if self.__class__ is myClass
# where myClass is the argument passed into inialiseoptions
self.initialiseoptions(Vector4Entry)
class ColorEntry(VectorEntry):
def __init__(self, parent = None, **kw):
# 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),
)
self.defineoptions(kw, optiondefs)
# Initialize the superclass, make sure dim makes it to superclass
VectorEntry.__init__(self, parent, dim = self['dim'])
# Add menu item to popup color picker
self.addMenuItem(
'Popup color picker',
command = lambda s = self: s.popupColorPicker())
# Needed because this method checks if self.__class__ is myClass
# where myClass is the argument passed into inialiseoptions
self.initialiseoptions(ColorEntry)
def popupColorPicker(self):
# Can pass in current color with: color = (255, 0, 0)
color = tkColorChooser.askcolor(
parent = self.interior(),
# Initialize it to current color
initialcolor = tuple(self.get()[:3]))[0]
if color:
self.set((color[0], color[1], color[2], self.getAt(3)))
if __name__ == '__main__':
root = Toplevel()
root.title('Vector Widget demo')
ve = VectorEntry(root); ve.pack()
v3e = Vector3Entry(root); v3e.pack()
v4e = Vector4Entry(root); v4e.pack()
ce = ColorEntry(root); ce.pack()