Я хочу создать два отдельных дисплея pygame
. Я знаю, что это невозможно сделать с одним экземпляром pygame. Мне было интересно, как/если бы я мог реализовать любое из следующих решений:
sys.argv
, а затем запустите функцию во втором экземпляре. Как обеспечить кроссплатформенную совместимость?Меня не волнует, насколько неэффективной, уродливой и т.д. станет программа в результате. Я просто хочу, чтобы это работало.
Важно отметить, что основной код должен взаимодействовать с соответствующим кодом (т. е. изменять некоторые переменные, принадлежащие extraDisplayManager
ниже), но я планирую использовать pickle для сохранения в файл и передачи данных таким образом. Однако я уже использую многопоточность в соответствующей части, поэтому отсутствие синхронности не имеет значения.
Мой код довольно длинный и сложный, поэтому я не буду публиковать его здесь, но суть соответствующей части такова:
def mainCode(*someargs):
d = pygame.display.set_mode(dimensions)
if relevantArg:
extraDisplayManager = RelevantClass(*someotherargs)
threading.Thread(target=extraDisplayManager.relevantFunction,
daemon=True).start()
...
class RelevantClass:
def relevantFunction(self, *someotherargs):
self.d = pygame.display.set_mode(dimensions)
while True:
updateDisplay(someargs) # This is part of my main code, but it
# is standalone, so I could copy it to a new module
Я был бы признателен, если бы вы ответили на некоторые из моих вопросов или показали мне соответствующую документацию.
Если вам действительно (В самом деле) нужны два дисплея, вы можете использовать модуль Python многопроцессорность для создания процесса и использовать Queue
для передачи данных между двумя процессами.
Вот пример, который я взломал вместе:
import pygame
import pygame.freetype
import random
import multiprocessing as mp
# a simple class that renders a button
# if you press it, it calls a callback function
class Button(pygame.sprite.Sprite):
def __init__(self, callback, *grps):
super().__init__(*grps)
self.image = pygame.Surface((200, 200))
self.image.set_colorkey((1,2,3))
self.image.fill((1,2,3))
pygame.draw.circle(self.image, pygame.Color('red'), (100, 100), 50)
self.rect = self.image.get_rect(center=(300, 240))
self.callback = callback
def update(self, events, dt):
for e in events:
if e.type == pygame.MOUSEBUTTONDOWN:
if (pygame.Vector2(e.pos) - pygame.Vector2(self.rect.center)).length() <= 50:
pygame.draw.circle(self.image, pygame.Color('darkred'), (100, 100), 50)
self.callback()
if e.type == pygame.MOUSEBUTTONUP:
pygame.draw.circle(self.image, pygame.Color('red'), (100, 100), 50)
# a simple class that display a text for 1 second anywhere
class Message(pygame.sprite.Sprite):
def __init__(self, screen_rect, text, font, *grps):
super().__init__(*grps)
self.image = pygame.Surface((300, 100))
self.image.set_colorkey((1,2,3))
self.image.fill((1,2,3))
self.rect = self.image.get_rect(center=(random.randint(0, screen_rect.width),
random.randint(0, screen_rect.height)))
font.render_to(self.image, (5, 5), text)
self.timeout = 1000
self.rect.clamp_ip(screen_rect)
def update(self, events, dt):
if self.timeout > 0:
self.timeout = max(self.timeout - dt, 0)
else:
self.kill()
# Since we start multiple processes, let's create a mainloop function
# that can be used by all processes. We pass a logic_init_func-function
# that can do some initialisation and returns a callback function itself.
# That callback function is called before all the events are handled.
def mainloop(logic_init_func, q):
import pygame
import pygame.freetype
pygame.init()
screen = pygame.display.set_mode((600, 480))
screen_rect = screen.get_rect()
clock = pygame.time.Clock()
dt = 0
sprites_grp = pygame.sprite.Group()
callback = logic_init_func(screen, sprites_grp, q)
while True:
events = pygame.event.get()
callback(events)
for e in events:
if e.type == pygame.QUIT:
return
sprites_grp.update(events, dt)
screen.fill((80, 80, 80))
sprites_grp.draw(screen)
pygame.display.flip()
dt = clock.tick(60)
# The main game function is returned by this function.
# We need a reference to the slave process so we can terminate it when
# we want to exit the game.
def game(slave):
def game_func(screen, sprites_grp, q):
# This initializes the game.
# A bunch of words, and one of it is randomly choosen to be
# put into the queue once the button is pressed
words = ('Ouch!', 'Hey!', 'NOT AGAIN!', 'that hurts...', 'STOP IT')
def trigger():
q.put_nowait(random.choice(words))
Button(trigger, sprites_grp)
def callback(events):
# in the mainloop, we check for the QUIT event
# and kill the slave process if we want to exit
for e in events:
if e.type == pygame.QUIT:
slave.terminate()
slave.join()
return callback
return game_func
def second_display(screen, sprites_grp, q):
# we create font before the mainloop
font = pygame.freetype.SysFont(None, 48)
def callback(events):
try:
# if there's a message in the queue, we display it
word = q.get_nowait()
Message(screen.get_rect(), word, font, sprites_grp)
except:
pass
return callback
def main():
# we use the spawn method to create the other process
# so it will use the same method on each OS.
# Otherwise, fork will be used on Linux instead of spawn
mp.set_start_method('spawn')
q = mp.Queue()
slave = mp.Process(target=mainloop, args=(second_display, q))
slave.start()
mainloop(game(slave), q)
if __name__ == '__main__':
main()
Конечно, это только простой пример; вам, вероятно, нужна дополнительная обработка ошибок, прекращение вложенных функций, таких как сумасшедшие, и т. д. и т. д. Кроме того, есть и другие способы сделать МПК в python.
И последнее, но не менее важное: подумайте, нужны ли вам В самом деле два дисплея pygame, потому что многопроцессорность добавляет массу сложности. Я уже ответил на некоторые вопросы о pygame на SO, и почти всегда ОП задавал вопрос XY, когда спрашивал о многопоточности/многопроцессорности с pygame.
Спасибо за подробный ответ и ссылки на документацию! Хотя это кажется довольно сложным в использовании
multiprocessing
, и я уверен, что есть лучшие способы сделать это (например, иметь оба «окна» рядом на одном дисплее), длина моего текущего кода гарантирует plug-and-play решение, которое вы предоставили.