From 36a36f5437441b3416cec10c84a8181b89716ac0 Mon Sep 17 00:00:00 2001 From: Roland W-H Date: Tue, 11 Feb 2025 18:11:54 +0000 Subject: [PATCH] implemented optional fields - needs further work --- src/main_window.py | 42 +++++++++++++++++++++++--------- src/output_window.py | 12 ++++++++-- src/platform_edit.py | 57 ++++++++++++++++++++++++++++++++------------ 3 files changed, 83 insertions(+), 28 deletions(-) diff --git a/src/main_window.py b/src/main_window.py index 14060e3..a012427 100644 --- a/src/main_window.py +++ b/src/main_window.py @@ -14,6 +14,7 @@ class SIPPCompare(QMainWindow): # Initialise class variables # Inputs + self.optional_boxes = [] self.fund_plat_fee = 0.0 self.plat_name = "" self.fund_deal_fee = 0.0 @@ -62,17 +63,34 @@ class SIPPCompare(QMainWindow): # Get variables from platform editor input fields def init_variables(self): - self.plat_name = self.platform_win.get_plat_name() - self.fund_plat_fee = self.platform_win.get_fund_plat_fee() - self.fund_deal_fee = self.platform_win.get_fund_deal_fee() - self.share_plat_fee = self.platform_win.get_share_plat_fee() - self.share_plat_max_fee = self.platform_win.get_share_plat_max_fee() - self.share_deal_fee = self.platform_win.get_share_deal_fee() - self.share_deal_reduce_trades = self.platform_win.get_share_deal_reduce_trades() - self.share_deal_reduce_amount = self.platform_win.get_share_deal_reduce_amount() + self.optional_boxes = self.platform_win.get_optional_boxes() + self.fund_plat_fee = self.platform_win.get_fund_plat_fee() + self.fund_deal_fee = self.platform_win.get_fund_deal_fee() + self.share_plat_fee = self.platform_win.get_share_plat_fee() + self.share_deal_fee = self.platform_win.get_share_deal_fee() + + # TODO: This is HORRIBLE - find better way of doing it! (maybe enums?) + if self.optional_boxes[0]: + self.plat_name = self.platform_win.get_plat_name() + else: + self.plat_name = None + + if self.optional_boxes[1]: + self.share_plat_max_fee = self.platform_win.get_share_plat_max_fee() + else: + self.share_plat_max_fee = None + + if self.optional_boxes[2]: + self.share_deal_reduce_trades = self.platform_win.get_share_deal_reduce_trades() + else: + self.share_deal_reduce_trades = None + + if self.optional_boxes[3]: + self.share_deal_reduce_amount = self.platform_win.get_share_deal_reduce_amount() + else: + self.share_deal_reduce_amount = None # Calculate fees - # TODO: Error checking on combo boxes def calculate_fees(self): self.init_variables() # Set to zero each time to avoid persistence @@ -98,13 +116,15 @@ class SIPPCompare(QMainWindow): break shares_value = (1 - (slider_val / 100)) * value_num - if (self.share_plat_fee * shares_value / 12) > self.share_plat_max_fee: + if self.share_plat_max_fee is not None and \ + (self.share_plat_fee * shares_value / 12) > self.share_plat_max_fee: self.share_plat_fees = self.share_plat_max_fee * 12 else: self.share_plat_fees = self.share_plat_fee * shares_value share_trades_num = int(self.share_trades_combo.currentText()) - if (share_trades_num / 12) >= self.share_deal_reduce_trades: + if self.share_deal_reduce_trades is not None and \ + (share_trades_num / 12) >= self.share_deal_reduce_trades: self.share_deal_fees = self.share_deal_reduce_amount * share_trades_num else: self.share_deal_fees = self.share_deal_fee * share_trades_num diff --git a/src/output_window.py b/src/output_window.py index e9fcfb7..1765a51 100644 --- a/src/output_window.py +++ b/src/output_window.py @@ -23,7 +23,12 @@ class OutputWindow(QWidget): cur_time = datetime.datetime.now() if not os.path.exists("output"): os.makedirs("output") - filename_str = f"output/{self.platform_name}-{cur_time.year}.{cur_time.month}.{cur_time.day}.txt" + filename_str = f"output/" + if self.platform_name is not None: + filename_str += f"{self.platform_name}" + else: + filename_str += "Unnamed" + filename_str += f"-{cur_time.year}.{cur_time.month}.{cur_time.day}.txt" output_file = open(filename_str, "wt", encoding = "utf-8") output_file.write(self.results_str) @@ -31,7 +36,10 @@ class OutputWindow(QWidget): def display_output(self, fund_plat_fees: float, fund_deal_fees: float, share_plat_fees: float, share_deal_fees: float, plat_name: str): self.platform_name = plat_name - self.results_str = f"Fees breakdown (Platform \"{self.platform_name}\"):" + if self.platform_name is not None: + self.results_str = f"Fees breakdown (Platform \"{self.platform_name}\"):" + else: + self.results_str = f"Fees breakdown:" self.results_str += "\n\nPlatform fees:" # :.2f is used in order to display 2 decimal places (currency form) diff --git a/src/platform_edit.py b/src/platform_edit.py index 846551d..2c076ef 100644 --- a/src/platform_edit.py +++ b/src/platform_edit.py @@ -1,6 +1,6 @@ from PyQt6.QtCore import QRegularExpression, QEvent, QObject, QTimer from PyQt6.QtGui import QRegularExpressionValidator -from PyQt6.QtWidgets import QWidget, QLayout +from PyQt6.QtWidgets import QWidget from PyQt6 import uic import main_window @@ -13,6 +13,9 @@ class PlatformEdit(QWidget): uic.loadUi("gui/platform_edit.ui", self) # Initialise class variables + # Create main window object, passing this instance as param + self.main_win = main_window.SIPPCompare(self) + # TODO: Make fund_plat_fee user-defined self.fund_plat_fee = [ [0, 250000, 1000000, 2000000], @@ -50,35 +53,42 @@ class PlatformEdit(QWidget): self.share_deal_reduce_amount_check ] - # Create main window object, passing this instance as param - self.main_win = main_window.SIPPCompare(self) + self.check_boxes_ticked = [ + True, + False, + False, + False + ] # Handle events - # NOTE: Signal defined in UI file to close window when save button clicked - self.save_but.clicked.connect(self.init_variables) - for field in self.required_fields: field.valueChanged.connect(self.check_valid) + field.installEventFilter(self) for field in self.optional_fields: - if field.staticMetaObject.className() == "QLineEdit": + field_type = field.staticMetaObject.className() + if field_type == "QLineEdit": field.textChanged.connect(self.check_valid) - elif field.staticMetaObject.className() == "QDoubleSpinBox": + elif field_type == "QDoubleSpinBox" or field_type == "QSpinBox": field.valueChanged.connect(self.check_valid) + field.installEventFilter(self) for check_box in self.optional_check_boxes: check_box.checkStateChanged.connect(self.check_valid) + # NOTE: Signal defined in UI file to close window when save button clicked + self.save_but.clicked.connect(self.init_variables) + # Install event filter on input boxes in order to select all text on focus + # TODO: Seems like my eventFilter() override is capturing all events - need to stop this + """ self.fund_deal_fee_box.installEventFilter(self) self.share_plat_fee_box.installEventFilter(self) self.share_plat_max_fee_box.installEventFilter(self) self.share_deal_fee_box.installEventFilter(self) self.share_deal_reduce_trades_box.installEventFilter(self) self.share_deal_reduce_amount_box.installEventFilter(self) - - #for check_box in self.optional_check_boxes: - # check_box.installEventFilter(self) + """ # Set validators # Regex accepts any characters that match [a-Z], [0-9] or _ @@ -117,21 +127,34 @@ class PlatformEdit(QWidget): QTimer.singleShot(0, obj.selectAll) return False - # Check if all required fields have valid (non-zero) input + # This method does multiple things in order to validate the user's inputs: + # 1) Check all required fields have a non-zero value + # 2) If an optional checkbox is toggled: toggle editing of the corresponding field + # 3) Check all optional fields the user has picked have a non-zero value + # 4) If the above two conditions are met (1 & 3), make the 'Save' button clickable + # 5) Keep a record of which optional fields the user has chosen to fill in + # It's called when an optional check box emits a checkStateChanged() signal + # It's also called when any field emits a textChanged() or valueChanged() signal def check_valid(self): valid = True + # Check all required fields have a non-zero value for field in self.required_fields: if field.value() == 0: valid = False - for check_box in self.optional_check_boxes: - check_box_pos = self.gridLayout.getItemPosition(self.gridLayout.indexOf(check_box)) + for i in range(len(self.optional_check_boxes)): + # Find the coordinates of the input box corresponding to the checkbox + # It will be on the same row, in the column to the left (-1) + check_box_idx = self.gridLayout.indexOf(self.optional_check_boxes[i]) + check_box_pos = self.gridLayout.getItemPosition(check_box_idx) input_box_pos = list(check_box_pos)[:2] input_box_pos[1] -= 1 + # Return copy of input field widget from its coordinates input_box_item = self.gridLayout.itemAtPosition(input_box_pos[0], input_box_pos[1]).widget() - if check_box.isChecked(): + if self.optional_check_boxes[i].isChecked(): input_box_item.setEnabled(True) + self.check_boxes_ticked[i] = True input_box_type = input_box_item.staticMetaObject.className() if input_box_type == "QLineEdit": if input_box_item.text() == "": @@ -141,6 +164,7 @@ class PlatformEdit(QWidget): valid = False else: input_box_item.setEnabled(False) + self.check_boxes_ticked[i] = False if valid: self.save_but.setEnabled(True) @@ -148,6 +172,9 @@ class PlatformEdit(QWidget): self.save_but.setEnabled(False) # Getter functions (is this necessary? maybe directly reading class vars would be best...) + def get_optional_boxes(self): + return self.check_boxes_ticked + def get_plat_name(self): return self.plat_name