【问题标题】:pyagme screen not working with multithreadingpygame屏幕不适用于多线程
【发布时间】:2019-07-22 03:09:41
【问题描述】:

我正在使用 python 2.7.14,目前正在尝试使用 multiproccesing 模块同时绘制到 pygame 屏幕的两侧(2 个线程正在从单个 pygame 屏幕调用函数)但每次我从屏幕调用函数时(比如以screen.get_width() 为例)引发以下错误:

Process Process-1:
Traceback (most recent call last):
  File "C:\Python27\lib\multiprocessing\process.py", line 267, in _bootstrap
    self.run()
  File "C:\Python27\lib\multiprocessing\process.py", line 114, in run
    self._target(*self._args, **self._kwargs)
  File "C:\Python27\multiplayer.py", line 9, in single_core_game
    print screen.get_width()
error: display Surface quit

我知道从线程写入不是最优雅的方式,所以我很高兴听到替代方案。

谢谢!

【问题讨论】:

  • 如果您不发布代码,没有人可以回答您的问题。
  • 多处理实际上是创建单独的进程,而不是线程。所以我的猜测是你没有正确共享屏幕资源。

标签: python multithreading python-2.7 pygame pygame-surface


【解决方案1】:

作为@Fredrik cmets,您可能想要使用线程,而不是单独的进程。线程共享对内存和变量等的访问,而单独的进程从子进程启动的那一刻起拥有单独的副本。

所以,如果我可以重新表述您的问题:

如何从多个线程绘制到 pygame 屏幕?

简短的回答是:“你不知道”。

通常,对于事件驱动、基于窗口的桌面,程序有一个线程来管理与用户的交互,包括处理屏幕的输入和输出。当然,由于Python GIL,您可能能够从多个线程调用screen.blit(...),但这不是一个好的设计路径。

但是!这并不是说其他​​线程和进程不能为显示创建内容,而是将其交给主处理程序线程以将最终的 blit 传送到屏幕,那怎么办?

独立的 python 进程可以通过 ClientListener pipes 相互通信(这也绕过了 GIL),并且如前所述,独立的线程可以共享内存。

这是一段杂乱无章的代码,它将背景图像渲染到屏幕外的表面,然后在每次新更新准备好时向主线程发布一个事件。显然这是一个微不足道的使用线程,但如果有一个更耗时的更新过程,它会更好。

thread 函数最初创建了一个 pygame Surface,使其看起来像是漆黑的空间的 8 位再现。然后永远更多地平移图像,通过事件队列将副本作为 Pygame.Event 发送到主线程。主线程看到这个事件,并更新它的背景图片。

结果有点生涩,但这是因为我在每次迭代中让线程休眠 500 毫秒以减慢它的速度。

import threading
import pygame
import random
import time
import sys

# Window size
WINDOW_WIDTH=400
WINDOW_HEIGHT=400
DARK_GREY   = (  50,  50,  50 )
SPACE_BLACK = (   0,   0,  77 )
STAR_WHITE  = ( 255, 252, 216 )


### Thread that paints a background image to an off-screen surface
### then posts an event to the main loop when the image is ready
### for displaying.
class BackgroundDrawThread( threading.Thread ):
    def __init__( self ):
        threading.Thread.__init__(self)
        self.daemon         = True # exit with parent
        self.sleep_event    = threading.Event()
        self.ofscreen_block = pygame.Surface( ( WINDOW_WIDTH, WINDOW_HEIGHT ) )
        self.pan_pixels     = 5

    def makeSpace( self ):
        """ Make a starry background """
        # Inky blackness of space
        self.ofscreen_block.fill( SPACE_BLACK )
        # With some (budget-minded) stars
        for i in range( 80 ):
            random_pixel = ( random.randrange( WINDOW_WIDTH ), random.randrange( WINDOW_HEIGHT ) )
            self.ofscreen_block.set_at( random_pixel, STAR_WHITE )

    def panSpace( self ):
        """ Shift space left, by some pixels, wrapping the image """
        rect_to_move = [0, 0, self.pan_pixels, WINDOW_HEIGHT-1]
        lost_part = self.ofscreen_block.subsurface( rect_to_move ).copy()
        self.ofscreen_block.scroll( dx=-self.pan_pixels, dy=0)
        self.ofscreen_block.blit( lost_part, ( WINDOW_WIDTH-1-self.pan_pixels,0) )

    def run( self ):
        """ Do Forever (or until interuppted) """
        # Make the space backdrop
        self.makeSpace()
        while ( True ):
            if ( True == self.sleep_event.wait( timeout=0.5 ) ):
                break # sleep was interrupted by self.stop()
            else:
                # Rotate space a bit
                self.panSpace()

                # Message the main-thread that space has been panned.
                new_event_args = { "move": self.pan_pixels, "bitmap": self.ofscreen_block.copy() }
                new_event = pygame.event.Event( pygame.USEREVENT + 1, new_event_args )
                pygame.event.post( new_event )

    def stop( self ):
        self.sleep_event.set() # interrupt the wait
        self.join()



### MAIN
pygame.init()
pygame.display.set_caption("Threaded Paint")
WINDOW  = pygame.display.set_mode( ( WINDOW_WIDTH, WINDOW_HEIGHT ), pygame.HWSURFACE|pygame.DOUBLEBUF|pygame.RESIZABLE )

# Start the render-my-background thread
thread1 = BackgroundDrawThread()
thread1.start()
background = None

# Main paint / update / event loop
done = False
clock = pygame.time.Clock()
while ( not done ):

    # Handle Events
    for event in pygame.event.get():
        if ( event.type == pygame.QUIT ):
            done = True

        elif ( event.type == pygame.USEREVENT + 1 ):
            background = event.bitmap   
            print( "Space folded by %d pixels" % ( event.move ) )

    # Paint the window
    if ( background != None ): # wait for the first backgroun to be ready message
        WINDOW.blit( background, (0,0) )
    pygame.display.flip()

    # Max FPS
    clock.tick_busy_loop(60)

thread1.stop()
pygame.quit()

嗯,看动画,好像我在空间折叠方面有一个偏离 1 的错误。我猜代码需要更多spice

【讨论】:

    猜你喜欢
    • 2015-09-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多