Python + KivyMD上的小部件之间的交互和删除以及代码优化 [英] Interaction and Deletion among widgets on Python + KivyMD and Code Optimization

查看:72
本文介绍了Python + KivyMD上的小部件之间的交互和删除以及代码优化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我目前正在构建一个用作差旅费用管理器的应用程序.我有一个系统,用户在其中将所需的支出金额写在MDTextField上,这样的支出将记录在MDList中,而请求的支出总额将添加到MDTextField中.为了清楚起见,我将发布一个屏幕截图:

I am currently building an app that serves as a travel expenses manager. I have a system in which the user writes the desired amount of the expense on an MDTextField, such expense will be recorded in an MDList and the total amount of requested expenses will be added to an MDTextField. For clarity I will post a screen shot:

到目前为止,所有该系统都可以正常工作.但是,用户可能会犯错误.我希望用户能够在按下垃圾箱图标时从MDList删除项目.同时,应从所要求的总额中减去已删除的支出额. (即,如果用户删除Alimentación的元素为$ 1,000.00,则删除后请求的总金额应为$ 2000.0).

All this system works correctly so far. However, there can be mistakes made by the user. I want the user to be able to delete an item from the MDList when the trash-can icon is pressed. At the same time, the amount of the deleted expenditure should be substracted from the total amount requested. (i.e. If the user deletes the element of Alimentación which had $1,000.00, the total amount requested after deletion should be $2000.0).

在我的代码中,我已经能够将图标的on_pressed事件绑定到remove_item函数.哪个成功进行了所需的减法,然后将显示成功的 toast .但是,这是在没有实际从MDList删除项目的情况下完成的.如第二张屏幕截图所示,总费用为0,但是MDList项尚未删除(remove_item函数中的 remove_widget 函数未执行所需的操作).

In my code I've been able to bind the on_pressed event of the icon to a remove_item function. Which succesfully does the desired substraction, and afterwards will show a success toast. However this is done without actually deleting the item from the MDList. As shown on the second screenshot, the total expenses amount is 0, nevertheless the MDList item has not been deleted (remove_widget function inside my remove_item function is not doing the desired action).

最小可重复示例的代码:

Python代码:

from kivy.properties import ObjectProperty
from kivy.uix.screenmanager import ScreenManager, Screen
from kivymd.app import MDApp
from kivymd.uix.expansionpanel import MDExpansionPanel, MDExpansionPanelOneLine
from kivy.uix.boxlayout import BoxLayout
from kivymd.uix.list import TwoLineIconListItem, IconLeftWidget
from kivymd.toast import toast


class ViaticosIconList(TwoLineIconListItem):
    pass


class MyContentAliment(BoxLayout):
    def apply_currency_format(self):
        # if len <= 3
        if len(self.ids.monto_aliment_viaje.text) <= 3 and self.ids.monto_aliment_viaje.text.isnumeric():
            self.ids.monto_aliment_viaje.text = "$" + self.ids.monto_aliment_viaje.text + '.00'
        # n,nnn
        elif len(self.ids.monto_aliment_viaje.text) == 4 and self.ids.monto_aliment_viaje.text.isnumeric():
            self.ids.monto_aliment_viaje.text = "$" + self.ids.monto_aliment_viaje.text[0] + "," + \
                                            self.ids.monto_aliment_viaje.text[1:] + '.00'
        # nn,nnn
        elif len(self.ids.monto_aliment_viaje.text) == 5 and self.ids.monto_aliment_viaje.text.isnumeric():
            self.ids.monto_aliment_viaje.text = "$" + self.ids.monto_aliment_viaje.text[:2] + "," + \
                                            self.ids.monto_aliment_viaje.text[2:] + '.00'

    def limit_currency(self):
        if len(self.ids.monto_aliment_viaje.text) > 5 and self.ids.monto_aliment_viaje.text.startswith('$') == False:
            self.ids.monto_aliment_viaje.text = self.ids.monto_aliment_viaje.text[:-1]

    def sumar_gasto(self):
        if self.ids.monto_aliment_viaje.text == "":
            pass
        elif self.ids.monto_aliment_viaje.text.startswith('$'):
            pass
        else:
            travel_manager = MDApp.get_running_app().root.get_screen('travelManager')
            monto_total = float(travel_manager.ids.suma_solic_viaje.text[2:])
            monto_total += float(self.ids.monto_aliment_viaje.text)
            travel_manager.ids.suma_solic_viaje.text = "$ " + str(monto_total)
            self.apply_currency_format()

            screen_list_view = MDApp.get_running_app().root.get_screen('travelManager').ids.viaticos_list
            icon = IconLeftWidget(icon='delete')
            add_item = ViaticosIconList(text=f"Alimentación Personal", secondary_text=self.ids.monto_aliment_viaje.text)
            add_item.add_widget(icon)
            icon.bind(on_press=self.remove_item)
            screen_list_view.add_widget(add_item)

    def remove_item(self, instance):
        # Remove item once the trash icon is clicked (pendiente)
        travel = MDApp.get_running_app().root.get_screen('travelManager')
        travel.ids.viaticos_list.remove_widget(instance)
        self.substract_expense()
        self.show_toast()

    def substract_expense(self):
        travel_manager = MDApp.get_running_app().root.get_screen('travelManager')
        monto_total = float(travel_manager.ids.suma_solic_viaje.text[2:])
        substract_amount = self.ids.monto_aliment_viaje.text[1:-3].replace(',', '')
        monto_total -= float(substract_amount)
        travel_manager.ids.suma_solic_viaje.text = "$ " + str(monto_total)
        self.ids.monto_aliment_viaje.text = ''

    def show_toast(self):
        toast("Monto de Alimentación Personal eliminado de la solicitud")


class MyContentCasetas(BoxLayout):
    def apply_currency_format(self):
        # if len <= 3
        if len(self.ids.monto_casetas_viaje.text) <= 3 and self.ids.monto_casetas_viaje.text.isnumeric():
            self.ids.monto_casetas_viaje.text = "$" + self.ids.monto_casetas_viaje.text + '.00'
        # n,nnn
        elif len(self.ids.monto_casetas_viaje.text) == 4 and self.ids.monto_casetas_viaje.text.isnumeric():
            self.ids.monto_casetas_viaje.text = "$" + self.ids.monto_casetas_viaje.text[0] + "," + \
                                            self.ids.monto_casetas_viaje.text[1:] + '.00'
        # nn,nnn
        elif len(self.ids.monto_casetas_viaje.text) == 5 and self.ids.monto_casetas_viaje.text.isnumeric():
            self.ids.monto_casetas_viaje.text = "$" + self.ids.monto_casetas_viaje.text[:2] + "," + \
                                            self.ids.monto_casetas_viaje.text[2:] + '.00'

    def limit_currency(self):
        if len(self.ids.monto_casetas_viaje.text) > 5 and self.ids.monto_casetas_viaje.text.startswith('$') == False:
            self.ids.monto_casetas_viaje.text = self.ids.monto_casetas_viaje.text[:-1]

    def sumar_gasto(self):
        if self.ids.monto_casetas_viaje.text == "":
            pass
        elif self.ids.monto_casetas_viaje.text.startswith('$'):
            pass
        else:
            travel_manager = MDApp.get_running_app().root.get_screen('travelManager')
            monto_total = float(travel_manager.ids.suma_solic_viaje.text[2:])
            monto_total += float(self.ids.monto_casetas_viaje.text)
            travel_manager.ids.suma_solic_viaje.text = "$ " + str(monto_total)
            self.apply_currency_format()

            screen_list_view = MDApp.get_running_app().root.get_screen('travelManager').ids.viaticos_list
            icon = IconLeftWidget(icon='delete')
            add_item = ViaticosIconList(text=f"Casetas", secondary_text=self.ids.monto_casetas_viaje.text)
            add_item.add_widget(icon)
            icon.bind(on_press=self.remove_item)
            screen_list_view.add_widget(add_item)

    def remove_item(self, instance):
        # Remove item once the trash icon is clicked (pendiente)
        travel = MDApp.get_running_app().root.get_screen('travelManager')
        travel.ids.viaticos_list.remove_widget(instance)
        self.substract_expense()
        self.show_toast()

    def substract_expense(self):
        travel_manager = MDApp.get_running_app().root.get_screen('travelManager')
        monto_total = float(travel_manager.ids.suma_solic_viaje.text[2:])
        substract_amount = self.ids.monto_casetas_viaje.text[1:-3].replace(',', '')
        monto_total -= float(substract_amount)
        travel_manager.ids.suma_solic_viaje.text = "$ " + str(monto_total)
        self.ids.monto_casetas_viaje.text = ''

    def show_toast(self):
        toast("Monto de Casetas eliminado de la solicitud")


class MyContentGasolina(BoxLayout):
    def apply_currency_format(self):
        # if len <= 3
        if len(self.ids.monto_gas_viaje.text) <= 3 and self.ids.monto_gas_viaje.text.isnumeric():
            self.ids.monto_gas_viaje.text = "$" + self.ids.monto_gas_viaje.text + '.00'
        # n,nnn
        elif len(self.ids.monto_gas_viaje.text) == 4 and self.ids.monto_gas_viaje.text.isnumeric():
            self.ids.monto_gas_viaje.text = "$" + self.ids.monto_gas_viaje.text[0] + "," + \
                                        self.ids.monto_gas_viaje.text[1:] + '.00'
        # nn,nnn
        elif len(self.ids.monto_gas_viaje.text) == 5 and self.ids.monto_gas_viaje.text.isnumeric():
            self.ids.monto_gas_viaje.text = "$" + self.ids.monto_gas_viaje.text[:2] + "," + \
                                        self.ids.monto_gas_viaje.text[2:] + '.00'

    def limit_currency(self):
        if len(self.ids.monto_gas_viaje.text) > 5 and self.ids.monto_gas_viaje.text.startswith('$') == False:
            self.ids.monto_gas_viaje.text = self.ids.monto_gas_viaje.text[:-1]

    def sumar_gasto(self):
        if self.ids.monto_gas_viaje.text == "":
            pass
        elif self.ids.monto_gas_viaje.text.startswith('$'):
            pass
        else:
            travel_manager = MDApp.get_running_app().root.get_screen('travelManager')
            monto_total = float(travel_manager.ids.suma_solic_viaje.text[2:])
            monto_total += float(self.ids.monto_gas_viaje.text)
            travel_manager.ids.suma_solic_viaje.text = "$ " + str(monto_total)
            self.apply_currency_format()

            screen_list_view = MDApp.get_running_app().root.get_screen('travelManager').ids.viaticos_list
            icon = IconLeftWidget(icon='delete')
            add_item = ViaticosIconList(text=f"Gasolina", secondary_text=self.ids.monto_gas_viaje.text)
            add_item.add_widget(icon)
            icon.bind(on_press=self.remove_item)
            screen_list_view.add_widget(add_item)

    def remove_item(self, instance):
        # Remove item once the trash icon is clicked (pendiente)
        travel = MDApp.get_running_app().root.get_screen('travelManager')
        travel.ids.viaticos_list.remove_widget(instance)
        self.substract_expense()
        self.show_toast()

    def substract_expense(self):
        travel_manager = MDApp.get_running_app().root.get_screen('travelManager')
        monto_total = float(travel_manager.ids.suma_solic_viaje.text[2:])
        substract_amount = self.ids.monto_gas_viaje.text[1:-3].replace(',', '')
        monto_total -= float(substract_amount)
        travel_manager.ids.suma_solic_viaje.text = "$ " + str(monto_total)
        self.ids.monto_gas_viaje.text = ''

    def show_toast(self):
        toast("Monto de Gasolina eliminado de la solicitud")


class LoginWindow(Screen):
    pass


class TravelManagerWindow(Screen):
    panel_container = ObjectProperty(None)

    # EXPANSION PANEL PARA SOLICITAR GV
    def set_expansion_panel(self):
        self.ids.panel_container.clear_widgets()
        # FOOD PANEL
        self.ids.panel_container.add_widget(MDExpansionPanel(icon="food", content=MyContentAliment(),
                                                         panel_cls=MDExpansionPanelOneLine(text="Alimentacion")))
        # CASETAS PANEL
        self.ids.panel_container.add_widget(MDExpansionPanel(icon="food", content=MyContentCasetas(),
                                                         panel_cls=MDExpansionPanelOneLine(text="Casetas")))
        # GAS PANEL
        self.ids.panel_container.add_widget(MDExpansionPanel(icon="food", content=MyContentGasolina(),
                                                         panel_cls=MDExpansionPanelOneLine(text="Gasolina")))


### WINDOW MANAGER ################################

class WindowManager(ScreenManager):
    pass


class ReprodExample(MDApp):
    def build(self):
        self.theme_cls.primary_palette = "Teal"
        return WindowManager()


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

KV文件:

<WindowManager>:
    LoginWindow:
    TravelManagerWindow:

<LoginWindow>:
    name: 'login'
    MDRaisedButton:
        text: 'Enter'
        pos_hint: {'center_x': 0.5, 'center_y': 0.5}
        size_hint: None, None
        on_release:
            root.manager.transition.direction = 'up'
            root.manager.current = 'travelManager'

<TravelManagerWindow>:
    name:'travelManager'
    on_pre_enter: root.set_expansion_panel()

    MDRaisedButton:
        text: 'Back'
        pos_hint: {'center_x': 0.5, 'center_y': 0.85}
        size_hint: None, None
        on_release:
            root.manager.transition.direction = 'down'
            root.manager.current = 'login'

    BoxLayout:
        orientation: 'vertical'
        size_hint:1,0.85
        pos_hint: {"center_x": 0.5, "center_y":0.37}
        adaptive_height:True
        height: self.minimum_height

        ScrollView:
            adaptive_height:True

            GridLayout:
                size_hint_y: None
                cols: 1
                row_default_height: root.height*0.10
                height: self.minimum_height

                BoxLayout:
                    adaptive_height: True
                    orientation: 'horizontal'

                    GridLayout:
                        id: panel_container
                        size_hint_x: 0.6
                        cols: 1
                        adaptive_height: True

                    BoxLayout:
                        size_hint_x: 0.05
                    MDCard:
                        id: resumen_solicitud
                        size_hint: None, None
                        size: "250dp", "350dp"
                        pos_hint: {"top": 0.9, "center_x": .5}
                        elevation: 0.1

                        BoxLayout:
                            orientation: 'vertical'
                            canvas.before:
                                Color:
                                    rgba: 0.8, 0.8, 0.8, 1
                                Rectangle:
                                    pos: self.pos
                                    size: self.size
                            MDLabel:
                                text: 'Monto Total Solicitado'
                                font_style: 'Button'
                                halign: 'center'
                                font_size: (root.width**2 + root.height**2) / 15.5**4
                                size_hint_y: 0.2
                            MDSeparator:
                                height: "1dp"
                            MDTextField:
                                id: suma_solic_viaje
                                text: "$ 0.00"
                                bold: True
                                line_color_normal: app.theme_cls.primary_color
                                halign: "center"
                                size_hint_x: 0.8
                                pos_hint: {'center_x': 0.5, 'center_y': 0.5}
                            MDSeparator:
                                height: "1dp"

                            MDBoxLayout:
                                padding: '5dp', '10dp', '5dp', '10dp'
                                MDList:
                                    pos_hint: {'x': 0, 'top': 1}
                                    id: viaticos_list
                                    padding: 0


<MyContentAliment>:
    adaptive_height: True
    MDBoxLayout:
        orientation:'horizontal'
        adaptive_height:True
        size_hint_x:self.width
        pos_hint: {"center_x":0.5, "center_y":0.5}
        spacing: dp(10)
        padding_horizontal: dp(10)
        MDLabel:
            text: 'Monto:'
            multiline: 'True'
            halign: 'center'
            pos_hint: {"x":0, "top":0.5}
            size_hint_x: 0.15
            font_style: 'Button'
            font_size: 19

        MDTextField:
            id: monto_aliment_viaje
            hint_text: 'Monto a solicitar'
            pos_hint: {"x":0, "top":0.5}
            halign: 'left'
            size_hint_x: 0.3
            helper_text: 'Ingresar el monto a solicitar'
            helper_text_mode: 'on_focus'
            write_tab: False
            required: True
            on_text: root.limit_currency()

        MDRaisedButton:
            id: boton_aliment_viaje
            pos_hint: {"x":0, "top":0.5}
            text:'Ingresar Gasto'
            on_release: root.sumar_gasto()

### CASETAS
<MyContentCasetas>:
    adaptive_height: True
    MDBoxLayout:
        orientation:'horizontal'
        adaptive_height:True
        size_hint_x:self.width
        pos_hint: {"center_x":0.5, "center_y":0.5}
        spacing: dp(10)
        padding_horizontal: dp(10)
        MDLabel:
            text: 'Monto:'
            multiline: 'True'
            halign: 'center'
            pos_hint: {"x":0, "top":0.5}
            size_hint_x: 0.15
            font_style: 'Button'
            font_size: 19

        MDTextField:
            id: monto_casetas_viaje
            hint_text: 'Monto a solicitar'
            pos_hint: {"x":0, "top":0.5}
            halign: 'left'
            size_hint_x: 0.3
            helper_text: 'Ingresar el monto a solicitar'
            helper_text_mode: 'on_focus'
            write_tab: False
            required: True
            on_text: root.limit_currency()

        MDRaisedButton:
            id: boton_casetas_viaje
            pos_hint: {"x":0, "top":0.5}
            text:'Ingresar Gasto'
            on_release: root.sumar_gasto()

        BoxLayout:
            size_hint_x: 0.05

### GASOLINA
<MyContentGasolina>:
    adaptive_height: True
    MDBoxLayout:
        orientation:'horizontal'
        adaptive_height:True
        size_hint_x:self.width
        pos_hint: {"center_x":0.5, "center_y":0.5}
        spacing: dp(10)
        padding_horizontal: dp(10)
        MDLabel:
            text: 'Monto:'
            multiline: 'True'
            halign: 'center'
            pos_hint: {"x":0, "top":0.5}
            size_hint_x: 0.15
            font_style: 'Button'
            font_size: 19

        MDTextField:
            id: monto_gas_viaje
            hint_text: 'Monto a solicitar'
            pos_hint: {"x":0, "top":0.5}
            halign: 'left'
            size_hint_x: 0.3
            helper_text: 'Ingresar el monto a solicitar'
            helper_text_mode: 'on_focus'
            write_tab: False
            required: True
            on_text: root.limit_currency()

        MDRaisedButton:
            id: boton_gas_viaje
            pos_hint: {"x":0, "top":0.5}
            text:'Ingresar Gasto'
            on_release: root.sumar_gasto()

        BoxLayout:
            size_hint_x: 0.05

最后,如果您能给我有关Python文件代码优化的建议,我将不胜感激.我在类中重复了基本上相同的代码,只引用了不同的小部件.我可以列出要与之交互的窗口小部件并进行循环,得到相同的结果吗?我是Python的新手,所以我想我需要学习更好的编码方法.

Lastly, I would really appreciate if you could give me suggestions for code optimization on my Python file. I am repeating basically the same code in my Classes, only referring to different widgets. Can I make a list of the widgets I want to interact with and make a loop, getting the same result? I'm fairly new to Python so I guess I need to learn better ways to code.

非常感谢.

推荐答案

问题是您的remove_item()试图删除错误的对象.传递给remove_item()instance是图标,而不是ViaticosIconList.为了将ViaticosIconList传递给remove_item(),您可以将bind调用更改为:

The problem is that your remove_item() is trying to remove the wrong object. The instance passed to remove_item() is the icon, not the ViaticosIconList. In order to get the ViaticosIconList passed to the remove_item() you can change your bind call to:

icon.bind(on_press=partial(self.remove_item, add_item))

partial本质上创建了一个新函数,该函数将以add_item作为参数.请参见文档.

The partial essentially creates a new function that will have add_item as an argument. See the documentation.

然后,您必须调整remove_item()方法定义以处理其他参数:

Then, you must adjust your remove_item() method definition to handle the additional argument:

def remove_item(self, instance, icon):
    # Remove item once the trash icon is clicked (pendiente)
    travel = MDApp.get_running_app().root.get_screen('travelManager')
    travel.ids.viaticos_list.remove_widget(instance)
    self.substract_expense()
    self.show_toast()

icon参数是曾经是instance的参数,而新参数是instance(add_item).

The icon argument is the argument that used to be instance and the new argument is the instance (the add_item).

您可以通过为包含所有通用代码的费用类型创建基类来简化类,然后每种不同的费用类型都可以扩展该基类.

You can simplify your classes by creating a base class for your expense types that contains all the common code, then each different expense type can just extend that base class.

这篇关于Python + KivyMD上的小部件之间的交互和删除以及代码优化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆