Search code examples
pythonmatplotlibkivy

Kivy update matplotlib plot


Starting to learn Kivy, I want to create a simple application consisting of a start page (very simple) and a second page where I plot data with a button to delete the old plot and display a new plot.

After some problems to control the size and position of the plot widget, I can show the 2 pages and the buttons. The problem is when I click on the "Update button", a new plot is displayed, but a second figure is added next to the old one. The same kind of problem appears when I return to the second page several times. Do you have any idea on how to reuse the old figure and update/reset it or how to fix this ?

Here is my code :

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen

from kivy.uix.boxlayout import BoxLayout
from kivy.uix.widget import Widget
from kivy.properties import ObjectProperty
from kivy.garden.matplotlib.backend_kivyagg import FigureCanvasKivyAgg
import matplotlib.pyplot as plt
import numpy as np

                                            
## Here for providing colour to the background  
from kivy.core.window import Window 

## Setting the window size 
Window.clearcolor = (1, 1, 1, 1) # White background
Window.size = (960, 540) 


kv = """
#:import SlideTransition kivy.uix.screenmanager.SlideTransition

<Manager>:
    transition: SlideTransition()

    StartPage:
        name: 'StartPage'

    SecondPage:
        name: 'SecondPage'

<StartPage>:

    canvas:
        
        Color:
            rgba: 0.1, 0.1, 1, 0.7
            
        Rectangle:
            pos: 0, 11*root.height/12
            size: root.width, root.height/12
            
        Rectangle:
            pos: 0, 0
            size: root.width, root.height/12

        
    BoxLayout:
        orientation: 'vertical'
        size: root.width, root.height


    Button:
        size_hint: 0.15, 0.08
        pos_hint: {'left': 0, 'top': 1 }
        text: 'Go to Second Page'
        on_release:
            root.manager.current = 'SecondPage'
        
    Button:
        size_hint: 0.15, 0.08
        pos_hint: {'right': 1, 'bottom': 1 }
        text: 'Quit'
        on_release: app.stop()
    
                
    Label:
        text: 'Start Screen'
        font_size: '20sp'
        color: 0,0,0,1
        pos_hint:{'x': 0.42, 'y': 0.46 }

    

<SecondPage>:
    box: box

    canvas.before:
        Color:
            rgba: 1, 1, 1, 1
        Rectangle:
            pos: self.pos
            size: self.size

    BoxLayout:
        id: box
        size_hint: 0.8, 0.75
        pos_hint:{'x': 0, 'y': 0.1 }

    Button:
        text:'Start Menu'
        font_size: 20
        bold: True
        size_hint: 0.15, 0.08
        pos_hint: {'left': 0, 'top': 1 }
        color: 1, 1, 1, 1
        on_press: root.manager.current = 'StartPage'
        
    Button:
        text:'Update'
        font_size: 20
        bold: True
        size_hint: 0.15, 0.08
        pos_hint: {'x':0.8, 'y': 0.5 }
        color: 1, 1, 1, 1
        on_press: root.Update()
"""

Builder.load_string(kv)

# Start Page
class StartPage(Screen):      
    pass

#Second Page
class SecondPage(Screen):
    box = ObjectProperty(None)


    def add_plot(self, N):
    
        phase = np.random.normal(-np.pi/2, +np.pi/2)
        noise = np.random.normal(0, 1, N)
        nbr = np.linspace(0,1,N)
        func = 0.01*noise+np.sin(nbr/0.1+phase)
    
        plt.plot(nbr,func,label='Line1',color='r')
        plt.ylabel('Sinus')
        plt.xlabel('Range')
        plt.grid(True)
        self.fig1 = plt.gcf()
    
        return self.fig1

    def on_pre_enter(self, *args):
    
        self.fig1 = SecondPage.add_plot(self,1000)
        self.box.add_widget(FigureCanvasKivyAgg(self.fig1))
    
    def Update(self):
    
        self.box.remove_widget(FigureCanvasKivyAgg(self.fig1))
    
        self.fig1 = SecondPage.add_plot(self,1000)
        self.box.add_widget(FigureCanvasKivyAgg(self.fig1))


class Manager(ScreenManager):
    pass


class MyNewApp(App):
    title = "Matplolib - plot Update"

    def build(self):
        return Manager()    

MyNewApp().run()

Solution

  • One method that does work which is a relatively easy fix: if you clear all the child widgets with self.box.clear_widgets() instead of specifying the widget, then call plt.cla() to remove your previous data from the plot, should do what you're after.

    Edited code below:

    from kivy.app import App
    from kivy.lang import Builder
    from kivy.uix.screenmanager import ScreenManager, Screen
    
    from kivy.uix.boxlayout import BoxLayout
    from kivy.uix.widget import Widget
    from kivy.properties import ObjectProperty
    from kivy.garden.matplotlib.backend_kivyagg import FigureCanvasKivyAgg
    import matplotlib.pyplot as plt
    import numpy as np
    
    
    ## Here for providing colour to the background  
    from kivy.core.window import Window 
    
    ## Setting the window size 
    Window.clearcolor = (1, 1, 1, 1) # White background
    Window.size = (960, 540) 
    
    
    kv = """
    #:import SlideTransition kivy.uix.screenmanager.SlideTransition
    
    <Manager>:
        transition: SlideTransition()
    
        StartPage:
            name: 'StartPage'
    
        SecondPage:
            name: 'SecondPage'
    
    <StartPage>:
    
        canvas:
    
            Color:
                rgba: 0.1, 0.1, 1, 0.7
    
            Rectangle:
                pos: 0, 11*root.height/12
                size: root.width, root.height/12
    
            Rectangle:
                pos: 0, 0
                size: root.width, root.height/12
    
    
        BoxLayout:
            orientation: 'vertical'
            size: root.width, root.height
    
    
        Button:
            size_hint: 0.15, 0.08
            pos_hint: {'left': 0, 'top': 1 }
            text: 'Go to Second Page'
            on_release:
                root.manager.current = 'SecondPage'
    
        Button:
            size_hint: 0.15, 0.08
            pos_hint: {'right': 1, 'bottom': 1 }
            text: 'Quit'
            on_release: app.stop()
    
    
        Label:
            text: 'Start Screen'
            font_size: '20sp'
            color: 0,0,0,1
            pos_hint:{'x': 0.42, 'y': 0.46 }
    
    
    
    <SecondPage>:
        box: box
    
        canvas.before:
            Color:
                rgba: 1, 1, 1, 1
            Rectangle:
                pos: self.pos
                size: self.size
    
        BoxLayout:
            id: box
            size_hint: 0.8, 0.75
            pos_hint:{'x': 0, 'y': 0.1 }
    
        Button:
            text:'Start Menu'
            font_size: 20
            bold: True
            size_hint: 0.15, 0.08
            pos_hint: {'left': 0, 'top': 1 }
            color: 1, 1, 1, 1
            on_press: root.manager.current = 'StartPage'
    
        Button:
            text:'Update'
            font_size: 20
            bold: True
            size_hint: 0.15, 0.08
            pos_hint: {'x':0.8, 'y': 0.5 }
            color: 1, 1, 1, 1
            on_press: root.Update()
    """
    
    Builder.load_string(kv)
    
    # Start Page
    class StartPage(Screen):      
        pass
    
    #Second Page
    class SecondPage(Screen):
        box = ObjectProperty(None)
    
    
        def add_plot(self, N):
    
            phase = np.random.normal(-np.pi/2, +np.pi/2)
            noise = np.random.normal(0, 1, N)
            nbr = np.linspace(0,1,N)
            func = 0.01*noise+np.sin(nbr/0.1+phase)
    
            plt.plot(nbr,func,label='Line1',color='r')
            plt.ylabel('Sinus')
            plt.xlabel('Range')
            plt.grid(True)
            self.fig1 = plt.gcf()
    
            return self.fig1
    
        def on_pre_enter(self, *args):
    
            self.fig1 = SecondPage.add_plot(self,1000)
            self.box.add_widget(FigureCanvasKivyAgg(self.fig1))
    
        def Update(self):
            #self.box.remove_widget(FigureCanvasKivyAgg(self.fig1))
            plt.cla()
            self.box.clear_widgets()
            self.fig1 = SecondPage.add_plot(self,1000)
            self.box.add_widget(FigureCanvasKivyAgg(self.fig1))
    
    
    class Manager(ScreenManager):
        pass
    
    
    class MyNewApp(App):
        title = "Matplolib - plot Update"
    
        def build(self):
            return Manager()    
    
    MyNewApp().run()