tkinter自定义下拉多选框问题
作者:奋斗中的打工人
这篇文章主要介绍了tkinter自定义下拉多选框问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
使用tkinter实现下拉多选框
效果如图:
1、选择一些选项
2、全选选项
代码如下:
import tkinter from ComBoPicker import Combopicker list = ["全选", "选项1", "选项2", "选项3", "选项4", "选项5", "选项6"] root = tkinter.Tk() co = Combopicker(root, values=list) co.pack() root.geometry("800x600") root.mainloop()
ComBoPicker.py代码:
''' 自定义多选下拉列表 ''' import tkinter.ttk as ttk from tkinter import * 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 self.index = 0 ttk.Frame.__init__(self, master, borderwidth=borderwidth, height=10, relief=relief) self.bind("<FocusIn>", lambda event: self.event_generate('<<PickerFocusIn>>')) self.bind("<FocusOut>", lambda event: self.event_generate('<<PickerFocusOut>>')) F = LabelFrame(self) F.pack(fill='x') self.canvas = Canvas(F, scrollregion=(0, 0, 500, (len(self._values) * 23))) vbar = Scrollbar(F, orient=VERTICAL) vbar.pack(side=RIGHT, fill=Y) frame = Frame(self.canvas) #创建框架 vbar.config(command=self.canvas.yview) sbar2 = Scrollbar(F, orient=HORIZONTAL) sbar2.pack(side=BOTTOM, fill=X) sbar2.config(command=self.canvas.yview) # self.canvas.pack(side='left',fill='x',expand=True) self.canvas.create_window((0, 0,), window=frame, anchor='nw', tags='frame') self.canvas.config(highlightthickness=0) # 去掉选中边框 vbar.config(command=self.canvas.yview) sbar2.config(command=self.canvas.xview) self.canvas.config(width=300, height=150) self.canvas.config(yscrollcommand=vbar.set,xscrollcommand=sbar2.set) # self.canvas.config(scrollregion=self.canvas.bbox('all')) # 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] = IntVar() self.dict_checkbutton[item] = ttk.Checkbutton(frame, 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=NSEW, padx=5) self.dict_intvar_item[item].set(0) if item in self._entry_wid.get().split(','): self.dict_intvar_item[item].set(1) self.canvas.pack(side=LEFT, expand=True, fill=BOTH) self.canvas.bind("<MouseWheel>", self.processWheel) frame.bind("<MouseWheel>", self.processWheel) for i in self.dict_checkbutton: self.dict_checkbutton[i].bind("<MouseWheel>", self.processWheel) self.bind("<MouseWheel>", self.processWheel) def processWheel(self, event): a = int(-(event.delta)) if a > 0: self.canvas.yview_scroll(1, UNITS) else: self.canvas.yview_scroll(-1, UNITS) class Combopicker(ttk.Combobox, Picker): def __init__(self, master, values=[], entryvar=None, entrywidth=None, entrystyle=None, onselect=None, activebackground='#ef476f', activeforeground='red', selectbackground='#ffd166', selectforeground='green', borderwidth=1, relief="solid"): self.values = values self.master = master self.activeforeground = activeforeground self.activebackground = activebackground self.selectbackground = selectbackground self.selectforeground = selectforeground if entryvar is not None: self.entry_var = entryvar else: self.entry_var = 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="") 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(self.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: if '全选' == str(SELECTED): value.clear() # 清空选项 else: value.remove(str(SELECTED)) value.sort() else: if '全选' == str(SELECTED): value = self.values 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) # 可以通过复选框的variable来让勾选中或取消,但下面也行,问题不大 if '全选' == str(SELECTED): self.hide_picker() self.show_picker() 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 = Picker(self.winfo_toplevel(), values=self.values, entry_wid=self.entry_var, activebackground=self.activebackground, activeforeground=self.activeforeground, selectbackground=self.selectbackground, selectforeground=self.selectforeground, command=self._on_selected_check) self.bind_all("<1>", self._on_click, "+") self.bind("<Escape>", lambda event: self.hide_picker()) self.picker_frame.lift() self.picker_frame.place(in_=self, relx=0, rely=1, relwidth=1) self._is_menuoptions_visible = True def hide_picker(self): if self._is_menuoptions_visible: self.picker_frame.place_forget() # 不知道为什么这个方式在mac下不起作用,所以就直接销毁这个控件 # self.picker_frame.destroy() self._is_menuoptions_visible = False
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。