Windy71
Windy71

Reputation: 909

Confusion over plt and plot to get a plot to display on canvas on a frame in Tkinter

I have been working on a gui to display a graph of values by two indices. That works fine as a standalone py file. I have made a gui that will successfully plot and clear a plot on a frame on canvas. I cannot figure out how to merge the two. The issue is in my plotData function (I have added and commented out a version of it that works very well with a simpler graph). I know my problem is in these lines below because I am using both plt and plot1, but now I don't know anymore what either of them do.

#fig1=plt.figure(1)

if len(blob0)>0:
    plt.plot(blob0_et, blob0_acc, "s", color="blue", markersize=10, label = '0')
if len(blob1)>0:
    plt.plot(blob1_et, blob1_acc, "s", color="red", markersize=10, label = '1')
if len(blob2)>0:    
    plt.plot(blob2_et, blob2_acc, "s", color="green", markersize=10, label = '2')


plot1 = fig.add_subplot(111) # adding the subplot 
plot1.plot(y) # plotting the graph 
plot1.set_title ("Circle Calibration, Accumulator vs Edge Threshold", fontsize=12)
plot1.set_ylabel("Accumulator", fontsize=14)
plot1.set_xlabel("Edge Threshold", fontsize=14)

Full Code

import tkinter.filedialog
import os
import tkinter as tk                     
from tkinter import ttk 
import matplotlib
matplotlib.use("TkAgg")  # this is the backend of matplotlib
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
from matplotlib.figure import Figure
import matplotlib.animation as animation
from matplotlib import style
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import easygui
from matplotlib.legend_handler import HandlerLine2D
import simple_data
import ETvACCUM3



root = tk.Tk() 
root.title("Tab Widget")
root.geometry("500x350") 
tabControl = ttk.Notebook(root) 
  
tab1 = ttk.Frame(tabControl) 
tab2 = ttk.Frame(tabControl)
tab3 = ttk.Frame(tabControl) 
tab4 = ttk.Frame(tabControl)
 
tabControl.add(tab1, text ='Tab 1') 
tabControl.add(tab2, text ='Tab 2')
tabControl.add(tab3, text ='Tab 3') 
tabControl.add(tab4, text ='Tab 4')

tk.Grid.rowconfigure(root, 0, weight=1)
tk.Grid.columnconfigure(root, 0, weight=1)
tabControl.grid(column=0, row=0, sticky=tk.E+tk.W+tk.N+tk.S)

#=====================================================================
# TAB 2
#=====================================================================
#=====================================================================
# my_frame_1  and its contents
#=====================================================================

# creating a frame (my_frame_1)
my_frame_1 = tk.Frame(tab2, bd=2, relief=tk.GROOVE)
my_frame_1.pack(side=tk.LEFT, anchor=tk.N, fill=tk.BOTH, expand=True)
#---------------------------------------------------------------------

# the figure that will contain the plot 
fig = Figure(figsize = (5, 5), dpi = 100) 

#=====================================================================
# frame2 and widgets it contains.
#=====================================================================

def plotData():
    fig.clear()
    file = easygui.fileopenbox(msg=None, title=None, default="/Users/.../Desktop/tk_gui_grid/", filetypes = None, multiple = False)
    print('\n', "This is the selected file:", file, '\n')

    
    # load data as a pandas dataframe
    df = pd.read_csv(file, sep='\t', lineterminator='\n')
    # make a smaller array by using the loc 
    df = df.loc[:,['Accum', 'EdgeThr','NumberOfBlobs']]
    blob0 = []
    blob1 = []
    blob2 = []
    blob0 = df[df['NumberOfBlobs'] == 0][['Accum', 'EdgeThr']]
    blob1 = df[df['NumberOfBlobs'] == 1][['Accum', 'EdgeThr']]
    blob2 = df[df['NumberOfBlobs'] == 2][['Accum', 'EdgeThr']]
    blob0 = blob0.values.tolist()
    blob1 = blob1.values.tolist()
    blob2 = blob2.values.tolist()

    print('blob2: ',blob2, '\n'*3)

    fontTitle = {'family': 'arial',
            'color':  'darkred',
            'weight': 'normal',
            'size': 16,
            }
    fontAxisLabels = {'family': 'helvetica',
            'color':  'darkblue',
            'weight': 'normal',
            'size': 16,
            }

    if len(blob0)>0:
        blob0_acc, blob0_et = map(list, zip(*blob0))
    if len(blob1)>0:
        blob1_acc, blob1_et = map(list, zip(*blob1))
    if len(blob2)>0:
        blob2_acc, blob2_et = map(list, zip(*blob2))

    #fig1=plt.figure(1)

    if len(blob0)>0:
        plt.plot(blob0_et, blob0_acc, "s", color="blue", markersize=10, label = '0')
    if len(blob1)>0:
        plt.plot(blob1_et, blob1_acc, "s", color="red", markersize=10, label = '1')
    if len(blob2)>0:    
        plt.plot(blob2_et, blob2_acc, "s", color="green", markersize=10, label = '2')


    plot1 = fig.add_subplot(111) # adding the subplot 
    plot1.plot(y) # plotting the graph 
    plot1.set_title ("Circle Calibration, Acc vs ET", fontsize=12)
    plot1.set_ylabel("Acc", fontsize=14)
    plot1.set_xlabel("ET", fontsize=14)

    toolbar = NavigationToolbar2Tk(my_canvas, my_frame_1)
    toolbar.update()
    my_canvas.get_tk_widget().pack(side = tkinter.TOP, fill = tkinter.BOTH, expand = 1)

    my_canvas.draw_idle()

    plt.axis([0,250,0,50])
    plt.xlabel('Edge Threshold', fontdict = fontAxisLabels, fontsize = 12)
    plt.ylabel('Accumulator', fontdict = fontAxisLabels, fontsize = 12)
    plt.title('Accum vs Edge threshold', fontname='arial', color=('black'),fontdict = fontTitle,fontsize = 10)
    plt.legend(loc = "upper right")

 

  
def getDataFile():
    file = easygui.fileopenbox(msg=None, title=None, default="/Users/.../Desktop/tk_gui_grid/", filetypes = None, multiple = False)
    print('\n', "This is the selected file:", file, '\n')
    # load data as a pandas dataframe
    df = pd.read_csv(file, sep='\t', lineterminator='\n')
    print(df)
    return df

"""
# this function successfully placed a simple plot onto the canvas when the button was pressed
def plotData():
    fig.clear()
    y = simple_data.some_yVals() #imported from a function in another module

    plot1 = fig.add_subplot(111) # adding the subplot 
    plot1.plot(y) # plotting the graph 
    plot1.set_title ("Circle Calibration, Accumulator vs Edge Threshold", fontsize=12)
    plot1.set_ylabel("Accumulator", fontsize=14)
    plot1.set_xlabel("Edge Threshold", fontsize=14)

    toolbar = NavigationToolbar2Tk(my_canvas, my_frame_1)
    toolbar.update()
    my_canvas.get_tk_widget().pack(side = tkinter.TOP, fill = tkinter.BOTH, expand = 1)

    my_canvas.draw_idle()
"""

def clearPlot():
    fig.clear()
    my_canvas.draw_idle()

my_canvas = FigureCanvasTkAgg(fig, master = my_frame_1) # creating the Tkinter canvas containing the Matplotlib figure  
my_canvas.get_tk_widget().pack() # placing the canvas on the Tkinter window
my_canvas.draw()

#create another frame(my_frame_2) 
my_frame_2 = tk.Frame(tab2, bd=2, relief=tk.GROOVE)
my_frame_2.pack(side=tk.RIGHT)
#---------------------------------------------------------------------
button1 = tk.Button(my_frame_2, text = "Browse \nfiles", command = getDataFile, relief = tk.GROOVE, bg = "red", padx =20,pady =20 )
button1.pack(side="top", fill="x")
button2 = tk.Button(my_frame_2, text = "Plot \nData", command = plotData, relief = tk.GROOVE, bg = "red", padx =20,pady =20 )
button2.pack(side="top", fill="x" )
button3 = tk.Button(my_frame_2, text = "Clear \nPlot", command = clearPlot, relief = tk.GROOVE, bg = "red", padx =20,pady =20 )
button3.pack(side="top", fill="x")
button4 = tk.Button(my_frame_2, text = "Doggy", relief = tk.GROOVE, bg = "red", padx =20,pady =20 )
button4.pack(side="top", fill="x")


root.mainloop()
print('\n'*4)

*** Desired output ***

Any knowledge that helps me understand how to get the plots onto the canvas and to understand the difference between plt.plot and plot1.plot

Upvotes: 0

Views: 231

Answers (1)

tmdavison
tmdavison

Reputation: 69116

You should read about the difference between the matplotlib object-oriented interface and the pyplot interface.

In your case you have mixed the two:

pyplot interface

when you call plt.plot, matplotlib will create an Axes instance if one does not already exist, or if one does, it will plot on the current Axes.

Object-oriented interface:

You create an Axes instance called plot1 using plot1 = plt.subplots(111), and then call the function plot from that instance: plot1.plot(). Anything you call from an Axes instance will be displayed on that Axes.

This issue likely comes because an Axes instance is created from your first plt.plot, but then you call plt.subplots and possibly create a different Axes instance which is used for everything afterwards.

In my opinion, it is usually better to use the object-oriented approach, since that way you always know where things you plot are going to end up. Its almost always a bad idea to mix the two approaches, as things end up getting confused somewhere.

Note that in the documentation and in many examples you will see around the web, the Axes instance is often called ax or ax1, rather than plot1 which you have here. Both will work just fine, but that might help to keep in mind when looking at examples elsewhere.

Its hard to tell exactly, and you don't say quite what your desired outcome is, but I think you probably want something like this. Create the plot1 Axes instance before you plot blob0, blob1 or blob2, then call plot1.plot for those three plotting functions too. That should ensure it all turns up on the same Axes.

plot1 = fig.add_subplot(111) # adding the subplot 

if len(blob0)>0:
    plot1.plot(blob0_et, blob0_acc, "s", color="blue", markersize=10, label = '0')
if len(blob1)>0:
    plot1.plot(blob1_et, blob1_acc, "s", color="red", markersize=10, label = '1')
if len(blob2)>0:    
    plot1.plot(blob2_et, blob2_acc, "s", color="green", markersize=10, label = '2')

plot1.plot(y) # plotting the graph 
plot1.set_title ("Circle Calibration, Acc vs ET", fontsize=12)
plot1.set_ylabel("Acc", fontsize=14)
plot1.set_xlabel("ET", fontsize=14)

Its probably not required, but if you are changing to the object-oriented interface, you might as well go the whole way and change it everywhere. For example, these lines:

plt.axis([0,250,0,50])
plt.xlabel('Edge Threshold', fontdict = fontAxisLabels, fontsize = 12)
plt.ylabel('Accumulator', fontdict = fontAxisLabels, fontsize = 12)
plt.title('Accum vs Edge threshold', fontname='arial', color=('black'),fontdict = fontTitle,fontsize = 10)
plt.legend(loc = "upper right")

should become:

plot1.axis([0,250,0,50])
plot1.set_xlabel('Edge Threshold', fontdict = fontAxisLabels, fontsize = 12)
plot1.set_ylabel('Accumulator', fontdict = fontAxisLabels, fontsize = 12)
plot1.set_title('Accum vs Edge threshold', fontname='arial', color=('black'),fontdict = fontTitle,fontsize = 10)
plot1.legend(loc = "upper right")

Upvotes: 2

Related Questions