我有下面的代码,它当前在空白的Pyglet窗口上输出图像,但是只输出了一个图像。我真的需要每两秒钟添加一个新图像,之前的图像也保持完整。例如,添加一个图像,两秒钟后添加另一个图像,并在添加另一个图像两秒后添加。我添加了随机库,因此可以添加随机图像。
我所拥有的代码如下所示,它只显示一张图片 - 我觉得这会卡在绘图部分的某处。
this.value
我们非常感谢您提供的任何帮助或建议。
答案 0 :(得分:2)
您的代码有一些问题/建议。 首先,以下代码是多余的:
while True:
images = ["Image 1.png", "Image 2.png", "Image 3.png"]
....
pyglet.app.run()
因为pyglet.app.run()
是一个阻塞调用,意思是,循环永远不会循环 - 因为pyglet.app.run()
本身就是一个循环(稍后会详细介绍)。
除非您的应用程序崩溃,但您没有处理这些异常,所以即使在这种情况下也不会重新运行/循环代码。
其次,你永远不应该在循环中定义数组/列表或任何内容。循环通常用于逻辑操作,而不是创建事物。最有趣的是,有时候在循环中创建东西很有用,但那些时候它们往往伴随着if
语句。
资源为计算机花费了很多,无论是设置还是内存/硬盘驱动器等。因此建议尽可能早地创建列表并在任何循环之外创建列表。例如:
window = pyglet.window.Window()
images = ["Image 1.png", "Image 2.png", "Image 3.png"]
while True:
choice = images[random.randint(0,2)]
本来是一个更好的选择,如果 - 再次 - 循环实际循环。在这种情况下,这只是一个整理的问题。
此外,这段代码:
@window.event
def on_draw():
window.clear()
sprite.draw()
也不应该在循环中创建,它意味着替换您的window
变量on_draw
函数。所以应该尽快将其移出并放入您的逻辑IMO中。至少保持与所有其他逻辑分开,因此它不在“添加随机图像”和“循环”内部。
现在,你的代码失败的主要原因是你认为这会循环,但事实并非如此。同样,pyglet.app.run()
将锁定您在该行上的代码执行,因为它是该函数调用中的永无止境的循环。
您可以扩展代码并从pyglet.py
的源代码中复制粘贴代码,它看起来像这样(只是为了让您了解正在发生的事情):
window = pyglet.window.Window()
while True:
images = ["Image 1.png", "Image 2.png", "Image 3.png"]
choice = images[random.randint(0,2)]
rawImage = pyglet.resource.image(choice)
sprite = pyglet.sprite.Sprite(rawImage)
@window.event
def on_draw():
window.clear()
sprite.draw()
time.sleep(2)
def run(self):
while True:
event = self.dispatch_events()
if event:
self.on_draw()
注意pyglet.app.run()
如何扩展到另一个while True
循环,从未真正中断。 有点过于简单,但这基本上就是发生了什么。
因此,您的sprite = pyglet.sprite.Sprite(rawImage)
永远不会被重新触发
那么,对于你的第二大问题,为什么这段代码永远不会起作用:
你在做:
def on_draw():
sprite.draw()
但是每个循环你都会用sprite
替换旧的sprite = pyglet.sprite.Sprite(rawImage)
对象,换一个新对象。所以你想要做的是将一个列表/字典保留在循环之外,包含所有可见图像,然后添加它并仅渲染图像。
很像这样:
import pyglet
import time
import random
width, height = 800, 600
window = pyglet.window.Window(width, height)
## Image options are the options we have,
## while `images` are the visible images, this is where we add images
## so that they can be rendered later
image_options = ["Image 1.png", "Image 2.png", "Image 3.png"]
images = {}
## Keep a timer of when we last added a image
last_add = time.time()
## Just a helper-function to generate a random image and return it
## as a sprite object (good idea to use sprites, more on that later)
def get_random_image():
choice = image_options[random.randint(0, len(image_options)-1)]
return pyglet.sprite.Sprite(pyglet.image.load(choice))
## Here, we define the `on_draw` replacement for `window.on_draw`,
## and it's here we'll check if we should add a nother image or not
## depending on how much time has passed.
@window.event
def on_draw():
window.clear()
## If two seconds have passed, and the ammount of images added are less/equal
## to how many images we have in our "database", aka `image_options`, then we'll
## add another image somewhere randomly in the window.
if time.time() - last_add > 2 and len(images) < len(image_options):
last_add = time.time()
image = get_random_image()
image.x = random.randint(0, width)
image.y = random.randint(0, height)
images[len(images)] = image
## Then here, is where we loop over all our added images,
## and render them one by one.
for _id_ in images:
images[_id_].draw()
## Ok, lets start off by adding one image.
image = get_random_image()
image.x = random.randint(0, width)
image.y = random.randint(0, height)
images[len(images)] = image
## And then enter the never ending render loop.
pyglet.app.run()
现在,只有当你按下一个键或在窗口内按下鼠标时,这才有效。这是因为这是调度事件的唯一时间。如果有一个被触发的事件,Pyglet只会渲染事物。有两种方法可以解决这个问题,即硬核OOP方式,我现在将跳过它。
第二种是使用所谓的Pyglet时钟,在那里你可以安排一些间隔发生的事情。我并不擅长这部分,因为我倾向于使用我自己的调度程序等。
但这是它的要点:
def add_image():
images[len(images)] = get_random_image()
pyglet.clock.schedule_interval(add_image, 2) # Every two seconds
这比做if time.time() - last_add > 2
更清洁
结果应如下所示:
import pyglet
import time
import random
width, height = 800, 600
window = pyglet.window.Window(width, height)
## Image options are the options we have,
## while `images` are the visible images, this is where we add images
## so that they can be rendered later
image_options = ["Image 1.png", "Image 2.png", "Image 3.png"]
images = {}
## Just a helper-function to generate a random image and return it
## as a sprite object (good idea to use sprites, more on that later)
def get_random_image():
choice = image_options[random.randint(0, len(image_options)-1)]
return pyglet.sprite.Sprite(pyglet.image.load(choice))
def add_image(actual_time_passed_since_last_clock_tick):
image = get_random_image()
image.x = random.randint(0, width)
image.y = random.randint(0, height)
images[len(images)] = image
## Here, we define the `on_draw` replacement for `window.on_draw`,
## and it's here we'll check if we should add a nother image or not
## depending on how much time has passed.
@window.event
def on_draw():
window.clear()
## Then here, is where we loop over all our added ima ges,
## and render them one by one.
for _id_ in images:
images[_id_].draw()
## Ok, lets start off by adding one image.
image = get_random_image()
image.x = random.randint(0, width)
image.y = random.randint(0, height)
images[len(images)] = image
## Add the schedule interval of adding a image every two seconds.
pyglet.clock.schedule_interval(add_image, 2)
## And then enter the never ending render loop.
pyglet.app.run()
这样,您无需按任何键或鼠标来触发Pyglet中的事件,它将为您处理该事件并执行您计划执行的操作。
接下来,是我的一个小优化。这是一个奖励,并会加快速度。它被称为批量渲染,当您渲染大量图像和精灵时,您当前一次向显卡发送一个图像。这非常 CPU 密集型。你想要做的就是把工作放在 GPU 上。因为毕竟你正在使用图形,对吗?
因此,在这种情况下,批量渲染非常简单。每次调用pyglet.sprite.Sprite
时,它都有一个名为batch=None
的参数(默认值)。如果您向精灵对象添加批处理,则可以通过调用batch.draw()
而不是每个sprite.draw()
来渲染整个批处理。
解决方案看起来像这样:
import pyglet
import time
from random import randint
width, height = 800, 600
window = pyglet.window.Window(width, height)
main_batch = pyglet.graphics.Batch()
## Image options are the options we have,
## while `images` are the visible images, this is where we add images
## so that they can be rendered later
image_options = ["Image 1.png", "Image 2.png", "Image 3.png"]
images = {}
## Just a helper-function to generate a random image and return it
## as a sprite object (good idea to use sprites, more on that later)
def get_random_image(x=0, y=0):
choice = image_options[randint(0, len(image_options)-1)]
return pyglet.sprite.Sprite(pyglet.image.load(choice), x=x, y=y, batch=main_batch)
def add_image(actual_time_passed_since_last_clock_tick=0):
image = get_random_image(x=randint(0, width), y=randint(0, height))
images[len(images)] = image
## Here, we define the `on_draw` replacement for `window.on_draw`,
## and it's here we'll check if we should add a nother image or not
## depending on how much time has passed.
@window.event
def on_draw():
window.clear()
## Instead of looping over each image in `images`,
## just do:
main_batch.draw()
## Ok, lets start off by adding one image.
## Instead of doing it manually, use the function add_image.
add_image()
## Add the schedule interval of adding a image every two seconds.
pyglet.clock.schedule_interval(add_image, 2)
## And then enter the never ending render loop.
pyglet.app.run()
我还对add_image
和get_random_image
进行了一些修改,主要是为了让你可以知道图片应该在函数内部的位置,因为pyglet.sprite.Sprite
还有两个其他参数, x
和y
。因此,在创建精灵后更改x
和y
是没有意义的,除非您想在之后移动它们(例如,在pyglet.clock.schedule_interval
调用中)。