Search code examples
pythonmatplotlibkivy

Kivy update plot on press button


Starting to learn Kivy and can not update the graph on the button. Ideally, in the future, data should come from outside and be added to the graph without pressing any keys.Tried to use Clock.schedule_interval but it didn't work.

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

x=2
y=4
lst=[x,y]
plt.ylabel('some numbers')

class MainApp(App):

    def update(self):
        Clock.schedule_interval(self.plot,1)
        
    def on_press_button(self, instance):
        for i in range(20):
            global y
            global x
            global lst
            x=x*2
            y=y*2
            lst.append(x)
            lst.append(y)
            print(lst)

        return MainApp.build(self)
        print('Button pressed!')
    
    def build(self):
        plt.cla()
        plt.plot(lst)
        plt.ylabel('some numbers')
        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))

        main_layout.add_widget(FigureCanvasKivyAgg(plt.gcf(), size_hint=(.8,1)))
        print ('build called')
        return main_layout
        
if __name__ == '__main__':
    app = MainApp()
    app.run()


Solution

  • The build() method of an App is called automatically when you call the run() method of the App, and the widget returned by build() is set as the root widget of the App. If you call that same build() method directly, it does not affect the root widget of the App.

    One way to accomplish what you want is to create a new method that does the plotting and returns an instance of FigureCanvasKivyAgg. This new method can be called inside the build() method, and can also be called in the on_press_button() method and the returned FigureCanvasKivyAgg can then be used to replace the original. Here is a modified version of your code that does that:

    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
    
    x = 2
    y = 4
    lst = [x, y]
    plt.ylabel('some numbers')
    
    
    class MainApp(App):
    
        def build_plt(self):
            plt.cla()
            plt.plot(lst)
            plt.ylabel('some numbers')
            return FigureCanvasKivyAgg(plt.gcf(), size_hint=(.8,1))
    
        def on_press_button(self, instance):
            for i in range(20):
                global y
                global x
                global lst
                x = x * 2
                y = y * 2
                lst.append(x)
                lst.append(y)
                print(lst)
    
            # return MainApp.build(self)
            self.root.remove_widget(self.fcka)  # remove the current plot
            self.fcka = self.build_plt()  # create the updated plot
            self.root.add_widget(self.fcka)  # insert the new plot
            print('Button pressed!')
    
        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))
            self.fcka = self.build_plt()
    
            main_layout.add_widget(self.fcka)
            print ('build called')
            return main_layout
    
    
    if __name__ == '__main__':
        app = MainApp()
        app.run()