создать собственный searchBar tkinter - PullRequest
0 голосов
/ 11 апреля 2020

Я видел много дискуссий о панели поиска, но я никогда не находил что-то действительно удобное. Я сделал пользовательский SearchBar (может быть, мы добавим это в tkinter, если он работает!).

Если вы попробуете этот код (Combobox), он позволит списку перекрывать другой виджет (поведение по умолчанию, я не мастер!), но вы не можете отобразить список И одновременно сохранить фокус на тексте, чтобы помочь пользователю:

import tkinter as tk
from tkinter import ttk

app=tk.Tk()
app_FRAME=tk.Frame(app,relief=tk.GROOVE)    
app_FRAME.pack()


def clicked_on_arrow():
    print("see the list")

def var_changed():
    print("var changed")

_list = ["a","b","c"]
_var = tk.StringVar()
_var.trace("w", lambda name, index, mode, x=_var: var_changed())

_COMBOBOX = ttk.Combobox(app_FRAME, postcommand=clicked_on_arrow(), values=_list, textvariable=_var)
_COMBOBOX.pack()
random_BUTTON = tk.Button(app_FRAME, text="the list can overlap over me !")
random_BUTTON.pack(fill=tk.BOTH, expand=True)

app.mainloop()

Этот код (более сложный и настраиваемый) не может перекрываться ( потому что я не совсем уверен, как реализовать его для создания собственного виджета, не зная фрейма root), но он действительно помогает пользователю, визуализируя список в режиме реального времени:

# -*- coding: utf-8 -*-
"""
Created on Thu Apr  9 17:25:59 2020

@author: aymeric LAUGEL
"""

import tkinter as tk

class Search_Bar:

    def create_new_list(self,string_given, string_list):
        new_list = []
        for string in string_list:
            if(len(string_given) <=len(string)):
                if(string.find(string_given) != -1):
                    new_list.append(string)
        return new_list      

    def update_display_list(self):
        if(self.listboxExist):
            self.m_LISTBOX.delete(0,tk.END)
            for string in self.m_list_to_display: self.m_LISTBOX.insert('end', string)
        else:
            print("listbox should exist...")


    def maybe_update_display_LISTBOX(self):
        possible_new_list_to_display = self.create_new_list(self.m_text_entry_object.get(), self.m_list)
        print()
        print("#############")
        print("LISTBOX exist:",self.listboxExist)
        print("var: |"+self.m_text_entry_object.get()+"|")
        print("old list:",self.m_list_to_display)
        print("new list:",possible_new_list_to_display)


        if(len(possible_new_list_to_display) == 0):
            print("nothing to display")
            if(self.listboxExist):
                self.delete_LISTBOX()
            else:
                print("La listbox n'existe déjà plus")
            return

        if possible_new_list_to_display == self.m_list_to_display:
            print("list are the same, no need to update")
            if(self.listboxExist):
                pass
            else:
                self.create_LISTBOX()
                print("Listbox should already exist cause length is more than 0...")
            return        

        if(len(possible_new_list_to_display) == len(self.m_list_to_display)):
            print("just need to update the values, not the length")
            self.m_list_to_display = possible_new_list_to_display
            if(self.listboxExist):
                self.update_display_list()
            else:
                print("Listbox should have already been created.")
                self.create_LISTBOX()
            return
        if(len(possible_new_list_to_display) >= self.m_max_element_to_display and 
           len(self.m_list_to_display) >= self.m_max_element_to_display):
            print("not the same len but we are gonna display the max number of row anyway")
            self.m_list_to_display = possible_new_list_to_display
            if(self.listboxExist):
                self.update_display_list()
            else:
                if not(self.itemInListboxSelected):
                    print("Listbox should have already been created....")
                    self.create_LISTBOX()

            return
        print("we have to change the length of the LISTBOX display (and the values)")
        self.m_list_to_display = possible_new_list_to_display
        if(self.listboxExist):
            self.delete_LISTBOX()
        self.create_LISTBOX()


    def func_called_when_text_change(self,event):
        print("the text changed")
        self.maybe_update_display_LISTBOX()

    def focus_in_ENTRY(self, event):
        print("focus in ENTRY")
        self.focusIn_ENTRY = True
        self.maybe_create_LISTBOX()

    def focus_out_ENTRY(self, event):
        print("focus out ENTRY")
        self.focusIn_ENTRY = False
        self.maybe_delete_LISTBOX()

    def cursor_in_ENTRY(self, event):
        print("cursor in ENTRY")
        self.cursorIn_ENTRY = True

    def cursor_out_ENTRY(self, event):
        print("cursor out ENTRY")
        self.cursorIn_ENTRY = False


    def focus_in_LISTBOX(self, event):
        print("focus in LISTBOX")
        self.focusIn_LISTBOX = True

    def focus_out_LISTBOX(self, event):
        print("focus out LISTBOX")
        self.focusIn_LISTBOX = False
        self.maybe_delete_LISTBOX()

    def cursor_in_LISTBOX(self, event):
        print("cursor in LISTBOX")
        self.cursorIn_LISTBOX = True

    def cursor_out_LISTBOX(self, event):
        print("cursor out LISTBOX")
        self.cursorIn_LISTBOX = False


    def maybe_create_LISTBOX(self):
        if(self.focusIn_ENTRY == True and self.listboxExist == False):
            self.create_LISTBOX()

    def maybe_delete_LISTBOX(self):
        if(#self.focusIn_ENTRY == False and 
           self.listboxExist == True and 
           self.cursorIn_LISTBOX == False and 
           self.cursorIn_ENTRY == False):
            self.delete_LISTBOX()

    def item_selected(self,event):
        print("an item have been selected from the listbox")
        self.itemInListboxSelected = True
        selected_item_tuple_id = self.m_LISTBOX.curselection()
        if(selected_item_tuple_id != ()):
            string = str(self.m_LISTBOX.get(self.m_LISTBOX.curselection()))
            self.m_text_entry_object.set(string)
        self.m_list_to_display = []
        self.delete_LISTBOX()
        self.itemInListboxSelected = False

    def create_LISTBOX(self):
        print("we create the listbox")
        if(self.listboxExist):
            print("Listbox should not be here")
            return
        if(self.m_LISTBOX != None):
            print("THE LIST SHOULD HAVE BEEN ABSENT !!!")
            return

        if(len(self.m_list_to_display) == 0):
            print("We shouldn't try to display an empty listbox...")
            return
        if(self.m_max_element_to_display > len(self.m_list_to_display)):
            nbr_of_row_to_display = len(self.m_list_to_display)
        else:
            nbr_of_row_to_display = self.m_max_element_to_display

        self.m_LISTBOX = tk.Listbox(self.m_FRAME, height=nbr_of_row_to_display)
        self.m_LISTBOX.pack()
        self.m_LISTBOX.bind("<FocusIn>", self.focus_in_LISTBOX)
        self.m_LISTBOX.bind("<FocusOut>", self.focus_out_LISTBOX)
        self.m_LISTBOX.bind("<Enter>", self.cursor_in_LISTBOX)
        self.m_LISTBOX.bind("<Leave>", self.cursor_out_LISTBOX)
        self.m_LISTBOX.bind('<<ListboxSelect>>', self.item_selected)
        self.listboxExist = True
        self.update_display_list()



    def delete_LISTBOX(self):
        print("we delete the listbox")
        if not(self.listboxExist):
            print("Listbox should be here")
            return
        if(self.m_LISTBOX == None):
            print("THE LIST SHOULD HAVE BEEN PRESENT !!!")
            return
        self.m_LISTBOX.destroy()
        del self.m_LISTBOX
        self.m_LISTBOX = None
        self.list_to_display = []
        self.listboxExist = False



    def __init__(self, actual_FRAME, _list, max_element_to_display=4):
        self.itemInListboxSelected = False
        self.focusIn_ENTRY = False
        self.cursorIn_ENTRY = False
        self.cursorIn_LISTBOX = False
        self.listboxExist = False
        self.m_list = _list
        if(max_element_to_display > len(_list)):
            print("vous ne pouvez pas display plus d'éléments qu'il y en a dans la list...")
            self.m_max_len_to_display = len(_list)
        self.m_max_element_to_display = max_element_to_display
        self.m_list_to_display = self.m_list
        self.m_FRAME = actual_FRAME
        self.m_text_entry_object = tk.StringVar()
        self.m_text_entry_object.trace("w", lambda name, index, mode, x=self.m_text_entry_object: self.func_called_when_text_change(x))
        self.m_ENTRY = tk.Entry(self.m_FRAME, textvariable=self.m_text_entry_object)
        self.m_ENTRY.pack(side = tk.TOP)

        self.m_ENTRY.bind("<FocusIn>", self.focus_in_ENTRY)
        self.m_ENTRY.bind("<FocusOut>", self.focus_out_ENTRY)
        self.m_ENTRY.bind("<Enter>", self.cursor_in_ENTRY)
        self.m_ENTRY.bind("<Leave>", self.cursor_out_ENTRY)

        self.m_LISTBOX = None
        #self.create_LISTBOX()
        #self.m_FRAME.pack_propagate(0)
        #self.delete_LISTBOX()






if __name__ == "__main__":

    app=tk.Tk()
    app.geometry('500x300')
    _list = ["fruit de la passion", "pomme","poire","pêche","abricot","pamplemousse","orange","raisin","cassis"]
    search_bar_FRAME=tk.Frame(app,relief=tk.GROOVE, borderwidth=5)   
    search_bar_FRAME.pack(side=tk.TOP,expand=True)
    SEARCH_BAR = Search_Bar(search_bar_FRAME, _list, max_element_to_display=5)


    _list_2 = ["amour","gloire","pouvoir de l'instant présent","beauté","guerre","action"]
    search_bar_2_FRAME=tk.Frame(app,relief=tk.GROOVE, borderwidth=5)   
    search_bar_2_FRAME.pack(side=tk.TOP)
    SEARCH_BAR_2 = Search_Bar(search_bar_2_FRAME, _list_2, max_element_to_display=3)

    other_FRAME=tk.Frame(app,relief=tk.GROOVE, borderwidth=5)    
    other_FRAME.pack()
    _random_Button = tk.Button(other_FRAME, text="This search bar can't overlap over me...")
    _random_Button.pack(side=tk.LEFT)
    app.mainloop()

Мой вопрос :

Как перекрывать другие виджеты списком, например списком со списком, но все же помогать пользователю, когда он печатает что-то, показывая список?

Надеюсь, у меня все ясно, вы можете запустить 2 отдельные коды, чтобы увидеть плюсы и минусы.

Любые советы, примеры или помощь приветствуется! :)

Может быть, как настроить Combobox, потому что он почти у цели!

В других темах говорится о чем-то, что может помочь:

...