Python 如果程序运行缓慢,Pygame窗口将不响应
我正在写一个程序来和电脑下棋。我有一个Pygame窗口,显示棋盘,让你选择你的移动。一旦你玩了你的招式,引擎就会开始计算招式树,以找到最好的招式。当它完成时,它会用它的移动进行回放。 问题是,当引擎“思考”时,Pygame窗口有一段时间没有更新(可能会更新很多,甚至一分钟或更长)。操作系统在不活动5秒后在窗口上提示消息“无响应”。 以下是一个最小可复制示例:Python 如果程序运行缓慢,Pygame窗口将不响应,python,windows,pygame,Python,Windows,Pygame,我正在写一个程序来和电脑下棋。我有一个Pygame窗口,显示棋盘,让你选择你的移动。一旦你玩了你的招式,引擎就会开始计算招式树,以找到最好的招式。当它完成时,它会用它的移动进行回放。 问题是,当引擎“思考”时,Pygame窗口有一段时间没有更新(可能会更新很多,甚至一分钟或更长)。操作系统在不活动5秒后在窗口上提示消息“无响应”。 以下是一个最小可复制示例: import pygame from time import sleep def engine_play(): # Here t
import pygame
from time import sleep
def engine_play():
# Here the engine searches for the best move for some time
for _ in range(1000000000):
a = 1+1
pygame.init()
clock = pygame.time.Clock()
clock.tick(60)
WIN = pygame.display.set_mode((500, 500))
engine_play()
# When the engine stops searching we get the events
pygame.event.get()
当引擎停止并调用pygame.event.get()
时,消息消失,一切正常。
主要问题是,如果在此期间单击窗口,Windows会警告您程序没有响应,并询问您是要关闭它还是等待
有人知道如何解决这个问题吗?提前谢谢 您需要确保操作系统窗口事件正在被处理,否则您会遇到可怕的“无响应”问题,正是因为您没有响应事件 一般来说,游戏总是有一个主循环,它一直在运行,并泵送事件、更新游戏逻辑、绘制屏幕等等。(根据应用程序的复杂性和设计方式,菜单可能会有另一个循环,但这不是重点。) 运用线程 要在程序中并发运行,您可能认为可以在另一个线程中泵送这些事件,但使用Pygame不安全,因此您需要在辅助线程中执行所有其他操作: 警告:
pygame.event.pump()
只应在初始化pygame.display的线程中调用
使用线程会有点麻烦,因为没有直接的方法从线程返回值,并且知道线程是否完成也需要一个事件。像这样的东西(可能有童车)应该可以解决问题
import threading
def engine_play(finish_event, output_list):
# Here the engine searches for the best move for some time
a = 0
for _ in range(1000000000):
a += _
output_list.append(a) # could also use a queue
finish_event.set()
def run_engine_play():
finish_event = threading.Event()
output_list = []
thread = threading.Thread(target=engine_play, args=(finish_event, output_list))
thread.start()
return (finish_event, output_list)
finish_event, output_list = run_engine_play()
while True:
for event in pygame.event.get():
pass # handle events...
if finish_event.is_set():
print(output_list[0])
使用期货
您可以通过使用来抽象线程的复杂性,但要点是:您有一项任务(未来)需要等待完成
使用发电机
您可以将engine\u play()
转换为一个函数,以避免使用线程,但您需要注意的是,引擎函数不时将控制权返回主循环
def engine_play():
# Here the engine searches for the best move for some time
a = 0
for _ in range(1000000000):
a += _
if _ % 1000 == 0: # every now and then, let the loop do other things
yield None # Not done yet...
yield a
# Instantiate the generator
engine_play_gen = engine_play()
while True:
for event in pygame.event.get():
pass # handle events...
val = next(engine_play_gen) # let the engine do its thing
if val is not None:
print(val)
有点混乱的解决方案,但它忽略了线程的使用。在
engine\u play
函数中,运行循环。在更新之前等待循环完成会导致程序出现大量延迟,足以让操作系统认为程序没有响应
这是它变得更有趣的地方。只要你在处理事件,操作系统就会认为你在响应。因此,您所要做的就是在整个程序中间歇性地进行一些事件调用。例如:
def engine_play():
# Here the engine searches for the best move for some time
for _ in range(1000000000):
a = a+1
if a%500 == 0:
pygame.event.get()
此函数每500个周期调用一次事件轮询器。这是足够的时间让操作系统认为你仍然在重新绑定,即使你没有
上述代码的问题在于您正在丢弃事件。如果要捕获这些事件,请创建自己的事件队列,并从中进行处理:
queue = []
def engine_play():
global queue
# Here the engine searches for the best move for some time
for _ in range(1000000000):
a = a+1
if a%500 == 0:
queue += pygame.event.get()
...
while running:
for event in (pygame.event.get()+queue):
queue = []
#handle events
在上面的程序中,它维护一个丢失的所有事件的列表,并在程序返回主循环时与当前事件一起处理。多线程:让引擎运行一个单独的线程,主线程继续主循环。另请参阅,非常感谢!我实现了您的解决方案并解决了问题。我非常确定调用
pygame.event.poll()
就足够了。这不会丢弃队列,因此get
以后仍然有完整的队列可用。get
的队列大小限制为128,因此事件最终将被删除,特别是在搜索程序持续更长时间的情况下