如何解决有时会绘制在glooey按钮pyglet上的图像有时不会
我有一个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 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
基本上是Stack
/Image
/Background
小部件的可配置Label
。如果我想对这些小部件做一些非常不寻常的事情,我只会从子类中重载Button
方法。但是在这种情况下,do_draw()
似乎根本没有对这些小部件做任何事情;只是用图像遮盖了它们。如果是这样,最好的办法是从Widget
继承,这样一开始就没有什么可掩盖的。请注意,所有窗口小部件(不仅仅是Button
)在单击时都会发出事件,并且您可以使用Rollover
在自定义Widget
子类中轻松实现翻转效果。 -
这是一件小事,但是使用PIL获取图像的宽度和高度有点奇怪。您可以直接从pyglet获取此信息,例如:
img = pyglet.image.load(image_path) x = self.rect.center.x - img.width / 2 y = self.rect.center.y - img.height / 2
如果这些注释无济于事,如果您发布一个实际运行的代码段(以及必要的图像文件)并显示出问题,我可能会给出更好的答案。
,这并不能完全回答原始问题,但似乎OP真正想做的是动态更改按钮的文本(例如,通过回调)。这很容易做到,并且不需要重载任何父类方法。
要理解的重要概念是按钮实际上只是其他小部件的专门容器。具体来说,一个按钮包含一个前景小部件和一些背景小部件(每个鼠标状态一个)。要动态更改按钮,您只需要访问包含的相关小部件(在本例中为前台)并进行更改即可。这种体系结构的优点在于,无论按钮中包含哪种小部件,它都可以工作。如果前景是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需要一个在前景上既有图像又有标签的按钮(当然还有鼠标响应的背景)。上面相同的“按钮就是容器”概念也与此有关。一个按钮只能有一个前景小部件,但是前景小部件本身可以包含任意数量的小部件。例如,您可以将前景设置为HBox
,以便将多个窗口小部件并排放置在一个按钮中。
要创建一个按钮,使前景小部件的图像上方带有文本,诀窍是使前景小部件成为Stack
。然后,您可以将文本和图像添加到该堆栈中。
以下示例显示了执行此操作的一种方法。请注意,前台堆栈被分解到名为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,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
:
@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的回答,也许这不是最正确的实现,但是我的目的是让按钮的行为具有响应性的悬停/单击背景,这也使我可以编辑显示在通过回调。如果我为每种潜在情况添加按钮图标(图像),我将无法达到这种自由度。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。