python - 如果程序很慢,Pygame 窗口没有响应
问题描述
我正在编写一个程序来与计算机下棋。我有一个 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()
转换为生成器函数以避免使用线程,但您需要注意引擎函数不时将控制权交还给主循环。
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)
推荐阅读
- javascript - Electron - IPC 模块不从渲染器向主发送数据
- c - fork() 如何停止创建孩子?
- python - 如何使用pyspark和scala在时间范围内每分钟查找事件的发生
- node.js - SyntaxError:JSON 中的意外标记 #
- cartodb - 如何使用carto js修改在carto builder中创建的地图?
- javascript - 分离的 DOM 树内存泄漏
- node.js - nodejs - 那么如何在里面有请求?
- ios - iOS CALayer mask 奇怪的半像素线
- android - 如何修复 RecyclerView 的 StaggeredGridLayoutManager 在滚动时分离内容并随机飞行
- python - 在列表和字典的字典中查找字符串