使用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
相关产品推荐
相关产品推荐

