一、项目地址

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

二、新增

1. 增加删除记录功能

1.1 功能详述

  • 点击删除按钮后,获取对应行的数据组成字典,用字典的键值对匹配到对应日期的记录元素;
  • 接着用该字典数据冲正存款账户余额(实现思路为新增记录时的反向操作),同时删除记录元素;
  • 最后再更新表格。

1.2 代码实现

  1. def deleteTableRow(self, triggeredBtn, tableWidget):
  2. print("触发了删除按钮")
  3. # 获取触发信号的控件所在行号
  4. current_row = tableWidget.indexAt(triggeredBtn.parent().pos()).row()
  5. if tableWidget == self.tableWidget_expense:
  6. const_class = expenseConst
  7. action = 'expense'
  8. elif tableWidget == self.tableWidget_income:
  9. const_class = incomeConst
  10. action = 'income'
  11. elif tableWidget == self.tableWidget_movement:
  12. const_class = movementConst
  13. action = 'movement'
  14. else:
  15. print('未知控件触发新增按钮!')
  16. return
  17. # 获取待删除行的数据
  18. old_data_dict = self.getExistTableCell(tableWidget, current_row, const_class)
  19. if old_data_dict is None:
  20. print("获取待删除数据失败!!")
  21. return
  22. print("待删除记录内容为: \n", old_data_dict)
  23. if self.file_processor.deleteRecord(old_data_dict, self.dateEdit.text().replace('/', ''), action):
  24. print("删除成功!")
  25. # 把删除结果写入文件
  26. self.file_processor.writeXMLFile(self.lineEdit_file_path.text())
  27. # 更新self.file_parse_result以及记录表
  28. records_list = self.file_parse_result[action + 's']
  29. records_list.pop(current_row)
  30. self.updateRecordTable(tableWidget, records_list, const_class)

其中处理xml文件的方法代码如下:

  1. def deleteRecord(self, old_record_dict, date_str, action):
  2. e_date = self.getSpecificDateElement(date_str)
  3. if isinstance(e_date, int):
  4. print("未找到这一天的数据!")
  5. return False
  6. if action == 'expense':
  7. action_path = ".//expenses"
  8. record_path = """.//expense[@necessity='{}'][@associatedFund='{}'][value='{}'][category='{}'][detail='{}'][describe='{}'][from='{}']""".format(
  9. old_record_dict['necessity'],
  10. old_record_dict['associatedFund'],
  11. old_record_dict['value'],
  12. old_record_dict['category'],
  13. old_record_dict['detail'],
  14. old_record_dict['describe'],
  15. old_record_dict['from'])
  16. elif action == 'income':
  17. action_path = ".//incomes"
  18. record_path = """.//income[@associatedFund='{}'][value='{}'][category='{}'][detail='{}'][describe='{}'][to='{}']""".format(
  19. old_record_dict['associatedFund'],
  20. old_record_dict['value'],
  21. old_record_dict['category'],
  22. old_record_dict['detail'],
  23. old_record_dict['describe'],
  24. old_record_dict['to'])
  25. elif action == 'movement':
  26. action_path = ".//movements"
  27. record_path = """.//movement[value='{}'][detail='{}'][describe='{}'][from='{}'][to='{}']""".format(
  28. old_record_dict['value'],
  29. old_record_dict['detail'],
  30. old_record_dict['describe'],
  31. old_record_dict['from'],
  32. old_record_dict['to'])
  33. else:
  34. print("未知的动账操作!!")
  35. return False
  36. # print(record_path)
  37. e_action = e_date.find(action_path)
  38. e_record = e_action.find(record_path)
  39. if e_record is None:
  40. print("未找到待删除的记录")
  41. return False
  42. e_action.remove(e_record)
  43. if action == 'movement':
  44. self.modifyBalance(old_record_dict['from'], Decimal(old_record_dict['value']))
  45. self.modifyBalance(old_record_dict['to'], Decimal(old_record_dict['value']) * (-1))
  46. else:
  47. self.reversalVariation(old_record_dict, e_date)
  48. return True
  49. def reversalVariation(self, change_dict, e_date):
  50. """
  51. Describe: 删除某条记录时,需要冲正原来的记录,将回退对应的存款账户数值变化、以及余额
  52. Args:
  53. change_dict: dict
  54. 记录字典
  55. e_date: Element
  56. 指定日期的day元素
  57. """
  58. e_variation = e_date.find(".//variation")
  59. if e_variation is None:
  60. print("冲正记录时异常!!该记录不存在余额变化")
  61. return
  62. if 'from' in change_dict:
  63. e_fund_variety = e_variation.find(".//fund[category='{}']/out".format(change_dict['from']))
  64. if e_fund_variety is None:
  65. print("冲正记录时异常!!该记录不存在余额变化")
  66. return
  67. e_fund_variety.text = str((Decimal(e_fund_variety.text) - Decimal(change_dict['value'])).quantize(Decimal('0.00')))
  68. self.modifyBalance(change_dict['from'], Decimal(change_dict['value']))
  69. self.reversalAssociatedFund(e_variation, change_dict, 'from')
  70. if 'to' in change_dict:
  71. e_fund_variety = e_variation.find(".//fund[category='{}']/in".format(change_dict['to']))
  72. if e_fund_variety is None:
  73. print("冲正记录时异常!!该记录不存在余额变化")
  74. return
  75. e_fund_variety.text = str((Decimal(e_fund_variety.text) - Decimal(change_dict['value'])).quantize(Decimal('0.00')))
  76. self.modifyBalance(change_dict['to'], Decimal(change_dict['value'])*(-1))
  77. self.reversalAssociatedFund(e_variation, change_dict, 'to')
  78. def reversalAssociatedFund(self, e_variation, change_dict, from_or_to):
  79. """
  80. Describe: 冲正关联账户
  81. Args:
  82. e_variation: Element
  83. change_dict: dict
  84. from_or_to: ['from', 'to']
  85. """
  86. # print(change_dict['associatedFund'])
  87. if 'associatedFund' in change_dict and change_dict['associatedFund'] != 'None':
  88. print('执行了associatedFund冲正,操作为', from_or_to)
  89. if e_variation.find(".//fund[category='{}']".format(change_dict['associatedFund'])) is None:
  90. print("冲正记录时异常!!该记录不存在关联账户余额变化")
  91. return
  92. if from_or_to == 'from':
  93. e_fund_variety = e_variation.find(".//fund[category='{}']/out".format(change_dict['associatedFund']))
  94. flag = 1
  95. elif from_or_to == 'to':
  96. e_fund_variety = e_variation.find(".//fund[category='{}']/in".format(change_dict['associatedFund']))
  97. flag = -1
  98. else:
  99. print('未知的收支动作!')
  100. return
  101. e_fund_variety.text = str((Decimal(e_fund_variety.text) - Decimal(change_dict['value'])).quantize(Decimal('0.00')))
  102. self.modifyBalance(change_dict['associatedFund'], Decimal(change_dict['value'])*flag)

2. 增加更新记录功能

2.1 功能详述

  • 点击更新按钮后,获取对应行的数据组成新记录字典;
  • 同时根据行号获取编辑账本界面的self.file_parse_result属性对应的旧记录字典(每次更新xml文件时,该属性都会同步);
  • 接着用旧字典数据冲正存款账户余额(实现思路为新增记录时的反向操作),再用新字典数据更新账户余额,>- 最后再更新self.file_parse_result属性。

2.2 代码实现

  1. def updateTableRow(self, triggeredBtn, tableWidget):
  2. print("触发了新增按钮")
  3. # 获取触发信号的控件所在行号
  4. current_row = tableWidget.indexAt(triggeredBtn.parent().pos()).row()
  5. if tableWidget == self.tableWidget_expense:
  6. const_class = expenseConst
  7. action = 'expense'
  8. elif tableWidget == self.tableWidget_income:
  9. const_class = incomeConst
  10. action = 'income'
  11. elif tableWidget == self.tableWidget_movement:
  12. const_class = movementConst
  13. action = 'movement'
  14. else:
  15. print('未知控件触发新增按钮!')
  16. return
  17. # 获取被更新的旧数据(文件解析后,记录顺序与表格顺序相同)
  18. old_data_dict = self.file_parse_result[action+'s'][current_row]
  19. print("旧记录内容为: \n", old_data_dict)
  20. # 获取更新后的新数据
  21. new_data_dict = self.getExistTableCell(tableWidget, current_row, const_class)
  22. if new_data_dict is None:
  23. print("获取更新后的数据失败!!")
  24. return
  25. print("更新后的记录内容为: \n", new_data_dict)
  26. if self.file_processor.updateRecord(old_data_dict, new_data_dict, self.dateEdit.text().replace('/', ''), action):
  27. print("更新成功!")
  28. # 把删除结果写入文件
  29. self.file_processor.writeXMLFile(self.lineEdit_file_path.text())
  30. # 更新self.file_parse_result
  31. self.file_parse_result[action+'s'][current_row] = new_data_dict
  32. # print(self.file_parse_result)

其中处理xml文件的方法代码如下:

  1. def updateRecord(self, old_record_dict, new_record_dict, date_str, action):
  2. e_date = self.getSpecificDateElement(date_str)
  3. if isinstance(e_date, int):
  4. print("未找到这一天的数据!")
  5. return False
  6. if action == 'expense':
  7. action_path = ".//expenses"
  8. record_path = """.//expense[@necessity='{}'][@associatedFund='{}'][value='{}'][category='{}'][detail='{}'][describe='{}'][from='{}']""".format(
  9. old_record_dict['necessity'],
  10. old_record_dict['associatedFund'],
  11. old_record_dict['value'],
  12. old_record_dict['category'],
  13. old_record_dict['detail'],
  14. old_record_dict['describe'],
  15. old_record_dict['from'])
  16. elif action == 'income':
  17. action_path = ".//incomes"
  18. record_path = """.//income[@associatedFund='{}'][value='{}'][category='{}'][detail='{}'][describe='{}'][to='{}']""".format(
  19. old_record_dict['associatedFund'],
  20. old_record_dict['value'],
  21. old_record_dict['category'],
  22. old_record_dict['detail'],
  23. old_record_dict['describe'],
  24. old_record_dict['to'])
  25. elif action == 'movement':
  26. action_path = ".//movements"
  27. record_path = """.//movement[value='{}'][detail='{}'][describe='{}'][from='{}'][to='{}']""".format(
  28. old_record_dict['value'],
  29. old_record_dict['detail'],
  30. old_record_dict['describe'],
  31. old_record_dict['from'],
  32. old_record_dict['to'])
  33. else:
  34. print("未知的动账操作!!")
  35. return False
  36. # print(record_path)
  37. e_action = e_date.find(action_path)
  38. e_record = e_action.find(record_path)
  39. if e_record is None:
  40. print("未找到待删除的记录")
  41. return False
  42. # 修改了数值则需要冲正
  43. if old_record_dict['value'] != new_record_dict['value']:
  44. # 先冲正原记录数据
  45. # 在用新数据修改账户变化和余额
  46. if action == 'movement':
  47. self.modifyBalance(old_record_dict['from'], Decimal(old_record_dict['value']))
  48. self.modifyBalance(old_record_dict['to'], Decimal(old_record_dict['value']) * (-1))
  49. self.modifyBalance(new_record_dict['from'], Decimal(new_record_dict['value']) * (-1))
  50. self.modifyBalance(new_record_dict['to'], Decimal(new_record_dict['value']))
  51. else:
  52. self.reversalVariation(old_record_dict, e_date)
  53. self.organizeVariation(new_record_dict, e_date)
  54. # 修改记录数据
  55. for key in new_record_dict.keys():
  56. if key == 'necessity':
  57. e_record.attrib['necessity'] = new_record_dict['necessity']
  58. elif key == 'associatedFund':
  59. e_record.attrib['associatedFund'] = new_record_dict['associatedFund']
  60. else:
  61. e_record.find(".//"+key).text = str(new_record_dict[key])
  62. return True

3. 优化界面控件(内置spinBox、增加按钮切换日期)

3.1 功能详述

将记录表格数值(value)列内置QSpinBox,以避免存于文件的数值小数点位不一致;增加按钮来控制日期的切换

3.2 效果展示

数值(value)列内置QSpinBox

增加按钮来控制日期的切换

3.3 代码实现

  1. # 内置QDoubleSpinBox
  2. spinBox = QDoubleSpinBox(decimals=2)# 设置保留小数后2
  3. # 设置范围,默认为[0.0, 100.0)。必须在调整数值前设置,否则大于等于100.0时会变成99.99
  4. spinBox.setRange(0.0, 100000000.0)
  5. spinBox.setSingleStep(0.1) # 设置步长
  6. spinBox.setValue(value) # 调整数值
  7. tableWidget.setCellWidget(current_row, keys_list.index(key), spinBox)
  8. # 日期切换
  9. # 利用QDate类的addDays(int)方法
  10. self.pushButton_prev_day.clicked.connect(lambda: self.dateEdit.setDate(self.dateEdit.date().addDays(-1)))
  11. 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区分动账类型,避免同一个功能需要实现三个方法

【日常收支账本】【Day05】编辑账本界面增加删除、更新记录功能——提高代码复用性的更多相关文章

  1. 2016-1-7第一个完整APP 私人通讯录的实现 6:在联系人界面增加删除联系人的功能

    一:在viewDidLoad方法中代码添加一个UIBarButtonItem,并将其的类型设置成垃圾桶,代码如下: - (void)viewDidLoad { [super viewDidLoad]; ...

  2. 使用Vue-TreeSelect组件的时候,用watch变量方式解决弹出编辑对话框界面无法触发更新的问题

    在前篇随笔<使用Vue-TreeSelect组件实现公司-部门-人员级联下拉列表的处理>中介绍了Vue-TreeSelect组件的使用,包括使用v-modal绑定值,normalizer ...

  3. MFC编辑框接收数据动态更新与刷新方法代码示例-如何让编辑框内容实时更新

    MFC编辑框接收数据动态更新与刷新方法代码示例-如何让编辑框内容实时更新 关键代码: //发送数据通知 //from txwtech@163.com LRESULT CCommSampleDlg::O ...

  4. C# 增加 删除 更新 方法

    /// <summary> /// 增加一条数据 /// </summary> public int Add(string 表名,string 参数,string 参数值) { ...

  5. HAproxy增加日志记录功能和自定义日志输出内容、格式

    http://blog.51cto.com/eric1/1854574 一.增加haproxy日志记录功能   1.1 由于数据分析的需要,我们必须打开haproxy日志,记录相关信息. 在配置前,我 ...

  6. PR 审批界面增加显示项方法

    PR 审批界面增加显示项 解决方法 Step 1:       进入审批界面: Step 2:       在上图中,点击左下角'About this Page'查看数据源 点击上图中'Expand ...

  7. UITableView 编辑模式(增加-删除-移动---自定义左滑 title)

    - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typica ...

  8. 小程序实践(十):textarea实现简单的编辑文本界面

    textarea是官方的原生组件,用于多行输入 简单的例子,监听文本内容.长度,以及设置最大可输入文本长度 wxml <view class='textarea-Style'> <t ...

  9. UITableView 编辑模式(增加-删除-移动---自定义左滑 title) xib cell

    参考:  http://www.open-open.com/lib/view/open1430008922468.html - (void)viewDidLoad { [super viewDidLo ...

  10. Android 高仿微信(QQ)滑动弹出编辑、删除菜单效果,增加下拉刷新功能

    不可否认,微信.QQ列表的滑动删除.编辑功能着实很经典(从IOS那边模仿过来的),然.Android这边,对列表的操作,其实大多还停留上下文菜单来实现. Android如何实现list item的滑动 ...

随机推荐

  1. switch写法详解

    我们在开发项目中经常遇到对数据的判断进行相应的逻辑(if..else  ,三元运算等),Switch 语句用来选择多个需要执行的代码块 ,一定程度上简化了if....else 1. 语法 switch ...

  2. 基于Avalonia 11.0.0+ReactiveUI 的跨平台项目开发2-功能开发

    基于Avalonia 11.0.0+ReactiveUI 的跨平台项目开发2-功能开发 项目简介:目标是开发一个跨平台的AI聊天和其他功能的客户端平台.目的来学习和了解Avalonia.将这个项目部署 ...

  3. C语言链表实现(郝斌数链表学习笔记)

    #include "stdafx.h" #include<stdio.h> #include<stdlib.h> typedef struct Node { ...

  4. 深度系统安装wine

    step1: 输入命令: sudo dpkg --add-architecture i386 step2: 1.切换成管理员权限: sudo su 2.打开源文件 vi /etc/apt/source ...

  5. vs(visual stuiod)中vc++工程的Filter和Folder及vcxproj知识

    vs中创建Filter 在一个新项目中右键 - Add - New,默认只有一选项 New Filter. 创建出来的Filter可以理解为是VS的过滤器(虚拟目录),它不会在本地的磁盘上新建目录,而 ...

  6. NativeBuferring,一种零分配的数据类型[下篇]

    上文说到Unmanaged.BufferedBinary和BufferedString是NativeBuffering支持的三个基本数据类型,其实我们也可以说NativeBuffering只支持Unm ...

  7. maven系列:简介和安装配置(Mac、Linux、Windows、settings.xml、IDEA配置)

    目录 一.简介 二.安装 三.配置 Mac配置 Centos配置 Window配置 settings.xml配置 IDEA配置 一.简介 官网:https://maven.apache.org mav ...

  8. 论文解读(ECACL)《ECACL: A Holistic Framework for Semi-Supervised Domain Adaptation》

    Note:[ wechat:Y466551 | 付费咨询,非诚勿扰 ] 论文信息 论文标题:ECACL: A Holistic Framework for Semi-Supervised Domain ...

  9. SSM登录操作

    1.编写实体类 略 2. 写mapper映射文件 通过名字查询 通过ID主键查询... 略 写dao CRUD相关抽象方法 List<Student> getAll(); Student ...

  10. GrapeCity Documents V6.0 Update 2发布,新增支持SpreadJS的.sjs文件格式

    近日,GrapeCity Documents 正式迎来其V6.2 的发布更新,能够支持 SpreadJS 中 .sjs 类型的文件.这一重大更新将为用户带来更多地惊喜. .sjs文件有两个关键优势:空 ...