如何让我的按钮、对象等大小依赖于窗口大小?

1 投票
3 回答
627 浏览
提问于 2025-06-18 03:57

我知道怎么让窗口可以调整大小,但如果我运行代码,只有窗口会变大或变小。里面的按钮、对象等却不会跟着一起变。那我该怎么做才能让它们也随着窗口的大小变化呢?如果这个问题有点难理解,你可以去看看我之前问的关于如何让窗口可调整大小的问题,那里有一些评论可以帮助你理解。这里是代码,给你们参考:

import pygame
from pygame.locals import *

pygame.init()

WINDOW_WIDTH = 800
WINDOW_HEIGHT = 600

SURFACE = pygame.HWSURFACE|pygame.DOUBLEBUF|pygame.RESIZABLE
win = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT), SURFACE)
win.fill((0, 180, 210))

pygame.display.set_caption("Baloon War!")
icon = pygame.image.load("Baloon war icon.png")
pygame.display.set_icon(icon)

centre_point = (WINDOW_WIDTH//2, WINDOW_HEIGHT//2)

class button():
    def __init__(self, color, x, y, width, height, text=''):
        self.color = color
        self.x = x
        self.y = y
        self.width = width
        self.height = height
        self.text = text

    def draw(self, win, outline=None):
        # Call this method to draw the button on the screen
        if outline:
            pygame.draw.rect(win, outline, (self.x - 2, self.y - 2, self.width + 4, self.height + 4), 0)

        pygame.draw.rect(win, self.color, (self.x, self.y, self.width, self.height), 0)

        if self.text != '':
            font = pygame.font.SysFont('comicsans', 60)
            text = font.render(self.text, 1, (0, 0, 0))
            win.blit(text, (self.x + (self.width / 2 - text.get_width() / 2), self.y + (self.height / 2 - text.get_height() / 2)))

    def isOver(self, pos):
        # Pos is the mouse position or a tuple of (x,y) coordinates
        if pos[0] > self.x and pos[0] < self.x + self.width:
            if pos[1] > self.y and pos[1] < self.y + self.height:
                return True

        return False

    def rescale(self):
        new_size = int(WINDOW_WIDTH * self.scale_factor)
        x, y = self.rect.center
        self.image = pygame.transform.smoothscale(self.original, (new_size, new_size))
        self.rect = self.image.get_rect()
        self.rect.center = (x, y)

def redrawMenuWindow():
    win.fill((0, 255, 110))
    greenButton.draw(win, (0, 0, 0))
    redButton.draw(win, (0, 0, 0))

def redrawGameWindow():
    win.fill((0, 150, 210))
    pygame.draw.rect(win, (0, 250, 110),(0, 450, 800, 250))




greenButton = button((0, 255, 0), 280, 255, 250, 100, "Start")
redButton = button ((255, 0, 0), 280, 380, 250, 100, "Quit")

game_state = "menu"
run = True
while run:
    if game_state == "menu":
        redrawMenuWindow()
    elif game_state == "game":
        redrawGameWindow()
    pygame.display.update()

    for event in pygame.event.get():
        pos = pygame.mouse.get_pos()

        if event.type == pygame.QUIT:
            run = False
            pygame.quit()
            quit()
        elif event.type == pygame.VIDEORESIZE:
            WINDOW_WIDTH = event.w
            WINDOW_HEIGHT = event.h
            win = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT), SURFACE)

        if event.type == pygame.MOUSEBUTTONDOWN:
            if greenButton.isOver(pos):
                print("clicked the button")
                game_state = "game"
            if redButton.isOver(pos):
                print("clicked the 2button")
                run = False
                pygame.quit()
                quit()


        if event.type == pygame.MOUSEMOTION:
            if greenButton.isOver(pos):
                greenButton.color = (105, 105, 105)
            else:
                greenButton.color = (0, 255, 0)
            if redButton.isOver(pos):
                redButton.color = (105, 105, 105)
            else:
                redButton.color = (255, 0, 0)

        pygame.display.update()

相关问题:

  • 暂无相关问题
暂无标签

3 个回答

3

希望下面的内容能帮你解决问题。在你的 Button 类里(类名首字母要大写),添加这个方法:

def resize_button(self, width, height, x, y):
        self.width = width
        self.height = height
        self.x = x
        self.y = y

然后,在 main_loop 中,当你捕捉到 VIDEORESIZE 事件时:

elif event.type == pygame.VIDEORESIZE:
            win = pygame.display.set_mode((event.w, event.h), pygame.RESIZABLE)
            greenButton.resize_button(event.w*0.3, event.h*0.3, event.w/2, event.h/2)

使用系数来乘以参数,以获得你想要的结果。可以多试试这个方法。主要的想法是让按钮的大小和位置相对于当前窗口的大小来调整。 另外,在你的 main_loop 中插入 clock.tick(FPS),并且别忘了在循环之前加上 clock = pygame.clock.Time()。这样你就可以控制每秒帧数(FPS),从而减少处理器的使用。

3

@kaktus_car 的回答是对的。代码需要根据窗口的大小来调整,重新缩放屏幕上的元素。

问题的根本在于,原作者的代码把窗口大小“死死地写死”了,即使接收到窗口大小变化的事件,新的窗口大小也被忘记了。这就意味着代码不知道窗口的实际大小,因此无法重新调整图形或控件的大小。

win = pygame.display.set_mode((800, 600), pygame.RESIZABLE)  # <-- FIXED SIZE

...

elif event.type == pygame.VIDEORESIZE:
    win = pygame.display.set_mode((event.w, event.h), pygame.RESIZABLE) # <-- NEW SIZE

这个问题相对简单可以解决。

第一步是要存储窗口的大小,并用它来缩放屏幕上的物体。不要写一个“50x50”的框,而是写一个占屏幕宽度12%的框。(每次都可以从表面获取大小,但我觉得这样有点麻烦。)

WINDOW_WIDTH      = 400
WINDOW_HEIGHT     = 400

SURFACE = pygame.HWSURFACE|pygame.DOUBLEBUF|pygame.RESIZABLE
window  = pygame.display.set_mode( ( WINDOW_WIDTH, WINDOW_HEIGHT ), SURFACE )

这就意味着有些事情要比简单地插入坐标要复杂一些。例如,窗口的中心在哪里,不是“200,200”,而是:

centre_point = ( WINDOW_WIDTH//2, WINDOW_HEIGHT//2 )

所以当窗口大小变化事件发生时,这些变量可以简单地更新,屏幕上的物体就会根据新的大小进行缩放:

# Handle user-input
for event in pygame.event.get():
    if ( event.type == pygame.QUIT ):
        done = True
    elif ( event.type == pygame.VIDEORESIZE ):
        WINDOW_WIDTH  = event.w
        WINDOW_HEIGHT = event.h
        window  = pygame.display.set_mode( ( WINDOW_WIDTH, WINDOW_HEIGHT ), SURFACE )

如果你有精灵对象,这些也可以重新缩放。记录下原始大小作为一个缩放因子。当窗口大小增大或减小时,将这个因子和新的精灵位图大小结合起来,就可以相应地重新缩放它。

class AlienSprite( pygame.sprite.Sprite ):

    def __init__( self, bitmap, x, y ):
        self.original     = bitmap
        self.image        = bitmap
        self.rect         = self.image.get_rect()
        self.rect.center  = ( x, y )
        # Keep the existing scale factor from startup to preserve the size ratio
        self.scale_factor = self.rect.width / WINDOW_WIDTH

    def rescale( self ):
        new_size = int( WINDOW_WIDTH * self.scale_factor )  # new size
        x, y = self.rect.center                             # existing centre position
        # Re-size the bitmap
        self.image = pygame.transform.smoothscale( self.original, ( new_size, new_size ) )
        self.rect  = self.image.get_rect()
        self.rect.center = ( x, y )                         # restore to the centre point

当发生 pygame.VIDEORESIZE 事件时:

    elif ( event.type == pygame.VIDEORESIZE ):
        WINDOW_WIDTH  = event.w
        WINDOW_HEIGHT = event.h
        window  = pygame.display.set_mode( ( WINDOW_WIDTH, WINDOW_HEIGHT ), SURFACE )

        for s in sprite_group:   # rescale all the sprites
            s.rescale()
0

使用 pygame.display.toggle_fullscreen() 这个命令就可以了。你可以查看它的说明文档(链接:https://www.pygame.org/docs/ref/display.html#pygame.display.toggle_fullscreen)。

if event.key == K_f:
    pygame.display.toggle_fullscreen()

你不需要做更多的事情。你可以做一个按钮,点击它后就可以使用这个命令。真的很简单!希望我能帮到你,老我! :) 我今天刚发现这个,所以想回答一下,让你和其他人也能找到这个答案!

撰写回答