Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
240 views
in Technique[技术] by (71.8m points)

Interaction and Deletion among widgets on Python + KivyMD and Code Optimization

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:

enter image description here

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

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

enter image description here

CODE FOR MINIMAL REPRODUCIBLE EXAMPLE:

Python code:

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", cont

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

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

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

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

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...