|
@@ -4,61 +4,152 @@ from DirectButton import *
|
|
|
|
|
|
|
|
class DirectDialog(DirectFrame):
|
|
class DirectDialog(DirectFrame):
|
|
|
def __init__(self, parent = guiTop, **kw):
|
|
def __init__(self, parent = guiTop, **kw):
|
|
|
|
|
+ """
|
|
|
|
|
+ DirectDialog(kw)
|
|
|
|
|
+
|
|
|
|
|
+ Creates a popup dialog to alert and/or interact with user.
|
|
|
|
|
+ Some of the main keywords that can be used to customize the dialog:
|
|
|
|
|
+ Keyword Definition
|
|
|
|
|
+ ------- ----------
|
|
|
|
|
+ text Text message/query displayed to user
|
|
|
|
|
+ geom Geometry to be displayed in dialog
|
|
|
|
|
+ buttonTextList List of text to show on each button
|
|
|
|
|
+ buttonGeomList List of geometry to show on each button
|
|
|
|
|
+ buttonImageList List of images to show on each button
|
|
|
|
|
+ buttonValueList List of values sent to dialog command for
|
|
|
|
|
+ each button. If value is [] then the
|
|
|
|
|
+ ordinal rank of the button is used as
|
|
|
|
|
+ its value
|
|
|
|
|
+ buttonSize 4-tuple used to specify custom size for
|
|
|
|
|
+ each button (to make bigger then geom/text
|
|
|
|
|
+ for example)
|
|
|
|
|
+ pad Space between border and interior graphics
|
|
|
|
|
+ topPad Extra space added above text/geom/image
|
|
|
|
|
+ midPad Extra space added between text/buttons
|
|
|
|
|
+ buttonPadSF Scale factor used to expand/contract
|
|
|
|
|
+ button horizontal spacing
|
|
|
|
|
+ command Callback command used when a button is
|
|
|
|
|
+ pressed. Value supplied to command
|
|
|
|
|
+ depends on values in buttonValueList
|
|
|
|
|
+
|
|
|
|
|
+ Note: Number of buttons on the dialog depends upon the maximum
|
|
|
|
|
+ length of any button[Text|Geom|Image|Value]List specified.
|
|
|
|
|
+ Values of None are substituted for lists that are shorter
|
|
|
|
|
+ than the max length
|
|
|
|
|
+ """
|
|
|
|
|
+
|
|
|
# Inherits from DirectFrame
|
|
# Inherits from DirectFrame
|
|
|
optiondefs = (
|
|
optiondefs = (
|
|
|
# Define type of DirectGuiWidget
|
|
# Define type of DirectGuiWidget
|
|
|
- ('pad', (0.4, 0.4), None),
|
|
|
|
|
- ('text', '', None),
|
|
|
|
|
- ('text_align', TMALIGNCENTER, None),
|
|
|
|
|
- ('geom', getDefaultDialogGeom(), None),
|
|
|
|
|
- ('relief', None, None),
|
|
|
|
|
- ('buttons', [], INITOPT),
|
|
|
|
|
- ('buttonOffset', 0.2, None),
|
|
|
|
|
- ('button_pad', (.1,.1), None),
|
|
|
|
|
- ('button_relief', RAISED, None),
|
|
|
|
|
- ('button_frameSize', (-1,1,-.2,.9), None),
|
|
|
|
|
- ('command', None, None),
|
|
|
|
|
- ('extraArgs', [], None),
|
|
|
|
|
|
|
+ ('pad', (0.1, 0.1), None),
|
|
|
|
|
+ ('text', '', None),
|
|
|
|
|
+ ('text_align', TMALIGNLEFT, None),
|
|
|
|
|
+ ('text_scale', 0.06, None),
|
|
|
|
|
+ ('image', getDefaultDialogGeom(), None),
|
|
|
|
|
+ ('relief', None, None),
|
|
|
|
|
+ ('buttonTextList', [], INITOPT),
|
|
|
|
|
+ ('buttonGeomList', [], INITOPT),
|
|
|
|
|
+ ('buttonImageList', [], INITOPT),
|
|
|
|
|
+ ('buttonValueList', [], INITOPT),
|
|
|
|
|
+ ('button_borderWidth',(.01,.01), None),
|
|
|
|
|
+ ('button_pad', (.01,.01), None),
|
|
|
|
|
+ ('button_relief', RAISED, None),
|
|
|
|
|
+ ('button_text_scale' , 0.06, None),
|
|
|
|
|
+ ('buttonSize', None, INITOPT),
|
|
|
|
|
+ ('topPad', 0.06, INITOPT),
|
|
|
|
|
+ ('midPad', 0.12, INITOPT),
|
|
|
|
|
+ ('buttonPadSF', 1.05, INITOPT),
|
|
|
|
|
+ ('command', None, None),
|
|
|
|
|
+ ('extraArgs', [], None),
|
|
|
)
|
|
)
|
|
|
# Merge keyword options with default options
|
|
# Merge keyword options with default options
|
|
|
self.defineoptions(kw, optiondefs, dynamicGroups = ("button",))
|
|
self.defineoptions(kw, optiondefs, dynamicGroups = ("button",))
|
|
|
|
|
|
|
|
# Initialize superclasses
|
|
# Initialize superclasses
|
|
|
DirectFrame.__init__(self, parent)
|
|
DirectFrame.__init__(self, parent)
|
|
|
-
|
|
|
|
|
- self.numButtons = len(self['buttons'])
|
|
|
|
|
|
|
+ # Determine number of buttons
|
|
|
|
|
+ self.numButtons = max(len(self['buttonTextList']),
|
|
|
|
|
+ len(self['buttonGeomList']),
|
|
|
|
|
+ len(self['buttonImageList']),
|
|
|
|
|
+ len(self['buttonValueList']))
|
|
|
|
|
+ # Create buttons
|
|
|
self.buttonList = []
|
|
self.buttonList = []
|
|
|
- for button in self['buttons']:
|
|
|
|
|
- name = button + 'Button'
|
|
|
|
|
|
|
+ index = 0
|
|
|
|
|
+ for i in range(self.numButtons):
|
|
|
|
|
+ name = 'Button' + `i`
|
|
|
|
|
+ try:
|
|
|
|
|
+ text = self['buttonTextList'][i]
|
|
|
|
|
+ except IndexError:
|
|
|
|
|
+ text = None
|
|
|
|
|
+ try:
|
|
|
|
|
+ geom = self['buttonGeomList'][i]
|
|
|
|
|
+ except IndexError:
|
|
|
|
|
+ geom = None
|
|
|
|
|
+ try:
|
|
|
|
|
+ image = self['buttonImageList'][i]
|
|
|
|
|
+ except IndexError:
|
|
|
|
|
+ image = None
|
|
|
|
|
+ try:
|
|
|
|
|
+ value = self['buttonValueList'][i]
|
|
|
|
|
+ except IndexError:
|
|
|
|
|
+ value = i
|
|
|
button = self.createcomponent(
|
|
button = self.createcomponent(
|
|
|
name, (), "button",
|
|
name, (), "button",
|
|
|
DirectButton, (self,),
|
|
DirectButton, (self,),
|
|
|
- text = button,
|
|
|
|
|
- command = lambda s = self, b = button: s.__buttonCommand(b)
|
|
|
|
|
|
|
+ text = text,
|
|
|
|
|
+ geom = geom,
|
|
|
|
|
+ image = image,
|
|
|
|
|
+ frameSize = self['buttonSize'],
|
|
|
|
|
+ command = lambda s = self, v = value: s.buttonCommand(v)
|
|
|
)
|
|
)
|
|
|
self.buttonList.append(button)
|
|
self.buttonList.append(button)
|
|
|
- self.numButtons += 1
|
|
|
|
|
|
|
+
|
|
|
|
|
+ # Update dialog when everything has been initialised
|
|
|
|
|
+ self.postInitialiseFuncList.append(self.configureDialog)
|
|
|
self.initialiseoptions(DirectDialog)
|
|
self.initialiseoptions(DirectDialog)
|
|
|
|
|
|
|
|
|
|
+ def configureDialog(self):
|
|
|
# Position buttons and text
|
|
# Position buttons and text
|
|
|
- # Get size of text
|
|
|
|
|
- text = self.component('text0')
|
|
|
|
|
- bounds = text.getTightBounds()
|
|
|
|
|
pad = self['pad']
|
|
pad = self['pad']
|
|
|
- l = bounds[0][0] - pad[0]
|
|
|
|
|
- r = bounds[1][0] + pad[0]
|
|
|
|
|
- b = bounds[0][2] - pad[1]
|
|
|
|
|
- t = bounds[1][2] + pad[1]
|
|
|
|
|
- text.setPos((l+r)/2.0, (b+t)/2.0)
|
|
|
|
|
- # Move buttons
|
|
|
|
|
- if self['button_frameSize']:
|
|
|
|
|
- frameSize = self['button_frameSize']
|
|
|
|
|
- bl = frameSize[0]
|
|
|
|
|
- br = frameSize[1]
|
|
|
|
|
- bb = frameSize[2]
|
|
|
|
|
- bt = frameSize[3]
|
|
|
|
|
|
|
+ bpad = self['button_pad']
|
|
|
|
|
+ image = self.component('image0')
|
|
|
|
|
+ # Get size of text/geom without image (for state 0)
|
|
|
|
|
+ if image:
|
|
|
|
|
+ image.reparentTo(hidden)
|
|
|
|
|
+ bounds = self.stateNodePath[0].getTightBounds()
|
|
|
|
|
+ if image:
|
|
|
|
|
+ image.reparentTo(self.stateNodePath[0])
|
|
|
|
|
+ l = bounds[0][0]
|
|
|
|
|
+ r = bounds[1][0]
|
|
|
|
|
+ b = bounds[0][2]
|
|
|
|
|
+ t = bounds[1][2]
|
|
|
|
|
+ # Center text and geom around origin
|
|
|
|
|
+ # How far is center of text from origin?
|
|
|
|
|
+ xOffset = -(l+r)/2.0
|
|
|
|
|
+ zOffset = -(b+t)/2.0
|
|
|
|
|
+ # Update bounds to reflect text movement
|
|
|
|
|
+ l += xOffset
|
|
|
|
|
+ r += xOffset
|
|
|
|
|
+ b += zOffset
|
|
|
|
|
+ t += zOffset
|
|
|
|
|
+ # Offset text and geom to center
|
|
|
|
|
+ if self['text']:
|
|
|
|
|
+ self['text_pos'] = (self['text_pos'][0] + xOffset,
|
|
|
|
|
+ self['text_pos'][1] + zOffset)
|
|
|
|
|
+ if self['geom']:
|
|
|
|
|
+ self['geom_pos'] = Point3(self['geom_pos'][0] + xOffset,
|
|
|
|
|
+ self['geom_pos'][1],
|
|
|
|
|
+ self['geom_pos'][2] + zOffset)
|
|
|
|
|
+ # Get button size
|
|
|
|
|
+ if self['buttonSize']:
|
|
|
|
|
+ # Either use given size
|
|
|
|
|
+ buttonSize = self['buttonSize']
|
|
|
|
|
+ bl = buttonSize[0]
|
|
|
|
|
+ br = buttonSize[1]
|
|
|
|
|
+ bb = buttonSize[2]
|
|
|
|
|
+ bt = buttonSize[3]
|
|
|
else:
|
|
else:
|
|
|
- # Get bounds of union of buttons
|
|
|
|
|
|
|
+ # Or get bounds of union of buttons
|
|
|
bl = br = bb = bt = 0
|
|
bl = br = bb = bt = 0
|
|
|
for button in self.buttonList:
|
|
for button in self.buttonList:
|
|
|
bounds = button.stateNodePath[0].getTightBounds()
|
|
bounds = button.stateNodePath[0].getTightBounds()
|
|
@@ -66,41 +157,156 @@ class DirectDialog(DirectFrame):
|
|
|
br = max(br, bounds[1][0])
|
|
br = max(br, bounds[1][0])
|
|
|
bb = min(bb, bounds[0][2])
|
|
bb = min(bb, bounds[0][2])
|
|
|
bt = max(bt, bounds[1][2])
|
|
bt = max(bt, bounds[1][2])
|
|
|
- bWidth = br - bl
|
|
|
|
|
- bSpacing = 1.1 * bWidth
|
|
|
|
|
|
|
+ bl -= bpad[0]
|
|
|
|
|
+ br += bpad[0]
|
|
|
|
|
+ bb -= bpad[1]
|
|
|
|
|
+ bt += bpad[1]
|
|
|
|
|
+ # Now resize buttons to match largest
|
|
|
|
|
+ for button in self.buttonList:
|
|
|
|
|
+ button['frameSize'] = (bl,br,bb,bt)
|
|
|
|
|
+ # Must compensate for scale
|
|
|
|
|
+ scale = self['button_scale']
|
|
|
|
|
+ # Can either be a Vec3 or a tuple of 3 values
|
|
|
|
|
+ if (isinstance(scale, Vec3) or
|
|
|
|
|
+ (type(scale) == types.ListType) or
|
|
|
|
|
+ (type(scale) == types.TupleType)):
|
|
|
|
|
+ sx = scale[0]
|
|
|
|
|
+ sz = scale[2]
|
|
|
|
|
+ elif ((type(scale) == types.IntType) or
|
|
|
|
|
+ (type(scale) == types.FloatType)):
|
|
|
|
|
+ sx = sz = scale
|
|
|
|
|
+ else:
|
|
|
|
|
+ sx = sz = 1
|
|
|
|
|
+ bl *= sx
|
|
|
|
|
+ br *= sx
|
|
|
|
|
+ bb *= sz
|
|
|
|
|
+ bt *= sz
|
|
|
|
|
+ # Position buttons
|
|
|
|
|
+ # Calc button width and height
|
|
|
bHeight = bt - bb
|
|
bHeight = bt - bb
|
|
|
- index = 0
|
|
|
|
|
- if self.numButtons == 0:
|
|
|
|
|
- return
|
|
|
|
|
- bPos = bSpacing * (self.numButtons - 1)/2.0
|
|
|
|
|
- for button in self.buttonList:
|
|
|
|
|
- button.setPos(bPos + index * bSpacing, 0,
|
|
|
|
|
- b - self['buttonOffset'] - bt)
|
|
|
|
|
- index += 1
|
|
|
|
|
- # Resize frame
|
|
|
|
|
- b = b - self['buttonOffset'] - bHeight - pad[1]
|
|
|
|
|
- frame = self.component('geom0')
|
|
|
|
|
- frame.setScale(r - l, 1, t - b)
|
|
|
|
|
- frame.setPos((l+r)/2.0, 0.0, (b+t)/2.0)
|
|
|
|
|
|
|
+ bWidth = br - bl
|
|
|
|
|
+ # Add pad between buttons
|
|
|
|
|
+ bSpacing = self['buttonPadSF'] * bWidth
|
|
|
|
|
+ if self.numButtons != 0:
|
|
|
|
|
+ bPos = -bSpacing * (self.numButtons - 1)/2.0
|
|
|
|
|
+ index = 0
|
|
|
|
|
+ for button in self.buttonList:
|
|
|
|
|
+ button.setPos(bPos + index * bSpacing, 0,
|
|
|
|
|
+ b - self['midPad'] - bpad[1] - bt)
|
|
|
|
|
+ index += 1
|
|
|
|
|
+ bMax = bPos + bSpacing * (self.numButtons - 1)
|
|
|
|
|
+ else:
|
|
|
|
|
+ bPos = 0
|
|
|
|
|
+ bMax = 0
|
|
|
|
|
+ # Resize frame to fit text and buttons
|
|
|
|
|
+ l = min(bPos + bl, l) - pad[0]
|
|
|
|
|
+ r = max(bMax + br, r) + pad[0]
|
|
|
|
|
+ # reduce bottom by pad, button height and 2*button pad
|
|
|
|
|
+ b = min(b - self['midPad'] - bpad[1] - bHeight - bpad[1], b) - pad[1]
|
|
|
|
|
+ t = t + self['topPad'] + pad[1]
|
|
|
|
|
+ self['image_scale'] = (r - l, 1, t - b)
|
|
|
|
|
+ # Center frame about text and buttons
|
|
|
|
|
+ self['image_pos'] = ((l+r)/2.0, 0.0,(b+t)/2.0)
|
|
|
self.resetFrameSize()
|
|
self.resetFrameSize()
|
|
|
|
|
|
|
|
- def __buttonCommand(self, button):
|
|
|
|
|
|
|
+ def buttonCommand(self, value):
|
|
|
if self['command']:
|
|
if self['command']:
|
|
|
- self['command'](button)
|
|
|
|
|
|
|
+ self['command'](value)
|
|
|
|
|
|
|
|
def destroy(self):
|
|
def destroy(self):
|
|
|
DirectFrame.destroy(self)
|
|
DirectFrame.destroy(self)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+class OKDialog(DirectDialog):
|
|
|
|
|
+ def __init__(self, parent = guiTop, **kw):
|
|
|
|
|
+ # Inherits from DirectFrame
|
|
|
|
|
+ optiondefs = (
|
|
|
|
|
+ # Define type of DirectGuiWidget
|
|
|
|
|
+ ('buttonTextList', ['OK'], INITOPT),
|
|
|
|
|
+ ('buttonValueList', [1], INITOPT),
|
|
|
|
|
+ )
|
|
|
|
|
+ # Merge keyword options with default options
|
|
|
|
|
+ self.defineoptions(kw, optiondefs)
|
|
|
|
|
+ DirectDialog.__init__(self, parent)
|
|
|
|
|
+ self.initialiseoptions(OKDialog)
|
|
|
|
|
+
|
|
|
|
|
+class OKCancelDialog(DirectDialog):
|
|
|
|
|
+ def __init__(self, parent = guiTop, **kw):
|
|
|
|
|
+ # Inherits from DirectFrame
|
|
|
|
|
+ optiondefs = (
|
|
|
|
|
+ # Define type of DirectGuiWidget
|
|
|
|
|
+ ('buttonTextList', ['OK','Cancel'], INITOPT),
|
|
|
|
|
+ ('buttonValueList', [1,-1], INITOPT),
|
|
|
|
|
+ )
|
|
|
|
|
+ # Merge keyword options with default options
|
|
|
|
|
+ self.defineoptions(kw, optiondefs)
|
|
|
|
|
+ DirectDialog.__init__(self, parent)
|
|
|
|
|
+ self.initialiseoptions(OKCancelDialog)
|
|
|
|
|
+
|
|
|
class YesNoDialog(DirectDialog):
|
|
class YesNoDialog(DirectDialog):
|
|
|
def __init__(self, parent = guiTop, **kw):
|
|
def __init__(self, parent = guiTop, **kw):
|
|
|
# Inherits from DirectFrame
|
|
# Inherits from DirectFrame
|
|
|
optiondefs = (
|
|
optiondefs = (
|
|
|
# Define type of DirectGuiWidget
|
|
# Define type of DirectGuiWidget
|
|
|
- ('buttons', ['Yes', 'No'], INITOPT),
|
|
|
|
|
- ('text', 'Yes or No?', None),
|
|
|
|
|
|
|
+ ('buttonTextList', ['Yes', 'No'], INITOPT),
|
|
|
|
|
+ ('buttonValueList', [1,0], INITOPT),
|
|
|
)
|
|
)
|
|
|
# Merge keyword options with default options
|
|
# Merge keyword options with default options
|
|
|
self.defineoptions(kw, optiondefs)
|
|
self.defineoptions(kw, optiondefs)
|
|
|
- apply(DirectDialog.__init__, (self, parent), kw)
|
|
|
|
|
|
|
+ DirectDialog.__init__(self, parent)
|
|
|
self.initialiseoptions(YesNoDialog)
|
|
self.initialiseoptions(YesNoDialog)
|
|
|
|
|
+
|
|
|
|
|
+class YesNoCancelDialog(DirectDialog):
|
|
|
|
|
+ def __init__(self, parent = guiTop, **kw):
|
|
|
|
|
+ # Inherits from DirectFrame
|
|
|
|
|
+ optiondefs = (
|
|
|
|
|
+ # Define type of DirectGuiWidget
|
|
|
|
|
+ ('buttonTextList', ['Yes', 'No', 'Cancel'], INITOPT),
|
|
|
|
|
+ ('buttonValueList', [1,0,-1], INITOPT),
|
|
|
|
|
+ )
|
|
|
|
|
+ # Merge keyword options with default options
|
|
|
|
|
+ self.defineoptions(kw, optiondefs)
|
|
|
|
|
+ DirectDialog.__init__(self, parent)
|
|
|
|
|
+ self.initialiseoptions(YesNoCancelDialog)
|
|
|
|
|
+
|
|
|
|
|
+class RetryCancelDialog(DirectDialog):
|
|
|
|
|
+ def __init__(self, parent = guiTop, **kw):
|
|
|
|
|
+ # Inherits from DirectFrame
|
|
|
|
|
+ optiondefs = (
|
|
|
|
|
+ # Define type of DirectGuiWidget
|
|
|
|
|
+ ('buttonTextList', ['Retry','Cancel'], INITOPT),
|
|
|
|
|
+ ('buttonValueList', [1,-1], INITOPT),
|
|
|
|
|
+ )
|
|
|
|
|
+ # Merge keyword options with default options
|
|
|
|
|
+ self.defineoptions(kw, optiondefs)
|
|
|
|
|
+ DirectDialog.__init__(self, parent)
|
|
|
|
|
+ self.initialiseoptions(RetryCancelDialog)
|
|
|
|
|
+
|
|
|
|
|
+class TTOkCancelDialog(DirectDialog):
|
|
|
|
|
+ def __init__(self, parent = guiTop, **kw):
|
|
|
|
|
+ # Inherits from DirectDialog
|
|
|
|
|
+ buttons = loader.loadModelOnce(
|
|
|
|
|
+ 'phase_3/models/gui/dialog_box_buttons_gui')
|
|
|
|
|
+ okImageList = (buttons.find('**/ChtBx_OKBtn_UP'),
|
|
|
|
|
+ buttons.find('**/ChtBx_OKBtn_DN'),
|
|
|
|
|
+ buttons.find('**/ChtBx_OKBtn_Rllvr'))
|
|
|
|
|
+ okTextList = ('', 'OK', 'OK')
|
|
|
|
|
+ cancelImageList = (buttons.find('**/CloseBtn_UP'),
|
|
|
|
|
+ buttons.find('**/CloseBtn_DN'),
|
|
|
|
|
+ buttons.find('**/CloseBtn_Rllvr'))
|
|
|
|
|
+ cancelTextList = ('', 'Cancel', 'Cancel')
|
|
|
|
|
+ optiondefs = (
|
|
|
|
|
+ # Define type of DirectGuiWidget
|
|
|
|
|
+ ('buttonTextList', [okTextList, cancelTextList], INITOPT),
|
|
|
|
|
+ ('buttonImageList', [okImageList, cancelImageList], INITOPT),
|
|
|
|
|
+ ('buttonValueList', [1,-1], INITOPT),
|
|
|
|
|
+ ('button_pad', (0,0), None),
|
|
|
|
|
+ ('button_relief', None, None),
|
|
|
|
|
+ ('button_text_pos', (0,-0.1), None),
|
|
|
|
|
+ ('buttonSize', (-.05,.05,-.05,.05), None),
|
|
|
|
|
+ )
|
|
|
|
|
+ # Merge keyword options with default options
|
|
|
|
|
+ self.defineoptions(kw, optiondefs)
|
|
|
|
|
+ DirectDialog.__init__(self, parent)
|
|
|
|
|
+ self.initialiseoptions(TTOkCancelDialog)
|
|
|
|
|
+
|