From 02e42ad5d0ceafe563cdd9efad8f686d4de06d5a Mon Sep 17 00:00:00 2001 From: OusmBlueNinja <89956790+OusmBlueNinja@users.noreply.github.com> Date: Mon, 7 Apr 2025 11:37:34 -0500 Subject: [PATCH] Dear PyGui but broken --- .../__pycache__/pygui.cpython-311.pyc | Bin 0 -> 12461 bytes .../pygui_pygame_backend.cpython-311.pyc | Bin 0 -> 4146 bytes pygame-imgui/example.py | 87 ++++++++ pygame-imgui/pygui.py | 194 ++++++++++++++++++ pygame-imgui/pygui_pygame_backend.py | 59 ++++++ 5 files changed, 340 insertions(+) create mode 100644 pygame-imgui/__pycache__/pygui.cpython-311.pyc create mode 100644 pygame-imgui/__pycache__/pygui_pygame_backend.cpython-311.pyc create mode 100644 pygame-imgui/pygui_pygame_backend.py diff --git a/pygame-imgui/__pycache__/pygui.cpython-311.pyc b/pygame-imgui/__pycache__/pygui.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c9df036609865546fc349bffcca8f15d91a21b93 GIT binary patch literal 12461 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 WIDTH: + self.velocity[0] = -self.velocity[0] + if self.pos[1] - self.radius < 0 or self.pos[1] + self.radius > HEIGHT: + self.velocity[1] = -self.velocity[1] + + def draw(self, surface): + pygame.draw.circle(surface, self.color, (int(self.pos[0]), int(self.pos[1])), int(self.radius)) + +ball = Ball((WIDTH // 2, HEIGHT // 2), (4, 3), 30, (255, 100, 50)) +ball_speed = 4.0 +ball_direction = 0.0 +ball_radius = 30.0 +ball_color_r = 255.0 +ball_color_g = 100.0 +ball_color_b = 50.0 +bounce_enabled = True + +running = True +while running: + dt = clock.tick(60) / 1000.0 + for event in pygame.event.get(): + if event.type == pygame.QUIT: + running = False + pygui.PyGUI.handle_event(event) + + screen.fill((30, 30, 30)) + + # Draw a draggable, auto–sizing 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"): + ball.pos = [WIDTH // 2, HEIGHT // 2] + pygui.PyGUI.End() + + rad = math.radians(ball_direction) + ball.velocity[0] = ball_speed * math.cos(rad) + ball.velocity[1] = ball_speed * math.sin(rad) + ball.radius = ball_radius + ball.color = (int(ball_color_r), int(ball_color_g), int(ball_color_b)) + if not bounce_enabled: + ball.velocity[0] *= 0.5 + ball.velocity[1] *= 0.5 + + ball.update() + ball.draw(screen) + + pygame.display.flip() + pygui.PyGUI.update() + +pygame.quit() +sys.exit() diff --git a/pygame-imgui/pygui.py b/pygame-imgui/pygui.py index e69de29..fb48453 100644 --- a/pygame-imgui/pygui.py +++ b/pygame-imgui/pygui.py @@ -0,0 +1,194 @@ +# pygui.py: Immediate–mode GUI library. +# This file contains no direct Pygame calls—it relies entirely on functions supplied by the backend. +# The backend must supply: Rect, get_mouse_pos, get_mouse_pressed, +# and event type constants: MOUSEBUTTONDOWN, MOUSEBUTTONUP, MOUSEMOTION. + +class Window: + TITLE_HEIGHT = 30 + PADDING = 5 + SPACING = 5 + + def __init__(self, title, x, y, width=None, height=None): + self.title = title + self.x = x + self.y = y + self.width = width if width is not None else 200 + # Start with a minimal height if none is provided. + self.height = height if height is not None else (self.TITLE_HEIGHT + self.PADDING) + self.cursor_y = self.y + self.TITLE_HEIGHT + self.PADDING + self.dragging = False + self.drag_offset = (0, 0) + + @property + def rect(self): + return Window.backend.Rect(self.x, self.y, self.width, self.height) + + @property + def title_rect(self): + return Window.backend.Rect(self.x, self.y, self.width, self.TITLE_HEIGHT) + + def handle_event(self, ev): + # ev is a dictionary with keys: type, button, pos. + if ev.get('type') == 'MOUSEBUTTONDOWN': + if ev.get('button') == 1 and self.title_rect.collidepoint(ev.get('pos')): + self.dragging = True + self.drag_offset = (ev.get('pos')[0] - self.x, ev.get('pos')[1] - self.y) + elif ev.get('type') == 'MOUSEBUTTONUP': + if ev.get('button') == 1: + self.dragging = False + elif ev.get('type') == 'MOUSEMOTION': + if self.dragging: + self.x = ev.get('pos')[0] - self.drag_offset[0] + self.y = ev.get('pos')[1] - self.drag_offset[1] + self.cursor_y = self.y + self.TITLE_HEIGHT + self.PADDING + + def begin(self, render): + render.draw_rect((50, 50, 50), self.rect.rect) + render.draw_rect((70, 70, 70), self.title_rect.rect) + render.draw_text(self.title, (self.x + self.PADDING, self.y + 5)) + self.cursor_y = self.y + self.TITLE_HEIGHT + self.PADDING + + def layout(self): + return (self.x + self.PADDING, self.cursor_y) + + def next(self, widget_height): + self.cursor_y += widget_height + self.SPACING + +class PyGUI: + # These are assigned via set_backend. + backend = None + renderer = None + + current_window = None + windows = [] + active_slider = None # Stores (window_id, label, offset) + prev_mouse = False # For one–click widget toggling + + @staticmethod + def set_backend(backend, renderer): + """ + backend: an object that provides Rect, get_mouse_pos, get_mouse_pressed, + and event type constants: MOUSEBUTTONDOWN, MOUSEBUTTONUP, MOUSEMOTION. + renderer: an instance of the backend’s Render class. + """ + PyGUI.backend = backend + PyGUI.renderer = renderer + Window.backend = backend + + @staticmethod + def Begin(title, x, y, width=None, height=None): + win = Window(title, x, y, width, height) + PyGUI.current_window = win + PyGUI.windows.append(win) + win.begin(PyGUI.renderer) + + @staticmethod + def End(): + if PyGUI.current_window: + win = PyGUI.current_window + # Auto–resize window height to enclose all widgets. + win.height = win.cursor_y - win.y + win.PADDING + PyGUI.current_window = None + + @staticmethod + def Label(text): + if not PyGUI.current_window: + return + win = PyGUI.current_window + pos = win.layout() + PyGUI.renderer.draw_text(text, pos) + win.next(20) + + @staticmethod + def Button(text): + if not PyGUI.current_window: + return False + win = PyGUI.current_window + btn_rect = PyGUI.backend.Rect(win.x + win.PADDING, win.cursor_y, win.width - 2 * win.PADDING, 25) + PyGUI.renderer.draw_rect((100, 100, 100), btn_rect.rect) + PyGUI.renderer.draw_text(text, (btn_rect.x + 5, btn_rect.y + 5)) + clicked = False + mouse_pos = PyGUI.backend.get_mouse_pos() + if btn_rect.collidepoint(mouse_pos): + PyGUI.renderer.draw_rect((150, 150, 150), btn_rect.rect, border=2) + if PyGUI.backend.get_mouse_pressed()[0]: + clicked = True + win.next(25) + return clicked + + @staticmethod + def Slider(label, value, min_val, max_val): + if not PyGUI.current_window: + return value + win = PyGUI.current_window + pos = win.layout() + text = f"{label}: {value:.2f}" + PyGUI.renderer.draw_text(text, pos) + win.next(20) + slider_width = win.width - 2 * win.PADDING + slider_rect = PyGUI.backend.Rect(win.x + win.PADDING, win.cursor_y, slider_width, 10) + PyGUI.renderer.draw_rect((100, 100, 100), slider_rect.rect) + norm = (value - min_val) / (max_val - min_val) + knob_x = win.x + win.PADDING + norm * slider_width + knob_rect = PyGUI.backend.Rect(knob_x - 5, win.cursor_y - 5, 10, 20) + PyGUI.renderer.draw_rect((200, 200, 200), knob_rect.rect) + + slider_id = (id(win), label) + mp = PyGUI.backend.get_mouse_pos() + mp_pressed = PyGUI.backend.get_mouse_pressed()[0] + # Start slider drag if none active and mouse is pressed inside slider_rect. + if PyGUI.active_slider is None and slider_rect.collidepoint(mp) and mp_pressed: + offset = mp[0] - knob_x + PyGUI.active_slider = (slider_id, offset) + if PyGUI.active_slider is not None and PyGUI.active_slider[0] == slider_id: + offset = PyGUI.active_slider[1] + rel = mp[0] - (win.x + win.PADDING) - offset + norm = max(0, min(rel / slider_width, 1)) + value = min_val + norm * (max_val - min_val) + if not mp_pressed: + PyGUI.active_slider = None + win.next(20) + return value + + @staticmethod + def Checkbox(label, value): + if not PyGUI.current_window: + return value + win = PyGUI.current_window + pos = win.layout() + box_rect = PyGUI.backend.Rect(win.x + win.PADDING, win.cursor_y, 20, 20) + PyGUI.renderer.draw_rect((100, 100, 100), box_rect.rect, border=2) + if value: + PyGUI.renderer.draw_rect((200, 200, 200), box_rect.rect) + PyGUI.renderer.draw_text(label, (box_rect.x + box_rect.width + 5, win.cursor_y)) + new_value = value + mp = PyGUI.backend.get_mouse_pos() + mp_pressed = PyGUI.backend.get_mouse_pressed()[0] + # Toggle only on the transition (mouse pressed now but not in the previous frame). + if box_rect.collidepoint(mp) and mp_pressed and not PyGUI.prev_mouse: + new_value = not value + win.next(20) + return new_value + + @staticmethod + def handle_event(event): + # Convert a backend event (e.g. a Pygame event) into a generic dictionary. + generic = {} + if event.type == PyGUI.backend.MOUSEBUTTONDOWN: + generic['type'] = 'MOUSEBUTTONDOWN' + generic['button'] = event.button + generic['pos'] = event.pos + elif event.type == PyGUI.backend.MOUSEBUTTONUP: + generic['type'] = 'MOUSEBUTTONUP' + generic['button'] = event.button + generic['pos'] = event.pos + elif event.type == PyGUI.backend.MOUSEMOTION: + generic['type'] = 'MOUSEMOTION' + generic['pos'] = event.pos + for win in PyGUI.windows: + win.handle_event(generic) + + @staticmethod + def update(): + # Call at end of frame to record the current mouse state (for one–click toggling). + PyGUI.prev_mouse = PyGUI.backend.get_mouse_pressed()[0] diff --git a/pygame-imgui/pygui_pygame_backend.py b/pygame-imgui/pygui_pygame_backend.py new file mode 100644 index 0000000..f098a01 --- /dev/null +++ b/pygame-imgui/pygui_pygame_backend.py @@ -0,0 +1,59 @@ +import pygame + +# Event type constants. +MOUSEBUTTONDOWN = pygame.MOUSEBUTTONDOWN +MOUSEBUTTONUP = pygame.MOUSEBUTTONUP +MOUSEMOTION = pygame.MOUSEMOTION + +class Render: + """ + Provides basic drawing functions via Pygame. + """ + def __init__(self, surface): + self.surface = surface + self.font = pygame.font.SysFont("Arial", 16) + + def draw_rect(self, color, rect, border=0): + pygame.draw.rect(self.surface, color, rect, border) + + def draw_text(self, text, pos, color=(255, 255, 255)): + text_surface = self.font.render(text, True, color) + self.surface.blit(text_surface, pos) + + def draw_line(self, color, start_pos, end_pos, width=1): + pygame.draw.line(self.surface, color, start_pos, end_pos, width) + + def draw_circle(self, color, center, radius, border=0): + pygame.draw.circle(self.surface, color, center, radius, border) + +class Rect: + """ + A simple rectangle wrapper. + """ + def __init__(self, x, y, width, height): + self.rect = pygame.Rect(x, y, width, height) + + @property + def x(self): + return self.rect.x + + @property + def y(self): + return self.rect.y + + @property + def width(self): + return self.rect.width + + @property + def height(self): + return self.rect.height + + def collidepoint(self, pos): + return self.rect.collidepoint(pos) + +def get_mouse_pos(): + return pygame.mouse.get_pos() + +def get_mouse_pressed(): + return pygame.mouse.get_pressed()