Skip to content

Commit 399de2f

Browse files
committed
Update event polling documentation.
Add examples for Python 3.10 match statement.
1 parent 125faed commit 399de2f

3 files changed

Lines changed: 68 additions & 23 deletions

File tree

.vscode/settings.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@
128128
"imageio",
129129
"INCOL",
130130
"INROW",
131+
"isinstance",
131132
"itleref",
132133
"ivar",
133134
"jice",

docs/tcod/event.rst

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,21 @@ tcod.event - SDL2 Event Handling
44
.. automodule:: tcod.event
55
:members:
66
:member-order: bysource
7-
:exclude-members: KeySym, Scancode, Modifier
7+
:exclude-members:
8+
KeySym, Scancode, Modifier, get, wait
89

910

11+
Getting events
12+
--------------
13+
14+
The primary way to capture events is with the :any:`tcod.event.get` and :any:`tcod.event.wait` functions.
15+
These functions return events in a loop until the internal event queue is empty.
16+
Use :func:`isinstance`, :any:`tcod.event.EventDispatch`, or `match statements <https://docs.python.org/3/tutorial/controlflow.html#match-statements>`_
17+
(introduced in Python 3.10) to determine which event was returned.
18+
19+
.. autofunction:: tcod.event.get
20+
.. autofunction:: tcod.event.wait
21+
1022
Keyboard Enums
1123
--------------
1224

tcod/event.py

Lines changed: 54 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,17 @@ def _pixel_to_tile(x: float, y: float) -> Optional[Tuple[float, float]]:
8080
return xy[0], xy[1]
8181

8282

83-
Point = NamedTuple("Point", [("x", int), ("y", int)])
83+
class Point(NamedTuple):
84+
"""A 2D position used for events with mouse coordinates.
85+
86+
.. seealso::
87+
:any:`MouseMotion` :any:`MouseButtonDown` :any:`MouseButtonUp`
88+
"""
89+
90+
x: int
91+
"""A pixel or tile coordinate starting with zero as the left-most position."""
92+
y: int
93+
"""A pixel or tile coordinate starting with zero as the top-most position."""
8494

8595

8696
def _verify_tile_coordinates(xy: Optional[Point]) -> Point:
@@ -774,19 +784,41 @@ def get() -> Iterator[Any]:
774784
775785
Example::
776786
787+
context: tcod.context.Context # Context object initialized earlier.
777788
for event in tcod.event.get():
778-
if event.type == "QUIT":
789+
context.convert_event(event) # Add tile coordinates to mouse events.
790+
if isinstance(event, tcod.event.Quit):
779791
print(event)
780792
raise SystemExit()
781-
elif event.type == "KEYDOWN":
782-
print(event)
783-
elif event.type == "MOUSEBUTTONDOWN":
784-
print(event)
785-
elif event.type == "MOUSEMOTION":
786-
print(event)
793+
elif isinstance(event, tcod.event.KeyDown):
794+
print(event) # Prints the Scancode and KeySym enums for this event.
795+
elif isinstance(event, tcod.event.MouseButtonDown):
796+
print(event) # Prints the mouse button constant names for this event.
797+
elif isinstance(event, tcod.event.MouseMotion):
798+
print(event) # Prints the mouse button mask bits in a readable format.
787799
else:
788-
print(event)
800+
print(event) # Print any unhandled events.
789801
# For loop exits after all current events are processed.
802+
803+
Python 3.10 introduced `match statements <https://docs.python.org/3/tutorial/controlflow.html#match-statements>`_
804+
which can be used to dispatch events more gracefully:
805+
806+
Example::
807+
808+
context: tcod.context.Context # Context object initialized earlier.
809+
for event in tcod.event.get():
810+
context.convert_event(event) # Add tile coordinates to mouse events.
811+
match event:
812+
case tcod.event.Quit():
813+
raise SystemExit()
814+
case tcod.event.KeyDown(sym, scancode, mod, repeat):
815+
print(f"KeyDown: {sym=}, {scancode=}, {mod=}, {repeat=}")
816+
case tcod.event.MouseButtonDown(button, pixel, tile):
817+
print(f"MouseButtonDown: {button=}, {pixel=}, {tile=}")
818+
case tcod.event.MouseMotion(pixel, pixel_motion, tile, tile_motion):
819+
print(f"MouseMotion: {pixel=}, {pixel_motion=}, {tile=}, {tile_motion=}")
820+
case tcod.event.Event() as event:
821+
print(event) # Show any unhandled events.
790822
"""
791823
sdl_event = ffi.new("SDL_Event*")
792824
while lib.SDL_PollEvent(sdl_event):
@@ -804,21 +836,21 @@ def wait(timeout: Optional[float] = None) -> Iterator[Any]:
804836
805837
Returns the same iterator as a call to :any:`tcod.event.get`.
806838
839+
This function is useful for simple games with little to no animations.
840+
The following example sleeps whenever no events are queued:
841+
807842
Example::
808843
809-
for event in tcod.event.wait():
810-
if event.type == "QUIT":
811-
print(event)
812-
raise SystemExit()
813-
elif event.type == "KEYDOWN":
814-
print(event)
815-
elif event.type == "MOUSEBUTTONDOWN":
816-
print(event)
817-
elif event.type == "MOUSEMOTION":
818-
print(event)
819-
else:
820-
print(event)
821-
# For loop exits on timeout or after at least one event is processed.
844+
context: tcod.context.Context # Context object initialized earlier.
845+
while True: # Main game-loop.
846+
console: tcod.Console # Console used for rendering.
847+
... # Render the frame to `console` and then:
848+
context.present(console) # Show the console to the display.
849+
# The ordering to draw first before waiting for events is important.
850+
for event in tcod.event.wait(): # Sleeps until the next events exist.
851+
... # All events are handled at once before the next frame.
852+
853+
See :any:`tcod.event.get` examples for how different events are handled.
822854
"""
823855
if timeout is not None:
824856
lib.SDL_WaitEventTimeout(ffi.NULL, int(timeout * 1000))

0 commit comments

Comments
 (0)