tkinter code does not proceed after .destroy() (multiple windows)

I am trying to create a simple tkinter application with two separate windows. The first window looks like this and is denoted plot_window in the code. It lets users select which columns should be plotted from dropdown menus. A category column is also used for visualising the labels in the plot.

enter image description here

However, when I run the code underneath and click either the ‘SAVE PLOT’ button or the ‘CANCEL’ button at the bottom one of these methods is triggered:

def close_plot_window():     self.plot_window.destroy() # This is reached  def set_save_plot_bool():     print('Destroy')  # This is reached     self.save_plot_bool = True     self.plot_window.destroy() 

and the second window save_window should show up, but the code does not proceed.

The strange thing is that if I comment out this snippet for the third and final drop-down menu the code works fine

# IF I COMMENT OUT THIS WHOLE IF STATEMENT, THE CODE RUNS  if len(data.columns) > 2:  # There exist a third columns as well -> include drop-down for category selection      # ******** Drop-down 3: Category selection ********     category_column = string_columns[0] if (len(string_columns) > 0) else numeric_columns[2]     dropdown_choice_category.set(     category_column)  # Set the default option in the dropdown with the first column     l3 = Label(self.plot_window, text="Select category column:")     l3.grid(row=2, column=0, sticky='e')     dropdown_menu_category = OptionMenu(self.plot_window, dropdown_choice_category, *choices)     dropdown_menu_category.config(width=16)     dropdown_menu_category.grid(row=2, column=1, sticky='w')     chosen_columns = {'x_col': x_values_column,                               'y_col': y_values_column,                               'category_col': category_column} 

The whole code:

import matplotlib matplotlib.use('TkAgg') from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg from matplotlib.figure import Figure import matplotlib.pyplot as plt  import numpy as np from tkinter import * from tkinter import filedialog  import pandas as pd import seaborn as sns  from pandas.api.types import is_numeric_dtype  def center_tk_window(window, height, width):     # Helper method for centering windows      screen_width = window.winfo_screenwidth()     screen_height = window.winfo_screenheight()     x_coordinate = int((screen_width / 2) - (width / 2))     y_coordinate = int((screen_height / 2) - (height / 2))     window.geometry("{}x{}+{}+{}".format(width, height, x_coordinate, y_coordinate))   def plot_scatter(data, chosen_columns, ax=None, initial_box=None):     plot_type = "scatter"     if plot_type == "scatter":         fig = Figure(figsize=(7, 6))         if ax is None:             # Create a new subplot             ax = fig.add_subplot(111)          # Selected x-coordinates         print('chosen_columns', chosen_columns)         x_data = data[chosen_columns['x_col']]          # Selected y-coordinates         y_data = data[chosen_columns['y_col']]          filled_markers = ('o', 'v', '^', '<', '>', '8', 's', 'p', '*', 'h', 'H', 'D', 'd', 'P', 'X')          # Category column         if 'category_col' in chosen_columns:             category_data = data[chosen_columns['category_col']]             print(np.unique(np.array(category_data)))              # Plotting it all             sns.scatterplot(ax=ax, x=x_data, y=y_data, hue=category_data, style=category_data,                             markers=filled_markers                             )             # Shrink current axis's height by 20% on the bottom             if initial_box is None:                 initial_box = ax.get_position()              ax.set_position([initial_box.x0, initial_box.y0 + initial_box.height * 0.2,                              initial_box.width, initial_box.height * 0.80])              # Put a legend below current axis             ax.legend(loc='upper center', bbox_to_anchor=(0.5, -0.15),                       fancybox=False, shadow=False, ncol=6)             plt.tight_layout()          else:  # Normal scatterplot without any categorical values             sns.scatterplot(ax=ax, x=x_data, y=y_data)      ax.set_title("User input plot name", fontsize=16)     ax.set_ylabel("User input y label", fontsize=14)     ax.set_xlabel("User input x label", fontsize=14)     return fig, ax, initial_box   class GenericPlot:     def __init__(self, data):         # Plot window         self.save_plot_bool = False         self.plot_window = Tk()         self.dynamic_plots(data)          self.plot_window.mainloop()         print("After first mainlooop")  # This line is never reached          print('save_plot_bool', self.save_plot_bool)          # Save window         self.save_plot_dir = ''         self.save_window = Tk()         self.save_plot()         self.save_window.mainloop()      def dynamic_plots(self, data):         """         Input :             window : tkinter window             data   : DataFrame object         """         def close_plot_window():             self.plot_window.destroy()          def set_save_plot_bool():             print('Destroy')  # This is reached             self.save_plot_bool = True             self.plot_window.destroy()          center_tk_window(self.plot_window, 720, 600)          # Drop-down variables (3 drop-downs)         dropdown_choice_x = StringVar(self.plot_window)  # Variable holding the dropdown selection for the x column         dropdown_choice_y = StringVar(self.plot_window)  # Variable holding the dropdown selection for the y column         dropdown_choice_category = StringVar(             self.plot_window)  # Variable holding the dropdown selection for the category column          # Create set of column names in the dataset         choices = set(data.columns.values)          # Find numeric and string columns         string_columns = []         numeric_columns = []         [numeric_columns.append(col) if is_numeric_dtype(data[col]) else string_columns.append(col) for col in          data.columns]          if len(numeric_columns) < 2:             raise Exception(                 "Unable to create scatter plot- need more than two numerical columns in the imported dataset.")          # GUI setup         self.plot_window.columnconfigure(0, weight=1)         self.plot_window.columnconfigure(1, weight=1)          self.plot_window.rowconfigure(0, weight=1)         self.plot_window.rowconfigure(1, weight=1)         self.plot_window.rowconfigure(2, weight=1)         self.plot_window.rowconfigure(3, weight=1)         self.plot_window.rowconfigure(4, weight=1)          # ******** Drop-down 1: x-value selection ********         x_values_column = numeric_columns[0]  # Select the first numeric column as the default x values to plot         dropdown_choice_x.set(x_values_column)  # Set the default option in the dropdown with the first column         Label(self.plot_window, text="Select x column:").grid(row=0, column=0, sticky="e")          choices_numeric = set(numeric_columns)  # Only show numeric columns in the drop-down for x and y          dropdown_menu_x = OptionMenu(self.plot_window, dropdown_choice_x, *choices_numeric)         dropdown_menu_x.grid(row=0, column=1, sticky="w")         dropdown_menu_x.config(width=16)          # ******** Drop-down 2: y-value selection ********         y_values_column = numeric_columns[1]  # Select the second alternative in the dropdown list for the y values         dropdown_choice_y.set(y_values_column)  # Set the default option in the dropdown with the first column         l2 = Label(self.plot_window, text="Select y column:")         l2.grid(row=1, column=0, sticky='e')         dropdown_menu_y = OptionMenu(self.plot_window, dropdown_choice_y, *choices_numeric)         dropdown_menu_y.config(width=16)         dropdown_menu_y.grid(row=1, column=1, sticky='w')          chosen_columns = {'x_col': x_values_column,                           'y_col': y_values_column}          #********* IF I COMMENT OUT THIS WHOLE IF STATEMENT, THE CODE RUNS***********         if len(data.columns) > 2:  # There exist a third columns as well -> include drop-down for category selection             # ******** Drop-down 3: Category selection ********             category_column = string_columns[0] if (len(string_columns) > 0) else numeric_columns[2]             dropdown_choice_category.set(                 category_column)  # Set the default option in the dropdown with the first column             l3=Label(self.plot_window, text="Select category column:")             l3.grid(row=2, column=0, sticky='e')             dropdown_menu_category = OptionMenu(self.plot_window, dropdown_choice_category, *choices)             dropdown_menu_category.config(width=16)             dropdown_menu_category.grid(row=2, column=1, sticky='w')              chosen_columns = {'x_col': x_values_column,                               'y_col': y_values_column,                               'category_col': category_column}          # Plot the initially selected columns          fig_initial, ax, initial_box = plot_scatter(data, chosen_columns)         canvas = FigureCanvasTkAgg(fig_initial, master=self.plot_window)         canvas.get_tk_widget().grid(row=3, columnspan=2, rowspan=True)         canvas.draw()          def change_dropdown_x(canvas, chosen_columns, ax, *args):             # This function is triggered once a dropdown selection is made             selected_x_col = dropdown_choice_x.get()             chosen_columns['x_col'] = selected_x_col             # Create a new plot now             ax.clear()  # Clearing the previous plot             _, ax, _ = plot_scatter(data, chosen_columns, ax, initial_box)             canvas.draw()          # chosen columns might not be updated...         def change_dropdown_y(canvas, chosen_columns, ax, *args):             # This function is triggered once a dropdown selection is made             selected_y_col = dropdown_choice_y.get()             chosen_columns['y_col'] = selected_y_col             # Create a new plot now             ax.clear()  # Clearing the previous plot             _, ax, _ = plot_scatter(data, chosen_columns, ax, initial_box)             canvas.draw()          def change_dropdown_category(canvas, chosen_columns, ax, *args):             # This function is triggered once a dropdown selection is made             selected_category = dropdown_choice_category.get()             chosen_columns['category_col'] = selected_category             # Create a new plot now             ax.clear()  # Clearing the previous plot             _, ax, _ = plot_scatter(data, chosen_columns, ax, initial_box)             canvas.draw()          # Link functions to change dropdown         dropdown_choice_x.trace('w',                                 lambda *args, canvas=canvas, chosen_columns=chosen_columns, ax=ax,                                        initial_box=initial_box: change_dropdown_x(canvas, chosen_columns, ax,                                                                                   initial_box,                                                                                   *args))         dropdown_choice_y.trace('w',                                 lambda *args, canvas=canvas, chosen_columns=chosen_columns, ax=ax,                                        initial_box=initial_box: change_dropdown_y(canvas, chosen_columns, ax,                                                                                   initial_box,                                                                                   *args))         dropdown_choice_category.trace('w', lambda *args, canvas=canvas, chosen_columns=chosen_columns, ax=ax,                                                    initial_box=initial_box: change_dropdown_category(canvas,                                                                                                      chosen_columns,                                                                                                      ax,                                                                                                      initial_box,                                                                                                      *args))          # Save and close buttons         Button(self.plot_window, text="CLOSE", command=close_plot_window).grid(row=4, column=0)            Button(self.plot_window, text="SAVE PLOT", command=set_save_plot_bool).grid(row=4, column=1)      def save_plot(self):         if self.save_plot_bool:             print(self.save_plot_bool)             self.save_window.columnconfigure(0, weight=1)             self.save_window.columnconfigure(1, weight=1)             self.save_window.rowconfigure(0, weight=1)             self.save_window.rowconfigure(1, weight=1)              self.save_window.title('Save plot')              # Get saving path             print("Please select a directory for saving the model...", flush=True)              l1 = Label(self.save_window, text=self.save_plot_dir)             l1.grid(row=0, columnspan=2)              def get_save_dir():                 self.save_plot_dir = filedialog.askdirectory()               def save_to_file_btn():                 print(self.save_plot_dir)                 plt.save(self.save_plot_dir + '.tif')              Button(self.save_window, text="Choose save directory...", command=get_save_dir).grid(row=0, column=1)             Button(self.save_window, text="SAVE PLOT TO FILE", command=save_to_file_btn).grid(row=1, columnspan=2)             center_tk_window(self.save_window, 300, 400)  # window, height, width  # Create matrix for testing df = pd.DataFrame(np.random.randint(0,100,size=(150, 4)), columns=list('ABCD'))  # Hard code a category column of string with length 150. int_labels = [5, 1, 1, 4, 5, 1, 2, 0, 0, 2, 1, 2, 5, 3, 1, 4, 1, 5, 1, 4, 5, 4, 5, 3, 2, 2, 3, 4, 4, 3, 1, 3,                    3, 2, 5, 1, 1, 5, 3, 3, 1, 2, 0, 1, 2, 0, 5, 3, 5, 1, 3, 5, 3, 5, 4, 2, 3, 3, 4, 1, 3, 3, 3, 4,                    2, 2, 5, 2, 0, 2, 0, 5, 5, 4, 2, 0, 2, 3, 1, 5, 2, 1, 5, 3, 1, 3, 4, 4, 1, 3, 5, 1, 2, 2, 4, 0,                    5, 0, 2, 2, 0, 4, 5, 2, 0, 2, 2, 3, 2, 0, 0, 5, 1, 0, 3, 1, 2, 2, 4, 0, 2, 2, 1, 1, 1, 1, 0, 2,                    1, 1, 3, 2, 4, 4, 0, 2, 0, 5, 4, 4, 3, 0, 1, 0, 2, 5, 3, 0, 3, 5] string_labels = [str(i) for i in int_labels] df['category2'] = string_labels  GenericPlot(df) 

If anyone has an idea as to why the code does not proceed if I include the category dropdown-menu, please let me know.

Add Comment
1 Answer(s)

I found the solution. It was the plt.tight_layout() causing the problems…

Add Comment

Your Answer

By posting your answer, you agree to the privacy policy and terms of service.