panda3d/samples/gamepad/mappingGUI.py
2016-08-10 13:11:24 +02:00

343 lines
13 KiB
Python

#!/usr/bin/env python
'''
Demonstrate how a simple button mapping gui can be written
'''
from direct.showbase.ShowBase import ShowBase
from direct.gui.DirectGui import (
DGG,
DirectFrame,
DirectButton,
DirectLabel,
OkCancelDialog,
DirectScrolledFrame)
from panda3d.core import (
VBase4,
TextNode,
Vec2,
InputDevice,
loadPrcFileData)
# Make sure the textures look crisp on every device that supports
# non-power-2 textures
loadPrcFileData("", "textures-auto-power-2 #t")
class App(ShowBase):
def __init__(self):
ShowBase.__init__(self)
self.setBackgroundColor(0, 0, 0)
# make the font look nice at a big scale
DGG.getDefaultFont().setPixelsPerUnit(100)
# a dict of actions and button/axis events
self.gamepadMapping = {
"Move forward":"Left Stick Y",
"Move backward":"Left Stick Y",
"Move left":"Left Stick X",
"Move right":"Left Stick X",
"Jump":"a",
"Action":"b",
"Sprint":"x",
"Map":"y",
"action-1":"c",
"action-2":"d",
"action-3":"e",
"action-4":"f",
"action-5":"g",
"action-6":"h",
"action-7":"i",
"action-8":"j",
"action-9":"k",
"action-10":"l",
"action-11":"m",
}
# this will store the action that we want to remap
self.actionToMap = ""
# this will store the key/axis that we want to asign to an action
self.newActionKey = ""
# this will store the label that needs to be actualized in the list
self.actualizeLabel = None
# The geometry for our basic buttons
maps = loader.loadModel("models/button_map")
self.buttonGeom = (
maps.find("**/ready"),
maps.find("**/click"),
maps.find("**/hover"),
maps.find("**/disabled"))
# Create the dialog that asks the user for input on a given
# action to map a key to.
DGG.setDefaultDialogGeom("models/dialog.png")
# setup a dialog to ask for device input
self.dlgInput = OkCancelDialog(
dialogName="dlg_device_input",
pos=(0, 0, 0.25),
text="Hit desired key:",
text_fg=VBase4(0.898, 0.839, 0.730, 1.0),
text_shadow=VBase4(0, 0, 0, 0.75),
text_shadowOffset=Vec2(0.05, 0.05),
text_scale=0.05,
text_align=TextNode.ACenter,
fadeScreen=0.65,
frameColor=VBase4(0.3, 0.3, 0.3, 1),
button_geom=self.buttonGeom,
button_scale=0.15,
button_text_scale=0.35,
button_text_align=TextNode.ALeft,
button_text_fg=VBase4(0.898, 0.839, 0.730, 1.0),
button_text_pos=Vec2(-0.9, -0.125),
button_relief=1,
button_pad=Vec2(0.01, 0.01),
button_frameColor=VBase4(0, 0, 0, 0),
button_frameSize=VBase4(-1.0, 1.0, -0.25, 0.25),
button_pressEffect=False,
command=self.closeDialog)
self.dlgInput.setTransparency(True)
self.dlgInput.configureDialog()
scale = self.dlgInput["image_scale"]
self.dlgInput["image_scale"] = (scale[0]/2.0, scale[1], scale[2]/2.0)
self.dlgInput["text_pos"] = (self.dlgInput["text_pos"][0], self.dlgInput["text_pos"][1] + 0.06)
self.dlgInput.hide()
# create a sample title
self.textscale = 0.1
self.title = DirectLabel(
scale=self.textscale,
pos=(base.a2dLeft + 0.05, 0.0, base.a2dTop - (self.textscale + 0.05)),
frameColor=VBase4(0, 0, 0, 0),
text="Button Mapping",
text_align=TextNode.ALeft,
text_fg=VBase4(1, 1, 1, 1),
text_shadow=VBase4(0, 0, 0, 0.75),
text_shadowOffset=Vec2(0.05, 0.05))
self.title.setTransparency(1)
# Set up the list of actions that we can map keys to
# create a frame that will create the scrollbars for us
# Load the models for the scrollbar elements
thumbMaps = loader.loadModel("models/thumb_map")
thumbGeom = (
thumbMaps.find("**/thumb_ready"),
thumbMaps.find("**/thumb_click"),
thumbMaps.find("**/thumb_hover"),
thumbMaps.find("**/thumb_disabled"))
incMaps = loader.loadModel("models/inc_map")
incGeom = (
incMaps.find("**/inc_ready"),
incMaps.find("**/inc_click"),
incMaps.find("**/inc_hover"),
incMaps.find("**/inc_disabled"))
decMaps = loader.loadModel("models/dec_map")
decGeom = (
decMaps.find("**/dec_ready"),
decMaps.find("**/dec_click"),
decMaps.find("**/dec_hover"),
decMaps.find("**/dec_disabled"))
# create the scrolled frame that will hold our list
self.lstActionMap = DirectScrolledFrame(
# make the frame occupy the whole window
frameSize=VBase4(base.a2dLeft, base.a2dRight, 0.0, 1.55),
# make the canvas as big as the frame
canvasSize=VBase4(base.a2dLeft, base.a2dRight, 0.0, 0.0),
# set the frames color to white
frameColor=VBase4(0, 0, 0.25, 0.75),
pos=(0, 0, -0.8),
verticalScroll_scrollSize=0.2,
verticalScroll_frameColor=VBase4(0.02, 0.02, 0.02, 1),
verticalScroll_thumb_relief=1,
verticalScroll_thumb_geom=thumbGeom,
verticalScroll_thumb_pressEffect=False,
verticalScroll_thumb_frameColor=VBase4(0, 0, 0, 0),
verticalScroll_incButton_relief=1,
verticalScroll_incButton_geom=incGeom,
verticalScroll_incButton_pressEffect=False,
verticalScroll_incButton_frameColor=VBase4(0, 0, 0, 0),
verticalScroll_decButton_relief=1,
verticalScroll_decButton_geom=decGeom,
verticalScroll_decButton_pressEffect=False,
verticalScroll_decButton_frameColor=VBase4(0, 0, 0, 0),)
# creat the list items
idx = 0
self.listBGEven = base.loader.loadModel("models/list_item_even")
self.listBGOdd = base.loader.loadModel("models/list_item_odd")
for key, value in self.gamepadMapping.items():
item = self.__makeListItem(key, key, value, idx)
item.reparentTo(self.lstActionMap.getCanvas())
idx += 1
# recalculate the canvas size to set scrollbars if necesary
self.lstActionMap["canvasSize"] = (
base.a2dLeft+0.05, base.a2dRight-0.05,
-(len(self.gamepadMapping.keys())*0.1), 0.09)
self.lstActionMap.setCanvasSize()
def closeDialog(self, result):
self.dlgInput.hide()
if result == DGG.DIALOG_OK:
# map the event to the given action
self.gamepadMapping[self.actionToMap] = self.newActionKey
# actualize the label in the list that shows the current
# event for the action
self.actualizeLabel["text"] = self.newActionKey
# cleanup
self.dlgInput["text"] ="Hit desired key:"
self.actionToMap = ""
self.newActionKey = ""
self.actualizeLabel = None
for bt in base.buttonThrowers:
bt.node().setButtonDownEvent("")
for bt in base.deviceButtonThrowers:
bt.node().setButtonDownEvent("")
taskMgr.remove("checkControls")
def changeMapping(self, action, label):
# set the action that we want to map a new key to
self.actionToMap = action
# set the label that needs to be actualized
self.actualizeLabel = label
# show our dialog
self.dlgInput.show()
# catch all button events
for bt in base.buttonThrowers:
bt.node().setButtonDownEvent("keyListenEvent")
for bt in base.deviceButtonThrowers:
bt.node().setButtonDownEvent("deviceListenEvent")
self.setKeyCalled = False
self.accept("keyListenEvent", self.setKey)
self.accept("deviceListenEvent", self.setDeviceKey)
# As there are no events thrown for control changes, we set up
# a task to check if the controls got moved
# This list will help us for checking which controls got moved
self.controlStates = {None:{}}
# fill it with all available controls
for device in base.devices.get_devices():
for ctrl in device.controls:
if device not in self.controlStates.keys():
self.controlStates.update({device: {ctrl.axis: ctrl.state}})
else:
self.controlStates[device].update({ctrl.axis: ctrl.state})
# start the task
taskMgr.add(self.watchControls, "checkControls")
def watchControls(self, task):
# move through all devices and all it's controls
for device in base.devices.get_devices():
for ctrl in device.controls:
# if a control got changed more than the given puffer zone
if self.controlStates[device][ctrl.axis] + 0.2 < ctrl.state or \
self.controlStates[device][ctrl.axis] - 0.2 > ctrl.state:
# set the current state in the dict
self.controlStates[device][ctrl.axis] = ctrl.state
# check which axis got moved
if ctrl.axis == InputDevice.C_left_x:
self.setKey("Left Stick X")
elif ctrl.axis == InputDevice.C_left_y:
self.setKey("Left Stick Y")
elif ctrl.axis == InputDevice.C_left_trigger:
self.setKey("Left Trigger")
elif ctrl.axis == InputDevice.C_right_x:
self.setKey("Right Stick X")
elif ctrl.axis == InputDevice.C_right_y:
self.setKey("Right Stick Y")
elif ctrl.axis == InputDevice.C_right_trigger:
self.setKey("Right Trigger")
elif ctrl.axis == InputDevice.C_x:
self.setKey("X")
elif ctrl.axis == InputDevice.C_y:
self.setKey("Y")
elif ctrl.axis == InputDevice.C_trigger:
self.setKey("Trigger")
elif ctrl.axis == InputDevice.C_throttle:
self.setKey("Throttle")
elif ctrl.axis == InputDevice.C_rudder:
self.setKey("Rudder")
elif ctrl.axis == InputDevice.C_hat_x:
self.setKey("Hat X")
elif ctrl.axis == InputDevice.C_hat_y:
self.setKey("Hat Y")
elif ctrl.axis == InputDevice.C_wheel:
self.setKey("Wheel")
elif ctrl.axis == InputDevice.C_accelerator:
self.setKey("Acclerator")
elif ctrl.axis == InputDevice.C_brake:
self.setKey("Break")
return task.cont
def setKey(self, args):
self.setKeyCalled = True
if self.dlgInput.buttonList[0].guiItem.getState() == 1:
# this occurs if the OK button was clicked. To prevent to
# always set the mouse1 event whenever the OK button was
# pressed, we instantly return from this function
return
self.dlgInput["text"] = "New event will be:\n\n" + args
self.newActionKey = args
def setDeviceKey(self, args):
if not self.setKeyCalled:
self.setKey(args)
self.setKeyCalled = False
def __makeListItem(self, itemName, action, event, index):
def dummy(): pass
if index % 2 == 0:
bg = self.listBGEven
else:
bg = self.listBGOdd
item = DirectFrame(
text=itemName,
geom=bg,
geom_scale=(base.a2dRight-0.05, 1, 0.1),
frameSize=VBase4(base.a2dLeft+0.05, base.a2dRight-0.05, -0.05, 0.05),
frameColor=VBase4(1,0,0,0),
text_align=TextNode.ALeft,
text_scale=0.05,
text_fg=VBase4(1,1,1,1),
text_pos=(base.a2dLeft + 0.3, -0.015),
text_shadow=VBase4(0, 0, 0, 0.35),
text_shadowOffset=Vec2(-0.05, -0.05),
pos=(0.05, 0, -(0.10 * index)))
item.setTransparency(True)
lbl = DirectLabel(
text=event,
text_fg=VBase4(1, 1, 1, 1),
text_scale=0.05,
text_pos=Vec2(0, -0.015),
frameColor=VBase4(0, 0, 0, 0),
)
lbl.reparentTo(item)
lbl.setTransparency(True)
buttonScale = 0.15
btn = DirectButton(
text="Change",
geom=self.buttonGeom,
scale=buttonScale,
text_scale=0.25,
text_align=TextNode.ALeft,
text_fg=VBase4(0.898, 0.839, 0.730, 1.0),
text_pos=Vec2(-0.9, -0.085),
relief=1,
pad=Vec2(0.01, 0.01),
frameColor=VBase4(0, 0, 0, 0),
frameSize=VBase4(-1.0, 1.0, -0.25, 0.25),
pos=(base.a2dRight-(0.898*buttonScale+0.3), 0, 0),
pressEffect=False,
command=self.changeMapping,
extraArgs=[action, lbl])
btn.setTransparency(True)
btn.reparentTo(item)
return item
app = App()
app.run()