0

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.

Windy71
  • 851
  • 1
  • 9
  • 30
  • 1
    I dont see any reason for this not to run, and have you tried running it from the terminal of vscode – Delrius Euphoria Sep 28 '20 at 09:07
  • 1
    If the output does not show any exit stat, like exit with error code 0 or something like that, its still running, otherwise it mustve been closed – Delrius Euphoria Sep 28 '20 at 09:10
  • Hi, it doesn't show anything when I close the gui, so I guess it is still running somewhere? – Windy71 Sep 28 '20 at 09:23
  • if I add the line root.destroy() to the end it fails the same way if I use the second tab (OPW), but will close or destroy the gui if I only use the first tab. Not sure why. – Windy71 Sep 28 '20 at 09:32
  • 1
    Im not sure as to why though, i always use the vscode integrated terminal.and it works fine – Delrius Euphoria Sep 28 '20 at 09:50

1 Answers1

0

I found this How do I close a tkinter window? and a comment from Bryan Oakley which gave me what I needed. For the problem tab (TAB B) I added a destroy button with the command=quit which works fine. Will need to add that to all other tabs too.

# MAKE BUTTON TO DESTROY GUI
button4_opw  = tk.Button(frame2, text = "Destroy", command = quit, relief = tk.GROOVE, padx =20, pady =20 )
button4_opw.pack(side="bottom")
Windy71
  • 851
  • 1
  • 9
  • 30