首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >Python Tkinter自动完成组合框与LIKE搜索?

Python Tkinter自动完成组合框与LIKE搜索?
EN

Stack Overflow用户
提问于 2017-12-16 05:01:58
回答 1查看 13.4K关注 0票数 3

我正在尝试使用预定义的值填充Tkinter组合框,以便从中进行选择。它正在普及,我能够输入并获得建议。然而,为了做到这一点,我必须明确地知道前几个字符。如果我知道在字符串的中间或结尾有一些文本,它是没有用的,因为组合框只执行'LIKE%‘搜索,而不是'%LIKE%’搜索

预期输出(键入单词“string”将获取包含该字符串的所有名称。注意:这不是Tkinter截图():

这是我到目前为止对代码的改编,如果有人能建议如何修改AutocompleteCombobox类来做LIKE搜索,那就太好了。

下面的代码片段,作为示例,具有值"Cranberry“和"Strawberry”,我的要求是输入"berry“并获取这两个水果的建议。

import Tkinter
import ttk
import sqlite3


class AutocompleteCombobox(ttk.Combobox):

        def set_completion_list(self, completion_list):
                """Use our completion list as our drop down selection menu, arrows move through menu."""
                self._completion_list = sorted(completion_list, key=str.lower) # Work with a sorted list
                self._hits = []
                self._hit_index = 0
                self.position = 0
                self.bind('<KeyRelease>', self.handle_keyrelease)
                self['values'] = self._completion_list  # Setup our popup menu

        def autocomplete(self, delta=0):
                """autocomplete the Combobox, delta may be 0/1/-1 to cycle through possible hits"""
                if delta: # need to delete selection otherwise we would fix the current position
                        self.delete(self.position, Tkinter.END)
                else: # set position to end so selection starts where textentry ended
                        self.position = len(self.get())
                # collect hits
                _hits = []
                for element in self._completion_list:
                        if element.lower().startswith(self.get().lower()): # Match case insensitively
                                _hits.append(element)
                # if we have a new hit list, keep this in mind
                if _hits != self._hits:
                        self._hit_index = 0
                        self._hits=_hits
                # only allow cycling if we are in a known hit list
                if _hits == self._hits and self._hits:
                        self._hit_index = (self._hit_index + delta) % len(self._hits)
                # now finally perform the auto completion
                if self._hits:
                        self.delete(0,Tkinter.END)
                        self.insert(0,self._hits[self._hit_index])
                        self.select_range(self.position,Tkinter.END)

        def handle_keyrelease(self, event):
                """event handler for the keyrelease event on this widget"""
                if event.keysym == "BackSpace":
                        self.delete(self.index(Tkinter.INSERT), Tkinter.END)
                        self.position = self.index(Tkinter.END)
                if event.keysym == "Left":
                        if self.position < self.index(Tkinter.END): # delete the selection
                                self.delete(self.position, Tkinter.END)
                        else:
                                self.position = self.position-1 # delete one character
                                self.delete(self.position, Tkinter.END)
                if event.keysym == "Right":
                        self.position = self.index(Tkinter.END) # go to end (no selection)
                if len(event.keysym) == 1:
                        self.autocomplete()
                # No need for up/down, we'll jump to the popup
                # list at the position of the autocompletion


def test(test_list):
        """Run a mini application to test the AutocompleteEntry Widget."""
        root = Tkinter.Tk(className='AutocompleteCombobox')

        combo = AutocompleteCombobox(root)
        combo.set_completion_list(test_list)
        combo.pack()
        combo.focus_set()
        # I used a tiling WM with no controls, added a shortcut to quit
        root.bind('<Control-Q>', lambda event=None: root.destroy())
        root.bind('<Control-q>', lambda event=None: root.destroy())
        root.mainloop()

if __name__ == '__main__':
        test_list = ('apple', 'banana', 'Cranberry', 'dogwood', 'alpha', 'Acorn', 'Anise', 'Strawberry' )
        test(test_list)
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2017-12-16 06:45:10

我怀疑你需要

 if self.get().lower() in element.lower():

而不是

 if element.lower().startswith(self.get().lower()):

要像在数据库中使用%LIKE%一样获取数据

但我不知道你是否得到了好的效果,因为这个Combobox用建议代替了文本,所以如果你输入be,它就会找到Cranberry,并把be放在适当的位置,这样你就不能写ber了。

也许你应该将Cranberry显示为单独的(下拉)列表,或者弹出式提示。

或者,您可能必须使用string.find()Cranberry中突出显示正确的位置,然后继续在正确的位置键入ber

EDIT:如何使用EntryListbox显示过滤列表的示例

listbox_update中,我添加了排序列表(比较小写字符串)

#!/usr/bin/env python3

import tkinter as tk

def on_keyrelease(event):
    
    # get text from entry
    value = event.widget.get()
    value = value.strip().lower()
    
    # get data from test_list
    if value == '':
        data = test_list
    else:
        data = []
        for item in test_list:
            if value in item.lower():
                data.append(item)                

    # update data in listbox
    listbox_update(data)
    
    
def listbox_update(data):
    # delete previous data
    listbox.delete(0, 'end')
    
    # sorting data 
    data = sorted(data, key=str.lower)

    # put new data
    for item in data:
        listbox.insert('end', item)


def on_select(event):
    # display element selected on list
    print('(event) previous:', event.widget.get('active'))
    print('(event)  current:', event.widget.get(event.widget.curselection()))
    print('---')


# --- main ---

test_list = ('apple', 'banana', 'Cranberry', 'dogwood', 'alpha', 'Acorn', 'Anise', 'Strawberry' )

root = tk.Tk()

entry = tk.Entry(root)
entry.pack()
entry.bind('<KeyRelease>', on_keyrelease)

listbox = tk.Listbox(root)
listbox.pack()
#listbox.bind('<Double-Button-1>', on_select)
listbox.bind('<<ListboxSelect>>', on_select)
listbox_update(test_list)

root.mainloop()

从完整列表开始

稍后仅包含已过滤的项目

编辑: 2020.07.21

如果要使用<KeyPress>,则必须更改on_keyrelease并使用event.charevent.keysym和/或event.keycode,因为KeyPress是在tkinter更新Entry中的文本之前执行的,并且您必须将event.char添加到<代码>D31中的文本(或在按<代码>D32时删除最后一个字符)

if event.keysym == 'BackSpace':
    value = event.widget.get()[:-1]  # remove last char
else:
    value = event.widget.get() + event.char  # add new char at the end

它可能需要对其他特殊键Ctrl+ACtrl+XCtrl+CCtrl+E等进行其他更改,这会造成很大的问题。

#!/usr/bin/env python3

import tkinter as tk

def on_keypress(event):

    print(event)
    print(event.state & 4) # Control
    print(event.keysym == 'a')
    # get text from entry
    if event.keysym == 'BackSpace':
        # remove last char
        value = event.widget.get()[:-1]
    elif (event.state & 4): # and (event.keysym in ('a', 'c', 'x', 'e')):
        value = event.widget.get()
    else:
        # add new char at the end        
        value = event.widget.get() + event.char
    #TODO: other special keys

    value = value.strip().lower()

    # get data from test_list
    if value == '':
        data = test_list
    else:
        data = []
        for item in test_list:
            if value in item.lower():
                data.append(item)                

    # update data in listbox
    listbox_update(data)


def listbox_update(data):
    # delete previous data
    listbox.delete(0, 'end')

    # sorting data 
    data = sorted(data, key=str.lower)

    # put new data
    for item in data:
        listbox.insert('end', item)


def on_select(event):
    # display element selected on list
    print('(event) previous:', event.widget.get('active'))
    print('(event)  current:', event.widget.get(event.widget.curselection()))
    print('---')


# --- main ---

test_list = ('apple', 'banana', 'Cranberry', 'dogwood', 'alpha', 'Acorn', 'Anise', 'Strawberry' )

root = tk.Tk()

entry = tk.Entry(root)
entry.pack()
entry.bind('<KeyPress>', on_keypress)

listbox = tk.Listbox(root)
listbox.pack()
#listbox.bind('<Double-Button-1>', on_select)
listbox.bind('<<ListboxSelect>>', on_select)
listbox_update(test_list)

root.mainloop()

BTW:

您还可以将Entry中的textvariableStringVartrace一起使用,后者在StringVar更改内容时执行函数。

var_text = tk.StringVar()
var_text.trace('w', on_change)

entry = tk.Entry(root, textvariable=var_text)
entry.pack()

#!/usr/bin/env python3

import tkinter as tk

def on_change(*args):
    #print(args)
          
    value = var_text.get()
    value = value.strip().lower()

    # get data from test_list
    if value == '':
        data = test_list
    else:
        data = []
        for item in test_list:
            if value in item.lower():
                data.append(item)                

    # update data in listbox
    listbox_update(data)


def listbox_update(data):
    # delete previous data
    listbox.delete(0, 'end')

    # sorting data 
    data = sorted(data, key=str.lower)

    # put new data
    for item in data:
        listbox.insert('end', item)


def on_select(event):
    # display element selected on list
    print('(event) previous:', event.widget.get('active'))
    print('(event)  current:', event.widget.get(event.widget.curselection()))
    print('---')

# --- main ---

test_list = ('apple', 'banana', 'Cranberry', 'dogwood', 'alpha', 'Acorn', 'Anise', 'Strawberry' )

root = tk.Tk()

var_text = tk.StringVar()
var_text.trace('w', on_change)

entry = tk.Entry(root, textvariable=var_text)
entry.pack()

listbox = tk.Listbox(root)
listbox.pack()
#listbox.bind('<Double-Button-1>', on_select)
listbox.bind('<<ListboxSelect>>', on_select)
listbox_update(test_list)

root.mainloop()
票数 10
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/47839813

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档