首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >仅在从python套接字连接恢复数据后运行Pygame显示代码

仅在从python套接字连接恢复数据后运行Pygame显示代码
EN

Stack Overflow用户
提问于 2019-03-15 04:26:20
回答 2查看 164关注 0票数 2

我想写一个pygame程序,在那里我通过套接字连接从树莓派接收数据。但是,我只希望Pygame代码在字符串到达后运行。这涉及到代码等待一段时间,然后当从Raspberry Pi收到一个字符串时,pygame运行代码来更新显示。目前,我的Pygame在等待数据收入时冻结并关闭。任何想法。谢谢。

EN

回答 2

Stack Overflow用户

发布于 2019-03-15 04:36:28

Pygame“冻结”很可能是由于网络调用被阻塞。考虑旋转一个新线程来处理数据通信,同时Pygame接管主线程并使用一些“等待”消息更新显示。收到数据后,第二个线程应将其传递给将显示数据的主线程。

票数 0
EN

Stack Overflow用户

发布于 2019-03-15 08:09:30

这里有几个问题在起作用。正如@Josep Valls回答的那样,代码需要在一个子线程中进行套接字通信。此外,PyGame主线程需要不断轮询事件循环(可能还会重新绘制屏幕)。显然,如果代码在每个循环中没有做太多的事情,那么可能没有必要每次都像下面的示例那样重新绘制整个屏幕。

那么,需要什么来处理与PyGame的套接字通信呢?首先,您需要侦听套接字以接受连接。在本例中,ConnectionHandlerThread类就是这样做的。它侦听传入的客户端连接,一旦连接发生,它就会创建一个ConversationHandlerThread来对每个客户端执行读/写操作。(如果您要处理大量来来去去的套接字,可能需要一个线程池,但我也会尽量保持示例的简单性。)

因此,为了解释示例代码-它绘制了一个窗口,其中有几个AlienSprite在弹来弹去。客户端可以通过端口5555 (比如使用telnet)连接到它并发出命令。发送一个颜色词(红/绿/蓝)会产生一个新的外星人的颜色。quit命令将断开连接。

为了在套接字处理线程和PyGame主线程之间进行通信,套接字线程使用PyGame event.post()发送事件。这些用户事件已经在一个名为NetworkEventsenum中实现,以跟踪它们。这样做的好处是,当两个线程都在发布不同事件类型的pygame.USEREVENT + 27时,您不会感到困惑。

代码语言:javascript
复制
import threading
import pygame
import random
import enum
import socket
import select

# Window size
WINDOW_WIDTH=400
WINDOW_HEIGHT=400

DARK    = (  50, 50, 50 )
WHITE   = ( 255,255,255 )
RED     = ( 255, 55, 55 )
GREEN   = (   5,255, 55 )
BLUE    = (   5, 55,255 )

LISTEN_ADDRESS = '127.0.0.1'
LISTEN_PORT    = 5555


class NetworkEvents( enum.IntEnum ):
    CLIENT_CONNECTED = pygame.USEREVENT + 1
    CLIENT_HANGUP    = pygame.USEREVENT + 2
    CLIENT_MESSAGE   = pygame.USEREVENT + 3


class AlienSprite( pygame.sprite.Sprite ):
    """ A tiny little alien, which wanders around the screen """
    def __init__( self, colour ):
        pygame.sprite.Sprite.__init__(self)
        self.image = self.render( colour )
        self.rect  = self.image.get_rect()
        self.rect.center = ( random.randrange( 0, WINDOW_WIDTH ), random.randrange( 0, WINDOW_HEIGHT ) )

    def render( self, colour ):
        new_image = pygame.Surface( ( 7, 6 ), pygame.SRCALPHA )
        new_image.fill( (0,0,0,0) )
        pixels = [                     (3,1),               \
                                (2,2), (3,2), (4,2),        \
                            (1,3),     (3,3),      (5,3),   \
                                (2,4), (3,4), (4,4) ]
        for p in pixels:
            new_image.set_at( p, colour )
        return new_image

    def update( self ):
        self.rect.x += random.randrange( -2, 3 )
        self.rect.y += random.randrange( -2, 3 )
        # remove if we wander off-screen
        if ( self.rect.x < 0 or self.rect.x >= WINDOW_WIDTH or \
             self.rect.y < 0 or self.rect.x >= WINDOW_HEIGHT ):
            self.kill() 




class ConversationHandlerThread( threading.Thread ):
    """ A thread that handles a conversation with a single remote client.
        Accepts commands of 'red', 'green' or 'blue', and posts messages
        to the main PyGame thread for processing """
    def __init__( self, client_socket, client_address ):
        threading.Thread.__init__(self)
        self.client_socket  = client_socket
        self.client_address = client_address
        self.data_buffer    = ''
        self.daemon         = True # exit with parent
        self.done           = False

    def stop( self ):
        self.done = True

    def run( self ):
        """ Loops until the client hangs-up """
        read_events_on   = [ self.client_socket ]
        while ( not self.done ):
            # Wait for incoming data, or errors, or 0.3 seconds
            (read_list, write_list, except_list) = select.select( read_events_on, [], [], 0.5 )

            if ( len( read_list ) > 0 ):
                # New data arrived, read it
                incoming = self.client_socket.recv( 8192 )
                if ( len(incoming) == 0):
                    # Socket has closed
                    new_event = pygame.event.Event( NetworkEvents.CLIENT_HANGUP, { "address" : self.client_address } )
                    pygame.event.post( new_event )
                    self.client_socket.close()
                    self.done = True
                else:
                    # Data has arrived
                    try:
                        new_str = incoming.decode('utf-8')
                        self.data_buffer += new_str
                    except: 
                        pass # don't understand buffer

                    # Parse incoming message (trivial parser, not high quality) 
                    # commands are '\n' separated
                    if (self.data_buffer.find('\n') != -1 ):
                        for line in self.data_buffer.split('\n'):
                            line = line.strip()
                            # client disconnect command
                            if ( line == 'close' ):
                                new_event = pygame.event.Event( NetworkEvents.CLIENT_HANGUP, { "address" : self.client_address } )
                                pygame.event.post( new_event )
                                self.client_socket.close()
                                self.done = True

                            # only make events for valid commands
                            elif ( line in ( 'red', 'green', 'blue' ) ):
                                new_event = pygame.event.Event( NetworkEvents.CLIENT_MESSAGE, { "address" : self.client_address, "message" : line  } )
                                pygame.event.post( new_event )
                        self.data_buffer = ''  # all used-up






class ConnectionHandlerThread( threading.Thread ):
    """ Opens a socket, listening for incoming connections.
        If a client connects, notifies the PyGame main thread with a message,
        and starts a conversation handler thread to accept commands """
    def __init__( self ):
        threading.Thread.__init__(self)
        self.daemon = True # exit with parent
        self.done   = False

    def stop( self ):
        self.done = True

    def run( self ):
        # Establish a socket-listener
        rx_sock = socket.socket() 
        rx_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        rx_sock.bind( ( LISTEN_ADDRESS, LISTEN_PORT ) )
        while ( not self.done ):
            rx_sock.listen( 3 ) # small queue
            client_socket, remote_addr = rx_sock.accept()
            print("Connection from %s" % ( str( remote_addr ) ) )
            # Tell the main thread someone connected via an event
            new_event_args = { "socket"  : client_socket, "address" : remote_addr  }
            new_event = pygame.event.Event( NetworkEvents.CLIENT_CONNECTED, new_event_args )
            pygame.event.post( new_event )
            # Start a thread to handle the socket-conversation with the client
            new_thread = ConversationHandlerThread( client_socket, remote_addr )
            new_thread.start()




###
### MAIN
###

# Create the window
pygame.init()
pygame.display.set_caption("Socket Messages")
WINDOW  = pygame.display.set_mode( ( WINDOW_WIDTH, WINDOW_HEIGHT ), pygame.HWSURFACE|pygame.DOUBLEBUF|pygame.RESIZABLE )

# Create some sprites
SPRITES = pygame.sprite.Group()
for i in range( 3 ):
    new_sprite = AlienSprite( WHITE )
    SPRITES.add( new_sprite )

# Start the connection-listener thread
thread1 = ConnectionHandlerThread()
thread1.start()

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

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

        elif ( event.type == NetworkEvents.CLIENT_HANGUP ):
            print(" CLIENT DISCONNECTED %s " % ( str(event.address) ) )

        elif ( event.type == NetworkEvents.CLIENT_CONNECTED ):
            print(" NEW CLIENT FROM %s " % ( str(event.address) ) )

        elif ( event.type == NetworkEvents.CLIENT_MESSAGE ):
            print(" CLIENT MESSAGE FROM %s - %s " % ( str(event.address), event.message ) )
            if ( event.message == 'red' ):
                new_sprite = AlienSprite( RED )
                SPRITES.add( new_sprite )
            elif ( event.message == 'blue' ):
                new_sprite = AlienSprite( BLUE )
                SPRITES.add( new_sprite )
            elif ( event.message == 'green' ):
                new_sprite = AlienSprite( GREEN )
                SPRITES.add( new_sprite )

        elif ( event.type == pygame.VIDEORESIZE ):
            WINDOW_WIDTH  = event.w
            WINDOW_HEIGHT = event.h
            WINDOW  = pygame.display.set_mode( ( WINDOW_WIDTH, WINDOW_HEIGHT ), pygame.HWSURFACE|pygame.DOUBLEBUF|pygame.RESIZABLE )

    WINDOW.fill( DARK )
    SPRITES.draw( WINDOW )
    pygame.display.flip()

    clock.tick_busy_loop(60)

thread1.stop()
pygame.quit()

很抱歉这个答案中的代码太长了,但我觉得有必要正确地演示一个合适的答案。省略枚举类型,或者套接字侦听器可能会阻碍学习值。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/55171411

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档