Search code examples
python-3.xmatplotlibtkinterfiguresubplot

Embedded Plot in Tkinter: disconnect y-axis & limit ticks and label to subplot


I have an application set up with an interactive plot window embedded. I would like to put a couple of subplots on top of eachother, with a shared x-axis and an independent y-axis. The whole thing is meant for data analysis.

I got the subplots set up, but for some reason the y-axis of the first subplot is somehow connected to the other subplots, but not the other way around. Additionally, the ticks and labels overlap instead of staying at their respective subplots.

I tried using the pyplot.subplots function, which takes care of the problem of the independent axes and ticks/labels, but this opens up another window when I call it instead of embedding it.

working example code that showcases the problem (Python 3.7):

# -*- coding: utf-8 -*-
import tkinter as tk
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
from matplotlib.figure import Figure
from tkinter import ttk
import numpy as np
import pandas as pd

class window(tk.Tk):
    def __init__(self,*args,**kwargs):
        tk.Tk.__init__(self,*args,**kwargs)
        self.generate_data()
        self.plotPane = self.plot_pane(self)

    def generate_data(self):
        self.data = pd.DataFrame()
        x = [1,2,3,4,5]
        y = np.zeros((len(x)))
        for i,yy in enumerate(y):
            y[i] = yy + 10.*float(np.random.random())
        self.data['x'] = x
        self.data['y'] = y

    class plot_pane():
        def __init__(self,parent):
            self.parent = parent
            self.labelframe = ttk.Labelframe(self.parent,text="Plot")

            replotButton = ttk.Button(self.parent,text="replot",command=self.update_plot)
            self.labelframe.grid(row=0, column=0)
            replotButton.grid(row=1,column=0)
            self.figure = Figure(figsize=(6,3),dpi=100)
            self.figure.subplots_adjust(left=0.11,bottom=0.09,right=0.77,top=0.92)# BaMa:. subplot margins, so labels are visible
            self.sub = self.figure.add_subplot(111)

            # toolbar and canvas for live plot
            canvas = FigureCanvasTkAgg(self.figure,self.labelframe)
            canvas.get_tk_widget().grid(row=1,column=1,rowspan=2,sticky="NSEW",pady=20)
            canvas._tkcanvas.grid(row=1,column=1,rowspan=2, sticky="NSEW")

            toolbar_frame = tk.Frame(self.labelframe)
            toolbar_frame.grid(row=0,column=1,sticky="SEW")
            toolbar = NavigationToolbar2Tk(canvas,toolbar_frame)
            toolbar.update()
            self.update_plot()

        def update_plot(self,*args):
            # clear figure
            self.figure.clf()

            stackAx = []
            for i in range(0,2):
                # generate new random data
                self.parent.generate_data()
                testX = self.parent.data['x']
                testY = self.parent.data['y']

                # add subplots
                if i == 0:
                    stackAx.append(self.figure.add_subplot(i+1,1,1))# y-axis of this subplot is somehow connected to the other
                else:
                    stackAx.append(self.figure.add_subplot(i+1,1,1,sharex=stackAx[0]))

                # plot, add labels, ticks and grid
                stackAx[i].plot(testX,testY)
                stackAx[i].set_ylabel("ax-"+str(i))
                stackAx[i].tick_params('y')
                stackAx[i].grid()
            self.figure.canvas.draw()


window = window()
window.mainloop()

So, when you move the subplot at the top, the bottom y-axis moves as well and the "ax-0" label + tickmarks breach the upper subplot. When you move the lower plot, the y-axis of the upper plot does not move (as it should be)


Solution

  • I figured it out. Apparently add_subplot and pyplot.subplots function somewhat differently and I didn't understand it correctly.

    The following update function works:

        def update_plot(self,*args):
            # clear figure
            self.figure.clf()
    
            stackAx = []
            numberOfPlots = 2
            for i in range(0,numberOfPlots):
                # generate new random data
                self.parent.generate_data()
                testX = self.parent.data['x']
                testY = self.parent.data['y']
    
                # add subplots
                if i == 0:
                    stackAx.append(self.figure.add_subplot(numberOfPlots,1,i+1))
                else:
                    stackAx.append(self.figure.add_subplot(numberOfPlots,1,i+1,sharex=stackAx[0]))
    
                # plot, add labels, ticks and grid
                stackAx[i].plot(testX,testY)
                stackAx[i].set_ylabel("ax-"+str(i))
                stackAx[i].tick_params('y')
                stackAx[i].grid()
            self.figure.canvas.draw()
    

    I got it from here: https://pythonprogramming.net/subplot2grid-add_subplot-matplotlib-tutorial/