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

使用Kivy与Matplotlib开发时图表无法绘制的问题求助

问题根源与修复方案

你的代码存在几个关键问题,导致画布无图形或调用draw()时崩溃:

1. 未将Matplotlib画布实例绑定到类属性

在GraphWidget1的__init__方法中,你创建了canvas = FigureCanvasKivyAgg(self.fig),但没有将其赋值给self.canvas。后续调用self.canvas.draw时,实际访问的是Kivy Widget自带的canvas属性(并非Matplotlib画布),而非你创建的Matplotlib画布实例。加括号调用self.canvas.draw()时,因调用了错误对象的方法直接导致崩溃。

2. 数据存储格式错误

self.x_data和self.y_data被定义为列表,但你每次append的是整个np.linspace生成的数组,最终self.x_data是数组的列表,而line.set_data()需要的是一维数组,无法正确渲染图形。

3. stop_update方法逻辑错误

if self.is_running()这里,is_running是布尔变量,不是函数,加括号调用会抛出TypeError。


修复后的完整代码

Python代码

import numpy as np
import matplotlib.pyplot as plt
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.boxlayout import BoxLayout
from kivy.clock import Clock
from kivy.lang import Builder
from matplotlib.backends.backend_kivyagg import FigureCanvasKivyAgg
import socket

button_log = []


class HomeScreen(Screen):
    def button_log(self, button):
        button_text = button.text
        global button_log
        button_log.append(button_text)
        return button_text, button_log


class LinePlotScreen(Screen):
    def on_pre_enter(self):
        graph_widget = self.ids.graph_widget
        graph_widget.start_update()


class WindowManager(ScreenManager):
    pass


class GraphWidget1(BoxLayout):
    def __init__(self, **kwargs):
        super(GraphWidget1, self).__init__(**kwargs)

        self.fig, self.ax = plt.subplots()
        self.line, = self.ax.plot([], [])
        # 将Matplotlib画布绑定到类属性self.canvas
        self.canvas = FigureCanvasKivyAgg(self.fig)
        self.add_widget(self.canvas)

        # 初始化数据为numpy一维数组,匹配set_data的参数要求
        self.x_data = np.array([])
        self.y_data = np.array([])

        self.is_running = False

    def start_update(self):
        if not self.is_running:
            self.is_running = True
            # 用schedule_interval实现定时更新,替代单次调度的循环逻辑
            Clock.schedule_interval(self.update_graph, 0.1)

    def stop_update(self):
        if self.is_running:
            self.is_running = False
            Clock.unschedule(self.update_graph)

    def update_graph(self, dt):
        # 生成带时间偏移的正弦数据,模拟实时变化
        x_new = np.linspace(0, 10, 100)
        y_new = np.sin(x_new + dt)

        # 替换数据(若需累加历史数据,可改用np.append)
        self.x_data = x_new
        self.y_data = y_new

        self.line.set_data(self.x_data, self.y_data)
        self.ax.relim()
        self.ax.autoscale_view(True, True, True)
        # 正确调用Matplotlib画布的重绘方法
        self.canvas.draw()


kv = Builder.load_file('Simplified.kv')


class TestApp(App):
    def build(self):
        self.stop_value = 0
        self.data_int = None
        self.left_shoe = []
        self.right_shoe = []
        return kv

    def on_button_press(self):
        global button_log
        if button_log[-1] == 'Start Data Collection':
            UDP_IP = "192.168.1.100"
            UDP_PORT = 3333
            sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            sock.bind((socket.gethostname(), UDP_PORT))

            def collect_data(dt):
                nonlocal sock
                nonlocal self
                global button_log

                data, addr = sock.recvfrom(1024)
                # 确保fmt_str函数已正确定义并导入
                self.data_int = fmt_str(data)
                if self.data_int[0] == 1:
                    self.left_shoe.append(self.data_int)
                elif self.data_int[0] == 2:
                    self.right_shoe.append(self.data_int)
                if self.stop_value == 1:
                    Clock.unschedule(collect_data)

            Clock.schedule_interval(collect_data, 0.1)

        elif button_log[-1] == 'Stop Data Collection':
            self.stop_value = 1
        elif button_log[-1] == 'Plot Line Charts':
            self.root.current = 'lineplot'
            line_plot_screen = self.root.get_screen('lineplot')
            graph_widget = line_plot_screen.ids.graph_widget


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

KV文件(无需修改)

WindowManager:
    HomeScreen:
    LinePlotScreen:

<HomeScreen>:
    name: 'home'
    BoxLayout:
        orientation: 'vertical'
        size: root.width,root.height

        Label:
            text: 'Home'
            font_size: 32

        Button:
            id: Start
            text: 'Start Data Collection'
            font_size: 32
            pos_hint: {'center_x':0.5}
            size_hint: (0.5,1)
            valign: 'center'
            on_press: root.button_log(self)
            on_release: app.on_button_press()

        Button:
            id: Stop
            text: 'Stop Data Collection'
            font_size: 32
            pos_hint: {'center_x':0.5}
            size_hint: (0.5,1)
            valign: 'center'
            on_press: root.button_log(self)
            on_release: app.on_button_press()

        Button:
            id: plot_line
            text: 'Plot Line Charts'
            font_size: 32
            pos_hint: {'center_x':0.5}
            size_hint: (0.5,1)
            valign: 'center'
            on_press:
                root.button_log(self)
                app.on_button_press()

<LinePlotScreen>:
    name: 'lineplot'
    BoxLayout:
        id: box
        orientation: 'vertical'
        size: root.width,root.height

        Label:
            text: 'Line Graphs'
            font_size: 32

        GraphWidget1:
            id: graph_widget

关键修改说明

  • 将Matplotlib画布实例赋值给self.canvas,确保后续操作的是正确的对象。
  • 把self.x_data和self.y_data改为numpy一维数组,匹配line.set_data()的参数要求。
  • 修复stop_update中错误的self.is_running()调用,改为直接判断布尔值,并添加取消调度的逻辑。
  • 使用Clock.schedule_interval简化实时更新逻辑,替代单次调度的循环调用。
  • 调用self.canvas.draw()时添加括号,正确触发Matplotlib画布重绘。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 20:37:02