Python tkinter 自定义筛选下拉框

from tkinter import Entry, Frame, Listbox, Scrollbar, StringVar, Tk, Toplevel
from tkinter.constants import END, EW, HORIZONTAL, NS


def singleton(cls, *args, **kwargs):
    instances = {}

    def _singleton():
        if cls not in instances:
            instances[cls] = cls(*args, **kwargs)
        return instances[cls]

    return _singleton


@singleton
class ListBox(object):
    """ListBox Class"""
    def __init__(self):
        self.top_window = None

    def init(self, width):
        self.width = width

    def show(self, p_master, values, master):
        self.p_master = p_master
        self.master = master
        self.values = values
        if self.top_window:
            return

        x = self.master.winfo_rootx()
        y = self.master.winfo_rooty() + self.master.winfo_height()
        self.top_window = Toplevel(self.master)
        self.top_window.wm_overrideredirect(1)
        self.top_window.wm_geometry("+%d+%d" % (x, y))
        self.top_window.bind('<Configure>', self.on_change_geometry)
        self.top_window.bind_all("<Button-1>", self.on_gloabl_click)

        self.sb_y = Scrollbar(self.top_window)
        self.sb_x = Scrollbar(self.top_window, orient=HORIZONTAL)
        self.sb_x.grid(row=1, column=0, sticky=EW)
        self.list_box = Listbox(self.top_window,
                                xscrollcommand=self.sb_x.set,
                                yscrollcommand=self.sb_y.set,
                                width=self.width)
        for data in self.values:
            self.list_box.insert(END, data)
        if self.p_master.input_var.get() != '' and self.p_master.input_var.get() in self.values:
            index = self.values.index(self.p_master.input_var.get())
        else:
            index = 0
        self.list_box.select_set(index)
        self.list_box.bind("<ButtonRelease-1>", self.on_click_item)
        self.sb_x.config(command=self.list_box.xview)
        self.sb_y.config(command=self.list_box.yview)
        self.sb_y.grid(row=0, column=1, sticky=NS)
        self.sb_x.grid(row=1, column=0, sticky=EW)
        self.list_box.grid(row=0, column=0)

    def on_gloabl_click(self, event):
        if isinstance(event.widget, Listbox) or isinstance(
                event.widget, Scrollbar):
            pass
        else:
            self.hide()

    def hide(self):
        if self.top_window:
            if self.p_master.input_var.get() == '' or self.p_master.input_var.get() not in self.values:
                self.p_master.input_var.set(self.list_box.get(0))
            self.p_master.entry.icursor(len(self.p_master.input_var.get()))
            self.top_window.destroy()
            self.top_window = None

    def on_change_geometry(self, event):
        x = self.master.winfo_rootx()
        y = self.master.winfo_rooty() + self.master.winfo_height()
        self.top_window.wm_geometry("+%d+%d" % (x, y))

    def on_click_item(self, event):
        self.p_master.input_var.set(
            self.list_box.get(self.list_box.curselection()))
        self.hide()

    def update_list(self):
        datas = []
        for value in self.values:
            if self.p_master.input_var.get() in value:
                datas.append(value)
        self.list_box.delete(0, END)
        if len(datas) == 0 and self.p_master.input_var.get() == '':
            datas = self.values
        elif len(datas) == 0 and self.p_master.input_var.get() != '' and self.p_master.input_var.get() not in self.values:
            datas = self.values
        else:
            pass

        for data in datas:
            self.list_box.insert(END, data)

    def is_show(self):
        if self.top_window and self.top_window.state() == 'normal':
            return True
        else:
            return False


class ComboBox(object):
    """ComboBox Class"""
    def __init__(self, values, width, master=None):
        self.master = master
        self.values = values
        self.frame = Frame(self.master)
        self.frame.grid(row=0, column=0)

        self.input_var = StringVar()
        self.input_var.set(self.values[0])
        self.entry = Entry(self.frame, textvariable=self.input_var, width=width)
        self.entry.grid()
        self.entry.bind('<KeyRelease>', self.on_update_list)
        self.entry.bind('<ButtonRelease-1>', self.on_entry_click)
        self.entry.bind('<Return>', self.on_enter)
        ListBox().init(width)

    def on_enter(self, event):
        if self.input_var.get() in self.values:
            pass
        else:
            self.input_var.set(self.values[0])
            self.entry.icursor(len(self.input_var.get()))

    def on_entry_click(self, event):
        if ListBox().is_show():
            ListBox().hide()
        else:
            ListBox().show(self, self.values, self.entry)

    def on_update_list(self, event):
        if ListBox().is_show():
            ListBox().update_list()


root = Tk()
root.geometry("800x480")
root.title("ComboBox")
root.bind_all('<Button-1>')
data = []
for index in range(1000):
    data.append(str(index))

frame = Frame(root, width=800, height=480)
frame.grid(row=0, column=0)
frame.grid_propagate(0)

frame1 = Frame(frame, width=800, height=35)
frame1.grid(row=0, column=0)
ComboBox(data, 35, frame1)
frame2 = Frame(frame, width=800, height=35)
frame2.grid(row=1, column=0)
ComboBox(data, 35, frame2)


root.mainloop()

Guess you like

Origin blog.csdn.net/swif_N_F/article/details/120227490