diff --git a/gui/main_gui.ui b/gui/main_gui.ui
index 13203cf..72eb4aa 100644
--- a/gui/main_gui.ui
+++ b/gui/main_gui.ui
@@ -58,25 +58,6 @@
- -
-
-
- £0000000000
-
-
- £
-
-
- 11
-
-
- true
-
-
- 1
-
-
-
-
@@ -138,6 +119,28 @@
+ -
+
+
+ true
+
+
+ QAbstractSpinBox::ButtonSymbols::NoButtons
+
+
+ £
+
+
+ QAbstractSpinBox::CorrectionMode::CorrectToNearestValue
+
+
+ £
+
+
+ 999999999.990000009536743
+
+
+
diff --git a/gui/platform_edit.ui b/gui/platform_edit.ui
index ba3416c..a3a70ad 100644
--- a/gui/platform_edit.ui
+++ b/gui/platform_edit.ui
@@ -6,8 +6,8 @@
0
0
- 484
- 290
+ 481
+ 305
@@ -19,7 +19,7 @@
10
10
461
- 226
+ 251
@@ -36,32 +36,11 @@
6
- 15
-
-
10
- -
-
-
- Share platform fee cap (£/mth)
-
-
-
- -
-
-
- Share dealing fee discount (£)
-
-
-
- -
-
-
- Share dealing fee (£)
-
-
-
+
+ 5
+
-
@@ -69,14 +48,12 @@
- -
-
-
- -
-
-
- -
-
+
-
+
+
+ Share dealing discount amount
+
+
-
@@ -84,26 +61,10 @@
-
- Share platform fee (%)
+ Share platform fee*
- -
-
-
- -
-
-
- -
-
-
- Fund dealing fee (£)
-
-
-
- -
-
-
-
@@ -111,14 +72,113 @@
+ -
+
+
+ Share dealing fee*
+
+
+
+ -
+
+
+ Fund dealing fee*
+
+
+
+ -
+
+
+ Share platform fee cap/mth
+
+
+
+ -
+
+
+ QAbstractSpinBox::ButtonSymbols::NoButtons
+
+
+ QAbstractSpinBox::CorrectionMode::CorrectToNearestValue
+
+
+ £
+
+
+
+ -
+
+
+ QAbstractSpinBox::ButtonSymbols::NoButtons
+
+
+ QAbstractSpinBox::CorrectionMode::CorrectToNearestValue
+
+
+ £
+
+
+
+ -
+
+
+ QAbstractSpinBox::ButtonSymbols::NoButtons
+
+
+ QAbstractSpinBox::CorrectionMode::CorrectToNearestValue
+
+
+ £
+
+
+
+ -
+
+
+ QAbstractSpinBox::ButtonSymbols::NoButtons
+
+
+ QAbstractSpinBox::CorrectionMode::CorrectToNearestValue
+
+
+ %
+
+
+
+ -
+
+
+ QAbstractSpinBox::ButtonSymbols::NoButtons
+
+
+ QAbstractSpinBox::CorrectionMode::CorrectToNearestValue
+
+
+ £
+
+
+
+ -
+
+
+ QAbstractSpinBox::ButtonSymbols::NoButtons
+
+
+ QAbstractSpinBox::CorrectionMode::CorrectToNearestValue
+
+
+
+
+ false
+
- 290
- 260
- 191
+ 330
+ 270
+ 141
24
@@ -126,6 +186,19 @@
Save
+
+
+
+ 20
+ 270
+ 191
+ 21
+
+
+
+ * Indicates required field
+
+
plat_name_box
@@ -133,7 +206,6 @@
share_plat_fee_box
share_plat_max_fee_box
share_deal_fee_box
- share_deal_reduce_trades_box
share_deal_reduce_amount_box
save_but
diff --git a/src/main.py b/src/main.py
index 9aa47e9..14c9b4d 100644
--- a/src/main.py
+++ b/src/main.py
@@ -7,6 +7,14 @@ import platform_edit
app = QApplication(sys.argv)
# Show platform edit window first, before main win
-window = platform_edit.PlatformEdit()
+# When debugging, can be useful to autofill values to save time
+if len(sys.argv) > 1:
+ if sys.argv[1] == "--DEBUG_AUTOFILL":
+ window = platform_edit.PlatformEdit(True)
+ else:
+ window = platform_edit.PlatformEdit(False)
+else:
+ window = platform_edit.PlatformEdit(False)
+
window.show()
app.exec()
diff --git a/src/main_window.py b/src/main_window.py
index 892cb1a..5d458b3 100644
--- a/src/main_window.py
+++ b/src/main_window.py
@@ -1,3 +1,4 @@
+from PyQt6.QtGui import QIntValidator
from PyQt6.QtWidgets import QMainWindow
from PyQt6 import uic
@@ -8,9 +9,11 @@ class SIPPCompare(QMainWindow):
# Receive instance of PlatformEdit() as parameter
def __init__(self, plat_edit_win):
super().__init__()
+ # Import Qt Designer UI XML file
uic.loadUi("gui/main_gui.ui", self)
# Initialise class variables
+ # Inputs
self.fund_plat_fee = 0.0
self.plat_name = ""
self.fund_deal_fee = 0.0
@@ -20,6 +23,7 @@ class SIPPCompare(QMainWindow):
self.share_deal_reduce_trades = 0.0
self.share_deal_reduce_amount = 0.0
+ # Results
self.fund_plat_fees = 0.0
self.fund_deal_fees = 0.0
self.share_plat_fees = 0.0
@@ -31,16 +35,22 @@ class SIPPCompare(QMainWindow):
# Handle events
self.calc_but.clicked.connect(self.calculate_fees)
+ # Menu bar entry (File -> Edit Platforms)
self.actionEdit_Platforms.triggered.connect(self.show_platform_edit)
+ # Update percentage mix label when slider moved
self.mix_slider.valueChanged.connect(self.update_slider_lab)
+ # Set validators
+ self.share_trades_combo.setValidator(QIntValidator(0, 999))
+ self.fund_trades_combo.setValidator(QIntValidator(0, 99))
+
# Display slider position as mix between two nums (funds/shares)
def update_slider_lab(self):
slider_val = self.mix_slider.value()
mix_lab_str = f"Investment mix (funds {slider_val}% / shares {100 - slider_val}%)"
self.mix_lab.setText(mix_lab_str)
- # Get local variables from user input
+ # 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()
@@ -52,15 +62,17 @@ class SIPPCompare(QMainWindow):
self.share_deal_reduce_amount = self.platform_win.get_share_deal_reduce_amount()
# Calculate fees
+ # TODO: Error checking on combo boxes
def calculate_fees(self):
self.init_variables()
+ # Set to zero each time to avoid persistence
self.fund_plat_fees = 0
- value_num = float(self.value_input.text()[1:]) # Filter out '£' symbol
+ value_num = float(self.value_input.value())
+ # Funds/shares mix
slider_val = self.mix_slider.value()
funds_value = (slider_val / 100) * value_num
fund_trades_num = int(self.fund_trades_combo.currentText())
self.fund_deal_fees = fund_trades_num * self.fund_deal_fee
- remaining = funds_value
for i in range(1, len(self.fund_plat_fee[0])):
band = self.fund_plat_fee[0][i]
@@ -68,11 +80,11 @@ class SIPPCompare(QMainWindow):
fee = self.fund_plat_fee[1][i]
gap = (band - prev_band)
- if remaining > gap:
+ if funds_value > gap:
self.fund_plat_fees += gap * (fee / 100)
- remaining -= gap
+ funds_value -= gap
else:
- self.fund_plat_fees += remaining * (fee / 100)
+ self.fund_plat_fees += funds_value * (fee / 100)
break
shares_value = (1 - (slider_val / 100)) * value_num
@@ -93,7 +105,9 @@ class SIPPCompare(QMainWindow):
def show_output_win(self):
# Refresh the results when new fees are calculated
self.output_win.display_output(self.fund_plat_fees, self.fund_deal_fees,
- self.share_plat_fees, self.share_deal_fees, self.plat_name)
+ self.share_plat_fees, self.share_deal_fees,
+ self.plat_name
+ )
self.output_win.show()
# Show the platform editor window (currently run-time only)
diff --git a/src/output_window.py b/src/output_window.py
index 0c7dbda..2e8a2b5 100644
--- a/src/output_window.py
+++ b/src/output_window.py
@@ -8,6 +8,7 @@ import os
class OutputWindow(QWidget):
def __init__(self):
super().__init__()
+ # Import Qt Designer UI XML file
uic.loadUi("gui/output_window.ui", self)
# Initialise class variables
diff --git a/src/platform_edit.py b/src/platform_edit.py
index 71c8664..2e51309 100644
--- a/src/platform_edit.py
+++ b/src/platform_edit.py
@@ -1,3 +1,5 @@
+from PyQt6.QtCore import QRegularExpression, QEvent, QObject, QTimer
+from PyQt6.QtGui import QRegularExpressionValidator
from PyQt6.QtWidgets import QWidget
from PyQt6 import uic
@@ -5,8 +7,9 @@ import main_window
class PlatformEdit(QWidget):
- def __init__(self):
+ def __init__(self, autofill: bool):
super().__init__()
+ # Import Qt Designer UI XML file
uic.loadUi("gui/platform_edit.ui", self)
# Initialise class variables
@@ -22,28 +25,83 @@ class PlatformEdit(QWidget):
self.share_deal_fee = 0.0
self.share_deal_reduce_trades = 0.0
self.share_deal_reduce_amount = 0.0
+ # Debugging feature: set with "--DEBUG_AUTOFILL" cmd argument
+ self.autofill = autofill
# Create main window object, passing this instance as param
self.main_win = main_window.SIPPCompare(self)
# Handle events
- # NOTE: Signal defined in Qt designer to close window when clicked
+ # NOTE: Signal defined in UI file to close window when save button clicked
self.save_but.clicked.connect(self.init_variables)
+ self.fund_deal_fee_box.valueChanged.connect(self.check_valid)
+ self.share_plat_fee_box.valueChanged.connect(self.check_valid)
+ self.share_deal_fee_box.valueChanged.connect(self.check_valid)
- # Get fee structure variables from user input
+ # Install event filter on input boxes in order to select all text on focus
+ 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)
+
+ # Set validators
+ # Regex accepts any characters that match [a-Z], [0-9] or _
+ self.plat_name_box.setValidator(
+ QRegularExpressionValidator(QRegularExpression("\\w*"))
+ )
+
+ # Get fee structure variables from user input when "Save" clicked
def init_variables(self):
- self.plat_name = self.plat_name_box.text()
- self.fund_deal_fee = float(self.fund_deal_fee_box.text())
- self.share_plat_fee = float(self.share_plat_fee_box.text()) / 100
- self.share_plat_max_fee = float(self.share_plat_max_fee_box.text())
- self.share_deal_fee = float(self.share_deal_fee_box.text())
- self.share_deal_reduce_trades = float(self.share_deal_reduce_trades_box.text())
- self.share_deal_reduce_amount = float(self.share_deal_reduce_amount_box.text())
+ # If debugging, save time by hardcoding
+ if self.autofill:
+ self.plat_name = "AJBell"
+ self.fund_deal_fee = 1.50
+ self.share_plat_fee = 0.0025
+ self.share_plat_max_fee = 3.50
+ self.share_deal_fee = 5.00
+ self.share_deal_reduce_trades = 10
+ self.share_deal_reduce_amount = 3.50
+ else:
+ self.plat_name = self.plat_name_box.text()
+ self.fund_deal_fee = float(self.fund_deal_fee_box.value())
+ self.share_plat_fee = float(self.share_plat_fee_box.value()) / 100
+ self.share_plat_max_fee = float(self.share_plat_max_fee_box.value())
+ self.share_deal_fee = float(self.share_deal_fee_box.value())
+ self.share_deal_reduce_trades = float(self.share_deal_reduce_trades_box.value())
+ self.share_deal_reduce_amount = float(self.share_deal_reduce_amount_box.value())
# Once user input is received show main window
self.main_win.show()
- # Getter functions
+ # When focus is given to an input box, select all text in it (easier to edit)
+ def eventFilter(self, obj: QObject, event: QEvent):
+ if event.type() == QEvent.Type.FocusIn:
+ # Alternative condition for % suffix - currently unused
+ #if obj.value() == 0 or obj == self.share_plat_fee_box:
+ QTimer.singleShot(0, obj.selectAll)
+ return False
+
+ # Check if all required fields have valid (non-zero) input
+ # TODO: Find a better way of doing this if possible
+ def check_valid(self):
+ values = [self.fund_deal_fee_box.value(),
+ self.share_plat_fee_box.value(),
+ self.share_deal_fee_box.value()
+ ]
+ valid = True
+
+ for value in values:
+ if value == 0:
+ valid = False
+
+ if valid:
+ self.save_but.setEnabled(True)
+ else:
+ self.save_but.setEnabled(False)
+
+ # Getter functions (is this necessary? maybe directly reading class vars would be best...)
def get_plat_name(self):
return self.plat_name