首页 > 解决方案 > 如果程序很慢,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 会警告您该程序没有响应,并询问您是要关闭它还是等待。

有谁知道如何解决这一问题?提前致谢!

标签: pythonwindowspygame

解决方案


您将需要确保正在处理操作系统窗口事件,否则您确实会遇到可怕的“不响应”的事情,这正是因为您没有响应事件。

一般来说,游戏总是有一个主循环,它一直在运行并泵送事件、更新游戏逻辑、绘制屏幕等等。(根据应用程序的复杂性以及您设计事物的方式,菜单可能有另一个循环,但这不是重点。)

使用线程

要在程序中同时运行,您可能认为可以在另一个线程中泵送这些事件,但这对 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)

推荐阅读