class TextScrolledFrame(object): def __init__(self, parent=None, scale=0.05, limitText=1, frameSize=(0, 1.3, 0.2, 0.697), pos=(0, 0, 0.1)): self.__scale = scale self.__frameSize = frameSize self.__canvasSize = (frameSize[0], frameSize[2] - 0.01, frameSize[2], frameSize[3]) self.__limitText = limitText self.__countLine = [] self.dsf = DirectScrolledFrame( parent=parent, canvasSize=self.__canvasSize, frameSize=self.__frameSize, pos=pos, frameColor=COR_OPACITY_03_PRETO, autoHideScrollBars=1, scrollBarWidth=0.05, borderWidth=(0, 0), verticalScroll_value=1, verticalScroll_decButton_frameColor=(1, 1, 1, 0.3), verticalScroll_decButton_rolloverSound=None, verticalScroll_decButton_clickSound=None, verticalScroll_incButton_frameColor=(1, 1, 1, 0.3), verticalScroll_incButton_rolloverSound=None, verticalScroll_incButton_clickSound=None, verticalScroll_thumb_frameColor=(1, 1, 1, 0.3), verticalScroll_thumb_rolloverSound=None, verticalScroll_thumb_clickSound=None, ) self.__textoHeight = scale self.__canvasHeight = self.dsf.getHeight() self.__canvas = self.dsf.getCanvas() def __command_clear_msg(self): self.etyMsg.set("") def __reposicionaTexto(self, obj_text): height = obj_text.getScale() * obj_text.textNode.getNumRows() for text in self.__countLine: lastPos = text.textNodePath.getPos() text.setPos((lastPos[0], 0, lastPos[2] + height)) def __resizeCanvasSize(self, text): self.__textoHeight += text.getScale() * text.textNode.getNumRows() if self.__textoHeight > self.__canvasHeight: self.__canvasHeight += self.__textoHeight - self.__canvasHeight self.dsf["canvasSize"] = ( self.dsf["canvasSize"][0], self.dsf["canvasSize"][1], self.dsf["canvasSize"][2], self.dsf["canvasSize"][2] + self.__canvasHeight, ) self.dsf.setCanvasSize() def show(self, texto, cor=(1, 1, 1, 1)): dfs_pos = self.dsf.getPos() pos = (dfs_pos[0] + 0.01, 0, dfs_pos[1] + 0.16) text = Text(parent=self.__canvas, scale=self.__scale, text=texto, wordwrap=25, pos=pos, cor=cor) numText = len(self.__countLine) if numText > 0: self.__reposicionaTexto(text) self.__countLine.append(text) if numText > self.__limitText: self.__countLine[0].remove() self.__countLine.pop(0) else: self.__resizeCanvasSize(text) self.dsf["verticalScroll_value"] = 1
class DeviceConnectivityMonitor(DirectObject): def __init__(self): super().__init__() self.mgr = InputDeviceManager.get_global_ptr() self.create_device_menu() self.devices = {} for device in self.mgr.get_devices(): self.connect_device(device) self.accept("connect-device", self.connect_device) self.accept("disconnect-device", self.disconnect_device) def create_device_menu(self): self.current_panel = None self.buttons = {} self.devices_frame = DirectScrolledFrame( frameSize=VBase4( 0, base.a2dLeft*-0.75, base.a2dBottom - base.a2dTop, 0, ), frameColor=VBase4(0, 0, 0.25, 1.0), canvasSize=VBase4( 0, base.a2dLeft*-0.75, 0, 0, ), scrollBarWidth=0.08, manageScrollBars=True, autoHideScrollBars=True, pos=(base.a2dLeft, 0, base.a2dTop), parent=base.aspect2d, ) self.devices_frame.setCanvasSize() def create_menu_button(self, device): button = DirectButton( command=self.switch_to_panel, extraArgs=[device], text=device.name, text_scale=0.05, text_align=TextNode.ALeft, text_fg=VBase4(0.0, 0.0, 0.0, 1.0), text_pos=Vec2(0.01, base.a2dBottom / 10.0), relief=1, pad=Vec2(0.01, 0.01), frameColor=VBase4(0.8, 0.8, 0.8, 1.0), frameSize=VBase4( 0.0, base.a2dLeft*-0.75 - 0.081, # 0.08=Scrollbar, 0.001=inaccuracy base.a2dBottom / 5.0, 0.0, ), parent=self.devices_frame.getCanvas(), ) self.buttons[device] = button def destroy_menu_button(self, device): self.buttons[device].detach_node() del self.buttons[device] def refresh_device_menu(self): self.devices_frame['canvasSize'] = VBase4( 0, base.a2dLeft*-0.75, base.a2dBottom / 5.0 * len(self.buttons), 0, ) self.devices_frame.setCanvasSize() sorted_buttons = sorted(self.buttons.items(), key=lambda i: i[0].name) for idx, (dev, button) in enumerate(sorted_buttons): button.set_pos( 0, 0, (base.a2dBottom / 5.0) * idx, ) def switch_to_panel(self, device): if self.current_panel is not None: self.devices[self.current_panel].hide() self.current_panel = device self.devices[self.current_panel].show() def connect_device(self, device): self.devices[device] = DeviceMonitor(device) self.switch_to_panel(device) self.create_menu_button(device) self.refresh_device_menu() def disconnect_device(self, device): self.devices[device].deactivate() del self.devices[device] if self.current_panel == device: self.current_panel = None if len(self.devices) > 0: active_device = sorted( self.devices.keys(), key=lambda d: d.name, )[0] self.switch_to_panel(active_device) self.destroy_menu_button(device) self.refresh_device_menu()
class DeviceMonitor(DirectObject): def __init__(self, device): super().__init__() self.device = device self.create_panel() self.activate() self.hide() def activate(self): print("Device connected") print(" Name : {}".format(self.device.name)) print(" Type : {}".format(self.device.device_class.name)) print(" Manufacturer: {}".format(self.device.manufacturer)) print(" ID : {:04x}:{:04x}".format(self.device.vendor_id, self.device.product_id)) axis_names = [axis.axis.name for axis in self.device.axes] print(" Axes : {} ({})".format(len(self.device.axes), ', '.join(axis_names))) button_names = [button.handle.name for button in self.device.buttons] print(" Buttons : {} ({})".format(len(self.device.buttons), ', '.join(button_names))) base.attachInputDevice(self.device) self.task = base.taskMgr.add( self.update, "Monitor for {}".format(self.device.name), sort=10, ) def deactivate(self): print("\"{}\" disconnected".format(self.device.name)) base.taskMgr.remove(self.task) self.panel.detach_node() def create_panel(self): panel_width = base.a2dLeft * -0.25 + base.a2dRight scroll_bar_width = 0.08 # NOTE: -0.001 because thanks to inaccuracy the vertical bar appears... canvas_width = panel_width - scroll_bar_width - 0.001 canvas_height = base.a2dBottom - base.a2dTop self.panel = DirectScrolledFrame( frameSize=VBase4( 0, panel_width, canvas_height, 0, ), frameColor=VBase4(0.8, 0.8, 0.8, 1), canvasSize=VBase4( 0, canvas_width, canvas_height, 0, ), scrollBarWidth=scroll_bar_width, manageScrollBars=True, autoHideScrollBars=True, pos=(base.a2dLeft * 0.25, 0, base.a2dTop), parent=base.aspect2d, ) panel_canvas = self.panel.getCanvas() offset = -0.0 # Style sheets half_width_entry = dict( frameSize=VBase4( 0, canvas_width / 2, -0.1, 0, ), parent=panel_canvas, frameColor=VBase4(0.8, 0.8, 0.8, 1), ) left_aligned_small_text = dict( text_align=TextNode.ALeft, text_scale=0.05, text_fg=VBase4(0,0,0,1), text_pos=(0.05, -0.06), ) half_width_text_frame = dict( **half_width_entry, **left_aligned_small_text, ) header = dict( frameSize=VBase4( 0, canvas_width, -0.1, 0, ), parent=panel_canvas, frameColor=VBase4(0.6, 0.6, 0.6, 1), text_align=TextNode.ALeft, text_scale=0.1, text_fg=VBase4(0,0,0,1), text_pos=(0.05, -0.075), ) # Basic device data (name, device class, manufacturer, USB ID) self.device_header = DirectLabel( text="Device data", pos=(0, 0, offset), **header, ) offset -= 0.1 def add_data_entry(offset, label, text): self.name = DirectLabel( text=label, pos=(0, 0, offset), **half_width_text_frame, ) self.name = DirectLabel( text=text, pos=(canvas_width / 2, 0, offset), **half_width_text_frame, ) metadata = [ ('Name', self.device.name), ('Device class', self.device.device_class.name), ('Manufacturer', self.device.manufacturer), ('USB ID', "{:04x}:{:04x}".format( self.device.vendor_id, self.device.product_id, ), ), ] for label, text in metadata: add_data_entry(offset, label, text) offset -= 0.1 # Axes self.axis_sliders = [] if len(self.device.axes) > 0: offset -= 0.1 self.axes_header = DirectLabel( text="Axes", pos=(0, 0, offset), **header, ) offset -= 0.1 def add_axis(offset, axis_name): slider_width = canvas_width / 2 label = DirectLabel( text=axis_name, **left_aligned_small_text, pos=(0.05, 0, offset), parent=panel_canvas, ) slider = DirectSlider( value=0.0, range=(-1.0, 1.0), state=DGG.DISABLED, frameSize=VBase4( 0, slider_width, -0.1, 0, ), thumb_frameSize=VBase4( 0.0, 0.04, -0.04, 0.04), frameColor=VBase4(0.3, 0.3, 0.3, 1), pos=(canvas_width - slider_width, 0, offset), parent=panel_canvas, ) return slider for axis in self.device.axes: axis_slider = add_axis(offset, axis.axis.name) self.axis_sliders.append(axis_slider) offset -= 0.1 # Buttons self.button_buttons = [] if len(self.device.buttons) > 0: offset -= 0.1 self.buttons_header = DirectLabel( text="Buttons", pos=(0, 0, offset), **header, ) offset -= 0.1 def add_button(offset, button_name): button_width = canvas_width / 2 label = DirectLabel( text=button_name, **left_aligned_small_text, pos=(0.05, 0, offset), parent=panel_canvas, ) button = DirectFrame( frameSize=VBase4( 0, button_width, -0.1, 0, ), text="", text_align=TextNode.ACenter, text_scale=0.05, text_fg=VBase4(0,0,0,1), text_pos=(button_width / 2, -0.06), frameColor=VBase4(0.3, 0.3, 0.3, 1), pos=(canvas_width - button_width, 0, offset), parent=panel_canvas, ) return button for i in range(len(self.device.buttons)): button_name = self.device.buttons[i].handle.name button_button = add_button(offset, button_name) self.button_buttons.append(button_button) offset -= 0.1 # Vibration self.vibration = [] if self.device.has_feature(InputDevice.Feature.vibration): offset -= 0.1 self.vibration_header = DirectLabel( text="Vibration", pos=(0, 0, offset), **header, ) offset -= 0.1 def add_vibration(offset, axis_name, index): slider_width = canvas_width / 2 label = DirectLabel( text=axis_name, **left_aligned_small_text, pos=(0.05, 0, offset), parent=panel_canvas, ) slider = DirectSlider( value=0.0, range=(0.0, 1.0), command=self.update_vibration, frameSize=VBase4( 0, slider_width, -0.1, 0, ), thumb_frameSize=VBase4( 0.0, 0.04, -0.04, 0.04), frameColor=VBase4(0.3, 0.3, 0.3, 1), pos=(canvas_width - slider_width, 0, offset), parent=panel_canvas, ) return slider for index, name in enumerate(["low frequency", "high frequency"]): self.vibration.append(add_vibration(offset, name, index)) offset -= 0.1 # Resize the panel's canvas to the widgets actually in it. if -offset > -canvas_height: self.panel['canvasSize'] = VBase4( 0, canvas_width, offset, 0, ) self.panel.setCanvasSize() def show(self): # FIXME: Activate update task here, and deactivate it in hide()? self.panel.show() def hide(self): self.panel.hide() def update_vibration(self): low = self.vibration[0]['value'] high = self.vibration[1]['value'] self.device.set_vibration(low, high) def update(self, task): # FIXME: There needs to be a demo of events here, too. for idx, slider in enumerate(self.axis_sliders): slider["value"] = self.device.axes[idx].value for idx, button in enumerate(self.button_buttons): if self.device.buttons[idx].known: if self.device.buttons[idx].pressed: button['frameColor'] = VBase4(0.0, 0.8, 0.0, 1) button['text'] = "down" else: button['frameColor'] = VBase4(0.3, 0.3, 0.3, 1) button['text'] = "up" else: # State is InputDevice.S_unknown. This happens if the device # manager hasn't polled yet, and in some cases before a button # has been pressed after the program's start. button['frameColor'] = VBase4(0.8, 0.8, 0.0, 1) button['text'] = "unknown" return task.cont
class MappingGUIDemo(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) # Store our mapping, with some sensible defaults. In a real game, you # will want to load these from a configuration file. self.mapping = InputMapping() self.mapping.mapAxis("Move forward", InputDevice.Axis.left_y) self.mapping.mapAxis("Move backward", InputDevice.Axis.left_y) self.mapping.mapAxis("Move left", InputDevice.Axis.left_x) self.mapping.mapAxis("Move right", InputDevice.Axis.left_x) self.mapping.mapButton("Jump", GamepadButton.face_a()) self.mapping.mapButton("Use", GamepadButton.face_b()) self.mapping.mapButton("Break", GamepadButton.face_x()) self.mapping.mapButton("Fix", GamepadButton.face_y()) # 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")) # Change the default dialog skin. DGG.setDefaultDialogGeom("models/dialog.png") # 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") self.actionLabels = {} for action in self.mapping.actions: mapped = self.mapping.formatMapping(action) item = self.__makeListItem(action, mapped, 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.mapping.actions) * 0.1), 0.09) self.lstActionMap.setCanvasSize() def closeDialog(self, action, newInputType, newInput): """Called in callback when the dialog is closed. newInputType will be "button" or "axis", or None if the remapping was cancelled.""" self.dlgInput = None if newInputType is not None: # map the event to the given action if newInputType == "axis": self.mapping.mapAxis(action, newInput) else: self.mapping.mapButton(action, newInput) # actualize the label in the list that shows the current # event for the action self.actionLabels[action]["text"] = self.mapping.formatMapping( action) # cleanup for bt in base.buttonThrowers: bt.node().setSpecificFlag(True) bt.node().setButtonDownEvent("") for bt in base.deviceButtonThrowers: bt.node().setSpecificFlag(True) bt.node().setButtonDownEvent("") taskMgr.remove("checkControls") # Now detach all the input devices. for device in self.attachedDevices: base.detachInputDevice(device) self.attachedDevices.clear() def changeMapping(self, action): # Create the dialog window self.dlgInput = ChangeActionDialog(action, button_geom=self.buttonGeom, command=self.closeDialog) # Attach all input devices. devices = base.devices.getDevices() for device in devices: base.attachInputDevice(device) self.attachedDevices = devices # Disable regular button events on all button event throwers, and # instead broadcast a generic event. for bt in base.buttonThrowers: bt.node().setSpecificFlag(False) bt.node().setButtonDownEvent("keyListenEvent") for bt in base.deviceButtonThrowers: bt.node().setSpecificFlag(False) bt.node().setButtonDownEvent("deviceListenEvent") self.accept("keyListenEvent", self.dlgInput.buttonPressed) self.accept("deviceListenEvent", self.dlgInput.buttonPressed) # As there are no events thrown for control changes, we set up a task # to check if the controls were moved # This list will help us for checking which controls were moved self.axisStates = {None: {}} # fill it with all available controls for device in devices: for axis in device.axes: if device not in self.axisStates.keys(): self.axisStates.update({device: {axis.axis: axis.value}}) else: self.axisStates[device].update({axis.axis: axis.value}) # start the task taskMgr.add(self.watchControls, "checkControls") def watchControls(self, task): # move through all devices and all it's controls for device in self.attachedDevices: if device.device_class == InputDevice.DeviceClass.mouse: # Ignore mouse axis movement, or the user can't even navigate # to the OK/Cancel buttons! continue for axis in device.axes: # if a control got changed more than the given dead zone if self.axisStates[device][axis.axis] + DEAD_ZONE < axis.value or \ self.axisStates[device][axis.axis] - DEAD_ZONE > axis.value: # set the current state in the dict self.axisStates[device][axis.axis] = axis.value # Format the axis for being displayed. if axis.axis != InputDevice.Axis.none: #label = axis.axis.name.replace('_', ' ').title() self.dlgInput.axisMoved(axis.axis) return task.cont def __makeListItem(self, action, event, index): def dummy(): pass if index % 2 == 0: bg = self.listBGEven else: bg = self.listBGOdd item = DirectFrame(text=action, 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) self.actionLabels[action] = lbl 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]) btn.setTransparency(True) btn.reparentTo(item) return item
class TextScrolledFrame(object): def __init__(self, parent=None, scale=.05, limitText=1, frameSize=(0, 1.3, .2, .697), pos=(0, 0, .1)): self.__scale = scale self.__frameSize = frameSize self.__canvasSize = (frameSize[0], frameSize[2] - .01, frameSize[2], frameSize[3]) self.__limitText = limitText self.__countLine = [] self.dsf = DirectScrolledFrame( parent=parent, canvasSize=self.__canvasSize, frameSize=self.__frameSize, pos=pos, frameColor=COR_OPACITY_03_PRETO, autoHideScrollBars=1, scrollBarWidth=0.05, borderWidth=(0, 0), verticalScroll_value=1, verticalScroll_decButton_frameColor=(1, 1, 1, 0.3), verticalScroll_decButton_rolloverSound=None, verticalScroll_decButton_clickSound=None, verticalScroll_incButton_frameColor=(1, 1, 1, 0.3), verticalScroll_incButton_rolloverSound=None, verticalScroll_incButton_clickSound=None, verticalScroll_thumb_frameColor=(1, 1, 1, 0.3), verticalScroll_thumb_rolloverSound=None, verticalScroll_thumb_clickSound=None) self.__textoHeight = scale self.__canvasHeight = self.dsf.getHeight() self.__canvas = self.dsf.getCanvas() def __command_clear_msg(self): self.etyMsg.set("") def __reposicionaTexto(self, obj_text): height = obj_text.getScale() * obj_text.textNode.getNumRows() for text in self.__countLine: lastPos = text.textNodePath.getPos() text.setPos((lastPos[0], 0, lastPos[2] + height)) def __resizeCanvasSize(self, text): self.__textoHeight += text.getScale() * text.textNode.getNumRows() if self.__textoHeight > self.__canvasHeight: self.__canvasHeight += self.__textoHeight - self.__canvasHeight self.dsf['canvasSize'] = (self.dsf['canvasSize'][0], self.dsf['canvasSize'][1], self.dsf['canvasSize'][2], self.dsf['canvasSize'][2] + self.__canvasHeight) self.dsf.setCanvasSize() def show(self, texto, cor=(1, 1, 1, 1)): dfs_pos = self.dsf.getPos() pos = (dfs_pos[0] + .01, 0, dfs_pos[1] + 0.16) text = Text(parent=self.__canvas, scale=self.__scale, text=texto, wordwrap=25, pos=pos, cor=cor) numText = len(self.__countLine) if numText > 0: self.__reposicionaTexto(text) self.__countLine.append(text) if numText > self.__limitText: self.__countLine[0].remove() self.__countLine.pop(0) else: self.__resizeCanvasSize(text) self.dsf['verticalScroll_value'] = 1
class MappingGUIDemo(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) # Store our mapping, with some sensible defaults. In a real game, you # will want to load these from a configuration file. self.mapping = InputMapping() self.mapping.mapAxis("Move forward", InputDevice.Axis.left_y) self.mapping.mapAxis("Move backward", InputDevice.Axis.left_y) self.mapping.mapAxis("Move left", InputDevice.Axis.left_x) self.mapping.mapAxis("Move right", InputDevice.Axis.left_x) self.mapping.mapButton("Jump", GamepadButton.face_a()) self.mapping.mapButton("Use", GamepadButton.face_b()) self.mapping.mapButton("Break", GamepadButton.face_x()) self.mapping.mapButton("Fix", GamepadButton.face_y()) # 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")) # Change the default dialog skin. DGG.setDefaultDialogGeom("models/dialog.png") # 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") self.actionLabels = {} for action in self.mapping.actions: mapped = self.mapping.formatMapping(action) item = self.__makeListItem(action, mapped, 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.mapping.actions)*0.1), 0.09) self.lstActionMap.setCanvasSize() def closeDialog(self, action, newInputType, newInput): """Called in callback when the dialog is closed. newInputType will be "button" or "axis", or None if the remapping was cancelled.""" self.dlgInput = None if newInputType is not None: # map the event to the given action if newInputType == "axis": self.mapping.mapAxis(action, newInput) else: self.mapping.mapButton(action, newInput) # actualize the label in the list that shows the current # event for the action self.actionLabels[action]["text"] = self.mapping.formatMapping(action) # cleanup for bt in base.buttonThrowers: bt.node().setSpecificFlag(True) bt.node().setButtonDownEvent("") for bt in base.deviceButtonThrowers: bt.node().setSpecificFlag(True) bt.node().setButtonDownEvent("") taskMgr.remove("checkControls") # Now detach all the input devices. for device in self.attachedDevices: base.detachInputDevice(device) self.attachedDevices.clear() def changeMapping(self, action): # Create the dialog window self.dlgInput = ChangeActionDialog(action, button_geom=self.buttonGeom, command=self.closeDialog) # Attach all input devices. devices = base.devices.getDevices() for device in devices: base.attachInputDevice(device) self.attachedDevices = devices # Disable regular button events on all button event throwers, and # instead broadcast a generic event. for bt in base.buttonThrowers: bt.node().setSpecificFlag(False) bt.node().setButtonDownEvent("keyListenEvent") for bt in base.deviceButtonThrowers: bt.node().setSpecificFlag(False) bt.node().setButtonDownEvent("deviceListenEvent") self.accept("keyListenEvent", self.dlgInput.buttonPressed) self.accept("deviceListenEvent", self.dlgInput.buttonPressed) # As there are no events thrown for control changes, we set up a task # to check if the controls were moved # This list will help us for checking which controls were moved self.axisStates = {None: {}} # fill it with all available controls for device in devices: for axis in device.axes: if device not in self.axisStates.keys(): self.axisStates.update({device: {axis.axis: axis.value}}) else: self.axisStates[device].update({axis.axis: axis.value}) # start the task taskMgr.add(self.watchControls, "checkControls") def watchControls(self, task): # move through all devices and all it's controls for device in self.attachedDevices: if device.device_class == InputDevice.DeviceClass.mouse: # Ignore mouse axis movement, or the user can't even navigate # to the OK/Cancel buttons! continue for axis in device.axes: # if a control got changed more than the given dead zone if self.axisStates[device][axis.axis] + DEAD_ZONE < axis.value or \ self.axisStates[device][axis.axis] - DEAD_ZONE > axis.value: # set the current state in the dict self.axisStates[device][axis.axis] = axis.value # Format the axis for being displayed. if axis.axis != InputDevice.Axis.none: #label = axis.axis.name.replace('_', ' ').title() self.dlgInput.axisMoved(axis.axis) return task.cont def __makeListItem(self, action, event, index): def dummy(): pass if index % 2 == 0: bg = self.listBGEven else: bg = self.listBGOdd item = DirectFrame( text=action, 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) self.actionLabels[action] = lbl 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]) btn.setTransparency(True) btn.reparentTo(item) return item