platform edit error checking, more comments, misc other changes

This commit is contained in:
Roland W-H 2025-02-10 22:11:57 +00:00
parent 6b4f6bb35f
commit 1748fb849f
6 changed files with 250 additions and 94 deletions

View File

@ -58,25 +58,6 @@
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="value_input">
<property name="inputMask">
<string>£0000000000</string>
</property>
<property name="text">
<string>£</string>
</property>
<property name="maxLength">
<number>11</number>
</property>
<property name="frame">
<bool>true</bool>
</property>
<property name="cursorPosition">
<number>1</number>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QLabel" name="mix_lab">
<property name="text">
@ -138,6 +119,28 @@
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QDoubleSpinBox" name="value_input">
<property name="frame">
<bool>true</bool>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::ButtonSymbols::NoButtons</enum>
</property>
<property name="specialValueText">
<string>£</string>
</property>
<property name="correctionMode">
<enum>QAbstractSpinBox::CorrectionMode::CorrectToNearestValue</enum>
</property>
<property name="prefix">
<string>£</string>
</property>
<property name="maximum">
<double>999999999.990000009536743</double>
</property>
</widget>
</item>
</layout>
</widget>
</widget>

View File

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>484</width>
<height>290</height>
<width>481</width>
<height>305</height>
</rect>
</property>
<property name="windowTitle">
@ -19,7 +19,7 @@
<x>10</x>
<y>10</y>
<width>461</width>
<height>226</height>
<height>251</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout">
@ -36,32 +36,11 @@
<number>6</number>
</property>
<property name="horizontalSpacing">
<number>15</number>
</property>
<property name="verticalSpacing">
<number>10</number>
</property>
<item row="4" column="0" colspan="2">
<widget class="QLabel" name="share_plat_max_fee_lab">
<property name="text">
<string>Share platform fee cap (£/mth)</string>
</property>
</widget>
</item>
<item row="8" column="0" colspan="2">
<widget class="QLabel" name="share_deal_reduce_amount_lab">
<property name="text">
<string>Share dealing fee discount (£)</string>
</property>
</widget>
</item>
<item row="5" column="0" colspan="2">
<widget class="QLabel" name="share_deal_fee_lab">
<property name="text">
<string>Share dealing fee (£)</string>
</property>
</widget>
</item>
<property name="verticalSpacing">
<number>5</number>
</property>
<item row="0" column="0" colspan="2">
<widget class="QLabel" name="plat_name_lab">
<property name="text">
@ -69,14 +48,12 @@
</property>
</widget>
</item>
<item row="5" column="2">
<widget class="QLineEdit" name="share_deal_fee_box"/>
</item>
<item row="1" column="2">
<widget class="QLineEdit" name="fund_deal_fee_box"/>
</item>
<item row="3" column="2">
<widget class="QLineEdit" name="share_plat_fee_box"/>
<item row="8" column="0" colspan="2">
<widget class="QLabel" name="share_deal_reduce_amount_lab">
<property name="text">
<string>Share dealing discount amount</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLineEdit" name="plat_name_box"/>
@ -84,26 +61,10 @@
<item row="3" column="0" colspan="2">
<widget class="QLabel" name="share_plat_fee_lab">
<property name="text">
<string>Share platform fee (%)</string>
<string>Share platform fee*</string>
</property>
</widget>
</item>
<item row="4" column="2">
<widget class="QLineEdit" name="share_plat_max_fee_box"/>
</item>
<item row="7" column="2">
<widget class="QLineEdit" name="share_deal_reduce_trades_box"/>
</item>
<item row="1" column="0" colspan="2">
<widget class="QLabel" name="fund_deal_fee_lab">
<property name="text">
<string>Fund dealing fee (£)</string>
</property>
</widget>
</item>
<item row="8" column="2">
<widget class="QLineEdit" name="share_deal_reduce_amount_box"/>
</item>
<item row="7" column="0" colspan="2">
<widget class="QLabel" name="share_deal_reduce_trades_lab">
<property name="text">
@ -111,14 +72,113 @@
</property>
</widget>
</item>
<item row="5" column="0" colspan="2">
<widget class="QLabel" name="share_deal_fee_lab">
<property name="text">
<string>Share dealing fee*</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QLabel" name="fund_deal_fee_lab">
<property name="text">
<string>Fund dealing fee*</string>
</property>
</widget>
</item>
<item row="4" column="0" colspan="2">
<widget class="QLabel" name="share_plat_max_fee_lab">
<property name="text">
<string>Share platform fee cap/mth</string>
</property>
</widget>
</item>
<item row="8" column="2">
<widget class="QDoubleSpinBox" name="share_deal_reduce_amount_box">
<property name="buttonSymbols">
<enum>QAbstractSpinBox::ButtonSymbols::NoButtons</enum>
</property>
<property name="correctionMode">
<enum>QAbstractSpinBox::CorrectionMode::CorrectToNearestValue</enum>
</property>
<property name="prefix">
<string>£</string>
</property>
</widget>
</item>
<item row="5" column="2">
<widget class="QDoubleSpinBox" name="share_deal_fee_box">
<property name="buttonSymbols">
<enum>QAbstractSpinBox::ButtonSymbols::NoButtons</enum>
</property>
<property name="correctionMode">
<enum>QAbstractSpinBox::CorrectionMode::CorrectToNearestValue</enum>
</property>
<property name="prefix">
<string>£</string>
</property>
</widget>
</item>
<item row="4" column="2">
<widget class="QDoubleSpinBox" name="share_plat_max_fee_box">
<property name="buttonSymbols">
<enum>QAbstractSpinBox::ButtonSymbols::NoButtons</enum>
</property>
<property name="correctionMode">
<enum>QAbstractSpinBox::CorrectionMode::CorrectToNearestValue</enum>
</property>
<property name="prefix">
<string>£</string>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QDoubleSpinBox" name="share_plat_fee_box">
<property name="buttonSymbols">
<enum>QAbstractSpinBox::ButtonSymbols::NoButtons</enum>
</property>
<property name="correctionMode">
<enum>QAbstractSpinBox::CorrectionMode::CorrectToNearestValue</enum>
</property>
<property name="suffix">
<string>%</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QDoubleSpinBox" name="fund_deal_fee_box">
<property name="buttonSymbols">
<enum>QAbstractSpinBox::ButtonSymbols::NoButtons</enum>
</property>
<property name="correctionMode">
<enum>QAbstractSpinBox::CorrectionMode::CorrectToNearestValue</enum>
</property>
<property name="prefix">
<string>£</string>
</property>
</widget>
</item>
<item row="7" column="2">
<widget class="QSpinBox" name="share_deal_reduce_trades_box">
<property name="buttonSymbols">
<enum>QAbstractSpinBox::ButtonSymbols::NoButtons</enum>
</property>
<property name="correctionMode">
<enum>QAbstractSpinBox::CorrectionMode::CorrectToNearestValue</enum>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QPushButton" name="save_but">
<property name="enabled">
<bool>false</bool>
</property>
<property name="geometry">
<rect>
<x>290</x>
<y>260</y>
<width>191</width>
<x>330</x>
<y>270</y>
<width>141</width>
<height>24</height>
</rect>
</property>
@ -126,6 +186,19 @@
<string>Save</string>
</property>
</widget>
<widget class="QLabel" name="label">
<property name="geometry">
<rect>
<x>20</x>
<y>270</y>
<width>191</width>
<height>21</height>
</rect>
</property>
<property name="text">
<string>* Indicates required field</string>
</property>
</widget>
</widget>
<tabstops>
<tabstop>plat_name_box</tabstop>
@ -133,7 +206,6 @@
<tabstop>share_plat_fee_box</tabstop>
<tabstop>share_plat_max_fee_box</tabstop>
<tabstop>share_deal_fee_box</tabstop>
<tabstop>share_deal_reduce_trades_box</tabstop>
<tabstop>share_deal_reduce_amount_box</tabstop>
<tabstop>save_but</tabstop>
</tabstops>

View File

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

View File

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

View File

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

View File

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