Skip to content

Commit 5ac99e4

Browse files
texx00Zuzu-Typ
authored andcommitted
Added a class to manage asynchronous event handling (#2)
* First setup for thread and dynamic event handling * Events callbacks * Adapted the interface test to use the thread class * First version of filters * Moved filter to handler object and set up for multiple handlers * Updated documentation * Updated threading implementation Also updated documentation and moved test to external file
1 parent 9117432 commit 5ac99e4

File tree

8 files changed

+932
-256
lines changed

8 files changed

+932
-256
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ XInput_Python.egg-info/
44
runreadmelang.bat
55
*.pyc
66
*.bat
7+
.vscode

README.md

Lines changed: 86 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ It can be inmported like this:
1515

1616
### Using XInput\-Python
1717
XInput\-Python provides a few functions:
18-
`get_connected() -> (bool, bool, bool, bool)` Query which controllers are connected \(note: don't query each frame\)
18+
`get_connected() -> (bool, bool, bool, bool)` Query which controllers are connected (note: don't query each frame)
1919

2020
`get_state(user_index) -> State` Gets the State of the controller `user_index`
2121

@@ -32,11 +32,11 @@ XInput\-Python provides a few functions:
3232
`set_deadzone(deadzone, value) -> None` Sets the deadzone values for left/right thumb stick and triggers\.
3333

3434
The following deadzones exist:
35-
`XInput.DEADZONE_LEFT_THUMB` \- \(range 0 to 32767\) Left thumb stick deadzone \(default is 7849\)
35+
`XInput.DEADZONE_LEFT_THUMB` \- (range 0 to 32767) Left thumb stick deadzone (default is 7849)
3636

37-
`XInput.DEADZONE_RIGHT_THUMB` \- \(range 0 to 32767\) Right thumb stick deadzone \(default is 8689\)
37+
`XInput.DEADZONE_RIGHT_THUMB` \- (range 0 to 32767) Right thumb stick deadzone (default is 8689)
3838

39-
`XInput.DEADZONE_TRIGGER` \- \(range 0 to 255\) Trigger deadzone \(default is 30\)
39+
`XInput.DEADZONE_TRIGGER` \- (range 0 to 255) Trigger deadzone (default is 30)
4040

4141
#### Using Events
4242
You can also use the Event\-system:
@@ -47,11 +47,11 @@ You can also use the Event\-system:
4747
`get_events` will return a generator that yields instances of the `Event` class\.
4848

4949
The `Event` class always has the following members:
50-
`Event.user_index` \(range 0 to 3\) \- the id of the controller that issued this event
50+
`Event.user_index` (range 0 to 3) \- the id of the controller that issued this event
5151
`Event.type` \- which type of event was issued
5252

5353
The following events exist:
54-
`XInput.EVENT_CONNECTED == 1` \- a controller with this `user_index` was connected \(this event will even occur if the controller was connected before the script was started\)
54+
`XInput.EVENT_CONNECTED == 1` \- a controller with this `user_index` was connected (this event will even occur if the controller was connected before the script was started)
5555

5656
`XInput.EVENT_DISCONNECTED == 2` \- a controller with this `user_index` was disconnected
5757

@@ -89,16 +89,88 @@ The following buttons exist:
8989

9090
**Trigger Events**
9191
All trigger related Events have the following additional members:
92-
`Event.trigger` \(either `XInput.LEFT == 0` or `XInput.RIGHT == 1`\) \- which trigger was moved
93-
`Event.value` \(range 0\.0 to 1\.0\) \- by how much the trigger is currently pressed
92+
`Event.trigger` (either `XInput.LEFT == 0` or `XInput.RIGHT == 1`) \- which trigger was moved
93+
`Event.value` (range 0\.0 to 1\.0) \- by how much the trigger is currently pressed
9494

9595
**Stick Events**
9696
All thumb stick related Events have the following additional members:
97-
`Event.stick` \(either `XInput.LEFT == 0` or `XInput.RIGHT == 1`\) \- which stick was moved
98-
`Event.x` \(range \-1\.0 to 1\.0\) \- the position of the stick on the X axis
99-
`Event.y` \(range \-1\.0 to 1\.0\) \- the position of the stick on the Y axis
100-
`Event.value` \(range 0\.0 to 1\.0\) \- the distance of the stick from it's center position
101-
`Event.dir` \(tuple of X and Y\) \- the direction the stick is currently pointing
97+
`Event.stick` (either `XInput.LEFT == 0` or `XInput.RIGHT == 1`) \- which stick was moved
98+
`Event.x` (range \-1\.0 to 1\.0) \- the position of the stick on the X axis
99+
`Event.y` (range \-1\.0 to 1\.0) \- the position of the stick on the Y axis
100+
`Event.value` (range 0\.0 to 1\.0) \- the distance of the stick from it's center position
101+
`Event.dir` (tuple of X and Y) \- the direction the stick is currently pointing
102+
103+
### Callback events and threading
104+
With the `GamepadThread` class it is possible to handle asynchronous events\.
105+
To use this feature, extend the `EventHandler` to create one or multiple handlers and add them to the thread\.
106+
The library will automatically check the status of the gamepad and use the appropriate callback for the triggering event\.
107+
It is also possible to filter the inputs for every single handler\.
108+
In case of multiple handlers it is possible to use a list of handlers as argument, as well as the `add_handler()` method and the `remove_handler()` method to remove them\.
109+
Filters can be applied to select events of only certain buttons, trigger or stick\. Also a "pressed\-only" and "released\-only" filter is available for buttons\.
110+
The available filters are:
111+
112+
113+
BUTTON_DPAD_UP
114+
BUTTON_DPAD_DOWN
115+
BUTTON_DPAD_LEFT
116+
BUTTON_DPAD_RIGHT
117+
BUTTON_START
118+
BUTTON_BACK
119+
BUTTON_LEFT_THUMB
120+
BUTTON_RIGHT_THUMB
121+
BUTTON_LEFT_SHOULDER
122+
BUTTON_RIGHT_SHOULDER
123+
BUTTON_A
124+
BUTTON_B
125+
BUTTON_X
126+
BUTTON_Y
127+
128+
STICK_LEFT
129+
STICK_RIGHT
130+
TRIGGER_LEFT
131+
TRIGGER_RIGHT
132+
133+
FILTER_PRESSED_ONLY
134+
FILTER_RELEASED_ONLY
135+
136+
137+
138+
The filters can be combined by adding them together:
139+
140+
141+
filter1 = STICK_LEFT + STICK_RIGHT + BUTTON_DPAD_DOWN + BUTTON_DPAD_UP
142+
filter2 = BUTTON_Y + BUTTON_X + FILTER_PRESSED_ONLY
143+
144+
145+
The filter can be applied using add\_filter:
146+
147+
148+
handler.add_filter(filter)
149+
150+
151+
**Example**
152+
153+
class MyHandler(EventHandler):
154+
def process_button_event(self, event):
155+
# put here the code to parse every event related only to the buttons
156+
157+
def process_trigger_event(self, event):
158+
# event reserved for the two triggers
159+
160+
def process_stick_event(self, event):
161+
# event reserved for the two sticks
162+
163+
def process_connection_event(self, event):
164+
# event related to the gamepad status
165+
166+
filter = STICK_LEFT + STICK_RIGHT
167+
my_handler = MyHandler()
168+
my_handler.add_filter(filter)
169+
my_gamepad_thread = GamepadThread(my_handler)
170+
171+
172+
The thread will start automatically upon creation\. It is possible to stop and start it again if necessary with the two methods `start()` and `stop()`
102173

103174
### Demo
104-
Run `XInput.py` as main \(`python XInput.py`\) to see a visual representation of the controller input\.
175+
Run `XInputTest.py` to see a visual representation of the controller input\.
176+
Run `XInputThreadTest.py` to test the visual representation using the asynchronous callbacks\.

README.rml

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,5 +92,69 @@ All thumb stick related Events have the following additional members:
9292
[code]Event.value[/code] (range 0.0 to 1.0) - the distance of the stick from it's center position
9393
[code]Event.dir[/code] (tuple of X and Y) - the direction the stick is currently pointing
9494

95+
[s2]Callback events and threading[/]
96+
With the [code]GamepadThread[/code] class it is possible to handle asynchronous events.
97+
To use this feature, extend the [code]EventHandler[/code] to create one or multiple handlers and add them to the thread.
98+
The library will automatically check the status of the gamepad and use the appropriate callback for the triggering event.
99+
It is also possible to filter the inputs for every single handler.
100+
In case of multiple handlers it is possible to use a list of handlers as argument, as well as the [code]add_handler()[/code] method and the [code]remove_handler()[/code] method to remove them.
101+
Filters can be applied to select events of only certain buttons, trigger or stick. Also a "pressed-only" and "released-only" filter is available for buttons.
102+
The available filters are:
103+
[code]
104+
BUTTON_DPAD_UP
105+
BUTTON_DPAD_DOWN
106+
BUTTON_DPAD_LEFT
107+
BUTTON_DPAD_RIGHT
108+
BUTTON_START
109+
BUTTON_BACK
110+
BUTTON_LEFT_THUMB
111+
BUTTON_RIGHT_THUMB
112+
BUTTON_LEFT_SHOULDER
113+
BUTTON_RIGHT_SHOULDER
114+
BUTTON_A
115+
BUTTON_B
116+
BUTTON_X
117+
BUTTON_Y
118+
119+
STICK_LEFT
120+
STICK_RIGHT
121+
TRIGGER_LEFT
122+
TRIGGER_RIGHT
123+
124+
FILTER_PRESSED_ONLY
125+
FILTER_RELEASED_ONLY
126+
[/code]
127+
128+
The filters can be combined by adding them together:
129+
130+
[code]filter1 = STICK_LEFT + STICK_RIGHT + BUTTON_DPAD_DOWN + BUTTON_DPAD_UP
131+
filter2 = BUTTON_Y + BUTTON_X + FILTER_PRESSED_ONLY[/code]
132+
133+
The filter can be applied using add_filter:
134+
135+
[code]handler.add_filter(filter)[/code]
136+
137+
[b]Example[/]
138+
[code]class MyHandler(EventHandler):
139+
def process_button_event(self, event):
140+
# put here the code to parse every event related only to the buttons
141+
142+
def process_trigger_event(self, event):
143+
# event reserved for the two triggers
144+
145+
def process_stick_event(self, event):
146+
# event reserved for the two sticks
147+
148+
def process_connection_event(self, event):
149+
# event related to the gamepad status
150+
151+
filter = STICK_LEFT + STICK_RIGHT
152+
my_handler = MyHandler()
153+
my_handler.add_filter(filter)
154+
my_gamepad_thread = GamepadThread(my_handler)[/code]
155+
156+
The thread will start automatically upon creation. It is possible to stop and start it again if necessary with the two methods [code]start()[/code] and [code]stop()[/code]
157+
95158
[s2]Demo[/]
96-
Run [code]XInput.py[/code] as main ([code]python XInput.py[/code]) to see a visual representation of the controller input.
159+
Run [code]XInputTest.py[/code] to see a visual representation of the controller input.
160+
Run [code]XInputThreadTest.py[/code] to test the visual representation using the asynchronous callbacks.

README.rst

Lines changed: 91 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,96 @@ Using Events
131131
| :code:`Event.dir` \(tuple of X and Y\) \- the direction the stick is currently pointing
132132
|
133133
134+
Callback events and threading
135+
-----------------------------
136+
| With the :code:`GamepadThread` class it is possible to handle asynchronous events\.
137+
| To use this feature\, extend the :code:`EventHandler` to create one or multiple handlers and add them to the thread\.
138+
| The library will automatically check the status of the gamepad and use the appropriate callback for the triggering event\.
139+
| It is also possible to filter the inputs for every single handler\.
140+
| In case of multiple handlers it is possible to use a list of handlers as argument\, as well as the :code:`add_handler()` method and the :code:`remove_handler()` method to remove them\.
141+
| Filters can be applied to select events of only certain buttons\, trigger or stick\. Also a \"pressed\-only\" and \"released\-only\" filter is available for buttons\.
142+
| The available filters are\:
143+
144+
145+
::
146+
147+
148+
BUTTON_DPAD_UP
149+
BUTTON_DPAD_DOWN
150+
BUTTON_DPAD_LEFT
151+
BUTTON_DPAD_RIGHT
152+
BUTTON_START
153+
BUTTON_BACK
154+
BUTTON_LEFT_THUMB
155+
BUTTON_RIGHT_THUMB
156+
BUTTON_LEFT_SHOULDER
157+
BUTTON_RIGHT_SHOULDER
158+
BUTTON_A
159+
BUTTON_B
160+
BUTTON_X
161+
BUTTON_Y
162+
163+
STICK_LEFT
164+
STICK_RIGHT
165+
TRIGGER_LEFT
166+
TRIGGER_RIGHT
167+
168+
FILTER_PRESSED_ONLY
169+
FILTER_RELEASED_ONLY
170+
171+
172+
173+
|
174+
| The filters can be combined by adding them together\:
175+
|
176+
177+
178+
::
179+
180+
filter1 = STICK_LEFT + STICK_RIGHT + BUTTON_DPAD_DOWN + BUTTON_DPAD_UP
181+
filter2 = BUTTON_Y + BUTTON_X + FILTER_PRESSED_ONLY
182+
183+
184+
|
185+
| The filter can be applied using add\_filter\:
186+
|
187+
188+
189+
::
190+
191+
handler.add_filter(filter)
192+
193+
194+
|
195+
| **Example**
196+
197+
198+
::
199+
200+
class MyHandler(EventHandler):
201+
def process_button_event(self, event):
202+
# put here the code to parse every event related only to the buttons
203+
204+
def process_trigger_event(self, event):
205+
# event reserved for the two triggers
206+
207+
def process_stick_event(self, event):
208+
# event reserved for the two sticks
209+
210+
def process_connection_event(self, event):
211+
# event related to the gamepad status
212+
213+
filter = STICK_LEFT + STICK_RIGHT
214+
my_handler = MyHandler()
215+
my_handler.add_filter(filter)
216+
my_gamepad_thread = GamepadThread(my_handler)
217+
218+
219+
|
220+
| The thread will start automatically upon creation\. It is possible to stop and start it again if necessary with the two methods :code:`start()` and :code:`stop()`
221+
|
222+
134223
Demo
135224
----
136-
| Run :code:`XInput.py` as main \(:code:`python XInput.py`\) to see a visual representation of the controller input\.
225+
| Run :code:`XInputTest.py` to see a visual representation of the controller input\.
226+
| Run :code:`XInputThreadTest.py` to test the visual representation using the asynchronous callbacks\.

0 commit comments

Comments
 (0)