Author: John McCardle <mccardle.john@gmail.com>
Co-Authored-By: Claude <noreply@anthropic.com>
commit dc47f2474c7b2642d368f9772894aed857527807
the UIEntity rant
commit 673ca8e1b089ea670257fc04ae1a676ed95a40ed
I forget when these tests were written, but I want them in the squash merge
commit 70c71565c684fa96e222179271ecb13a156d80ad
Fix UI object segfault by switching from managed to manual weakref management
The UI types (Frame, Caption, Sprite, Grid, Entity) were using
Py_TPFLAGS_MANAGED_WEAKREF while also trying to manually create weakrefs
for the PythonObjectCache. This is fundamentally incompatible - when
Python manages weakrefs internally, PyWeakref_NewRef() cannot access the
weakref list properly, causing segfaults.
Changed all UI types to use manual weakref management (like PyTimer):
- Restored weakreflist field in all UI type structures
- Removed Py_TPFLAGS_MANAGED_WEAKREF from all UI type flags
- Added tp_weaklistoffset for all UI types in module initialization
- Initialize weakreflist=NULL in tp_new and init methods
- Call PyObject_ClearWeakRefs() in dealloc functions
This allows the PythonObjectCache to continue working correctly,
maintaining Python object identity for C++ objects across the boundary.
Fixes segfault when creating UI objects (e.g., Caption, Grid) that was
preventing tutorial scripts from running.
This is the bulk of the required behavior for Issue #126.
that issure isn't ready for closure yet; several other sub-issues left.
closes #110
mention issue #109 - resolves some __init__ related nuisances
commit 3dce3ec539ae99e32d869007bf3f49d03e4e2f89
Refactor timer system for cleaner architecture and enhanced functionality
Major improvements to the timer system:
- Unified all timer logic in the Timer class (C++)
- Removed PyTimerCallable subclass, now using PyCallable directly
- Timer objects are now passed to callbacks as first argument
- Added 'once' parameter for one-shot timers that auto-stop
- Implemented proper PythonObjectCache integration with weakref support
API enhancements:
- New callback signature: callback(timer, runtime) instead of just (runtime)
- Timer objects expose: name, interval, remaining, paused, active, once properties
- Methods: pause(), resume(), cancel(), restart()
- Comprehensive documentation with examples
- Enhanced repr showing timer state (active/paused/once/remaining time)
This cleanup follows the UIEntity/PyUIEntity pattern and makes the timer
system more Pythonic while maintaining backward compatibility through
the legacy setTimer/delTimer API.
closes #121
commit 145834cfc31b8dabc4cb3591b9cb4ed99fc8b964
Implement Python object cache to preserve derived types in collections
Add a global cache system that maintains weak references to Python objects,
ensuring that derived Python classes maintain their identity when stored in
and retrieved from C++ collections.
Key changes:
- Add PythonObjectCache singleton with serial number system
- Each cacheable object (UIDrawable, UIEntity, Timer, Animation) gets unique ID
- Cache stores weak references to prevent circular reference memory leaks
- Update all UI type definitions to support weak references (Py_TPFLAGS_MANAGED_WEAKREF)
- Enable subclassing for all UI types (Py_TPFLAGS_BASETYPE)
- Collections check cache before creating new Python wrappers
- Register objects in cache during __init__ methods
- Clean up cache entries in C++ destructors
This ensures that Python code like:
```python
class MyFrame(mcrfpy.Frame):
def __init__(self):
super().__init__()
self.custom_data = "preserved"
frame = MyFrame()
scene.ui.append(frame)
retrieved = scene.ui[0] # Same MyFrame instance with custom_data intact
```
Works correctly, with retrieved maintaining the derived type and custom attributes.
Closes #112
commit c5e7e8e298
Update test demos for new Python API and entity system
- Update all text input demos to use new Entity constructor signature
- Fix pathfinding showcase to work with new entity position handling
- Remove entity_waypoints tracking in favor of simplified movement
- Delete obsolete exhaustive_api_demo.py (superseded by newer demos)
- Adjust entity creation calls to match Entity((x, y), texture, sprite_index) pattern
commit 6d29652ae7
Update animation demo suite with crash fixes and improvements
- Add warnings about AnimationManager segfault bug in sizzle_reel_final.py
- Create sizzle_reel_final_fixed.py that works around the crash by hiding objects instead of removing them
- Increase font sizes for better visibility in demos
- Extend demo durations for better showcase of animations
- Remove debug prints from animation_sizzle_reel_working.py
- Minor cleanup and improvements to all animation demos
commit a010e5fa96
Update game scripts for new Python API
- Convert entity position access from tuple to x/y properties
- Update caption size property to font_size
- Fix grid boundary checks to use grid_size instead of exceptions
- Clean up demo timer on menu exit to prevent callbacks
These changes adapt the game scripts to work with the new standardized
Python API constructors and property names.
commit 9c8d6c4591
Fix click event z-order handling in PyScene
Changed click detection to properly respect z-index by:
- Sorting ui_elements in-place when needed (same as render order)
- Using reverse iterators to check highest z-index elements first
- This ensures top-most elements receive clicks before lower ones
commit dcd1b0ca33
Add roguelike tutorial implementation files
Implement Parts 0-2 of the classic roguelike tutorial adapted for McRogueFace:
- Part 0: Basic grid setup and tile rendering
- Part 1: Drawing '@' symbol and basic movement
- Part 1b: Variant with sprite-based player
- Part 2: Entity system and NPC implementation with three movement variants:
- part_2.py: Standard implementation
- part_2-naive.py: Naive movement approach
- part_2-onemovequeued.py: Queued movement system
Includes tutorial assets:
- tutorial2.png: Tileset for dungeon tiles
- tutorial_hero.png: Player sprite sheet
commit 6813fb5129
Standardize Python API constructors and remove PyArgHelpers
- Remove PyArgHelpers.h and all macro-based argument parsing
- Convert all UI class constructors to use PyArg_ParseTupleAndKeywords
- Standardize constructor signatures across UICaption, UIEntity, UIFrame, UIGrid, and UISprite
- Replace PYARGHELPER_SINGLE/MULTI macros with explicit argument parsing
- Improve error messages and argument validation
- Maintain backward compatibility with existing Python code
This change improves code maintainability and consistency across the Python API.
commit 6f67fbb51e
Fix animation callback crashes from iterator invalidation (#119)
Resolved segfaults caused by creating new animations from within
animation callbacks. The issue was iterator invalidation in
AnimationManager::update() when callbacks modified the active
animations vector.
Changes:
- Add deferred animation queue to AnimationManager
- New animations created during update are queued and added after
- Set isUpdating flag to track when in update loop
- Properly handle Animation destructor during callback execution
- Add clearCallback() method for safe cleanup scenarios
This fixes the "free(): invalid pointer" and "malloc(): unaligned
fastbin chunk detected" errors that occurred with rapid animation
creation in callbacks.
commit eb88c7b3aa
Add animation completion callbacks (#119)
Implement callbacks that fire when animations complete, enabling direct
causality between animation end and game state changes. This eliminates
race conditions from parallel timer workarounds.
- Add optional callback parameter to Animation constructor
- Callbacks execute synchronously when animation completes
- Proper Python reference counting with GIL safety
- Callbacks receive (anim, target) parameters (currently None)
- Exception handling prevents crashes from Python errors
Example usage:
```python
def on_complete(anim, target):
player_moving = False
anim = mcrfpy.Animation("x", 300.0, 1.0, "easeOut", callback=on_complete)
anim.start(player)
```
closes #119
commit 9fb428dd01
Update ROADMAP with GitHub issue numbers (#111-#125)
Added issue numbers from GitHub tracker to roadmap items:
- #111: Grid Click Events Broken in Headless
- #112: Object Splitting Bug (Python type preservation)
- #113: Batch Operations for Grid
- #114: CellView API
- #115: SpatialHash Implementation
- #116: Dirty Flag System
- #117: Memory Pool for Entities
- #118: Scene as Drawable
- #119: Animation Completion Callbacks
- #120: Animation Property Locking
- #121: Timer Object System
- #122: Parent-Child UI System
- #123: Grid Subgrid System
- #124: Grid Point Animation
- #125: GitHub Issues Automation
Also updated existing references:
- #101/#110: Constructor standardization
- #109: Vector class indexing
Note: Tutorial-specific items and Python-implementable features
(input queue, collision reservation) are not tracked as engine issues.
commit 062e4dadc4
Fix animation segfaults with RAII weak_ptr implementation
Resolved two critical segmentation faults in AnimationManager:
1. Race condition when creating multiple animations in timer callbacks
2. Exit crash when animations outlive their target objects
Changes:
- Replace raw pointers with std::weak_ptr for automatic target invalidation
- Add Animation::complete() to jump animations to final value
- Add Animation::hasValidTarget() to check if target still exists
- Update AnimationManager to auto-remove invalid animations
- Add AnimationManager::clear() call to GameEngine::cleanup()
- Update Python bindings to pass shared_ptr instead of raw pointers
This ensures animations can never reference destroyed objects, following
proper RAII principles. Tested with sizzle_reel_final.py and stress
tests creating/destroying hundreds of animated objects.
commit 98fc49a978
Directory structure cleanup and organization overhaul
574 lines
No EOL
16 KiB
Python
574 lines
No EOL
16 KiB
Python
#!/usr/bin/env python3
|
|
"""Generate .pyi type stub files for McRogueFace Python API - Version 2.
|
|
|
|
This script creates properly formatted type stubs by manually defining
|
|
the API based on the documentation we've created.
|
|
"""
|
|
|
|
import os
|
|
import mcrfpy
|
|
|
|
def generate_mcrfpy_stub():
|
|
"""Generate the main mcrfpy.pyi stub file."""
|
|
return '''"""Type stubs for McRogueFace Python API.
|
|
|
|
Core game engine interface for creating roguelike games with Python.
|
|
"""
|
|
|
|
from typing import Any, List, Dict, Tuple, Optional, Callable, Union, overload
|
|
|
|
# Type aliases
|
|
UIElement = Union['Frame', 'Caption', 'Sprite', 'Grid']
|
|
Transition = Union[str, None]
|
|
|
|
# Classes
|
|
|
|
class Color:
|
|
"""SFML Color Object for RGBA colors."""
|
|
|
|
r: int
|
|
g: int
|
|
b: int
|
|
a: int
|
|
|
|
@overload
|
|
def __init__(self) -> None: ...
|
|
@overload
|
|
def __init__(self, r: int, g: int, b: int, a: int = 255) -> None: ...
|
|
|
|
def from_hex(self, hex_string: str) -> 'Color':
|
|
"""Create color from hex string (e.g., '#FF0000' or 'FF0000')."""
|
|
...
|
|
|
|
def to_hex(self) -> str:
|
|
"""Convert color to hex string format."""
|
|
...
|
|
|
|
def lerp(self, other: 'Color', t: float) -> 'Color':
|
|
"""Linear interpolation between two colors."""
|
|
...
|
|
|
|
class Vector:
|
|
"""SFML Vector Object for 2D coordinates."""
|
|
|
|
x: float
|
|
y: float
|
|
|
|
@overload
|
|
def __init__(self) -> None: ...
|
|
@overload
|
|
def __init__(self, x: float, y: float) -> None: ...
|
|
|
|
def add(self, other: 'Vector') -> 'Vector': ...
|
|
def subtract(self, other: 'Vector') -> 'Vector': ...
|
|
def multiply(self, scalar: float) -> 'Vector': ...
|
|
def divide(self, scalar: float) -> 'Vector': ...
|
|
def distance(self, other: 'Vector') -> float: ...
|
|
def normalize(self) -> 'Vector': ...
|
|
def dot(self, other: 'Vector') -> float: ...
|
|
|
|
class Texture:
|
|
"""SFML Texture Object for images."""
|
|
|
|
def __init__(self, filename: str) -> None: ...
|
|
|
|
filename: str
|
|
width: int
|
|
height: int
|
|
sprite_count: int
|
|
|
|
class Font:
|
|
"""SFML Font Object for text rendering."""
|
|
|
|
def __init__(self, filename: str) -> None: ...
|
|
|
|
filename: str
|
|
family: str
|
|
|
|
class Drawable:
|
|
"""Base class for all drawable UI elements."""
|
|
|
|
x: float
|
|
y: float
|
|
visible: bool
|
|
z_index: int
|
|
name: str
|
|
pos: Vector
|
|
|
|
def get_bounds(self) -> Tuple[float, float, float, float]:
|
|
"""Get bounding box as (x, y, width, height)."""
|
|
...
|
|
|
|
def move(self, dx: float, dy: float) -> None:
|
|
"""Move by relative offset (dx, dy)."""
|
|
...
|
|
|
|
def resize(self, width: float, height: float) -> None:
|
|
"""Resize to new dimensions (width, height)."""
|
|
...
|
|
|
|
class Frame(Drawable):
|
|
"""Frame(x=0, y=0, w=0, h=0, fill_color=None, outline_color=None, outline=0, click=None, children=None)
|
|
|
|
A rectangular frame UI element that can contain other drawable elements.
|
|
"""
|
|
|
|
@overload
|
|
def __init__(self) -> None: ...
|
|
@overload
|
|
def __init__(self, x: float = 0, y: float = 0, w: float = 0, h: float = 0,
|
|
fill_color: Optional[Color] = None, outline_color: Optional[Color] = None,
|
|
outline: float = 0, click: Optional[Callable] = None,
|
|
children: Optional[List[UIElement]] = None) -> None: ...
|
|
|
|
w: float
|
|
h: float
|
|
fill_color: Color
|
|
outline_color: Color
|
|
outline: float
|
|
click: Optional[Callable[[float, float, int], None]]
|
|
children: 'UICollection'
|
|
clip_children: bool
|
|
|
|
class Caption(Drawable):
|
|
"""Caption(text='', x=0, y=0, font=None, fill_color=None, outline_color=None, outline=0, click=None)
|
|
|
|
A text display UI element with customizable font and styling.
|
|
"""
|
|
|
|
@overload
|
|
def __init__(self) -> None: ...
|
|
@overload
|
|
def __init__(self, text: str = '', x: float = 0, y: float = 0,
|
|
font: Optional[Font] = None, fill_color: Optional[Color] = None,
|
|
outline_color: Optional[Color] = None, outline: float = 0,
|
|
click: Optional[Callable] = None) -> None: ...
|
|
|
|
text: str
|
|
font: Font
|
|
fill_color: Color
|
|
outline_color: Color
|
|
outline: float
|
|
click: Optional[Callable[[float, float, int], None]]
|
|
w: float # Read-only, computed from text
|
|
h: float # Read-only, computed from text
|
|
|
|
class Sprite(Drawable):
|
|
"""Sprite(x=0, y=0, texture=None, sprite_index=0, scale=1.0, click=None)
|
|
|
|
A sprite UI element that displays a texture or portion of a texture atlas.
|
|
"""
|
|
|
|
@overload
|
|
def __init__(self) -> None: ...
|
|
@overload
|
|
def __init__(self, x: float = 0, y: float = 0, texture: Optional[Texture] = None,
|
|
sprite_index: int = 0, scale: float = 1.0,
|
|
click: Optional[Callable] = None) -> None: ...
|
|
|
|
texture: Texture
|
|
sprite_index: int
|
|
scale: float
|
|
click: Optional[Callable[[float, float, int], None]]
|
|
w: float # Read-only, computed from texture
|
|
h: float # Read-only, computed from texture
|
|
|
|
class Grid(Drawable):
|
|
"""Grid(x=0, y=0, grid_size=(20, 20), texture=None, tile_width=16, tile_height=16, scale=1.0, click=None)
|
|
|
|
A grid-based tilemap UI element for rendering tile-based levels and game worlds.
|
|
"""
|
|
|
|
@overload
|
|
def __init__(self) -> None: ...
|
|
@overload
|
|
def __init__(self, x: float = 0, y: float = 0, grid_size: Tuple[int, int] = (20, 20),
|
|
texture: Optional[Texture] = None, tile_width: int = 16, tile_height: int = 16,
|
|
scale: float = 1.0, click: Optional[Callable] = None) -> None: ...
|
|
|
|
grid_size: Tuple[int, int]
|
|
tile_width: int
|
|
tile_height: int
|
|
texture: Texture
|
|
scale: float
|
|
points: List[List['GridPoint']]
|
|
entities: 'EntityCollection'
|
|
background_color: Color
|
|
click: Optional[Callable[[int, int, int], None]]
|
|
|
|
def at(self, x: int, y: int) -> 'GridPoint':
|
|
"""Get grid point at tile coordinates."""
|
|
...
|
|
|
|
class GridPoint:
|
|
"""Grid point representing a single tile."""
|
|
|
|
texture_index: int
|
|
solid: bool
|
|
color: Color
|
|
|
|
class GridPointState:
|
|
"""State information for a grid point."""
|
|
|
|
texture_index: int
|
|
color: Color
|
|
|
|
class Entity(Drawable):
|
|
"""Entity(grid_x=0, grid_y=0, texture=None, sprite_index=0, name='')
|
|
|
|
Game entity that lives within a Grid.
|
|
"""
|
|
|
|
@overload
|
|
def __init__(self) -> None: ...
|
|
@overload
|
|
def __init__(self, grid_x: float = 0, grid_y: float = 0, texture: Optional[Texture] = None,
|
|
sprite_index: int = 0, name: str = '') -> None: ...
|
|
|
|
grid_x: float
|
|
grid_y: float
|
|
texture: Texture
|
|
sprite_index: int
|
|
grid: Optional[Grid]
|
|
|
|
def at(self, grid_x: float, grid_y: float) -> None:
|
|
"""Move entity to grid position."""
|
|
...
|
|
|
|
def die(self) -> None:
|
|
"""Remove entity from its grid."""
|
|
...
|
|
|
|
def index(self) -> int:
|
|
"""Get index in parent grid's entity collection."""
|
|
...
|
|
|
|
class UICollection:
|
|
"""Collection of UI drawable elements (Frame, Caption, Sprite, Grid)."""
|
|
|
|
def __len__(self) -> int: ...
|
|
def __getitem__(self, index: int) -> UIElement: ...
|
|
def __setitem__(self, index: int, value: UIElement) -> None: ...
|
|
def __delitem__(self, index: int) -> None: ...
|
|
def __contains__(self, item: UIElement) -> bool: ...
|
|
def __iter__(self) -> Any: ...
|
|
def __add__(self, other: 'UICollection') -> 'UICollection': ...
|
|
def __iadd__(self, other: 'UICollection') -> 'UICollection': ...
|
|
|
|
def append(self, item: UIElement) -> None: ...
|
|
def extend(self, items: List[UIElement]) -> None: ...
|
|
def remove(self, item: UIElement) -> None: ...
|
|
def index(self, item: UIElement) -> int: ...
|
|
def count(self, item: UIElement) -> int: ...
|
|
|
|
class EntityCollection:
|
|
"""Collection of Entity objects."""
|
|
|
|
def __len__(self) -> int: ...
|
|
def __getitem__(self, index: int) -> Entity: ...
|
|
def __setitem__(self, index: int, value: Entity) -> None: ...
|
|
def __delitem__(self, index: int) -> None: ...
|
|
def __contains__(self, item: Entity) -> bool: ...
|
|
def __iter__(self) -> Any: ...
|
|
def __add__(self, other: 'EntityCollection') -> 'EntityCollection': ...
|
|
def __iadd__(self, other: 'EntityCollection') -> 'EntityCollection': ...
|
|
|
|
def append(self, item: Entity) -> None: ...
|
|
def extend(self, items: List[Entity]) -> None: ...
|
|
def remove(self, item: Entity) -> None: ...
|
|
def index(self, item: Entity) -> int: ...
|
|
def count(self, item: Entity) -> int: ...
|
|
|
|
class Scene:
|
|
"""Base class for object-oriented scenes."""
|
|
|
|
name: str
|
|
|
|
def __init__(self, name: str) -> None: ...
|
|
|
|
def activate(self) -> None:
|
|
"""Called when scene becomes active."""
|
|
...
|
|
|
|
def deactivate(self) -> None:
|
|
"""Called when scene becomes inactive."""
|
|
...
|
|
|
|
def get_ui(self) -> UICollection:
|
|
"""Get UI elements collection."""
|
|
...
|
|
|
|
def on_keypress(self, key: str, pressed: bool) -> None:
|
|
"""Handle keyboard events."""
|
|
...
|
|
|
|
def on_click(self, x: float, y: float, button: int) -> None:
|
|
"""Handle mouse clicks."""
|
|
...
|
|
|
|
def on_enter(self) -> None:
|
|
"""Called when entering the scene."""
|
|
...
|
|
|
|
def on_exit(self) -> None:
|
|
"""Called when leaving the scene."""
|
|
...
|
|
|
|
def on_resize(self, width: int, height: int) -> None:
|
|
"""Handle window resize events."""
|
|
...
|
|
|
|
def update(self, dt: float) -> None:
|
|
"""Update scene logic."""
|
|
...
|
|
|
|
class Timer:
|
|
"""Timer object for scheduled callbacks."""
|
|
|
|
name: str
|
|
interval: int
|
|
active: bool
|
|
|
|
def __init__(self, name: str, callback: Callable[[float], None], interval: int) -> None: ...
|
|
|
|
def pause(self) -> None:
|
|
"""Pause the timer."""
|
|
...
|
|
|
|
def resume(self) -> None:
|
|
"""Resume the timer."""
|
|
...
|
|
|
|
def cancel(self) -> None:
|
|
"""Cancel and remove the timer."""
|
|
...
|
|
|
|
class Window:
|
|
"""Window singleton for managing the game window."""
|
|
|
|
resolution: Tuple[int, int]
|
|
fullscreen: bool
|
|
vsync: bool
|
|
title: str
|
|
fps_limit: int
|
|
game_resolution: Tuple[int, int]
|
|
scaling_mode: str
|
|
|
|
@staticmethod
|
|
def get() -> 'Window':
|
|
"""Get the window singleton instance."""
|
|
...
|
|
|
|
class Animation:
|
|
"""Animation object for animating UI properties."""
|
|
|
|
target: Any
|
|
property: str
|
|
duration: float
|
|
easing: str
|
|
loop: bool
|
|
on_complete: Optional[Callable]
|
|
|
|
def __init__(self, target: Any, property: str, start_value: Any, end_value: Any,
|
|
duration: float, easing: str = 'linear', loop: bool = False,
|
|
on_complete: Optional[Callable] = None) -> None: ...
|
|
|
|
def start(self) -> None:
|
|
"""Start the animation."""
|
|
...
|
|
|
|
def update(self, dt: float) -> bool:
|
|
"""Update animation, returns True if still running."""
|
|
...
|
|
|
|
def get_current_value(self) -> Any:
|
|
"""Get the current interpolated value."""
|
|
...
|
|
|
|
# Module functions
|
|
|
|
def createSoundBuffer(filename: str) -> int:
|
|
"""Load a sound effect from a file and return its buffer ID."""
|
|
...
|
|
|
|
def loadMusic(filename: str) -> None:
|
|
"""Load and immediately play background music from a file."""
|
|
...
|
|
|
|
def setMusicVolume(volume: int) -> None:
|
|
"""Set the global music volume (0-100)."""
|
|
...
|
|
|
|
def setSoundVolume(volume: int) -> None:
|
|
"""Set the global sound effects volume (0-100)."""
|
|
...
|
|
|
|
def playSound(buffer_id: int) -> None:
|
|
"""Play a sound effect using a previously loaded buffer."""
|
|
...
|
|
|
|
def getMusicVolume() -> int:
|
|
"""Get the current music volume level (0-100)."""
|
|
...
|
|
|
|
def getSoundVolume() -> int:
|
|
"""Get the current sound effects volume level (0-100)."""
|
|
...
|
|
|
|
def sceneUI(scene: Optional[str] = None) -> UICollection:
|
|
"""Get all UI elements for a scene."""
|
|
...
|
|
|
|
def currentScene() -> str:
|
|
"""Get the name of the currently active scene."""
|
|
...
|
|
|
|
def setScene(scene: str, transition: Optional[str] = None, duration: float = 0.0) -> None:
|
|
"""Switch to a different scene with optional transition effect."""
|
|
...
|
|
|
|
def createScene(name: str) -> None:
|
|
"""Create a new empty scene."""
|
|
...
|
|
|
|
def keypressScene(handler: Callable[[str, bool], None]) -> None:
|
|
"""Set the keyboard event handler for the current scene."""
|
|
...
|
|
|
|
def setTimer(name: str, handler: Callable[[float], None], interval: int) -> None:
|
|
"""Create or update a recurring timer."""
|
|
...
|
|
|
|
def delTimer(name: str) -> None:
|
|
"""Stop and remove a timer."""
|
|
...
|
|
|
|
def exit() -> None:
|
|
"""Cleanly shut down the game engine and exit the application."""
|
|
...
|
|
|
|
def setScale(multiplier: float) -> None:
|
|
"""Scale the game window size (deprecated - use Window.resolution)."""
|
|
...
|
|
|
|
def find(name: str, scene: Optional[str] = None) -> Optional[UIElement]:
|
|
"""Find the first UI element with the specified name."""
|
|
...
|
|
|
|
def findAll(pattern: str, scene: Optional[str] = None) -> List[UIElement]:
|
|
"""Find all UI elements matching a name pattern (supports * wildcards)."""
|
|
...
|
|
|
|
def getMetrics() -> Dict[str, Union[int, float]]:
|
|
"""Get current performance metrics."""
|
|
...
|
|
|
|
# Submodule
|
|
class automation:
|
|
"""Automation API for testing and scripting."""
|
|
|
|
@staticmethod
|
|
def screenshot(filename: str) -> bool:
|
|
"""Save a screenshot to the specified file."""
|
|
...
|
|
|
|
@staticmethod
|
|
def position() -> Tuple[int, int]:
|
|
"""Get current mouse position as (x, y) tuple."""
|
|
...
|
|
|
|
@staticmethod
|
|
def size() -> Tuple[int, int]:
|
|
"""Get screen size as (width, height) tuple."""
|
|
...
|
|
|
|
@staticmethod
|
|
def onScreen(x: int, y: int) -> bool:
|
|
"""Check if coordinates are within screen bounds."""
|
|
...
|
|
|
|
@staticmethod
|
|
def moveTo(x: int, y: int, duration: float = 0.0) -> None:
|
|
"""Move mouse to absolute position."""
|
|
...
|
|
|
|
@staticmethod
|
|
def moveRel(xOffset: int, yOffset: int, duration: float = 0.0) -> None:
|
|
"""Move mouse relative to current position."""
|
|
...
|
|
|
|
@staticmethod
|
|
def dragTo(x: int, y: int, duration: float = 0.0, button: str = 'left') -> None:
|
|
"""Drag mouse to position."""
|
|
...
|
|
|
|
@staticmethod
|
|
def dragRel(xOffset: int, yOffset: int, duration: float = 0.0, button: str = 'left') -> None:
|
|
"""Drag mouse relative to current position."""
|
|
...
|
|
|
|
@staticmethod
|
|
def click(x: Optional[int] = None, y: Optional[int] = None, clicks: int = 1,
|
|
interval: float = 0.0, button: str = 'left') -> None:
|
|
"""Click mouse at position."""
|
|
...
|
|
|
|
@staticmethod
|
|
def mouseDown(x: Optional[int] = None, y: Optional[int] = None, button: str = 'left') -> None:
|
|
"""Press mouse button down."""
|
|
...
|
|
|
|
@staticmethod
|
|
def mouseUp(x: Optional[int] = None, y: Optional[int] = None, button: str = 'left') -> None:
|
|
"""Release mouse button."""
|
|
...
|
|
|
|
@staticmethod
|
|
def keyDown(key: str) -> None:
|
|
"""Press key down."""
|
|
...
|
|
|
|
@staticmethod
|
|
def keyUp(key: str) -> None:
|
|
"""Release key."""
|
|
...
|
|
|
|
@staticmethod
|
|
def press(key: str) -> None:
|
|
"""Press and release a key."""
|
|
...
|
|
|
|
@staticmethod
|
|
def typewrite(text: str, interval: float = 0.0) -> None:
|
|
"""Type text with optional interval between characters."""
|
|
...
|
|
'''
|
|
|
|
def main():
|
|
"""Generate type stubs."""
|
|
print("Generating comprehensive type stubs for McRogueFace...")
|
|
|
|
# Create stubs directory
|
|
os.makedirs('stubs', exist_ok=True)
|
|
|
|
# Write main stub file
|
|
with open('stubs/mcrfpy.pyi', 'w') as f:
|
|
f.write(generate_mcrfpy_stub())
|
|
|
|
print("Generated stubs/mcrfpy.pyi")
|
|
|
|
# Create py.typed marker
|
|
with open('stubs/py.typed', 'w') as f:
|
|
f.write('')
|
|
|
|
print("Created py.typed marker")
|
|
|
|
print("\nType stubs generated successfully!")
|
|
print("\nTo use in your IDE:")
|
|
print("1. Add the 'stubs' directory to your project")
|
|
print("2. Most IDEs will automatically detect the .pyi files")
|
|
print("3. For VS Code: add to python.analysis.extraPaths in settings.json")
|
|
print("4. For PyCharm: mark 'stubs' directory as Sources Root")
|
|
|
|
if __name__ == '__main__':
|
|
main() |