#python #bluetooth #pygame #serial-port #socat
#python #Windows #pygame #pyglet #psychopy
Вопрос:
У меня есть USB-контроллер, с которого я пытаюсь получить входные данные, игровая панель Microsoft® SideWinder® Plug amp; Play. У меня возникают трудности, пытаясь понять, как правильно получать его входные данные. К сожалению, я не могу использовать pygame, поскольку для получения входных данных требуется окно, но я должен сгенерировать окно pyglet (через PsychoPy) для запуска моей программы. С pygame он может подключаться и отображать состояние кнопок, но он не может получать входные данные без создания окна. Я пытался искать другие библиотеки, но все, с чем я столкнулся, это входные данные, которые несовместимы с моим контроллером (не обнаруживает устройство после установки). Сам контроллер работает так, как я тестировал его с помощью онлайн-тестера геймпада. API джойстика PsychoPy в настоящее время сломан и не работает, так что там тоже не повезло.
Я действительно надеялся, что у кого-нибудь есть совет о том, как получать входные данные с моего контроллера / геймпада в мою программу?
Комментарии:
1. Вы застряли в использовании PyGlet? Я нашел это: entitycrisis.blogspot.com/2008/02/pyglet-joystick.html
2. К сожалению, я использую компьютер с Windows, и, похоже, он работает только для Linux, спасибо!
3. «он не может получать входные данные без создания окна» — конечно. В Windows вам понадобится цикл сообщений окна для получения входных данных.
Ответ №1:
Для Windows вы можете использовать WINMM.dll напрямую.
Используйте библиотеку ctypes для загрузки dll (см. раздел Загрузка общих библиотек). Используется ctypes.WINFUNCTYPE
для создания прототипов функции:
import ctypes
winmmdll = ctypes.WinDLL('winmm.dll')
# [joyGetNumDevs](https://learn.microsoft.com/en-us/windows/win32/api/joystickapi/nf-joystickapi-joygetnumdevs)
"""
UINT joyGetNumDevs();
"""
joyGetNumDevs_proto = ctypes.WINFUNCTYPE(ctypes.c_uint)
joyGetNumDevs_func = joyGetNumDevs_proto(("joyGetNumDevs", winmmdll))
# [joyGetDevCaps](https://learn.microsoft.com/en-us/windows/win32/api/joystickapi/nf-joystickapi-joygetdevcaps)
"""
MMRESULT joyGetDevCaps(UINT uJoyID, LPJOYCAPS pjc, UINT cbjc);
32 bit: joyGetDevCapsA
64 bit: joyGetDevCapsW
sizeof(JOYCAPS): 728
"""
joyGetDevCaps_proto = ctypes.WINFUNCTYPE(ctypes.c_uint, ctypes.c_uint, ctypes.c_void_p, ctypes.c_uint)
joyGetDevCaps_param = (1, "uJoyID", 0), (1, "pjc", None), (1, "cbjc", 0)
joyGetDevCaps_func = joyGetDevCaps_proto(("joyGetDevCapsW", winmmdll), joyGetDevCaps_param)
# [joyGetPosEx](https://learn.microsoft.com/en-us/windows/win32/api/joystickapi/nf-joystickapi-joygetposex)
"""
MMRESULT joyGetPosEx(UINT uJoyID, LPJOYINFOEX pji);
sizeof(JOYINFOEX): 52
"""
joyGetPosEx_proto = ctypes.WINFUNCTYPE(ctypes.c_uint, ctypes.c_uint, ctypes.c_void_p)
joyGetPosEx_param = (1, "uJoyID", 0), (1, "pji", None)
joyGetPosEx_func = joyGetPosEx_proto(("joyGetPosEx", winmmdll), joyGetPosEx_param)
Создайте функцию python joyGetNumDevs
joyGetDevCaps
и joyGetPosEx
делегируйте ее в DLL. И создайте классы для типов JOYCAPS
соответственно JOYINFOEX
:
# joystickapi - joyGetNumDevs
def joyGetNumDevs():
try:
num = joyGetNumDevs_func()
except:
num = 0
return num
# joystickapi - joyGetDevCaps
def joyGetDevCaps(uJoyID):
try:
buffer = (ctypes.c_ubyte * JOYCAPS.SIZE_W)()
p1 = ctypes.c_uint(uJoyID)
p2 = ctypes.cast(buffer, ctypes.c_void_p)
p3 = ctypes.c_uint(JOYCAPS.SIZE_W)
ret_val = joyGetDevCaps_func(p1, p2, p3)
ret = (False, None) if ret_val != JOYERR_NOERROR else (True, JOYCAPS(buffer))
except:
ret = False, None
return ret
# joystickapi - joyGetPosEx
def joyGetPosEx(uJoyID):
try:
buffer = (ctypes.c_uint32 * (JOYINFOEX.SIZE // 4))()
buffer[0] = JOYINFOEX.SIZE
buffer[1] = JOY_RETURNALL
p1 = ctypes.c_uint(uJoyID)
p2 = ctypes.cast(buffer, ctypes.c_void_p)
ret_val = joyGetPosEx_func(p1, p2)
ret = (False, None) if ret_val != JOYERR_NOERROR else (True, JOYINFOEX(buffer))
except:
ret = False, None
return ret
JOYERR_NOERROR = 0
JOY_RETURNX = 0x00000001
JOY_RETURNY = 0x00000002
JOY_RETURNZ = 0x00000004
JOY_RETURNR = 0x00000008
JOY_RETURNU = 0x00000010
JOY_RETURNV = 0x00000020
JOY_RETURNPOV = 0x00000040
JOY_RETURNBUTTONS = 0x00000080
JOY_RETURNRAWDATA = 0x00000100
JOY_RETURNPOVCTS = 0x00000200
JOY_RETURNCENTERED = 0x00000400
JOY_USEDEADZONE = 0x00000800
JOY_RETURNALL = (JOY_RETURNX | JOY_RETURNY | JOY_RETURNZ |
JOY_RETURNR | JOY_RETURNU | JOY_RETURNV |
JOY_RETURNPOV | JOY_RETURNBUTTONS)
# joystickapi - JOYCAPS
class JOYCAPS:
SIZE_W = 728
OFFSET_V = 4 32*2
def __init__(self, buffer):
ushort_array = (ctypes.c_uint16 * 2).from_buffer(buffer)
self.wMid, self.wPid = ushort_array
wchar_array = (ctypes.c_wchar * 32).from_buffer(buffer, 4)
self.szPname = ctypes.cast(wchar_array, ctypes.c_wchar_p).value
uint_array = (ctypes.c_uint32 * 19).from_buffer(buffer, JOYCAPS.OFFSET_V)
self.wXmin, self.wXmax, self.wYmin, self.wYmax, self.wZmin, self.wZmax,
self.wNumButtons, self.wPeriodMin, self.wPeriodMax,
self.wRmin, self.wRmax, self.wUmin, self.wUmax, self.wVmin, self.wVmax,
self.wCaps, self.wMaxAxes, self.wNumAxes, self.wMaxButtons = uint_array
# joystickapi - JOYINFOEX
class JOYINFOEX:
SIZE = 52
def __init__(self, buffer):
uint_array = (ctypes.c_uint32 * (JOYINFOEX.SIZE // 4)).from_buffer(buffer)
self.dwSize, self.dwFlags,
self.dwXpos, self.dwYpos, self.dwZpos, self.dwRpos, self.dwUpos, self.dwVpos,
self.dwButtons, self.dwButtonNumber, self.dwPOV, self.dwReserved1, self.dwReserved2 = uint_array
Смотрите простой пример для тестирования API:
import joystickapi
import msvcrt
import time
print("start")
num = joystickapi.joyGetNumDevs()
ret, caps, startinfo = False, None, None
for id in range(num):
ret, caps = joystickapi.joyGetDevCaps(id)
if ret:
print("gamepad detected: " caps.szPname)
ret, startinfo = joystickapi.joyGetPosEx(id)
break
else:
print("no gamepad detected")
run = ret
while run:
time.sleep(0.1)
if msvcrt.kbhit() and msvcrt.getch() == chr(27).encode(): # detect ESC
run = False
ret, info = joystickapi.joyGetPosEx(id)
if ret:
btns = [(1 << i) amp; info.dwButtons != 0 for i in range(caps.wNumButtons)]
axisXYZ = [info.dwXpos-startinfo.dwXpos, info.dwYpos-startinfo.dwYpos, info.dwZpos-startinfo.dwZpos]
axisRUV = [info.dwRpos-startinfo.dwRpos, info.dwUpos-startinfo.dwUpos, info.dwVpos-startinfo.dwVpos]
if info.dwButtons:
print("buttons: ", btns)
if any([abs(v) > 10 for v in axisXYZ]):
print("axis:", axisXYZ)
if any([abs(v) > 10 for v in axisRUV]):
print("roation axis:", axisRUV)
print("end")
Привязка api и пример предоставлены в репозитории GitHub python_windows_joystickapi
Комментарии:
1. Примечание:
ctypes
у модуля обычно уже есть подписи, поэтому вы можете вызыватьctypes.windll.winmm.joyGetDevCapsW
etc. как обычная функция, без необходимости объявлять сигнатуру функции. Прочитайте документацию модуля для получения более подробной информации. Кроме того, коды ошибок можно найти по адресу autohotkey.com/board/topic/17212-midi-output-from-ahk или hbtapi.com/help/Units/mmsystem.pas/Constants /…