使用 Python 的过滤方法的预测文本 MDDropdown

Predictive Text MDDropdown with Python's filter Method

我正在尝试在 KivyMD 应用程序上构建预测文本 MDDropdown。我有一个 MDTextField,用户将在其中输入文本。当用户写下 his/her 搜索时,MDDropdown 应该显示包含所写文本的项目。选择任何给定项目时,应将所选内容的全文添加到 TextField。

我试过Python的过滤方法来完成这样的任务,但是我没有成功。输入文本时永远不会打开 MDDropdown。另外,我注意到过滤器函数会查找完全匹配项。有没有办法过滤字符串的部分部分?

Python代码如下:

from kivy.uix.screenmanager import ScreenManager, Screen
from kivymd.app import MDApp
from kivymd.uix.button import MDIconButton, MDTooltip
from kivymd.uix.menu import MDDropdownMenu
from kivy.clock import Clock


class TooltipMDIconButton(MDIconButton, MDTooltip):
    pass

# HOJA DE ACTIVIDADES ################################
class IngActivWindow(Screen):
    menu_cliente = None

    def cliente_dropdown(self):

        clientes = ['Amazon', 'Adidas','Banana Republic', 'Calvin Klein','Coach', 'DKNY', 'Microsoft','Nike', 'Sony']

        if len(self.ids.name_client.text) != 0:
            def filtar_clientes(cliente):
                filtrar_valor = self.ids.name_client.text
                if cliente in filtrar_valor:
                    return True
                else:
                    return False
            filtered = filter(lambda l: str(self.ids.name_client.text) in l, clientes)

            for valores in filtered:
                print(valores)

            # Create the drop down menu
            for cliente in filtered:
                menu_items = [{"text": f"{valores}"} for valores in filtered]
                self.menu_cliente = MDDropdownMenu(
                    caller=self.ids.name_client,
                    items=menu_items,
                    width_mult=5,
                )
                self.menu_cliente.open()
                self.menu_cliente.bind(on_release=self.set_item_cliente)

    def open_clientes_list(self):
        clientes = ['Amazon', 'Adidas','Banana Republic', 'Calvin Klein','Coach', 'DKNY', 'Microsoft','Nike', 'Sony']
        # Create the drop down menu
        menu_items = [{"text": f"{cliente}"} for cliente in clientes]
        self.menu_cliente = MDDropdownMenu(
            caller=self.ids.name_client,
            items=menu_items,
            width_mult=5,
        )
        self.menu_cliente.open()
        self.menu_cliente.bind(on_release=self.set_item_cliente)

    def set_item_cliente(self, instance_menu, instance_menu_item):
        def set_item_cliente(interval):
            self.ids.name_client.text = instance_menu_item.text
            instance_menu.dismiss()

        Clock.schedule_once(set_item_cliente, 0.5)


# WINDOW MANAGER ################################
class WindowManager(ScreenManager):
    pass


# MAIN CLASS ################################
class PredictiveDropdown(MDApp):


    def build(self):
        return WindowManager()


if __name__ == "__main__":
    PredictiveDropdown().run()

基维密码:

<WindowManager>:
    id: screen_manager

    IngActivWindow:
        id: ingActiv
        name: 'ingActiv'


<IngActivWindow>:
    MDBoxLayout:
        orientation: 'horizontal'
        size_hint: 1, 1
        pos_hint: {'center_x':0.5 , 'center_y':0.5}
        padding: '20dp'
        MDTextField:
            id: name_client
            pos_hint: {"x":0, 'center_y': 0.5}
            write_tab: False
            size_hint: 0.45, None
            halign: 'left'
            valign: 'center'
            hint_text:'Seleccionar Cliente o Prospecto'
            on_text: root.cliente_dropdown()

        TooltipMDIconButton:
            id: client_list
            pos_hint: {"x":0, 'center_y': 0.5}
            valign: 'center'
            icon: 'arrow-down-drop-circle-outline'
            tooltip_text: 'Haga clic para visualizar la lista completa de clientes'
            on_release: root.open_clientes_list()

有什么关于如何完成预期任务的建议吗?我做错了什么?

非常感谢。

编辑 有没有一种方法可以按角色的位置进行过滤?我更改了 filtered 变量代码。现在,当我打字时,我确实得到了一份打印清单。但是,如果我写 'a',我会得到所有带有 a 的元素,例如 'Calvin Klein'。我希望程序只打印 'Amazon' 和 'Adidas'。我已经相应地修改了我的代码。

DropDown 永远不会打开,因为您正在使用 filter 在您 print filter 的结果时产生的 iteratoriterator 一旦使用,就不能重复使用。要解决这个问题,只需删除以下行:

        for valores in filtered:
            print(valores)

考虑使用 regular expressions。而不是 filter 来获取您的 filtered 列表。

另外两个问题。您正在循环 for valores in filtered: 中打开 DropDown,但我不明白您为什么要在循环中这样做。由于您在 kv 中使用 on_text 触发 cliente_dropdown() 方法,因此当您的 set_item_cliente() 方法更改文本时,它将再次触发。解决方法是在下拉菜单更改文本时禁用 cliente_dropdown() 方法。

这是您的 IngActivWindow class 的修改版本,解决了这些问题:

class IngActivWindow(Screen):
    menu_cliente = None
    do_dropdown = True

    def cliente_dropdown(self):
        # do not open a dropdown if the text was changed by the dropdown
        if not self.do_dropdown:
            return

        if self.menu_cliente:
            # if dropdown is already open, close it
            self.menu_cliente.dismiss()
            self.menu_cliente = None

        clientes = ['Amazon', 'Adidas','Banana Republic', 'Calvin Klein','Coach', 'DKNY', 'Microsoft','Nike', 'Sony']
        filtered_list = []

        if len(self.ids.name_client.text) != 0:
            prefix = self.ids.name_client.text.title()
            filtered = filter(lambda l: l.startswith(prefix), clientes)
            menu_items = [{"text": f"{valores}"} for valores in filtered]
            if len(menu_items) < 1:
                return
            self.menu_cliente = MDDropdownMenu(
                caller=self.ids.name_client,
                callback=self.set_item_cliente,  # use callback instead of bind
                items=menu_items,
                width_mult=5,
            )
            self.menu_cliente.open()

    def open_clientes_list(self):
        clientes = ['Amazon', 'Adidas','Banana Republic', 'Calvin Klein','Coach', 'DKNY', 'Microsoft','Nike', 'Sony']
        # Create the drop down menu
        menu_items = [{"text": f"{cliente}"} for cliente in clientes]
        self.menu_cliente = MDDropdownMenu(
            caller=self.ids.name_client,
            items=menu_items,
            width_mult=5,
        )
        self.menu_cliente.open()
        self.menu_cliente.bind(on_release=self.set_item_cliente)

    def set_item_cliente(self, instance_menu_item):
        def set_item_cliente(interval):
            self.do_dropdown = False
            self.ids.name_client.text = instance_menu_item.text
            self.menu_cliente.dismiss()
            self.menu_cliente = None
            self.do_dropdown = True

        Clock.schedule_once(set_item_cliente, 0.5)

我将 cliente_dropdown 函数更改为:

def cliente_dropdown(self):

    clientes = ['Amazon', 'Adidas','Banana Republic', 'Calvin Klein','Coach', 'DKNY', 'Microsoft','Nike', 'Sony']
    filtered_list = []

    if len(self.ids.name_client.text) != 0:
        prefix = self.ids.name_client.text.title()
        filtered = filter(lambda l: l.startswith(prefix), clientes)

        # Create the drop down menu
        for valores in filtered:
            filtered_list.append(valores)
            menu_items = [{"text": f"{valores}"} for valores in filtered_list]
            self.menu_cliente = MDDropdownMenu(
                caller=self.ids.name_client,
                items=menu_items,
                width_mult=5,
            )
            self.menu_cliente.open()
            self.menu_cliente.bind(on_release=self.set_item_cliente)

这很管用,正是我需要的。但是,下拉菜单会不断打开一个在另一个之上。我希望前一个 MDDropdown 在后一个打开之前关闭。

有什么建议吗?非常感谢!