我想写一个pygame程序,在那里我通过套接字连接从树莓派接收数据。但是,我只希望Pygame代码在字符串到达后运行。这涉及到代码等待一段时间,然后当从Raspberry Pi收到一个字符串时,pygame运行代码来更新显示。目前,我的Pygame在等待数据收入时冻结并关闭。任何想法。谢谢。
发布于 2019-03-15 04:36:28
Pygame“冻结”很可能是由于网络调用被阻塞。考虑旋转一个新线程来处理数据通信,同时Pygame接管主线程并使用一些“等待”消息更新显示。收到数据后,第二个线程应将其传递给将显示数据的主线程。
发布于 2019-03-15 08:09:30
这里有几个问题在起作用。正如@Josep Valls回答的那样,代码需要在一个子线程中进行套接字通信。此外,PyGame主线程需要不断轮询事件循环(可能还会重新绘制屏幕)。显然,如果代码在每个循环中没有做太多的事情,那么可能没有必要每次都像下面的示例那样重新绘制整个屏幕。
那么,需要什么来处理与PyGame的套接字通信呢?首先,您需要侦听套接字以接受连接。在本例中,ConnectionHandlerThread
类就是这样做的。它侦听传入的客户端连接,一旦连接发生,它就会创建一个ConversationHandlerThread
来对每个客户端执行读/写操作。(如果您要处理大量来来去去的套接字,可能需要一个线程池,但我也会尽量保持示例的简单性。)
因此,为了解释示例代码-它绘制了一个窗口,其中有几个AlienSprite
在弹来弹去。客户端可以通过端口5555
(比如使用telnet
)连接到它并发出命令。发送一个颜色词(红/绿/蓝)会产生一个新的外星人的颜色。quit
命令将断开连接。
为了在套接字处理线程和PyGame主线程之间进行通信,套接字线程使用PyGame event.post()
发送事件。这些用户事件已经在一个名为NetworkEvents
的enum
中实现,以跟踪它们。这样做的好处是,当两个线程都在发布不同事件类型的pygame.USEREVENT + 27
时,您不会感到困惑。
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()
很抱歉这个答案中的代码太长了,但我觉得有必要正确地演示一个合适的答案。省略枚举类型,或者套接字侦听器可能会阻碍学习值。
https://stackoverflow.com/questions/55171411
复制相似问题