-
-
Notifications
You must be signed in to change notification settings - Fork 192
Add window example #2626
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add window example #2626
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
#!/usr/bin/env python | ||
""" pygame.examples.window | ||
|
||
Demonstrates the new Window API, which can be used in place of | ||
pygame.display.set_mode, providing more control and an object oriented | ||
interface. | ||
""" | ||
import pygame | ||
|
||
WIN_MOVE_SPEED = 25 | ||
WIN_GROW_SPEED = 15 | ||
COLOR_PROGRESSION = ["cadetblue2", "darkorange2", "lightslateblue", "seagreen"] | ||
UNHOVERED_OPACITY = 0.8 | ||
SHOW_WINDOW = pygame.event.custom_type() | ||
|
||
pygame.init() | ||
pygame.key.set_repeat(500, 100) | ||
|
||
main_window = pygame.Window("demo window", (500, 500), resizable=True) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. could the main window also be Another trivial note, how about setting an icon with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. At least on Windows, the spawned windows spawn in the center of the main window (I'm not controlling the spawn point). So if the main window was always_on_top it would seem like the spawn window command does nothing, because the other windows would not be visible unless you move the main window with the mouse. I was looking around the examples data folder for art to use for this, (icons, backgrounds), and none of it spoke to me. I don't feel the need to set an icon in this example. |
||
main_surface = main_window.get_surface() | ||
windows_and_surfaces = [(main_window, main_surface)] | ||
|
||
instructions = """Welcome to the window demo! | ||
Controls: | ||
m.) maximize main window | ||
n.) minimize main window | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In addition to maximise/minimise, could also two keys for add/remove full screen? |
||
r.) restore main window | ||
(from being minimized or maximized) | ||
h.) hide main window for 1 second | ||
s.) spawn a new window | ||
arrow keys.) move window(s) around screen | ||
escape.) destroy the most recently created window | ||
1.) shrink all windows | ||
2.) grow all windows | ||
""" | ||
|
||
font = pygame.font.SysFont("Arial", 24) | ||
rendered_instructions = font.render(instructions, True, "black") | ||
|
||
# Make sure instructions can be shown | ||
main_window.minimum_size = rendered_instructions.get_size() | ||
|
||
clock = pygame.Clock() | ||
running = True | ||
|
||
while running: | ||
for event in pygame.event.get(): | ||
# If there are multiple windows, QUIT fires when the last one is destroyed | ||
if event.type == pygame.QUIT: | ||
running = False | ||
|
||
if event.type == pygame.WINDOWCLOSE: | ||
index = [win_surface[0] for win_surface in windows_and_surfaces].index( | ||
event.window | ||
) | ||
del windows_and_surfaces[index] | ||
event.window.destroy() | ||
|
||
if event.type == pygame.WINDOWENTER: | ||
if event.window != main_window: | ||
try: | ||
event.window.opacity = 1.0 | ||
except pygame.error: | ||
pass | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How about adding a |
||
|
||
if event.type == pygame.WINDOWLEAVE: | ||
# Test not None because WINDOWLEAVE will trigger when a window | ||
# closes, can't set opacity of now-nonexistent Window. | ||
if event.window != main_window and event.window is not None: | ||
try: | ||
event.window.opacity = UNHOVERED_OPACITY | ||
except pygame.error: | ||
pass | ||
|
||
if event.type == SHOW_WINDOW: | ||
main_window.show() | ||
|
||
if event.type == pygame.KEYDOWN: | ||
if event.key == pygame.K_ESCAPE: | ||
win, _ = windows_and_surfaces.pop() | ||
win.destroy() | ||
|
||
# Destroying the windows will not automatically do a QUIT on | ||
# last window, unlike closing the windows manually. | ||
if len(windows_and_surfaces) == 0: | ||
pygame.event.post(pygame.Event(pygame.QUIT)) | ||
|
||
if event.key == pygame.K_m: | ||
main_window.maximize() | ||
|
||
if event.key == pygame.K_n: | ||
main_window.minimize() | ||
|
||
if event.key == pygame.K_r: | ||
main_window.restore() | ||
|
||
if event.key == pygame.K_h: | ||
main_window.hide() | ||
pygame.time.set_timer(SHOW_WINDOW, 1000, 1) | ||
|
||
if event.key == pygame.K_s: | ||
win = pygame.Window("spawned window", (300, 300)) | ||
try: | ||
win.opacity = UNHOVERED_OPACITY | ||
except pygame.error: | ||
pass | ||
windows_and_surfaces.append((win, win.get_surface())) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. API commentThis has gotten me thinking about the API. As a user, I think it would be much neater if we could do something like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well I think I could be calling get_surface() every time I need the surface. I'm just not sure how that works SDL-side. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is also why I thought of subclassing window, issue raised as #2625. In this demo it would be nice to store the color of the window with the window. (Store arbitrary things in instance of class, subclass) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So, SDL stores the surface in the window struct (both in SDL2 and SDL3), and if the surface needs updating it updates the surface, so for most of the calls to this function it is just a cheap struct member access. On the python side, we could basically the change this There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here is a commit on a branch I made on top of yours, implementing my idea: 6b620f1 I also updated this example to see/test the property in action |
||
|
||
if event.key == pygame.K_UP: | ||
for win, _ in windows_and_surfaces: | ||
win.position += pygame.Vector2(0, -WIN_MOVE_SPEED) | ||
|
||
if event.key == pygame.K_DOWN: | ||
for win, _ in windows_and_surfaces: | ||
win.position += pygame.Vector2(0, WIN_MOVE_SPEED) | ||
|
||
if event.key == pygame.K_LEFT: | ||
for win, _ in windows_and_surfaces: | ||
win.position += pygame.Vector2(-WIN_MOVE_SPEED, 0) | ||
|
||
if event.key == pygame.K_RIGHT: | ||
for win, _ in windows_and_surfaces: | ||
win.position += pygame.Vector2(WIN_MOVE_SPEED, 0) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. API commentSMH my head, Nothing moves when I press the arrow keys. 😢 All these "direct wayland" issues are fine/ignorable for now because these issues don't happen under XWayland which SDL2 picks by default, but this could be a problem in the SDL3 era if SDL3 devs decide to do wayland-default. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is by design. Wayland does not want applications to know this for API design and security reasons. There is nothing SDL3 can do about it short of sending patches to KDE and GNOME and sway There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do I need to add try/except pygame.error blocks around them then? I did it for opacity given your previous report. I'm also unsure if we really want to raise an exception if the operation is not supported. We could return True/False about success? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In this case the position is silently ignored by SDL, like I think for this example, we should not have this position-moving thing as a highlight, and make it more low-key somehow? |
||
|
||
if event.key == pygame.K_1: | ||
for win, _ in windows_and_surfaces: | ||
win.size -= pygame.Vector2(WIN_GROW_SPEED, WIN_GROW_SPEED) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. API commentContinuously pressing 1 leads to the error Interestingly, this issue does not happen for the "main window" which has This could be an API inconsistency? I'd like an error to be not raised here and it should just cap at There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's unclear reading your comment whether you see the It would happen to the main window if it was ever told to do a negative size there. Which it can't because the minimum size dimensions are higher than WIN_GROW_SPEED. I agree it shouldn't be possible to crash the example by messing around with it, but I'm not sure what the best solution would be. |
||
|
||
if event.key == pygame.K_2: | ||
for win, _ in windows_and_surfaces: | ||
win.size += pygame.Vector2(WIN_GROW_SPEED, WIN_GROW_SPEED) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. might also be a good idea to cap it to some upper bound? Perhaps using the |
||
|
||
for i, win_surface in enumerate(windows_and_surfaces): | ||
win, surface = win_surface | ||
|
||
surface.fill(COLOR_PROGRESSION[win.id % len(COLOR_PROGRESSION)]) | ||
if surface == main_surface: | ||
centered_rect = rendered_instructions.get_rect( | ||
center=pygame.Vector2(win.size) / 2 | ||
) | ||
surface.blit(rendered_instructions, centered_rect) | ||
win.flip() | ||
|
||
clock.tick(144) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would be a small and nice addition to set FPS in the title for the main window, also serves as a demonstration of the Also how about something like |
||
|
||
pygame.quit() |
Uh oh!
There was an error while loading. Please reload this page.