Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
*.pyc
*.json
*.txt
*.txt
__pycache__/
42 changes: 42 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,48 @@ This project is part of the [Viron](https://github.com/Preponderous-Software/Vir
- Interactive toggling of cell states
- Modular structure designed for future support of other graphics libraries
- Clean interface for testing Viron entity placement and behavior
- **RenderWindow** class for simplified Pygame window management

## RenderWindow

Patchwork provides a `RenderWindow` class that encapsulates Pygame initialization and window management. This class is designed for **composition, not inheritance**, making it easy to integrate into projects without subclassing.

### Key Features

- Automatic Pygame initialization
- Window and surface management
- Event loop handling with custom event handlers
- Frame rate control
- Clean API for common rendering operations

### Basic Usage

```python
from render_window import RenderWindow
import pygame

# Create window
window = RenderWindow("My Application", 800, 600)
surface = window.get_surface()

# Register custom event handlers
def handle_input(event):
if event.type == pygame.KEYDOWN:
print(f"Key pressed: {event.key}")

window.register_event_handler(handle_input)

# Main loop
while window.should_continue():
surface.fill((0, 0, 0))
# ... render your content ...
pygame.display.update()
window.tick(60) # 60 FPS

pygame.quit()
```

See `render_window_example.py` for more usage examples, including integration with the existing `Graphik` class.

## Getting Started

Expand Down
78 changes: 78 additions & 0 deletions render_window.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import pygame

# @author Daniel McCoy Stephenson
# @since October 2nd, 2025
class RenderWindow:
"""
RenderWindow class encapsulates Pygame window initialization and management.
Designed for composition, not inheritance - use as a container for rendering.

Manages:
- Pygame initialization
- Window creation and display surface
- Event loop processing
- Frame rate control
- Custom event handlers
"""

def __init__(self, title, width, height):
"""
Initialize the RenderWindow with the specified title and dimensions.

Args:
title (str): Window title
width (int): Window width in pixels
height (int): Window height in pixels
"""
pygame.init()
self._surface = pygame.display.set_mode((width, height))
pygame.display.set_caption(title)
self._clock = pygame.time.Clock()
self._running = True
self._event_handlers = []

def get_surface(self):
"""
Get the display surface for rendering.

Returns:
pygame.Surface: The display surface
"""
return self._surface

def tick(self, fps):
"""
Control the frame rate by limiting the number of frames per second.

Args:
fps (int): Target frames per second
"""
self._clock.tick(fps)

def should_continue(self):
"""
Process events and check if the window should continue running.
Handles QUIT events internally and calls registered event handlers.

Returns:
bool: True if the window should continue running, False otherwise
"""
for event in pygame.event.get():
if event.type == pygame.QUIT:
self._running = False
else:
# Call all registered event handlers
for handler in self._event_handlers:
handler(event)

return self._running

def register_event_handler(self, handler):
"""
Register a custom event handler function.
The handler will be called for each event (except QUIT which is handled internally).

Args:
handler (callable): A function that takes a pygame.Event as parameter
"""
self._event_handlers.append(handler)
139 changes: 139 additions & 0 deletions render_window_example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
#!/usr/bin/env python3
"""
Example demonstrating RenderWindow usage in the Patchwork context.
This shows how to refactor main.py to use RenderWindow instead of raw Pygame calls.
"""

import random
import pygame
from render_window import RenderWindow
from graphik import Graphik

# Example 1: Simple usage replacing raw Pygame initialization
def simple_example():
"""Demonstrates basic RenderWindow usage"""
# Instead of:
# pygame.init()
# gameDisplay = pygame.display.set_mode((800, 800))
# pygame.display.set_caption("Title")

# Use RenderWindow:
window = RenderWindow("Simple Example", 800, 800)
surface = window.get_surface()

# Create Graphik instance with the surface
graphik = Graphik(surface)

# Main loop - instead of manual event handling
while window.should_continue():
surface.fill((255, 255, 255))
graphik.drawText("RenderWindow Example", 400, 400, 30, (0, 0, 0))
pygame.display.update()
window.tick(60)

pygame.quit()


# Example 2: With custom event handlers
def event_handler_example():
"""Demonstrates custom event handler registration"""
window = RenderWindow("Event Handler Example", 800, 800)
surface = window.get_surface()
graphik = Graphik(surface)

# State
click_count = [0]

# Custom event handler
def handle_mouse_click(event):
if event.type == pygame.MOUSEBUTTONDOWN:
click_count[0] += 1
print(f"Mouse clicked! Total clicks: {click_count[0]}")

# Register the handler
window.register_event_handler(handle_mouse_click)

# Main loop
while window.should_continue():
surface.fill((255, 255, 255))
graphik.drawText(f"Clicks: {click_count[0]}", 400, 400, 30, (0, 0, 0))
graphik.drawText("Click anywhere or close window", 400, 450, 20, (100, 100, 100))
pygame.display.update()
window.tick(60)

pygame.quit()


# Example 3: Integration pattern for main.py
def integration_example():
"""
Shows how main.py could be refactored to use RenderWindow.

The refactored main() function would replace:
pygame.init()
gameDisplay = pygame.display.set_mode((displayWidth, displayHeight))
pygame.display.set_caption("Visualizing Environment With Random Colors")

while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()

With:
window = RenderWindow("Visualizing Environment With Random Colors", displayWidth, displayHeight)
surface = window.get_surface()
graphik = Graphik(surface)

while window.should_continue():
# ... existing drawing code ...
window.tick(60) # Optional: add frame rate limiting
"""
displayWidth = 800
displayHeight = 800

# Create window using RenderWindow
window = RenderWindow("Visualizing Environment With Random Colors", displayWidth, displayHeight)
surface = window.get_surface()
graphik = Graphik(surface)

# Example: drawing random rectangles (simulating environment visualization)
white = (255, 255, 255)

while window.should_continue():
surface.fill(white)

# Draw some example content
for i in range(10):
for j in range(10):
red = random.randrange(50, 200)
green = random.randrange(50, 200)
blue = random.randrange(50, 200)
x = i * (displayWidth / 10)
y = j * (displayHeight / 10)
graphik.drawRectangle(x, y, displayWidth / 10, displayHeight / 10, (red, green, blue))

pygame.display.update()
window.tick(60) # Limit to 60 FPS

pygame.quit()


if __name__ == "__main__":
print("RenderWindow Usage Examples")
print("=" * 60)
print("1. Simple example")
print("2. Event handler example")
print("3. Integration example (simulates main.py refactoring)")
print()
choice = input("Select example (1-3): ").strip()

if choice == "1":
simple_example()
elif choice == "2":
event_handler_example()
elif choice == "3":
integration_example()
else:
print("Invalid choice. Running integration example by default.")
integration_example()