2
0

mappingGUI.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  1. #!/usr/bin/env python
  2. '''
  3. Demonstrate how a simple button mapping gui can be written
  4. '''
  5. from direct.showbase.ShowBase import ShowBase
  6. from direct.gui.DirectGui import (
  7. DGG,
  8. DirectFrame,
  9. DirectButton,
  10. DirectLabel,
  11. OkCancelDialog,
  12. DirectScrolledFrame)
  13. from panda3d.core import (
  14. VBase4,
  15. TextNode,
  16. Vec2,
  17. InputDevice,
  18. loadPrcFileData)
  19. # Make sure the textures look crisp on every device that supports
  20. # non-power-2 textures
  21. loadPrcFileData("", "textures-auto-power-2 #t")
  22. class App(ShowBase):
  23. def __init__(self):
  24. ShowBase.__init__(self)
  25. self.setBackgroundColor(0, 0, 0)
  26. # make the font look nice at a big scale
  27. DGG.getDefaultFont().setPixelsPerUnit(100)
  28. # a dict of actions and button/axis events
  29. self.gamepadMapping = {
  30. "Move forward":"Left Stick Y",
  31. "Move backward":"Left Stick Y",
  32. "Move left":"Left Stick X",
  33. "Move right":"Left Stick X",
  34. "Jump":"a",
  35. "Action":"b",
  36. "Sprint":"x",
  37. "Map":"y",
  38. "action-1":"c",
  39. "action-2":"d",
  40. "action-3":"e",
  41. "action-4":"f",
  42. "action-5":"g",
  43. "action-6":"h",
  44. "action-7":"i",
  45. "action-8":"j",
  46. "action-9":"k",
  47. "action-10":"l",
  48. "action-11":"m",
  49. }
  50. # this will store the action that we want to remap
  51. self.actionToMap = ""
  52. # this will store the key/axis that we want to asign to an action
  53. self.newActionKey = ""
  54. # this will store the label that needs to be actualized in the list
  55. self.actualizeLabel = None
  56. # The geometry for our basic buttons
  57. maps = loader.loadModel("models/button_map")
  58. self.buttonGeom = (
  59. maps.find("**/ready"),
  60. maps.find("**/click"),
  61. maps.find("**/hover"),
  62. maps.find("**/disabled"))
  63. # Create the dialog that asks the user for input on a given
  64. # action to map a key to.
  65. DGG.setDefaultDialogGeom("models/dialog.png")
  66. # setup a dialog to ask for device input
  67. self.dlgInput = OkCancelDialog(
  68. dialogName="dlg_device_input",
  69. pos=(0, 0, 0.25),
  70. text="Hit desired key:",
  71. text_fg=VBase4(0.898, 0.839, 0.730, 1.0),
  72. text_shadow=VBase4(0, 0, 0, 0.75),
  73. text_shadowOffset=Vec2(0.05, 0.05),
  74. text_scale=0.05,
  75. text_align=TextNode.ACenter,
  76. fadeScreen=0.65,
  77. frameColor=VBase4(0.3, 0.3, 0.3, 1),
  78. button_geom=self.buttonGeom,
  79. button_scale=0.15,
  80. button_text_scale=0.35,
  81. button_text_align=TextNode.ALeft,
  82. button_text_fg=VBase4(0.898, 0.839, 0.730, 1.0),
  83. button_text_pos=Vec2(-0.9, -0.125),
  84. button_relief=1,
  85. button_pad=Vec2(0.01, 0.01),
  86. button_frameColor=VBase4(0, 0, 0, 0),
  87. button_frameSize=VBase4(-1.0, 1.0, -0.25, 0.25),
  88. button_pressEffect=False,
  89. command=self.closeDialog)
  90. self.dlgInput.setTransparency(True)
  91. self.dlgInput.configureDialog()
  92. scale = self.dlgInput["image_scale"]
  93. self.dlgInput["image_scale"] = (scale[0]/2.0, scale[1], scale[2]/2.0)
  94. self.dlgInput["text_pos"] = (self.dlgInput["text_pos"][0], self.dlgInput["text_pos"][1] + 0.06)
  95. self.dlgInput.hide()
  96. # create a sample title
  97. self.textscale = 0.1
  98. self.title = DirectLabel(
  99. scale=self.textscale,
  100. pos=(base.a2dLeft + 0.05, 0.0, base.a2dTop - (self.textscale + 0.05)),
  101. frameColor=VBase4(0, 0, 0, 0),
  102. text="Button Mapping",
  103. text_align=TextNode.ALeft,
  104. text_fg=VBase4(1, 1, 1, 1),
  105. text_shadow=VBase4(0, 0, 0, 0.75),
  106. text_shadowOffset=Vec2(0.05, 0.05))
  107. self.title.setTransparency(1)
  108. # Set up the list of actions that we can map keys to
  109. # create a frame that will create the scrollbars for us
  110. # Load the models for the scrollbar elements
  111. thumbMaps = loader.loadModel("models/thumb_map")
  112. thumbGeom = (
  113. thumbMaps.find("**/thumb_ready"),
  114. thumbMaps.find("**/thumb_click"),
  115. thumbMaps.find("**/thumb_hover"),
  116. thumbMaps.find("**/thumb_disabled"))
  117. incMaps = loader.loadModel("models/inc_map")
  118. incGeom = (
  119. incMaps.find("**/inc_ready"),
  120. incMaps.find("**/inc_click"),
  121. incMaps.find("**/inc_hover"),
  122. incMaps.find("**/inc_disabled"))
  123. decMaps = loader.loadModel("models/dec_map")
  124. decGeom = (
  125. decMaps.find("**/dec_ready"),
  126. decMaps.find("**/dec_click"),
  127. decMaps.find("**/dec_hover"),
  128. decMaps.find("**/dec_disabled"))
  129. # create the scrolled frame that will hold our list
  130. self.lstActionMap = DirectScrolledFrame(
  131. # make the frame occupy the whole window
  132. frameSize=VBase4(base.a2dLeft, base.a2dRight, 0.0, 1.55),
  133. # make the canvas as big as the frame
  134. canvasSize=VBase4(base.a2dLeft, base.a2dRight, 0.0, 0.0),
  135. # set the frames color to white
  136. frameColor=VBase4(0, 0, 0.25, 0.75),
  137. pos=(0, 0, -0.8),
  138. verticalScroll_scrollSize=0.2,
  139. verticalScroll_frameColor=VBase4(0.02, 0.02, 0.02, 1),
  140. verticalScroll_thumb_relief=1,
  141. verticalScroll_thumb_geom=thumbGeom,
  142. verticalScroll_thumb_pressEffect=False,
  143. verticalScroll_thumb_frameColor=VBase4(0, 0, 0, 0),
  144. verticalScroll_incButton_relief=1,
  145. verticalScroll_incButton_geom=incGeom,
  146. verticalScroll_incButton_pressEffect=False,
  147. verticalScroll_incButton_frameColor=VBase4(0, 0, 0, 0),
  148. verticalScroll_decButton_relief=1,
  149. verticalScroll_decButton_geom=decGeom,
  150. verticalScroll_decButton_pressEffect=False,
  151. verticalScroll_decButton_frameColor=VBase4(0, 0, 0, 0),)
  152. # creat the list items
  153. idx = 0
  154. self.listBGEven = base.loader.loadModel("models/list_item_even")
  155. self.listBGOdd = base.loader.loadModel("models/list_item_odd")
  156. for key, value in self.gamepadMapping.items():
  157. item = self.__makeListItem(key, key, value, idx)
  158. item.reparentTo(self.lstActionMap.getCanvas())
  159. idx += 1
  160. # recalculate the canvas size to set scrollbars if necesary
  161. self.lstActionMap["canvasSize"] = (
  162. base.a2dLeft+0.05, base.a2dRight-0.05,
  163. -(len(self.gamepadMapping.keys())*0.1), 0.09)
  164. self.lstActionMap.setCanvasSize()
  165. def closeDialog(self, result):
  166. self.dlgInput.hide()
  167. if result == DGG.DIALOG_OK:
  168. # map the event to the given action
  169. self.gamepadMapping[self.actionToMap] = self.newActionKey
  170. # actualize the label in the list that shows the current
  171. # event for the action
  172. self.actualizeLabel["text"] = self.newActionKey
  173. # cleanup
  174. self.dlgInput["text"] ="Hit desired key:"
  175. self.actionToMap = ""
  176. self.newActionKey = ""
  177. self.actualizeLabel = None
  178. for bt in base.buttonThrowers:
  179. bt.node().setButtonDownEvent("")
  180. for bt in base.deviceButtonThrowers:
  181. bt.node().setButtonDownEvent("")
  182. taskMgr.remove("checkControls")
  183. def changeMapping(self, action, label):
  184. # set the action that we want to map a new key to
  185. self.actionToMap = action
  186. # set the label that needs to be actualized
  187. self.actualizeLabel = label
  188. # show our dialog
  189. self.dlgInput.show()
  190. # catch all button events
  191. for bt in base.buttonThrowers:
  192. bt.node().setButtonDownEvent("keyListenEvent")
  193. for bt in base.deviceButtonThrowers:
  194. bt.node().setButtonDownEvent("deviceListenEvent")
  195. self.setKeyCalled = False
  196. self.accept("keyListenEvent", self.setKey)
  197. self.accept("deviceListenEvent", self.setDeviceKey)
  198. # As there are no events thrown for control changes, we set up
  199. # a task to check if the controls got moved
  200. # This list will help us for checking which controls got moved
  201. self.controlStates = {None:{}}
  202. # fill it with all available controls
  203. for device in base.devices.get_devices():
  204. for ctrl in device.controls:
  205. if device not in self.controlStates.keys():
  206. self.controlStates.update({device: {ctrl.axis: ctrl.state}})
  207. else:
  208. self.controlStates[device].update({ctrl.axis: ctrl.state})
  209. # start the task
  210. taskMgr.add(self.watchControls, "checkControls")
  211. def watchControls(self, task):
  212. # move through all devices and all it's controls
  213. for device in base.devices.get_devices():
  214. for ctrl in device.controls:
  215. # if a control got changed more than the given puffer zone
  216. if self.controlStates[device][ctrl.axis] + 0.2 < ctrl.state or \
  217. self.controlStates[device][ctrl.axis] - 0.2 > ctrl.state:
  218. # set the current state in the dict
  219. self.controlStates[device][ctrl.axis] = ctrl.state
  220. # check which axis got moved
  221. if ctrl.axis == InputDevice.C_left_x:
  222. self.setKey("Left Stick X")
  223. elif ctrl.axis == InputDevice.C_left_y:
  224. self.setKey("Left Stick Y")
  225. elif ctrl.axis == InputDevice.C_left_trigger:
  226. self.setKey("Left Trigger")
  227. elif ctrl.axis == InputDevice.C_right_x:
  228. self.setKey("Right Stick X")
  229. elif ctrl.axis == InputDevice.C_right_y:
  230. self.setKey("Right Stick Y")
  231. elif ctrl.axis == InputDevice.C_right_trigger:
  232. self.setKey("Right Trigger")
  233. elif ctrl.axis == InputDevice.C_x:
  234. self.setKey("X")
  235. elif ctrl.axis == InputDevice.C_y:
  236. self.setKey("Y")
  237. elif ctrl.axis == InputDevice.C_trigger:
  238. self.setKey("Trigger")
  239. elif ctrl.axis == InputDevice.C_throttle:
  240. self.setKey("Throttle")
  241. elif ctrl.axis == InputDevice.C_rudder:
  242. self.setKey("Rudder")
  243. elif ctrl.axis == InputDevice.C_hat_x:
  244. self.setKey("Hat X")
  245. elif ctrl.axis == InputDevice.C_hat_y:
  246. self.setKey("Hat Y")
  247. elif ctrl.axis == InputDevice.C_wheel:
  248. self.setKey("Wheel")
  249. elif ctrl.axis == InputDevice.C_accelerator:
  250. self.setKey("Acclerator")
  251. elif ctrl.axis == InputDevice.C_brake:
  252. self.setKey("Break")
  253. return task.cont
  254. def setKey(self, args):
  255. self.setKeyCalled = True
  256. if self.dlgInput.buttonList[0].guiItem.getState() == 1:
  257. # this occurs if the OK button was clicked. To prevent to
  258. # always set the mouse1 event whenever the OK button was
  259. # pressed, we instantly return from this function
  260. return
  261. self.dlgInput["text"] = "New event will be:\n\n" + args
  262. self.newActionKey = args
  263. def setDeviceKey(self, args):
  264. if not self.setKeyCalled:
  265. self.setKey(args)
  266. self.setKeyCalled = False
  267. def __makeListItem(self, itemName, action, event, index):
  268. def dummy(): pass
  269. if index % 2 == 0:
  270. bg = self.listBGEven
  271. else:
  272. bg = self.listBGOdd
  273. item = DirectFrame(
  274. text=itemName,
  275. geom=bg,
  276. geom_scale=(base.a2dRight-0.05, 1, 0.1),
  277. frameSize=VBase4(base.a2dLeft+0.05, base.a2dRight-0.05, -0.05, 0.05),
  278. frameColor=VBase4(1,0,0,0),
  279. text_align=TextNode.ALeft,
  280. text_scale=0.05,
  281. text_fg=VBase4(1,1,1,1),
  282. text_pos=(base.a2dLeft + 0.3, -0.015),
  283. text_shadow=VBase4(0, 0, 0, 0.35),
  284. text_shadowOffset=Vec2(-0.05, -0.05),
  285. pos=(0.05, 0, -(0.10 * index)))
  286. item.setTransparency(True)
  287. lbl = DirectLabel(
  288. text=event,
  289. text_fg=VBase4(1, 1, 1, 1),
  290. text_scale=0.05,
  291. text_pos=Vec2(0, -0.015),
  292. frameColor=VBase4(0, 0, 0, 0),
  293. )
  294. lbl.reparentTo(item)
  295. lbl.setTransparency(True)
  296. buttonScale = 0.15
  297. btn = DirectButton(
  298. text="Change",
  299. geom=self.buttonGeom,
  300. scale=buttonScale,
  301. text_scale=0.25,
  302. text_align=TextNode.ALeft,
  303. text_fg=VBase4(0.898, 0.839, 0.730, 1.0),
  304. text_pos=Vec2(-0.9, -0.085),
  305. relief=1,
  306. pad=Vec2(0.01, 0.01),
  307. frameColor=VBase4(0, 0, 0, 0),
  308. frameSize=VBase4(-1.0, 1.0, -0.25, 0.25),
  309. pos=(base.a2dRight-(0.898*buttonScale+0.3), 0, 0),
  310. pressEffect=False,
  311. command=self.changeMapping,
  312. extraArgs=[action, lbl])
  313. btn.setTransparency(True)
  314. btn.reparentTo(item)
  315. return item
  316. app = App()
  317. app.run()