Canvas异常行为:点击btn按钮缩放时意外改变btn1尺寸
首先得先修正你代码里的一个基础错误:你用了Layout这个抽象基类,它不能直接实例化,而且也没有text和orientation属性,应该换成BoxLayout才能正常运行。
接下来咱们拆解你遇到的核心问题:
1. 为什么点击btn会“改变”btn1的尺寸?
你看到的btn1尺寸变化其实是视觉错觉——btn1的实际size属性根本没动!原因是你用了BoxLayout,它会按size_hint给子控件分配空间:btn和btn1各占父容器高度的50%。当你给btn的canvas添加Scale指令后,btn的绘制内容被缩放变小,但它的布局占位尺寸还是原来的50%高度,所以看起来btn1好像“变大”了,实际上只是btn的视觉区域缩小,让出了更多空白而已。
另外你的代码还有个逻辑问题:每次点击都往canvas里新增一个Scale指令,而不是修改已有指令,这会导致缩放效果不断累积,进一步加剧视觉上的混乱。
2. 为什么canvas.before会同时影响两个按钮?
这要从Kivy的Canvas绘图机制说起:
canvas.before:这里的指令会在控件自身内容(比如Button的背景、文字)绘制之前执行canvas:默认画布,包含控件自带的绘制内容canvas.after:指令会在控件自身内容绘制之后执行
变换类指令(比如Scale)的本质是修改当前的绘图上下文矩阵,这个修改会影响后续所有的绘图操作。当你把Scale添加到btn.canvas.before时:
- 这个Scale会先于btn的内容执行,修改了绘图上下文的缩放比例
- 如果你每次点击都新增一个Scale指令,这些指令会累积起来,导致绘图上下文的缩放比例越来越小
- 虽然Kivy会在绘制每个控件前后保存/恢复上下文状态,但重复添加的Scale指令会打破这个逻辑,导致btn1绘制时也继承了被修改的上下文,最终看起来两个按钮都被缩放了
而canvas.after的指令是在btn自身内容绘制完成后执行的,它只会影响你在after里新增的绘图内容,不会干扰btn的默认绘制,也不会影响btn1——因为BoxLayout绘制btn1前会重置上下文状态。
3. 正确的实现方式
不要每次点击都新增Scale指令,而是在btn初始化时就创建一个Scale指令并保存引用,每次点击只修改它的缩放比例:
from kivy.app import App from kivy.uix.boxlayout import BoxLayout from kivy.uix.button import Button from kivy.graphics import Scale class TutorialApp(App): def build(self): bl = BoxLayout(orientation='vertical') btn = Button(text='OK', size_hint=(1,0.5)) btn1 = Button(text='OK1', size_hint=(1,0.5)) # 在btn的canvas.before中初始化Scale指令并保存引用 with btn.canvas.before: btn.scale_inst = Scale(xyz=(1, 1, 1)) def btn_click(elem): if elem.scale_inst.x < 0.2: return new_scale = elem.scale_inst.x * 0.8 elem.scale_inst.xyz = (new_scale, new_scale, 1) print(new_scale) btn.bind(on_press=btn_click) bl.add_widget(btn) bl.add_widget(btn1) return bl if __name__ == '__main__': TutorialApp().run()
这样修改后,每次点击只会更新已有的Scale指令,不会累积新增,而且只会影响btn自身的绘制,完全不会干扰btn1的显示。
内容的提问来源于stack exchange,提问作者Александр

