*** empty log message ***

This commit is contained in:
Mark Mine 2001-07-11 21:50:23 +00:00
parent ae803609f6
commit a3b8ed2062
8 changed files with 333 additions and 205 deletions

View File

@ -1,20 +1,21 @@
from DirectLabel import *
from DirectFrame import *
class DirectButton(DirectLabel):
class DirectButton(DirectFrame):
"""
DirectButton(parent) - Create a DirectGuiWidget which responds
to mouse clicks and execute a callback function if defined
"""
def __init__(self, parent = guiTop, **kw):
# Inherits from DirectLabel
# A Direct Label can have:
# Inherits from DirectFrame
# A Direct Frame can have:
# - A background texture (pass in path to image, or Texture Card)
# - A midground geometry item (pass in geometry)
# - A foreground text Node (pass in text string or Onscreen Text)
# For a direct button:
# Each of these has 4 states (ready, press, rollover, disabled)
# The same object can be used for all four states or each
# Each button has 4 states (ready, press, rollover, disabled)
# The same image/geom/text can be used for all four states or each
# state can have a different text/geom/image
# State transitions happen automatically based upon mouse interaction
# Responds to click event and calls command if None
optiondefs = (
# Define type of DirectGuiWidget
@ -34,11 +35,10 @@ class DirectButton(DirectLabel):
('pressEffect', 1, INITOPT),
)
# Merge keyword options with default options
self.defineoptions(kw, optiondefs,
dynamicGroups = ('text', 'geom', 'image'))
self.defineoptions(kw, optiondefs)
# Initialize superclasses
DirectLabel.__init__(self, parent)
DirectFrame.__init__(self, parent)
# If specifed, add scaling to the pressed state to make it look
# like the button is moving when you press it

View File

@ -0,0 +1,156 @@
from DirectGuiBase import *
class DirectFrame(DirectGuiWidget):
def __init__(self, parent = guiTop, **kw):
# Inherits from DirectGuiWidget
# A Direct Frame can have:
# - A background texture (pass in path to image, or Texture Card)
# - A midground geometry item (pass in geometry)
# - A foreground text Node (pass in text string or Onscreen Text)
# Each of these has 1 or more states
# The same object can be used for all states or each
# state can have a different text/geom/image (for radio button
# and check button indicators, for example
optiondefs = (
# Define type of DirectGuiWidget
('pgFunc', PGItem, None),
('numStates', 1, None),
# Frame can have:
# A background texture
('image', None, self.setImage),
# A midground geometry item
('geom', None, self.setGeom),
# A foreground text node
('text', None, self.setText),
)
# Merge keyword options with default options
self.defineoptions(kw, optiondefs,
dynamicGroups = ('text', 'geom', 'image'))
# Initialize superclasses
DirectGuiWidget.__init__(self, parent)
# Call option initialization functions
self.initialiseoptions(DirectFrame)
def setText(self):
# Determine if user passed in single string or a sequence
if self['text'] == None:
textList = (None,) * self['numStates']
elif type(self['text']) == types.StringType:
# If just passing in a single string, make a tuple out of it
textList = (self['text'],) * self['numStates']
else:
# Otherwise, hope that the user has passed in a tuple/list
textList = self['text']
# Create/destroy components
for i in range(self['numStates']):
component = 'text' + `i`
# If fewer items specified than numStates,
# just repeat last item
try:
text = textList[i]
except IndexError:
text = textList[-1]
if self.hascomponent(component):
if text == None:
# Destroy component
self.destroycomponent(component)
else:
self[component + '_text'] = text
else:
if text == None:
return
else:
self.createcomponent(
component, (), 'text',
OnscreenText.OnscreenText,
(), parent = self.stateNodePath[i],
text = text, scale = 1,
sort = TEXT_SORT_INDEX,
mayChange = 1)
def setGeom(self):
# Determine argument type
if self['geom'] == None:
# Passed in None
geomList = (None,) * self['numStates']
elif isinstance(self['geom'], NodePath):
# Passed in a single node path, make a tuple out of it
geomList = (self['geom'],) * self['numStates']
else:
# Otherwise, hope that the user has passed in a tuple/list
geomList = self['geom']
# Create/destroy components
for i in range(self['numStates']):
component = 'geom' + `i`
# If fewer items specified than numStates,
# just repeat last item
try:
geom = geomList[i]
except IndexError:
geom = geomList[-1]
if self.hascomponent(component):
if geom == None:
# Destroy component
self.destroycomponent(component)
else:
self[component + '_geom'] = geom
else:
if geom == None:
return
else:
self.createcomponent(
component, (), 'geom',
OnscreenGeom.OnscreenGeom,
(), parent = self.stateNodePath[i],
geom = geom, scale = 1,
sort = GEOM_SORT_INDEX)
def setImage(self):
# Determine argument type
arg = self['image']
if arg == None:
# Passed in None
imageList = (None,) * self['numStates']
elif type(arg) == types.StringType:
# Passed in a single node path, make a tuple out of it
imageList = (arg,) * self['numStates']
else:
# Otherwise, hope that the user has passed in a tuple/list
if ((len(arg) == 2) and
(type(arg[0]) == types.StringType) and
(type(arg[1]) == types.StringType)):
# Its a model/node pair of strings
image = (arg,) * self['numStates']
else:
# Assume its a list of node paths
imageList = arg
# Create/destroy components
for i in range(self['numStates']):
component = 'image' + `i`
# If fewer items specified than numStates,
# just repeat last item
try:
image = imageList[i]
except IndexError:
image = imageList[-1]
if self.hascomponent(component):
if image == None:
# Destroy component
self.destroycomponent(component)
else:
self[component + '_image'] = image
else:
if image == None:
return
else:
self.createcomponent(
component, (), 'image',
OnscreenImage.OnscreenImage,
(), parent = self.stateNodePath[i],
image = image, scale = 1,
sort = IMAGE_SORT_INDEX)

View File

@ -6,6 +6,7 @@ __builtin__.guiTop = aspect2d.attachNewNode(PGTop('DirectGuiTop'))
guiTop.node().setMouseWatcher(base.mouseWatcher.node())
# Direct Gui Classes
from DirectFrame import *
from DirectButton import *
from DirectLabel import *

View File

@ -1,4 +1,6 @@
from DirectGuiGlobals import *
from DirectGeometry import ROUND_TO
"""
Base class for all Direct Gui items. Handles composite widgets and
command line argument parsing.
@ -89,7 +91,7 @@ class DirectGuiBase(PandaObject):
# Default id of all gui object, subclasses should override this
self.guiId = 'guiObject'
# List of all active hooks
self.hookDict = {}
self._hookDict = {}
# To avoid doing things redundantly during initialisation
self.fInit = 1
# Mapping from each megawidget option to a list of information
@ -141,6 +143,9 @@ class DirectGuiBase(PandaObject):
# no components with this group have been created.
# self._dynamicGroups = ()
def __del__(self):
print 'Bye'
def defineoptions(self, keywords, optionDefs, dynamicGroups = ()):
""" defineoptions(keywords, optionDefs, dynamicGroups = {}) """
# Create options, providing the default value and the method
@ -592,8 +597,11 @@ class DirectGuiBase(PandaObject):
def destroy(self):
# Clean out any hooks
for event in self.hookDict.keys():
for event in self._hookDict.keys():
self.ignore(event)
del(self._optionInfo)
del(self._hookDict)
del(self.__componentInfo)
def bind(self, event, command):
"""
@ -605,7 +613,7 @@ class DirectGuiBase(PandaObject):
gEvent = event + self.guiId
self.accept(gEvent, command)
# Keep track of all events you're accepting
self.hookDict[gEvent] = command
self._hookDict[gEvent] = command
def unbind(self, event):
"""
@ -614,11 +622,20 @@ class DirectGuiBase(PandaObject):
# Need to tack on gui item specific id
gEvent = event + self.guiId
self.ignore(gEvent)
if self.hookDict.has_key(gEvent):
del(self.hookDict[gEvent])
if self._hookDict.has_key(gEvent):
del(self._hookDict[gEvent])
def toggleGuiGridSnap():
DirectGuiWidget.snapToGrid = 1 - DirectGuiWidget.snapToGrid
def setGuiGridSpacing(spacing):
DirectGuiWidget.gridSpacing = spacing
class DirectGuiWidget(DirectGuiBase, NodePath):
# Toggle if you wish widget's to snap to grid when draggin
snapToGrid = 0
gridSpacing = 0.05
def __init__(self, parent = guiTop, **kw):
# Direct gui widgets are node paths
# Direct gui widgets have:
@ -633,20 +650,20 @@ class DirectGuiWidget(DirectGuiBase, NodePath):
# - Can position/scale them
optiondefs = (
# Widget's constructor
('pgFunc', PGItem, None),
('numStates', 1, None),
('invertedFrames', (), None),
('pgFunc', PGItem, None),
('numStates', 1, None),
('invertedFrames', (), None),
# Widget's initial state
('state', NORMAL, self.setState),
('state', NORMAL, self.setState),
# Widget's frame characteristics
('relief', FLAT, self.setRelief),
('borderWidth', (.1,.1), self.setBorderWidth),
('frameSize', None, self.setFrameSize),
('frameColor', (1,1,1,1), self.setFrameColor),
('pad', (.25,.15), self.resetFrameSize),
('relief', FLAT, self.setRelief),
('borderWidth', (.1,.1), self.setBorderWidth),
('frameSize', None, self.setFrameSize),
('frameColor', (.8,.8,.8,1), self.setFrameColor),
('pad', (.25,.15), self.resetFrameSize),
# Initial pos/scale of the widget
('pos', None, INITOPT),
('scale', None, INITOPT),
('pos', None, INITOPT),
('scale', None, INITOPT),
)
# Merge keyword options with default options
self.defineoptions(kw, optiondefs)
@ -677,8 +694,7 @@ class DirectGuiWidget(DirectGuiBase, NodePath):
else:
apply(self.setScale, self['scale'])
# Initialize names
self.guiItem.setName(self.guiId)
self.setName(self.guiId + 'NodePath')
self.setName(self.guiId)
# Create
self.stateNodePath = []
for i in range(self['numStates']):
@ -690,6 +706,14 @@ class DirectGuiWidget(DirectGuiBase, NodePath):
# For holding bounds info
self.ll = Point3(0)
self.ur = Point3(0)
# Is drag and drop enabled?
if base.config.GetBool('direct-gui-edit', 0):
self.enableEdit()
# Bind destroy hook
self.bind(DESTROY, self.destroy)
# Call option initialization functions
self.initialiseoptions(DirectGuiWidget)
@ -699,6 +723,63 @@ class DirectGuiWidget(DirectGuiBase, NodePath):
if not self['frameSize']:
self.setFrameSize()
def enableEdit(self):
self.bind(B2PRESS, self.editStart)
self.bind(B2RELEASE, self.editStop)
self.bind(PRINT, self.printConfig)
mb = ModifierButtons(base.mouseWatcherNode.getModifierButtons())
mb.addButton(KeyboardButton.alt())
base.mouseWatcherNode.setModifierButtons(mb)
def disableEdit(self):
self.unbind(B2PRESS)
self.unbind(B2RELEASE)
self.unbind(PRINT)
mb = ModifierButtons(base.mouseWatcherNode.getModifierButtons())
mb.removeButton(KeyboardButton.alt())
base.mouseWatcherNode.setModifierButtons(mb)
def editStart(self, event):
taskMgr.removeTasksNamed('guiEditTask')
vWidget2render2d = self.getPos(render2d)
vMouse2render2d = Point3(event.getMouse()[0], 0, event.getMouse()[1])
editVec = Vec3(vWidget2render2d - vMouse2render2d)
if base.mouseWatcherNode.getModifierButtons().isDown(
KeyboardButton.alt()):
t = taskMgr.spawnMethodNamed(self.guiScaleTask, 'guiEditTask')
t.refPos = vWidget2render2d
t.editVecLen = editVec.length()
t.initScale = self.getScale()
else:
t = taskMgr.spawnMethodNamed(self.guiDragTask, 'guiEditTask')
t.editVec = editVec
def guiScaleTask(self, state):
mwn = base.mouseWatcherNode
if mwn.hasMouse():
vMouse2render2d = Point3(mwn.getMouse()[0], 0, mwn.getMouse()[1])
newEditVecLen = Vec3(state.refPos - vMouse2render2d).length()
self.setScale(state.initScale * (newEditVecLen/state.editVecLen))
return Task.cont
def guiDragTask(self, state):
mwn = base.mouseWatcherNode
if mwn.hasMouse():
vMouse2render2d = Point3(mwn.getMouse()[0], 0, mwn.getMouse()[1])
newPos = vMouse2render2d + state.editVec
self.setPos(render2d, newPos)
if DirectGuiWidget.snapToGrid:
newPos = self.getPos()
newPos.set(
ROUND_TO(newPos[0], DirectGuiWidget.gridSpacing),
ROUND_TO(newPos[1], DirectGuiWidget.gridSpacing),
ROUND_TO(newPos[2], DirectGuiWidget.gridSpacing))
self.setPos(newPos)
return Task.cont
def editStop(self, event):
taskMgr.removeTasksNamed('guiEditTask')
def setState(self):
if type(self['state']) == type(0):
self.guiItem.setActive(self['state'])
@ -744,24 +825,29 @@ class DirectGuiWidget(DirectGuiBase, NodePath):
def setRelief(self, fSetStyle = 1):
relief = self['relief']
# Convert None, and string arguments
if relief == None:
for i in range(self['numStates']):
self.frameStyle[i].setType(PGFrameStyle.TNone)
elif (relief == FLAT) or (relief == 'flat'):
for i in range(self['numStates']):
self.frameStyle[i].setType(FLAT)
elif (relief == RAISED) or (relief == 'raised'):
relief = PGFrameStyle.TNone
elif type(relief) == types.StringType:
# Convert string to frame style int
relief = FrameStyleDict[relief]
# Set style
if relief == RAISED:
for i in range(self['numStates']):
if i in self['invertedFrames']:
self.frameStyle[1].setType(SUNKEN)
else:
self.frameStyle[i].setType(RAISED)
elif (relief == SUNKEN) or (relief == 'sunken'):
elif relief == SUNKEN:
for i in range(self['numStates']):
if i in self['invertedFrames']:
self.frameStyle[1].setType(RAISED)
else:
self.frameStyle[i].setType(SUNKEN)
else:
for i in range(self['numStates']):
self.frameStyle[i].setType(relief)
# Apply styles
self.updateFrameStyle()
def setFrameColor(self):
@ -777,7 +863,21 @@ class DirectGuiWidget(DirectGuiBase, NodePath):
self.updateFrameStyle()
def destroy(self):
# Destroy children
for child in self.getChildrenAsList():
messenger.send(DESTROY + child.getName())
# Call superclass destruction method (clears out hooks)
DirectGuiBase.destroy(self)
# Get rid of node path
self.removeNode()
def printConfig(self, indent = 0):
space = ' ' * indent
print space + self.guiId
print space + 'Pos: ' + `self.getPos()`
print space + 'Scale: ' + `self.getScale()`
# Print out children info
for child in self.getChildrenAsList():
messenger.send(PRINT + child.getName(), [indent + 2])

View File

@ -33,36 +33,27 @@ DISABLED = 'disabled'
FLAT = PGFrameStyle.TFlat
RAISED = PGFrameStyle.TBevelOut
SUNKEN = PGFrameStyle.TBevelIn
GROOVE = PGFrameStyle.TGroove
RIDGE = PGFrameStyle.TRidge
# Use to extract common prefix of mouse click, press, and release events
def getGenericMouseEvent(event, mouse):
if mouse == 1:
mb = MouseButton.one()
elif mouse == 2:
mb = MouseButton.two()
elif mouse == 3:
mb = MouseButton.three()
if event == 'click':
eventFunc = PGButton().getClickEvent
elif event == 'press':
eventFunc = PGButton().getPressEvent
elif event == 'release':
eventFunc = PGButton().getReleaseEvent
eventString = eventFunc(mb)
return eventString[:eventString.rfind('-') + 1]
FrameStyleDict = {'flat' : FLAT, 'raised' : RAISED, 'sunken': SUNKEN,
'groove' : GROOVE, 'ridge' : RIDGE
}
# User can bind commands to these gui events
ENTER = 'enter-'
EXIT = 'exit-'
B1CLICK = getGenericMouseEvent('click', 1)
B2CLICK = getGenericMouseEvent('click', 2)
B3CLICK = getGenericMouseEvent('click', 3)
B1PRESS = getGenericMouseEvent('press', 1)
B2PRESS = getGenericMouseEvent('press', 2)
B3PRESS = getGenericMouseEvent('press', 3)
B1RELEASE = getGenericMouseEvent('release', 1)
B2RELEASE = getGenericMouseEvent('release', 2)
B3RELEASE = getGenericMouseEvent('release', 3)
DESTROY = 'destroy-'
PRINT = 'print-'
ENTER = PGButton.getEnterPrefix()
EXIT = PGButton.getExitPrefix()
B1CLICK = PGButton.getClickPrefix() + MouseButton.one().getName() + '-'
B2CLICK = PGButton.getClickPrefix() + MouseButton.two().getName() + '-'
B3CLICK = PGButton.getClickPrefix() + MouseButton.three().getName() + '-'
B1PRESS = PGButton.getPressPrefix() + MouseButton.one().getName() + '-'
B2PRESS = PGButton.getPressPrefix() + MouseButton.two().getName() + '-'
B3PRESS = PGButton.getPressPrefix() + MouseButton.three().getName() + '-'
B1RELEASE = PGButton.getReleasePrefix() + MouseButton.one().getName() + '-'
B2RELEASE = PGButton.getReleasePrefix() + MouseButton.two().getName() + '-'
B3RELEASE = PGButton.getReleasePrefix() + MouseButton.three().getName() + '-'
# For setting the sorting order of a widget's visible components
IMAGE_SORT_INDEX = 10

View File

@ -42,7 +42,7 @@ def ouch(db):
db.lerpPos(Point3(newX, 0, newZ), 1.0, task = 'runAway',
blendType = 'easeOut')
dl = DirectLabel(image = 'phase_4/maps/middayskyB.jpg')
dl = DirectFrame(image = 'phase_4/maps/middayskyB.jpg')
dl.setScale(.5)
# Create a button with a background image, smiley as a geometry element,
@ -69,7 +69,7 @@ for i in range(10):
db.bind(EXIT, lambda x, db = db: expand(db))
db.bind(B1PRESS, lambda x, db = db: ouch(db))
# Pop up placer when button 2 is pressed
db.bind(B2PRESS, lambda x, db = db: db.place())
db.bind(B3PRESS, lambda x, db = db: db.place())
# To get rid of button and clear out hooks call:
# db.destroy()

View File

@ -1,156 +1,36 @@
from DirectGuiBase import *
from DirectFrame import *
class DirectLabel(DirectGuiWidget):
class DirectLabel(DirectFrame):
"""
DirectLabel(parent) - Create a DirectGuiWidget which has multiple
states. User explicitly chooses a state to display
"""
def __init__(self, parent = guiTop, **kw):
# Inherits from DirectGuiWidget
# A Direct Label can have:
# Inherits from DirectFrame
# A Direct Frame can have:
# - A background texture (pass in path to image, or Texture Card)
# - A midground geometry item (pass in geometry)
# - A foreground text Node (pass in text string or Onscreen Text)
# Each of these has 1 or more states
# The same object can be used for all states or each
# state can have a different text/geom/image (for radio button
# and check button indicators, for example
# For a direct label:
# Each label has 1 or more states
# The same image/geom/text can be used for all states or each
# state can have a different text/geom/image
# State transitions happen under user control
optiondefs = (
# Define type of DirectGuiWidget
('pgFunc', PGItem, None),
('numStates', 1, None),
# Label can have:
# A background texture
('image', None, self.setImage),
# A midground geometry item
('geom', None, self.setGeom),
# A foreground text node
('text', None, self.setText),
('pgFunc', PGItem, None),
('numStates', 1, None),
('activeState', 0, self.setActiveState),
)
# Merge keyword options with default options
self.defineoptions(kw, optiondefs,
dynamicGroups = ('text', 'geom', 'image'))
self.defineoptions(kw, optiondefs)
# Initialize superclasses
DirectGuiWidget.__init__(self, parent)
DirectFrame.__init__(self, parent)
# Call option initialization functions
self.initialiseoptions(DirectLabel)
def setText(self):
# Determine if user passed in single string or a sequence
if self['text'] == None:
textList = (None,) * self['numStates']
elif type(self['text']) == types.StringType:
# If just passing in a single string, make a tuple out of it
textList = (self['text'],) * self['numStates']
else:
# Otherwise, hope that the user has passed in a tuple/list
textList = self['text']
# Create/destroy components
for i in range(self['numStates']):
component = 'text' + `i`
# If fewer items specified than numStates,
# just repeat last item
try:
text = textList[i]
except IndexError:
text = textList[-1]
if self.hascomponent(component):
if text == None:
# Destroy component
self.destroycomponent(component)
else:
self[component + '_text'] = text
else:
if text == None:
return
else:
self.createcomponent(
component, (), 'text',
OnscreenText.OnscreenText,
(), parent = self.stateNodePath[i],
text = text, scale = 1,
sort = TEXT_SORT_INDEX,
mayChange = 1)
def setGeom(self):
# Determine argument type
if self['geom'] == None:
# Passed in None
geomList = (None,) * self['numStates']
elif isinstance(self['geom'], NodePath):
# Passed in a single node path, make a tuple out of it
geomList = (self['geom'],) * self['numStates']
else:
# Otherwise, hope that the user has passed in a tuple/list
geomList = self['geom']
# Create/destroy components
for i in range(self['numStates']):
component = 'geom' + `i`
# If fewer items specified than numStates,
# just repeat last item
try:
geom = geomList[i]
except IndexError:
geom = geomList[-1]
if self.hascomponent(component):
if geom == None:
# Destroy component
self.destroycomponent(component)
else:
self[component + '_geom'] = geom
else:
if geom == None:
return
else:
self.createcomponent(
component, (), 'geom',
OnscreenGeom.OnscreenGeom,
(), parent = self.stateNodePath[i],
geom = geom, scale = 1,
sort = GEOM_SORT_INDEX)
def setImage(self):
# Determine argument type
arg = self['image']
if arg == None:
# Passed in None
imageList = (None,) * self['numStates']
elif type(arg) == types.StringType:
# Passed in a single node path, make a tuple out of it
imageList = (arg,) * self['numStates']
else:
# Otherwise, hope that the user has passed in a tuple/list
if ((len(arg) == 2) and
(type(arg[0]) == types.StringType) and
(type(arg[1]) == types.StringType)):
# Its a model/node pair of strings
image = (arg,) * self['numStates']
else:
# Assume its a list of node paths
imageList = arg
# Create/destroy components
for i in range(self['numStates']):
component = 'image' + `i`
# If fewer items specified than numStates,
# just repeat last item
try:
image = imageList[i]
except IndexError:
image = imageList[-1]
if self.hascomponent(component):
if image == None:
# Destroy component
self.destroycomponent(component)
else:
self[component + '_image'] = image
else:
if image == None:
return
else:
self.createcomponent(
component, (), 'image',
OnscreenImage.OnscreenImage,
(), parent = self.stateNodePath[i],
image = image, scale = 1,
sort = IMAGE_SORT_INDEX)
def setActiveState(self):
""" setActiveState - change label to specifed state """
self.guiItem.setState(self['activeState'])

View File

@ -57,7 +57,7 @@ class OnscreenImage(PandaObject, NodePath):
node = model.find(image[1])
if node:
self.assign(node)
self.reparentTo(parent, sort)
self.copyTo(parent, sort)
else:
print 'OnscreenImage: node %s not found' % image[1]
return