-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
filedialog/messagebox and CTkToplevel #2650
Comments
@Geo9999 Could you please have Sample Reproducible Code along with its visual output? Regards. |
I've written two similar mock versions of the code, no1 works but does not try to pass the secondary_window as a parent, hence the load/plots appear over the root and behind the toplevel, and no2 which produces the same error. |
@Geo9999 The bug lies in your code misspelling. It is recommended that you should use proper naming conventions. Also, instead of passing object reference as Here is your corrected code: import tkinter
from tkinter.messagebox import showerror, showinfo
from tkinter import filedialog as fd
import customtkinter
from customtkinter import CTkButton, CTkToplevel, CTkFrame
from CTkMessagebox import CTkMessagebox
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import (FigureCanvasTkAgg, NavigationToolbar2Tk)
class secondary_window(customtkinter.CTkToplevel):
def __init__(self, parent, **kwargs):
super().__init__(parent, **kwargs)
self.title("Toplevel Window") #### Should open over the main
self.geometry("300x300")
self.data = [[1,2,3,4], [5,6,7,8]]
self.button=CTkButton(self, text="load", command=lambda: self.load_cif(self))
self.button.grid(row=0, column=0)
self.button=CTkButton(self, text="plot", command=lambda: self.toplevel_plots(self.data))
self.button.grid(row=1, column=0)
self.after_idle(self.lift)
def load_cif(self, parent):
filetypes = (
('cif␣files', '*.cif'),
('All␣files', '*.*')
)
filename = fd.askopenfilename(
title='Open a file',
initialdir='/',
filetypes=filetypes, parent=parent)
if filename:
CTkMessagebox(self, title='Selected File', message=filename)
else:
showinfo(title='Message',
message="No file was selected!",
parent = parent)
def toplevel_plots(self, data):
window=CTkToplevel(self)
window.title("Plots in separate window")
window.after(200, window.lift)
frame = CTkFrame(window)
frame.grid(row=0, column=0, sticky="nsew")
canvas = self.create_canvas(frame, data)
canvas.get_tk_widget().grid(row=0, column=0, sticky="nsew")
print(data)
def create_canvas(self, master, data):
x, y = data
fig = plt.figure()
ax = fig.add_axes([0.05,0.05,0.9,0.9])
canvas = FigureCanvasTkAgg(fig, master=master)
ax.scatter(x, y)
canvas.draw()
return canvas
def secondary():
window = secondary_window(parent=root)
root = customtkinter.CTk() #### Should always stay open at the back, everything else opens from in
root.title("Main Window")
root.geometry("300x300")
button=CTkButton(root, command=secondary)
button.grid(row=0, column=0)
root.mainloop() You could read some standard naming convention over here. I also created a refined version of this code: from customtkinter import (
CTk,
CTkButton,
CTkFrame,
CTkToplevel
)
import matplotlib.pyplot as plt
from tkinter import messagebox, filedialog
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
class PlottingWindow(CTkToplevel):
def __init__(self, data: list, **kwargs):
self._data = data
super().__init__(**kwargs)
self.wm_title("Plotting Window")
# Setting it over the option window
self.after(200, self.lift)
self._frame = CTkFrame(self)
self._frame.grid(row=0, column=0, sticky="nsew")
self._create_canvas()
def _create_canvas(self):
x, y = self._data
fig = plt.figure()
ax = fig.add_axes([0.05, 0.05, 0.9, 0.9])
canvas = FigureCanvasTkAgg(fig, master=self._frame)
ax.scatter(x, y)
canvas.draw()
canvas.get_tk_widget().grid(row=0, column=0, sticky="nsew")
class OptionWindow(CTkToplevel):
def __init__(self, master: CTk = None, **kwargs):
super().__init__(master, **kwargs)
self.wm_title("Option Window")
self.geometry("400x250")
self._plotting_loaded: bool = False
self._default_data: list[list[int]] = [[1,2,3,4], [5,6,7,8]]
# Setting it over the main window
self.after(200, self.lift)
# Adding options
self._load_button = CTkButton(self, text="Load", command=self._on_load)
self._load_button.place(relx=0.5, anchor="center", rely=0.4)
self._plot_button = CTkButton(self, text="Plot", command=self._on_plot)
self._plot_button.place(relx=0.5, anchor="center", rely=0.6)
def _on_load(self):
filetypes = (('cif␣files', '*.cif'), ('All␣files', '*.*'))
filename = filedialog.askopenfilename(title="Open a file",
initialdir='/',
filetypes=filetypes,
parent=self)
if filename:
messagebox.showinfo("Message", f"Selected file is:\n\"{filename}\"", parent = self)
else:
messagebox.showinfo("Message", f"No file was selected!", parent = self)
def _on_plot(self):
if not self._plotting_loaded:
self._plotting_loaded = True
plotting_window = PlottingWindow(self._default_data)
plotting_window.bind("<Destroy>", self._restore, True)
def _restore(self, e=None):
self._plotting_loaded = False
class MainWindow(CTk):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.wm_title("Main Window")
self.geometry("400x250")
self._options_loaded: bool = False
self._option_button = CTkButton(self, text="Options", command=self._on_option)
self._option_button.place(relx=0.5, anchor="center", rely=0.4)
self._exit_button = CTkButton(self, text="Quit", command=self.quit) # If it is not used, internal bug error may occur after using plot.
self._exit_button.place(relx=0.5, anchor="center", rely=0.6)
def _on_option(self):
if not self._options_loaded:
self._options_loaded = True
option_window = OptionWindow(self)
option_window.bind("<Destroy>", self._restore, True)
def _restore(self, e=None):
self._options_loaded = False
if __name__ == "__main__":
app = MainWindow()
app.mainloop() Hope, this information is helpful to you. |
Thank you so much for the help, I really appreciate how fast and detailed you were. I will implement the changes tomorrow and check again. |
Hello, I'm new to programming and github so excuse any omission or issue with this Issue. Let me know if uploading part of the code would help, I wasn't sure whether I should.
I'm working on an app with a main window and one or more CTkToplevel windows that should be able to load files and make plots on separate windows (or on the main one). The Toplevel is it's own class called hkl_input_window and will be called from the main window.
Problem is the tkinter messagebox, the filedialog windows and the toplevels containing the plots open over the root window and not the toplevel. When I tried to add my Toplevel window (called hkl_input_window) as a parent/master to them, I got the following error: " type object 'hkl_input_window' has no attribute 'tk' ". I added the full error here. Similar errors happened with the messagebox.showerror etc.
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Users\GeorgicsB\AppData\Local\Programs\Python\Python312\Lib\tkinter_init_.py", line 1968, in call
return self.func(*args)
^^^^^^^^^^^^^^^^
File "C:\Users\GeorgicsB\AppData\Local\Programs\Python\Python312\Lib\site-packages\customtkinter\windows\widgets\ctk_button.py", line 554, in _clicked
self.command()
File "c:\Users\GeorgicsB\Desktop\New_folder\hkl_input_window.py", line 345, in
self.button=CTkButton(self.frame2, text="Plot poles in a separate window", height=30, command=lambda: self.calculate_poles(plotting="separate")) #### Plot Pole Figures in a separate window ####
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "c:\Users\GeorgicsB\Desktop\New_folder\hkl_input_window.py", line 420, in calculate_poles
self.separate_plot_window(data)
File "c:\Users\GeorgicsB\Desktop\New_folder\hkl_input_window.py", line 427, in separate_plot_window
plot_window = customtkinter.CTkToplevel(hkl_input_window)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\GeorgicsB\AppData\Local\Programs\Python\Python312\Lib\site-packages\customtkinter\windows\ctk_toplevel.py", line 36, in init
super().init(*args, **pop_from_dict_by_set(kwargs, self.valid_tk_toplevel_arguments))
File "C:\Users\GeorgicsB\AppData\Local\Programs\Python\Python312\Lib\tkinter_init.py", line 2708, in init
BaseWidget.init(self, master, 'toplevel', cnf, {}, extra)
File "C:\Users\GeorgicsB\AppData\Local\Programs\Python\Python312\Lib\tkinter_init.py", line 2653, in init
self.setup(master, cnf)
File "C:\Users\GeorgicsB\AppData\Local\Programs\Python\Python312\Lib\tkinter_init.py", line 2622, in _setup
self.tk = master.tk
^^^^^^^^^
AttributeError: type object 'hkl_input_window' has no attribute 'tk'
I tried the following:
Any time I tried to enter my Toplevel as a parent/master in the filedialog.askopenfilename or plotting windows I got the " type object 'hkl_input_window' has no attribute 'tk' "
Is it possible that the CTkToplevel widget isn't able to be used like this? What possibilities exist to combat this?
Sorry for the long message and thank you in advance for any help!!
The text was updated successfully, but these errors were encountered: