Обработчик событий работает не так, как ожидалось, при попытке эмулировать интерактивную функцию matplotlib с помощью Kivy

#python #matplotlib #event-handling #kivy

Вопрос:

Я пытаюсь эмулировать интерактивную функцию matplotlib с помощью kivy, где я намеревался построить график во всплывающем окне, в котором есть две кнопки: одна для следующего графика, другая для выхода из всплывающего окна и макет для построения фактического графика. В корневом окне есть переключатель kivy, который запустит всплывающее окно. Он изначально находится в выключенном состоянии, и я хотел бы сохранить его в выключенном состоянии после выхода из всплывающего окна. Код работает нормально, но мне нужно дважды нажать кнопку «Закрыть» во всплывающем окне, чтобы успешно выйти из окна, что должно произойти одним щелчком мыши.

 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()
 

и файл киви:

 #: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:

Решение довольно простое:

 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()
 

И файл kv:

 <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()