如何解决如果程序很慢,Pygame windows 没有响应
我正在编写一个程序来与计算机下棋。我有一个 Pygame 窗口,显示棋盘并让您选择您的移动。一旦您完成了您的移动,引擎就会开始计算移动树,以找到最佳移动。当它完成时,它会重播它的移动。 问题是,当引擎在“思考”时,Pygame 窗口有一段时间没有更新(可能很多,甚至一分钟或更长时间)。并且在 5 秒不活动后,操作系统会在窗口上提示“无响应”消息。 这是一个最小的可重现示例:
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])
使用期货
您可以通过使用 concurrent.futures
来抽象出线程的复杂性,但要点是:您有一项任务(未来),您需要等待才能完成。
使用生成器
您可以将 engine_play()
转换为 generator 函数以避免使用线程,但您需要注意引擎函数时不时地将控制权交还给主循环。
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_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
在上面的程序中,它维护了一个所有遗漏事件的列表,并在程序返回主循环时将它们与当前事件一起处理。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。