I wanted to creat a multi select combo box using python tkinter.ttk and needed to understand ttk widgets
I used the code below to get something that works, but I really want to understand how the ttk.Combobox can be controlled better. This question(How to know all style options of a ttk widget?) shows how to print out all the styles an layout, but I would like to see the events and component structure.
import tkinter as tk
from tkinter import ttk
class MultiBox(ttk.Combobox):
def __init__(self,window,**kwds):
self._separator = kwds.pop('separator',None)
self._joinChar = self._separator if self._separator is not None else ' '
kwds["textvariable"] = self._v = kwds.pop('textvariable',tk.StringVar())
self._v.trace('w',self.vwrite)
self.onchange = kwds.pop('onchange',None)
super().__init__(window,**kwds)
self["values"] = self._values = list(kwds.pop('values',()))
self._cv = ''
def vwrite(self,*args):
v = set((_ for _ in self._v.get().split(self._separator) if _ in self._values))
cv = set((_ for _ in self._cv.split(self._separator) if _ in self._values))
lv = v if v&cv else v.union(cv)
self._cv = self._joinChar.join(self._vsort(lv))
self._v.set(self._cv)
#print(f'{v=} {cv=} {lv=} --> {self._v}')
self["values"] = self._vsort((_ for _ in self._values if _ not in lv))
if self.onchange: self.onchange()
def _vsort(self,L):
return [_[1] for _ in sorted((self._values.index(_),_) for _ in L)]
@property
def value(self):
return self._v.get()
if __name__=='__main__':
# Creating tkinter window
window = tk.Tk()
window.geometry('350x270')
# Label
ttk.Label(window, text = "Select the Month :",
font = ("Times New Roman", 10)).grid(column = 0,
row = 0, padx = 10, pady = 25)
mb0 = MultiBox(window, width = 20,
values=('January','February', 'March', 'April', 'May',
'June', 'July', 'August', 'September', 'October',
'November', 'December'),
)
mb0.grid(column = 1, row = 0)
# Shows item 1 as a default value
mb0.current(1)
mb1 = MultiBox(window,width=20,values='A a,B b,C c,D d,E e,F f'.split(','),separator=',')
mb1.grid(column = 1, row = 5, pady=10)
mb1.current(0)
b0 = ttk.Button(window, text="Print",
command=lambda :print(f'mb0.value={mb0.value} mb1.value={mb1.value}')).grid(column=1, row=15, pady=5)
b1 = ttk.Button(window, text="Quit", command=window.destroy).grid(column=1, row=20, pady=5)
#from ttkstyopt import ttkStyOpt
#window.after(10000, lambda : ttkStyOpt(multibox))
window.mainloop()