I have a ctk application with many widgets spread across multiple tabs and frames. The inputs are so extensive I would ultimately like the user to be able to save their entries to a named file and retrieve them later. At the moment I am struggling to get all the widget values which are spread across all tabs when the Save button is clicked. The save button is on one frame but my coded functions within the various classes are not seeing each other.
A minimal example that covers the complexities is provided below:
import customtkinter as ctk
from tkinterdnd2 import TkinterDnD, DND_ALL
from tkinter import StringVar
class TabView(ctk.CTkTabview, TkinterDnD.DnDWrapper):
def __init__(self, master):
super().__init__(master)
self.TkdndVersion = TkinterDnD._require(self)
global path_string
self.add('Tab 1')
self.add('Tab 2')
# Input file drag and drop.
path_string = StringVar(value='Drop a file here')
self.PathLabel = ctk.CTkLabel(master=self.tab('Tab 1'), text='Input File', anchor='w')
self.PathLabel.grid(row=0, column=0, padx=10, pady=10, sticky='ew')
self.PathEntry = ctk.CTkEntry(master=self.tab('Tab 1'), textvariable=path_string)
self.PathEntry.grid(row=0, column=1, columnspan=3, padx=10, pady=10, sticky='ew')
self.PathEntry.drop_target_register(DND_ALL)
self.PathEntry.dnd_bind('<<Drop>>', self.fn_get_path)
# Number of shuffles.
self.ShuffleEntry = ctk.CTkEntry(master=self.tab('Tab 1'), textvariable=ctk.StringVar(value='10'))
self.ShuffleEntry.grid(row=1, column=1, padx=10, pady=10, sticky='ew')
# Number of units.
self.UnitsEntry = ctk.CTkEntry(master=self.tab('Tab 2'), textvariable=ctk.StringVar(value='1000'))
self.UnitsEntry.grid(row=1, column=1, padx=10, pady=10, sticky='ew')
# Settings frame with save now button.
self.SettingsFrame = SettingsFrame(master=self.tab('Tab 2'))
self.SettingsFrame.grid(row=2, column=1, padx=20, pady=20)
def fn_get_path(self, event):
path_string.set(value=event.data.strip('{}'))
def fn_save_now(self):
App.fn_save_settings(TabView)
return
class SettingsFrame(ctk.CTkFrame):
def __init__(self, master):
super().__init__(master)
self.SaveNowButton = ctk.CTkButton(master=self, width=40, height=20, text='Save',
command=lambda: TabView.fn_save_now(self))
self.SaveNowButton.grid(row=5, column=4, columnspan=1, padx=10, pady=10, sticky='ew')
class App(ctk.CTk):
def __init__(self):
super().__init__()
self.tab_view = TabView(master=self)
self.tab_view.grid(row=0, column=0, padx=20, pady=20)
def fn_save_settings(self):
Setting_1 = path_string.get()
Setting_2 = self.tab_view.ShuffleEntry.get() # Gives a no attribute error
Setting_3 = self.tab_view.UnitsEntry.get() # Gives a no attribute error
print(Setting_1, Setting_2, Setting_3)
return
app = App()
app.mainloop()
In this case the user can drop an input file into the PathEntry box and specify a couple of other entries. I would like them to be able to save their entries at any point by clicking the Save button on the second tab. I am managing to retrieve the path_string but not the other two settings of interest. How can I retrieve Setting_2 and Setting_3? I have tried using tab_view / master / controller options without success and I have tried moving functions around. If I can retrieve all the values then I expect I will be able to write code to save them but any further tips would be appreciated.
A solution I have got working is to remove the SettingsFrame class and put the contents into the TabView class:
import customtkinter as ctk
from tkinterdnd2 import TkinterDnD, DND_ALL
from tkinter import StringVar
class TabView(ctk.CTkTabview, TkinterDnD.DnDWrapper):
def __init__(self, master):
super().__init__(master)
self.TkdndVersion = TkinterDnD._require(self)
global path_string
self.add('Tab 1')
self.add('Tab 2')
# Input file drag and drop.
path_string = StringVar(value='Drop a file here')
self.PathLabel = ctk.CTkLabel(master=self.tab('Tab 1'), text='Input File', anchor='w')
self.PathLabel.grid(row=0, column=0, padx=10, pady=10, sticky='ew')
self.PathEntry = ctk.CTkEntry(master=self.tab('Tab 1'), textvariable=path_string)
self.PathEntry.grid(row=0, column=1, columnspan=3, padx=10, pady=10, sticky='ew')
self.PathEntry.drop_target_register(DND_ALL)
self.PathEntry.dnd_bind('<<Drop>>', self.fn_get_path)
# Number of shuffles.
self.ShuffleEntry = ctk.CTkEntry(master=self.tab('Tab 1'), textvariable=ctk.StringVar(value='10'))
self.ShuffleEntry.grid(row=1, column=1, padx=10, pady=10, sticky='ew')
# Number of units.
self.UnitsEntry = ctk.CTkEntry(master=self.tab('Tab 2'), textvariable=ctk.StringVar(value='1000'))
self.UnitsEntry.grid(row=1, column=1, padx=10, pady=10, sticky='ew')
# Settings frame with save now button.
self.settings_frame()
def fn_get_path(self, event):
path_string.set(value=event.data.strip('{}'))
def settings_frame(self):
self.SettingsFrame = ctk.CTkFrame(master=self.tab('Tab 2'))
self.SettingsFrame.grid(row=2, column=1, padx=20, pady=20)
self.SaveNowButton = ctk.CTkButton(self.SettingsFrame, width=40, height=20, text='Save',
command=lambda: self.fn_save_now())
self.SaveNowButton.grid(row=5, column=4, columnspan=1, padx=10, pady=10, sticky='ew')
def fn_save_now(self, *_args):
App.fn_save_settings(self)
return
class App(ctk.CTk):
def __init__(self):
super().__init__()
self.tab_view = TabView(master=self)
self.tab_view.grid(row=0, column=0, padx=20, pady=20)
def fn_save_settings(self):
Setting_1 = path_string.get()
Setting_2 = self.ShuffleEntry.get() # Gives a no attribute error
Setting_3 = self.UnitsEntry.get() # Gives a no attribute error
print(Setting_1, Setting_2, Setting_3)
return
app = App()
app.mainloop()
The TabView class will get massive but at least I have something that works!