Source code for direct.gui.OnscreenText

"""OnscreenText module: contains the OnscreenText class.

See the :ref:`onscreentext` page in the programming manual for explanation of
this class.
"""

__all__ = ['OnscreenText', 'Plain', 'ScreenTitle', 'ScreenPrompt', 'NameConfirm', 'BlackOnWhite']

from panda3d.core import *
from . import DirectGuiGlobals as DGG
import warnings

## These are the styles of text we might commonly see.  They set the
## overall appearance of the text according to one of a number of
## pre-canned styles.  You can further customize the appearance of the
## text by specifying individual parameters as well.
Plain = 1
ScreenTitle = 2
ScreenPrompt = 3
NameConfirm = 4
BlackOnWhite = 5

[docs]class OnscreenText(NodePath):
[docs] def __init__(self, text = '', style = Plain, pos = (0, 0), roll = 0, scale = None, fg = None, bg = None, shadow = None, shadowOffset = (0.04, 0.04), frame = None, align = None, wordwrap = None, drawOrder = None, decal = 0, font = None, parent = None, sort = 0, mayChange = True, direction = None): """ Make a text node from string, put it into the 2d sg and set it up with all the indicated parameters. Parameters: text: the actual text to display. This may be omitted and specified later via setText() if you don't have it available, but it is better to specify it up front. style: one of the pre-canned style parameters defined at the head of this file. This sets up the default values for many of the remaining parameters if they are unspecified; however, a parameter may still be specified to explicitly set it, overriding the pre-canned style. pos: the x, y position of the text on the screen. scale: the size of the text. This may either be a single float (and it will usually be a small number like 0.07) or it may be a 2-tuple of floats, specifying a different x, y scale. fg: the (r, g, b, a) foreground color of the text. This is normally a 4-tuple of floats or ints. bg: the (r, g, b, a) background color of the text. If the fourth value, a, is nonzero, a card is created to place behind the text and set to the given color. shadow: the (r, g, b, a) color of the shadow behind the text. If the fourth value, a, is nonzero, a little drop shadow is created and placed behind the text. frame: the (r, g, b, a) color of the frame drawn around the text. If the fourth value, a, is nonzero, a frame is created around the text. align: one of TextNode.ALeft, TextNode.ARight, or TextNode.ACenter. wordwrap: either the width to wordwrap the text at, or None to specify no automatic word wrapping. drawOrder: the drawing order of this text with respect to all other things in the 'fixed' bin within render2d. The text will actually use drawOrder through drawOrder + 2. decal: if this is True, the text is decalled onto its background card. Useful when the text will be parented into the 3-D scene graph. font: the font to use for the text. parent: the NodePath to parent the text to initially. mayChange: pass true if the text or its properties may need to be changed at runtime, false if it is static once created (which leads to better memory optimization). direction: this can be set to 'ltr' or 'rtl' to override the direction of the text. """ if parent is None: from direct.showbase import ShowBaseGlobal parent = ShowBaseGlobal.aspect2d # make a text node textNode = TextNode('') self.textNode = textNode # We ARE a node path. Initially, we're an empty node path. NodePath.__init__(self) # Choose the default parameters according to the selected # style. if style == Plain: scale = scale or 0.07 fg = fg or (0, 0, 0, 1) bg = bg or (0, 0, 0, 0) shadow = shadow or (0, 0, 0, 0) frame = frame or (0, 0, 0, 0) if align is None: align = TextNode.ACenter elif style == ScreenTitle: scale = scale or 0.15 fg = fg or (1, 0.2, 0.2, 1) bg = bg or (0, 0, 0, 0) shadow = shadow or (0, 0, 0, 1) frame = frame or (0, 0, 0, 0) if align is None: align = TextNode.ACenter elif style == ScreenPrompt: scale = scale or 0.1 fg = fg or (1, 1, 0, 1) bg = bg or (0, 0, 0, 0) shadow = shadow or (0, 0, 0, 1) frame = frame or (0, 0, 0, 0) if align is None: align = TextNode.ACenter elif style == NameConfirm: scale = scale or 0.1 fg = fg or (0, 1, 0, 1) bg = bg or (0, 0, 0, 0) shadow = shadow or (0, 0, 0, 0) frame = frame or (0, 0, 0, 0) if align is None: align = TextNode.ACenter elif style == BlackOnWhite: scale = scale or 0.1 fg = fg or (0, 0, 0, 1) bg = bg or (1, 1, 1, 1) shadow = shadow or (0, 0, 0, 0) frame = frame or (0, 0, 0, 0) if align is None: align = TextNode.ACenter else: raise ValueError if not isinstance(scale, tuple): # If the scale is already a tuple, it's a 2-d (x, y) scale. # Otherwise, it's a uniform scale--make it a tuple. scale = (scale, scale) # Save some of the parameters for posterity. self.__scale = scale self.__pos = pos self.__roll = roll self.__wordwrap = wordwrap if decal: textNode.setCardDecal(1) if font is None: font = DGG.getDefaultFont() textNode.setFont(font) textNode.setTextColor(fg[0], fg[1], fg[2], fg[3]) textNode.setAlign(align) if wordwrap: textNode.setWordwrap(wordwrap) if bg[3] != 0: # If we have a background color, create a card. textNode.setCardColor(bg[0], bg[1], bg[2], bg[3]) textNode.setCardAsMargin(0.1, 0.1, 0.1, 0.1) if shadow[3] != 0: # If we have a shadow color, create a shadow. # Can't use the *shadow interface because it might be a VBase4. #textNode.setShadowColor(*shadow) textNode.setShadowColor(shadow[0], shadow[1], shadow[2], shadow[3]) textNode.setShadow(*shadowOffset) if frame[3] != 0: # If we have a frame color, create a frame. textNode.setFrameColor(frame[0], frame[1], frame[2], frame[3]) textNode.setFrameAsMargin(0.1, 0.1, 0.1, 0.1) if direction is not None: if isinstance(direction, str): direction = direction.lower() if direction == 'rtl': direction = TextProperties.D_rtl elif direction == 'ltr': direction = TextProperties.D_ltr else: raise ValueError('invalid direction') textNode.setDirection(direction) # Create a transform for the text for our scale and position. # We'd rather do it here, on the text itself, rather than on # our NodePath, so we have one fewer transforms in the scene # graph. self.updateTransformMat() if drawOrder is not None: textNode.setBin('fixed') textNode.setDrawOrder(drawOrder) self.setText(text) if not text: # If we don't have any text, assume we'll be changing it later. self.mayChange = 1 else: self.mayChange = mayChange # Ok, now update the node. if not self.mayChange: # If we aren't going to change the text later, we can # throw away the TextNode. self.textNode = textNode.generate() self.isClean = 0 # Set ourselves up as the NodePath that points to this node. self.assign(parent.attachNewNode(self.textNode, sort))
[docs] def cleanup(self): self.textNode = None if self.isClean == 0: self.isClean = 1 self.removeNode()
[docs] def destroy(self): self.cleanup()
[docs] def freeze(self): pass
[docs] def thaw(self): pass
# Allow changing of several of the parameters after the text has # been created. These should be used with caution; it is better # to set all the parameters up front. These functions are # primarily intended for interactive placement of the initial # text, and for those rare occasions when you actually want to # change a text's property after it has been created.
[docs] def setDecal(self, decal): self.textNode.setCardDecal(decal)
[docs] def getDecal(self): return self.textNode.getCardDecal()
decal = property(getDecal, setDecal)
[docs] def setFont(self, font): self.textNode.setFont(font)
[docs] def getFont(self): return self.textNode.getFont()
font = property(getFont, setFont)
[docs] def clearText(self): self.textNode.clearText()
[docs] def setText(self, text): assert not isinstance(text, bytes) self.textNode.setWtext(text)
[docs] def appendText(self, text): assert not isinstance(text, bytes) self.textNode.appendWtext(text)
[docs] def getText(self): return self.textNode.getWtext()
text = property(getText, setText)
[docs] def setTextX(self, x): """ .. versionadded:: 1.10.8 """ self.setTextPos(x, self.__pos[1])
[docs] def setX(self, x): """ .. deprecated:: 1.11.0 Use `.setTextX()` method instead. """ if __debug__: warnings.warn("Use `.setTextX()` method instead.", DeprecationWarning, stacklevel=2) self.setTextPos(x, self.__pos[1])
[docs] def setTextY(self, y): """ .. versionadded:: 1.10.8 """ self.setTextPos(self.__pos[0], y)
[docs] def setY(self, y): """ .. deprecated:: 1.11.0 Use `.setTextY()` method instead. """ if __debug__: warnings.warn("Use `.setTextY()` method instead.", DeprecationWarning, stacklevel=2) self.setTextPos(self.__pos[0], y)
[docs] def setTextPos(self, x, y=None): """ Position the onscreen text in 2d screen space .. versionadded:: 1.10.8 """ if y is None: self.__pos = tuple(x) else: self.__pos = (x, y) self.updateTransformMat()
[docs] def getTextPos(self): """ .. versionadded:: 1.10.8 """ return self.__pos
text_pos = property(getTextPos, setTextPos)
[docs] def setPos(self, x, y): """setPos(self, float, float) Position the onscreen text in 2d screen space .. deprecated:: 1.11.0 Use `.setTextPos()` method or `.text_pos` property instead. """ if __debug__: warnings.warn("Use `.setTextPos()` method or `.text_pos` property instead.", DeprecationWarning, stacklevel=2) self.__pos = (x, y) self.updateTransformMat()
[docs] def getPos(self): """ .. deprecated:: 1.11.0 Use `.getTextPos()` method or `.text_pos` property instead. """ if __debug__: warnings.warn("Use `.getTextPos()` method or `.text_pos` property instead.", DeprecationWarning, stacklevel=2) return self.__pos
pos = property(getPos)
[docs] def setTextR(self, r): """setTextR(self, float) Rotates the text around the screen's normal. .. versionadded:: 1.10.8 """ self.__roll = -r self.updateTransformMat()
[docs] def getTextR(self): return -self.__roll
text_r = property(getTextR, setTextR)
[docs] def setRoll(self, roll): """setRoll(self, float) Rotate the onscreen text around the screen's normal. .. deprecated:: 1.11.0 Use ``setTextR(-roll)`` instead (note the negated sign). """ if __debug__: warnings.warn("Use ``setTextR(-roll)`` instead (note the negated sign).", DeprecationWarning, stacklevel=2) self.__roll = roll self.updateTransformMat()
[docs] def getRoll(self): """ .. deprecated:: 1.11.0 Use ``-getTextR()`` instead (note the negated sign). """ if __debug__: warnings.warn("Use ``-getTextR()`` instead (note the negated sign).", DeprecationWarning, stacklevel=2) return self.__roll
roll = property(getRoll, setRoll)
[docs] def setTextScale(self, sx, sy = None): """setTextScale(self, float, float) Scale the text in 2d space. You may specify either a single uniform scale, or two scales, or a tuple of two scales. .. versionadded:: 1.10.8 """ if sy is None: if isinstance(sx, tuple): self.__scale = sx else: self.__scale = (sx, sx) else: self.__scale = (sx, sy) self.updateTransformMat()
[docs] def getTextScale(self): """ .. versionadded:: 1.10.8 """ return self.__scale
text_scale = property(getTextScale, setTextScale)
[docs] def setScale(self, sx, sy = None): """setScale(self, float, float) Scale the text in 2d space. You may specify either a single uniform scale, or two scales, or a tuple of two scales. .. deprecated:: 1.11.0 Use `.setTextScale()` method or `.text_scale` property instead. """ if __debug__: warnings.warn("Use `.setTextScale()` method or `.text_scale` property instead.", DeprecationWarning, stacklevel=2) if sy is None: if isinstance(sx, tuple): self.__scale = sx else: self.__scale = (sx, sx) else: self.__scale = (sx, sy) self.updateTransformMat()
[docs] def getScale(self): """ .. deprecated:: 1.11.0 Use `.getTextScale()` method or `.text_scale` property instead. """ if __debug__: warnings.warn("Use `.getTextScale()` method or `.text_scale` property instead.", DeprecationWarning, stacklevel=2) return self.__scale
scale = property(getScale, setScale)
[docs] def updateTransformMat(self): assert isinstance(self.textNode, TextNode) mat = ( Mat4.scaleMat(Vec3.rfu(self.__scale[0], 1, self.__scale[1])) * Mat4.rotateMat(self.__roll, Vec3.back()) * Mat4.translateMat(Point3.rfu(self.__pos[0], 0, self.__pos[1])) ) self.textNode.setTransform(mat)
[docs] def setWordwrap(self, wordwrap): self.__wordwrap = wordwrap if wordwrap: self.textNode.setWordwrap(wordwrap) else: self.textNode.clearWordwrap()
[docs] def getWordwrap(self): return self.__wordwrap
wordwrap = property(getWordwrap, setWordwrap) def __getFg(self): return self.textNode.getTextColor()
[docs] def setFg(self, fg): self.textNode.setTextColor(fg[0], fg[1], fg[2], fg[3])
fg = property(__getFg, setFg) def __getBg(self): if self.textNode.hasCard(): return self.textNode.getCardColor() else: return LColor(0)
[docs] def setBg(self, bg): if bg[3] != 0: # If we have a background color, create a card. self.textNode.setCardColor(bg[0], bg[1], bg[2], bg[3]) self.textNode.setCardAsMargin(0.1, 0.1, 0.1, 0.1) else: # Otherwise, remove the card. self.textNode.clearCard()
bg = property(__getBg, setBg) def __getShadow(self): return self.textNode.getShadowColor()
[docs] def setShadow(self, shadow): if shadow[3] != 0: # If we have a shadow color, create a shadow. self.textNode.setShadowColor(shadow[0], shadow[1], shadow[2], shadow[3]) self.textNode.setShadow(0.04, 0.04) else: # Otherwise, remove the shadow. self.textNode.clearShadow()
shadow = property(__getShadow, setShadow) def __getFrame(self): return self.textNode.getFrameColor()
[docs] def setFrame(self, frame): if frame[3] != 0: # If we have a frame color, create a frame. self.textNode.setFrameColor(frame[0], frame[1], frame[2], frame[3]) self.textNode.setFrameAsMargin(0.1, 0.1, 0.1, 0.1) else: # Otherwise, remove the frame. self.textNode.clearFrame()
frame = property(__getFrame, setFrame)
[docs] def configure(self, option=None, **kw): # These is for compatibility with DirectGui functions if not self.mayChange: print('OnscreenText.configure: mayChange == 0') return for option, value in kw.items(): # Use option string to access setter function try: if option == 'pos': self.setTextPos(value[0], value[1]) elif option == 'roll': self.setTextR(-value) elif option == 'scale': self.setTextScale(value) elif option == 'x': self.setTextX(value) elif option == 'y': self.setTextY(value) else: setter = getattr(self, 'set' + option[0].upper() + option[1:]) setter(value) except AttributeError: print('OnscreenText.configure: invalid option: %s' % option)
# Allow index style references def __setitem__(self, key, value): self.configure(*(), **{key: value})
[docs] def cget(self, option): # Get current configuration setting. # This is for compatibility with DirectGui functions if option == 'pos': return self.__pos elif option == 'roll': return self.__roll elif option == 'scale': return self.__scale elif option == 'x': return self.__pos[0] elif option == 'y': return self.__pos[1] getter = getattr(self, 'get' + option[0].upper() + option[1:]) return getter()
def __getAlign(self): return self.textNode.getAlign()
[docs] def setAlign(self, align): self.textNode.setAlign(align)
align = property(__getAlign, setAlign) # Allow index style refererences __getitem__ = cget