pythonmatplotlibkivydesktop-application

Matplotlib NavigationToolbar in Kivy


I've been using Kivy for a couple days and am currently working on an application that requires embedding a Matplotlib plot within it. With help from online resources, I've managed to embed a plot within my kivy app, but I'm having trouble with the NavigationToolbar.

Problem Details:

## Imports
## ...

class MyApp(App):
    
    def build(self):
        box = BoxLayout(orientation='vertical')
        box.add_widget(Label(text='My App', size_hint_y=.1))

        plt.plot([1, 3, 2, 4])

        canvas = FigureCanvasKivyAgg(plt.gcf())
        toolbar = NavigationToolbar2Kivy(canvas)

        box.add_widget(canvas)
        box.add_widget(toolbar.actionbar)
        
        return box

MyApp().run()

The code above yields the following output:

Output

As you can see, only a black rectangle is visible in place of the NavigationToolbar. I can't see any of the buttons (i.e, tools - "Pan", "Zoom", "Home", etc.).


What I Tried:

I did think that maybe the colour of the buttons was the same as the background colour of the NavigationToolbar, so I tried changing background colour like so:

toolbar.actionbar.background_image = ''
toolbar.actionbar.background_color = (1, 1, 1, .5)

box.add_widget(toolbar.actionbar)

But that just resulted in a grey coloured rectangle instead. I still can't see any of the NavigationToolbar's buttons to interact with the plot.


EDIT:

There is a question similar to this here, but the solution provided does not seem to work in my case.

The answer to the post suggested that the NavigationToolbar only works on a parent layout, which I have tried like shown below:

class MyApp(App):

    def build(self):
        box = BoxLayout(orientation='vertical')
        child_box = BoxLayout(orientation='vertical')
        box.add_widget(child_box)  ## parent layout: box, child layout: child_box
        
        child_box.add_widget(Label(text='My App', size_hint_y=.1))

        plt.plot([1, 3, 2, 4])

        canvas = FigureCanvasKivyAgg(plt.gcf())
        toolbar = NavigationToolbar2Kivy(canvas)
        
        ## toolbar.actionbar.background_image = ''
        ## toolbar.actionbar.background_color = (1, 1, 1, .5)

        child_box.add_widget(canvas)
        box.add_widget(toolbar.actionbar)

        return box

MyApp().run()

But this yields the same result as the one shown in my output image (with the rectangular area being grey in colour if I uncomment the commented lines).


Solution

  • The problem was with the version of matplotlib. This error is encountered in matplotlib versions from 3.3.3 onwards. Hence, the solution is to downgrade matplotlib to 3.3.2.

    In order to do this, you might need to downgrade python to 3.8 or so.

    The workaround is as follows:

    1. Try pip install "matplotlib<3.3.3"
      If it doesn't work (error in building wheels), download and install Python 3.8
    2. Install the older matplotlib version using pip install "matplotlib<3.3.3"
    3. Run your code

    The same code worked for me using matplotlib 3.3.2 and Python 3.8:

    import matplotlib.pyplot as plt
    import kivy
    from kivy.app import App
    from kivy.uix.boxlayout import BoxLayout
    from kivy.uix.label import Label
    from kivy.garden.matplotlib.backend_kivyagg import FigureCanvasKivyAgg
    from kivy.garden.matplotlib.backend_kivyagg import NavigationToolbar2Kivy
    
    class MyApp(App):
        
        def build(self):
            box = BoxLayout(orientation='vertical')
            box.add_widget(Label(text='My App', size_hint_y=.1))
    
            plt.plot([1, 3, 2, 4])
    
            canvas = FigureCanvasKivyAgg(plt.gcf())
            toolbar = NavigationToolbar2Kivy(canvas)
    
            box.add_widget(canvas)
            box.add_widget(toolbar.actionbar)
            
            return box
    
    MyApp().run()
    

    Output Image Output Image

    The background colour of the NavigationToolbar can be changed using:

    toolbar.actionbar.background_image = ''
    toolbar.actionbar.background_color = (1, 1, 1, 1)  ## White background