Search code examples
pythonpython-3.xkivykivy-language

Update custom property from Kivy binding?


I would like to be able to update the custom widget class property whenever I passed the value to Kivy. I am not sure if this is the same as binding. I have read about Event Dispatcher and the on but I was not able to get it to work.

For example, given the code below I would like to pass in chart_type: 'barh' in Kivy and whenever I run the program it should show a horizontal bar.

However, my brain does not seem to understand how to do this. Can someone point me in the right direction?

from kivy.event import EventDispatcher
from kivy.lang import Builder
from kivymd.app import MDApp
from kivy.garden.matplotlib.backend_kivyagg import FigureCanvasKivyAgg
import matplotlib.pyplot as plt
import matplotlib.lines as mlines
from kivy.properties import (
    ListProperty,
    StringProperty,
)

kv = """
BoxLayout:
    MDBarChart:
        id: bar
        chart_type: 'barh' # Not working
    MDFlatButton:
        text: "Update"
        # on_press: app.mdchart.update()
"""


class MDBarChart(FigureCanvasKivyAgg, EventDispatcher):

    x_nums = ListProperty([1, 2, 3])
    y_nums = ListProperty([2, 5, 7])
    label_x = StringProperty('x')
    label_y = StringProperty('y')

    # Be able to change this in kivy lang and reflect on Kivy
    chart_type = StringProperty('bar')


    def on_chart_type(self, instance, value):
        self.chart_type = self.chart_type


    def update(self):
        blue_dot = mlines.Line2D(range(1), range(1), marker='o', color='#6200EA',
                                 markersize=15, label='Purple Dot', linewidth=0)

        x = self.x_nums
        y = self.y_nums

        if self.chart_type == "bar":
            plt.bar(x, y, label='Bars1', color='#6200EA')
        elif self.chart_type == "barh":
            plt.barh(x, y, label='Bars1', color='#6200EA')
        elif self.chart_type == "line":
            plt.plot(x, y, label='Bars1', color='#6200EA')
        elif self.chart_type == "stackplot":
            plt.stackplot(x, y, color=['#6200EA','#D1C4E9', '#5E35B1'])

        plt.xlabel(self.label_x)
        plt.ylabel(self.label_y)
        plt.legend(handles=[blue_dot], loc='upper right', prop={'size': 14})

    def remove_borders(self):
        for spine in plt.gca().spines.values():
            spine.set_visible(False)

        plt.tick_params(top=False, bottom=False, left=False, right=False)


    def __init__(self, **kwargs):
        super(MDBarChart, self).__init__(plt.gcf(), **kwargs)
        self.update()


class ProgressApp(MDApp):
    def build(self):
        return Builder.load_string(kv)


if __name__ == "__main__":
    ProgressApp().run()

Solution

  • Well, I guess waking up at 4:30 in the morning and drinking loads of caffeine makes my brain think better.

    I updated my init method to look as below:

        def __init__(self, **kwargs):
            super(MDBarChart, self).__init__(plt.gcf(), **kwargs)
            Clock.schedule_once(lambda *args:self.update())
    

    By updating that now I can update the KV Lang file and the changes reflect on the GUI.