pyagme屏幕不适用于多线程 [英] pyagme screen not working with multithreading

查看:92
本文介绍了pyagme屏幕不适用于多线程的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用python 2.7.14,当前正在尝试使用多处理模块同时绘制到pygame屏幕的两面(2个线程从一个pygame屏幕调用函数),但是每次我从screen调用函数时(例如screen.get_width()),将引发以下错误:

I am using python 2.7.14 and am currently trying to draw to 2 sides of a pygame screen simultaneously using the multiproccesing module(2 threads are calling functions from a single pygame screen) but everytime I call a function from screen(like screen.get_width() for example) the following error is raised:

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

我知道用线程写并不是最优雅的方式,所以我很高兴听到替代方法.

I know writing from threads isn't the most elegant way so I'll be glad to hear alternatives.

谢谢!

推荐答案

作为@Fredrik注释,您可能希望使用线程,而不是单独的进程.线程共享对内存的访问权限;变量等),而从子进程开始时,单独的进程具有单独的副本.

As @Fredrik comments, you probably want to use a thread, not a separate process. Threads share access to memory & variables etc., whereas separate processes have a separate copy from the point the child process was started.

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

So if I may re-phrase your question:

如何从多个线程吸引到pygame屏幕?

How do I draw to the pygame screen from multiple threads?

简短的回答是:您不要".

And to which the short-answer is: "You don't".

通常,对于事件驱动的,基于窗口的桌面,程序具有单个线程来管理与用户的交互,包括处理输入和输出到屏幕.由于 Python,确保您可能可以从多个线程调用screen.blit(...) GIL ,但这不是步行的好设计途径.

Generally, with event driven, window-based desktops, programs have a single thread that manages interaction with the user, including handling input and output to the screen. Sure you might be able to call screen.blit(...) from multiple threads because of the Python GIL, but it's not a good design path to be walking.

但是!那不是说其他​​线程和进程不能为显示内容 create 创建内容,而是将其移交给主处理程序线程以将最后的内容提交给屏幕,那又如何呢?

But! That is not to say other threads and processes cannot create content for the display, but then hand it off to the main handler thread for that final blit to the screen, so how?

单独的python进程可以与ClientListener

Separate python processes can communicate with each other with Client and Listener pipes (this gets around the GIL too), and as mentioned before, separate threads can just share memory.

这是一段繁琐的代码,它将背景图像渲染到屏幕外的表面,然后在每次新更新准备就绪时将事件发布到主线程.显然,这是对线程的琐碎使用,但是如果更新过程比较耗时,那么它会更合适.

Here's a kludgey piece of code that renders a background image to an off-screen surface, and then posts an event to the main thread when each new update is ready. Obviously it's a trivial use of a thread, but if there was a more time-consuming update process, it would fit better.

线程函数最初会创建一个pygame曲面,使其看起来像是空间的漆黑度的8位再现.然后遍历整个图像,通过事件队列将副本作为Pygame.Event发送到主线程.主线程会看到此事件,并更新它的背景图像.

The thread function initially creates a pygame Surface, making it look like an 8-bit rendition of the inky blackness of space. Then forever-more pans across the image, sending a copy as a Pygame.Event to the main thread via the event queue. The main thread sees this event, and updates it's background image.

结果有点生涩,但这是因为我在每次迭代中使线程休眠500毫秒,以使其速度变慢.

The result is kind-of jerky, but this is because I put the thread to sleep for 500ms on each iteration to slow it down a bit.

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比1的错误.我猜代码需要更多的香料.

Hmm, looking at the animation, it seems like I have an off-by-1 bug in the folding of space. I guess the code needs more spice.

这篇关于pyagme屏幕不适用于多线程的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆