TomSchimansky / CustomTkinter

A modern and customizable python UI-library based on Tkinter
MIT License
10.77k stars 1.03k forks source link

invalid command name "<numbers>update/check_dpi_scaling" #2460

Open ohshitgorillas opened 3 weeks ago

ohshitgorillas commented 3 weeks ago

I am creating data reduction software and the general workflow is to view a list of analysis sequences, select one, and then go to view a plot of raw data. I'm getting this cryptic error when I switch from one GUI to the next:

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

Here is a 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=("Stuff 1","Stuff 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))
    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()

Am I doing something wrong? If so, what?

dipeshSam commented 3 weeks ago

Why are you creating Two instances of CTk? Use CTkToplevel if you want multiple windows.

By the way, it's a bug needs to be fixed causes looping contradictions in customtkinter. Whenever @TomSchimansky will come, PRs may be accepted.

Regards.

ohshitgorillas commented 3 weeks ago

To be clear, I don't want multiple windows at the same time. I am trying to create one GUI, utilize it, destroy it, then create a second GUI. Perhaps I should be passing the window as an argument to the second GUI instead, as:

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=lambda: create_second_gui(window))
    button.pack()

    window.mainloop()

def create_second_gui(window):
    for widget in window.winfo_children():
        widget.destroy()

    window.title('Second GUI')

    fig = Figure.Figure(figsize=(5, 4))
    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)

create_first_gui()

??

This is easy in my example, however, it's not such a straightforward change within my real code. If there's a way to keep the behavior of destroying the first window and then creating a second without generating this error and having to pass the window through as an argument, that would be ideal.

dipeshSam commented 3 weeks ago

The case you just described. You should still use CTkToplevel, all the functionalities will be same and you can pass your window object within it.

In your very “first example:

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

    fig = Figure.Figure(figsize=(5, 4))
    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)

No need to use mainloop within this window. But if main window you want to destroy, only then you should call mainloop method of this Toplevel and this window will be alive regardless of main window.

Although, the exact behaviour you want, need to be fixed when owner of this repo accept some PRs regarding this issue.

Regards.