You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于Kivy中另一个控件类调整当前控件的尺寸?

修复Kivy控件尺寸复用的循环依赖问题

问题原因

你遇到的超长报错栈是因为循环属性绑定:在KButton2的BaseButtonSize中,你设置width: self.width*1.25,但BaseButtonSize的size已经绑定了Window.width*0.0441作为宽度。这会形成无限循环:width依赖自身计算后的值,而size又依赖width,Kivy的属性系统会不断触发更新,最终导致栈溢出。

直接修复方案

修改Kv代码,避免在BaseButtonSize内部修改自身的width,而是在KButton2层直接基于基础尺寸计算目标尺寸:

from kivy.uix.button import Button
from kivy.uix.relativelayout import RelativeLayout
from kivy.uix.boxlayout import BoxLayout

from kivy.lang import Builder


Builder.load_string("""
#:import Window kivy.core.window.Window

<ViewPort>:
    KButton1:
    KButton2:

<BaseButtonSize>:
    size_hint: None,None
    size: Window.width*0.0441,Window.height*0.147

<KButton1>:
    size_hint: None,None
    size: button.size
    
    BaseButtonSize:
        id: button

<KButton2>:
    size_hint: None,None
    # 直接基于基础按钮的宽度乘以系数,而非修改基础按钮自身宽度
    size: button.width * 1.25, button.height
    
    BaseButtonSize:
        id: button
""")

class BaseButtonSize(Button):
    pass

class KButton1(RelativeLayout):
    pass

class KButton2(RelativeLayout):
    pass

class ViewPort(BoxLayout):
    pass

if __name__=="__main__":
    from kivy.app import App
    class TestApp(App):
        def build(self):
            return ViewPort()
    
    TestApp().run()

更优实现方式:尺寸逻辑复用(而非嵌套控件)

你当前的方案通过嵌套BaseButtonSize控件来复用尺寸,其实没必要——嵌套控件会增加布局层级,且容易引发绑定问题。更简洁的方式是抽离尺寸配置逻辑,让按钮直接复用:

方式1:Kv语言定义全局尺寸变量

直接在Kv中定义基础尺寸变量,所有按钮直接引用并调整系数:

from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout

from kivy.lang import Builder


Builder.load_string("""
#:import Window kivy.core.window.Window
#:set base_btn_width Window.width*0.0441
#:set base_btn_height Window.height*0.147

<ViewPort>:
    KButton1:
    KButton2:

<KButton1>:
    size_hint: None, None
    size: base_btn_width, base_btn_height

<KButton2>:
    size_hint: None, None
    size: base_btn_width*1.25, base_btn_height
""")

class KButton1(Button):
    pass

class KButton2(Button):
    pass

class ViewPort(BoxLayout):
    pass

if __name__=="__main__":
    from kivy.app import App
    class TestApp(App):
        def build(self):
            return ViewPort()
    
    TestApp().run()

方式2:用Mixin类复用尺寸逻辑

如果需要支持窗口大小变化时自动更新尺寸,可以用Mixin类封装基础尺寸计算:

from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout

from kivy.lang import Builder
from kivy.properties import NumericProperty


class BaseButtonMixin:
    base_width = NumericProperty(0)
    base_height = NumericProperty(0)

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        from kivy.core.window import Window
        self._update_base_size(Window)
        # 监听窗口尺寸变化,自动更新基础尺寸
        Window.bind(width=self._update_base_size, height=self._update_base_size)
    
    def _update_base_size(self, window, *args):
        self.base_width = window.width * 0.0441
        self.base_height = window.height * 0.147

class KButton1(BaseButtonMixin, Button):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.size_hint = (None, None)
        # 绑定尺寸到基础属性
        self.bind(base_width=self._update_size, base_height=self._update_size)
    
    def _update_size(self, *args):
        self.size = (self.base_width, self.base_height)

class KButton2(BaseButtonMixin, Button):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.size_hint = (None, None)
        self.bind(base_width=self._update_size, base_height=self._update_size)
    
    def _update_size(self, *args):
        self.size = (self.base_width * 1.25, self.base_height)

Builder.load_string("""
<ViewPort>:
    KButton1:
    KButton2:
""")

class ViewPort(BoxLayout):
    pass

if __name__=="__main__":
    from kivy.app import App
    class TestApp(App):
        def build(self):
            return ViewPort()
    
    TestApp().run()

这种方式既实现了尺寸逻辑复用,又能自动响应窗口大小变化,布局层级也更简洁。

内容的提问来源于stack exchange,提问作者Ten Kho

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 21:24:26