0

so... I've been trying to get kivy to plot data generated in real time, using matplotlib. I've used similar code to this one before in TkInter and it worked like a charm so I'm really confused why it does not work here.

Here's the code:

import numpy as np
import matplotlib
matplotlib.use('module://kivy.garden.matplotlib.backend_kivy')
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.clock import Clock
import matplotlib.pyplot as plt

class MyApp(App):
    def build(self):
        box = BoxLayout()

        self.fig,self.ax = plt.subplots(1)
        self.plt_canvas = self.fig.canvas
        box.add_widget(self.plt_canvas)

        self.line = self.ax.plot([])[0]
        self.i = 0
        plt.show()
        Clock.schedule_interval(self.update, 1)

        return box

    def update(self, *args):
        self.line.set_xdata(np.arange(self.i))
        self.line.set_ydata(np.arange(self.i))
        self.i +=1

        plt.draw()

MyApp().run()

I wanted to get it to plot the line, 1 point each second. But instead I get this:

Seems like the canvas is not redrawing. What am I doing wrong?

eyllanesc
  • 235,170
  • 19
  • 170
  • 241
Matej Rajchl
  • 1
  • 1
  • 1
  • 1
    I have no clue whatsoever about kivy. But what I can say is that if using Tkinter with an embedded plot you would not use `plt.draw()` to update a plot. Rather you'd call `self.plt_canvas.draw_idle()`. So when saying it previously worked with Tkinter did you actually embed the plot into some GUI or did you just call all the pyplot methods from within some class like here? I could well imagine that the kivy backend in use does not even allow for `plt.draw()` at all. – ImportanceOfBeingErnest Dec 12 '17 at 23:47
  • 1
    By saying it worked before I meant, that calling the method using scheduler and using `self.line.set_xdata()` worked. Of course the code had to be editted to work with kivy. But according to official [example](https://github.com/kivy-garden/garden.matplotlib/blob/master/examples/test_plt.py) there is no trouble using `plt.draw()` . – Matej Rajchl Dec 13 '17 at 01:00
  • I'm currently facing the same problem, did you find a solution ? – MaxouMask Aug 24 '18 at 11:23

3 Answers3

1

Potentially this has nothing to do with kivy. As can be seen the axes limits are small and outside the range where the data resides. It makes sense to update the limits once data is changed. The easiest way would be to use

self.ax.autoscale()

inside the updating function.

ImportanceOfBeingErnest
  • 321,279
  • 53
  • 665
  • 712
1

As suggested by @ImportanceOfBeingErnest, the axes limits need to be updated.

The following code should solve your problem:

import matplotlib
matplotlib.use('module://kivy.garden.matplotlib.backend_kivy')
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.clock import Clock
import matplotlib.pyplot as plt

fig, ax = plt.subplots()
canvas = fig.canvas


class MyApp(App):
    def build(self):
        box = BoxLayout()
        self.i = 0
        self.line = [self.i]
        box.add_widget(canvas)
        plt.show()
        Clock.schedule_interval(self.update, 1)
        return box

    def update(self, *args):
        plt.plot(self.line, self.line)
        self.i += 1
        self.line.append(self.i)
        canvas.draw_idle()


MyApp().run()
Marine
  • 408
  • 4
  • 10
0

With python-3.7 kivy-2.0.0 matplotlib-3.5.1 Marines code throws

Traceback (most recent call last): File "c:\Users\wiredworks\Documents\Simulation-Studio2\StudioInterface\MatplotlibUpdate.py", line 32, in MyApp().run() File "C:\ProgramData\Anaconda3\envs\NewPanda3d\lib\site-packages\kivy\app.py", line 949, in run self._run_prepare() File "C:\ProgramData\Anaconda3\envs\NewPanda3d\lib\site-packages\kivy\app.py", line 919, in _run_prepare root = self.build() File "c:\Users\wiredworks\Documents\Simulation-Studio2\StudioInterface\MatplotlibUpdate.py", line 21, in build plt.show() File "C:\ProgramData\Anaconda3\envs\NewPanda3d\lib\site-packages\matplotlib\pyplot.py", line 368, in show return _backend_mod.show(*args, **kwargs) File "C:\ProgramData\Anaconda3\envs\NewPanda3d\lib\site-packages\matplotlib\backend_bases.py", line 3579, in call return self.show(block=block) File "C:\ProgramData\Anaconda3\envs\NewPanda3d\lib\site-packages\matplotlib\backend_bases.py", line 3544, in show cls.mainloop() TypeError: mainloop() missing 1 required positional argument: 'self'

to solve this:

import matplotlib
matplotlib.use('module://kivy.garden.matplotlib.backend_kivy')
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.clock import Clock
import matplotlib.pyplot as plt

fig, ax = plt.subplots()
canvas = fig.canvas
plt.plot()


class MyApp(App):
    def build(self):
        box = BoxLayout()
        self.i = 0
        self.line = [self.i]
        box.add_widget(canvas)
        Clock.schedule_interval(self.update,1)
        return box

    def update(self, *args):
        plt.plot(self.line, self.line)
        self.i += 1
        self.line.append(self.i)
        canvas.draw_idle()


MyApp().run()
martburg
  • 490
  • 3
  • 12