OnscreenText.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  1. """OnscreenText module: contains the OnscreenText class"""
  2. from PandaObject import *
  3. import DirectGuiGlobals
  4. import types
  5. ## These are the styles of text we might commonly see. They set the
  6. ## overall appearance of the text according to one of a number of
  7. ## pre-canned styles. You can further customize the appearance of the
  8. ## text by specifying individual parameters as well.
  9. Plain = 1
  10. ScreenTitle = 2
  11. ScreenPrompt = 3
  12. NameConfirm = 4
  13. BlackOnWhite = 5
  14. class OnscreenText(PandaObject, NodePath):
  15. def __init__(self, text = '',
  16. style = Plain,
  17. pos = (0, 0),
  18. scale = None,
  19. fg = None,
  20. bg = None,
  21. shadow = None,
  22. frame = None,
  23. align = None,
  24. wordwrap = None,
  25. drawOrder = None,
  26. font = None,
  27. parent = aspect2d,
  28. sort = 0,
  29. mayChange = 0):
  30. """__init__(self, ...)
  31. Make a text node from string, put it into the 2d sg and set it
  32. up with all the indicated parameters.
  33. The parameters are as follows:
  34. text: the actual text to display. This may be omitted and
  35. specified later via setText() if you don't have it
  36. available, but it is better to specify it up front.
  37. style: one of the pre-canned style parameters defined at the
  38. head of this file. This sets up the default values for
  39. many of the remaining parameters if they are
  40. unspecified; however, a parameter may still be specified
  41. to explicitly set it, overriding the pre-canned style.
  42. pos: the x, y position of the text on the screen.
  43. scale: the size of the text. This may either be a single
  44. float (and it will usually be a small number like 0.07)
  45. or it may be a 2-tuple of floats, specifying a different
  46. x, y scale.
  47. fg: the (r, g, b, a) foreground color of the text. This is
  48. normally a 4-tuple of floats or ints.
  49. bg: the (r, g, b, a) background color of the text. If the
  50. fourth value, a, is nonzero, a card is created to place
  51. behind the text and set to the given color.
  52. shadow: the (r, g, b, a) color of the shadow behind the text.
  53. If the fourth value, a, is nonzero, a little drop shadow
  54. is created and placed behind the text.
  55. frame: the (r, g, b, a) color of the frame drawn around the
  56. text. If the fourth value, a, is nonzero, a frame is
  57. created around the text.
  58. align: one of TextNode.ALeft, TextNode.ARight, or TextNode.ACenter.
  59. wordwrap: either the width to wordwrap the text at, or None
  60. to specify no automatic word wrapping.
  61. drawOrder: the drawing order of this text with respect to
  62. all other things in the 'fixed' bin within render2d.
  63. The text will actually use drawOrder through drawOrder +
  64. 2.
  65. font: the font to use for the text.
  66. parent: the NodePath to parent the text to initially.
  67. mayChange: pass true if the text or its properties may need
  68. to be changed at runtime, false if it is static once
  69. created (which leads to better memory optimization).
  70. """
  71. # make a text node
  72. textNode = TextNode('')
  73. self.textNode = textNode
  74. # We ARE a node path. Initially, we're an empty node path.
  75. NodePath.__init__(self)
  76. # Choose the default parameters according to the selected
  77. # style.
  78. if style == Plain:
  79. scale = scale or 0.07
  80. fg = fg or (0, 0, 0, 1)
  81. bg = bg or (0, 0, 0, 0)
  82. shadow = shadow or (0, 0, 0, 0)
  83. frame = frame or (0, 0, 0, 0)
  84. if align == None:
  85. align = TextNode.ACenter
  86. elif style == ScreenTitle:
  87. scale = scale or 0.15
  88. fg = fg or (1, 0.2, 0.2, 1)
  89. bg = bg or (0, 0, 0, 0)
  90. shadow = shadow or (0, 0, 0, 1)
  91. frame = frame or (0, 0, 0, 0)
  92. if align == None:
  93. align = TextNode.ACenter
  94. elif style == ScreenPrompt:
  95. scale = scale or 0.1
  96. fg = fg or (1, 1, 0, 1)
  97. bg = bg or (0, 0, 0, 0)
  98. shadow = shadow or (0, 0, 0, 1)
  99. frame = frame or (0, 0, 0, 0)
  100. if align == None:
  101. align = TextNode.ACenter
  102. elif style == NameConfirm:
  103. scale = scale or 0.1
  104. fg = fg or (0, 1, 0, 1)
  105. bg = bg or (0, 0, 0, 0)
  106. shadow = shadow or (0, 0, 0, 0)
  107. frame = frame or (0, 0, 0, 0)
  108. if align == None:
  109. align = TextNode.ACenter
  110. elif style == BlackOnWhite:
  111. scale = scale or 0.1
  112. fg = fg or (0, 0, 0, 1)
  113. bg = bg or (1, 1, 1, 1)
  114. shadow = shadow or (0, 0, 0, 0)
  115. frame = frame or (0, 0, 0, 0)
  116. if align == None:
  117. align = TextNode.ACenter
  118. else:
  119. raise ValueError
  120. if not isinstance(scale, types.TupleType):
  121. # If the scale is already a tuple, it's a 2-d (x,y) scale.
  122. # Otherwise, it's a uniform scale--make it a tuple.
  123. scale = (scale, scale)
  124. # Save some of the parameters for posterity.
  125. self.scale = scale
  126. self.pos = pos
  127. if font == None:
  128. font = DirectGuiGlobals.getDefaultFont()
  129. # Freeze the node while we set all the properties
  130. textNode.freeze()
  131. textNode.setFont(font)
  132. textNode.setTextColor(fg[0], fg[1], fg[2], fg[3])
  133. textNode.setAlign(align)
  134. if wordwrap:
  135. textNode.setWordwrap(wordwrap)
  136. if bg[3] != 0:
  137. # If we have a background color, create a card.
  138. textNode.setCardColor(bg[0], bg[1], bg[2], bg[3])
  139. textNode.setCardAsMargin(0.1, 0.1, 0.1, 0.1)
  140. if shadow[3] != 0:
  141. # If we have a shadow color, create a shadow.
  142. textNode.setShadowColor(shadow[0], shadow[1], shadow[2], shadow[3])
  143. textNode.setShadow(0.03, 0.03)
  144. if frame[3] != 0:
  145. # If we have a frame color, create a frame.
  146. textNode.setFrameColor(frame[0], frame[1], frame[2], frame[3])
  147. textNode.setFrameAsMargin(0.1, 0.1, 0.1, 0.1)
  148. # Create a transform for the text for our scale and position.
  149. # We'd rather do it here, on the text itself, rather than on
  150. # our NodePath, so we have one fewer transforms in the scene
  151. # graph.
  152. mat = Mat4.scaleMat(
  153. scale[0], 1, scale[1]) * Mat4.translateMat(pos[0], 0, pos[1])
  154. textNode.setTransform(mat)
  155. if drawOrder != None:
  156. textNode.setBin('fixed')
  157. textNode.setDrawOrder(drawOrder)
  158. textNode.setText(text)
  159. if not text:
  160. # If we don't have any text, assume we'll be changing it later.
  161. self.mayChange = 1
  162. else:
  163. self.mayChange = mayChange
  164. # Ok, now update the node.
  165. if self.mayChange:
  166. # If we might change the text later, we have to keep the
  167. # TextNode around.
  168. textNode.thaw()
  169. else:
  170. # Otherwise, we can throw it away.
  171. self.textNode = textNode.generate()
  172. self.isClean = 0
  173. # Set ourselves up as the NodePath that points to this node.
  174. self.assign(parent.attachNewNode(self.textNode, sort))
  175. def cleanup(self):
  176. """cleanup(self)
  177. """
  178. self.textNode = None
  179. if self.isClean == 0:
  180. self.isClean = 1
  181. self.removeNode()
  182. def destroy(self):
  183. self.cleanup()
  184. def freeze(self):
  185. self.textNode.freeze()
  186. def thaw(self):
  187. self.textNode.thaw()
  188. # Allow changing of several of the parameters after the text has
  189. # been created. These should be used with caution; it is better
  190. # to set all the parameters up front. These functions are
  191. # primarily intended for interactive placement of the initial
  192. # text, and for those rare occasions when you actually want to
  193. # change a text's property after it has been created.
  194. # If you need to change several properties at the same time at
  195. # runtime, you should call freeze() first and thaw() afterward.
  196. def setFont(self, font):
  197. self.textNode.setFont(font)
  198. def getFont(self):
  199. return self.textNode.getFont()
  200. def setText(self, text):
  201. self.textNode.setText(text)
  202. def getText(self):
  203. return self.textNode.getText()
  204. def setX(self, x):
  205. self.setPos(x, self.pos[1])
  206. def setY(self, y):
  207. self.setPos(self.pos[0], y)
  208. def setPos(self, x, y):
  209. """setPos(self, float, float)
  210. Position the onscreen text in 2d screen space
  211. """
  212. self.pos = (x, y)
  213. mat = Mat4.scaleMat(
  214. self.scale[0],
  215. 1,
  216. self.scale[1]) * Mat4.translateMat(self.pos[0], 0, self.pos[1])
  217. self.textNode.setTransform(mat)
  218. def getPos(self):
  219. return self.pos
  220. def setScale(self, sx, sy = None):
  221. """setScale(self, float, float)
  222. Scale the text in 2d space. You may specify either a single
  223. uniform scale, or two scales, or a tuple of two scales.
  224. """
  225. if sy == None:
  226. if isinstance(sx, types.TupleType):
  227. self.scale = sx
  228. else:
  229. self.scale = (sx, sx)
  230. else:
  231. self.scale = (sx, sy)
  232. mat = Mat4.scaleMat(
  233. self.scale[0],
  234. 1,
  235. self.scale[1]) * Mat4.translateMat(self.pos[0], 0, self.pos[1])
  236. self.textNode.setTransform(mat)
  237. def getScale(self):
  238. return self.scale
  239. def setWordwrap(self, wordwrap):
  240. if wordwrap:
  241. self.textNode.setWordwrap(wordwrap)
  242. else:
  243. self.textNode.clearWordwrap()
  244. def setFg(self, fg):
  245. self.textNode.setTextColor(fg[0], fg[1], fg[2], fg[3])
  246. def setBg(self, bg):
  247. self.textNode.freeze()
  248. if bg[3] != 0:
  249. # If we have a background color, create a card.
  250. self.textNode.setCardColor(bg[0], bg[1], bg[2], bg[3])
  251. self.textNode.setCardAsMargin(0.1, 0.1, 0.1, 0.1)
  252. else:
  253. # Otherwise, remove the card.
  254. self.textNode.clearCard()
  255. self.textNode.thaw()
  256. def setShadow(self, shadow):
  257. self.textNode.freeze()
  258. if shadow[3] != 0:
  259. # If we have a shadow color, create a shadow.
  260. self.textNode.setShadowColor(shadow[0], shadow[1], shadow[2], shadow[3])
  261. self.textNode.setShadow(0.03, 0.03)
  262. else:
  263. # Otherwise, remove the shadow.
  264. self.textNode.clearShadow()
  265. self.textNode.thaw()
  266. def setFrame(self, frame):
  267. self.textNode.freeze()
  268. if frame[3] != 0:
  269. # If we have a frame color, create a frame.
  270. self.textNode.setFrameColor(frame[0], frame[1], frame[2], frame[3])
  271. self.textNode.setFrameAsMargin(0.1, 0.1, 0.1, 0.1)
  272. else:
  273. # Otherwise, remove the frame.
  274. self.textNode.clearFrame()
  275. self.textNode.thaw()
  276. def configure(self, option=None, **kw):
  277. # These is for compatability with DirectGui functions
  278. if not self.mayChange:
  279. print 'OnscreenText.configure: mayChange == 0'
  280. return
  281. # Freeze text node prior to making changes
  282. self.freeze()
  283. for option, value in kw.items():
  284. # Use option string to access setter function
  285. try:
  286. setter = eval('self.set' +
  287. string.upper(option[0]) + option[1:])
  288. if setter == self.setPos:
  289. setter(value[0], value[1])
  290. else:
  291. setter(value)
  292. except AttributeError:
  293. print 'OnscreenText.configure: invalid option:', option
  294. self.thaw()
  295. # Allow index style references
  296. def __setitem__(self, key, value):
  297. apply(self.configure, (), {key: value})
  298. def cget(self, option):
  299. # Get current configuration setting.
  300. # This is for compatability with DirectGui functions
  301. getter = eval('self.get' + string.upper(option[0]) + option[1:])
  302. return getter()
  303. def setAlign(self, align):
  304. self.textNode.setAlign(align)
  305. # Allow index style refererences
  306. __getitem__ = cget