I have a Tkinter gui that works when I run it from vs code. When I then close it and press 'run' again on vs code I only see the filename appear in the vs code terminal. At the moment I have to close vs code and re-open it to run the gui. After some hints from Cool Cloud it showed that when I ran the gui from the terminal it was not closing when I clicked to close the gui,if I add root.destrpy() to the end of the script it will correctly destroy the gui if I have only worked on the TAB A of the gui but will not successfully close the gui if I have used the TAB B of the gui.
Code
print('\n'*2)
import tkinter.filedialog
import os
import re
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
from scipy.stats import linregress
pd.set_option("display.max_rows", None, "display.max_columns", None)
#=====================================================================
# ROOT FIGURE FOR GUI
#=====================================================================
root = tk.Tk()
root.title("Tab Widget")
root.geometry("600x450")
tabControl = ttk.Notebook(root)
tab1 = ttk.Frame(tabControl)
tab2 = ttk.Frame(tabControl)
tab3 = ttk.Frame(tabControl)
tab4 = ttk.Frame(tabControl)
tabControl.add(tab1, text ='Circle Cal')
tabControl.add(tab2, text ='OPW')
tabControl.add(tab3, text ='C')
tabControl.add(tab4, text ='D')
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 A
#=====================================================================
#MAKE A FIGURE OBJECT
my_figure1 = Figure(figsize = (4, 4), dpi = 100)
#MAKE A FRAME WIDGET
frame1 = tk.Frame(tab1, bd=2, relief=tk.GROOVE)
frame1.pack(side=tk.LEFT, anchor=tk.N, fill=tk.BOTH, expand=True)
#create another frame(frame2)
frame2 = tk.Frame(tab1, bd=2, relief=tk.GROOVE)
frame2.pack(side=tk.RIGHT)
#MAKE A CANVAS OBJECT
my_canvas1 = FigureCanvasTkAgg(my_figure1, master = frame1) # creating the Tkinter canvas containing the Matplotlib figure
# TURN THE CANVAS OBJECT INTO A CANVAS WIDGET
my_canvas1.get_tk_widget().pack(side = tkinter.TOP, fill = tkinter.BOTH, expand = 1) # placing the canvas on the Tkinter window
my_canvas1.draw()
def plotData():
#my_figure1.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))
# MAKE INSTANCE OF FIGURE OBJECT AND RETURN IT WITH SUBPLOT ADDED
plot1 = my_figure1.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')
# plotting the graph
plot1.set_title ("Circle Calibration,Number of drops\n Accumulator vs Edge Threshold", fontname='arial', color=('black'),fontdict = fontTitle,fontsize = 10)
plot1.set_ylabel("Accumulator", fontdict = fontAxisLabels, fontsize = 12)
plot1.set_xlabel("Edge Threshold", fontdict = fontAxisLabels, fontsize = 12)
plot1.axis([0,250,0,50])
plot1.legend(loc = "upper right")
my_canvas1.draw()
def clearPlot():
my_figure1.clear()
my_canvas1.draw_idle()
# MAKE BUTTON TO PLOT GRAPH
button1 = tk.Button(frame2, text = "Plot", command = plotData, relief = tk.GROOVE, padx =20, pady =20 )
button1.pack(side="right")
# MAKE BUTTON TO CLEAR PLOT
button2 = tk.Button(frame2, text = "Clear", command = clearPlot, relief = tk.GROOVE, padx =20, pady =20 )
button2.pack(side="right")
#=====================================================================
# TAB B
#=====================================================================
#MAKE A FIGURE OBJECT
my_figure2 = Figure(figsize = (6, 6), dpi = 100)
#MAKE A FRAME WIDGET
frame1 = tk.Frame(tab2, bd=2, relief=tk.GROOVE)
frame1.pack(side=tk.LEFT, anchor=tk.N, fill=tk.BOTH, expand=True)
frame2 = tk.Frame(tab2, bd=2, relief=tk.GROOVE)
frame2.pack(side=tk.RIGHT)
#MAKE A CANVAS OBJECT
my_canvas2 = FigureCanvasTkAgg(my_figure2, master = frame1) # creating the Tkinter canvas containing the Matplotlib figure
# TURN THE CANVAS OBJECT INTO A CANVAS WIDGET
my_canvas2.get_tk_widget().pack() # placing the canvas on the Tkinter window
my_canvas2.draw()
def rsquared(x, y):
""" Return R^2 where x and y are array-like."""
slope, intercept, r_value, p_value, std_err = linregress(x, y)
return r_value**2
def plotOPWData():
fileOPW = easygui.fileopenbox(msg=None, title=None, default="/Users/.../Desktop/tk_gui_grid/", filetypes = None, multiple = False)
print('\n', "This is the selected file:", fileOPW, '\n')
# load data as a pandas dataframe
df = pd.read_csv(fileOPW, sep='\t', lineterminator='\r')
print('size of orig df:\n', df.shape)
#=====================================================================
# EXTRACT DATA FROM THE COMMENTS TAB
#=====================================================================
cols = ['rownumber', 'volts', 'wfm', 'sclk', 'image', 'segment']
exp = re.compile(r'Row\s'
r'(?P<rownumber>\d+).*\s' #(?P<name>...) stores the following part (\d+) under the name
# rownumber in the match object which we're going to use later on
r'(?P<volts>\d+\.\d+)V\s'
r'(?P<wfm>\w+)\sSclk\s'
r'(?P<sclk>\d+)ns\s'
r'(?P<image>\w+)\s'
r'(?P<segment>\d+)segs.*$')
df[cols] = df['Comments'].str.extract(exp, expand=True)
#create empty column for Pulse width data
dfLen = len(df)
pulse = [0]*dfLen
df['Pulse'] = pulse
#=====================================================================
# FILTERS
#=====================================================================
pw_filter_voltage = 17.0
filter_segment = 24
df[["Velocity_ms", "Volume_pl", "Trajectory_deg"]] = df[["Velocity_ms", "Volume_pl", "Trajectory_deg"]].apply(pd.to_numeric, errors = 'coerce')
df = df.dropna(subset=["Velocity_ms"])
df = df.reset_index(drop=True)
print('size of df:\n', df.shape)
#convert column data to numeric
df[[ "segment", "sclk", "rownumber", "volts"]] = df[["segment", "sclk", "rownumber", "volts"]].apply(pd.to_numeric)
#print('DEBUG --- Types of data: ', df.dtypes)
# Calculate the Pulse Width
df['Pulse'] = (df['segment'] * df['sclk']) / 1000
# selecting rows based on voltage condition
FIRST_filtered_df = df[df['volts'] == pw_filter_voltage]
#=====================================================================
# THIS WILL NEED TO BE LINKED TO BUTTONS IN THE GUI - WILL ALSO NEED TO HAVE DEFAULTS
pw_low_range = 1.4
pw_high_range = 3.0
range_filtered_df = FIRST_filtered_df [FIRST_filtered_df ['Pulse'] >= pw_low_range]
#=====================================================================
#Convert datarame columns to lists
PulseList = range_filtered_df['Pulse'].to_list()
VelList = range_filtered_df['Velocity_ms'].to_list()
VolList = range_filtered_df['Volume_pl'].to_list()
print('shape of range_filtered_df:\n', range_filtered_df.shape)
#=====================================================================
# Polynomial
#=====================================================================
p2 = np.polyfit(PulseList ,VelList ,2) # these are the x coefficients for the equation of the line
print('Coefficients for the polynomial p2:', p2,'\n')
# rounding down the polynomial values
np_array = np.array(p2)
np_round_to_tenths = np.around(np_array, 4)
P2round = list(np_round_to_tenths)
print('round_to_tenths:', P2round)
print(p2)
eq = ['x**2','x']
equation = ('y = ' + str(P2round[0])+eq[0] + ' + ' + str(P2round[1])+eq[1] + ' + ' +str(P2round[2]))
# ============================================================
# START PLOTTING
# ============================================================
plot2 = my_figure2.add_subplot(111) # adding the subplot
plot2.set_xlabel('Pulse Width $uS$')
plot2.set_ylabel('Velocity $(m/s)$ and Drop Volume $(pL)$')
#fig.clear()
image_used = range_filtered_df['image'][0]
print(' contents of df image:\n', image_used,'\n')
plot2.scatter(x=PulseList, y=VelList, marker='.', c='none', edgecolor='b', label = 'Velocity m/s')
plot2.scatter(x=PulseList, y=VolList, marker='.', c='none', edgecolor='r', label = 'Volume pL')
xp = np.linspace(pw_low_range, pw_high_range ,100) # this creates a linear vector, 100 values between -2 and 6
plot2.plot(xp,np.polyval(p2,xp),'m:', label = equation)
plot2.legend(loc='upper left')
plot2.set_xticks(np.arange(0, 3.2, 0.2))
plot2.set_yticks(np.arange(0, 8, 1))
plot2.set_facecolor('xkcd:light gray')
plot2.set_title('Drop Velocity and Volume vs Pulse Width ' +' Image:' + image_used, fontsize=10, fontweight='bold')
plot2.set_xlim([1, 3.2])
plot2.set_ylim([0, 8])
plot2.grid(True, linestyle='--')
plot2.legend(loc = "upper right")
print('func r-squared: ', rsquared(PulseList, VelList))
r2val = rsquared(PulseList, VelList)
r2val = round(r2val,4)
# place a text box in upper left in axes coords
plt.annotate('R^2: '+ str(r2val), xy=(0.05, 0.95), xycoords='axes fraction')
#Optimal sample clock
optimalSampleClock = (((-0.5)* P2round[1])/(P2round[0]))*100
optimalSampleClock = round(optimalSampleClock ,1)
print('optimal sample clock: ', optimalSampleClock)
plt.annotate('OPTsclk: '+ str(optimalSampleClock), xy=(0.05, 0.85), xycoords='axes fraction')
my_canvas2.draw()
# Add the toolbar
toolbar = NavigationToolbar2Tk(my_canvas2, frame1)
toolbar.update()
def clearPlotOPW():
my_figure2.clear()
my_canvas2.draw_idle()
# MAKE BUTTON TO PLOT GRAPH
button1_opw = tk.Button(frame2, text = "Plot", command = plotOPWData, relief = tk.GROOVE, padx =20, pady =20 )
button1_opw.pack(side="right")
# MAKE BUTTON TO CLEAR PLOT
button2_opw = tk.Button(frame2, text = "Clear", command = clearPlotOPW, relief = tk.GROOVE, padx =20, pady =20 )
button2_opw.pack(side="right")
#=====================================================================
# TAB C
#=====================================================================
#MAKE A FIGURE OBJECT
my_figure3 = Figure(figsize = (4, 4), dpi = 100)
#MAKE A FRAME WIDGET
frame1 = tk.Frame(tab3, bd=2, relief=tk.GROOVE)
frame1.pack(side=tk.LEFT, anchor=tk.N, fill=tk.BOTH, expand=True)
frame2 = tk.Frame(tab3, bd=2, relief=tk.GROOVE)
frame2.pack(side=tk.RIGHT)
#MAKE A CANVAS OBJECT
my_canvas3 = FigureCanvasTkAgg(my_figure3, master = frame1) # creating the Tkinter canvas containing the Matplotlib figure
# TURN THE CANVAS OBJECT INTO A CANVAS WIDGET
my_canvas3.get_tk_widget().pack() # placing the canvas on the Tkinter window
my_canvas3.draw()
def plotData():
plot1 = my_figure3.add_subplot(111) # adding the subplot
x = [1,2,3,4,5]
y = [1, 2, 4, 8,16]
plot1.plot(x, y, marker='o', c='r')
my_canvas3.draw()
def clearPlot():
my_figure3.clear()
my_canvas3.draw_idle()
# MAKE BUTTON TO PLOT GRAPH
button1 = tk.Button(frame2, text = "Plot", command = plotData, relief = tk.GROOVE, padx =20, pady =20 )
button1.pack(side="right")
# MAKE BUTTON TO CLEAR PLOT
button2 = tk.Button(frame2, text = "Clear", command = clearPlot, relief = tk.GROOVE, padx =20, pady =20 )
button2.pack(side="right")
#=====================================================================
# TAB D
#=====================================================================
#MAKE A FIGURE OBJECT
my_figure4 = Figure(figsize = (4, 4), dpi = 100)
#MAKE A FRAME WIDGET
frame1 = tk.Frame(tab4, bd=2, relief=tk.GROOVE)
frame1.pack(side=tk.LEFT, anchor=tk.N, fill=tk.BOTH, expand=True)
frame2 = tk.Frame(tab4, bd=2, relief=tk.GROOVE)
frame2.pack(side=tk.RIGHT)
#MAKE A CANVAS OBJECT
my_canvas4 = FigureCanvasTkAgg(my_figure4, master = frame1) # creating the Tkinter canvas containing the Matplotlib figure
# TURN THE CANVAS OBJECT INTO A CANVAS WIDGET
my_canvas4.get_tk_widget().pack() # placing the canvas on the Tkinter window
my_canvas4.draw()
def plotData():
plot1 = my_figure4.add_subplot(111) # adding the subplot
x = [1,2,3,4,5]
y = [1, 2, 3, 4, 5]
plot1.plot(x, y, marker='o', c='y')
my_canvas4.draw()
def clearPlot():
my_figure4.clear()
my_canvas4.draw_idle()
# MAKE BUTTON TO PLOT GRAPH
button1 = tk.Button(frame2, text = "Plot", command = plotData, relief = tk.GROOVE, padx =20, pady =20 )
button1.pack(side="right")
# MAKE BUTTON TO CLEAR PLOT
button2 = tk.Button(frame2, text = "Clear", command = clearPlot, relief = tk.GROOVE, padx =20, pady =20 )
button2.pack(side="right")
root.mainloop()
Terminal output from VS Code
/Users/me/opt/anaconda3/bin/python /Users/me/Desktop/tk_gui_grid/c_06.py /Users/me/opt/anaconda3/bin/python /Users/me/Desktop/tk_gui_grid/c_06.py
Desired output
A gui I can see running each time I press run on VS code editor.