python - 如何创建两个单独的 pygame 显示?如果我不能,我怎么能创建两个 pygame 实例?
问题描述
我想创建两个单独pygame
的显示器。我知道使用 pygame 的单个实例是不可能做到这一点的。我想知道如何/是否可以实施以下任何解决方案:
- 创建一个单独的模块来运行使用单独显示的功能。这是否会从我的主代码中使用单独的 pygame 实例并因此能够运行?
- 使用 subshell 使用一些特殊参数从自身内部运行脚本, parse
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 的multiprocessing模块来生成一个进程并使用 aQueue
在两个进程之间传递数据。
这是我一起破解的一个例子:
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中执行IPC 。
最后但同样重要的是,想想你是否真的需要两个 pygame 显示器,因为多处理增加了很多复杂性。我已经回答了一些关于 SO 的 pygame 问题,并且在询问有关 pygame 的线程/多处理时,OP 几乎总是在问一个 XY 问题。
推荐阅读
- python - 如何在 Python 中计算实物期权决策树模型中的期望值?
- php - 离开循环而不在php中终止其条件的原因
- spring-boot - Grails 3.3.11 和 Spring boot 执行器不适用于健康端点
- docker - Kuberentes 上的 Istio JMX 端口公开
- model - 为 CNN model.summary() 发布的答案参考
- node.js - 找不到快速路线
- java - 无法设置 ChromeOptions 功能
- javascript - 即使 jQuery 验证失败,AJAX 表单也会提交
- firebase - Flutter:在 Firestore 中检测新的一天
- rpm - 对 Rocky Linux 中已安装包的依赖失败