Open ohshitgorillas opened 1 month ago
I've made some progress here:
analysis_list_var = tk.StringVar()
analysis_list = []
for analysis in filtered_data:
if analysis.reduced:
analysis_list.append(f'☑ {analysis.analysis_label}')
else:
analysis_list.append(f'☐ {analysis.analysis_label}')
analysis_list_var.set(analysis_list)
analysis_listbox = CTkListbox(right_frame, width=220)
analysis_listbox.listvariable = analysis_list_var
analysis_listbox.pack(side=tk.BOTTOM, fill=tk.BOTH, expand=True)
analysis_listbox.bind("<<ListboxSelect>>",
lambda event: on_analysis_select(analysis_listbox, analysis_list_var, filtered_data, fit_type, figure, canvas, stats_frame)
)
and
# highlights the current analysis in the list
def highlight_current_analysis(analysis_listbox, analysis_list_var, filtered_data):
global analysis_index
# check that the analysis is reduced and that the symbol is ☐
if filtered_data[analysis_index].reduced and analysis_list_var.get()[analysis_index][0] == '☐':
print('checking box')
analysis_list_var.set(analysis_list_var.get()[:analysis_index] + f'☑ {analysis_list_var.get()[analysis_index+3:]}')
analysis_listbox.update_listvar()
analysis_listbox.activate(analysis_index)
# moves to the selected analysis when clicked in the list
def on_analysis_select(analysis_listbox, analysis_list_var, filtered_data, fit_type, figure, canvas, stats_frame):
global analysis_index
selected_item = analysis_listbox.curselection()
analysis_index = int(analysis_listbox.get(selected_item))
interactive_update(filtered_data[analysis_index], fit_type, figure, canvas, stats_frame)
update_buttons(filtered_data)
highlight_current_analysis(analysis_listbox, analysis_list_var, filtered_data)
This should work, however, I'm encountering an empty list instead of a populated one. I can also confirm that anaylsis_list_var
isn't empty:
analysis_list_var: ('☑ CB2244 5min', '☑ LB1023', '☑ LB1024', '☑ Q3778_', '☑ Q3779_', '☑ DT913_', '☑ Q3780_', '☑ DT914_', '☑ Q3781_', '☑ DT915_', '☑ Q3782_', '☑ Q3783_', '☑ LB1025', '☑ LB1026', '☑ CB2245 5min')
With a friend's help I figured out that I shouldn't be calling listboxvar or update_listbox, but just inserting and deleting items like normally.
So I've come back to this configuration:
# insert items into the analysis listbox
analysis_listbox = CTkListbox(right_frame, width=220)
for i, analysis in enumerate(filtered_data):
if analysis.reduced:
analysis_listbox.insert(i, f'☑ {analysis.analysis_label}')
else:
analysis_listbox.insert(i, f'☐ {analysis.analysis_label}')
analysis_listbox.pack(side=tk.BOTTOM, fill=tk.BOTH, expand=True)
analysis_listbox.bind("<<ListboxSelect>>",
lambda event: on_analysis_select(analysis_listbox, filtered_data, fit_type, figure, canvas, stats_frame)
)
with these helper functions:
# highlights the current analysis in the list
def highlight_current_analysis(analysis_listbox, filtered_data):
global analysis_index
# check that the analysis is reduced and that the symbol is ☐ before changing to ☑
if filtered_data[analysis_index].reduced and analysis_listbox.get(analysis_index)[0] == '☐':
analysis_listbox.delete(analysis_index)
analysis_listbox.insert(analysis_index, f'☑ {filtered_data[analysis_index].analysis_label}')
analysis_listbox.activate(analysis_index)
# moves to the selected analysis when clicked in the list
def on_analysis_select(analysis_listbox, filtered_data, fit_type, figure, canvas, stats_frame):
global analysis_index
analysis_index = analysis_listbox.curselection()
print(analysis_index)
interactive_update(filtered_data[analysis_index], fit_type, figure, canvas, stats_frame)
update_buttons(filtered_data)
analysis_listbox.unbind("<<ListboxSelect>>")
highlight_current_analysis(analysis_listbox, filtered_data)
analysis_listbox.bind("<<ListboxSelect>>",
lambda event: on_analysis_select(analysis_listbox, filtered_data, fit_type, figure, canvas, stats_frame)
)
however, the behavior is extremely erratic with the analysis clicked sometimes being deleted and never re-added, sometimes another sample's name is deleted and re-added, etc accompanied by the following errors:
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Users\atomg\AppData\Local\Programs\Python\Python311\Lib\tkinter\__init__.py", line 1948, in __call__
return self.func(*args)
^^^^^^^^^^^^^^^^
File "C:\Users\atomg\AppData\Local\Programs\Python\Python311\Lib\tkinter\__init__.py", line 861, in callit
func(*args)
File "C:\Users\atomg\AppData\Local\Programs\Python\Python311\Lib\site-packages\CTkListbox\ctk_listbox.py", line 125, in <lambda>
self.after(100, lambda: selected_button.configure(hover=self.hover))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\atomg\AppData\Local\Programs\Python\Python311\Lib\site-packages\customtkinter\windows\widgets\ctk_button.py", line 442, in configure
super().configure(require_redraw=require_redraw, **kwargs)
File "C:\Users\atomg\AppData\Local\Programs\Python\Python311\Lib\site-packages\customtkinter\windows\widgets\core_widget_classes\ctk_base_class.py", line 130, in configure
super().configure(**pop_from_dict_by_set(kwargs, self._valid_tk_frame_attributes)) # configure tkinter.Frame
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\atomg\AppData\Local\Programs\Python\Python311\Lib\tkinter\__init__.py", line 1702, in configure
return self._configure('configure', cnf, kw)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\atomg\AppData\Local\Programs\Python\Python311\Lib\tkinter\__init__.py", line 1689, in _configure
return self._getconfigure(_flatten((self._w, cmd)))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\atomg\AppData\Local\Programs\Python\Python311\Lib\tkinter\__init__.py", line 1673, in _getconfigure
for x in self.tk.splitlist(self.tk.call(*args)):
^^^^^^^^^^^^^^^^^^^
_tkinter.TclError: invalid command name ".!ctkframe.!ctkframe2.!ctkframe2.!ctkframe.!canvas.!ctklistbox.!ctkbutton5"
Sorry for spamming. I got it working, except for one error when I click on the item in the list:
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Users\atomg\AppData\Local\Programs\Python\Python311\Lib\tkinter\__init__.py", line 1948, in __call__
return self.func(*args)
^^^^^^^^^^^^^^^^
File "C:\Users\atomg\AppData\Local\Programs\Python\Python311\Lib\tkinter\__init__.py", line 861, in callit
func(*args)
File "C:\Users\atomg\AppData\Local\Programs\Python\Python311\Lib\site-packages\CTkListbox\ctk_listbox.py", line 125, in <lambda>
self.after(100, lambda: selected_button.configure(hover=self.hover))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\atomg\AppData\Local\Programs\Python\Python311\Lib\site-packages\customtkinter\windows\widgets\ctk_button.py", line 442, in configure
super().configure(require_redraw=require_redraw, **kwargs)
File "C:\Users\atomg\AppData\Local\Programs\Python\Python311\Lib\site-packages\customtkinter\windows\widgets\core_widget_classes\ctk_base_class.py", line 130, in configure
super().configure(**pop_from_dict_by_set(kwargs, self._valid_tk_frame_attributes)) # configure tkinter.Frame
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\atomg\AppData\Local\Programs\Python\Python311\Lib\tkinter\__init__.py", line 1702, in configure
return self._configure('configure', cnf, kw)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\atomg\AppData\Local\Programs\Python\Python311\Lib\tkinter\__init__.py", line 1689, in _configure
return self._getconfigure(_flatten((self._w, cmd)))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\atomg\AppData\Local\Programs\Python\Python311\Lib\tkinter\__init__.py", line 1673, in _getconfigure
for x in self.tk.splitlist(self.tk.call(*args)):
^^^^^^^^^^^^^^^^^^^
As for the above code, the solution was to insert but NOT delete:
# highlights the current analysis in the list
def highlight_current_analysis(analysis_listbox, analysis_index, filtered_data):
# check that the analysis is reduced and that the symbol is ☐ before changing to ☑
if filtered_data[analysis_index].reduced and analysis_listbox.get(analysis_index)[0] == '☐':
analysis_listbox.insert(analysis_index, f'☑ {filtered_data[analysis_index].analysis_label}')
analysis_listbox.activate(analysis_index)
# moves to the selected analysis when clicked in the list
def on_analysis_select(analysis_listbox, filtered_data, fit_type, figure, canvas, stats_frame):
global analysis_index
analysis_index = analysis_listbox.curselection()
interactive_update(filtered_data[analysis_index], fit_type, figure, canvas, stats_frame)
update_buttons(filtered_data)
analysis_listbox.unbind("<<ListboxSelect>>")
highlight_current_analysis(analysis_listbox, analysis_index, filtered_data)
analysis_listbox.bind("<<ListboxSelect>>",
lambda event: on_analysis_select(analysis_listbox, filtered_data, fit_type, figure, canvas, stats_frame)
)
Any idea on the error I'm getting and how I can hide/resolve it?
Traceback (most recent call last):
File "C:\Users\atomg\AppData\Local\Programs\Python\Python37\lib\tkinter\__init__.py", line 1705, in __call__
return self.func(*args)
File "C:\Users\atomg\AppData\Local\Programs\Python\Python37\lib\tkinter\__init__.py", line 749, in callit
func(*args)
File "C:\Users\atomg\OneDrive - Thermochron Systems LLC\heman_code\Pro\.venv\lib\site-packages\CTkListbox\ctk_listbox.py", line 125, in <lambda>
self.after(100, lambda: selected_button.configure(hover=self.hover))
File "C:\Users\atomg\OneDrive - Thermochron Systems LLC\heman_code\Pro\.venv\lib\site-packages\customtkinter\windows\widgets\ctk_button.py", line 442, in configure
super().configure(require_redraw=require_redraw, **kwargs)
File "C:\Users\atomg\OneDrive - Thermochron Systems LLC\heman_code\Pro\.venv\lib\site-packages\customtkinter\windows\widgets\core_widget_classes\ctk_base_class.py", line 130, in configure
super().configure(**pop_from_dict_by_set(kwargs, self._valid_tk_frame_attributes)) # configure tkinter.Frame
File "C:\Users\atomg\AppData\Local\Programs\Python\Python37\lib\tkinter\__init__.py", line 1485, in configure
return self._configure('configure', cnf, kw)
File "C:\Users\atomg\AppData\Local\Programs\Python\Python37\lib\tkinter\__init__.py", line 1473, in _configure
return self._getconfigure(_flatten((self._w, cmd)))
File "C:\Users\atomg\AppData\Local\Programs\Python\Python37\lib\tkinter\__init__.py", line 1457, in _getconfigure
for x in self.tk.splitlist(self.tk.call(*args)):
_tkinter.TclError: invalid command name ".!ctkframe.!ctkframe2.!ctkframe2.!ctkframe.!canvas.!ctklistbox.!ctkbutton13"
The behavior is totally fine, everything works as expected, this is just a random error I'm trying to clear. It only occurs when I click on an analysis that I haven't touched yet, if I go back to an analysis that has already been seen/loaded then there's no error.
I kinda (for me) fixed the error problem by replacing the code line 125 in ctk_listbox.py by : self.after(100, lambda: selected_button.configure(hover=self.hover) if selected_button.winfo_exists() else None)
It checks if the selected_button object exists at the time the lamda function is called. (Thanks to GitHub Copilot)
I am relatively new to Python, so reading the source code for this is hard for me. Can you please add an example of how to use update_listvar and listvariable to the documentation? I love the way that this frame looks, but I am trying to update the text content of a list item in place and running into errors when I try to delete and re-insert the item.
Basically, I am writing mass spectrometry data reduction software and I want to give the user a list of samples in the sequence, preceded by a ☐ when the data has not been reduced, and a ☑ when the data has been reduced. Also, if the user clicks on a sample in the list, the other frames should be updated to show that sample's data.
This is the function giving me trouble: