glooey 按钮 (pyglet) 上的图像有时绘制有时不绘制
Images on glooey buttons (pyglet) sometimes drawn sometimes not
我有一个 pyglet 主窗口,我想在上面添加一些控制按钮。每个按钮上都绘制了一个图标。
这是我的简约示例:
from PIL import Image
import glooey
import pyglet
class BasicButton(glooey.Button):
def __init__(self, *, button_text, image_path):
super().__init__(button_text)
self.sprite = None
self.image_path = image_path
def do_draw(self):
image_width, image_height = Image.open(self.image_path).size
x = self.rect.left + self.rect.width/2 - image_width/2
y = self.rect.bottom + self.rect.height/2 - image_height/2
if self.sprite is None:
self.sprite = pyglet.sprite.Sprite(img=pyglet.image.load(image_path),
x=x, y=y, batch=self.batch, group=self.group)
else:
self.sprite.x = x
self.sprite.y = y
class AddWidgetsToWindow(glooey.Widget):
def __init__(self, pyglet_window: pyglet.window.Window):
super().__init__()
self.gui = glooey.Gui(pyglet_window)
vbox = glooey.VBox(default_cell_size=40)
vbox.padding = 5
vbox.alignment = 'left'
menu_buttons = [
"""
All buttons listed here
"""
]
for button in menu_buttons:
vbox.add(button)
self.gui.add(vbox)
window = pyglet.window.Window()
AddWidgetsToWindow(window)
pyglet.app.run()
如果我 运行 这几次,我总是会得到一组不同的正确绘制的按钮:
问题是,为什么会发生这种情况,如何解决?
我不太确定发生了什么,但我有一些意见可能会有所帮助:
这种问题(图片随机不显示)通常是两张图片在同一个OrderedGroup
中的标志。这会导致图像随机排序。当应该在后面的图像出现在前面时,看起来应该在前面的图像不存在了。
在这种情况下,self.sprite
与按钮在同一组中,因此看起来很可疑。
您可能不应该从 Button
继承并实现 do_draw()
。 Button
class 设计为使用 class 变量进行配置,如下所示:
class BasicButton(glooey.Button):
custom_base_image = pyglet.image.load('...')
custom_over_image = pyglet.image.load('...')
custom_down_image = pyglet.image.load('...')
如果您出于某种原因需要实施 do_draw()
,但此简单示例中未显示,最好从 Widget
而不是 Button
派生新的小部件。 Button
基本上是 Image
/Background
/Label
小部件的可配置 Stack
。如果我想用这些小部件做一些非常不寻常的事情,我只会从 subclass 重载 Button
方法。但是在这种情况下,do_draw()
似乎根本没有对这些小部件执行任何操作;它只是用图像覆盖它们。如果是这样的话,最好的办法是从 Widget
继承,这样一开始就没有什么可以掩盖的了。请注意,所有小部件(不仅仅是 Button
)在被单击时都会发出事件,您可以使用 Rollover
在自定义 Widget
subclasses 中轻松实现翻转效果.
这是一件小事,但是用PIL来获取一张图片的宽高有点奇怪。您可以直接从 pyglet 获取此信息,例如:
img = pyglet.image.load(image_path)
x = self.rect.center.x - img.width / 2
y = self.rect.center.y - img.height / 2
如果这些评论没有帮助,如果您 post 实际运行的代码片段(连同必要的图像文件)并显示问题,我可能会给出更好的答案。
@Kale Kundert 绝对让我找到了解决这个问题的正确方向。是的,组顺序是基本的,并且在本文档的帮助下:https://pyglet.readthedocs.io/en/latest/modules/graphics/ 我设法通过确保将我实现中的 Sprite
对象添加到前景组中来获得所需的行为:
class BasicButton(glooey.Button):
def __init__(self, *, button_text, image_path):
super().__init__(button_text)
self.sprite = None
self.image_path = image_path
def do_draw(self):
image_width, image_height = Image.open(self.image_path).size
x = self.rect.left + self.rect.width/2 - image_width/2
y = self.rect.bottom + self.rect.height/2 - image_height/2
if self.sprite is None:
self.sprite = pyglet.sprite.Sprite(
img=pyglet.image.load(image_path),
x=x, y=y,
batch=self.batch,
group=self._foreground.get_group()
)
else:
self.sprite.x = x
self.sprite.y = y
注意 self._foreground.get_group()
的变化。
考虑到@Kale Kundert 的回答,也许这不是最正确的实现,但我的目标是在 hoovering/clicking 上获得具有响应背景的按钮的行为,这也允许我通过回调编辑显示在其上的文本。如果我添加为每种潜在情况创建按钮图标(图像),我将无法实现这种自由度。
这并不能完全回答最初的问题,但似乎 OP 真正想要做的是动态更改按钮的文本(例如通过回调)。这很容易做到,并且不需要重载任何父 class 方法。
要理解的重要概念是按钮实际上只是其他小部件的专用容器。具体来说,一个按钮包含一个前景小部件和一些背景小部件(每个鼠标状态一个)。要动态更改按钮,您只需访问相关的包含小部件(在本例中为前景)并进行更改。这种架构的好处在于,无论按钮中的小部件是什么类型,它都能正常工作。如果前景是Label
,可以设置button.foreground.text
。如果是Image
,可以设置button.foreground.image
,以此类推
这是一个简单但完整的示例,说明如何执行此操作。在此示例中,按钮滚动状态只是纯色而不是图像,按钮文本只是每次单击按钮时递增的数字,但希望它能展示如何做这样的事情。
import pyglet
import glooey
class MyButton(glooey.Button):
custom_base_color = 40, 40, 40 # dark grey
custom_over_color = 80, 80, 80 # normal grey
custom_down_color = 240, 240, 240 # light grey
window = pyglet.window.Window()
gui = glooey.Gui(window)
# Note that arguments to the Button constructor are passed directly to the
# constructor of the foreground widget, which is `Text` by default.
button = MyButton('1')
gui.add(button)
def on_click(button):
i = int(button.foreground.text)
button.foreground.text = str(i + 1)
button.push_handlers(on_click)
pyglet.app.run()
编辑:
我现在明白 OP 想要一个在前景中既有图像又有标签的按钮(当然还有 mouse-responsive 背景)。上面相同的 button-is-a-container 概念也适用于此。一个按钮只能有一个前景小部件,但前景小部件本身可以包含任意数量的小部件。例如,您可以将前景设置为 HBox
,以便在一个按钮中放置多个小部件 side-by-side。
要制作一个前景在图像顶部有文本的按钮,诀窍是将前景小部件设为 Stack
。然后您可以将文本和图像添加到该堆栈。
下面的例子展示了一种方法。请注意,前景堆栈被分解到它自己的自定义小部件 class 中,称为 MyImageLabel
。我认为这使代码更加清晰和可重用,但您可以在不这样做的情况下获得相同的效果(即通过直接在 MyButton.Foreground
中创建堆栈,或者甚至在实例化按钮后创建堆栈并将其分配给button.foreground
)。另请注意,文本和图像都可以通过回调进行修改,方法与上述几乎相同。
#!/usr/bin/env python3
import pyglet
import glooey
class MyImageLabel(glooey.Widget):
Image = glooey.Image
Label = glooey.Label
def __init__(self):
super().__init__()
self._stack = glooey.Stack()
self.label = self.Label()
self.image = self.Image()
self._stack.add(self.image)
self._stack.add(self.label)
self._attach_child(self._stack)
class MyButton(glooey.Button):
class Foreground(MyImageLabel):
class Image(MyImageLabel.Image):
custom_image = pyglet.image.load('img0.png')
class Label(MyImageLabel.Label):
custom_text = '1'
custom_alignment = 'center'
custom_base_color = 40, 40, 40 # dark grey
custom_over_color = 80, 80, 80 # normal grey
custom_down_color = 240, 240, 240 # light grey
window = pyglet.window.Window()
gui = glooey.Gui(window)
button = MyButton()
gui.add(button)
def on_click(button):
i = int(button.foreground.label.text)
button.foreground.label.text = str(i + 1)
button.foreground.image.image = pyglet.image.load(f'img{i%2}.png')
button.push_handlers(on_click)
pyglet.app.run()
如果你想运行上面的例子,你需要下载这些图片并分别命名为img0.png
和img1.png
:
我有一个 pyglet 主窗口,我想在上面添加一些控制按钮。每个按钮上都绘制了一个图标。
这是我的简约示例:
from PIL import Image
import glooey
import pyglet
class BasicButton(glooey.Button):
def __init__(self, *, button_text, image_path):
super().__init__(button_text)
self.sprite = None
self.image_path = image_path
def do_draw(self):
image_width, image_height = Image.open(self.image_path).size
x = self.rect.left + self.rect.width/2 - image_width/2
y = self.rect.bottom + self.rect.height/2 - image_height/2
if self.sprite is None:
self.sprite = pyglet.sprite.Sprite(img=pyglet.image.load(image_path),
x=x, y=y, batch=self.batch, group=self.group)
else:
self.sprite.x = x
self.sprite.y = y
class AddWidgetsToWindow(glooey.Widget):
def __init__(self, pyglet_window: pyglet.window.Window):
super().__init__()
self.gui = glooey.Gui(pyglet_window)
vbox = glooey.VBox(default_cell_size=40)
vbox.padding = 5
vbox.alignment = 'left'
menu_buttons = [
"""
All buttons listed here
"""
]
for button in menu_buttons:
vbox.add(button)
self.gui.add(vbox)
window = pyglet.window.Window()
AddWidgetsToWindow(window)
pyglet.app.run()
如果我 运行 这几次,我总是会得到一组不同的正确绘制的按钮:
问题是,为什么会发生这种情况,如何解决?
我不太确定发生了什么,但我有一些意见可能会有所帮助:
这种问题(图片随机不显示)通常是两张图片在同一个
OrderedGroup
中的标志。这会导致图像随机排序。当应该在后面的图像出现在前面时,看起来应该在前面的图像不存在了。在这种情况下,
self.sprite
与按钮在同一组中,因此看起来很可疑。您可能不应该从
Button
继承并实现do_draw()
。Button
class 设计为使用 class 变量进行配置,如下所示:class BasicButton(glooey.Button): custom_base_image = pyglet.image.load('...') custom_over_image = pyglet.image.load('...') custom_down_image = pyglet.image.load('...')
如果您出于某种原因需要实施
do_draw()
,但此简单示例中未显示,最好从Widget
而不是Button
派生新的小部件。Button
基本上是Image
/Background
/Label
小部件的可配置Stack
。如果我想用这些小部件做一些非常不寻常的事情,我只会从 subclass 重载Button
方法。但是在这种情况下,do_draw()
似乎根本没有对这些小部件执行任何操作;它只是用图像覆盖它们。如果是这样的话,最好的办法是从Widget
继承,这样一开始就没有什么可以掩盖的了。请注意,所有小部件(不仅仅是Button
)在被单击时都会发出事件,您可以使用Rollover
在自定义Widget
subclasses 中轻松实现翻转效果.这是一件小事,但是用PIL来获取一张图片的宽高有点奇怪。您可以直接从 pyglet 获取此信息,例如:
img = pyglet.image.load(image_path) x = self.rect.center.x - img.width / 2 y = self.rect.center.y - img.height / 2
如果这些评论没有帮助,如果您 post 实际运行的代码片段(连同必要的图像文件)并显示问题,我可能会给出更好的答案。
@Kale Kundert 绝对让我找到了解决这个问题的正确方向。是的,组顺序是基本的,并且在本文档的帮助下:https://pyglet.readthedocs.io/en/latest/modules/graphics/ 我设法通过确保将我实现中的 Sprite
对象添加到前景组中来获得所需的行为:
class BasicButton(glooey.Button):
def __init__(self, *, button_text, image_path):
super().__init__(button_text)
self.sprite = None
self.image_path = image_path
def do_draw(self):
image_width, image_height = Image.open(self.image_path).size
x = self.rect.left + self.rect.width/2 - image_width/2
y = self.rect.bottom + self.rect.height/2 - image_height/2
if self.sprite is None:
self.sprite = pyglet.sprite.Sprite(
img=pyglet.image.load(image_path),
x=x, y=y,
batch=self.batch,
group=self._foreground.get_group()
)
else:
self.sprite.x = x
self.sprite.y = y
注意 self._foreground.get_group()
的变化。
考虑到@Kale Kundert 的回答,也许这不是最正确的实现,但我的目标是在 hoovering/clicking 上获得具有响应背景的按钮的行为,这也允许我通过回调编辑显示在其上的文本。如果我添加为每种潜在情况创建按钮图标(图像),我将无法实现这种自由度。
这并不能完全回答最初的问题,但似乎 OP 真正想要做的是动态更改按钮的文本(例如通过回调)。这很容易做到,并且不需要重载任何父 class 方法。
要理解的重要概念是按钮实际上只是其他小部件的专用容器。具体来说,一个按钮包含一个前景小部件和一些背景小部件(每个鼠标状态一个)。要动态更改按钮,您只需访问相关的包含小部件(在本例中为前景)并进行更改。这种架构的好处在于,无论按钮中的小部件是什么类型,它都能正常工作。如果前景是Label
,可以设置button.foreground.text
。如果是Image
,可以设置button.foreground.image
,以此类推
这是一个简单但完整的示例,说明如何执行此操作。在此示例中,按钮滚动状态只是纯色而不是图像,按钮文本只是每次单击按钮时递增的数字,但希望它能展示如何做这样的事情。
import pyglet
import glooey
class MyButton(glooey.Button):
custom_base_color = 40, 40, 40 # dark grey
custom_over_color = 80, 80, 80 # normal grey
custom_down_color = 240, 240, 240 # light grey
window = pyglet.window.Window()
gui = glooey.Gui(window)
# Note that arguments to the Button constructor are passed directly to the
# constructor of the foreground widget, which is `Text` by default.
button = MyButton('1')
gui.add(button)
def on_click(button):
i = int(button.foreground.text)
button.foreground.text = str(i + 1)
button.push_handlers(on_click)
pyglet.app.run()
编辑:
我现在明白 OP 想要一个在前景中既有图像又有标签的按钮(当然还有 mouse-responsive 背景)。上面相同的 button-is-a-container 概念也适用于此。一个按钮只能有一个前景小部件,但前景小部件本身可以包含任意数量的小部件。例如,您可以将前景设置为 HBox
,以便在一个按钮中放置多个小部件 side-by-side。
要制作一个前景在图像顶部有文本的按钮,诀窍是将前景小部件设为 Stack
。然后您可以将文本和图像添加到该堆栈。
下面的例子展示了一种方法。请注意,前景堆栈被分解到它自己的自定义小部件 class 中,称为 MyImageLabel
。我认为这使代码更加清晰和可重用,但您可以在不这样做的情况下获得相同的效果(即通过直接在 MyButton.Foreground
中创建堆栈,或者甚至在实例化按钮后创建堆栈并将其分配给button.foreground
)。另请注意,文本和图像都可以通过回调进行修改,方法与上述几乎相同。
#!/usr/bin/env python3
import pyglet
import glooey
class MyImageLabel(glooey.Widget):
Image = glooey.Image
Label = glooey.Label
def __init__(self):
super().__init__()
self._stack = glooey.Stack()
self.label = self.Label()
self.image = self.Image()
self._stack.add(self.image)
self._stack.add(self.label)
self._attach_child(self._stack)
class MyButton(glooey.Button):
class Foreground(MyImageLabel):
class Image(MyImageLabel.Image):
custom_image = pyglet.image.load('img0.png')
class Label(MyImageLabel.Label):
custom_text = '1'
custom_alignment = 'center'
custom_base_color = 40, 40, 40 # dark grey
custom_over_color = 80, 80, 80 # normal grey
custom_down_color = 240, 240, 240 # light grey
window = pyglet.window.Window()
gui = glooey.Gui(window)
button = MyButton()
gui.add(button)
def on_click(button):
i = int(button.foreground.label.text)
button.foreground.label.text = str(i + 1)
button.foreground.image.image = pyglet.image.load(f'img{i%2}.png')
button.push_handlers(on_click)
pyglet.app.run()
如果你想运行上面的例子,你需要下载这些图片并分别命名为img0.png
和img1.png
: