ohshitgorillas
ohshitgorillas

Reputation: 171

"check_dpi_scaling" and "click_animation" errors when switching GUIs

I have a program that does mass spec data reduction. The workflow is:

  1. Present the user with a list of sequences, and ask them to select a sequence from the list.
  2. Once a sequence of analyses has been selected, the user is presented with a plot of the raw data to analyze.

For a while, this transition between the sequence selector GUI and the raw data GUI has been generating a cryptic error which looks like:

invalid command name "2073977167560update"
    while executing
"2073977167560update"
    ("after" script)
invalid command name "2073972183304check_dpi_scaling"
    while executing
"2073972183304check_dpi_scaling"
    ("after" script)

I have no idea what this means. Last time I saw it, I was using matplotlib.pyplot instead of matplotlib.figure in conjunction with tkinter. This time, I suspect it has to do with the transition between the GUIs, but I have no idea what I'm doing wrong or what I should be doing differently.

Here's a minimal reproducible example:

from tkinter import ttk
import customtkinter as ctk
import matplotlib.figure as Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg

def create_first_gui():
    window = ctk.CTk()
    window.title('First GUI')

    tree = ttk.Treeview(window)
    tree["columns"]=("one","two")
    tree.column("#0", width=100)
    tree.column("one", width=100)
    tree.column("two", width=100)
    tree.heading("#0",text="Column A")
    tree.heading("one", text="Column B")
    tree.heading("two", text="Column C")

    tree.insert("", 0, text="Line 1", values=("Random 1","Random 2"))

    tree.pack()

    button = ctk.CTkButton(window, text="Proceed to Second GUI", command=window.destroy)
    button.pack()

    window.mainloop()

def create_second_gui():
    window = ctk.CTk()
    window.title('Second GUI')

    fig = Figure.Figure(figsize=(5, 4), dpi=100)
    t = [i/100 for i in range(100)]
    fig.add_subplot(111).plot(t, [i**2 for i in t])

    canvas = FigureCanvasTkAgg(fig, master=window)
    canvas.draw()
    canvas.get_tk_widget().pack(side=ctk.TOP, fill=ctk.BOTH, expand=1)

    window.mainloop()

create_first_gui()
create_second_gui()

The above script also produces a "click_animation" error:

invalid command name "2224692384648update"
    while executing
"2224692384648update"
    ("after" script)
invalid command name "2224697676232check_dpi_scaling"
    while executing
"2224697676232check_dpi_scaling"
    ("after" script)
invalid command name "2224697708616_click_animation"
    while executing
"2224697708616_click_animation"
    ("after" script)

Upvotes: 3

Views: 170

Answers (1)

Cole St John
Cole St John

Reputation: 21

I dont fully understand the mechanism but this seems to be an issue of the default in tkinter (behind customtkinter) to use a "default root", which is based on the first instance of Tk that is invoked in your program (examine the contents of init.py of tkinter library):

_support_default_root = True
_default_root = None

def NoDefaultRoot(): 
    """Inhibit setting of default root window.

    Call this function to inhibit that the first instance of
    Tk is used for windows without an explicit parent window.
    """
    ...

If you destroy the "default root", as you inevitably do with this button:

button = ctk.CTkButton(window, text="Proceed to Second GUI", command=window.destroy)

then different errors are brought up and printed during the next mainloop execution -it is not incredibly clear to understand from the generated error text.

There seems to be different ways to avoid this - here are a few suggestions:

  • use withdraw and quit instead of destroy:
    def exit_gui():
        window.withdraw() # Hide root window
        window.quit() # End the mainloop and stop tcl interpreter commands

    button = ctk.CTkButton(window, text="Proceed to Second GUI", command=exit_gui)
  • Or use CtkFrames for the actual gui definition
import customtkinter as ctk

# Establish base window
root = ctk.CTk()

# Establish 'Gui One'
frame1 = ctk.CTkFrame(root)
ctk.CTkLabel(frame1, text="First GUI").pack(pady=20)


def switch():
    """Button functionality to switch to next gui by forgetting / packing"""
    frame1.pack_forget()  # Hide all frames
    frame2.pack(fill="both", expand=True)  # Show target frame


ctk.CTkButton(frame1, text="Go to Second GUI", command=switch).pack()

# Establishing 'Gui Two'
frame2 = ctk.CTkFrame(root)
ctk.CTkLabel(frame2, text="Second GUI - Long Title ========").pack(pady=20)


def end():
    root.withdraw()
    root.quit()


ctk.CTkButton(frame2, text="End", command=end).pack()

# Setting the 'first gui'
frame1.pack(fill="both", expand=True)


root.mainloop()

Upvotes: 0

Related Questions