mirror of
https://github.com/panda3d/panda3d.git
synced 2025-09-29 08:15:18 -04:00
parent
585ba2d647
commit
f8520773e9
456
samples/gamepad/device_tester.py
Normal file
456
samples/gamepad/device_tester.py
Normal file
@ -0,0 +1,456 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import sys
|
||||
|
||||
from direct.showbase.ShowBase import ShowBase
|
||||
from direct.showbase.DirectObject import DirectObject
|
||||
from panda3d.core import InputDeviceManager, InputDevice
|
||||
from panda3d.core import VBase4, Vec2
|
||||
from panda3d.core import TextNode
|
||||
from direct.gui.DirectGui import (
|
||||
DGG,
|
||||
DirectFrame,
|
||||
DirectButton,
|
||||
DirectLabel,
|
||||
DirectScrolledFrame,
|
||||
DirectSlider,
|
||||
)
|
||||
|
||||
|
||||
class Main(ShowBase):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
base.disableMouse()
|
||||
self.accept("escape", sys.exit)
|
||||
self.device_connectivity_monitor = DeviceConnectivityMonitor()
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main = Main()
|
||||
main.run()
|
Loading…
x
Reference in New Issue
Block a user