lengyue869 发表于 2021-1-8 08:33:39

Combobox怎么实现模糊匹配?

下拉列表中有多个选项,怎么实现输入部分字符后自动将最接近的选项调到下拉列表的第一位?
或者通过其他方法达到类似的功能也行

例如我输入Pn7,自动将Pn789调到下拉列表的第一位

z5560636 发表于 2021-1-8 08:53:50

你说的应该是一个事件,内容改变事件,具体代码百度下。

qq1151985918 发表于 2021-1-8 09:04:03

本帖最后由 qq1151985918 于 2021-1-8 09:05 编辑

我从网上给你找了个,用的是PyQt5
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import Qt, QSortFilterProxyModel
from PyQt5.QtWidgets import QCompleter, QComboBox

class ExtendedComboBox(QComboBox):
    def __init__(self, parent=None):
      super(ExtendedComboBox, self).__init__(parent)

      self.setFocusPolicy(Qt.StrongFocus)
      self.setEditable(True)

      # add a filter model to filter matching items
      self.pFilterModel = QSortFilterProxyModel(self)
      self.pFilterModel.setFilterCaseSensitivity(Qt.CaseInsensitive)
      self.pFilterModel.setSourceModel(self.model())

      # add a completer, which uses the filter model
      self.completer = QCompleter(self.pFilterModel, self)
      # always show all (filtered) completions
      self.completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion)
      self.setCompleter(self.completer)

      # connect signals
      self.lineEdit().textEdited.connect(self.pFilterModel.setFilterFixedString)
      self.completer.activated.connect(self.on_completer_activated)


    # on selection of an item from the completer, select the corresponding item from combobox
    def on_completer_activated(self, text):
      if text:
            index = self.findText(text)
            self.setCurrentIndex(index)
            self.activated.emit(self.itemText(index))


    # on model change, update the models of the filter and completer as well
    def setModel(self, model):
      super(ExtendedComboBox, self).setModel(model)
      self.pFilterModel.setSourceModel(model)
      self.completer.setModel(self.pFilterModel)


    # on model column change, update the model column of the filter and completer as well
    def setModelColumn(self, column):
      self.completer.setCompletionColumn(column)
      self.pFilterModel.setFilterKeyColumn(column)
      super(ExtendedComboBox, self).setModelColumn(column)


if __name__ == "__main__":
    import sys
    from PyQt5.QtWidgets import QApplication
    from PyQt5.QtCore import QStringListModel

    app = QApplication(sys.argv)

    string_list = ['语文 Chinese', '数学 Mathematics', '英语 English']

    combo = ExtendedComboBox()

    # either fill the standard model of the combobox
    combo.addItems(string_list)

    # or use another model
    #combo.setModel(QStringListModel(string_list))

    combo.resize(300, 40)
    combo.show()

    sys.exit(app.exec_())

原文见链接pyQt5 QcomboBox 下拉选项模糊搜索
https://www.cnblogs.com/6min/articles/10751959.html

lengyue869 发表于 2021-1-8 09:11:45

z5560636 发表于 2021-1-8 08:53
你说的应该是一个事件,内容改变事件,具体代码百度下。

搜了一下这方面的资料很少,沒找到tkinter的combobox的相关操作...

lengyue869 发表于 2021-1-8 09:12:48

qq1151985918 发表于 2021-1-8 09:04
我从网上给你找了个,用的是PyQt5

原文见链接pyQt5 QcomboBox 下拉选项模糊搜索


感谢!~~
效果OK,还不会pyqt,我看看先

qq1151985918 发表于 2021-1-8 09:23:19

本帖最后由 qq1151985918 于 2021-1-8 09:28 编辑

lengyue869 发表于 2021-1-8 09:11
搜了一下这方面的资料很少,沒找到tkinter的combobox的相关操作...

楼主你贴的图片是你自己写的效果图吗?有没有代码看一下?你怎么实现的输入文字显示下拉列表内容?

lengyue869 发表于 2021-1-8 09:38:52

qq1151985918 发表于 2021-1-8 09:23
楼主你贴的图片是你自己写的效果图吗?有没有代码看一下?你怎么实现的输入文字显示下拉列表内容?



from tkinter import ttk
import tkinter as tk

root = tk.Tk()
root.geometry('850x400+300+100')

comb1Text = tk.StringVar()
comb2Text = tk.StringVar()
etyText = tk.StringVar()


def com1_select(*args):
    print(comb1.get())
    return comb1.get()


def com2_select(*args):
    print(comb2.get())
    return comb2.get()


# PN下拉框
comb1 = ttk.Combobox(root, textvariable=comb1Text, width=70)
comb1["values"] = ['Pn123', 'Pn456', 'Pn789']
comb1.bind("<<ComboboxSelected>>", com1_select)
pn = com1_select()

# user configuration下拉框
comb2 = ttk.Combobox(root, textvariable=comb2Text, width=70)
comb2["values"] = ['A', 'B', 'C']
comb2.bind("<<ComboboxSelected>>", com2_select)
user = com2_select()

tk.Label(root, text='BOM PN & Ver:').grid(row=0, column=0)
tk.Label(root, text='').grid(row=1, column=0)
tk.Label(root, text='User Configuration:').grid(row=2, column=0)

comb1.grid(row=0, column=1)
comb2.grid(row=2, column=1)

root.mainloop()

qq1151985918 发表于 2021-1-8 09:48:04

lengyue869 发表于 2021-1-8 09:38


原来你没能做到输入文字显示下拉列表内容,我说嘛,如果做到这个要求了应该就能做到模糊匹配了,因为我直觉感觉如果Combobox如果有一个textchange事件或者能够实现的话,就能够做到模糊匹配了

lengyue869 发表于 2021-1-8 09:57:31

qq1151985918 发表于 2021-1-8 09:48
原来你没能做到输入文字显示下拉列表内容,我说嘛,如果做到这个要求了应该就能做到模糊匹配了,因为我直 ...

额,我再找找combobox的教程看看

lengyue869 发表于 2021-1-8 10:03:52

找到了一个,哈哈
import tkinter
from tkinter import ttk


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.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)

qq1151985918 发表于 2021-1-8 10:12:21

lengyue869 发表于 2021-1-8 10:03
找到了一个,哈哈

我试了下,这个不应该算作模糊搜索,应该算输入提示,比如 要搜索banana,
模糊搜索应该是输入ana    nan都可以
不过应该可以 在这个基础上修改了

lengyue869 发表于 2021-1-8 10:23:11

qq1151985918 发表于 2021-1-8 10:12
我试了下,这个不应该算作模糊搜索,应该算输入提示,比如 要搜索banana,
模糊搜索应该是输入ana   ...

是的,不过看起来好像很复杂的样子....

lengyue869 发表于 2021-1-8 10:32:37

qq1151985918 发表于 2021-1-8 10:12
我试了下,这个不应该算作模糊搜索,应该算输入提示,比如 要搜索banana,
模糊搜索应该是输入ana   ...

还是PyQt5那个效果好一些,要是能移植到tkinter上来就好了{:5_109:}

qq1151985918 发表于 2021-1-8 10:50:19

lengyue869 发表于 2021-1-8 10:32
还是PyQt5那个效果好一些,要是能移植到tkinter上来就好了

我先研究研究能不能把你刚刚搜索到的修改一下

lengyue869 发表于 2021-1-8 10:54:28

qq1151985918 发表于 2021-1-8 10:50
我先研究研究能不能把你刚刚搜索到的修改一下

那真是太感谢了!~{:5_108:}{:5_108:}{:5_108:}

qq1151985918 发表于 2021-1-8 11:07:51

lengyue869 发表于 2021-1-8 10:54
那真是太感谢了!~

只能说是尝试

lengyue869 发表于 2021-1-8 11:17:03

qq1151985918 发表于 2021-1-8 11:07
只能说是尝试

那也感谢{:5_109:}

qq1151985918 发表于 2021-1-8 12:13:30

lengyue869 发表于 2021-1-8 10:54
那真是太感谢了!~

抱歉,我改了半天,只能实现得很不完善,我想或许我得加入re来改,实在是太没有必要,因为这还得配合Combobox的其他用法和事件相配合,我功夫还不够...暂时改不成了,或许论坛的大神们出手可以。

lengyue869 发表于 2021-1-8 12:18:59

qq1151985918 发表于 2021-1-8 12:13
抱歉,我改了半天,只能实现得很不完善,我想或许我得加入re来改,实在是太没有必要,因为这还得配合Comb ...

it's ok,不行的话就用pyqt的布局,我抽时间学下pyqt{:5_110:}

qq1151985918 发表于 2021-1-8 12:27:54

lengyue869 发表于 2021-1-8 12:18
it's ok,不行的话就用pyqt的布局,我抽时间学下pyqt

你先研究着,我也还在想修改的办法,如果改成功了会告诉你
页: [1] 2
查看完整版本: Combobox怎么实现模糊匹配?