Python tkinter自定义多选下拉列表框(带滚动条、全选)

Python tkinter自定义多选下拉列表框

    • 1、demo.py文件
    • 2、ComBoPicker.py文件
    • 3、ComBoPicker1.py文件
    • 4、效果

上一篇文章,很多小伙伴不知道怎么加全选和滚动条,所以做出的修改,增加全选和滚动条,如果有不足之处,欢迎指出,感谢!

问题 方法
怎么动态赋值 COMBOPICKER.values = [1,2,3,4]
获取选中值 1.COMBOPICKER.current_value
2.COMBOPICKER.get()
3.COMBOPICKER.entry_var.get()

1、demo.py文件


from tkinter import *
from ComBoPicker import Combopicker# 导入自定义下拉多选框
from ComBoPicker1 import Combopicker as Combopicker1# 导入自定义下拉多选框
if __name__ == "__main__":
    

    root = Tk()
    root.geometry("300x300")

    F =Frame(root)
    F.pack(expand=False, fill="both",padx=10,pady=10)
    Label(F,text='全选、可滚动:').pack(side='left')
    COMBOPICKER = Combopicker(F, values = ['全选','项目1','项目2','项目3','项目4','项目5','项目11','项目22','项目33','项目44','项目55'])
    COMBOPICKER.pack(anchor="w")

    
    F2 =Frame(root)
    F2.pack(expand=False, fill="both",padx=10,pady=10)
    Label(F2,text='普通:').pack(side='left')
    COMBOPICKER1 = Combopicker1(F2, values = [f'项目{i}' for i in range(5)])
    COMBOPICKER1.pack(anchor="w")

    root.mainloop()

2、ComBoPicker.py文件

'''
	自定义多选下拉列表
'''
import tkinter.font as tkFont
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("", lambda event:self.event_generate('<>'))
        self.bind("", lambda event:self.event_generate('<>'))
        F = LabelFrame(self)
        F.pack(fill='x')
        self.canvas=Canvas(F,scrollregion=(0,0,500,(len(self._values)*21)))
        vbar=Scrollbar(F,orient=VERTICAL)
        vbar.pack(side=RIGHT,fill=Y)
        frame = Frame(self.canvas)
        vbar.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)
        self.canvas.config(width=300,height=150)
        self.canvas.config(yscrollcommand=vbar.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("",self.processWheel)
        frame.bind("",self.processWheel)
        for i in self.dict_checkbutton:
            self.dict_checkbutton[i].bind("",self.processWheel)
        self.bind("",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.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"):

        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("", 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 = []
        all_name = '全选'
        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 all_name == str(SELECTED):
                value.clear()  # 清空选项
            else:
                if all_name in value:
                    value.remove(all_name)
                value.remove(str(SELECTED))
                value.sort()
        else: 
            if all_name == 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 all_name == 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("", 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

3、ComBoPicker1.py文件

'''
	自定义多选下拉列表
'''
import tkinter.font as tkFont
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
        ttk.Frame.__init__(self, master, borderwidth=borderwidth, relief=relief)

        self.bind("", lambda event:self.event_generate('<>'))
        self.bind("", lambda event:self.event_generate('<>'))

        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(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=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"):
        
        self.values=values

        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 = "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("", 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:
            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



4、效果

Python tkinter自定义多选下拉列表框(带滚动条、全选)_第1张图片

你可能感兴趣的:(Python,笔记,Python,小工具,游戏,tkinter,开发语言)