Event handler is not working as expected when trying to emulate matplotlib interactive feature with Kivy

Viewed 67

I am trying to emulate matplotlib interactive feature with kivy where I intended to plot the graph on a popup window which has two button: one for next plot and another one for leaving the popup window and a layout for plotting the actual graph. There is a kivy switch in root window which will launch the popup window. It's initially in off state and I would like to keep it on off state upon leaving the popup window. The code is working fine but I have to press the close button twice in popup window for successfully leaving the window which should be happened on single click.

from kivy.app import App
from kivy.properties import ObjectProperty
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from matplotlib import pyplot as plt
import numpy as np
from kivy.garden.matplotlib import FigureCanvasKivyAgg
from kivy.uix.popup import Popup

Builder.load_file('label_custom.kv')


def add_plot():
    signals = [[7, 89.6, 45.-56.34],
               [16, 30.6, 25.-56.34], [20, 39.6, 15.-56.34], ]

    for sig in signals:
        plt.clf()
        signal = np.array(sig)

        # this will plot the signal on graph
        plt.plot(signal)

        # setting x label
        plt.xlabel('Time(s)')

        # setting y label
        plt.ylabel('signal (norm)')
        plt.grid(True, color='lightgray')
        fig1 = plt.gcf()
        yield fig1


class MyPopup(Popup):
    start_btn = ObjectProperty(None)
    graph_layout = ObjectProperty(None)
    dismiss_button = ObjectProperty(None)

    def __init__(self, **kwargs):
        super(MyPopup, self).__init__(**kwargs)
        self.plot = add_plot()

    def testing(self):
        self.graph_layout.clear_widgets()
        fig = next(self.plot)
        self.graph_layout.add_widget(FigureCanvasKivyAgg(fig))

    def test(self):
        self.dismiss()
        App.get_running_app().root.switch.active = False


class MyBoxLayout(BoxLayout):
    switch = ObjectProperty(None)


class MyApp(App):
    def build(self):
        box = MyBoxLayout()
        return box


if __name__ == '__main__':
    MyApp().run()

and the kivy file:

#:import Factory kivy.factory.Factory
<MyPopup>:
    start_btn: start_btn
    graph_layout: graph_layout
    dismiss_button: _dismiss_button
    auto_dismiss: False
    size_hint:.8, .8
    BoxLayout:
        canvas.before:
            Color:
                rgba: (1, 1, 1, 1)
            Rectangle:
                size: self.size
                pos: self.pos

        orientation: "vertical"
        padding: 20
        spacing: 10
        BoxLayout:
            id: graph_layout
            size_hint_y: .8
        Button:
            id: start_btn
            text:"Click"
            size_hint_y: .1
            pos_hint:{'center_x': .5}
            on_release: root.testing()
        Button:
            id: _dismiss_button
            text: 'Close me!'
            size_hint_y: .1
            pos_hint:{'center_x': .5, 'y': 0}
            on_release: root.test()

<MyBoxLayout>:
    switch: tog_switch
    canvas.before:
        Color:
            rgba: (1, 1, 1, 1)
        Rectangle:
            size: self.size
            pos: self.pos

    orientation: "vertical"
    padding: 20
    spacing: 10
    Switch:
        id: tog_switch
        on_active: Factory.MyPopup().open()


1 Answers

The Solution is quite simple:

from kivy.app import App
from kivy.properties import ObjectProperty
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from matplotlib import pyplot as plt
import numpy as np
from kivy.garden.matplotlib import FigureCanvasKivyAgg
from kivy.uix.popup import Popup

Builder.load_file('label_custom.kv')


def add_plot():
    signals = [[7, 89.6, 45.-56.34],
               [16, 30.6, 25.-56.34], [20, 39.6, 15.-56.34], ]

    for sig in signals:
        plt.clf()
        signal = np.array(sig)

        # this will plot the signal on graph
        plt.plot(signal)

        # setting x label
        plt.xlabel('Time(s)')

        # setting y label
        plt.ylabel('signal (norm)')
        plt.grid(True, color='lightgray')
        fig1 = plt.gcf()
        yield fig1


class MyPopup(Popup):

    def __init__(self, **kwargs):
        super(MyPopup, self).__init__(**kwargs)
        self.plot = add_plot()

    def testing(self):
        self.graph_layout.clear_widgets()
        try:
            fig = next(self.plot)
        except StopIteration:
            App.get_running_app().root.set_switch_state()
        else:
            self.graph_layout.add_widget(FigureCanvasKivyAgg(fig))


class MyBoxLayout(BoxLayout):
    switch = ObjectProperty(None)

    def __init__(self, **kwargs):
        super(MyBoxLayout, self).__init__(**kwargs)
        self.popup = MyPopup()

    def set_switch_state(self):
        self.switch.active = not(self.switch.active)
        self.popup.dismiss()


class MyApp(App):
    def build(self):
        box = MyBoxLayout()
        return box


if __name__ == '__main__':
    MyApp().run()

And the kv file:

<MyPopup>:
    graph_layout: _graph_layout
    auto_dismiss: False
    size_hint:.8, .8
    BoxLayout:
        canvas.before:
            Color:
                rgba: (1, 1, 1, 1)
            Rectangle:
                size: self.size
                pos: self.pos

        orientation: "vertical"
        padding: 20
        spacing: 10
        BoxLayout:
            id: _graph_layout
            size_hint_y: .8
        Button:
            text:"Click"
            size_hint_y: .1
            pos_hint:{'center_x': .5}
            on_release: root.testing()
        Button:
            text: 'Close me!'
            size_hint_y: .1
            pos_hint:{'center_x': .5, 'y': 0}
            on_release: app.root.set_switch_state()
                

<MyBoxLayout>:
    switch: tog_switch
    canvas.before:
        Color:
            rgba: (1, 1, 1, 1)
        Rectangle:
            size: self.size
            pos: self.pos

    orientation: "vertical"
    padding: 20
    spacing: 10
    Switch:
        id: tog_switch
        on_active: root.popup.open()
Related