【问题标题】:Resize pygame window without losing keyboard state在不丢失键盘状态的情况下调整 pygame 窗口的大小
【发布时间】:2013-12-22 10:22:13
【问题描述】:

我在使用基于 SDL 1.2.15 的 pygame 1.9.2 时遇到问题:我看到的以编程方式调整窗口大小的唯一方法是 pygame.display.set_mode(...)。但是当我调用这个方法时,我的键盘状态会被重置,所以所有当前按下的键和修饰符都会发送KEYUP 事件,重复的键也会停止。

通过用户键盘状态手动调整窗口大小是完全正常的。事件队列在调整大小时暂停,然后出现 VIDEORESIZEVIDEOEXPOSE 事件,并且按键仍然被按下,如果是这样的话。

有几个问题:

  1. 有没有办法在调整窗口大小时保持正常的键盘状态?
  2. 如果没有,我想至少保留修饰键,但我不能只使用key.set_mods(),因为我无法获得用户释放键的时刻。
  3. 最后有没有办法直接调用 SDL 窗口的大小调整?

示例程序演示了不良行为。左右箭头将窗口大小调整为相同,但如果按下它们,则只有一个按键。如果按住,其他键会正常重复。输出是所有发生的事件的事件日志。

import pygame
import sys

# same size all the time
size = (200, 200)

pygame.init()
pygame.display.set_mode(size, pygame.RESIZABLE)

# if left or right arrow key is held nothing will happen despite this line
pygame.key.set_repeat(500, 200)

run = True
while run:
    for event in pygame.event.get():
        out = '%s\t%s'%(event.type, str(event.dict))
        if event.type == pygame.QUIT:
            pygame.display.quit()
            run = False
        if event.type == pygame.KEYDOWN:
            # mods are also reset on set_mode() call
            out += ', mods = %i' % (pygame.key.get_mods())
            if event.key == pygame.K_LEFT:
                pygame.display.set_mode(size, pygame.RESIZABLE)
            if event.key == pygame.K_RIGHT:
                pygame.display.set_mode(size, pygame.RESIZABLE)

        print(out)

    pygame.time.Clock().tick(60)

在 Win 机器上测试过,如果 linux 也失败了,很快就会报告。

UPD:我一直在研究 pygame 的源代码,但看不出原因,但与 pygame 的源代码捆绑在一起的文档指出,Pygame can only have a single display active at any time. Creating a new one with pygame.display.set_mode() will close the previous display 这意味着无法调整大小窗口,只重新创建它。我觉得有点奇怪。

UPD:我已经改变了我的程序行为,以便在用户完全释放键盘之前不会实际发生调整大小。它运行良好且不会破坏可用性。

不过,很高兴知道是否存在针对所述问题的解决方案。

【问题讨论】:

  • 真的很简单。将键的状态存储在您可以控制的数组中。
  • 你是说如果K_LEFTK_RIGHT同时被按下,那么只会找到一个KEYDOWN事件?
  • @Haz,不,如果按下并按住其中任何一个,就会有 一个 KEYDOWN 和一个 KEYUP

标签: python keyboard pygame sdl


【解决方案1】:

一种解决方案是,例如 self.已经提到,要存储键的状态,以便您可以忽略这个特定的键向上事件,而不是仅使用事件。然而,这可能有一个问题。如果您尝试创建一个按键控件来调整屏幕大小(您可能正在这样做),那么您也将忽略真实的按键事件。因为这种控制很少实现,所以似乎没有任何一种干净的解决方案,比如只改变屏幕的功能,或者抑制假键事件的功能。但是,您可以找到解决关键事件问题的方法。实际上有两种可行的解决方案。第一个(可能是两者中更糟糕的一个)是创建一种识别假事件的方法,并选择性地忽略关键事件。可能最简单的方法是在固定的时间内定期调整屏幕大小,以便您可以预期此时会出现错误的键盘事件,并忽略它。下面是阻止 a 键每秒调整大小的代码示例(具体来说,它假定每次系统时间具有精确的秒值时都会发生屏幕更新,而不是秒的小数部分):

import pygame
import datetime

a_keydown = False
for event in pygame.event.get():
    if item.type == pygame.KEYDOWN and item.key == pygame.K_a:
        a_keydown = True
        print "keydown event"
    if item.type == pygame.KEYUP and item.key == pygame.K_a:
        if datetime.datetime.now().time()[4] > 15:           #leaves a window of fifteen microseconds for the screen resize, this may need to be adjusted
        a_keydown = False
        print "key up event"

然而,这两种解决方案中更好的不是这个。假设由 set_mode 创建的 keyup 事件单独存在于 python 中,您可以从其他地方获取关键事件,例如 pywin32。尝试使用 win32api 模块和 GetKeyState() 函数来满足您的需要。这将为您提供密钥的最后一个已知状态。不如 python 事件流方便,但它可能是您最好的解决方案,因为 pygame 没有为此提供优雅的解决方案。

【讨论】:

  • 你说得对,为什么我不能存储状态:当用户释放密钥时它不会被设置为正常。手动获取键盘状态似乎是一个不错的解决方案,但缺乏可移植性。看来,如果没有 set_mode 就无法调整大小,那就是它了。
  • 嗯,有一种可能的解决方法,改变窗口对象的实际大小属性并调用某种更新。虽然这基本上正是您想要的,但没有任何功能可以满足您的需求,并且在网上环顾四周,看起来并没有很多关于该主题的文档。窗口对象甚至可能无法像那样修改......
  • 我想这是可能的,因为用户可以调整大小并且SDL本身就有这样的功能。我现在正在考虑发送 SDL 调整大小事件,但还没有找到合适的方法。
猜你喜欢
  • 1970-01-01
  • 2016-05-22
  • 2018-05-11
  • 1970-01-01
  • 1970-01-01
  • 2023-03-12
  • 1970-01-01
  • 2016-08-10
  • 1970-01-01
相关资源
最近更新 更多