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

如何在Kivy中创建带相对尺寸定位的PID控制器自定义Widget

解决方案

核心思路

Rectangle是画布图形元素,本身不支持size_hint和pos_hint,解决办法是用布局组件(如BoxLayout)作为自定义组件的容器,将Rectangle绘制在容器的画布上,通过绑定容器的size和pos属性,让背景矩形随容器自动适配尺寸与位置。内部的显示区、标签、按钮则用嵌套布局组织,全程使用相对尺寸参数实现窗口自适应。

自定义组件实现(纯Python代码)

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.graphics import Color, Rectangle
from kivy.properties import NumericProperty, StringProperty

class PIDControllerDisplay(BoxLayout):
    # 定义可外部修改的绑定属性,用于更新温度显示
    set_temp = NumericProperty(25.0)
    current_temp = NumericProperty(23.5)

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.orientation = 'vertical'
        self.size_hint = (0.45, 0.45)  # 相对尺寸,外部可覆盖
        self.padding = 10
        self.spacing = 10

        # 绘制组件浅灰色背景
        with self.canvas.before:
            Color(0.8, 0.8, 0.8, 1)
            self.bg_rect = Rectangle(size=self.size, pos=self.pos)
        # 绑定背景矩形与组件尺寸、位置
        self.bind(size=self._update_bg, pos=self._update_bg)

        # 上部黑色显示区容器
        display_layout = BoxLayout(orientation='vertical', size_hint=(1, 0.6))
        with display_layout.canvas.before:
            Color(0, 0, 0, 1)
            self.display_rect = Rectangle(size=display_layout.size, pos=display_layout.pos)
        display_layout.bind(size=self._update_display_bg, pos=self._update_display_bg)

        # 温度标签,绑定到组件属性实现自动更新
        set_label = Label(text=f"设定温度: {self.set_temp:.1f}°C", color=(1,1,1,1), font_size=20)
        current_label = Label(text=f"当前温度: {self.current_temp:.1f}°C", color=(1,1,1,1), font_size=20)
        self.bind(set_temp=lambda _, val: set_label.setter('text')(set_label, f"设定温度: {val:.1f}°C"))
        self.bind(current_temp=lambda _, val: current_label.setter('text')(current_label, f"当前温度: {val:.1f}°C"))

        display_layout.add_widget(set_label)
        display_layout.add_widget(current_label)
        self.add_widget(display_layout)

        # 下部按钮区
        button_layout = BoxLayout(size_hint=(1, 0.3), spacing=5)
        for text in ["升温", "降温", "确认", "重置"]:
            button_layout.add_widget(Button(text=text))
        self.add_widget(button_layout)

    def _update_bg(self, *args):
        self.bg_rect.size = self.size
        self.bg_rect.pos = self.pos

    def _update_display_bg(self, instance, *args):
        self.display_rect.size = instance.size
        self.display_rect.pos = instance.pos

class PIDApp(App):
    def build(self):
        # 主布局,横向+纵向嵌套排列4个自定义组件
        main_layout = BoxLayout(orientation='vertical', spacing=10, padding=10)
        row1 = BoxLayout(spacing=10)
        row1.add_widget(PIDControllerDisplay())
        row1.add_widget(PIDControllerDisplay())
        row2 = BoxLayout(spacing=10)
        row2.add_widget(PIDControllerDisplay())
        row2.add_widget(PIDControllerDisplay())
        main_layout.add_widget(row1)
        main_layout.add_widget(row2)
        return main_layout

if __name__ == "__main__":
    PIDApp().run()

关键细节说明

  • 画布绑定:通过bind方法将Rectangle的尺寸、位置与所属布局的属性关联,布局尺寸变化时自动触发更新,实现背景自适应。
  • 相对尺寸:所有控件均使用size_hint设置相对占比,无需固定像素值,窗口缩放时自动调整。
  • 可绑定属性:定义set_temp、current_temp属性,外部只需修改组件的这些属性,就能自动更新内部标签文本,简化交互逻辑。

替代方案:用KV语言简化UI定义

如果偏好将UI与逻辑分离,可使用KV语言编写组件结构,代码更简洁:

Python代码(main.py)

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import NumericProperty, StringProperty

class PIDControllerDisplay(BoxLayout):
    set_temp = NumericProperty(25.0)
    current_temp = NumericProperty(23.5)

class PIDApp(App):
    def build(self):
        return BoxLayout(orientation='vertical', spacing=10, padding=10)

if __name__ == "__main__":
    PIDApp().run()

KV文件(pidapp.kv)

<PIDControllerDisplay>:
    orientation: 'vertical'
    size_hint: 0.45, 0.45
    padding: 10
    spacing: 10

    # 组件浅灰色背景
    canvas.before:
        Color:
            rgba: 0.8, 0.8, 0.8, 1
        Rectangle:
            size: self.size
            pos: self.pos

    # 上部黑色显示区
    BoxLayout:
        orientation: 'vertical'
        size_hint: 1, 0.6

        canvas.before:
            Color:
                rgba: 0, 0, 0, 1
            Rectangle:
                size: self.size
                pos: self.pos

        Label:
            text: f"设定温度: {root.set_temp:.1f}°C"
            color: 1,1,1,1
            font_size: 20
        Label:
            text: f"当前温度: {root.current_temp:.1f}°C"
            color: 1,1,1,1
            font_size: 20

    # 下部按钮区
    BoxLayout:
        size_hint: 1, 0.3
        spacing: 5

        Button: text: "升温"
        Button: text: "降温"
        Button: text: "确认"
        Button: text: "重置"

# 主布局排列4个组件
<BoxLayout>:
    BoxLayout:
        spacing: 10
        PIDControllerDisplay:
        PIDControllerDisplay:
    BoxLayout:
        spacing: 10
        PIDControllerDisplay:
        PIDControllerDisplay:

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 07:33:20