OnscreenPanel.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413
  1. from ShowBaseGlobal import *
  2. import GuiGlobals
  3. import PandaObject
  4. import Button
  5. import Label
  6. import OnscreenText
  7. import types
  8. def findPanel(uniqueName):
  9. """findPanel(string uniqueName)
  10. Returns the panel whose uniqueName is given. This is mainly
  11. useful for debugging, to get a pointer to the current onscreen
  12. panel of a particular type.
  13. """
  14. if OnscreenPanel.AllPanels.has_key(uniqueName):
  15. return OnscreenPanel.AllPanels[uniqueName]
  16. return None
  17. def cleanupPanel(uniqueName):
  18. """cleanupPanel(string uniqueName)
  19. Cleans up (removes) the panel with the given uniqueName. This
  20. may be useful when some panels know about each other and know
  21. that opening panel A should automatically close panel B, for
  22. instance.
  23. """
  24. if OnscreenPanel.AllPanels.has_key(uniqueName):
  25. # calling cleanup() will remove it out of the AllPanels dict
  26. # This way it will get removed from the dict even it we did
  27. # not clean it up using this interface (ie somebody called
  28. # self.cleanup() directly
  29. OnscreenPanel.AllPanels[uniqueName].cleanup()
  30. class OnscreenPanel(PandaObject.PandaObject, NodePath):
  31. """OnscreenPanel:
  32. This class defines the basic interface to a user-interface panel
  33. that pops up within the Panda window, overlaying whatever graphics
  34. we may have already.
  35. """
  36. AllPanels = {}
  37. def __init__(self, panelName):
  38. self.panelName = panelName
  39. self.panelSetup = 0
  40. # initialize our NodePath essence.
  41. NodePath.__init__(self, aspect2d.attachNewNode(panelName))
  42. NodePath.hide(self)
  43. def makePanel(self,
  44. rect = (-0.5, 0.5, -0.5, 0.5),
  45. bg = (1, 1, 1, 1),
  46. geom = GuiGlobals.getDefaultPanel(),
  47. geomRect = (-0.5, 0.5, -0.5, 0.5),
  48. drawOrder = 0,
  49. font = GuiGlobals.getDefaultFont(),
  50. support3d = 0):
  51. """makePanel()
  52. Initializes the geometry to render the panel with the
  53. specified parameters. This should only be called once, and
  54. generally by the __init__ function.
  55. The parameters are as follows:
  56. rect: the (left, right, bottom, top) of the panel on the
  57. screen. This is in aspect2d coordinates. The panel
  58. will be set up in its own coordinate system so that (0,
  59. 0) is the center of the panel.
  60. bg: the (r, g, b, a) background color of the panel.
  61. geom: the model to use as the background panel geometry.
  62. Normally you can safely let this default.
  63. geomRect: the (left, right, bottom, top) rectangle around
  64. the background panel geometry as it is modeled. This is
  65. used to compute how the panel should be scaled to make
  66. it fit the rectangle specified by rect, above.
  67. drawOrder: the drawing order of this panel with respect to
  68. all other things in the 'fixed' bin within render2d.
  69. Buttons and text created within the panel via
  70. makeButton() and makeText() will by default be given a
  71. drawOrder slightly higher than this. Normally you can
  72. safely let this default, unless you really expect this
  73. panel to overlay some other panels.
  74. font: the default font for buttons and text created within
  75. the panel via makeButton() and makeText().
  76. support3d: if this is set true, the panel will be set up so
  77. that 3-d geometry (like a floating head) may be safely
  78. parented to the panel.
  79. """
  80. assert not self.panelSetup
  81. # Clean up any previously existing panel with the same unique
  82. # name. We don't allow any two panels with the same name to
  83. # coexist.
  84. uniqueName = self.getUniqueName()
  85. cleanupPanel(uniqueName)
  86. # Store this panel in our map of all open panels.
  87. OnscreenPanel.AllPanels[uniqueName] = self
  88. self.panelSetup = 1
  89. self.panelDrawOrder = drawOrder
  90. self.panelFont = font
  91. self.panelButtons = []
  92. self.panelText = []
  93. if geom == None:
  94. # If 'geom' is None, it means not to have a background
  95. # panel at all.
  96. self.panelGeom = None
  97. elif isinstance(geom, types.StringType):
  98. # If 'geom' is a string, it's the name of a model to load.
  99. self.panelGeom = loader.loadModelCopy(geom)
  100. self.panelGeom.reparentTo(self)
  101. else:
  102. # Otherwise, it's a model to instance.
  103. self.panelGeom = geom.instanceTo(self)
  104. centerX = (rect[0] + rect[1]) / 2.0
  105. centerY = (rect[2] + rect[3]) / 2.0
  106. NodePath.setPos(self, centerX, 0, centerY)
  107. self.setBin('fixed', self.panelDrawOrder)
  108. self.panelRegion = None
  109. if self.panelGeom != None:
  110. # Scale and position the geometry to fill up our desired
  111. # rectangle.
  112. gCenterX = (geomRect[0] + geomRect[1]) / 2.0
  113. gCenterY = (geomRect[2] + geomRect[3]) / 2.0
  114. self.panelGeom.setPos(-gCenterX, 0, -gCenterY)
  115. self.panelGeom.setScale((rect[1] - rect[0]) / (geomRect[1] - geomRect[0]), 1,
  116. (rect[3] - rect[2]) / (geomRect[3] - geomRect[2]))
  117. if bg[3] != 1:
  118. self.panelGeom.setTransparency(1)
  119. self.panelGeom.setColor(bg[0], bg[1], bg[2], bg[3])
  120. if support3d:
  121. # If we're supposed to support 3-d geometry in front of
  122. # the panel, set a few extra attributes to ensure the
  123. # panel geometry fills up the depth buffer as it goes,
  124. # making it safe to put 3-d geometry in front of it.
  125. dw = DepthWriteTransition()
  126. self.panelGeom.setY(100)
  127. self.panelGeom.arc().setTransition(dw, 1)
  128. # Set up the panel as its own mouse region so mouse clicks on
  129. # the panel don't inadvertently drive the toon around. This
  130. # must be done after the panelGeom has been scaled
  131. # appropriately, above.
  132. self.geomRect = geomRect
  133. self.panelRegion = MouseWatcherRegion(uniqueName, 0, 0, 0, 0)
  134. self.panelRegion.setRelative(self.panelGeom,
  135. geomRect[0], geomRect[1],
  136. geomRect[2], geomRect[3])
  137. self.panelRegion.setSort(self.panelDrawOrder)
  138. def cleanup(self):
  139. """cleanup(self):
  140. Removes the panel and frees all the resources associated with
  141. it. This must be called to safely remove the panel from the
  142. screen.
  143. The return value is true if the panel was cleaned up, or false
  144. if it had already been cleaned up.
  145. """
  146. if not self.panelSetup:
  147. return 0
  148. self.hide()
  149. for button in self.panelButtons:
  150. button.cleanup()
  151. del self.panelButtons
  152. for text in self.panelText:
  153. text.cleanup()
  154. del self.panelText
  155. if not self.isEmpty():
  156. self.removeNode()
  157. # Remove this panel out of the AllPanels list
  158. uniqueName = self.getUniqueName()
  159. if OnscreenPanel.AllPanels.has_key(uniqueName):
  160. del OnscreenPanel.AllPanels[uniqueName]
  161. self.panelSetup = 0
  162. return 1
  163. def show(self):
  164. """show(self):
  165. Show everything and hang hooks
  166. """
  167. if not self.panelSetup:
  168. return 0
  169. NodePath.show(self)
  170. # show the buttons that are meant to be shown
  171. for button in self.panelButtons:
  172. if button.panelManage:
  173. button.manage(self)
  174. if button.func != None:
  175. if (button.event != None):
  176. self.accept(button.event, button.func, [button.button])
  177. else:
  178. self.accept(button.button.getDownRolloverEvent(),
  179. button.func, [button.button])
  180. button.startBehavior()
  181. if self.panelRegion != None:
  182. base.mouseWatcherNode.addRegion(self.panelRegion)
  183. def hide(self):
  184. """hide(self):
  185. Hide everything and remove hooks
  186. """
  187. if not self.panelSetup:
  188. return 0
  189. NodePath.hide(self)
  190. # hide the shown buttons and remove all hooks
  191. for button in self.panelButtons:
  192. if (button.event != None):
  193. self.ignore(button.event)
  194. else:
  195. self.ignore(button.button.getDownRolloverEvent())
  196. if button.panelManage:
  197. button.unmanage()
  198. if self.panelRegion != None:
  199. base.mouseWatcherNode.removeRegion(self.panelRegion)
  200. def makeButton(self, name,
  201. func = None,
  202. manage = 1,
  203. label = None,
  204. labels = None,
  205. scale = 0.1,
  206. width = None,
  207. align = None,
  208. drawOrder = None,
  209. font = None,
  210. pos = (0, 0),
  211. geomRect = None,
  212. supportInactive = 0,
  213. inactive = 0,
  214. upStyle = Label.ButtonUp,
  215. litStyle = Label.ButtonLit,
  216. downStyle = Label.ButtonDown,
  217. inactiveStyle = Label.ButtonInactive,
  218. event = None):
  219. """makeButton(self, ...)
  220. Creates a button on the panel. The return value is the button
  221. itself. The position of the button is relative to the panel,
  222. where (0, 0) is the center.
  223. The button will automatically be managed (i.e. made visible)
  224. unless manage is set to 0.
  225. If func is specified, it is the name of a function that will
  226. be called when the button is clicked. In this case, a hook
  227. will automatically be assigned for the button, and removed
  228. when cleanup() is called.
  229. """
  230. assert self.panelSetup
  231. if (label == None) and (labels == None):
  232. label = name
  233. if drawOrder == None:
  234. drawOrder = self.panelDrawOrder + 10
  235. if font == None:
  236. font = self.panelFont
  237. buttonName = self.getUniqueName() + '-' + name
  238. button = Button.Button(buttonName,
  239. label = label,
  240. labels = labels,
  241. scale = scale,
  242. width = width,
  243. align = align,
  244. drawOrder = drawOrder,
  245. font = font,
  246. pos = pos,
  247. geomRect = geomRect,
  248. supportInactive = supportInactive,
  249. inactive = inactive,
  250. upStyle = upStyle,
  251. litStyle = litStyle,
  252. downStyle = downStyle,
  253. inactiveStyle = inactiveStyle,
  254. event = event)
  255. self.panelButtons.append(button)
  256. button.panelManage = manage
  257. button.func = func
  258. return button
  259. def makeText(self, text = '',
  260. style = OnscreenText.Plain,
  261. pos = (0, 0),
  262. scale = None,
  263. fg = None,
  264. bg = None,
  265. shadow = None,
  266. frame = None,
  267. align = None,
  268. wordwrap = None,
  269. drawOrder = None,
  270. font = None,
  271. parent = None,
  272. mayChange = 0):
  273. """makeText(self, ...)
  274. Creates some text on the panel. The return value is an
  275. OnscreenText object. The position of the text is relative to
  276. the panel, where (0, 0) is the center.
  277. The text need not be further managed by the derived class. It
  278. will automatically be removed when cleanup() is called.
  279. """
  280. assert self.panelSetup
  281. if drawOrder == None:
  282. drawOrder = self.panelDrawOrder + 10
  283. if font == None:
  284. font = self.panelFont
  285. if parent == None:
  286. parent = self
  287. text = OnscreenText.OnscreenText(text,
  288. style = style,
  289. pos = pos,
  290. scale = scale,
  291. fg = fg,
  292. bg = bg,
  293. shadow = shadow,
  294. frame = frame,
  295. align = align,
  296. wordwrap = wordwrap,
  297. drawOrder = drawOrder,
  298. font = font,
  299. parent = parent,
  300. mayChange = mayChange)
  301. self.panelText.append(text)
  302. return text
  303. def getUniqueName(self):
  304. """getUniqueName(self):
  305. Returns a name unique to this panel. If no two instances of
  306. the same panel will ever be in existence at once, this can
  307. simply be the panel name. If, for some reason, you want to
  308. define a panel type that can have multiple instances, you
  309. should redefine this function to return a unique name for each
  310. instance.
  311. """
  312. return self.panelName
  313. def setPos(self, x, y, z):
  314. """setPos(self, x, y, z)
  315. Repositions the panel onscreen, taking all of the panel's
  316. managed buttons along with it.
  317. """
  318. assert self.panelSetup
  319. NodePath.setPos(self, x, y, z)
  320. for button in self.panelButtons:
  321. if button.managed:
  322. button.unmanage()
  323. button.manage(self)
  324. if self.panelRegion != None:
  325. self.panelRegion.setRelative(self.panelGeom,
  326. self.geomRect[0], self.geomRect[1],
  327. self.geomRect[2], self.geomRect[3])