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

为何Kivy布局画布绘制的线条会短暂显示后消失?

Kivy绘制线条短暂显示后消失的原因及解决方法

问题描述

点击按钮在布局画布上绘制线条,但线条短暂显示后就消失了。

相关代码

KV语言代码

#:import Window kivy.core.window.Window

MDScreenManager:
    BigScreen:

<BigScreen>:
    name: "page"

    # layout holding cards
    MDFloatLayout:
        id: game_layout
        pos_hint: {'center_x': .5, 'center_y': .5}
        size_hint: None, None
        size: Window.width, Window.height
        md_bg_color: "black"

        MDIconButton:
            id: hint
            icon: "language-python"
            pos_hint: {"center_x": .5, "y": .9}
            theme_icon_color: "Custom"
            icon_color: "orange"
            md_bg_color: "white"
            on_release:
                app.draw_line()

Python代码

class BigScreen(MDScreen):
    pass

class DrawingApp(MDApp):

    def build(self):
        return Builder.load_string(kv)

    def draw_line(self):
        game_layout = self.root.get_screen("page").ids.game_layout

        with game_layout.canvas:
            Line(
                points = [0, 0, 700, 1000], 
                width = 10,
                color = (1, 0, 0),
            )

if __name__ == '__main__':
    DrawingApp().run()

原因分析

  1. 画布重绘机制:Kivy布局的canvas属于静态绘制层,每次布局触发重绘(比如窗口调整、组件状态变化)时,会清空原有内容并重新渲染,临时添加的Line指令不会被持久化保存。
  2. 垃圾回收:创建的Line对象没有被任何变量引用,Python的垃圾回收机制会在短时间内回收这个对象,导致线条从画布上消失。

解决方法

方法一:保存线条引用并使用canvas.after

通过实例变量保存绘制的线条对象,同时将线条添加到canvas.after层(确保线条绘制在布局内容之上):

修改后的Python代码:

from kivy.graphics import Line
from kivy.uix.screenmanager import MDScreen
from kivymd.app import MDApp
from kivy.lang import Builder

kv = """
#:import Window kivy.core.window.Window

MDScreenManager:
    BigScreen:

<BigScreen>:
    name: "page"

    MDFloatLayout:
        id: game_layout
        pos_hint: {'center_x': .5, 'center_y': .5}
        size_hint: None, None
        size: Window.width, Window.height
        md_bg_color: "black"

        MDIconButton:
            id: hint
            icon: "language-python"
            pos_hint: {"center_x": .5, "y": .9}
            theme_icon_color: "Custom"
            icon_color: "orange"
            md_bg_color: "white"
            on_release:
                app.draw_line()
"""

class BigScreen(MDScreen):
    pass

class DrawingApp(MDApp):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.drawn_lines = []  # 保存线条引用,避免被垃圾回收

    def build(self):
        return Builder.load_string(kv)

    def draw_line(self):
        game_layout = self.root.get_screen("page").ids.game_layout
        
        line = Line(points=[0, 0, 700, 1000], width=10, color=(1, 0, 0))
        game_layout.canvas.after.add(line)
        self.drawn_lines.append(line)

if __name__ == '__main__':
    DrawingApp().run()

方法二:使用独立的绘制Widget

创建一个专门用于绘制的Widget,所有动态绘制内容都添加到这个Widget的画布上,避免布局重绘时丢失内容:

修改后的代码:

from kivy.graphics import Line
from kivy.uix.widget import Widget
from kivy.uix.screenmanager import MDScreen
from kivymd.app import MDApp
from kivy.lang import Builder

kv = """
#:import Window kivy.core.window.Window

MDScreenManager:
    BigScreen:

<BigScreen>:
    name: "page"

    MDFloatLayout:
        id: game_layout
        pos_hint: {'center_x': .5, 'center_y': .5}
        size_hint: None, None
        size: Window.width, Window.height
        md_bg_color: "black"

        DrawingWidget:
            id: drawing_canvas
            size: game_layout.size
            pos: game_layout.pos

        MDIconButton:
            id: hint
            icon: "language-python"
            pos_hint: {"center_x": .5, "y": .9}
            theme_icon_color: "Custom"
            icon_color: "orange"
            md_bg_color: "white"
            on_release:
                app.draw_line()
"""

class DrawingWidget(Widget):
    pass

class BigScreen(MDScreen):
    pass

class DrawingApp(MDApp):
    def build(self):
        return Builder.load_string(kv)

    def draw_line(self):
        drawing_canvas = self.root.get_screen("page").ids.drawing_canvas
        
        with drawing_canvas.canvas:
            Line(points=[0, 0, 700, 1000], width=10, color=(1, 0, 0))

if __name__ == '__main__':
    DrawingApp().run()

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 19:43:11