【日常收支賬本】【Day05】編輯賬本界面增加刪除、更新記錄功能——提高代碼復用性

来源:https://www.cnblogs.com/LinfengBingyi/archive/2023/10/11/17756104.html
-Advertisement-
Play Games

一、項目地址 https://github.com/LinFeng-BingYi/DailyAccountBook 二、新增 1. 增加刪除記錄功能 1.1 功能詳述 點擊刪除按鈕後,獲取對應行的數據組成字典,用字典的鍵值對匹配到對應日期的記錄元素; 接著用該字典數據沖正存款賬戶餘額(實現思路為新增 ...


一、項目地址

https://github.com/LinFeng-BingYi/DailyAccountBook

二、新增

1. 增加刪除記錄功能

1.1 功能詳述

  • 點擊刪除按鈕後,獲取對應行的數據組成字典,用字典的鍵值對匹配到對應日期的記錄元素;
  • 接著用該字典數據沖正存款賬戶餘額(實現思路為新增記錄時的反向操作),同時刪除記錄元素;
  • 最後再更新表格。

1.2 代碼實現

    def deleteTableRow(self, triggeredBtn, tableWidget):
        print("觸發了刪除按鈕")
        # 獲取觸發信號的控制項所在行號
        current_row = tableWidget.indexAt(triggeredBtn.parent().pos()).row()
        if tableWidget == self.tableWidget_expense:
            const_class = expenseConst
            action = 'expense'
        elif tableWidget == self.tableWidget_income:
            const_class = incomeConst
            action = 'income'
        elif tableWidget == self.tableWidget_movement:
            const_class = movementConst
            action = 'movement'
        else:
            print('未知控制項觸發新增按鈕!')
            return
        # 獲取待刪除行的數據
        old_data_dict = self.getExistTableCell(tableWidget, current_row, const_class)
        if old_data_dict is None:
            print("獲取待刪除數據失敗!!")
            return
        print("待刪除記錄內容為: \n", old_data_dict)

        if self.file_processor.deleteRecord(old_data_dict, self.dateEdit.text().replace('/', ''), action):
            print("刪除成功!")
            # 把刪除結果寫入文件
            self.file_processor.writeXMLFile(self.lineEdit_file_path.text())

            # 更新self.file_parse_result以及記錄表
            records_list = self.file_parse_result[action + 's']
            records_list.pop(current_row)
            self.updateRecordTable(tableWidget, records_list, const_class)

其中處理xml文件的方法代碼如下:

    def deleteRecord(self, old_record_dict, date_str, action):
        e_date = self.getSpecificDateElement(date_str)
        if isinstance(e_date, int):
            print("未找到這一天的數據!")
            return False

        if action == 'expense':
            action_path = ".//expenses"
            record_path = """.//expense[@necessity='{}'][@associatedFund='{}'][value='{}'][category='{}'][detail='{}'][describe='{}'][from='{}']""".format(
                old_record_dict['necessity'],
                old_record_dict['associatedFund'],
                old_record_dict['value'],
                old_record_dict['category'],
                old_record_dict['detail'],
                old_record_dict['describe'],
                old_record_dict['from'])
        elif action == 'income':
            action_path = ".//incomes"
            record_path = """.//income[@associatedFund='{}'][value='{}'][category='{}'][detail='{}'][describe='{}'][to='{}']""".format(
                old_record_dict['associatedFund'],
                old_record_dict['value'],
                old_record_dict['category'],
                old_record_dict['detail'],
                old_record_dict['describe'],
                old_record_dict['to'])
        elif action == 'movement':
            action_path = ".//movements"
            record_path = """.//movement[value='{}'][detail='{}'][describe='{}'][from='{}'][to='{}']""".format(
                old_record_dict['value'],
                old_record_dict['detail'],
                old_record_dict['describe'],
                old_record_dict['from'],
                old_record_dict['to'])
        else:
            print("未知的動賬操作!!")
            return False
        # print(record_path)

        e_action = e_date.find(action_path)
        e_record = e_action.find(record_path)
        if e_record is None:
            print("未找到待刪除的記錄")
            return False
        e_action.remove(e_record)

        if action == 'movement':
            self.modifyBalance(old_record_dict['from'], Decimal(old_record_dict['value']))
            self.modifyBalance(old_record_dict['to'], Decimal(old_record_dict['value']) * (-1))
        else:
            self.reversalVariation(old_record_dict, e_date)
        return True

    def reversalVariation(self, change_dict, e_date):
        """
        Describe: 刪除某條記錄時,需要衝正原來的記錄,將回退對應的存款賬戶數值變化、以及餘額

        Args:
            change_dict: dict
                記錄字典
            e_date: Element
                指定日期的day元素
        """
        e_variation = e_date.find(".//variation")
        if e_variation is None:
            print("沖正記錄時異常!!該記錄不存在餘額變化")
            return
        if 'from' in change_dict:
            e_fund_variety = e_variation.find(".//fund[category='{}']/out".format(change_dict['from']))
            if e_fund_variety is None:
                print("沖正記錄時異常!!該記錄不存在餘額變化")
                return
            e_fund_variety.text = str((Decimal(e_fund_variety.text) - Decimal(change_dict['value'])).quantize(Decimal('0.00')))
            self.modifyBalance(change_dict['from'], Decimal(change_dict['value']))

            self.reversalAssociatedFund(e_variation, change_dict, 'from')
        if 'to' in change_dict:
            e_fund_variety = e_variation.find(".//fund[category='{}']/in".format(change_dict['to']))
            if e_fund_variety is None:
                print("沖正記錄時異常!!該記錄不存在餘額變化")
                return
            e_fund_variety.text = str((Decimal(e_fund_variety.text) - Decimal(change_dict['value'])).quantize(Decimal('0.00')))
            self.modifyBalance(change_dict['to'], Decimal(change_dict['value'])*(-1))

            self.reversalAssociatedFund(e_variation, change_dict, 'to')

    def reversalAssociatedFund(self, e_variation, change_dict, from_or_to):
        """
        Describe: 沖正關聯賬戶

        Args:
            e_variation: Element
            change_dict: dict
            from_or_to: ['from', 'to']
        """
        # print(change_dict['associatedFund'])
        if 'associatedFund' in change_dict and change_dict['associatedFund'] != 'None':
            print('執行了associatedFund沖正,操作為', from_or_to)
            if e_variation.find(".//fund[category='{}']".format(change_dict['associatedFund'])) is None:
                print("沖正記錄時異常!!該記錄不存在關聯賬戶餘額變化")
                return
            if from_or_to == 'from':
                e_fund_variety = e_variation.find(".//fund[category='{}']/out".format(change_dict['associatedFund']))
                flag = 1
            elif from_or_to == 'to':
                e_fund_variety = e_variation.find(".//fund[category='{}']/in".format(change_dict['associatedFund']))
                flag = -1
            else:
                print('未知的收支動作!')
                return
            e_fund_variety.text = str((Decimal(e_fund_variety.text) - Decimal(change_dict['value'])).quantize(Decimal('0.00')))
            self.modifyBalance(change_dict['associatedFund'], Decimal(change_dict['value'])*flag)

2. 增加更新記錄功能

2.1 功能詳述

  • 點擊更新按鈕後,獲取對應行的數據組成新記錄字典;
  • 同時根據行號獲取編輯賬本界面的self.file_parse_result屬性對應的舊記錄字典(每次更新xml文件時,該屬性都會同步);
  • 接著用舊字典數據沖正存款賬戶餘額(實現思路為新增記錄時的反向操作),再用新字典數據更新賬戶餘額,>- 最後再更新self.file_parse_result屬性。

2.2 代碼實現

    def updateTableRow(self, triggeredBtn, tableWidget):
        print("觸發了新增按鈕")
        # 獲取觸發信號的控制項所在行號
        current_row = tableWidget.indexAt(triggeredBtn.parent().pos()).row()
        if tableWidget == self.tableWidget_expense:
            const_class = expenseConst
            action = 'expense'
        elif tableWidget == self.tableWidget_income:
            const_class = incomeConst
            action = 'income'
        elif tableWidget == self.tableWidget_movement:
            const_class = movementConst
            action = 'movement'
        else:
            print('未知控制項觸發新增按鈕!')
            return
        # 獲取被更新的舊數據(文件解析後,記錄順序與表格順序相同)
        old_data_dict = self.file_parse_result[action+'s'][current_row]
        print("舊記錄內容為: \n", old_data_dict)
        # 獲取更新後的新數據
        new_data_dict = self.getExistTableCell(tableWidget, current_row, const_class)
        if new_data_dict is None:
            print("獲取更新後的數據失敗!!")
            return
        print("更新後的記錄內容為: \n", new_data_dict)

        if self.file_processor.updateRecord(old_data_dict, new_data_dict, self.dateEdit.text().replace('/', ''), action):
            print("更新成功!")
            # 把刪除結果寫入文件
            self.file_processor.writeXMLFile(self.lineEdit_file_path.text())

            # 更新self.file_parse_result
            self.file_parse_result[action+'s'][current_row] = new_data_dict
            # print(self.file_parse_result)

其中處理xml文件的方法代碼如下:

    def updateRecord(self, old_record_dict, new_record_dict, date_str, action):
        e_date = self.getSpecificDateElement(date_str)
        if isinstance(e_date, int):
            print("未找到這一天的數據!")
            return False

        if action == 'expense':
            action_path = ".//expenses"
            record_path = """.//expense[@necessity='{}'][@associatedFund='{}'][value='{}'][category='{}'][detail='{}'][describe='{}'][from='{}']""".format(
                old_record_dict['necessity'],
                old_record_dict['associatedFund'],
                old_record_dict['value'],
                old_record_dict['category'],
                old_record_dict['detail'],
                old_record_dict['describe'],
                old_record_dict['from'])
        elif action == 'income':
            action_path = ".//incomes"
            record_path = """.//income[@associatedFund='{}'][value='{}'][category='{}'][detail='{}'][describe='{}'][to='{}']""".format(
                old_record_dict['associatedFund'],
                old_record_dict['value'],
                old_record_dict['category'],
                old_record_dict['detail'],
                old_record_dict['describe'],
                old_record_dict['to'])
        elif action == 'movement':
            action_path = ".//movements"
            record_path = """.//movement[value='{}'][detail='{}'][describe='{}'][from='{}'][to='{}']""".format(
                old_record_dict['value'],
                old_record_dict['detail'],
                old_record_dict['describe'],
                old_record_dict['from'],
                old_record_dict['to'])
        else:
            print("未知的動賬操作!!")
            return False
        # print(record_path)

        e_action = e_date.find(action_path)
        e_record = e_action.find(record_path)
        if e_record is None:
            print("未找到待刪除的記錄")
            return False

        # 修改了數值則需要衝正
        if old_record_dict['value'] != new_record_dict['value']:
            # 先沖正原記錄數據
            # 在用新數據修改賬戶變化和餘額
            if action == 'movement':
                self.modifyBalance(old_record_dict['from'], Decimal(old_record_dict['value']))
                self.modifyBalance(old_record_dict['to'], Decimal(old_record_dict['value']) * (-1))
                self.modifyBalance(new_record_dict['from'], Decimal(new_record_dict['value']) * (-1))
                self.modifyBalance(new_record_dict['to'], Decimal(new_record_dict['value']))
            else:
                self.reversalVariation(old_record_dict, e_date)
                self.organizeVariation(new_record_dict, e_date)

        # 修改記錄數據

        for key in new_record_dict.keys():
            if key == 'necessity':
                e_record.attrib['necessity'] = new_record_dict['necessity']
            elif key == 'associatedFund':
                e_record.attrib['associatedFund'] = new_record_dict['associatedFund']
            else:
                e_record.find(".//"+key).text = str(new_record_dict[key])
        return True

3. 優化界面控制項(內置spinBox、增加按鈕切換日期)

3.1 功能詳述

將記錄表格數值(value)列內置QSpinBox,以避免存於文件的數值小數點位不一致;增加按鈕來控制日期的切換

3.2 效果展示


數值(value)列內置QSpinBox

增加按鈕來控制日期的切換

3.3 代碼實現

# 內置QDoubleSpinBox
spinBox = QDoubleSpinBox(decimals=2)# 設置保留小數後2位
# 設置範圍,預設為[0.0, 100.0)。必須在調整數值前設置,否則大於等於100.0時會變成99.99
spinBox.setRange(0.0, 100000000.0)
spinBox.setSingleStep(0.1)          # 設置步長
spinBox.setValue(value)             # 調整數值
tableWidget.setCellWidget(current_row, keys_list.index(key), spinBox)

# 日期切換
# 利用QDate類的addDays(int)方法
self.pushButton_prev_day.clicked.connect(lambda: self.dateEdit.setDate(self.dateEdit.date().addDays(-1)))
self.pushButton_post_day.clicked.connect(lambda: self.dateEdit.setDate(self.dateEdit.date().addDays(1)))

三、開發總結

1. 提高代碼復用性

最近幾篇都沒有技術含量,就不總結了。唯一值得一提的就是提高代碼復用性。

  • 對於支出、收入、轉移三種動賬類型,欄位大量重覆,於是創建表格時,將所有列的初始化集中到一個函數中,根據列名執行對應初始化方式。
  • 通過設置ExpenseConst、IncomeConst、MovementConst三個具有同名常量的類,更好地支持代碼復用
  • 第二章刪除和新增記錄時修改xml文件的方法deleteRecord(old_record_dict, date_str, action)updateRecord(self, old_record_dict, new_record_dict, date_str, action)是通過action區分動賬類型,避免同一個功能需要實現三個方法

本文來自博客園,作者:林風冰翼,轉載請註明原文鏈接:https://www.cnblogs.com/LinfengBingyi/p/17756104.html


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • # 浮動會帶來的影響 —— 會造成父標簽塌陷的問題 解決辦法: 方法一:自己加一個div,設置高度 方法二:利用clear屬性 #d1{ clear: left; /*該標簽的左邊(地面和空中)都不能有浮動的元素*/ } 方法三:使用通用方法 在寫HTML代碼前,先提前寫好處理浮動帶來的影響的css ...
  • 這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 在本文中,我們將探討如何使用 CSS 以最少的代碼創造出精美的 CSS 絲帶形狀,並最終實現下麵這個效果: 下麵我們使用html和css來實現這個效果。我們使用內容自適應方式佈局,不用擔心裡面的文字長度。本文介紹兩種絲帶:左側的絲帶稱為“ ...
  • 目錄Vue中的響應式對象獨立的響應式值計算變數監聽響應式變數setup方法 Vue中的響應式對象 Vue3允許在setup()中定義組件需要的數據和方法, 通過return在模板中可以直接使用 reactive方法 <body> <div id = "Application"> </div> <sc ...
  • Effect的概念起源 從輸入輸出的角度理解Effect https://link.excalidraw.com/p/readonly/KXAy7d2DlnkM8X1yps6L 編程中的Effect起源於函數式編程中純函數的概念 純函數是指在相同的輸入下,總是產生相同的輸出,並且沒有任何副作用(si ...
  • 移動互聯網風起雲涌的數十年來,App 似乎成為了企業與用戶打交道最“理所當然”的形式,更年輕一代的用戶甚至可能認為 App 就是一個“與生俱來”的事物,但隨著移動互聯網發展的高峰離去,App 面臨著發展的困境和疲態。最明顯的感知就是這幾年以微信、支付寶、抖音等“超級 App”們大行其道,占據了用戶超... ...
  • 我們是袋鼠雲數棧 UED 團隊,致力於打造優秀的一站式數據中台產品。我們始終保持工匠精神,探索前端道路,為社區積累並傳播經驗價值。 本文作者:佳嵐 回顧傳統React動畫 對於普通的 React 動畫,我們大多使用官方推薦的 react-transition-group,其提供了四個基本組件 Tra ...
  • 常用的物聯網管理系統主要有以下幾種:智能家居系統:通過物聯網技術,將家庭設備和電器互聯起來,實現智能化控制和管理的系統。智能家居系統可以實現家庭設備的遠程式控制制、智能化場景設置、安防監控等功能,方便用戶提高家居生活的便利性和舒適度。智能工廠系統:利用物聯網技術,通過互聯的工廠設備、感測器和電腦系統來 ...
  • 一、引言 在當今數字化時代,零售業正迅速發展,消費者的購物行為和期望發生了巨大的變化。為了滿足不斷增長的需求,零售企業必須構建高度靈活、穩健可靠的商品系統。 本文將深入探討零售商品系統的底層邏輯,聚焦領域驅動設計(DDD)和複雜業務系統架構經驗,揭示其在零售業務中的應用和價值。 二、面臨的挑戰 商品 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...