如何在不使用kv文件的Kivy项目中正确使用self.size?
问题:Kivy纯Python实现图片填满Widget失败,kv实现正常
我尝试用Kivy创建一个图片填满整个Widget的简单应用,使用kv文件实现时一切正常,但改用纯Python代码实现时却无法正常运行:self.size返回值为(100, 100),导致图片仅以100×100的尺寸显示在窗口左下角。
kv文件实现代码
Python代码:
from kivy.app import App from kivy.uix.widget import Widget class MainWidget(Widget): pass class MainApp(App): def build(self): return MainWidget() MainApp().run()
kv文件内容:
<MainWidget>: canvas.before: Rectangle: source: "background.jpg" size: self.size
尝试的纯Python代码
from kivy.app import App from kivy.uix.widget import Widget from kivy.graphics import * class MainWidget(Widget): def __init__(self, **kwargs): super(MainWidget, self).__init__(**kwargs) with self.canvas.before: Rectangle(size = self.size, source = "background.jpg") class MainApp(App): def build(self): return MainWidget() MainApp().run()
这两种实现方式难道不是等价的吗?是什么导致了这种不符合预期的输出?
原因分析
kv语言中的size: self.size是属性绑定,它会建立一种动态关联:当Widget的size属性发生变化时,Rectangle的size会自动同步更新。
而你在Python的__init__方法中直接赋值size=self.size,只是把Widget初始化时的默认尺寸(100,100)这个静态值传给了Rectangle。此时Widget还没有完成布局,真正的窗口尺寸还没生效,后续窗口或Widget尺寸变化时,Rectangle的尺寸不会自动跟进。
纯Python实现的正确写法
方法1:使用属性绑定(推荐,支持实时更新)
通过bind()方法建立Widget的size属性和Rectangle尺寸的联动,确保任何尺寸变化都能同步:
from kivy.app import App from kivy.uix.widget import Widget from kivy.graphics import Rectangle class MainWidget(Widget): def __init__(self, **kwargs): super(MainWidget, self).__init__(**kwargs) # 先创建矩形并保存引用 with self.canvas.before: self.bg_rect = Rectangle(source="background.jpg") # 绑定size属性,当Widget尺寸变化时调用更新方法 self.bind(size=self.update_background_size) # 初始化时手动调用一次,确保初始尺寸正确 self.update_background_size() def update_background_size(self, *args): self.bg_rect.size = self.size class MainApp(App): def build(self): return MainWidget() MainApp().run()
方法2:延迟初始化(仅适配初始布局)
利用Clock.schedule_once延迟执行背景初始化,等Widget完成布局、拿到正确尺寸后再设置Rectangle的大小:
from kivy.app import App from kivy.uix.widget import Widget from kivy.graphics import Rectangle from kivy.clock import Clock class MainWidget(Widget): def __init__(self, **kwargs): super(MainWidget, self).__init__(**kwargs) # 延迟1帧执行,等布局完成 Clock.schedule_once(self.init_background) def init_background(self, dt): with self.canvas.before: Rectangle(size=self.size, source="background.jpg") class MainApp(App): def build(self): return MainWidget() MainApp().run()
注意:方法2仅在窗口首次打开时设置一次尺寸,如果后续拉伸窗口,图片不会跟着放大;方法1则会实时响应尺寸变化,更符合需求。
内容的提问来源于stack exchange,提问作者Rajdeep Sindhu
相关产品推荐
相关产品推荐

