imgui stuffz

This commit is contained in:
OusmBlueNinja 2025-04-08 11:28:40 -05:00
parent 9bab6a0265
commit d54ecc33da
5 changed files with 706 additions and 14 deletions

View File

@ -0,0 +1,364 @@
#!/usr/bin/env python3
"""
A single-player pygame racing simulation where engine RPM and speed (in mph)
are modeled as independentbut coupledsystems. The engines RPM (tachometer)
is updated by throttle/brake input plus a damping effect that tends to lock
the engine to the value expected from the current vehicle speed and gear.
Vehicle acceleration is computed from the RPM difference (engine load)
minus drag, yielding a more realistic feel.
Controls:
- LEFT/RIGHT arrows: steer
- UP arrow: throttle (increases engine RPM and, via the dynamics, accelerates the car)
- DOWN arrow: brake
- E: upshift (gear +1 with rev-match drop)
- Q: downshift (gear -1 with a slight rev bump)
- R: toggle reverse (gear 0) versus forward (gear 1)
"""
import sys
import math
import random
import pygame
# ----- Screen & Road Configuration -----
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
ROAD_WIDTH = 150 # Road drawn as a wide filled polygon
FPS = 60
# ----- Engine & Vehicle Dynamics -----
IDLE_RPM = 800
REDLINE_RPM = 6000
# Engine dynamics (in rpm/sec)
THROTTLE_RPM_RATE = 800 # When throttle is pressed, engine spins up
BRAKE_RPM_RATE = 2500 # When braking, engine RPM drops rapidly
DAMPING_FACTOR = 5 # How strongly engine RPM is forced toward the "ideal" RPM
# These dynamics model the connection between engine torque and vehicle acceleration:
# The vehicle's acceleration (in mph/s) is proportional to the difference between the
# current engine RPM and the ideal RPM that would be produced by the current speed in the given gear.
ACCELERATION_CONVERSION = 150 # (rpm difference) to mph/s conversion factor
DRAG_COEFF = 0.02 # Drag (per second) that always slows the vehicle
# Conversion constant (empirical) for what engine RPM should be for a given vehicle speed
# in a particular gear. In this model, ideal_rpm = speed × gear_ratio × K.
K = 21
# Maximum vehicle speed (in mph)
MAX_SPEED = 90
# ----- Transmission & Gear Ratios -----
# These gear ratios (including reverse = gear 0) are assumed to incorporate the effect
# of the transmission (and final drive) so that:
# ideal_rpm = speed (mph) × gear_ratio × K.
GEAR_RATIOS = {
0: 4.866, # Reverse
1: 4.696,
2: 2.985,
3: 2.146,
4: 1.769,
5: 1.520,
6: 1.275,
7: 1.000,
8: 0.854,
9: 0.689,
10: 0.636
}
# Turning rate (radians per second)
TURN_RATE = 2.0
# ----- Gauge Configuration -----
GAUGE_RADIUS = 50
SPEED_GAUGE_CENTER = (150, 100)
TACH_GAUGE_CENTER = (650, 100)
# ----- Helper Functions -----
def rotate_point(point, angle):
"""Rotate a 2D point by angle in radians."""
x, y = point
cos_a = math.cos(angle)
sin_a = math.sin(angle)
return (x * cos_a - y * sin_a, x * sin_a + y * cos_a)
def draw_gauge(surface, center, radius, min_val, max_val, current_val, label):
"""
Draw an analog gauge (circle with a needle) mapping a value from min_val to max_val
to an angle between -135° and +135°.
"""
pygame.draw.circle(surface, (255, 255, 255), center, radius, 2)
min_angle = -135
max_angle = 135
ratio = (current_val - min_val) / (max_val - min_val) if max_val != min_val else 0
angle_deg = min_angle + ratio * (max_angle - min_angle)
angle_rad = math.radians(angle_deg)
needle_length = radius - 10
needle_end = (center[0] + needle_length * math.cos(angle_rad),
center[1] + needle_length * math.sin(angle_rad))
pygame.draw.line(surface, (255, 0, 0), center, needle_end, 3)
font = pygame.font.SysFont(None, 24)
text_surf = font.render(f"{label}: {int(current_val)}", True, (255, 255, 255))
text_rect = text_surf.get_rect(center=(center[0], center[1] + radius + 20))
surface.blit(text_surf, text_rect)
def point_line_distance(P, A, B):
"""Return the shortest distance from point P to line segment AB."""
AB = (B[0] - A[0], B[1] - A[1])
AP = (P[0] - A[0], P[1] - A[1])
ab2 = AB[0]**2 + AB[1]**2
if ab2 == 0:
return math.hypot(P[0] - A[0], P[1] - A[1])
t = max(0, min(1, (AP[0]*AB[0] + AP[1]*AB[1]) / ab2))
proj = (A[0] + t*AB[0], A[1] + t*AB[1])
return math.hypot(P[0] - proj[0], P[1] - proj[1])
# ----- Game Objects -----
class Car:
def __init__(self):
self.x = 0.0 # Position (in feet or arbitrary units that scale to mph)
self.y = 0.0
self.angle = 0.0 # Orientation (radians; 0 means facing right)
self.engine_rpm = IDLE_RPM
self.gear = 1 # 0 = reverse, 1..10 forward
self.speed = 0.0 # Vehicle speed in mph
def update(self, throttle, brake, dt):
"""
Update engine RPM and vehicle speed over time.
- The engine RPM is updated based on throttle input, brake input, and a damping term that
forces it toward the "ideal" RPM determined by the current speed.
- The vehicle acceleration is then computed from the difference between actual and ideal RPM.
"""
# Compute the ideal engine RPM given the current speed and gear.
# For realism, if the car is stopped, ideal RPM should be at least IDLE_RPM.
ideal_rpm = max(IDLE_RPM, self.speed * GEAR_RATIOS[self.gear] * K)
# --- Engine RPM Dynamics ---
# Throttle increases engine RPM; braking drops it.
throttle_effect = THROTTLE_RPM_RATE if throttle else 0
brake_effect = BRAKE_RPM_RATE if brake else 0
# Engine tends to return toward the ideal RPM.
d_rpm = throttle_effect - brake_effect - DAMPING_FACTOR * (self.engine_rpm - ideal_rpm)
self.engine_rpm += d_rpm * dt
# Clamp engine RPM between IDLE and REDLINE.
if self.engine_rpm < IDLE_RPM:
self.engine_rpm = IDLE_RPM
if self.engine_rpm > REDLINE_RPM:
self.engine_rpm = REDLINE_RPM
# --- Vehicle Acceleration Dynamics ---
# The acceleration is proportional to how much the engine is "over-revving" relative to ideal.
# A positive difference means extra torque; a negative difference means the engine is not producing enough torque.
accel = (self.engine_rpm - ideal_rpm) / ACCELERATION_CONVERSION
# Subtract drag proportional to speed.
accel -= DRAG_COEFF * self.speed
# Update vehicle speed.
self.speed += accel * dt
if self.speed < 0:
self.speed = 0
if self.speed > MAX_SPEED:
self.speed = MAX_SPEED
# --- Update Position ---
# When in reverse (gear 0), the vehicle moves backward.
direction = 1 if self.gear != 0 else -1
# Convert speed (mph) to distance per second. (1 mph ≈ 1.46667 ft/s)
ft_per_sec = self.speed * 1.46667
self.x += math.cos(self.angle) * ft_per_sec * dt * direction
self.y += math.sin(self.angle) * ft_per_sec * dt * direction
def rev_match_on_shift_up(self):
"""Simulate a realistic drop in RPM when upshifting (rev-match)."""
# On an upshift, the engine RPM typically drops by 1020%.
self.engine_rpm *= 0.85
if self.engine_rpm < IDLE_RPM:
self.engine_rpm = IDLE_RPM
def rev_match_on_shift_down(self):
"""Simulate a realistic bump in RPM when downshifting."""
self.engine_rpm *= 1.15
if self.engine_rpm > REDLINE_RPM:
self.engine_rpm = REDLINE_RPM
class Track:
"""
A procedural track that extends as the car moves, giving the illusion of an infinite road.
"""
def __init__(self):
self.segments = []
self.last_point = (0.0, 0.0)
self.last_angle = 0.0
self.generate_initial_track()
def generate_initial_track(self):
num_segments = 20
for _ in range(num_segments):
self.extend_segment()
def extend_segment(self):
length = 150
angle_change = random.uniform(-0.3, 0.3)
self.last_angle += angle_change
dx = length * math.cos(self.last_angle)
dy = length * math.sin(self.last_angle)
next_point = (self.last_point[0] + dx, self.last_point[1] + dy)
self.segments.append((self.last_point, next_point))
self.last_point = next_point
def ensure_track_length(self, car_pos, min_distance=500):
dist = math.hypot(self.last_point[0] - car_pos[0], self.last_point[1] - car_pos[1])
while dist < min_distance:
self.extend_segment()
dist = math.hypot(self.last_point[0] - car_pos[0], self.last_point[1] - car_pos[1])
def is_on_road(car, track, road_width):
"""
Checks whether the car's center is within half the road width of any track segment.
"""
P = (car.x, car.y)
hw = road_width / 2
for seg in track.segments:
A, B = seg
if point_line_distance(P, A, B) <= hw:
return True
return False
# ----- Drawing Functions -----
def draw_road(surface, track, offset_x, offset_y, road_width):
"""
Draw each track segment as a filled polygon representing a wide road.
"""
for seg in track.segments:
start, end = seg
dx = end[0] - start[0]
dy = end[1] - start[1]
seg_len = math.hypot(dx, dy)
if seg_len == 0:
continue
# Compute a perpendicular vector.
nx = -dy / seg_len
ny = dx / seg_len
half_w = road_width / 2
start_left = (start[0] + nx * half_w, start[1] + ny * half_w)
start_right = (start[0] - nx * half_w, start[1] - ny * half_w)
end_left = (end[0] + nx * half_w, end[1] + ny * half_w)
end_right = (end[0] - nx * half_w, end[1] - ny * half_w)
poly = [
(int(start_left[0] + offset_x), int(start_left[1] + offset_y)),
(int(end_left[0] + offset_x), int(end_left[1] + offset_y)),
(int(end_right[0] + offset_x), int(end_right[1] + offset_y)),
(int(start_right[0] + offset_x), int(start_right[1] + offset_y))
]
pygame.draw.polygon(surface, (100, 100, 100), poly)
# Draw a center line for visual effect.
center_start = (start[0] + offset_x, start[1] + offset_y)
center_end = (end[0] + offset_x, end[1] + offset_y)
pygame.draw.line(surface, (255, 255, 255), center_start, center_end, 2)
def draw_car(surface, car, offset_x, offset_y):
"""
Draw the car as a rotated, car-like polygon.
"""
center = (car.x + offset_x, car.y + offset_y)
half_length = 40 / 2
half_width = 20 / 2
# Define a 5-point polygon for the car shape.
points = [
( half_length, 0), # Front tip
( half_length * 0.2, -half_width), # Rear top
(-half_length, -half_width), # Rear left
(-half_length, half_width), # Rear right
( half_length * 0.2, half_width) # Rear bottom
]
rotated = []
for p in points:
rp = rotate_point(p, car.angle)
rotated.append((rp[0] + center[0], rp[1] + center[1]))
pygame.draw.polygon(surface, (255, 0, 0), rotated)
pygame.draw.polygon(surface, (255, 255, 255), rotated, 2)
# ----- Main Game Loop -----
def main():
pygame.init()
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("Super Realistic Car Simulation")
clock = pygame.time.Clock()
font = pygame.font.SysFont(None, 24)
car = Car()
track = Track()
running = True
while running:
dt = clock.tick(FPS) / 1000.0
# ----- Event Handling -----
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN:
# Upshift (E)
if event.key == pygame.K_e and car.gear < 10:
old_ratio = GEAR_RATIOS[car.gear]
car.gear += 1
# Rev-match on upshift: drop RPM to roughly maintain consistency.
car.rev_match_on_shift_up()
# Downshift (Q), but do not downshift below gear 1.
elif event.key == pygame.K_q and car.gear > 1:
old_ratio = GEAR_RATIOS[car.gear]
car.gear -= 1
car.rev_match_on_shift_down()
# Toggle reverse (R)
elif event.key == pygame.K_r:
if car.gear == 0:
car.gear = 1
else:
car.gear = 0
# ----- Continuous Input -----
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
car.angle -= TURN_RATE * dt
if keys[pygame.K_RIGHT]:
car.angle += TURN_RATE * dt
throttle = keys[pygame.K_UP]
brake = keys[pygame.K_DOWN]
# ----- Update Car & Extend Track -----
car.update(throttle, brake, dt)
track.ensure_track_length((car.x, car.y), min_distance=500)
# Determine the view offset so the car is centered.
offset_x = SCREEN_WIDTH / 2 - car.x
offset_y = SCREEN_HEIGHT / 2 - car.y
# ----- Drawing -----
screen.fill((0, 150, 0)) # Grass background.
draw_road(screen, track, offset_x, offset_y, ROAD_WIDTH)
draw_car(screen, car, offset_x, offset_y)
# Display status (on-road/off-road)
status_text = "On Road" if is_on_road(car, track, ROAD_WIDTH) else "Off Road"
status_surf = font.render(status_text, True, (255, 255, 255))
screen.blit(status_surf, (10, 10))
# Draw gauges.
# Speed gauge shows vehicle speed in mph.
draw_gauge(screen, SPEED_GAUGE_CENTER, GAUGE_RADIUS, 0, MAX_SPEED, car.speed, "Speed (mph)")
# Tachometer shows engine RPM.
draw_gauge(screen, TACH_GAUGE_CENTER, GAUGE_RADIUS, 0, REDLINE_RPM, car.engine_rpm, "RPM")
# Display current gear.
gear_text = "Reverse" if (car.gear == 0) else f"Gear: {car.gear}"
gear_surf = font.render(gear_text, True, (255, 255, 255))
screen.blit(gear_surf, (SPEED_GAUGE_CENTER[0] - GAUGE_RADIUS, SPEED_GAUGE_CENTER[1] + GAUGE_RADIUS + 40))
pygame.display.flip()
pygame.quit()
sys.exit()
if __name__ == "__main__":
main()

Binary file not shown.

View File

@ -0,0 +1,72 @@
import pygame
import sys
from imgui import PyGUI
WIDTH, HEIGHT = 800, 600
class Ball:
def __init__(self):
self.pos = [WIDTH // 2, HEIGHT // 2]
self.speed = 5
self.direction = 45
self.radius = 20
self.color = (255, 0, 0)
def update(self):
# (For demonstration, implement ball movement if desired.)
pass
def draw(self, surface):
pygame.draw.circle(surface, self.color, (int(self.pos[0]), int(self.pos[1])), self.radius)
def main():
pygame.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("PyGUI Example")
clock = pygame.time.Clock()
PyGUI.init(screen)
ball = Ball()
ball_speed = ball.speed
ball_direction = ball.direction
ball_radius = ball.radius
ball_color_r, ball_color_g, ball_color_b = ball.color
bounce_enabled = False
while True:
events = pygame.event.get()
for event in events:
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
PyGUI.Update(events)
screen.fill((50, 50, 50))
# Draw the GUI window.
PyGUI.Begin("Ball Controls", 10, 10)
ball_speed = PyGUI.Slider("Speed", ball_speed, 1, 10)
ball_direction = PyGUI.Slider("Direction", ball_direction, 0, 360)
ball_radius = PyGUI.Slider("Radius", ball_radius, 10, 100)
ball_color_r = PyGUI.Slider("Red", ball_color_r, 0, 255)
ball_color_g = PyGUI.Slider("Green", ball_color_g, 0, 255)
ball_color_b = PyGUI.Slider("Blue", ball_color_b, 0, 255)
bounce_enabled = PyGUI.Checkbox("Bounce", bounce_enabled)
if PyGUI.Button("Reset Position"):
ball.pos = [WIDTH // 2, HEIGHT // 2]
PyGUI.End()
# Update the ball parameters.
ball.speed = ball_speed
ball.direction = ball_direction
ball.radius = int(ball_radius)
ball.color = (int(ball_color_r), int(ball_color_g), int(ball_color_b))
ball.draw(screen)
pygame.display.flip()
clock.tick(60)
if __name__ == "__main__":
main()

256
pygame-imgui-v2/imgui.py Normal file
View File

@ -0,0 +1,256 @@
import pygame
class PyGUI:
_surface = None
_font = None
_events = []
_window_stack = []
# UI layout constants
TITLE_BAR_HEIGHT = 30
PADDING = 10
SPACING = 5
BOTTOM_PADDING = 10
MIN_WINDOW_WIDTH = 220
# Colors and style
WINDOW_BG_COLOR = (60, 60, 60)
TITLE_BAR_COLOR = (30, 30, 30)
BORDER_COLOR = (20, 20, 20)
BUTTON_COLOR = (100, 100, 100)
BUTTON_HOVER_COLOR = (130, 130, 130)
SLIDER_TRACK_COLOR = (100, 100, 100)
SLIDER_KNOB_COLOR = (150, 150, 150)
WIDGET_TEXT_COLOR = (255, 255, 255)
CHECKBOX_BG_COLOR = (80, 80, 80)
CHECKBOX_CHECK_COLOR = (200, 200, 200)
@staticmethod
def init(surface, font_name='Arial', font_size=18):
"""Initialize PyGUI with the main Pygame surface and font."""
PyGUI._surface = surface
PyGUI._font = pygame.font.SysFont(font_name, font_size)
@staticmethod
def Begin(title, x, y):
"""
Begin a new draggable, autosizing window.
A new window context is created; widget calls will record
their draw commands and update the context layout.
"""
context = {
'title': title,
'x': x,
'y': y,
'width': PyGUI.MIN_WINDOW_WIDTH,
'content_height': 0, # the vertical extent of all widgets
'commands': [], # list of widget draw command lambdas
'drag': False,
'drag_offset': (0, 0),
'local_y': 0 # current Y offset (relative to content area)
}
PyGUI._window_stack.append(context)
@staticmethod
def End():
"""
Ends the current window, calculates final dimensions,
draws the window background, title bar and border, then blits the window.
"""
if not PyGUI._window_stack:
return
context = PyGUI._window_stack.pop()
final_height = PyGUI.TITLE_BAR_HEIGHT + context['content_height'] + PyGUI.BOTTOM_PADDING
# Create an offscreen surface for the entire window with alpha support.
window_surf = pygame.Surface((context['width'], final_height), pygame.SRCALPHA)
# Draw the window background with rounded corners.
pygame.draw.rect(window_surf, PyGUI.WINDOW_BG_COLOR,
(0, 0, context['width'], final_height), border_radius=8)
# Execute widget drawing commands onto window_surf.
for cmd in context['commands']:
cmd(window_surf)
# Draw the title bar on top.
title_bar_rect = pygame.Rect(0, 0, context['width'], PyGUI.TITLE_BAR_HEIGHT)
pygame.draw.rect(window_surf, PyGUI.TITLE_BAR_COLOR, title_bar_rect, border_radius=8)
# Render the window title.
title_surf = PyGUI._font.render(context['title'], True, PyGUI.WIDGET_TEXT_COLOR)
window_surf.blit(title_surf, (PyGUI.PADDING, (PyGUI.TITLE_BAR_HEIGHT - title_surf.get_height()) // 2))
# Draw a border around the entire window.
pygame.draw.rect(window_surf, PyGUI.BORDER_COLOR,
(0, 0, context['width'], final_height), 2, border_radius=8)
# Blit the finished window onto the main surface.
PyGUI._surface.blit(window_surf, (context['x'], context['y']))
@staticmethod
def _current_window():
if not PyGUI._window_stack:
return None
return PyGUI._window_stack[-1]
@staticmethod
def Update(events):
"""Update the internal event cache and process dragging for all windows."""
PyGUI._events = events
for context in PyGUI._window_stack:
PyGUI._process_dragging(context)
@staticmethod
def _process_dragging(context):
"""If the mouse clicks the title bar, allow dragging to update window position."""
mouse_pos = pygame.mouse.get_pos()
mouse_buttons = pygame.mouse.get_pressed()
title_rect = pygame.Rect(context['x'], context['y'], context['width'], PyGUI.TITLE_BAR_HEIGHT)
for event in PyGUI._events:
if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
if title_rect.collidepoint(mouse_pos):
context['drag'] = True
context['drag_offset'] = (mouse_pos[0] - context['x'],
mouse_pos[1] - context['y'])
if event.type == pygame.MOUSEBUTTONUP and event.button == 1:
context['drag'] = False
if context['drag'] and mouse_buttons[0]:
context['x'] = mouse_pos[0] - context['drag_offset'][0]
context['y'] = mouse_pos[1] - context['drag_offset'][1]
@staticmethod
def Button(label, width=100, height=30):
"""
Create a button widget; returns True if the button is clicked.
The interactive test is done immediately and the draw command is recorded.
"""
context = PyGUI._current_window()
if context is None:
return False
# Compute local widget position inside the window's content area.
local_x = PyGUI.PADDING
local_y = context['local_y'] + PyGUI.PADDING
# Calculate absolute position for interaction.
abs_x = context['x'] + local_x
abs_y = context['y'] + PyGUI.TITLE_BAR_HEIGHT + local_y
widget_rect = pygame.Rect(abs_x, abs_y, width, height)
clicked = False
hovered = widget_rect.collidepoint(pygame.mouse.get_pos())
for event in PyGUI._events:
if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1 and hovered:
clicked = True
color = PyGUI.BUTTON_HOVER_COLOR if hovered else PyGUI.BUTTON_COLOR
# Record the draw command (drawing relative to the window surface).
def draw_command(surf):
btn_rect = pygame.Rect(local_x, local_y, width, height)
pygame.draw.rect(surf, color, btn_rect, border_radius=4)
text_surf = PyGUI._font.render(label, True, PyGUI.WIDGET_TEXT_COLOR)
text_rect = text_surf.get_rect(center=btn_rect.center)
surf.blit(text_surf, text_rect)
context['commands'].append(draw_command)
context['local_y'] += height + PyGUI.SPACING
context['content_height'] = max(context['content_height'],
context['local_y'] + PyGUI.PADDING)
needed_width = local_x + width + PyGUI.PADDING
if needed_width > context['width']:
context['width'] = needed_width
return clicked
@staticmethod
def Slider(label, value, min_val, max_val, width=150, height=20):
"""
Create a slider widget; returns the updated value.
A label is drawn above the slider track.
"""
context = PyGUI._current_window()
if context is None:
return value
local_x = PyGUI.PADDING
local_y = context['local_y'] + PyGUI.PADDING
abs_x = context['x'] + local_x
abs_y = context['y'] + PyGUI.TITLE_BAR_HEIGHT + local_y
# Render label (with current value) and get its height.
label_surf = PyGUI._font.render(f"{label}: {value:.2f}", True, PyGUI.WIDGET_TEXT_COLOR)
label_height = label_surf.get_height()
# Define slider track's absolute rectangle.
track_rect = pygame.Rect(abs_x, abs_y + label_height + 2, width, height)
new_value = value
if track_rect.collidepoint(pygame.mouse.get_pos()) and pygame.mouse.get_pressed()[0]:
mouse_x = pygame.mouse.get_pos()[0]
relative = (mouse_x - track_rect.x) / track_rect.width
relative = max(0.0, min(1.0, relative))
new_value = min_val + relative * (max_val - min_val)
relative = (new_value - min_val) / (max_val - min_val)
def draw_command(surf):
# Draw the label.
surf.blit(label_surf, (local_x, local_y))
# Draw the slider track.
local_track_rect = pygame.Rect(local_x, local_y + label_height + 2, width, height)
pygame.draw.rect(surf, PyGUI.SLIDER_TRACK_COLOR, local_track_rect, border_radius=4)
# Draw the knob.
knob_rect = pygame.Rect(local_track_rect.x + int(relative * local_track_rect.width) - 5,
local_track_rect.y - 2, 10, local_track_rect.height + 4)
pygame.draw.rect(surf, PyGUI.SLIDER_KNOB_COLOR, knob_rect, border_radius=4)
context['commands'].append(draw_command)
total_height = label_height + 2 + height + PyGUI.SPACING
context['local_y'] += total_height
context['content_height'] = max(context['content_height'],
context['local_y'] + PyGUI.PADDING)
if local_x + width + PyGUI.PADDING > context['width']:
context['width'] = local_x + width + PyGUI.PADDING
return new_value
@staticmethod
def Checkbox(label, value):
"""
Create a checkbox widget; returns the toggled boolean value.
"""
context = PyGUI._current_window()
if context is None:
return value
local_x = PyGUI.PADDING
local_y = context['local_y'] + PyGUI.PADDING
abs_x = context['x'] + local_x
abs_y = context['y'] + PyGUI.TITLE_BAR_HEIGHT + local_y
box_size = 20
box_rect = pygame.Rect(abs_x, abs_y, box_size, box_size)
new_value = value
hovered = box_rect.collidepoint(pygame.mouse.get_pos())
for event in PyGUI._events:
if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1 and hovered:
new_value = not value
def draw_command(surf):
local_box_rect = pygame.Rect(local_x, local_y, box_size, box_size)
pygame.draw.rect(surf, PyGUI.CHECKBOX_BG_COLOR, local_box_rect, border_radius=3)
if new_value:
pygame.draw.line(surf, PyGUI.CHECKBOX_CHECK_COLOR,
(local_box_rect.left, local_box_rect.top),
(local_box_rect.right, local_box_rect.bottom), 2)
pygame.draw.line(surf, PyGUI.CHECKBOX_CHECK_COLOR,
(local_box_rect.left, local_box_rect.bottom),
(local_box_rect.right, local_box_rect.top), 2)
text_surf = PyGUI._font.render(label, True, PyGUI.WIDGET_TEXT_COLOR)
surf.blit(text_surf, (local_box_rect.right + 5, local_box_rect.top))
context['commands'].append(draw_command)
text_surf = PyGUI._font.render(label, True, PyGUI.WIDGET_TEXT_COLOR)
line_height = max(box_size, text_surf.get_height())
context['local_y'] += line_height + PyGUI.SPACING
context['content_height'] = max(context['content_height'],
context['local_y'] + PyGUI.PADDING)
needed_width = local_x + box_size + 5 + text_surf.get_width() + PyGUI.PADDING
if needed_width > context['width']:
context['width'] = needed_width
return new_value

View File

@ -1,7 +1,7 @@
import pygame
import sys
import math
import pygui
from pygui import PyGUI
from pygui_pygame_backend import Render, Rect, get_mouse_pos, get_mouse_pressed, MOUSEBUTTONDOWN, MOUSEBUTTONUP, MOUSEMOTION
import pygui_pygame_backend as backend
@ -13,7 +13,7 @@ clock = pygame.time.Clock()
# Create the backend renderer and assign the backend to PyGUI.
renderer = Render(screen)
pygui.PyGUI.set_backend(backend, renderer)
PyGUI.set_backend(backend, renderer)
# ---------------------
# Ball Simulation
@ -51,22 +51,22 @@ while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
pygui.PyGUI.handle_event(event)
PyGUI.handle_event(event)
screen.fill((30, 30, 30))
# Draw a draggable, autosizing GUI window.
pygui.PyGUI.Begin("Ball Controls", 10, 10)
ball_speed = pygui.PyGUI.Slider("Speed", ball_speed, 1, 10)
ball_direction = pygui.PyGUI.Slider("Direction", ball_direction, 0, 360)
ball_radius = pygui.PyGUI.Slider("Radius", ball_radius, 10, 100)
ball_color_r = pygui.PyGUI.Slider("Red", ball_color_r, 0, 255)
ball_color_g = pygui.PyGUI.Slider("Green", ball_color_g, 0, 255)
ball_color_b = pygui.PyGUI.Slider("Blue", ball_color_b, 0, 255)
bounce_enabled = pygui.PyGUI.Checkbox("Bounce", bounce_enabled)
if pygui.PyGUI.Button("Reset Position"):
PyGUI.Begin("Ball Controls", 10, 10)
ball_speed = PyGUI.Slider("Speed", ball_speed, 1, 10)
ball_direction = PyGUI.Slider("Direction", ball_direction, 0, 360)
ball_radius = PyGUI.Slider("Radius", ball_radius, 10, 100)
ball_color_r = PyGUI.Slider("Red", ball_color_r, 0, 255)
ball_color_g = PyGUI.Slider("Green", ball_color_g, 0, 255)
ball_color_b = PyGUI.Slider("Blue", ball_color_b, 0, 255)
bounce_enabled = PyGUI.Checkbox("Bounce", bounce_enabled)
if PyGUI.Button("Reset Position"):
ball.pos = [WIDTH // 2, HEIGHT // 2]
pygui.PyGUI.End()
PyGUI.End()
rad = math.radians(ball_direction)
ball.velocity[0] = ball_speed * math.cos(rad)
@ -81,7 +81,7 @@ while running:
ball.draw(screen)
pygame.display.flip()
pygui.PyGUI.update()
PyGUI.update()
pygame.quit()
sys.exit()