mirror of
https://github.com/panda3d/panda3d.git
synced 2025-09-28 15:53:55 -04:00
*** empty log message ***
This commit is contained in:
parent
f96734023b
commit
81cbc5013f
@ -1,5 +1,16 @@
|
||||
from PandaModules import *
|
||||
from PandaObject import *
|
||||
|
||||
X_AXIS = Vec3(1,0,0)
|
||||
Y_AXIS = Vec3(0,1,0)
|
||||
Z_AXIS = Vec3(0,0,1)
|
||||
NEG_X_AXIS = Vec3(-1,0,0)
|
||||
NEG_Y_AXIS = Vec3(0,-1,0)
|
||||
NEG_Z_AXIS = Vec3(0,0,-1)
|
||||
ZERO_VEC = ORIGIN = Vec3(0)
|
||||
UNIT_VEC = Vec3(1)
|
||||
ZERO_POINT = Point3(0)
|
||||
|
||||
class LineNodePath(NodePath):
|
||||
def __init__(self, parent = None, **kw):
|
||||
|
||||
@ -71,3 +82,5 @@ def planeIntersect (lineOrigin, lineDir, planeOrigin, normal):
|
||||
hitPt = lineDir * t
|
||||
return hitPt + lineOrigin
|
||||
|
||||
def roundTo(value, divisor):
|
||||
return round(value/float(divisor)) * divisor
|
||||
|
@ -43,8 +43,10 @@ class DirectGrid(NodePath,PandaObject):
|
||||
|
||||
# Initialize Grid characteristics
|
||||
self.fXyzSnap = 1
|
||||
self.fHprSnap = 1
|
||||
self.gridSize = 100.0
|
||||
self.gridSpacing = 5.0
|
||||
self.snapAngle = 15.0
|
||||
self.enable()
|
||||
|
||||
def enable(self):
|
||||
@ -96,24 +98,28 @@ class DirectGrid(NodePath,PandaObject):
|
||||
minor.create()
|
||||
major.create()
|
||||
self.gridBack.setScale(scaledSize)
|
||||
|
||||
def roundTo(self, value, divisor):
|
||||
return round(value/float(divisor)) * divisor
|
||||
|
||||
|
||||
def setXyzSnap(self, fSnap):
|
||||
self.fXyzSnap = fSnap
|
||||
|
||||
def getXyzSnap(self):
|
||||
return self.fXyzSnap
|
||||
|
||||
def snapToGrid(self, point):
|
||||
def setHprSnap(self, fSnap):
|
||||
self.fHprSnap = fSnap
|
||||
|
||||
def getHprSnap(self):
|
||||
return self.fHprSnap
|
||||
|
||||
def computeSnapPoint(self, point):
|
||||
# Start of with current point
|
||||
self.snapPos.assign(point)
|
||||
# Snap if necessary
|
||||
if self.fXyzSnap:
|
||||
self.snapPos.set(
|
||||
roundTo(point[0], self.gridSpacing),
|
||||
roundTo(point[1], self.gridSpacing),
|
||||
roundTo(point[2], self.gridSpacing))
|
||||
else:
|
||||
self.snapPos.assign(point)
|
||||
roundTo(self.snapPos[0], self.gridSpacing),
|
||||
roundTo(self.snapPos[1], self.gridSpacing),
|
||||
roundTo(self.snapPos[2], self.gridSpacing))
|
||||
|
||||
# Move snap marker to this point
|
||||
self.snapMarker.setPos(self.snapPos)
|
||||
@ -121,6 +127,15 @@ class DirectGrid(NodePath,PandaObject):
|
||||
# Return the hit point
|
||||
return self.snapPos
|
||||
|
||||
def computeSnapAngle(self, angle):
|
||||
return roundTo(angle, self.snapAngle)
|
||||
|
||||
def setSnapAngle(self, angle):
|
||||
self.snapAngle = angle
|
||||
|
||||
def getSnapAngle(self):
|
||||
return self.snapAngle
|
||||
|
||||
def setGridSpacing(self, spacing):
|
||||
self.gridSpacing = spacing
|
||||
self.updateGrid()
|
||||
|
@ -18,12 +18,10 @@ class DirectNodePath(NodePath):
|
||||
# and its center of action (COA)
|
||||
self.mCoa2Dnp = Mat4()
|
||||
self.mCoa2Dnp.assign(Mat4.identMat())
|
||||
self.mCoa2Dnp.setRow(3, Vec4(center[0], center[1], center[2], 1))
|
||||
# self.mCoa2Dnp.setRow(3, Vec4(center[0], center[1], center[2], 1))
|
||||
# Transform from nodePath to widget
|
||||
self.mDnp2Widget = Mat4()
|
||||
self.mDnp2Widget.assign(Mat4.identMat())
|
||||
# Use value of this pointer as unique ID
|
||||
self.id = self.node().this
|
||||
|
||||
def highlight(self):
|
||||
self.bbox.show()
|
||||
@ -65,7 +63,7 @@ class SelectedNodePaths(PandaObject):
|
||||
self.deselectAll()
|
||||
|
||||
# Get this pointer
|
||||
id = nodePath.node().this
|
||||
id = nodePath.id()
|
||||
# First see if its already in the selected dictionary
|
||||
dnp = self.selectedDict.get(id, None)
|
||||
# If so, we're done
|
||||
@ -84,14 +82,14 @@ class SelectedNodePaths(PandaObject):
|
||||
# Show its bounding box
|
||||
dnp.highlight()
|
||||
# Add it to the selected dictionary
|
||||
self.selectedDict[dnp.id] = dnp
|
||||
self.selectedDict[dnp.id()] = dnp
|
||||
# And update last
|
||||
self.last = dnp
|
||||
return dnp
|
||||
|
||||
def deselect(self, nodePath):
|
||||
# Get this pointer
|
||||
id = nodePath.node().this
|
||||
id = nodePath.id()
|
||||
# See if it is in the selected dictionary
|
||||
dnp = self.selectedDict.get(id, None)
|
||||
if dnp:
|
||||
@ -108,11 +106,16 @@ class SelectedNodePaths(PandaObject):
|
||||
list = []
|
||||
for key in self.selectedDict.keys():
|
||||
list.append(self.selectedDict[key])
|
||||
return list
|
||||
|
||||
def __getitem__(self,index):
|
||||
return self.selectedAsList()[index]
|
||||
|
||||
def deselectedAsList(self):
|
||||
list = []
|
||||
for key in self.deselectedDict.keys():
|
||||
list.append(self.deselectedDict[key])
|
||||
return list
|
||||
|
||||
def forEachSelectedNodePathDo(self, func):
|
||||
duplicateKeys = self.selectedDict.keys()[:]
|
||||
@ -171,7 +174,7 @@ class SelectedNodePaths(PandaObject):
|
||||
|
||||
def getDirectNodePath(self, nodePath):
|
||||
# Get this pointer
|
||||
id = nodePath.node().this
|
||||
id = nodePath.id()
|
||||
# First check selected dict
|
||||
dnp = self.selectedDict.get(id, None)
|
||||
if dnp:
|
||||
|
@ -43,6 +43,10 @@ class DirectSession(PandaObject):
|
||||
self.iRay = self.iRayList[0]
|
||||
self.hitPt = Point3(0.0)
|
||||
|
||||
# One run through the context task to init everything
|
||||
for context in self.contextList:
|
||||
context.contextTask(None)
|
||||
|
||||
self.actionEvents = [('select', self.select),
|
||||
('deselect', self.deselect),
|
||||
('deselectAll', self.deselectAll),
|
||||
@ -89,8 +93,9 @@ class DirectSession(PandaObject):
|
||||
|
||||
def followSelectedNodePathTask(self, state):
|
||||
mCoa2Render = state.dnp.mCoa2Dnp * state.dnp.getMat(render)
|
||||
mCoa2Render.decomposeMatrix(
|
||||
self.scale,self.hpr,self.pos,getDefaultCoordinateSystem())
|
||||
decomposeMatrix(mCoa2Render,
|
||||
self.scale,self.hpr,self.pos,
|
||||
getDefaultCoordinateSystem())
|
||||
self.widget.setPosHpr(self.pos,self.hpr)
|
||||
return Task.cont
|
||||
|
||||
|
@ -4,6 +4,10 @@
|
||||
of the NodePath class
|
||||
"""
|
||||
|
||||
def id(self):
|
||||
"""Returns the bottom node's this pointer as a unique id"""
|
||||
return self.node().this
|
||||
|
||||
def getNodePathName(self):
|
||||
from PandaModules import *
|
||||
# Initialize to a default value
|
||||
@ -70,7 +74,8 @@
|
||||
|
||||
def getAncestry(self):
|
||||
from PandaObject import *
|
||||
if self.node() != render.node():
|
||||
node = self.node()
|
||||
if ((node != render.node()) | (node != hidden.node())):
|
||||
ancestry = self.getParent().getAncestry()
|
||||
ancestry.append(self)
|
||||
return ancestry
|
||||
|
@ -14,10 +14,10 @@ Generates Python code for the C++ libraries listed.
|
||||
|
||||
Example:
|
||||
Linux:
|
||||
ppython -d generatePythonCode -v -d $DIRECT/lib/py -e $DIRECT/src/extensions -i libdtool libpandaexpress libpanda libdirect
|
||||
ppython -d generatePythonCode -v -d $DIRECT/lib/py -e $DIRECT/src/extensions -i libdtool libpandaexpress libpanda libdirect libtoontown
|
||||
|
||||
Windows:
|
||||
ppython -d generatePythonCode -v -d `cygpath -w $DIRECT/lib/py` -e `cygpath -w $DIRECT/src/extensions` -i libdtool libpandaexpress libpanda libdirect
|
||||
ppython -d generatePythonCode -v -d `cygpath -w $DIRECT/lib/py` -e `cygpath -w $DIRECT/src/extensions` -i libdtool libpandaexpress libpanda libdirect libtoontown
|
||||
|
||||
|
||||
Options:
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -4,28 +4,31 @@ from DirectGeometry import *
|
||||
class PieMenu(NodePath, PandaObject):
|
||||
def __init__(self, direct, menu, action = None, fUpdateOnlyOnChange = 1):
|
||||
NodePath.__init__(self)
|
||||
# Become the menu
|
||||
self.assign(menu)
|
||||
# Create a toplevel node for aspect ratio scaling
|
||||
self.assign(hidden.attachNewNode(NamedNode('PieMenu')))
|
||||
# Attach the menu
|
||||
self.menu = menu
|
||||
self.menu.reparentTo(self)
|
||||
# Initialize instance variables
|
||||
self.direct = direct
|
||||
self.numItems = self.getNumChildren()
|
||||
self.numItems = self.menu.getNumChildren()
|
||||
self.degreesPerItem = 360.0/self.numItems
|
||||
self.sfx = self.getSx()
|
||||
self.sfz = self.getSz()
|
||||
self.itemOffset = self.degreesPerItem / 2.0
|
||||
self.sfx = self.menu.getSx()
|
||||
self.sfz = self.menu.getSz()
|
||||
# Record target and action
|
||||
self.action = action
|
||||
self.initialState = None
|
||||
# Marking lines
|
||||
self.lines = LineNodePath(self)
|
||||
self.lines = LineNodePath(self.menu)
|
||||
self.lines.setColor(VBase4(1))
|
||||
self.lines.setThickness(1)
|
||||
# Set flags
|
||||
self.fUpdateOnlyOnChange = fUpdateOnlyOnChange
|
||||
self.itemOffset = 0
|
||||
|
||||
def performAction(self, value):
|
||||
if action:
|
||||
action(value)
|
||||
if self.action:
|
||||
self.action(value)
|
||||
|
||||
def removePieMenuTask(self):
|
||||
taskMgr.removeTasksNamed('pieMenuTask')
|
||||
@ -43,7 +46,9 @@ class PieMenu(NodePath, PandaObject):
|
||||
# Pop up menu
|
||||
self.reparentTo(render2d)
|
||||
self.setPos(self.originX,0.0,self.originY)
|
||||
|
||||
# Compensate for window aspect ratio
|
||||
self.setScale(1.0, 1.0,1.0)
|
||||
#self.direct.chan.width/float(self.direct.chan.height))
|
||||
# Start drawing the selection line
|
||||
self.lines.reset()
|
||||
self.lines.moveTo(0,0,0)
|
||||
@ -62,7 +67,7 @@ class PieMenu(NodePath, PandaObject):
|
||||
deltaY = mouseY - self.originY
|
||||
|
||||
# Update the line
|
||||
self.lines.setVertex(1,(deltaX/sfx),0.0,(deltaY / sfz))
|
||||
self.lines.setVertex(1,(deltaX/self.sfx),0.0,(deltaY/self.sfz))
|
||||
|
||||
# How far from starting point has user moved the cursor?
|
||||
if ((abs(deltaX) < 0.1) & (abs(deltaY) < 0.1)):
|
||||
@ -74,15 +79,16 @@ class PieMenu(NodePath, PandaObject):
|
||||
else:
|
||||
# Alway let use know mouse is in the center
|
||||
self.performAction(-1)
|
||||
|
||||
self.currItem = -1
|
||||
else:
|
||||
# Outside of the center
|
||||
# Interacting with menu
|
||||
# subtract half a slice to effectively center item
|
||||
menuAngle = rad2Deg(math.atan2(deltaY, deltaX)) + self.itemOffset
|
||||
if menuAngle < 0.0:
|
||||
menuAngle = menuAngle + 360.0
|
||||
menuAngle = menuAngle % 360.0
|
||||
newItem = math.floor(menuAngle / self.degreesPerItem)
|
||||
newItem = int(math.floor(menuAngle / self.degreesPerItem))
|
||||
|
||||
if self.fUpdateOnlyOnChange:
|
||||
if (self.currItem != newItem):
|
||||
@ -109,3 +115,4 @@ class PieMenu(NodePath, PandaObject):
|
||||
|
||||
def setUpdateOnlyOnChange(self,flag):
|
||||
self.fUpdateOnlyOnChange = flag
|
||||
|
||||
|
@ -1,87 +1,93 @@
|
||||
title = 'DIRECT Floater megawidget'
|
||||
"""
|
||||
Floater Class: Velocity style controller for floating point values with
|
||||
a label, entry (validated), and scale
|
||||
"""
|
||||
|
||||
import string
|
||||
import Tkinter
|
||||
from Tkinter import *
|
||||
import Pmw
|
||||
|
||||
OK = 1
|
||||
ERROR = 0
|
||||
import string
|
||||
|
||||
class Floater(Pmw.MegaWidget):
|
||||
""" Megawidget containing a label, an entry, and a scale.
|
||||
Used as a velocity style controller for floating point values
|
||||
"""
|
||||
"Velocity style floating point controller"
|
||||
|
||||
def __init__(self, parent = None, **kw):
|
||||
|
||||
# Define the megawidget options.
|
||||
optiondefs = (
|
||||
('command', None, None),
|
||||
('value', 0.0, self._updateValue),
|
||||
('text', '', self._updateLabelText),
|
||||
('min', None, None),
|
||||
('max', None, None),
|
||||
('resolution', None, None),
|
||||
('maxVelocity', 100.0, None),
|
||||
('errorbackground', 'pink', None),
|
||||
('significantDigits', 2, self._setSigDigits),
|
||||
('initialValue', 0.0, Pmw.INITOPT),
|
||||
('resolution', None, None),
|
||||
('command', None, None),
|
||||
('maxVelocity', 100.0, None),
|
||||
('min', None, self._updateValidate),
|
||||
('max', None, self._updateValidate),
|
||||
('text', 'Floater', self._updateLabelText),
|
||||
('significantDigits', 2, self._setSigDigits),
|
||||
)
|
||||
self.defineoptions(kw, optiondefs)
|
||||
|
||||
# Initialise base class (after defining options).
|
||||
# Initialise superclass
|
||||
Pmw.MegaWidget.__init__(self, parent)
|
||||
|
||||
# Initialize some variables
|
||||
self.value = 0.0
|
||||
# Initialize some class variables
|
||||
self.value = self['initialValue']
|
||||
self.velocity = 0.0
|
||||
self.entryValue = Tkinter.StringVar()
|
||||
self.entryFormat = '%.2f'
|
||||
self.normalBackground = None
|
||||
|
||||
# Create the components.
|
||||
interior = self.interior()
|
||||
interior['relief'] = 'groove'
|
||||
interior['borderwidth'] = 2
|
||||
|
||||
self.infoFrame = self.createcomponent('frame',
|
||||
(), None,
|
||||
Tkinter.Frame, interior)
|
||||
self.infoFrame.pack(expand = 1, fill = 'both')
|
||||
|
||||
# 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 floater's value
|
||||
self.entryValue = StringVar()
|
||||
self.entryValue.set(self['initialValue'])
|
||||
self.entry = self.createcomponent('entryField',
|
||||
# Access floaters 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 Floater's label
|
||||
self.label = self.createcomponent('label',
|
||||
(), None,
|
||||
Tkinter.Label, self.infoFrame,
|
||||
self.label = self.createcomponent('label', (), None,
|
||||
Label, self.labelFrame,
|
||||
text = self['text'],
|
||||
anchor = 'center',
|
||||
width = 12,
|
||||
anchor = 'center',
|
||||
font = "Arial 12 bold")
|
||||
self.label.pack(side='left', expand = 1, fill = 'x')
|
||||
|
||||
# Create an entry field to display and validate the floater's value
|
||||
self.entry = self.createcomponent('entry',
|
||||
(), None,
|
||||
Tkinter.Entry, self.infoFrame,
|
||||
width = 10,
|
||||
justify = 'right',
|
||||
textvar = self.entryValue)
|
||||
self.entry.pack(side='left',padx = 4)
|
||||
self.entry.bind('<Return>', self._entryCommand)
|
||||
|
||||
# Now pack the frame
|
||||
self.labelFrame.pack(expand = 1, fill = 'both')
|
||||
|
||||
# Create the scale component.
|
||||
self.scale = self.createcomponent('scale',
|
||||
(), None,
|
||||
Tkinter.Scale, interior,
|
||||
self.scale = self.createcomponent('scale', (), None,
|
||||
Scale, interior,
|
||||
command = self._scaleToVelocity,
|
||||
orient = 'horizontal',
|
||||
length = 150,
|
||||
from_ = -1.0,
|
||||
to = 1.0,
|
||||
resolution = 0.01,
|
||||
resolution = 0.001,
|
||||
showvalue = 0)
|
||||
self.scale.pack(expand = 1, fill = 'x')
|
||||
# Set scale to the middle of its range
|
||||
self.scale.set(0.0)
|
||||
# When interacting with mouse:
|
||||
|
||||
# Add scale bindings: When interacting with mouse:
|
||||
self.scale.bind('<Button-1>', self._startFloaterTask)
|
||||
self.scale.bind('<ButtonRelease-1>', self._floaterReset)
|
||||
# In case you wish to interact using keys
|
||||
@ -90,17 +96,31 @@ class Floater(Pmw.MegaWidget):
|
||||
self.scale.bind('<KeyPress-Left>', self._floaterKeyCommand)
|
||||
self.scale.bind('<KeyRelease-Left>', self._floaterReset)
|
||||
|
||||
# Check keywords and initialise options.
|
||||
# Check keywords and initialise options based on input values.
|
||||
self.initialiseoptions(Floater)
|
||||
|
||||
# Now that the widgets have been created, update significant digits
|
||||
self._setSigDigits()
|
||||
self._updateValue()
|
||||
def label(self):
|
||||
return self.label
|
||||
def scale(self):
|
||||
return self.scale
|
||||
def entry(self):
|
||||
return self.entry
|
||||
|
||||
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})
|
||||
|
||||
def _scaleToVelocity(self, strVal):
|
||||
# convert scale val to float
|
||||
val = string.atof(strVal)
|
||||
# retain sign of velocity by only calling abs once
|
||||
# Square val, but retain sign of velocity by only calling abs once
|
||||
self.velocity = self['maxVelocity'] * val * abs(val)
|
||||
|
||||
def _startFloaterTask(self,event):
|
||||
@ -109,7 +129,7 @@ class Floater(Pmw.MegaWidget):
|
||||
|
||||
def _floaterTask(self):
|
||||
if self.velocity != 0.0:
|
||||
self.setValue( self.value + self.velocity )
|
||||
self.set( self.value + self.velocity )
|
||||
if self._fFloaterTask:
|
||||
self.after(50, self._floaterTask)
|
||||
|
||||
@ -120,90 +140,205 @@ class Floater(Pmw.MegaWidget):
|
||||
|
||||
def _floaterKeyCommand(self, event):
|
||||
if self.velocity != 0.0:
|
||||
self.setValue( self.value + self.velocity )
|
||||
self.set( self.value + self.velocity )
|
||||
|
||||
def _entryCommand(self, event = None):
|
||||
try:
|
||||
val = string.atof( self.entryValue.get() )
|
||||
self.setValue( val )
|
||||
self.set( val )
|
||||
except ValueError:
|
||||
# invalid entry, ring bell set background to warning color
|
||||
self.entry.bell()
|
||||
if self.normalBackground is None:
|
||||
self.normalBackground = self.entry.cget('background')
|
||||
self.entry.configure( background = self['errorbackground'] )
|
||||
pass
|
||||
|
||||
def _updateValue(self):
|
||||
self.setValue(self['value'])
|
||||
|
||||
def setValue(self, newVal):
|
||||
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']
|
||||
|
||||
# Update floater's value
|
||||
self.value = newVal
|
||||
# Update entry to reflect formatted value
|
||||
self.entryValue.set( self.entryFormat % self.value )
|
||||
# Reset background
|
||||
if self.normalBackground is not None:
|
||||
self.entry.configure(background = self.normalBackground)
|
||||
self.normalBackground = None
|
||||
# execute command
|
||||
command = self['command']
|
||||
if command is not None:
|
||||
command( newVal )
|
||||
|
||||
def _setSigDigits(self):
|
||||
sd = self['significantDigits']
|
||||
self.entryFormat = '%.' + '%d' % sd + 'f'
|
||||
self.scale['resolution'] = 10 ** (-1.0 * sd)
|
||||
# And reset value to reflect change
|
||||
self.entryValue.set( self.entryFormat % self.value )
|
||||
|
||||
def _updateLabelText(self):
|
||||
self.label['text'] = self['text']
|
||||
self.entry.checkentry()
|
||||
|
||||
Pmw.forwardmethods(Floater, Tkinter.Scale, 'scale')
|
||||
# execute command
|
||||
if fCommand & (self['command'] is not None):
|
||||
self['command']( newVal )
|
||||
|
||||
|
||||
class FloaterGroup(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', 'Floater Group', None),
|
||||
# A tuple of initial values, one for each floater
|
||||
('initialValue', DEFAULT_VALUE, INITOPT),
|
||||
# The command to be executed any time one of the floaters is updated
|
||||
('command', None, None),
|
||||
# A tuple of labels, one for each floater
|
||||
('labels', DEFAULT_LABELS, self._updateLabels),
|
||||
)
|
||||
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)
|
||||
|
||||
# FloaterGroup Menu
|
||||
menubar.addmenu('Floater Group', 'Floater Group Operations')
|
||||
menubar.addmenuitem(
|
||||
'Floater Group', 'command', 'Reset the Floater Group panel',
|
||||
label = 'Reset',
|
||||
command = lambda s = self: s.reset())
|
||||
menubar.addmenuitem(
|
||||
'Floater Group', 'command', 'Dismiss Floater Group panel',
|
||||
label = 'Dismiss', command = self.withdraw)
|
||||
|
||||
menubar.addmenu('Help', 'Floater 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.floaterList = []
|
||||
for index in range(self['dim']):
|
||||
# Add a group alias so you can configure the floaters via:
|
||||
# fg.configure(Floater_XXX = YYY)
|
||||
f = self.createcomponent(
|
||||
'floater%d' % index, (), 'Floater', Floater,
|
||||
(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._floaterSetAt(i, val)
|
||||
f.pack(side = self['side'], expand = 1, fill = X)
|
||||
self.floaterList.append(f)
|
||||
|
||||
# Make sure floaters are initialized
|
||||
self.set(self['initialValue'])
|
||||
|
||||
# Make sure input variables processed
|
||||
self.initialiseoptions(FloaterGroup)
|
||||
|
||||
def _updateLabels(self):
|
||||
if self['labels']:
|
||||
for index in range(self['dim']):
|
||||
self.floaterList[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 floater, but don't execute its command
|
||||
self.floaterList[i].set(value[i], 0)
|
||||
if fCommand & (self['command'] is not None):
|
||||
self['command'](self._value)
|
||||
|
||||
def setAt(self, index, value):
|
||||
# Update floater and execute its command
|
||||
self.floaterList[index].set(value)
|
||||
|
||||
# This is the command used by the floater
|
||||
def _floaterSetAt(self, index, value):
|
||||
self._value[index] = value
|
||||
if self['command']:
|
||||
self['command'](self._value)
|
||||
|
||||
def reset(self):
|
||||
self.set(self['initialValue'])
|
||||
|
||||
|
||||
|
||||
## SAMPLE CODE
|
||||
if __name__ == '__main__':
|
||||
# Initialise Tkinter and Pmw.
|
||||
root = Pmw.initialise()
|
||||
root = Toplevel()
|
||||
root.title('Pmw Floater demonstration')
|
||||
|
||||
# Dummy command
|
||||
def printVal(val):
|
||||
print `val`
|
||||
print val
|
||||
|
||||
# Create and pack a Floater megawidget.
|
||||
mega1 = Floater(root)
|
||||
mega1 = Floater(root, command = printVal)
|
||||
mega1.pack(side = 'left', expand = 1, fill = 'x')
|
||||
|
||||
"""
|
||||
# These are things you can set/configure
|
||||
mega1['command'] = printVal
|
||||
# Starting value for floater
|
||||
mega1['initialValue'] = 123.456
|
||||
mega1['text'] = 'Drive delta X'
|
||||
mega1['min'] = 0.0
|
||||
#mega1['max'] = 1000.0
|
||||
mega1['max'] = 1000.0
|
||||
mega1['resolution'] = 1.0
|
||||
|
||||
# UNCOMMENT THESE TO CUSTOMIZE THE FLOATER
|
||||
# To change the color of the label:
|
||||
# mega1.label['foreground'] = 'Red'
|
||||
|
||||
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
|
||||
"""
|
||||
|
||||
# Starting value for floater
|
||||
# mega1['value'] = 123.4557
|
||||
|
||||
# Let's go.
|
||||
# To create a floater group to set an RGBA value:
|
||||
group1 = FloaterGroup(root, dim = 4,
|
||||
title = 'Simple RGBA Panel',
|
||||
labels = ('R', 'G', 'B', 'A'),
|
||||
Floater_min = 0.0,
|
||||
Floater_max = 255.0,
|
||||
Floater_resolution = 1.0,
|
||||
command = printVal)
|
||||
|
||||
# Uncomment this if you aren't running in IDLE
|
||||
#root.mainloop()
|
||||
|
Loading…
x
Reference in New Issue
Block a user