How do I enable multiple selection of values from a combobox?

13,218

Solution 1

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()

Solution 2

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()
Share:
13,218
jknicely
Author by

jknicely

Updated on June 04, 2022

Comments

  • jknicely
    jknicely almost 2 years

    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?

  • kakyo
    kakyo over 5 years
    Trying this example... However, on macOS 10.13.6 and Python 3.7.1 Homebrew, clicking on the menu items gives me messy result, the checkmarks appear and disappear not according to the assumed logic as you click on each item.