I am writing a dictionary program using customtkinter library. There is one button and a switch. When I press the right button, the programm destroys first ten sets of widgets and creates next 10 so it looks like I turned a page. So if I turn on the switch before hitting the right button, there are no errors. But if I first click the rightbutton and then click the switcher, it raises errors. I understand that there are some logical conflicts at this point, but I can't find out where the mistake in the code is. The buttons which the switcher activates all at once will be used for acces to editing menus. Here is a simplified reproducible code:
import customtkinter
customtkinter.set_appearance_mode('light')
customtkinter.set_default_color_theme('dark-blue')
root = customtkinter.CTk()
root.geometry('750x800')
root.title('My dictionary')
root.resizable(width=False, height=False)
database_example = (
[1, 1, 1], [2, 2, 2], [3, 3, 3], [4, 4, 4], [5, 5, 5], [6, 6, 6], [7, 7, 7], [8, 8, 8], [9, 9, 9],
[10, 10, 10], [11, 11, 11], [12, 12, 12], [13, 13, 13], [14, 14, 14], [15, 15, 15], [16, 16, 16],
[17, 17, 17], [18, 18, 18], [19, 19, 19], [20, 20, 20], [21, 21, 21], [22, 22, 22], [23, 23, 23],
[24, 24, 24], [25, 25, 25], [26, 26, 26], [27, 27, 27], [28, 28, 28], [29, 29, 29], [30, 30, 30],
[31, 31, 31], [32, 32, 32], [33, 33, 33], [34, 34, 34], [35, 35, 35], [36, 36, 36], [37, 37, 38],
[39, 39, 39], [40, 40, 40]
)
start_pos = 0
end_pos = 10
class WordBox:
button_container = []
instances = []
def __init__(self,
word='',
translation='',
context='',
):
self.instances.append(self)
word_fontsize = 23
translation_fontsize = 23
self.outer_frame = customtkinter.CTkFrame(master=scroll_frame, width=700, height=150, fg_color='#AED5D5')
self.outer_frame.pack(pady=12, padx=10, side='top')
self.inner_LabelWord = customtkinter.CTkLabel(master=self.outer_frame,
text=word,
fg_color='#C9D1D8',
text_color='#0B7BDE',
font=('Arial Black', word_fontsize),
width=450, height=50, anchor='nw')
self.inner_LabelWord.grid(row=0, column=0)
self.inner_LabelTranslation = customtkinter.CTkLabel(master=self.outer_frame,
text=translation,
fg_color='#C9D1D8',
text_color='black',
font=('Yu Gothic UI Semibold', translation_fontsize),
width=450, height=50, anchor='nw')
self.inner_LabelTranslation.grid(row=1, column=0)
self.inner_LabelContext = customtkinter.CTkLabel(master=self.outer_frame,
text=context,
fg_color='#908D8B',
text_color='black',
font=('Dubai', 14),
width=450, height=50, anchor='nw')
self.inner_LabelContext.grid(row=2, column=0)
self.inner_PicFrame = customtkinter.CTkFrame(master=self.outer_frame, fg_color='#48E5E0', width=150, height=150)
self.inner_PicFrame.grid(row=0, column=1, rowspan=3)
self.inner_PicLabel = customtkinter.CTkLabel(master=self.inner_PicFrame, text='PICTURE')
self.inner_PicLabel.pack()
self.inner_Button = customtkinter.CTkButton(master=self.outer_frame, width=50, height=150, text='O',
font=('', 20), fg_color='#C9D1D8', hover_color='#FFA60B',
state='disabled')
self.inner_Button.grid(row=0, column=2, rowspan=3)
self.button_container.append(self.inner_Button)
def switcher():
if switch_var.get() == 'on':
for i in WordBox.button_container:
i.configure(state='normal')
if switch_var.get() == 'off':
for i in WordBox.button_container:
i.configure(state='disabled')
def start_programm(db):
for word in db[:10]:
WordBox(
word=word[0],
translation=word[1],
context=word[2],
)
def rightbutton():
global start_pos
global end_pos
start_pos += 10
end_pos += 10
for item in WordBox.button_container:
del item
for instance in WordBox.instances:
instance.inner_Button.destroy()
instance.outer_frame.destroy()
instance.inner_PicFrame.destroy()
instance.inner_LabelContext.destroy()
instance.inner_LabelTranslation.destroy()
instance.inner_LabelWord.destroy()
del instance
for word in database_example[start_pos:end_pos]:
WordBox(
word=word[0],
translation=word[1],
context=word[2],
)
switch_var = customtkinter.StringVar(value="off")
switch = customtkinter.CTkSwitch(master=root, text='', command=switcher,
variable=switch_var, onvalue="on", offvalue="off",
progress_color='#E5611F')
switch.place(relx=0.862, rely=0.26)
scroll_frame = customtkinter.CTkScrollableFrame(master=root, width=700, height=500)
scroll_frame.place(relx=0.01, rely=0.3)
right_button = customtkinter.CTkButton(master=root, text='>>', command=rightbutton, width=35, height=30,
corner_radius=17, fg_color='#817A7A', hover_color='#E5611F')
right_button.place(relx=0.12, rely=0.26)
start_programm(database_example)
root.mainloop()
Oh, I've found the solution. For some reason del operator wasn't working the way it should (or I thought it should). Instead of using del, I have rewritten the rightbutton function, using clear() method to empty button_container and instances containers of WordBox class. And now it works. Here is the rewritten function:
def rightbutton():
global start_pos
global end_pos
start_pos += 10
end_pos += 10
for instance in WordBox.instances:
instance.inner_Button.destroy()
instance.outer_frame.destroy()
instance.inner_PicFrame.destroy()
instance.inner_LabelContext.destroy()
instance.inner_LabelTranslation.destroy()
instance.inner_LabelWord.destroy()
WordBox.button_container.clear()
WordBox.instances.clear()
for word in database_example[start_pos:end_pos]:
WordBox(
word=word[0],
translation=word[1],
context=word[2],
)
You need to clear the two lists WordBox.button_container
and WordBox.instances
after deleting their items, otherwise .configure(...)
will be called on those deleted items and raise the exception when switcher()
is executed. Also you need to call switcher()
to set the correct state of those newly created inner_buttons:
def rightbutton():
global start_pos
global end_pos
start_pos += 10
end_pos += 10
for item in WordBox.button_container:
del item
WordBox.button_container.clear() # clear the list
for instance in WordBox.instances:
instance.inner_Button.destroy()
instance.outer_frame.destroy()
instance.inner_PicFrame.destroy()
instance.inner_LabelContext.destroy()
instance.inner_LabelTranslation.destroy()
instance.inner_LabelWord.destroy()
del instance
WordBox.instances.clear() # clear the list
for word in database_example[start_pos:end_pos]:
WordBox(
word=word[0],
translation=word[1],
context=word[2],
)
switcher() # set the correct state of the buttons