I have a set of simple comboboxes and I created two buttons: one to clear the selections and one to validate if all the comboboxes have been selected. I'm using frames so I can treat the comboboxes as a group for each operation and I want to display a messagebox if one or more of the comboboxes have no selection. I found selection_present in the docs and a validatecommand, but I'm getting an error: AttributeError: 'ComboBoxTestGroup' object has no attribute 'tk', which is a separate issue that is preventing me from trying things.
My main question is: for a tkinter Combobox, what is a simple way to check if all the comboboxes are selected and message the user if they are not? Then, why am I getting an attribute error on the comboboxes as they should be members of the ComboBoxTestGroup class?
I'm new to tkinter and I'm stuck on this. Here is the code:
import tkinter
from tkinter import *
from tkinter import messagebox
from tkinter.ttk import Combobox
import ttkbootstrap as ttk
from ttkbootstrap.constants import *
window = ttk.Window(themename = 'superhero')
window.title('Combobox Test Group')
window.geometry('1200x275')
class ComboBoxTestGroup:
def __init__(self, master):
# Create the main frame.
self.main_frame = ttk.Frame(master, relief=SUNKEN, borderwidth=1)
self.main_frame.grid(row=0, column=0)
# Dropdowns
self.base_frame = tkinter.LabelFrame(self.main_frame, text='BASE', relief=SUNKEN, borderwidth=1)
self.base_frame.grid(row=4, column=0, sticky="news", padx=10, pady=15)
self.base_label_ti = ttk.Label(self.base_frame, text='Technical Interface (TI)', font=('Helvetica', 10))
self.base_label_ti.grid(row=0, column=0)
self.ti_values = [("High", "H", "1.0"), ("Medium", "M", "0.5"), ("Low", "L", "0.1"), ("None", "N", "0.0")]
self.ti_combobox = ttk.Combobox(self.base_frame, bootstyle='primary', values=self.ti_values, state = 'readonly')
self.ti_combobox.grid(row=1, column=0)
self.base_label_ap = ttk.Label(self.base_frame, text='Application Project (AP)', font=('Helvetica', 10))
self.base_label_ap.grid(row=0, column=1)
self.ap_values = [("High", "H", "1.0"), ("Medium", "M", "0.5"), ("Low", "L", "0.1"), ("None", "N", "0.0")]
self.ap_combobox = ttk.Combobox(self.base_frame, bootstyle='primary', values=self.ap_values, state = 'readonly')
self.ap_combobox.grid(row=1, column=1)
self.base_label_al = ttk.Label(self.base_frame, text='Application Logs (AL)', font=('Helvetica', 10))
self.base_label_al.grid(row=0, column=2)
self.al_values = [("High", "H", "1.0"), ("Medium", "M", "0.5"), ("Low", "L", "0.1"), ("None", "N", "0.0")]
self.al_combobox = ttk.Combobox(self.base_frame, bootstyle='primary', values=self.al_values, state = 'readonly')
self.al_combobox.grid(row=1, column=2)
self.base_label_ic = ttk.Label(self.base_frame, text='Internal Composition (IC)', font=('Helvetica', 10))
self.base_label_ic.grid(row=0, column=3)
self.ic_values = [("High", "H", "1.0"), ("Medium", "M", "0.5"), ("Low", "L", "0.1"), ("None", "N", "0.0")]
self.ic_combobox = ttk.Combobox(self.base_frame, bootstyle='primary', values=self.ic_values, state = 'readonly')
self.ic_combobox.grid(row=1, column=3)
self.base_label_fc = ttk.Label(self.base_frame, text='Fixed Controls (FC)', font=('Helvetica', 10))
self.base_label_fc.grid(row=0, column=4)
self.fc_values = [("High", "H", "1.0"), ("Medium", "M", "0.5"), ("Low", "L", "0.1"), ("None", "N", "0.0")]
self.fc_combobox = ttk.Combobox(self.base_frame, bootstyle='primary', values=self.fc_values, state = 'readonly')
self.fc_combobox.grid(row=1, column=4)
# For loop to space widgets within the base_frame
for widget in self.base_frame.winfo_children():
widget.grid_configure(padx=20, pady=10)
# Create a frame for the buttons.
button_frame = ttk.Frame(self.main_frame, relief=SUNKEN, borderwidth=1)
button_frame.grid(row=5, column=0)
# Validate Button
self.calculate_button = ttk.Button(button_frame,
command = lambda: validateComboboxes(self), # Use a lambda to send params to the function for command.
width = 12,
text = "VALIDATE",
bootstyle = 'primary',
state = 'enabled')
self.calculate_button.grid(row=10, column=0, padx=75, pady=10)
# Clear Button
self.clear_button = ttk.Button(button_frame,
command = lambda: clear(self), # Use a lambda to send params to the function for command.
width = 12,
text = "CLEAR",
bootstyle = 'primary',
state = 'enabled')
self.clear_button.grid(row=10, column=1, padx=75, pady=10)
# Function to clear the ttk widgets.
def clear(self):
# This clears the Comboboxes by the enclosing frame.
for widget in self.base_frame.winfo_children():
if isinstance(widget, ttk.Combobox):
widget.set("")
# Function to validate if all the Comboboxes were selected.
def validateComboboxes(self):
for widget in self.base_frame.winfo_children():
if not ttk.Combobox.selection_present(self):
messagebox.showerror("error", "All Comboboxes must be selected in order to do calculations!")
# widget.set("")
widget_class = ComboBoxTestGroup(window)
# Run the GUI
window.mainloop()
I used selection present as that appeared to be the obvious choice for my use case, but the attribute error is blocking me from getting to that result.
You are receiving an error because you are attempting to call selection_present
as a class method when it should be used as an instance method.
Either way, selection_present
is not the method to use for this situation.
One possible solution to the problem would be to check if the widget is an Combobox similar to what you are already doing in the clear
method, and then use the get
method to retrieve the current text, and check if it's == ""
to see if its valid selection or not:
For example something similar to this should probably work:
def validateComboboxes(self):
for widget in self.base_frame.winfo_children():
if isinstance(widget, ttk.Combobox) and widget.get() == "":
messagebox.showerror("error", "All Comboboxes must be selected in order to do calculations!")
...