你需要修改Rectangle指令(或者删除替换,但修改更容易):
with self.canvas:
self.rect = Rectangle(source='image.png')
然后:
self.rect.source = 'newImage.png'
至于问题的第二部分,问题是图像在 Kivy 中加载时被缓存。因此,当您保存 newimage.png 并再次重新加载时,Kivy 知道您已经加载了 newimage.png。对于 Kivy 应用来说,这并不是一个好的设计。
此外,在on_touch_down 中创建Rectangle 意味着每次按下小部件时都会创建一个新的Rectangle,因此您只需添加越来越多不必要的绘图指令。
您可能还注意到,当您将多个这些小部件添加到布局时,它们都以窗口的完整大小呈现在同一个位置。小部件不限于在其区域内绘制,并且可以在应用程序内的任何位置绘制。您需要通过传递size 和pos 参数来确保Rectangle 指令知道在哪里绘制。
最后,您可以只显示一张图片,而不是保存图片。这将更有效率,并且不需要使用唯一的文件名或弄乱缓存系统。
您绝对应该看看Kivy language (kv),因为它更容易用于设计小部件和布局您的应用程序。
这是使用 kv 执行此操作的示例:
<TileWidget>:
tilesource: ''
canvas:
Color:
rgba: 1, 1, 1, 1
Rectangle:
size: self.size
pos: self.pos
source: 'image.png'
Color:
a: 1 if self.tilesource else 0
Rectangle:
size: self.size
pos: self.pos
source: self.tilesource
现在我将解释这一切的作用。
<TileWidget>:
这是一个类规则,因为名称被<> 包围。它将适用于TileWidget 的所有实例。
tilesource: ''
我们是setting the value of the propertytilesource。但是tilesource 不是Widget 类的属性!不用担心。在 kv 中,当您像这样为不存在的属性赋值时,该属性将自动创建。由于我们分配给属性的值是一个字符串,Kivy 会意识到我们希望这是一个StringProperty。
通过创建这个属性,我们可以从外部影响这个小部件。稍后我们将使用此属性的值来显示图像。
canvas:
就像在 Python 中使用 with self.canvas: 一样。
Color:
rgba: 1, 1, 1, 1
在canvas 块内,我们可以添加绘图指令。我们从Color instruction 开始,因为您不知道当前设置的颜色。这种颜色会为我们显示的图像着色,我们不想要任何颜色,所以我们使用全白色。
Rectangle:
size: self.size
pos: self.pos
source: 'image.png'
现在我们要渲染第一张图片。我们确保Rectangle 的大小和位置与小部件匹配。在 kv 中,这样做会自动创建一个绑定。如果小部件四处移动,它的大小将会改变,Rectangle 将自行更新以匹配。
Color:
a: 1 if self.tilesource else 0
这一次,我们知道颜色设置为什么。但是,在设置图像之前,我们不想绘制任何其他内容。在 kv 中,属性将接受任何 Python 值。这意味着您可以像这样使用ternary expressions,也可以使用函数调用或算术等。因此,如果self.tilesource 的计算结果为False(如空字符串,我们在上面设置的默认值),那么a 的属性颜色(alpha 分量)将设置为 0,否则为 1。
Rectangle:
size: self.size
pos: self.pos
source: self.tilesource
最后,我们渲染选中的图像。
现在,要创建小部件本身,我们只需要一点 Python:
class TileWidget(Widget):
def on_touch_down(self, touch):
if self.collide_point(*touch.pos):
self.tilesource = '64x64tile.png'
return True
return super(TileWidget, self).on_touch_down(touch)
在处理触摸时使用collide_point 很重要。就像小部件可以在应用程序的任何位置绘制一样,它们也可以在应用程序的任何位置处理触摸。这可以确保在操作之前触摸实际上是在我们的小部件的范围内。 在 collide_point 块中,我们将返回 True 以让 Kivy 知道我们已经处理了这个触摸并且没有其他东西应该处理它。否则,我们调用super() 让默认处理程序接管。