How do I enable multiple selection of values from a combobox?
Question:
Python 3.4.3, Windows 10, Tkinter
I am attempting to create a combobox that allows for multiple selections from the dropdown. I have found similar work for listbox (Python Tkinter multiple selection Listbox), but cannot get it work with the combobox.
Is there a simple way to enable multiple selection from the dropdown of the combobox?
Answers:
By design the ttk combobox doesn’t support multiple selections. It is designed to allow you to pick one item from a list of choices.
If you need to be able to make multiple choices you can use a menubutton with an associated menu, and add checkbuttons or radiobuttons to the menu.
Here’s an example:
import Tkinter as tk
class Example(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
menubutton = tk.Menubutton(self, text="Choose wisely",
indicatoron=True, borderwidth=1, relief="raised")
menu = tk.Menu(menubutton, tearoff=False)
menubutton.configure(menu=menu)
menubutton.pack(padx=10, pady=10)
self.choices = {}
for choice in ("Iron Man", "Superman", "Batman"):
self.choices[choice] = tk.IntVar(value=0)
menu.add_checkbutton(label=choice, variable=self.choices[choice],
onvalue=1, offvalue=0,
command=self.printValues)
def printValues(self):
for name, var in self.choices.items():
print "%s: %s" % (name, var.get())
if __name__ == "__main__":
root = tk.Tk()
Example(root).pack(fill="both", expand=True)
root.mainloop()
Try this maybe…
import tkinter as Tkinter
import tkinter.font as tkFont
import tkinter.ttk as ttk
class Picker(ttk.Frame):
def __init__(self, master=None,activebackground='#b1dcfb',values=[],entry_wid=None,activeforeground='black', selectbackground='#003eff', selectforeground='white', command=None, borderwidth=1, relief="solid"):
self._selected_item = None
self._values = values
self._entry_wid = entry_wid
self._sel_bg = selectbackground
self._sel_fg = selectforeground
self._act_bg = activebackground
self._act_fg = activeforeground
self._command = command
ttk.Frame.__init__(self, master, borderwidth=borderwidth, relief=relief)
self.bind("<FocusIn>", lambda event:self.event_generate('<<PickerFocusIn>>'))
self.bind("<FocusOut>", lambda event:self.event_generate('<<PickerFocusOut>>'))
self._font = tkFont.Font()
self.dict_checkbutton = {}
self.dict_checkbutton_var = {}
self.dict_intvar_item = {}
for index,item in enumerate(self._values):
self.dict_intvar_item[item] = Tkinter.IntVar()
self.dict_checkbutton[item] = ttk.Checkbutton(self, text = item, variable=self.dict_intvar_item[item],command=lambda ITEM = item:self._command(ITEM))
self.dict_checkbutton[item].grid(row=index, column=0, sticky=Tkinter.NSEW)
self.dict_intvar_item[item].set(0)
class Combopicker(ttk.Entry, Picker):
def __init__(self, master, values= [] ,entryvar=None, entrywidth=None, entrystyle=None, onselect=None,activebackground='#b1dcfb', activeforeground='black', selectbackground='#003eff', selectforeground='white', borderwidth=1, relief="solid"):
if entryvar is not None:
self.entry_var = entryvar
else:
self.entry_var = Tkinter.StringVar()
entry_config = {}
if entrywidth is not None:
entry_config["width"] = entrywidth
if entrystyle is not None:
entry_config["style"] = entrystyle
ttk.Entry.__init__(self, master, textvariable=self.entry_var, **entry_config, state = "readonly")
self._is_menuoptions_visible = False
self.picker_frame = Picker(self.winfo_toplevel(), values=values,entry_wid = self.entry_var,activebackground=activebackground, activeforeground=activeforeground, selectbackground=selectbackground, selectforeground=selectforeground, command=self._on_selected_check)
self.bind_all("<1>", self._on_click, "+")
self.bind("<Escape>", lambda event: self.hide_picker())
@property
def current_value(self):
try:
value = self.entry_var.get()
return value
except ValueError:
return None
@current_value.setter
def current_value(self, INDEX):
self.entry_var.set(values.index(INDEX))
def _on_selected_check(self, SELECTED):
value = []
if self.entry_var.get() != "" and self.entry_var.get() != None:
temp_value = self.entry_var.get()
value = temp_value.split(",")
if str(SELECTED) in value:
value.remove(str(SELECTED))
else:
value.append(str(SELECTED))
value.sort()
temp_value = ""
for index,item in enumerate(value):
if item!= "":
if index != 0:
temp_value += ","
temp_value += str(item)
self.entry_var.set(temp_value)
def _on_click(self, event):
str_widget = str(event.widget)
if str_widget == str(self):
if not self._is_menuoptions_visible:
self.show_picker()
else:
if not str_widget.startswith(str(self.picker_frame)) and self._is_menuoptions_visible:
self.hide_picker()
def show_picker(self):
if not self._is_menuoptions_visible:
self.picker_frame.place(in_=self, relx=0, rely=1, relwidth=1 )
self.picker_frame.lift()
self._is_menuoptions_visible = True
def hide_picker(self):
if self._is_menuoptions_visible:
self.picker_frame.place_forget()
self._is_menuoptions_visible = False
if __name__ == "__main__":
import sys
try:
from Tkinter import Tk, Frame, Label
except ImportError:
from tkinter import Tk, Frame, Label
root = Tk()
root.geometry("500x600")
main =Frame(root, pady =15, padx=15)
main.pack(expand=True, fill="both")
Label(main, justify="left", text=__doc__).pack(anchor="w", pady=(0,15))
COMBOPICKER1 = Combopicker(main, values = [1, 2, 3, 4])
COMBOPICKER1.pack(anchor="w")
if 'win' not in sys.platform:
style = ttk.Style()
style.theme_use('clam')
root.mainloop()
I used the example of #SilverHalo which I finally improved I think I added the possibility of searching in the combobox and also added a scrollbar because in his example the frame became much too large with many values. I also added the possibility to enlarge the combobox with a right click in the search bar or a right click on the checkbutton you just have to install scrolledframe from pip and see if the result can be useful.
import tkinter as tk
from tkinter import *
from tkinter import ttk
from tkscrolledframe import ScrolledFrame #https://github.com/bmjcode/tkScrolledFrame
# for installation pip install tkScrolledFrame
class Picker(ttk.Frame):
def __init__(self, master=None,
values=[],
entry_wid=None,
command=None,
entlarge_fonction=None,
borderwidth=1,
relief="solid",
font=('Arial, 11')):
self._selected_item = None
self._values = values
self._entry_wid = entry_wid
self._command = command
if type(entlarge_fonction).__name__!='NoneType':
self.entlarge_fonction=entlarge_fonction
else:
self.entlarge_fonction= lambda : 1
style = ttk.Style()
style.configure('Custom.TFrame', background='#ffffff')
ttk.Frame.__init__(self, master, borderwidth=borderwidth, relief=relief, style='Custom.TFrame')
self.bind("<Button-3>", lambda event : self.entlarge_fonction())
self.bind("<FocusIn>", lambda event:self.event_generate('<<PickerFocusIn>>'))
self.bind("<FocusOut>", lambda event:self.event_generate('<<PickerFocusOut>>'))
# search field
self.searchfield=ttk.Entry(self , font=font)
self.searchfield.pack(fill=X)
self.searchfield.insert(END, 'Search')
self.searchfield.bind("<Button-1>", lambda event : self.clear_search_field())
self.searchfield.bind("<Button-3>", lambda event : self.entlarge_fonction())
self.searchfield.bind('<KeyRelease>',lambda event : self.update_checkbutton_list())
self.scrolledframe=ScrolledFrame(self)
self.scrolledframe.pack(fill=BOTH)
self.scrolledframe.bind_arrow_keys(self)
self.scrolledframe.bind_scroll_wheel(self)
self.frame = self.scrolledframe.display_widget(Frame)
style = ttk.Style()
style.configure('Custom.TFrame', background='#ffffff')
#checkbutton frame
self.check_buttonFrame=ttk.Frame(self.frame, style='Custom.TFrame')
self.check_buttonFrame.pack(fill=BOTH)
self.dict_checkbutton = {}
self.dict_intvar_item = {}
self.selected_value=[]
self.create_check_button()
@property
def selection(self):
return self.selected_value
def set_value(self, new_values):
if type(new_values).__name__=='list':
self.selected_value=[]
self._values=new_values
else:
raise('ValueError')
def create_check_button(self, values=None):
self.dict_checkbutton = {}
self.dict_intvar_item = {}
if type(values).__name__=='NoneType':
values=self._values
# first clear my frame
for widget in self.check_buttonFrame.winfo_children():
widget.destroy()
for index,item in enumerate(values):
self.dict_intvar_item[item] = tk.IntVar()
self.dict_checkbutton[item] = ttk.Checkbutton(self.check_buttonFrame,
text = item,
variable=self.dict_intvar_item[item],
command=lambda selected=item : self.check_box_select(selected))
self.dict_checkbutton[item].grid(row=index, column=0, sticky=tk.NSEW)
self.dict_checkbutton[item].bind("<Button-3>", lambda e : self.entlarge_fonction())
if item not in self.selected_value:
self.dict_intvar_item[item].set(0)
else:
self.dict_intvar_item[item].set(1)
def check_box_select(self, selected_value=None):
for keys in self.dict_intvar_item.keys():
if self.dict_intvar_item[keys].get()!=0:
if keys not in self.selected_value:
self.selected_value.append(keys)
else:
if keys in self.selected_value:
self.selected_value.remove(keys)
try:
self._command(selected_value)
except Exception as e:
print(e)
def clear_search_field(self):
self.searchfield.delete(0, END)
self.update_checkbutton_list()
def update_checkbutton_list(self):
search_value=self.searchfield.get()
update_values=[]
if search_value!='':
for item in self._values:
if search_value.lower() in str(item).lower():
update_values.append(item)
else:
update_values=self._values.copy()
self.create_check_button(update_values)
class Combopicker(ttk.Combobox, Picker):
def __init__(self, master,
values= [] ,
entryvar=None,
entrywidth=25,
entrystyle=None,
font=('Arial, 16'),
command=None):
if entryvar is not None:
self.entry_var = entryvar
else:
self.entry_var = tk.StringVar()
self.entrywidth=entrywidth
if type(command).__name__=='NoneType':
self.command= lambda : 1
else:
self.command = command
entry_config = {}
if entrywidth is not None:
entry_config["width"] = entrywidth
if entrystyle is not None:
entry_config["style"] = entrystyle
ttk.Combobox.__init__(self, master,
textvariable=self.entry_var,
**entry_config,
state = "readonly", font=font)
self.unbind_class("TCombobox", "<Button-1>")
self.unbind_class("TCombobox", "<Double-1>")
self.unbind_class("TCombobox", "<Triple-1>")
self.unbind_class("TCombobox","<<ComboboxSelected>>")
self._is_menuoptions_visible = False
self.picker_frame = Picker(self.winfo_toplevel(),
values=values,
entry_wid = self.entry_var,
entlarge_fonction=self.entlarge,
command=self._on_selected_check)
self.bind_all("<1>", self._on_click, "+")
self.bind("<Escape>", lambda event: self.hide_picker())
# get widget size
self.expand_var=False
def set_value(self, new_value):
self.picker_frame.set_value(new_value)
def get(self):
return self.picker_frame.selection
def entlarge(self):
if self.expand_var==False:
self.config(width=80)
self.picker_frame.place(in_=self, relx=0, rely=1, relwidth=1, height=300 )
self.expand_var=True
else:
self.config(width=self.entrywidth)
self.picker_frame.place(in_=self, relx=0, rely=1, relwidth=1, height=150 )
self.expand_var=False
def _on_selected_check(self, SELECTED):
''' add selected values on entry'''
values=self.picker_frame.selection.copy()
if len(values)!=0:
values=str(values)[1:len(str(values))-1] # transform my list to string and remove []
self.entry_var.set(values)
self.command()
def _on_click(self, event):
str_widget = str(event.widget)
if str_widget == str(self):
if not self._is_menuoptions_visible:
self.show_picker()
else:
try:
self.hide_picker()
except:
pass
else:
if not str_widget.startswith(str(self.picker_frame)) and self._is_menuoptions_visible:
self.hide_picker()
def show_picker(self):
if not self._is_menuoptions_visible:
self.picker_frame.place(in_=self, relx=0, rely=1, relwidth=1, height=150 )
self.picker_frame.lift()
self._is_menuoptions_visible = True
def hide_picker(self):
if self._is_menuoptions_visible:
self.picker_frame.place_forget()
self._is_menuoptions_visible = False
if __name__ == "__main__":
root = Tk()
root.geometry("500x600")
main =Frame(root, pady =15, padx=15,)
main.pack(expand=True, fill="both")
data = {'Name': ['Alice', 'Bob', 'Charlie', 'David', 'Emma', 'Frank', 'Grace', 'Henry', 'Isabel', 'Jack'],
'Gender': ['F', 'M', 'M', 'M', 'F', 'M', 'F', 'M', 'F', 'M'],
'Age': [24, 32, 18, 47, 29, 53, 41, 19, 37, 28]}
def print_selection():
print('click')
for key in combobox.keys():
print(key, combobox[key].get())
combobox={}
counter=0
for key in data.keys():
Label(main, text=key).grid(row=counter, column= 0)
combobox[key]=Combopicker(main, values=list(set(data[key])) , command=lambda :print_selection())
combobox[key].grid(row=counter, column= 1)
counter+=1
root.mainloop()
After searching for a long time I found an answer to the same question:
https://stackoverflow.com/a/64961761/22078610
He created a ChecklistCombobox, which looks just like the normal ttk.Combobox but with Checkboxes. You can find his solution on GitHub: https://github.com/hatfullr/ChecklistCombobox
Example use:
from tkinter import *
from checklistcombobox import ChecklistCombobox
master = Tk()
mydropdown = ChecklistCombobox(master, values=[f"Value {a}" for a in range(1,20)])
def output():
print(mydropdown.get())
btn = Button(master, text="Print Output", command=output)
btn.pack()
mydropdown.pack()
master.mainloop()
Python 3.4.3, Windows 10, Tkinter
I am attempting to create a combobox that allows for multiple selections from the dropdown. I have found similar work for listbox (Python Tkinter multiple selection Listbox), but cannot get it work with the combobox.
Is there a simple way to enable multiple selection from the dropdown of the combobox?
By design the ttk combobox doesn’t support multiple selections. It is designed to allow you to pick one item from a list of choices.
If you need to be able to make multiple choices you can use a menubutton with an associated menu, and add checkbuttons or radiobuttons to the menu.
Here’s an example:
import Tkinter as tk
class Example(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
menubutton = tk.Menubutton(self, text="Choose wisely",
indicatoron=True, borderwidth=1, relief="raised")
menu = tk.Menu(menubutton, tearoff=False)
menubutton.configure(menu=menu)
menubutton.pack(padx=10, pady=10)
self.choices = {}
for choice in ("Iron Man", "Superman", "Batman"):
self.choices[choice] = tk.IntVar(value=0)
menu.add_checkbutton(label=choice, variable=self.choices[choice],
onvalue=1, offvalue=0,
command=self.printValues)
def printValues(self):
for name, var in self.choices.items():
print "%s: %s" % (name, var.get())
if __name__ == "__main__":
root = tk.Tk()
Example(root).pack(fill="both", expand=True)
root.mainloop()
Try this maybe…
import tkinter as Tkinter
import tkinter.font as tkFont
import tkinter.ttk as ttk
class Picker(ttk.Frame):
def __init__(self, master=None,activebackground='#b1dcfb',values=[],entry_wid=None,activeforeground='black', selectbackground='#003eff', selectforeground='white', command=None, borderwidth=1, relief="solid"):
self._selected_item = None
self._values = values
self._entry_wid = entry_wid
self._sel_bg = selectbackground
self._sel_fg = selectforeground
self._act_bg = activebackground
self._act_fg = activeforeground
self._command = command
ttk.Frame.__init__(self, master, borderwidth=borderwidth, relief=relief)
self.bind("<FocusIn>", lambda event:self.event_generate('<<PickerFocusIn>>'))
self.bind("<FocusOut>", lambda event:self.event_generate('<<PickerFocusOut>>'))
self._font = tkFont.Font()
self.dict_checkbutton = {}
self.dict_checkbutton_var = {}
self.dict_intvar_item = {}
for index,item in enumerate(self._values):
self.dict_intvar_item[item] = Tkinter.IntVar()
self.dict_checkbutton[item] = ttk.Checkbutton(self, text = item, variable=self.dict_intvar_item[item],command=lambda ITEM = item:self._command(ITEM))
self.dict_checkbutton[item].grid(row=index, column=0, sticky=Tkinter.NSEW)
self.dict_intvar_item[item].set(0)
class Combopicker(ttk.Entry, Picker):
def __init__(self, master, values= [] ,entryvar=None, entrywidth=None, entrystyle=None, onselect=None,activebackground='#b1dcfb', activeforeground='black', selectbackground='#003eff', selectforeground='white', borderwidth=1, relief="solid"):
if entryvar is not None:
self.entry_var = entryvar
else:
self.entry_var = Tkinter.StringVar()
entry_config = {}
if entrywidth is not None:
entry_config["width"] = entrywidth
if entrystyle is not None:
entry_config["style"] = entrystyle
ttk.Entry.__init__(self, master, textvariable=self.entry_var, **entry_config, state = "readonly")
self._is_menuoptions_visible = False
self.picker_frame = Picker(self.winfo_toplevel(), values=values,entry_wid = self.entry_var,activebackground=activebackground, activeforeground=activeforeground, selectbackground=selectbackground, selectforeground=selectforeground, command=self._on_selected_check)
self.bind_all("<1>", self._on_click, "+")
self.bind("<Escape>", lambda event: self.hide_picker())
@property
def current_value(self):
try:
value = self.entry_var.get()
return value
except ValueError:
return None
@current_value.setter
def current_value(self, INDEX):
self.entry_var.set(values.index(INDEX))
def _on_selected_check(self, SELECTED):
value = []
if self.entry_var.get() != "" and self.entry_var.get() != None:
temp_value = self.entry_var.get()
value = temp_value.split(",")
if str(SELECTED) in value:
value.remove(str(SELECTED))
else:
value.append(str(SELECTED))
value.sort()
temp_value = ""
for index,item in enumerate(value):
if item!= "":
if index != 0:
temp_value += ","
temp_value += str(item)
self.entry_var.set(temp_value)
def _on_click(self, event):
str_widget = str(event.widget)
if str_widget == str(self):
if not self._is_menuoptions_visible:
self.show_picker()
else:
if not str_widget.startswith(str(self.picker_frame)) and self._is_menuoptions_visible:
self.hide_picker()
def show_picker(self):
if not self._is_menuoptions_visible:
self.picker_frame.place(in_=self, relx=0, rely=1, relwidth=1 )
self.picker_frame.lift()
self._is_menuoptions_visible = True
def hide_picker(self):
if self._is_menuoptions_visible:
self.picker_frame.place_forget()
self._is_menuoptions_visible = False
if __name__ == "__main__":
import sys
try:
from Tkinter import Tk, Frame, Label
except ImportError:
from tkinter import Tk, Frame, Label
root = Tk()
root.geometry("500x600")
main =Frame(root, pady =15, padx=15)
main.pack(expand=True, fill="both")
Label(main, justify="left", text=__doc__).pack(anchor="w", pady=(0,15))
COMBOPICKER1 = Combopicker(main, values = [1, 2, 3, 4])
COMBOPICKER1.pack(anchor="w")
if 'win' not in sys.platform:
style = ttk.Style()
style.theme_use('clam')
root.mainloop()
I used the example of #SilverHalo which I finally improved I think I added the possibility of searching in the combobox and also added a scrollbar because in his example the frame became much too large with many values. I also added the possibility to enlarge the combobox with a right click in the search bar or a right click on the checkbutton you just have to install scrolledframe from pip and see if the result can be useful.
import tkinter as tk
from tkinter import *
from tkinter import ttk
from tkscrolledframe import ScrolledFrame #https://github.com/bmjcode/tkScrolledFrame
# for installation pip install tkScrolledFrame
class Picker(ttk.Frame):
def __init__(self, master=None,
values=[],
entry_wid=None,
command=None,
entlarge_fonction=None,
borderwidth=1,
relief="solid",
font=('Arial, 11')):
self._selected_item = None
self._values = values
self._entry_wid = entry_wid
self._command = command
if type(entlarge_fonction).__name__!='NoneType':
self.entlarge_fonction=entlarge_fonction
else:
self.entlarge_fonction= lambda : 1
style = ttk.Style()
style.configure('Custom.TFrame', background='#ffffff')
ttk.Frame.__init__(self, master, borderwidth=borderwidth, relief=relief, style='Custom.TFrame')
self.bind("<Button-3>", lambda event : self.entlarge_fonction())
self.bind("<FocusIn>", lambda event:self.event_generate('<<PickerFocusIn>>'))
self.bind("<FocusOut>", lambda event:self.event_generate('<<PickerFocusOut>>'))
# search field
self.searchfield=ttk.Entry(self , font=font)
self.searchfield.pack(fill=X)
self.searchfield.insert(END, 'Search')
self.searchfield.bind("<Button-1>", lambda event : self.clear_search_field())
self.searchfield.bind("<Button-3>", lambda event : self.entlarge_fonction())
self.searchfield.bind('<KeyRelease>',lambda event : self.update_checkbutton_list())
self.scrolledframe=ScrolledFrame(self)
self.scrolledframe.pack(fill=BOTH)
self.scrolledframe.bind_arrow_keys(self)
self.scrolledframe.bind_scroll_wheel(self)
self.frame = self.scrolledframe.display_widget(Frame)
style = ttk.Style()
style.configure('Custom.TFrame', background='#ffffff')
#checkbutton frame
self.check_buttonFrame=ttk.Frame(self.frame, style='Custom.TFrame')
self.check_buttonFrame.pack(fill=BOTH)
self.dict_checkbutton = {}
self.dict_intvar_item = {}
self.selected_value=[]
self.create_check_button()
@property
def selection(self):
return self.selected_value
def set_value(self, new_values):
if type(new_values).__name__=='list':
self.selected_value=[]
self._values=new_values
else:
raise('ValueError')
def create_check_button(self, values=None):
self.dict_checkbutton = {}
self.dict_intvar_item = {}
if type(values).__name__=='NoneType':
values=self._values
# first clear my frame
for widget in self.check_buttonFrame.winfo_children():
widget.destroy()
for index,item in enumerate(values):
self.dict_intvar_item[item] = tk.IntVar()
self.dict_checkbutton[item] = ttk.Checkbutton(self.check_buttonFrame,
text = item,
variable=self.dict_intvar_item[item],
command=lambda selected=item : self.check_box_select(selected))
self.dict_checkbutton[item].grid(row=index, column=0, sticky=tk.NSEW)
self.dict_checkbutton[item].bind("<Button-3>", lambda e : self.entlarge_fonction())
if item not in self.selected_value:
self.dict_intvar_item[item].set(0)
else:
self.dict_intvar_item[item].set(1)
def check_box_select(self, selected_value=None):
for keys in self.dict_intvar_item.keys():
if self.dict_intvar_item[keys].get()!=0:
if keys not in self.selected_value:
self.selected_value.append(keys)
else:
if keys in self.selected_value:
self.selected_value.remove(keys)
try:
self._command(selected_value)
except Exception as e:
print(e)
def clear_search_field(self):
self.searchfield.delete(0, END)
self.update_checkbutton_list()
def update_checkbutton_list(self):
search_value=self.searchfield.get()
update_values=[]
if search_value!='':
for item in self._values:
if search_value.lower() in str(item).lower():
update_values.append(item)
else:
update_values=self._values.copy()
self.create_check_button(update_values)
class Combopicker(ttk.Combobox, Picker):
def __init__(self, master,
values= [] ,
entryvar=None,
entrywidth=25,
entrystyle=None,
font=('Arial, 16'),
command=None):
if entryvar is not None:
self.entry_var = entryvar
else:
self.entry_var = tk.StringVar()
self.entrywidth=entrywidth
if type(command).__name__=='NoneType':
self.command= lambda : 1
else:
self.command = command
entry_config = {}
if entrywidth is not None:
entry_config["width"] = entrywidth
if entrystyle is not None:
entry_config["style"] = entrystyle
ttk.Combobox.__init__(self, master,
textvariable=self.entry_var,
**entry_config,
state = "readonly", font=font)
self.unbind_class("TCombobox", "<Button-1>")
self.unbind_class("TCombobox", "<Double-1>")
self.unbind_class("TCombobox", "<Triple-1>")
self.unbind_class("TCombobox","<<ComboboxSelected>>")
self._is_menuoptions_visible = False
self.picker_frame = Picker(self.winfo_toplevel(),
values=values,
entry_wid = self.entry_var,
entlarge_fonction=self.entlarge,
command=self._on_selected_check)
self.bind_all("<1>", self._on_click, "+")
self.bind("<Escape>", lambda event: self.hide_picker())
# get widget size
self.expand_var=False
def set_value(self, new_value):
self.picker_frame.set_value(new_value)
def get(self):
return self.picker_frame.selection
def entlarge(self):
if self.expand_var==False:
self.config(width=80)
self.picker_frame.place(in_=self, relx=0, rely=1, relwidth=1, height=300 )
self.expand_var=True
else:
self.config(width=self.entrywidth)
self.picker_frame.place(in_=self, relx=0, rely=1, relwidth=1, height=150 )
self.expand_var=False
def _on_selected_check(self, SELECTED):
''' add selected values on entry'''
values=self.picker_frame.selection.copy()
if len(values)!=0:
values=str(values)[1:len(str(values))-1] # transform my list to string and remove []
self.entry_var.set(values)
self.command()
def _on_click(self, event):
str_widget = str(event.widget)
if str_widget == str(self):
if not self._is_menuoptions_visible:
self.show_picker()
else:
try:
self.hide_picker()
except:
pass
else:
if not str_widget.startswith(str(self.picker_frame)) and self._is_menuoptions_visible:
self.hide_picker()
def show_picker(self):
if not self._is_menuoptions_visible:
self.picker_frame.place(in_=self, relx=0, rely=1, relwidth=1, height=150 )
self.picker_frame.lift()
self._is_menuoptions_visible = True
def hide_picker(self):
if self._is_menuoptions_visible:
self.picker_frame.place_forget()
self._is_menuoptions_visible = False
if __name__ == "__main__":
root = Tk()
root.geometry("500x600")
main =Frame(root, pady =15, padx=15,)
main.pack(expand=True, fill="both")
data = {'Name': ['Alice', 'Bob', 'Charlie', 'David', 'Emma', 'Frank', 'Grace', 'Henry', 'Isabel', 'Jack'],
'Gender': ['F', 'M', 'M', 'M', 'F', 'M', 'F', 'M', 'F', 'M'],
'Age': [24, 32, 18, 47, 29, 53, 41, 19, 37, 28]}
def print_selection():
print('click')
for key in combobox.keys():
print(key, combobox[key].get())
combobox={}
counter=0
for key in data.keys():
Label(main, text=key).grid(row=counter, column= 0)
combobox[key]=Combopicker(main, values=list(set(data[key])) , command=lambda :print_selection())
combobox[key].grid(row=counter, column= 1)
counter+=1
root.mainloop()
After searching for a long time I found an answer to the same question:
https://stackoverflow.com/a/64961761/22078610
He created a ChecklistCombobox, which looks just like the normal ttk.Combobox but with Checkboxes. You can find his solution on GitHub: https://github.com/hatfullr/ChecklistCombobox
Example use:
from tkinter import *
from checklistcombobox import ChecklistCombobox
master = Tk()
mydropdown = ChecklistCombobox(master, values=[f"Value {a}" for a in range(1,20)])
def output():
print(mydropdown.get())
btn = Button(master, text="Print Output", command=output)
btn.pack()
mydropdown.pack()
master.mainloop()