我试图在matplotlib中真实的优化绘图。我尝试通过plt.plot
和plt.figure
实现。在plt.plot
的情况下,由于返回FigureCanvasKivyAgg(plt.gcf())
,图形被更新,这种方法处理得很糟糕,在100点之后,fps从30下降到10。使用plt.figure
(使用plt.canvas.draw
),情况稍微好一些,初始fps大约是70…80,大约500…600点后下降到15。
import matplotlib
from kivymd.app import MDApp
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.screenmanager import ScreenManager, Screen
from garden_matplotlib.backend_kivyagg import FigureCanvasKivyAgg
import matplotlib.pyplot as plt
from kivy.uix.button import Button
from kivy.properties import ObjectProperty, StringProperty
from kivy.clock import Clock
from kivy.uix.label import Label
import matplotlib.animation as animation
#-------------------------------------------------------------------------#
n = 0
x = 0
y = 0
s=0
lst = [[x],
[y]]
plt_limit_x=200
plt_limit_y=1000
fig=plt.figure()
#--------------------------------MENUSCREEN------------------_-------------#
class MenuScreen(Screen):
pass
box = ObjectProperty(None)
#------------------------------------PLOT----------------------------------#
class MyFigure(FigureCanvasKivyAgg): #Plot
def __init__(self, **kwargs):
global fig
global x
global y
y=x+x
x+=1
lst[0].append(x)
lst[1].append(y)
self.ax=fig.add_subplot(111)
self.line = self.ax.plot(lst[0], lst[1])
super(MyFigure, self).__init__(fig, **kwargs)
fig.canvas.draw()
def start_updating(self):
Clock.schedule_interval(self.Update, 1/30)
def stop_updating(self):
Clock.unschedule(self.Update)
def Update(self, *args):
global fig, x, y, lst, n
self.line.clear()
n=n+1
y=x+x
x+=1
lst[0].append(x)
lst[1].append(y)
self.line = self.ax.plot(lst[0], lst[1], color='black')
return fig.canvas.draw()
#------------------------------------MAINAPP----------------------------------#
class MainApp(MDApp):
def build(self):
sm = ScreenManager()
sm.add_widget(MenuScreen(name='menu'))
return sm
def on_start(self):
self.fps_monitor_start()
#---------------------------------END-------------------------------------#
if __name__=='__main__':
app = MainApp()
app.run()
我决定尝试实现matplotlib.animation,遵循我写的plt.show()派生的例子,一切都很正常。
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
x=0
y=0
data = ([x],[y])
def update_line(*args, **kwargs):
global x
global y
x=x+1
y=y+2
data[0].append(x)
data[1].append(y)
l.set_data(data)
#print (l)
return l,
fig1 = plt.figure()
l, = plt.plot([], [], 'r-')
plt.xlim(0, 10000)
plt.ylim(0, 10000)
plt.xlabel('x')
plt.title('test')
line_ani = animation.FuncAnimation(fig1, update_line, 10000, fargs=(data, l),
interval=1, blit=True)
plt.show()
但是当我试图将它翻译成kivy时,图不想被更新,有一个假设问题出在init方法,但我不知道没有它如何实现程序。
import matplotlib
from kivymd.app import MDApp
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.screenmanager import ScreenManager, Screen
from garden_matplotlib.backend_kivyagg import FigureCanvasKivyAgg
import matplotlib.pyplot as plt
from kivy.uix.button import Button
from kivy.properties import ObjectProperty, StringProperty
from kivy.clock import Clock
from kivy.uix.label import Label
import matplotlib.animation as animation
#-------------------------------------------------------------------------#
n = 0
x = 0
y = 0
s=0
lst = [[x],
[y]]
fig=plt.figure()
data = ([x],[y])
l, = plt.plot([], [], 'r-')
#--------------------------------MENUSCREEN------------------_-------------#
class MenuScreen(Screen):
pass
box = ObjectProperty(None)
#------------------------------------PLOT----------------------------------#
class MyFigure(FigureCanvasKivyAgg):
def __init__(self, **kwargs):
global l
plt.xlim(0, 10000)
plt.ylim(0, 10000)
plt.xlabel('x')
plt.title('test')
super(MyFigure, self).__init__(fig, **kwargs)
def start_updating(self):
Clock.schedule_interval(self.Update, 1/30)
def stop_updating(self):
Clock.unschedule(self.Update)
def Update(self, *args, **kwargs):
global x, y, l #data
x=x+1
y=y+2
data[0].append(x)
data[1].append(y)
#print (data)
l.set_data(data)
print (l)
return l,
line_ani = animation.FuncAnimation(fig,
Update,
10000,
fargs=(data, l),
interval=1,
blit=True)
fig.canvas.draw()
#------------------------------------MAINAPP----------------------------------#
class MainApp(MDApp):
def build(self):
sm = ScreenManager()
sm.add_widget(MenuScreen(name='menu'))
return sm
def on_start(self):
self.fps_monitor_start()
#---------------------------------END-------------------------------------#
if __name__=='__main__':
app = MainApp()
app.run()
请帮助优化程序。可以使用fig.canvas.draw()来实现这一点。但在理想的情况下,人们希望使用matplotlib.animation。
UPD,我完全忘了,这是主要的.kv文件代码
<Manager>:
transition: SlideTransition()
MenuScreen:
name: 'MenuScreen'
<MenuScreen>:
box: box
BoxLayout:
orientation: 'horizontal'
GridLayout:
cols: 1
size_hint: .2, 1
Button:
text: 'Start measurement'
on_press: myfig.start_updating()
Button:
text: 'Stop measurement'
on_press: myfig.stop_updating()
Button:
text: 'Quit'
on_press: app.root_window.close ()
BoxLayout:
size_hint: .8, 1
id:box
MyFigure:
id:myfig
UPD.在使用Tkinter Embedding a matplotlib animation into a tkinter frame时发现类似问题,转换代码后添加init_plot函数,
import matplotlib
from kivymd.app import MDApp
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.screenmanager import ScreenManager, Screen
from garden_matplotlib.backend_kivyagg import FigureCanvasKivyAgg
import matplotlib.pyplot as plt
from kivy.uix.button import Button
from kivy.properties import ObjectProperty, StringProperty
from kivy.clock import Clock
from kivy.uix.label import Label
import matplotlib.animation as animation
#-------------------------------------------------------------------------#
n = 0
x = 0
y = 0
s=0
lst = [[x],
[y]]
fig=plt.figure()
data = ([x],[y])
l, = plt.plot([], [], 'r-')
#--------------------------------MENUSCREEN------------------_-------------#
class MenuScreen(Screen):
pass
box = ObjectProperty(None)
#------------------------------------PLOT----------------------------------#
class MyFigure(FigureCanvasKivyAgg):
def __init__(self, **kwargs):
super(MyFigure, self).__init__(fig, **kwargs)
self.init_plot()
def start_updating(self):
Clock.schedule_interval(self.Update, 1/30)
def stop_updating(self):
Clock.unschedule(self.Update)
#fig.canvas.draw()
def Update(self, *args, **kwargs):
global x, y, l #data
x=x+1
y=y+2
data[0].append(x)
data[1].append(y)
#print (data)
l.set_data(data)
print (l)
return l,
def init_plot(self):
global fig
global l
plt.xlim(0, 10000)
plt.ylim(0, 10000)
plt.xlabel('x')
plt.title('test')
line_ani = animation.FuncAnimation(fig,
self.Update,
10000,
fargs=(data, l),
interval=1,
blit=True)
#------------------------------------MAINAPP----------------------------------#
class MainApp(MDApp):
def build(self):
sm = ScreenManager()
sm.add_widget(MenuScreen(name='menu'))
return sm
def on_start(self):
self.fps_monitor_start()
#---------------------------------END-------------------------------------#
if __name__=='__main__':
app = MainApp()
app.run()
我得到以下错误:
File "C:\Python\Stackoverflow question\13.05.2023\garden_matplotlib\backend_kivy.py", line 1078, in _timer_set_interval
if self._timer is not None:
AttributeError: 'TimerKivy' object has no attribute '_timer'. Did you mean: '_on_timer'?
如果我把line_ani
放到build
函数中,我会得到类似的错误。
我试图在简单的Kivy项目中使用matplotlib.animation,但也得到了同样的错误
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.gridlayout import GridLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.garden.matplotlib.backend_kivyagg import FigureCanvasKivyAgg
import matplotlib.pyplot as plt
from kivy.clock import Clock
import matplotlib.animation as animation
n = 0
x = 0
y = 0
s=0
lst = [[x],
[y]]
fig=plt.figure()
data = ([x],[y])
l, = plt.plot([], [], 'r-')
canvas = FigureCanvasKivyAgg(fig)
class MainApp(App):
def init_plot(self):
global fig, l
plt.xlim(0, 10000)
plt.ylim(0, 10000)
plt.xlabel('x')
plt.title('test')
def on_press_button(self, instance):
Clock.schedule_interval(self.Update, 1/30)
def Update(self, *args, **kwargs):
global x, y, l #data
x=x+1
y=y+2
data[0].append(x)
data[1].append(y)
#print (data)
l.set_data(data)
print (l)
return l,
def build(self):
main_layout = BoxLayout(orientation='horizontal')
grid_layout=GridLayout(cols=1,
row_force_default=True,
row_default_height=100,
size_hint=(.2,1)
)
main_layout.add_widget(grid_layout)
grid_layout.add_widget(Label(text='Hello from Kivy'))
button1=grid_layout.add_widget(Button(text='Hello 1', on_press=self.on_press_button))
global canvas
main_layout.add_widget(canvas)
print ('build called')
return main_layout
line_ani = animation.FuncAnimation(fig,
Update,
10000,
fargs=(data, l),
interval=1,
blit=True)
if __name__ == '__main__':
app = MainApp()
app.run()
1条答案
按热度按时间klsxnrf11#
问题解决了,重点是,每次更新,都会建立新的图表。我用
print (self.ax.get_lines())
弄明白了。实际上这就是解决办法。我们没有用self.line = self.ax.plot(lst[0], lst[1], color='black')
在循环中构建新的图(我的意思是schedule_interval
),而是设置__init__
,self.line = self.ax.plot (lst[0], lst[1], color='black')
。请注意,line
后面有逗号。这对于返回Line2D
对象列表是必需的。然后,我们在循环中绘制图,如第一个选项:结果,获得了稳定的60 fps。下面的bug问题仍然是开放的。对我来说,看起来没有办法用
matplotlib.animation
在Kivy
中工作。UPD。绘制方法
fig.canvas.draw_idle()
在我的例子中工作得更快,稳定70 FPS。UPD我们最后知道什么
clear()
、cla()
或clf()
方法,因为它们非常慢。plt.plot
与FigureCanvasKivyAgg(plt.gcf())
是慢的。FigureCanvasKivyAgg(plt.fig())
。draw()
方法,它每次都会返回整个图,每次都会在每个循环中返回一个新的行。__init__
函数中使用self.ax=fig.add_subplot()
和self.line,= self.ax.plot()
并加上逗号。为了更新图表,我们使用self.line.set_data([],[])
在每个循环中添加数据。fig.canvas.draw_idle()
,它比fig.canvas.draw()
快一些(大约10-20%)。