Browse Source

*** empty log message ***

Mark Mine 24 years ago
parent
commit
2b13cd507b
2 changed files with 495 additions and 44 deletions
  1. 1 1
      direct/src/directtools/DirectSession.py
  2. 494 43
      direct/src/gui/DirectButton.py

+ 1 - 1
direct/src/directtools/DirectSession.py

@@ -35,7 +35,7 @@ class DirectSession(PandaObject):
         self.drList = DisplayRegionList()
         self.drList = DisplayRegionList()
         self.iRayList = map(lambda x: x.iRay, self.drList)
         self.iRayList = map(lambda x: x.iRay, self.drList)
         self.dr = self.drList[0]
         self.dr = self.drList[0]
-        self.camera = self.dr.cam
+        self.camera = base.cameraList[0]
         self.iRay = self.dr.iRay
         self.iRay = self.dr.iRay
 
 
         self.cameraControl = DirectCameraControl()
         self.cameraControl = DirectCameraControl()

+ 494 - 43
direct/src/gui/DirectButton.py

@@ -4,6 +4,7 @@ from PGTop import *
 from PGButton import *
 from PGButton import *
 from PGItem import *
 from PGItem import *
 from PGFrameStyle import *
 from PGFrameStyle import *
+import types
 import __builtin__
 import __builtin__
 
 
 NORMAL = 'normal'
 NORMAL = 'normal'
@@ -26,7 +27,9 @@ __builtin__.guiTop = aspect2d.attachNewNode(PGTop('DirectGuiTop'))
 guiTop.node().setMouseWatcher(base.mouseWatcher.node())
 guiTop.node().setMouseWatcher(base.mouseWatcher.node())
 
 
 class DirectGuiObject(PandaObject):
 class DirectGuiObject(PandaObject):
-    def __init__(self, optiondefs, **kw):
+    def __init__(self, optiondefs, dynamicGroups, **kw):
+        # Default id of all gui object, subclasses should override this
+        self.guiId = 'guiObject'
 	# Mapping from each megawidget option to a list of information
 	# Mapping from each megawidget option to a list of information
 	# about the option
 	# about the option
 	#   - default value
 	#   - default value
@@ -51,6 +54,10 @@ class DirectGuiObject(PandaObject):
 	#   - the name of the component group of this component, if any
 	#   - the name of the component group of this component, if any
 	self.__componentInfo = {}
 	self.__componentInfo = {}
 
 
+	# Mapping from alias names to the names of components or
+	# sub-components.
+	self.__componentAliases = {}
+
 	# Contains information about the keywords provided to the
 	# Contains information about the keywords provided to the
 	# constructor.  It is a mapping from the keyword to a tuple
 	# constructor.  It is a mapping from the keyword to a tuple
 	# containing:
 	# containing:
@@ -65,9 +72,16 @@ class DirectGuiObject(PandaObject):
 	# unused options given to the constructor.
 	# unused options given to the constructor.
 	#
 	#
 	# self._constructorKeywords = {}
 	# self._constructorKeywords = {}
-        self.defineoptions(kw, optiondefs)
+
+        # List of dynamic component groups.  If a group is included in
+        # this list, then it not an error if a keyword argument for
+        # the group is given to the constructor or to configure(), but
+        # no components with this group have been created.
+        # self._dynamicGroups = ()
+
+        self.defineoptions(kw, optiondefs, dynamicGroups)
         
         
-    def defineoptions(self, keywords, optionDefs):
+    def defineoptions(self, keywords, optionDefs, dynamicGroups = ()):
 	# Create options, providing the default value and the method
 	# Create options, providing the default value and the method
 	# to call when the value is changed.  If any option created by
 	# to call when the value is changed.  If any option created by
 	# base classes has the same name as one in <optionDefs>, the
 	# base classes has the same name as one in <optionDefs>, the
@@ -79,9 +93,14 @@ class DirectGuiObject(PandaObject):
 	if not hasattr(self, '_constructorKeywords'):
 	if not hasattr(self, '_constructorKeywords'):
 	    tmp = {}
 	    tmp = {}
 	    for option, value in keywords.items():
 	    for option, value in keywords.items():
-		tmp[option] = value
+		tmp[option] = [value, 0]
             self._constructorKeywords = tmp
             self._constructorKeywords = tmp
 	    self._optionInfo = {}
 	    self._optionInfo = {}
+        # Initialize dictionary of dynamic groups
+        if not hasattr(self, '_dynamicGroups'):
+            self._dynamicGroups = ()
+        self._dynamicGroups = self._dynamicGroups + tuple(dynamicGroups)
+        # Reconcile command line and default options
         self.addoptions(optionDefs)
         self.addoptions(optionDefs)
         
         
     def addoptions(self, optionDefs):
     def addoptions(self, optionDefs):
@@ -105,7 +124,7 @@ class DirectGuiObject(PandaObject):
                 if not optionInfo_has_key(name):
                 if not optionInfo_has_key(name):
                     if keywords_has_key(name):
                     if keywords_has_key(name):
                         # Overridden by keyword, use keyword value
                         # Overridden by keyword, use keyword value
-                        value = keywords[name]
+                        value = keywords[name][0]
                         optionInfo[name] = [default, value, function]
                         optionInfo[name] = [default, value, function]
                         del keywords[name]
                         del keywords[name]
                     else:
                     else:
@@ -127,8 +146,15 @@ class DirectGuiObject(PandaObject):
 	    unusedOptions = []
 	    unusedOptions = []
 	    keywords = self._constructorKeywords
 	    keywords = self._constructorKeywords
 	    for name in keywords.keys():
 	    for name in keywords.keys():
-                # This keyword argument has not been used.
-                unusedOptions.append(name)
+                print name
+		used = keywords[name][1]
+		if not used:
+                    # This keyword argument has not been used.  If it
+                    # does not refer to a dynamic group, mark it as
+                    # unused.
+                    index = string.find(name, '_')
+                    if index < 0 or name[:index] not in self._dynamicGroups:
+                        unusedOptions.append(name)
 	    self._constructorKeywords = {}
 	    self._constructorKeywords = {}
 	    if len(unusedOptions) > 0:
 	    if len(unusedOptions) > 0:
 		if len(unusedOptions) == 1:
 		if len(unusedOptions) == 1:
@@ -195,6 +221,8 @@ class DirectGuiObject(PandaObject):
 	optionInfo_has_key = optionInfo.has_key
 	optionInfo_has_key = optionInfo.has_key
 	componentInfo = self.__componentInfo
 	componentInfo = self.__componentInfo
 	componentInfo_has_key = componentInfo.has_key
 	componentInfo_has_key = componentInfo.has_key
+	componentAliases = self.__componentAliases
+	componentAliases_has_key = componentAliases.has_key
 	VALUE = _OPT_VALUE
 	VALUE = _OPT_VALUE
 	FUNCTION = _OPT_FUNCTION
 	FUNCTION = _OPT_FUNCTION
         
         
@@ -227,17 +255,38 @@ class DirectGuiObject(PandaObject):
 		    # This option may be of the form <component>_<option>.
 		    # This option may be of the form <component>_<option>.
 		    component = option[:index]
 		    component = option[:index]
 		    componentOption = option[(index + 1):]
 		    componentOption = option[(index + 1):]
+
+		    # Expand component alias
+		    if componentAliases_has_key(component):
+			component, subComponent = componentAliases[component]
+			if subComponent is not None:
+			    componentOption = subComponent + '_' \
+				    + componentOption
+
+			# Expand option string to write on error
+			option = component + '_' + componentOption
+
                     # Does this component exist
                     # Does this component exist
 		    if componentInfo_has_key(component):
 		    if componentInfo_has_key(component):
 			# Get the configure func for the named component
 			# Get the configure func for the named component
-			componentConfigFunc = componentInfo[component][1]
+			componentConfigFuncs = [componentInfo[component][1]]
 		    else:
 		    else:
-			componentConfigFunc = None
-                        raise KeyError, 'Unknown option "' + option + \
-                              '" for ' + self.__class__.__name__
-
-		    # Add the configure method and option/value to dictionary.
-                    if componentConfigFunc:
+			# Check if this is a group name and configure all
+			# components in the group.
+			componentConfigFuncs = []
+			for info in componentInfo.values():
+			    if info[4] == component:
+			        componentConfigFuncs.append(info[1])
+
+                        if len(componentConfigFuncs) == 0 and \
+                                component not in self._dynamicGroups:
+			    raise KeyError, 'Unknown option "' + option + \
+				    '" for ' + self.__class__.__name__
+
+		    # Add the configure method(s) (may be more than
+		    # one if this is configuring a component group)
+		    # and option/value to dictionary.
+		    for componentConfigFunc in componentConfigFuncs:
 			if not indirectOptions_has_key(componentConfigFunc):
 			if not indirectOptions_has_key(componentConfigFunc):
 			    indirectOptions[componentConfigFunc] = {}
 			    indirectOptions[componentConfigFunc] = {}
 			indirectOptions[componentConfigFunc][componentOption] \
 			indirectOptions[componentConfigFunc][componentOption] \
@@ -273,10 +322,27 @@ class DirectGuiObject(PandaObject):
 		component = option[:index]
 		component = option[:index]
 		componentOption = option[(index + 1):]
 		componentOption = option[(index + 1):]
 
 
+		# Expand component alias
+		if self.__componentAliases.has_key(component):
+		    component, subComponent = self.__componentAliases[
+                        component]
+		    if subComponent is not None:
+			componentOption = subComponent + '_' + componentOption
+
+		    # Expand option string to write on error
+		    option = component + '_' + componentOption
+
 		if self.__componentInfo.has_key(component):
 		if self.__componentInfo.has_key(component):
 		    # Call cget on the component.
 		    # Call cget on the component.
 		    componentCget = self.__componentInfo[component][3]
 		    componentCget = self.__componentInfo[component][3]
 		    return componentCget(componentOption)
 		    return componentCget(componentOption)
+		else:
+		    # If this is a group name, call cget for one of
+		    # the components in the group.
+		    for info in self.__componentInfo.values():
+			if info[4] == component:
+			    componentCget = info[3]
+			    return componentCget(componentOption)
 
 
         # Option not found
         # Option not found
 	raise KeyError, 'Unknown option "' + option + \
 	raise KeyError, 'Unknown option "' + option + \
@@ -285,7 +351,8 @@ class DirectGuiObject(PandaObject):
     # Allow index style refererences
     # Allow index style refererences
     __getitem__ = cget
     __getitem__ = cget
     
     
-    def createcomponent(self, componentName, widgetClass, *widgetArgs, **kw):
+    def createcomponent(self, componentName, componentAliases, componentGroup,
+                        widgetClass, *widgetArgs, **kw):
 	"""Create a component (during construction or later)."""
 	"""Create a component (during construction or later)."""
         # Check for invalid component name
         # Check for invalid component name
 	if '_' in componentName:
 	if '_' in componentName:
@@ -296,6 +363,29 @@ class DirectGuiObject(PandaObject):
 	    keywords = self._constructorKeywords
 	    keywords = self._constructorKeywords
 	else:
 	else:
 	    keywords = {}
 	    keywords = {}
+
+	for alias, component in componentAliases:
+	    # Create aliases to the component and its sub-components.
+	    index = string.find(component, '_')
+	    if index < 0:
+		self.__componentAliases[alias] = (component, None)
+	    else:
+		mainComponent = component[:index]
+		subComponent = component[(index + 1):]
+		self.__componentAliases[alias] = (mainComponent, subComponent)
+
+	    # Remove aliases from the constructor keyword arguments by
+	    # replacing any keyword arguments that begin with *alias*
+	    # with corresponding keys beginning with *component*.
+
+	    alias = alias + '_'
+	    aliasLen = len(alias)
+	    for option in keywords.keys():
+		if len(option) > aliasLen and option[:aliasLen] == alias:
+		    newkey = component + '_' + option[aliasLen:]
+		    keywords[newkey] = keywords[option]
+		    del keywords[option]
+
         # Find any keyword arguments for this component
         # Find any keyword arguments for this component
 	componentPrefix = componentName + '_'
 	componentPrefix = componentName + '_'
 	nameLen = len(componentPrefix)
 	nameLen = len(componentPrefix)
@@ -303,9 +393,22 @@ class DirectGuiObject(PandaObject):
 	    if len(option) > nameLen and option[:nameLen] == componentPrefix:
 	    if len(option) > nameLen and option[:nameLen] == componentPrefix:
 		# The keyword argument refers to this component, so add
 		# The keyword argument refers to this component, so add
 		# this to the options to use when constructing the widget.
 		# this to the options to use when constructing the widget.
-		kw[option[nameLen:]] = keywords[option]
+		kw[option[nameLen:]] = keywords[option][0]
                 # And delete it from main construction keywords
                 # And delete it from main construction keywords
 		del keywords[option]
 		del keywords[option]
+	    else:
+		# Check if this keyword argument refers to the group
+		# of this component.  If so, add this to the options
+		# to use when constructing the widget.  Mark the
+		# keyword argument as being used, but do not remove it
+		# since it may be required when creating another
+		# component.
+		index = string.find(option, '_')
+		if index >= 0 and componentGroup == option[:index]:
+		    rest = option[(index + 1):]
+		    kw[rest] = keywords[option][0]
+		    keywords[option][1] = 1
+
         # Return None if no widget class is specified
         # Return None if no widget class is specified
 	if widgetClass is None:
 	if widgetClass is None:
 	    return None
 	    return None
@@ -319,7 +422,7 @@ class DirectGuiObject(PandaObject):
 	widget = apply(widgetClass, widgetArgs, kw)
 	widget = apply(widgetClass, widgetArgs, kw)
 	componentClass = widget.__class__.__name__
 	componentClass = widget.__class__.__name__
 	self.__componentInfo[componentName] = (widget, widget.configure,
 	self.__componentInfo[componentName] = (widget, widget.configure,
-		componentClass, widget.cget)
+		componentClass, widget.cget, componentGroup)
 	return widget
 	return widget
 
 
     def component(self, name):
     def component(self, name):
@@ -337,6 +440,16 @@ class DirectGuiObject(PandaObject):
 	    component = name[:index]
 	    component = name[:index]
 	    remainingComponents = name[(index + 1):]
 	    remainingComponents = name[(index + 1):]
 
 
+	# Expand component alias
+	if self.__componentAliases.has_key(component):
+	    component, subComponent = self.__componentAliases[component]
+	    if subComponent is not None:
+		if remainingComponents is None:
+		    remainingComponents = subComponent
+		else:
+		    remainingComponents = subComponent + '_' \
+			    + remainingComponents
+
 	widget = self.__componentInfo[component][0]
 	widget = self.__componentInfo[component][0]
 	if remainingComponents is None:
 	if remainingComponents is None:
 	    return widget
 	    return widget
@@ -367,10 +480,10 @@ class DirectGuiObject(PandaObject):
 	self._optionInfo = {}
 	self._optionInfo = {}
 
 
     def bind(self, sequence, command):
     def bind(self, sequence, command):
-        self.accept(self.idString + '_' + sequence, command)
+        self.accept(sequence + '-' + self.guiId, command)
         
         
     def unbind(self, sequence):
     def unbind(self, sequence):
-        self.ignore(self.idString + '_' + sequence)
+        self.ignore(sequence + '-' + self.guiId)
 
 
 class DirectButton(DirectGuiObject, NodePath):
 class DirectButton(DirectGuiObject, NodePath):
     def __init__(self, parent = guiTop, **kw):
     def __init__(self, parent = guiTop, **kw):
@@ -386,22 +499,25 @@ class DirectButton(DirectGuiObject, NodePath):
         #  - a VBase4(L,R,B,T)
         #  - a VBase4(L,R,B,T)
         #  - a bounding box object
         #  - a bounding box object
         optiondefs = (
         optiondefs = (
+            ('image',         None,       self.setImage),
+            ('geom',          None,       self.setGeom),
             ('text',          '',         self.setText),
             ('text',          '',         self.setText),
-            ('geom',          None,       None),
-            ('image',         None,       None),
+            ('command',       None,       self.setCommand),
             ('relief',        FLAT,       self.setRelief),
             ('relief',        FLAT,       self.setRelief),
             ('frameColor',    (1,1,1,1),  self.setFrameColor),
             ('frameColor',    (1,1,1,1),  self.setFrameColor),
             ('borderWidth',   (.1,.1),    self.setBorderWidth),
             ('borderWidth',   (.1,.1),    self.setBorderWidth),
             ('frameSize',     None,       self.setFrameSize),
             ('frameSize',     None,       self.setFrameSize),
+            ('pressEffect',   1,          None),
+            ('padSX',         1.2,        None),
+            ('padSZ',         1.1,        None),
             ('pos',           None,       None),
             ('pos',           None,       None),
             ('scale',         None,       None),
             ('scale',         None,       None),
             ('state',         NORMAL,     self.setState),
             ('state',         NORMAL,     self.setState),
-            ('command',       None,       None),
             ('rolloverSound', None,       None),
             ('rolloverSound', None,       None),
             ('clickSound',    None,       None),
             ('clickSound',    None,       None),
             )
             )
         # Update options to reflect keyword parameters
         # Update options to reflect keyword parameters
-        apply(DirectGuiObject.__init__, (self, optiondefs), kw)
+        apply(DirectGuiObject.__init__, (self, optiondefs, ('text',)), kw)
         # Initialize the superclass
         # Initialize the superclass
         NodePath.__init__(self)
         NodePath.__init__(self)
         # Create a button
         # Create a button
@@ -415,18 +531,26 @@ class DirectButton(DirectGuiObject, NodePath):
         self.stateNodePath = []
         self.stateNodePath = []
         for i in range(4):
         for i in range(4):
             self.stateNodePath.append(NodePath(self.guiItem.getStateDef(i)))
             self.stateNodePath.append(NodePath(self.guiItem.getStateDef(i)))
-        # Adjust frame
-        self.frameStyle = [PGFrameStyle(),
-                           PGFrameStyle(),
-                           PGFrameStyle(),
-                           PGFrameStyle()]
+        if self['pressEffect']:
+            np = self.stateNodePath[1].attachNewNode('pressEffect')
+            np.setScale(0.98)
+            self.stateNodePath[1] = np
+        # Initialize frame style
+        self.frameStyle = []
+        for i in range(4):
+            self.frameStyle.append(PGFrameStyle())
+        # For holding bounds info
+        self.ll = Point3(0)
+        self.ur = Point3(0)
+        # Call initialization functions if necessary
         # To avoid doing things redundantly
         # To avoid doing things redundantly
         self.fInit = 1
         self.fInit = 1
-        # Call initialization functions if necessary
         self.initialiseoptions(DirectButton)
         self.initialiseoptions(DirectButton)
         self.fInit = 0
         self.fInit = 0
+        # Allow changes to take effect
         self.updateFrameStyle()
         self.updateFrameStyle()
-        self.setFrameSize()
+        if not self['frameSize']:
+            self.setFrameSize()
         # Update pose
         # Update pose
         if self['pos']:
         if self['pos']:
             if type(self['pos']) == type(()):
             if type(self['pos']) == type(()):
@@ -463,15 +587,32 @@ class DirectButton(DirectGuiObject, NodePath):
         if not self.fInit:
         if not self.fInit:
             self.updateFrameStyle()
             self.updateFrameStyle()
 
 
-    def setFrameSize(self):
+    def resetFrameSize(self):
+        self.setFrameSize(fClearFrame = 1)
+        
+    def setFrameSize(self, fClearFrame = 0):
         if self['frameSize']:
         if self['frameSize']:
+            # Use user specified bounds
             bounds = self['frameSize']
             bounds = self['frameSize']
-            self.guiItem.setFrame(bounds[0], bounds[1],
-                                  bounds[2], bounds[3])
         else:
         else:
-            bounds = self.component('text0').textNode.getCardActual()
-            self.guiItem.setFrame(bounds[0] - 0.4, bounds[1] + 0.4,
-                                  bounds[2] - 0.15, bounds[3] + 0.15)
+            # Use ready state to compute bounds
+            frameType = self.frameStyle[0].getType()
+            if fClearFrame and (frameType != PGFrameStyle.TNone):
+                self.frameStyle[0].setType(PGFrameStyle.TNone)
+                self.guiItem.setFrameStyle(0, self.frameStyle[0])
+                # To force an update of the button
+                self.guiItem.getStateDef(0)
+            # Clear out frame before computing bounds
+            self.stateNodePath[0].calcTightBounds(self.ll, self.ur)
+            # Scale bounds to give a pad around graphics
+            bounds = (self.ll[0] * self['padSX'], self.ur[0] * self['padSX'],
+                      self.ll[2] * self['padSZ'], self.ur[2] * self['padSZ'])
+            # Restore frame style if necessary
+            if (frameType != PGFrameStyle.TNone):
+                self.frameStyle[0].setType(frameType)
+                self.guiItem.setFrameStyle(0, self.frameStyle[0])
+        # Set frame to new dimensions
+        self.guiItem.setFrame(bounds[0], bounds[1],bounds[2], bounds[3])
 
 
     def setFrameColor(self):
     def setFrameColor(self):
         color = self['frameColor']
         color = self['frameColor']
@@ -488,6 +629,11 @@ class DirectButton(DirectGuiObject, NodePath):
             self.updateFrameStyle()
             self.updateFrameStyle()
 
 
     def setText(self):
     def setText(self):
+        if not self['text']:
+            print "No Text"
+            return
+        else:
+            print "SetText"
         if ((type(self['text']) == type(())) or
         if ((type(self['text']) == type(())) or
             (type(self['text']) == type([]))):
             (type(self['text']) == type([]))):
             text = self['text']
             text = self['text']
@@ -497,13 +643,59 @@ class DirectButton(DirectGuiObject, NodePath):
             component = 'text' + `i`
             component = 'text' + `i`
             if not self.hascomponent(component):
             if not self.hascomponent(component):
                 self.createcomponent(
                 self.createcomponent(
-                    component, OnscreenText.OnscreenText,
+                    component, (), 'text',
+                    OnscreenText.OnscreenText,
                     (), parent = self.stateNodePath[i],
                     (), parent = self.stateNodePath[i],
                     text = text[i], scale = 1,
                     text = text[i], scale = 1,
                     mayChange = 1)
                     mayChange = 1)
             else:
             else:
                 self[component + '_text'] = text[i]
                 self[component + '_text'] = text[i]
 
 
+    def setGeom(self):
+        if not self['geom']:
+            print "No Geom"
+            return
+        else:
+            print "SetGeom"
+        if ((type(self['geom']) == type(())) or
+            (type(self['geom']) == type([]))):
+            geom = self['geom']
+        else:
+            geom = (self['geom'],) * 4
+        for i in range(4):
+            component = 'geom' + `i`
+            if not self.hascomponent(component):
+                self.createcomponent(
+                    component, (), 'geom',
+                    OnscreenGeom,
+                    (), parent = self.stateNodePath[i],
+                    geom = geom[i], scale = 1)
+            else:
+                self[component + '_geom'] = geom[i]
+
+    def setImage(self):
+        if not self['image']:
+            print "No Image"
+            return
+        else:
+            print "SetImage"
+        if ((type(self['image']) == type(())) or
+            (type(self['image']) == type([]))):
+            if len(self['image']) == 4:
+                image = self['image']
+            else:
+                image = (self['image'],) * 4
+        for i in range(4):
+            component = 'image' + `i`
+            if not self.hascomponent(component):
+                self.createcomponent(
+                    component, (), 'image',
+                    OnscreenImage,
+                    (), parent = self.stateNodePath[i],
+                    image = image[i], scale = 1)
+            else:
+                self[component + '_image'] = image[i]
+
     def setState(self):
     def setState(self):
         if type(self['state']) == type(0):
         if type(self['state']) == type(0):
             self.guiItem.setActive(self['state'])
             self.guiItem.setActive(self['state'])
@@ -511,12 +703,12 @@ class DirectButton(DirectGuiObject, NodePath):
             self.guiItem.setActive(1)
             self.guiItem.setActive(1)
         else:
         else:
             self.guiItem.setActive(0)
             self.guiItem.setActive(0)
-            
-    def setImage(self):
-        pass
-    def setGeom(self):
-        pass
 
 
+    def setCommand(self):
+        self.unbind('click')
+        if self['command']:
+            self.bind('click', self['command'])
+            
 class DirectLabel(DirectGuiObject, PGItem):
 class DirectLabel(DirectGuiObject, PGItem):
     def __init__(self, parent = None, **kw):
     def __init__(self, parent = None, **kw):
         # Pass in a background texture, and/or a geometry object,
         # Pass in a background texture, and/or a geometry object,
@@ -536,7 +728,7 @@ class DirectLabel(DirectGuiObject, PGItem):
             ('textPos',       (0,0,0),    self.setTextPos),
             ('textPos',       (0,0,0),    self.setTextPos),
             ('textScale',     (1,1,1),    self.setTextPos),
             ('textScale',     (1,1,1),    self.setTextPos),
             )
             )
-        apply(DirectGuiObject.__init__, (self, optiondefs), kw)            
+        apply(DirectGuiObject.__init__, (self, optiondefs, ()), kw)            
         self.initialiseoptions(DirectLabel)
         self.initialiseoptions(DirectLabel)
 
 
     def setImage(self):
     def setImage(self):
@@ -566,3 +758,262 @@ class DirectLabel(DirectGuiObject, PGItem):
     def setState(self):
     def setState(self):
         pass
         pass
 
 
+
+class OnscreenGeom(PandaObject, NodePath):
+    def __init__(self, geom = None,
+                 pos = None,
+                 hpr = None,
+                 scale = None,
+                 color = None,
+                 parent = aspect2d):
+        """__init__(self, ...)
+
+        Make a geom node from string or a node path,
+        put it into the 2d sg and set it up with all the indicated parameters.
+
+        The parameters are as follows:
+
+          geom: the actual geometry to display or a file name.
+                This may be omitted and specified later via setGeom()
+                if you don't have it available.
+
+          pos: the x, y, z position of the geometry on the screen.
+               This maybe a 3-tuple of floats or a vector.
+               y should be zero
+
+          hpr: the h,p,r of the geometry on the screen.
+               This maybe a 3-tuple of floats or a vector.
+
+          scale: the size of the geometry.  This may either be a single
+                 float, a 3-tuple of floats, or a vector, specifying a
+                 different x, y, z scale.  y should be 1
+
+          color: the (r, g, b, a) color of the geometry.  This is
+                 normally a 4-tuple of floats or ints.
+
+          parent: the NodePath to parent the geometry to initially.
+        """
+        # We ARE a node path.  Initially, we're an empty node path.
+        NodePath.__init__(self)
+        # Assign geometry
+        if isinstance(geom, NodePath):
+            self.assign(geom.copyTo(parent))
+        elif type(geom) == type(''):
+            self.assign(loader.loadModelCopy(geom))
+            self.reparentTo(parent)
+
+        # Adjust pose
+        # Set pos
+        if (isinstance(pos, types.TupleType) or
+            isinstance(pos, types.ListType)):
+            apply(self.setPos, pos)
+        elif isinstance(pos, VBase3):
+            self.setPos(pos)
+        # Hpr
+        if (isinstance(hpr, types.TupleType) or
+            isinstance(hpr, types.ListType)):
+            apply(self.setHpr, hpr)
+        elif isinstance(hpr, VBase3):
+            self.setPos(hpr)
+        # Scale
+        if (isinstance(scale, types.TupleType) or
+            isinstance(scale, types.ListType)):
+            apply(self.setScale, scale)
+        elif isinstance(scale, VBase3):
+            self.setPos(scale)
+        elif (isinstance(scale, types.FloatType) or
+              isinstance(scale, types.IntType)):
+            self.setScale(scale)
+
+        # Set color
+        if color:
+            # Set color, if specified
+            self.setColor(color[0], color[1], color[2], color[3])
+
+    def setGeom(self, geom):
+        # Assign geometry
+        self.removeNode()
+        # Assign geometry
+        if isinstance(geom, NodePath):
+            self.assign(geom.copyTo(parent))
+        elif type(geom) == type(''):
+            self.assign(loader.loadModelCopy(geom))
+            self.reparentTo(parent)
+
+    def getGeom(self):
+        return self
+    
+    def configure(self, option=None, **kw):
+	for option, value in kw.items():
+            # Use option string to access setter function
+            try:
+                setter = eval('self.set' +
+                              string.upper(option[0]) + option[1:])
+                if (((setter == self.setPos) or
+                     (setter == self.setHpr) or
+                     (setter == self.setScale)) and
+                    (isinstance(value, types.TupleType) or
+                     isinstance(value, types.ListType))):
+                    apply(setter,value)
+                else:
+                    setter(value)
+            except AttributeError:
+                print 'OnscreenText.configure: invalid option:', option
+
+    # Allow index style references
+    def __setitem__(self, key, value):
+        apply(self.configure, (), {key: value})
+        
+    def cget(self, option):
+	# Get current configuration setting.
+        # This is for compatability with DirectGui functions
+        getter = eval('self.get' + string.upper(option[0]) + option[1:])
+        return getter()
+
+    # Allow index style refererences
+    __getitem__ = cget
+    
+
+
+class OnscreenImage(PandaObject, NodePath):
+    def __init__(self, image = None,
+                 pos = None,
+                 hpr = None,
+                 scale = None,
+                 color = None,
+                 parent = aspect2d):
+        """__init__(self, ...)
+
+        Make a image node from string or a node path,
+        put it into the 2d sg and set it up with all the indicated parameters.
+
+        The parameters are as follows:
+
+          image: the actual geometry to display or a file name.
+                This may be omitted and specified later via setImage()
+                if you don't have it available.
+
+          pos: the x, y, z position of the geometry on the screen.
+               This maybe a 3-tuple of floats or a vector.
+               y should be zero
+
+          hpr: the h,p,r of the geometry on the screen.
+               This maybe a 3-tuple of floats or a vector.
+
+          scale: the size of the geometry.  This may either be a single
+                 float, a 3-tuple of floats, or a vector, specifying a
+                 different x, y, z scale.  y should be 1
+
+          color: the (r, g, b, a) color of the geometry.  This is
+                 normally a 4-tuple of floats or ints.
+
+          parent: the NodePath to parent the geometry to initially.
+        """
+        # We ARE a node path.  Initially, we're an empty node path.
+        NodePath.__init__(self)
+        # Assign geometry
+        if isinstance(image, NodePath):
+            self.assign(image.copyTo(parent))
+        elif type(image) == type(()):
+            model = loader.loadModelOnce(image[0])
+            self.assign(model.find(image[1]))
+            self.reparentTo(parent)
+            model.removeNode()
+
+        # Adjust pose
+        # Set pos
+        if (isinstance(pos, types.TupleType) or
+            isinstance(pos, types.ListType)):
+            apply(self.setPos, pos)
+        elif isinstance(pos, VBase3):
+            self.setPos(pos)
+        # Hpr
+        if (isinstance(hpr, types.TupleType) or
+            isinstance(hpr, types.ListType)):
+            apply(self.setHpr, hpr)
+        elif isinstance(hpr, VBase3):
+            self.setPos(hpr)
+        # Scale
+        if (isinstance(scale, types.TupleType) or
+            isinstance(scale, types.ListType)):
+            apply(self.setScale, scale)
+        elif isinstance(scale, VBase3):
+            self.setPos(scale)
+        elif (isinstance(scale, types.FloatType) or
+              isinstance(scale, types.IntType)):
+            self.setScale(scale)
+
+        # Set color
+        if color:
+            # Set color, if specified
+            self.setColor(color[0], color[1], color[2], color[3])
+
+    def setImage(self, image):
+        # Assign geometry
+        self.removeNode()
+        if isinstance(image, NodePath):
+            self.assign(image.copyTo(parent))
+        elif type(image) == type(()):
+            model = loader.loadModelOnce(image[0])
+            self.assign(model.find(image[1]))
+            self.reparentTo(parent)
+            model.removeNode()
+
+    def getImage(self):
+        return self
+    
+    def configure(self, option=None, **kw):
+	for option, value in kw.items():
+            # Use option string to access setter function
+            try:
+                setter = eval('self.set' +
+                              string.upper(option[0]) + option[1:])
+                if (((setter == self.setPos) or
+                     (setter == self.setHpr) or
+                     (setter == self.setScale)) and
+                    (isinstance(value, types.TupleType) or
+                     isinstance(value, types.ListType))):
+                    apply(setter,value)
+                else:
+                    setter(value)
+            except AttributeError:
+                print 'OnscreenText.configure: invalid option:', option
+
+    # Allow index style references
+    def __setitem__(self, key, value):
+        apply(self.configure, (), {key: value})
+        
+    def cget(self, option):
+	# Get current configuration setting.
+        # This is for compatability with DirectGui functions
+        getter = eval('self.get' + string.upper(option[0]) + option[1:])
+        return getter()
+
+    # Allow index style refererences
+    __getitem__ = cget
+    
+
+
+"""
+EXAMPLE CODE
+import DirectButton
+smiley = loader.loadModel('models/directmodels/smiley')
+db = DirectButton.DirectButton(geom = smiley, text = 'hi',
+                               scale = .15, relief = 'raised')
+db['text_pos'] = (.8, -.8)
+db['text_scale'] = .5
+db['geom1_color'] = VBase4(1,0,0,1)
+db['text2_text'] = 'bye'
+
+def dummyCmd():
+    print 'Amazing!'
+
+db['command'] = dummyCmd
+
+rolloverSmiley = db.component('geom2')
+def shrink():
+    rolloverSmiley.setScale(1)
+    rolloverSmiley.lerpScale(.1,.1,.1, 1.0, blendType = 'easeInOut',
+                             task = 'shrink')
+db.bind('enter', shrink)
+"""