mirror of
https://github.com/RolandWH/SIPPCompare.git
synced 2025-09-03 16:41:28 +01:00
Compare commits
23 Commits
348b027ede
...
submission
Author | SHA1 | Date | |
---|---|---|---|
4b8bdfbeea | |||
09f8b7bba8 | |||
abbcbe41d0 | |||
75e26e229a | |||
5eb0c3c4a3 | |||
78323c2ad8 | |||
2422395759 | |||
7cb41652b4 | |||
ae01d912e1 | |||
9a8b0045fa | |||
9e79d986e4 | |||
71d9590205 | |||
3b6b75ee48 | |||
|
ca8f4409c1 | ||
ba204becc9 | |||
42afd128e5 | |||
629ea6833d | |||
e2ca298919 | |||
593dec96d1 | |||
fa74e9e8da | |||
16802648bc | |||
c151b19a3c | |||
f2a0735972 |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -2,8 +2,8 @@
|
|||||||
.idea/workspace.xml
|
.idea/workspace.xml
|
||||||
.idea/discord.xml
|
.idea/discord.xml
|
||||||
.idea/encodings.xml
|
.idea/encodings.xml
|
||||||
build/
|
*build/
|
||||||
dist/
|
*dist/
|
||||||
output/
|
output/
|
||||||
src/*/__pycache__/
|
src/*/__pycache__/
|
||||||
src/__pycache__/
|
src/__pycache__/
|
2
.idea/SIPPCompare.iml
generated
2
.idea/SIPPCompare.iml
generated
@@ -3,6 +3,8 @@
|
|||||||
<component name="NewModuleRootManager">
|
<component name="NewModuleRootManager">
|
||||||
<content url="file://$MODULE_DIR$">
|
<content url="file://$MODULE_DIR$">
|
||||||
<excludeFolder url="file://$MODULE_DIR$/.venv" />
|
<excludeFolder url="file://$MODULE_DIR$/.venv" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/build" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/output" />
|
||||||
</content>
|
</content>
|
||||||
<orderEntry type="jdk" jdkName="Python 3.13 (SIPPCompare)" jdkType="Python SDK" />
|
<orderEntry type="jdk" jdkName="Python 3.13 (SIPPCompare)" jdkType="Python SDK" />
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
@@ -1,45 +0,0 @@
|
|||||||
# -*- mode: python ; coding: utf-8 -*-
|
|
||||||
|
|
||||||
|
|
||||||
a = Analysis(
|
|
||||||
['src\\main.py'],
|
|
||||||
pathex=[],
|
|
||||||
binaries=[],
|
|
||||||
datas=[('gui/*.ui', 'gui'), ('icon2.ico', '.')],
|
|
||||||
hiddenimports=[],
|
|
||||||
hookspath=[],
|
|
||||||
hooksconfig={},
|
|
||||||
runtime_hooks=[],
|
|
||||||
excludes=[],
|
|
||||||
noarchive=False,
|
|
||||||
optimize=0,
|
|
||||||
)
|
|
||||||
pyz = PYZ(a.pure)
|
|
||||||
|
|
||||||
exe = EXE(
|
|
||||||
pyz,
|
|
||||||
a.scripts,
|
|
||||||
[],
|
|
||||||
exclude_binaries=True,
|
|
||||||
name='SIPPCompare',
|
|
||||||
debug=False,
|
|
||||||
bootloader_ignore_signals=False,
|
|
||||||
strip=False,
|
|
||||||
upx=True,
|
|
||||||
console=False,
|
|
||||||
disable_windowed_traceback=False,
|
|
||||||
argv_emulation=False,
|
|
||||||
target_arch=None,
|
|
||||||
codesign_identity=None,
|
|
||||||
entitlements_file=None,
|
|
||||||
icon="icon2.ico"
|
|
||||||
)
|
|
||||||
coll = COLLECT(
|
|
||||||
exe,
|
|
||||||
a.binaries,
|
|
||||||
a.datas,
|
|
||||||
strip=False,
|
|
||||||
upx=True,
|
|
||||||
upx_exclude=[],
|
|
||||||
name='SIPPCompare',
|
|
||||||
)
|
|
@@ -10,6 +10,18 @@
|
|||||||
<height>100</height>
|
<height>100</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>300</width>
|
||||||
|
<height>100</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>300</width>
|
||||||
|
<height>100</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
<string>Name Platform</string>
|
<string>Name Platform</string>
|
||||||
</property>
|
</property>
|
||||||
|
@@ -25,9 +25,6 @@
|
|||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
<string>SIPPCompare</string>
|
<string>SIPPCompare</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="tabShape">
|
|
||||||
<enum>QTabWidget::TabShape::Rounded</enum>
|
|
||||||
</property>
|
|
||||||
<widget class="QWidget" name="centralwidget">
|
<widget class="QWidget" name="centralwidget">
|
||||||
<widget class="QWidget" name="formLayoutWidget">
|
<widget class="QWidget" name="formLayoutWidget">
|
||||||
<property name="geometry">
|
<property name="geometry">
|
||||||
@@ -35,7 +32,7 @@
|
|||||||
<x>10</x>
|
<x>10</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>401</width>
|
<width>401</width>
|
||||||
<height>184</height>
|
<height>188</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QFormLayout" name="formLayout">
|
<layout class="QFormLayout" name="formLayout">
|
||||||
@@ -70,9 +67,6 @@
|
|||||||
<pointsize>11</pointsize>
|
<pointsize>11</pointsize>
|
||||||
</font>
|
</font>
|
||||||
</property>
|
</property>
|
||||||
<property name="frame">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
<property name="buttonSymbols">
|
<property name="buttonSymbols">
|
||||||
<enum>QAbstractSpinBox::ButtonSymbols::NoButtons</enum>
|
<enum>QAbstractSpinBox::ButtonSymbols::NoButtons</enum>
|
||||||
</property>
|
</property>
|
||||||
@@ -145,18 +139,6 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="4" column="1">
|
|
||||||
<widget class="QComboBox" name="fund_trades_combo">
|
|
||||||
<property name="font">
|
|
||||||
<font>
|
|
||||||
<pointsize>11</pointsize>
|
|
||||||
</font>
|
|
||||||
</property>
|
|
||||||
<property name="editable">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="5" column="0" colspan="2">
|
<item row="5" column="0" colspan="2">
|
||||||
<widget class="QPushButton" name="calc_but">
|
<widget class="QPushButton" name="calc_but">
|
||||||
<property name="enabled">
|
<property name="enabled">
|
||||||
@@ -173,14 +155,35 @@
|
|||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="1">
|
<item row="3" column="1">
|
||||||
<widget class="QComboBox" name="share_trades_combo">
|
<widget class="FastEditQSpinBox" name="share_trades_box">
|
||||||
<property name="font">
|
<property name="font">
|
||||||
<font>
|
<font>
|
||||||
<pointsize>11</pointsize>
|
<pointsize>11</pointsize>
|
||||||
</font>
|
</font>
|
||||||
</property>
|
</property>
|
||||||
<property name="editable">
|
<property name="correctionMode">
|
||||||
<bool>true</bool>
|
<enum>QAbstractSpinBox::CorrectionMode::CorrectToNearestValue</enum>
|
||||||
|
</property>
|
||||||
|
<property name="prefix">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<number>999</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="1">
|
||||||
|
<widget class="FastEditQSpinBox" name="fund_trades_box">
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<pointsize>11</pointsize>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="correctionMode">
|
||||||
|
<enum>QAbstractSpinBox::CorrectionMode::CorrectToNearestValue</enum>
|
||||||
|
</property>
|
||||||
|
<property name="prefix">
|
||||||
|
<string/>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@@ -188,9 +191,6 @@
|
|||||||
</widget>
|
</widget>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QStatusBar" name="statusbar">
|
<widget class="QStatusBar" name="statusbar">
|
||||||
<property name="enabled">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
<property name="font">
|
<property name="font">
|
||||||
<font>
|
<font>
|
||||||
<pointsize>10</pointsize>
|
<pointsize>10</pointsize>
|
||||||
@@ -235,11 +235,18 @@
|
|||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
</widget>
|
</widget>
|
||||||
|
<customwidgets>
|
||||||
|
<customwidget>
|
||||||
|
<class>FastEditQSpinBox</class>
|
||||||
|
<extends>QSpinBox</extends>
|
||||||
|
<header>widgets/fastedit_spinbox</header>
|
||||||
|
</customwidget>
|
||||||
|
</customwidgets>
|
||||||
<tabstops>
|
<tabstops>
|
||||||
<tabstop>value_input</tabstop>
|
<tabstop>value_input</tabstop>
|
||||||
<tabstop>mix_slider</tabstop>
|
<tabstop>mix_slider</tabstop>
|
||||||
<tabstop>share_trades_combo</tabstop>
|
<tabstop>share_trades_box</tabstop>
|
||||||
<tabstop>fund_trades_combo</tabstop>
|
<tabstop>fund_trades_box</tabstop>
|
||||||
<tabstop>calc_but</tabstop>
|
<tabstop>calc_but</tabstop>
|
||||||
</tabstops>
|
</tabstops>
|
||||||
<resources/>
|
<resources/>
|
||||||
|
@@ -7,9 +7,21 @@
|
|||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>1330</width>
|
<width>1330</width>
|
||||||
<height>630</height>
|
<height>685</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>1330</width>
|
||||||
|
<height>685</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>1330</width>
|
||||||
|
<height>685</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
<string>Results</string>
|
<string>Results</string>
|
||||||
</property>
|
</property>
|
||||||
@@ -23,6 +35,85 @@
|
|||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
|
<widget class="QPushButton" name="save_graph_but">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>1231</x>
|
||||||
|
<y>642</y>
|
||||||
|
<width>91</width>
|
||||||
|
<height>31</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<pointsize>10</pointsize>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Save graph</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QSlider" name="time_slider">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>370</x>
|
||||||
|
<y>635</y>
|
||||||
|
<width>581</width>
|
||||||
|
<height>41</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="minimum">
|
||||||
|
<number>1</number>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<number>20</number>
|
||||||
|
</property>
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Orientation::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="tickPosition">
|
||||||
|
<enum>QSlider::TickPosition::TicksAbove</enum>
|
||||||
|
</property>
|
||||||
|
<property name="tickInterval">
|
||||||
|
<number>1</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QPushButton" name="save_csv_but">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>1134</x>
|
||||||
|
<y>642</y>
|
||||||
|
<width>91</width>
|
||||||
|
<height>31</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<pointsize>10</pointsize>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Save CSV</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QLabel" name="time_lab">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>10</x>
|
||||||
|
<y>640</y>
|
||||||
|
<width>341</width>
|
||||||
|
<height>31</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<pointsize>11</pointsize>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Fees over 1 year(s) (assuming no change in value)</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
</widget>
|
</widget>
|
||||||
<customwidgets>
|
<customwidgets>
|
||||||
<customwidget>
|
<customwidget>
|
||||||
@@ -32,6 +123,11 @@
|
|||||||
<container>1</container>
|
<container>1</container>
|
||||||
</customwidget>
|
</customwidget>
|
||||||
</customwidgets>
|
</customwidgets>
|
||||||
|
<tabstops>
|
||||||
|
<tabstop>time_slider</tabstop>
|
||||||
|
<tabstop>save_csv_but</tabstop>
|
||||||
|
<tabstop>save_graph_but</tabstop>
|
||||||
|
</tabstops>
|
||||||
<resources/>
|
<resources/>
|
||||||
<connections/>
|
<connections/>
|
||||||
</ui>
|
</ui>
|
||||||
|
@@ -2,27 +2,36 @@
|
|||||||
<ui version="4.0">
|
<ui version="4.0">
|
||||||
<class>PlatformEdit</class>
|
<class>PlatformEdit</class>
|
||||||
<widget class="QWidget" name="PlatformEdit">
|
<widget class="QWidget" name="PlatformEdit">
|
||||||
<property name="windowModality">
|
|
||||||
<enum>Qt::WindowModality::ApplicationModal</enum>
|
|
||||||
</property>
|
|
||||||
<property name="geometry">
|
<property name="geometry">
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>630</width>
|
<width>630</width>
|
||||||
<height>567</height>
|
<height>580</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>630</width>
|
||||||
|
<height>580</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>630</width>
|
||||||
|
<height>580</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
<string>Platform Editor</string>
|
<string>Platform Editor</string>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QWidget" name="gridLayoutWidget">
|
<widget class="QWidget" name="gridLayoutWidget">
|
||||||
<property name="geometry">
|
<property name="geometry">
|
||||||
<rect>
|
<rect>
|
||||||
<x>10</x>
|
<x>13</x>
|
||||||
<y>20</y>
|
<y>20</y>
|
||||||
<width>611</width>
|
<width>616</width>
|
||||||
<height>241</height>
|
<height>242</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QGridLayout" name="gridLayout">
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
@@ -42,10 +51,13 @@
|
|||||||
<number>10</number>
|
<number>10</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="verticalSpacing">
|
<property name="verticalSpacing">
|
||||||
<number>5</number>
|
<number>6</number>
|
||||||
</property>
|
</property>
|
||||||
<item row="0" column="3">
|
<item row="0" column="3">
|
||||||
<widget class="QCheckBox" name="plat_name_check">
|
<widget class="QCheckBox" name="plat_name_check">
|
||||||
|
<property name="text">
|
||||||
|
<string> </string>
|
||||||
|
</property>
|
||||||
<property name="checked">
|
<property name="checked">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
@@ -218,7 +230,7 @@
|
|||||||
</font>
|
</font>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Fund dealing fee*</string>
|
<string>Fund dealing fee</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@@ -324,7 +336,7 @@
|
|||||||
<property name="geometry">
|
<property name="geometry">
|
||||||
<rect>
|
<rect>
|
||||||
<x>482</x>
|
<x>482</x>
|
||||||
<y>534</y>
|
<y>545</y>
|
||||||
<width>141</width>
|
<width>141</width>
|
||||||
<height>24</height>
|
<height>24</height>
|
||||||
</rect>
|
</rect>
|
||||||
@@ -342,7 +354,7 @@
|
|||||||
<property name="geometry">
|
<property name="geometry">
|
||||||
<rect>
|
<rect>
|
||||||
<x>8</x>
|
<x>8</x>
|
||||||
<y>262</y>
|
<y>549</y>
|
||||||
<width>191</width>
|
<width>191</width>
|
||||||
<height>21</height>
|
<height>21</height>
|
||||||
</rect>
|
</rect>
|
||||||
@@ -354,7 +366,7 @@
|
|||||||
<widget class="QLabel" name="active_lab">
|
<widget class="QLabel" name="active_lab">
|
||||||
<property name="geometry">
|
<property name="geometry">
|
||||||
<rect>
|
<rect>
|
||||||
<x>577</x>
|
<x>572</x>
|
||||||
<y>10</y>
|
<y>10</y>
|
||||||
<width>61</width>
|
<width>61</width>
|
||||||
<height>16</height>
|
<height>16</height>
|
||||||
@@ -370,16 +382,28 @@
|
|||||||
<widget class="QWidget" name="gridLayoutWidget_2">
|
<widget class="QWidget" name="gridLayoutWidget_2">
|
||||||
<property name="geometry">
|
<property name="geometry">
|
||||||
<rect>
|
<rect>
|
||||||
<x>11</x>
|
<x>19</x>
|
||||||
<y>309</y>
|
<y>307</y>
|
||||||
<width>611</width>
|
<width>591</width>
|
||||||
<height>31</height>
|
<height>40</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QGridLayout" name="gridLayout_2">
|
<layout class="QGridLayout" name="gridLayout_2">
|
||||||
<property name="verticalSpacing">
|
<property name="leftMargin">
|
||||||
<number>0</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="spacing">
|
||||||
|
<number>6</number>
|
||||||
|
</property>
|
||||||
<item row="0" column="3">
|
<item row="0" column="3">
|
||||||
<widget class="FastEditQDoubleSpinBox" name="first_tier_fee_box">
|
<widget class="FastEditQDoubleSpinBox" name="first_tier_fee_box">
|
||||||
<property name="font">
|
<property name="font">
|
||||||
@@ -455,7 +479,7 @@
|
|||||||
<property name="geometry">
|
<property name="geometry">
|
||||||
<rect>
|
<rect>
|
||||||
<x>532</x>
|
<x>532</x>
|
||||||
<y>481</y>
|
<y>487</y>
|
||||||
<width>91</width>
|
<width>91</width>
|
||||||
<height>24</height>
|
<height>24</height>
|
||||||
</rect>
|
</rect>
|
||||||
@@ -476,7 +500,7 @@
|
|||||||
<property name="geometry">
|
<property name="geometry">
|
||||||
<rect>
|
<rect>
|
||||||
<x>440</x>
|
<x>440</x>
|
||||||
<y>481</y>
|
<y>487</y>
|
||||||
<width>91</width>
|
<width>91</width>
|
||||||
<height>24</height>
|
<height>24</height>
|
||||||
</rect>
|
</rect>
|
||||||
@@ -493,8 +517,8 @@
|
|||||||
<widget class="QLabel" name="val_above_lab">
|
<widget class="QLabel" name="val_above_lab">
|
||||||
<property name="geometry">
|
<property name="geometry">
|
||||||
<rect>
|
<rect>
|
||||||
<x>6</x>
|
<x>10</x>
|
||||||
<y>479</y>
|
<y>486</y>
|
||||||
<width>421</width>
|
<width>421</width>
|
||||||
<height>21</height>
|
<height>21</height>
|
||||||
</rect>
|
</rect>
|
||||||
@@ -508,16 +532,34 @@
|
|||||||
<string>on the value above £ there is no charge</string>
|
<string>on the value above £ there is no charge</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
|
<widget class="QLabel" name="fund_plat_fee_lab">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>10</x>
|
||||||
|
<y>285</y>
|
||||||
|
<width>151</width>
|
||||||
|
<height>16</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<pointsize>11</pointsize>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Fund platform fee*</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
</widget>
|
</widget>
|
||||||
<customwidgets>
|
<customwidgets>
|
||||||
<customwidget>
|
<customwidget>
|
||||||
<class>FastEditQDoubleSpinBox</class>
|
<class>FastEditQSpinBox</class>
|
||||||
<extends>QDoubleSpinBox</extends>
|
<extends>QSpinBox</extends>
|
||||||
<header>widgets/fastedit_spinbox</header>
|
<header>widgets/fastedit_spinbox</header>
|
||||||
</customwidget>
|
</customwidget>
|
||||||
<customwidget>
|
<customwidget>
|
||||||
<class>FastEditQSpinBox</class>
|
<class>FastEditQDoubleSpinBox</class>
|
||||||
<extends>QSpinBox</extends>
|
<extends>QDoubleSpinBox</extends>
|
||||||
<header>widgets/fastedit_spinbox</header>
|
<header>widgets/fastedit_spinbox</header>
|
||||||
</customwidget>
|
</customwidget>
|
||||||
</customwidgets>
|
</customwidgets>
|
||||||
|
@@ -2,17 +2,26 @@
|
|||||||
<ui version="4.0">
|
<ui version="4.0">
|
||||||
<class>PlatformList</class>
|
<class>PlatformList</class>
|
||||||
<widget class="QWidget" name="PlatformList">
|
<widget class="QWidget" name="PlatformList">
|
||||||
<property name="windowModality">
|
|
||||||
<enum>Qt::WindowModality::ApplicationModal</enum>
|
|
||||||
</property>
|
|
||||||
<property name="geometry">
|
<property name="geometry">
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>264</width>
|
<width>263</width>
|
||||||
<height>473</height>
|
<height>473</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>263</width>
|
||||||
|
<height>473</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>263</width>
|
||||||
|
<height>473</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
<string>Platform List</string>
|
<string>Platform List</string>
|
||||||
</property>
|
</property>
|
||||||
@@ -122,6 +131,14 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</widget>
|
</widget>
|
||||||
|
<tabstops>
|
||||||
|
<tabstop>platListWidget</tabstop>
|
||||||
|
<tabstop>add_plat_but</tabstop>
|
||||||
|
<tabstop>del_plat_but</tabstop>
|
||||||
|
<tabstop>edit_plat_but</tabstop>
|
||||||
|
<tabstop>plist_save_but</tabstop>
|
||||||
|
<tabstop>plat_enabled_check</tabstop>
|
||||||
|
</tabstops>
|
||||||
<resources/>
|
<resources/>
|
||||||
<connections>
|
<connections>
|
||||||
<connection>
|
<connection>
|
||||||
|
@@ -80,6 +80,7 @@ class DBHandler:
|
|||||||
|
|
||||||
return plat_name_list
|
return plat_name_list
|
||||||
|
|
||||||
|
# Write updated platform data to DB when changes are saved
|
||||||
def write_platforms(self, plat_list: list[Platform]):
|
def write_platforms(self, plat_list: list[Platform]):
|
||||||
for i in range(len(plat_list)):
|
for i in range(len(plat_list)):
|
||||||
platforms_data = [
|
platforms_data = [
|
||||||
@@ -242,7 +243,20 @@ class DBHandler:
|
|||||||
|
|
||||||
return user_details_dict
|
return user_details_dict
|
||||||
|
|
||||||
def toggle_platform_state(self, index: int, state: bool):
|
# Remove a platform from the DB - update the IDs to keep them sequential
|
||||||
state_data = [state, index]
|
def remove_platform(self, index: int):
|
||||||
self.cur.execute("UPDATE tblPlatforms SET IsEnabled = ? WHERE PlatformID = ?", state_data)
|
tbl_list = ["tblPlatforms", "tblFlatPlatFees", "tblFlatDealFees", "tblFundPlatFee"]
|
||||||
|
for tbl in tbl_list:
|
||||||
|
self.cur.execute(f"DELETE FROM {tbl} WHERE PlatformID = {index}")
|
||||||
|
|
||||||
|
res = self.cur.execute("SELECT PlatformID from tblPlatforms").fetchall()
|
||||||
|
n = len(res)
|
||||||
|
for i in range(n):
|
||||||
|
for tbl in tbl_list:
|
||||||
|
self.cur.execute(f"""
|
||||||
|
UPDATE {tbl}
|
||||||
|
SET PlatformID = {index + i}
|
||||||
|
WHERE PlatformID = {index + 1 + i}
|
||||||
|
""")
|
||||||
|
|
||||||
self.conn.commit()
|
self.conn.commit()
|
||||||
|
13
src/main.py
13
src/main.py
@@ -1,3 +1,16 @@
|
|||||||
|
## Nuitka compilation options (Windows only)
|
||||||
|
# nuitka-project: --mode=onefile
|
||||||
|
# nuitka-project: --enable-plugin=pyqt6
|
||||||
|
# nuitka-project: --include-module=widgets.mpl_widget
|
||||||
|
# nuitka-project: --include-data-files=icon2.ico=icon2.ico
|
||||||
|
# nuitka-project: --include-data-dir=gui=gui
|
||||||
|
# nuitka-project: --windows-console-mode=disable
|
||||||
|
# nuitka-project: --windows-icon-from-ico=icon2.ico
|
||||||
|
# nuitka-project: --product-name=SIPPCompare
|
||||||
|
# nuitka-project: --file-description=SIPPCompare
|
||||||
|
# nuitka-project: --product-version=1
|
||||||
|
# nuitka-project: --output-dir=build
|
||||||
|
# nuitka-project: --output-filename=SIPPCompare
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from PyQt6.QtWidgets import QApplication
|
from PyQt6.QtWidgets import QApplication
|
||||||
|
@@ -1,33 +1,22 @@
|
|||||||
from PyQt6 import uic
|
from PyQt6 import uic
|
||||||
|
from PyQt6.QtCore import QTimer
|
||||||
from PyQt6.QtGui import QIntValidator, QIcon
|
from PyQt6.QtGui import QIntValidator, QIcon
|
||||||
from PyQt6.QtWidgets import QMainWindow
|
from PyQt6.QtWidgets import QMainWindow, QApplication
|
||||||
|
|
||||||
import resource_finder
|
import resource_finder
|
||||||
from db_handler import DBHandler
|
from db_handler import DBHandler
|
||||||
from output_window import OutputWindow
|
|
||||||
from platform_list import PlatformList
|
from platform_list import PlatformList
|
||||||
|
from output_window import OutputWindow
|
||||||
|
|
||||||
|
|
||||||
class SIPPCompare(QMainWindow):
|
class SIPPCompare(QMainWindow):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
# Import Qt Designer UI XML file
|
# Import Qt Designer UI XML file
|
||||||
|
|
||||||
uic.loadUi(resource_finder.get_res_path("gui/main_gui.ui"), self)
|
uic.loadUi(resource_finder.get_res_path("gui/main_gui.ui"), self)
|
||||||
self.setWindowIcon(QIcon(resource_finder.get_res_path("icon2.ico")))
|
self.setWindowIcon(QIcon(resource_finder.get_res_path("icon2.ico")))
|
||||||
|
|
||||||
# Initialise class variables
|
## Initialise class variables
|
||||||
# Inputs
|
|
||||||
self.optional_boxes = []
|
|
||||||
self.fund_plat_fee = 0.0
|
|
||||||
self.plat_name = ""
|
|
||||||
self.fund_deal_fee = 0.0
|
|
||||||
self.share_plat_fee = 0.0
|
|
||||||
self.share_plat_max_fee = 0.0
|
|
||||||
self.share_deal_fee = 0.0
|
|
||||||
self.share_deal_reduce_trades = 0.0
|
|
||||||
self.share_deal_reduce_amount = 0.0
|
|
||||||
|
|
||||||
# Results
|
# Results
|
||||||
self.fund_plat_fees = 0.0
|
self.fund_plat_fees = 0.0
|
||||||
self.fund_deal_fees = 0.0
|
self.fund_deal_fees = 0.0
|
||||||
@@ -38,29 +27,28 @@ class SIPPCompare(QMainWindow):
|
|||||||
# Create window objects
|
# Create window objects
|
||||||
self.db = DBHandler()
|
self.db = DBHandler()
|
||||||
self.platform_list_win = PlatformList(self.db)
|
self.platform_list_win = PlatformList(self.db)
|
||||||
self.output_win = OutputWindow()
|
if len(self.platform_list_win.plat_name_list) == 0:
|
||||||
|
QTimer.singleShot(1, self.platform_list_win.show)
|
||||||
|
self.output_win = None
|
||||||
|
|
||||||
# Handle events
|
## Handle events
|
||||||
self.calc_but.clicked.connect(self.calculate_fees)
|
self.calc_but.clicked.connect(self.indicate_loading)
|
||||||
# Menu bar entry (File -> Edit Platforms)
|
# Menu bar entry (File -> Platform List)
|
||||||
self.actionList_Platforms.triggered.connect(self.show_platform_list)
|
self.actionList_Platforms.triggered.connect(self.show_platform_list)
|
||||||
# Update percentage mix label when slider moved
|
# Update percentage mix label when slider moved
|
||||||
self.mix_slider.valueChanged.connect(self.update_slider_lab)
|
self.mix_slider.valueChanged.connect(self.update_slider_lab)
|
||||||
self.value_input.valueChanged.connect(self.check_valid)
|
self.value_input.valueChanged.connect(self.check_valid)
|
||||||
self.share_trades_combo.currentTextChanged.connect(self.check_valid)
|
# Validate input
|
||||||
self.fund_trades_combo.currentTextChanged.connect(self.check_valid)
|
self.share_trades_box.valueChanged.connect(self.check_valid)
|
||||||
|
self.fund_trades_box.valueChanged.connect(self.check_valid)
|
||||||
|
|
||||||
# Set validators
|
## Restore last session
|
||||||
self.share_trades_combo.setValidator(QIntValidator(0, 999))
|
|
||||||
self.fund_trades_combo.setValidator(QIntValidator(0, 99))
|
|
||||||
|
|
||||||
# Restore last session
|
|
||||||
prev_session_data = self.db.retrieve_user_details()
|
prev_session_data = self.db.retrieve_user_details()
|
||||||
if "NO_RECORD" not in prev_session_data:
|
if "NO_RECORD" not in prev_session_data:
|
||||||
self.value_input.setValue(prev_session_data["pension_val"])
|
self.value_input.setValue(prev_session_data["pension_val"])
|
||||||
self.mix_slider.setValue(prev_session_data["slider_val"])
|
self.mix_slider.setValue(prev_session_data["slider_val"])
|
||||||
self.share_trades_combo.setCurrentText(str(prev_session_data["share_trades"]))
|
self.share_trades_box.setValue(prev_session_data["share_trades"])
|
||||||
self.fund_trades_combo.setCurrentText(str(prev_session_data["fund_trades"]))
|
self.fund_trades_box.setValue(prev_session_data["fund_trades"])
|
||||||
self.calc_but.setFocus()
|
self.calc_but.setFocus()
|
||||||
|
|
||||||
# Display slider position as mix between two nums (funds/shares)
|
# Display slider position as mix between two nums (funds/shares)
|
||||||
@@ -69,15 +57,18 @@ class SIPPCompare(QMainWindow):
|
|||||||
mix_lab_str = f"Investment mix (funds {slider_val}% / shares {100 - slider_val}%)"
|
mix_lab_str = f"Investment mix (funds {slider_val}% / shares {100 - slider_val}%)"
|
||||||
self.mix_lab.setText(mix_lab_str)
|
self.mix_lab.setText(mix_lab_str)
|
||||||
|
|
||||||
|
# Ensure that trade fields aren't blank and pension value > 0
|
||||||
def check_valid(self):
|
def check_valid(self):
|
||||||
if self.share_trades_combo.currentText() != "" \
|
if self.value_input.value() != 0:
|
||||||
and self.fund_trades_combo.currentText() != "" \
|
|
||||||
and self.value_input.value() != 0:
|
|
||||||
self.calc_but.setEnabled(True)
|
self.calc_but.setEnabled(True)
|
||||||
else:
|
else:
|
||||||
self.calc_but.setEnabled(False)
|
self.calc_but.setEnabled(False)
|
||||||
|
|
||||||
# Calculate fees
|
def indicate_loading(self):
|
||||||
|
self.calc_but.setText("Working...")
|
||||||
|
QTimer.singleShot(1, self.calculate_fees)
|
||||||
|
|
||||||
|
# Calculate fees for all active platforms
|
||||||
def calculate_fees(self):
|
def calculate_fees(self):
|
||||||
# Set to empty list each time to avoid persistence
|
# Set to empty list each time to avoid persistence
|
||||||
self.results = []
|
self.results = []
|
||||||
@@ -85,21 +76,27 @@ class SIPPCompare(QMainWindow):
|
|||||||
# Get user input
|
# Get user input
|
||||||
value_num = float(self.value_input.value())
|
value_num = float(self.value_input.value())
|
||||||
slider_val: int = self.mix_slider.value()
|
slider_val: int = self.mix_slider.value()
|
||||||
fund_trades_num = int(self.fund_trades_combo.currentText())
|
fund_trades_num = int(self.fund_trades_box.value())
|
||||||
share_trades_num = int(self.share_trades_combo.currentText())
|
share_trades_num = int(self.share_trades_box.value())
|
||||||
funds_value = (slider_val / 100) * value_num
|
|
||||||
shares_value = (1 - (slider_val / 100)) * value_num
|
shares_value = (1 - (slider_val / 100)) * value_num
|
||||||
|
index = 0
|
||||||
|
|
||||||
for platform in self.platform_list_win.plat_list:
|
for platform in self.platform_list_win.plat_list:
|
||||||
|
if not platform.enabled:
|
||||||
|
continue
|
||||||
|
|
||||||
fund_plat_fees = 0.0
|
fund_plat_fees = 0.0
|
||||||
fund_deal_fees = 0.0
|
fund_deal_fees = 0.0
|
||||||
share_plat_fees = 0.0
|
share_plat_fees = 0.0
|
||||||
share_deal_fees = 0.0
|
share_deal_fees = 0.0
|
||||||
plat_name = platform.plat_name
|
plat_name = platform.plat_name
|
||||||
|
if plat_name is None or plat_name == "":
|
||||||
|
plat_name = f"Unnamed [ID: {index}]"
|
||||||
|
|
||||||
if platform.fund_deal_fee is not None:
|
if platform.fund_deal_fee is not None:
|
||||||
fund_deal_fees = fund_trades_num * platform.fund_deal_fee
|
fund_deal_fees = fund_trades_num * platform.fund_deal_fee
|
||||||
|
|
||||||
|
funds_value = (slider_val / 100) * value_num
|
||||||
for i in range(1, len(platform.fund_plat_fee[0])):
|
for i in range(1, len(platform.fund_plat_fee[0])):
|
||||||
band = platform.fund_plat_fee[0][i]
|
band = platform.fund_plat_fee[0][i]
|
||||||
prev_band = platform.fund_plat_fee[0][i - 1]
|
prev_band = platform.fund_plat_fee[0][i - 1]
|
||||||
@@ -126,15 +123,22 @@ class SIPPCompare(QMainWindow):
|
|||||||
share_deal_fees = platform.share_deal_fee * share_trades_num
|
share_deal_fees = platform.share_deal_fee * share_trades_num
|
||||||
|
|
||||||
self.results.append([fund_plat_fees, fund_deal_fees, share_plat_fees, share_deal_fees, plat_name])
|
self.results.append([fund_plat_fees, fund_deal_fees, share_plat_fees, share_deal_fees, plat_name])
|
||||||
|
index += 1
|
||||||
|
|
||||||
|
# Save details entered by user for next session
|
||||||
self.db.write_user_details(value_num, slider_val, share_trades_num, fund_trades_num)
|
self.db.write_user_details(value_num, slider_val, share_trades_num, fund_trades_num)
|
||||||
self.show_output_win()
|
self.show_output_win()
|
||||||
|
|
||||||
# Show the output window - this func is called from calculate_fee()
|
# Show the output window - this func is called from calculate_fee()
|
||||||
def show_output_win(self):
|
def show_output_win(self):
|
||||||
# Refresh the results when new fees are calculated
|
# Refresh the results when new fees are calculated
|
||||||
self.output_win.display_output(self.results)
|
self.output_win = OutputWindow()
|
||||||
|
self.output_win.display_output(self.results, 1)
|
||||||
|
self.calc_but.setText("Calculate")
|
||||||
|
self.output_win.activateWindow()
|
||||||
|
self.output_win.raise_()
|
||||||
self.output_win.show()
|
self.output_win.show()
|
||||||
|
QApplication.alert(self.output_win)
|
||||||
|
|
||||||
def show_platform_list(self):
|
def show_platform_list(self):
|
||||||
self.platform_list_win.show()
|
self.platform_list_win.show()
|
||||||
|
@@ -1,13 +1,33 @@
|
|||||||
import datetime
|
|
||||||
import os
|
import os
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
from PyQt6 import uic
|
from PyQt6 import uic
|
||||||
from PyQt6.QtGui import QIcon
|
from PyQt6.QtGui import QIcon, QFont
|
||||||
from PyQt6.QtWidgets import QWidget
|
from PyQt6.QtWidgets import QWidget, QFileDialog, QMessageBox, QDialogButtonBox
|
||||||
|
|
||||||
import resource_finder
|
import resource_finder
|
||||||
|
|
||||||
|
|
||||||
|
class SaveFailure(QMessageBox):
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
self.setWindowIcon(QIcon(resource_finder.get_res_path("icon2.ico")))
|
||||||
|
self.setWindowTitle("Save failure")
|
||||||
|
|
||||||
|
self.setIcon(QMessageBox.Icon.Critical)
|
||||||
|
font = QFont()
|
||||||
|
font.setPointSize(11)
|
||||||
|
self.setFont(font)
|
||||||
|
self.setText("Failed to save file")
|
||||||
|
self.setDetailedText(
|
||||||
|
"This could be due to a permissions issue, or the file being in use by another process"
|
||||||
|
)
|
||||||
|
|
||||||
|
self.setStandardButtons(QMessageBox.StandardButton.Ok)
|
||||||
|
font.setPointSize(10)
|
||||||
|
self.findChild(QDialogButtonBox).setFont(font)
|
||||||
|
|
||||||
|
|
||||||
class OutputWindow(QWidget):
|
class OutputWindow(QWidget):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
@@ -15,17 +35,103 @@ class OutputWindow(QWidget):
|
|||||||
uic.loadUi(resource_finder.get_res_path("gui/output_window.ui"), self)
|
uic.loadUi(resource_finder.get_res_path("gui/output_window.ui"), self)
|
||||||
self.setWindowIcon(QIcon(resource_finder.get_res_path("icon2.ico")))
|
self.setWindowIcon(QIcon(resource_finder.get_res_path("icon2.ico")))
|
||||||
|
|
||||||
def display_output(self, results: list):
|
# Define class variables
|
||||||
ax = self.graphWidget.canvas.axes
|
self.save_err_dialog = SaveFailure()
|
||||||
ax.clear()
|
self.canvas = self.graphWidget.canvas
|
||||||
ax.cla()
|
self.ax = self.canvas.axes
|
||||||
self.graphWidget.canvas.draw_idle()
|
self.fig = self.canvas.figure
|
||||||
|
self.results = []
|
||||||
|
|
||||||
|
# Handle events
|
||||||
|
self.save_graph_but.clicked.connect(self.save_graph)
|
||||||
|
self.save_csv_but.clicked.connect(self.save_csv)
|
||||||
|
self.time_slider.valueChanged.connect(self.change_time)
|
||||||
|
|
||||||
|
def display_output(self, results: list, years: int):
|
||||||
|
self.results = results
|
||||||
|
self.ax.clear()
|
||||||
|
self.ax.cla()
|
||||||
|
self.canvas.draw_idle()
|
||||||
|
|
||||||
names = []
|
names = []
|
||||||
values = []
|
values = []
|
||||||
for result in results:
|
for result in results:
|
||||||
names.append(result[4])
|
names.append(result[4])
|
||||||
values.append(sum(result[:4]))
|
values.append(sum(result[:4]) * years)
|
||||||
|
|
||||||
h_bars = ax.barh(names, values)
|
names = sorted(names, key=lambda x: values[names.index(x)], reverse=True)
|
||||||
ax.bar_label(h_bars, label_type='center', labels=[f"£{x:,.2f}" for x in h_bars.datavalues])
|
values = sorted(values, reverse=True)
|
||||||
|
|
||||||
|
h_bars = self.ax.barh(names, values)
|
||||||
|
self.ax.bar_label(h_bars, label_type='center', labels=[f"£{x:,.2f}" for x in h_bars.datavalues])
|
||||||
|
|
||||||
|
def save_graph(self):
|
||||||
|
file_picker = QFileDialog(self)
|
||||||
|
file_picker.setFileMode(QFileDialog.FileMode.AnyFile)
|
||||||
|
file_picker.setDefaultSuffix("png")
|
||||||
|
file_picker.setWindowTitle("Save results as PNG")
|
||||||
|
file_picker.setAcceptMode(QFileDialog.AcceptMode.AcceptSave)
|
||||||
|
file_picker.setNameFilter("*.png")
|
||||||
|
file_path = ""
|
||||||
|
cur_time = datetime.now()
|
||||||
|
filename_str = f"{file_path}/SIPPCompare-{cur_time.year}.{cur_time.month}.{cur_time.day}.png"
|
||||||
|
file_picker.selectFile(filename_str)
|
||||||
|
if file_picker.exec():
|
||||||
|
file_path = file_picker.selectedFiles()[0]
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.fig.savefig(file_path, dpi=150)
|
||||||
|
except OSError:
|
||||||
|
self.save_err_dialog.exec()
|
||||||
|
|
||||||
|
def save_csv(self):
|
||||||
|
# TODO: Sort CSV output, either alphabetically or by total fees
|
||||||
|
file_picker = QFileDialog(self)
|
||||||
|
file_picker.setFileMode(QFileDialog.FileMode.AnyFile)
|
||||||
|
file_picker.setDefaultSuffix("csv")
|
||||||
|
file_picker.setWindowTitle("Save results as CSV")
|
||||||
|
file_picker.setAcceptMode(QFileDialog.AcceptMode.AcceptSave)
|
||||||
|
file_picker.setNameFilter("*.csv")
|
||||||
|
file_path = ""
|
||||||
|
cur_time = datetime.now()
|
||||||
|
filename_str = f"{file_path}/SIPPCompare-{cur_time.year}.{cur_time.month}.{cur_time.day}.csv"
|
||||||
|
file_picker.selectFile(filename_str)
|
||||||
|
if file_picker.exec():
|
||||||
|
file_path = file_picker.selectedFiles()[0]
|
||||||
|
|
||||||
|
try:
|
||||||
|
csvfile = open(file_path, "wt")
|
||||||
|
csv_string = (
|
||||||
|
"Platform Name,Fund Platform Fee,Share Platform Fee,Fund Dealing Fee,"
|
||||||
|
"Share Dealing Fee,Total Platform Fees,Total Dealing Fees,Total Fund Fees,"
|
||||||
|
"Total Share Fees,Total Fees"
|
||||||
|
)
|
||||||
|
|
||||||
|
for result in self.results:
|
||||||
|
csv_string += '\n'
|
||||||
|
pn = result[4]
|
||||||
|
fpf = result[0]
|
||||||
|
spf = result[2]
|
||||||
|
fdf = result[1]
|
||||||
|
sdf = result[3]
|
||||||
|
|
||||||
|
tpf = fpf + spf
|
||||||
|
tdf = sdf + fdf
|
||||||
|
|
||||||
|
tff = fpf + fdf
|
||||||
|
tsf = spf + sdf
|
||||||
|
tf = tff + tsf
|
||||||
|
csv_string += (
|
||||||
|
f"{pn},\"£{fpf:,.2f}\",\"£{spf:,.2f}\",\"£{fdf:,.2f}\",\"£{sdf:,.2f}\","
|
||||||
|
f"\"£{tpf:,.2f}\",\"£{tdf:,.2f}\",\"£{tff:,.2f}\",\"£{tsf:,.2f}\",\"£{tf:,.2f}\""
|
||||||
|
)
|
||||||
|
|
||||||
|
csvfile.write(csv_string)
|
||||||
|
csvfile.close()
|
||||||
|
except OSError:
|
||||||
|
self.save_err_dialog.exec()
|
||||||
|
|
||||||
|
def change_time(self):
|
||||||
|
years: int = self.time_slider.value()
|
||||||
|
self.time_lab.setText(f"Fees over {years} year(s) (assuming no change in value)")
|
||||||
|
self.display_output(self.results, years)
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
from PyQt6 import uic
|
from PyQt6 import uic
|
||||||
from PyQt6.QtCore import QRegularExpression, QRect
|
from PyQt6.QtCore import QRegularExpression, QRect
|
||||||
from PyQt6.QtGui import QRegularExpressionValidator, QFont, QIcon
|
from PyQt6.QtGui import QRegularExpressionValidator, QFont, QIcon
|
||||||
from PyQt6.QtWidgets import QWidget, QLabel
|
from PyQt6.QtWidgets import QLabel, QDialog
|
||||||
|
|
||||||
import resource_finder
|
import resource_finder
|
||||||
from db_handler import DBHandler
|
from db_handler import DBHandler
|
||||||
@@ -9,7 +9,7 @@ from data_struct import Platform
|
|||||||
from widgets.fastedit_spinbox import FastEditQDoubleSpinBox
|
from widgets.fastedit_spinbox import FastEditQDoubleSpinBox
|
||||||
|
|
||||||
|
|
||||||
class PlatformEdit(QWidget):
|
class PlatformEdit(QDialog):
|
||||||
def __init__(self, plat: Platform):
|
def __init__(self, plat: Platform):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
# Import Qt Designer UI XML file
|
# Import Qt Designer UI XML file
|
||||||
@@ -18,7 +18,6 @@ class PlatformEdit(QWidget):
|
|||||||
|
|
||||||
# Initialise class variables
|
# Initialise class variables
|
||||||
self.plat = plat
|
self.plat = plat
|
||||||
self.fund_plat_fee = self.plat.fund_plat_fee
|
|
||||||
self.widgets_list_list = []
|
self.widgets_list_list = []
|
||||||
if len(self.plat.fund_plat_fee[0]) > 1:
|
if len(self.plat.fund_plat_fee[0]) > 1:
|
||||||
self.fund_fee_rows = len(self.plat.fund_plat_fee[0]) - 1
|
self.fund_fee_rows = len(self.plat.fund_plat_fee[0]) - 1
|
||||||
@@ -65,7 +64,7 @@ class PlatformEdit(QWidget):
|
|||||||
|
|
||||||
if self.plat.fund_deal_fee is None:
|
if self.plat.fund_deal_fee is None:
|
||||||
self.check_boxes_ticked[1] = False
|
self.check_boxes_ticked[1] = False
|
||||||
self.plat_fund_deal_fee_check.setChecked(False)
|
self.fund_deal_fee_check.setChecked(False)
|
||||||
else:
|
else:
|
||||||
self.check_boxes_ticked[1] = True
|
self.check_boxes_ticked[1] = True
|
||||||
self.fund_deal_fee_check.setChecked(True)
|
self.fund_deal_fee_check.setChecked(True)
|
||||||
@@ -178,6 +177,8 @@ class PlatformEdit(QWidget):
|
|||||||
else:
|
else:
|
||||||
self.plat.share_deal_reduce_amount = None
|
self.plat.share_deal_reduce_amount = None
|
||||||
|
|
||||||
|
self.accept()
|
||||||
|
|
||||||
# This method does multiple things in order to validate the user's inputs:
|
# This method does multiple things in order to validate the user's inputs:
|
||||||
# 1) Check all required fields have a non-zero value
|
# 1) Check all required fields have a non-zero value
|
||||||
# 2) If an optional checkbox is toggled: toggle editing of the corresponding field
|
# 2) If an optional checkbox is toggled: toggle editing of the corresponding field
|
||||||
@@ -299,7 +300,7 @@ class PlatformEdit(QWidget):
|
|||||||
grid_height = int(round(28.5 * self.fund_fee_rows))
|
grid_height = int(round(28.5 * self.fund_fee_rows))
|
||||||
else:
|
else:
|
||||||
grid_height = int(round(28.5 * (self.fund_fee_rows + 1)))
|
grid_height = int(round(28.5 * (self.fund_fee_rows + 1)))
|
||||||
self.gridLayoutWidget_2.setGeometry(QRect(11, 309, 611, grid_height))
|
self.gridLayoutWidget_2.setGeometry(QRect(19, 307, 591, grid_height))
|
||||||
for i in range(len(widgets)):
|
for i in range(len(widgets)):
|
||||||
if loading:
|
if loading:
|
||||||
self.gridLayout_2.addWidget(widgets[i], x + 1, i, 1, 1)
|
self.gridLayout_2.addWidget(widgets[i], x + 1, i, 1, 1)
|
||||||
@@ -317,8 +318,6 @@ class PlatformEdit(QWidget):
|
|||||||
|
|
||||||
prev_box_row = cur_box_pos[0] - 1
|
prev_box_row = cur_box_pos[0] - 1
|
||||||
prev_box_item = self.gridLayout_2.itemAtPosition(prev_box_row, cur_box_pos[1]).widget()
|
prev_box_item = self.gridLayout_2.itemAtPosition(prev_box_row, cur_box_pos[1]).widget()
|
||||||
#if loading:
|
|
||||||
# prev_box_item.setValue(self.plat.fund_plat_fee[0][x+1])
|
|
||||||
cur_label_item = self.gridLayout_2.itemAtPosition(cur_label_pos[0], cur_label_pos[1]).widget()
|
cur_label_item = self.gridLayout_2.itemAtPosition(cur_label_pos[0], cur_label_pos[1]).widget()
|
||||||
cur_label_item.setText(f"between £{int(prev_box_item.value())} and")
|
cur_label_item.setText(f"between £{int(prev_box_item.value())} and")
|
||||||
|
|
||||||
@@ -339,7 +338,7 @@ class PlatformEdit(QWidget):
|
|||||||
widget.hide()
|
widget.hide()
|
||||||
self.widgets_list_list.pop()
|
self.widgets_list_list.pop()
|
||||||
self.fund_fee_rows -= 1
|
self.fund_fee_rows -= 1
|
||||||
self.gridLayoutWidget_2.setGeometry(11, 309, 611, int(round(28.5 * self.fund_fee_rows, 0)))
|
self.gridLayoutWidget_2.setGeometry(19, 307, 591, int(round(28.5 * self.fund_fee_rows, 0)))
|
||||||
|
|
||||||
if self.fund_fee_rows < 2:
|
if self.fund_fee_rows < 2:
|
||||||
self.del_row_but.setEnabled(False)
|
self.del_row_but.setEnabled(False)
|
||||||
@@ -349,3 +348,7 @@ class PlatformEdit(QWidget):
|
|||||||
|
|
||||||
self.check_valid()
|
self.check_valid()
|
||||||
self.update_tier_labels()
|
self.update_tier_labels()
|
||||||
|
|
||||||
|
def closeEvent(self, event):
|
||||||
|
event.ignore()
|
||||||
|
self.reject()
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
from PyQt6 import uic
|
from PyQt6 import uic
|
||||||
from PyQt6.QtCore import QRegularExpression
|
from PyQt6.QtCore import QRegularExpression
|
||||||
from PyQt6.QtGui import QIcon, QRegularExpressionValidator
|
from PyQt6.QtGui import QIcon, QRegularExpressionValidator, QFont
|
||||||
from PyQt6.QtWidgets import QWidget, QListWidgetItem, QDialog
|
from PyQt6.QtWidgets import QWidget, QListWidgetItem, QDialog, QDialogButtonBox, QMessageBox
|
||||||
|
|
||||||
import resource_finder
|
import resource_finder
|
||||||
from db_handler import DBHandler
|
from db_handler import DBHandler
|
||||||
@@ -34,6 +34,25 @@ class PlatformRename(QDialog):
|
|||||||
event.ignore()
|
event.ignore()
|
||||||
self.reject()
|
self.reject()
|
||||||
|
|
||||||
|
|
||||||
|
class RemoveConfirm(QMessageBox):
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
self.setWindowIcon(QIcon(resource_finder.get_res_path("icon2.ico")))
|
||||||
|
self.setWindowTitle("Remove platform?")
|
||||||
|
self.setIcon(QMessageBox.Icon.Warning)
|
||||||
|
font = QFont()
|
||||||
|
font.setPointSize(11)
|
||||||
|
self.setFont(font)
|
||||||
|
self.setText("Are you sure you want to remove this platform?")
|
||||||
|
self.setInformativeText("This action is immediate and permanent")
|
||||||
|
|
||||||
|
self.setStandardButtons(QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.Cancel)
|
||||||
|
self.setDefaultButton(QMessageBox.StandardButton.Cancel)
|
||||||
|
font.setPointSize(10)
|
||||||
|
self.findChild(QDialogButtonBox).setFont(font)
|
||||||
|
|
||||||
|
|
||||||
class PlatformList(QWidget):
|
class PlatformList(QWidget):
|
||||||
def __init__(self, db: DBHandler):
|
def __init__(self, db: DBHandler):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
@@ -41,23 +60,16 @@ class PlatformList(QWidget):
|
|||||||
uic.loadUi(resource_finder.get_res_path("gui/platform_list.ui"), self)
|
uic.loadUi(resource_finder.get_res_path("gui/platform_list.ui"), self)
|
||||||
self.setWindowIcon(QIcon(resource_finder.get_res_path("icon2.ico")))
|
self.setWindowIcon(QIcon(resource_finder.get_res_path("icon2.ico")))
|
||||||
|
|
||||||
|
# Initialise class variables
|
||||||
self.db = db
|
self.db = db
|
||||||
self.plat_edit_win = None
|
self.plat_edit_win = None
|
||||||
self.plat_list_dialog = PlatformRename()
|
self.plat_list_dialog = None
|
||||||
|
self.del_plat_dialog = RemoveConfirm()
|
||||||
self.plat_list = []
|
self.plat_list = []
|
||||||
self.plat_name_list = []
|
self.plat_name_list = []
|
||||||
self.new_plat_name = ""
|
self.new_plat_name = ""
|
||||||
self.update_plat_list()
|
self.update_plat_list()
|
||||||
|
self.db_indices = [x for x in range(len(self.plat_name_list))]
|
||||||
for i in range(len(self.plat_name_list)):
|
|
||||||
plat_name = self.plat_name_list[i]
|
|
||||||
item = QListWidgetItem()
|
|
||||||
if plat_name is not None:
|
|
||||||
item.setText(plat_name)
|
|
||||||
else:
|
|
||||||
item.setText(f"Unnamed [ID: {i}]")
|
|
||||||
|
|
||||||
self.platListWidget.addItem(item)
|
|
||||||
|
|
||||||
# Handle events
|
# Handle events
|
||||||
self.add_plat_but.clicked.connect(self.add_platform)
|
self.add_plat_but.clicked.connect(self.add_platform)
|
||||||
@@ -70,8 +82,20 @@ class PlatformList(QWidget):
|
|||||||
def update_plat_list(self):
|
def update_plat_list(self):
|
||||||
self.plat_name_list = self.db.retrieve_plat_list()
|
self.plat_name_list = self.db.retrieve_plat_list()
|
||||||
self.plat_list = self.db.retrieve_platforms()
|
self.plat_list = self.db.retrieve_platforms()
|
||||||
|
self.platListWidget.clear()
|
||||||
|
|
||||||
|
for i in range(len(self.plat_name_list)):
|
||||||
|
plat_name = self.plat_name_list[i]
|
||||||
|
item = QListWidgetItem()
|
||||||
|
if plat_name is not None:
|
||||||
|
item.setText(plat_name)
|
||||||
|
else:
|
||||||
|
item.setText(f"Unnamed [ID: {i}]")
|
||||||
|
|
||||||
|
self.platListWidget.addItem(item)
|
||||||
|
|
||||||
def add_platform(self):
|
def add_platform(self):
|
||||||
|
self.plat_list_dialog = PlatformRename()
|
||||||
name_dialog_res = self.plat_list_dialog.exec()
|
name_dialog_res = self.plat_list_dialog.exec()
|
||||||
if name_dialog_res == QDialog.DialogCode.Accepted:
|
if name_dialog_res == QDialog.DialogCode.Accepted:
|
||||||
name = self.plat_list_dialog.new_name
|
name = self.plat_list_dialog.new_name
|
||||||
@@ -87,28 +111,47 @@ class PlatformList(QWidget):
|
|||||||
index, [[0], [0]], name_param, True, 0, 0, None, 0, None, None)
|
index, [[0], [0]], name_param, True, 0, 0, None, 0, None, None)
|
||||||
)
|
)
|
||||||
self.plat_edit_win = PlatformEdit(self.plat_list[index])
|
self.plat_edit_win = PlatformEdit(self.plat_list[index])
|
||||||
self.plat_edit_win.show()
|
plat_edit_res = self.plat_edit_win.exec()
|
||||||
|
if plat_edit_res == QDialog.DialogCode.Rejected:
|
||||||
|
self.plat_list.pop()
|
||||||
|
self.platListWidget.takeItem(self.platListWidget.count() - 1)
|
||||||
|
|
||||||
def get_enabled_state(self):
|
def get_enabled_state(self):
|
||||||
index = self.platListWidget.currentRow()
|
index = self.platListWidget.currentRow()
|
||||||
is_enabled = self.plat_list[index].enabled
|
if len(self.plat_list) > 0:
|
||||||
if is_enabled:
|
is_enabled = self.plat_list[index].enabled
|
||||||
self.plat_enabled_check.setChecked(True)
|
if is_enabled:
|
||||||
|
self.plat_enabled_check.setChecked(True)
|
||||||
|
else:
|
||||||
|
self.plat_enabled_check.setChecked(False)
|
||||||
else:
|
else:
|
||||||
self.plat_enabled_check.setChecked(False)
|
self.plat_enabled_check.setChecked(False)
|
||||||
|
|
||||||
|
if index in self.db_indices:
|
||||||
|
self.del_plat_but.setEnabled(True)
|
||||||
|
else:
|
||||||
|
self.del_plat_but.setEnabled(False)
|
||||||
|
|
||||||
def edit_platform(self):
|
def edit_platform(self):
|
||||||
index = self.platListWidget.currentRow()
|
index = self.platListWidget.currentRow()
|
||||||
self.plat_edit_win = PlatformEdit(self.plat_list[index])
|
if len(self.plat_list) > 0:
|
||||||
self.plat_edit_win.show()
|
self.plat_edit_win = PlatformEdit(self.plat_list[index])
|
||||||
|
self.plat_edit_win.exec()
|
||||||
|
|
||||||
def save_platforms(self):
|
def save_platforms(self):
|
||||||
self.db.write_platforms(self.plat_list)
|
self.db.write_platforms(self.plat_list)
|
||||||
|
self.update_plat_list()
|
||||||
|
self.db_indices = [x for x in range(len(self.plat_name_list))]
|
||||||
|
|
||||||
def toggle_platform_state(self):
|
def toggle_platform_state(self):
|
||||||
index = self.platListWidget.currentRow()
|
index = self.platListWidget.currentRow()
|
||||||
state = self.plat_enabled_check.isChecked()
|
state = self.plat_enabled_check.isChecked()
|
||||||
self.db.toggle_platform_state(index, state)
|
if len(self.plat_list) > 0 and index >= 0:
|
||||||
|
self.plat_list[index].enabled = state
|
||||||
|
|
||||||
def remove_platform(self):
|
def remove_platform(self):
|
||||||
return None
|
index = self.platListWidget.currentRow()
|
||||||
|
del_dialog_res = self.del_plat_dialog.exec()
|
||||||
|
if del_dialog_res == QMessageBox.StandardButton.Yes:
|
||||||
|
self.db.remove_platform(index)
|
||||||
|
self.update_plat_list()
|
||||||
|
@@ -2,12 +2,24 @@ import os.path
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
|
||||||
# If using PyInstaller, use it's temporary path, otherwise use cwd
|
# Returns the correct path for Nuitka onefile mode, standalone mode or normal Python
|
||||||
# Credit: https://stackoverflow.com/questions/7674790/bundling-data-files-with-pyinstaller-onefile/13790741#13790741
|
# Credit: https://nuitka.net/user-documentation/common-issue-solutions.html#onefile-finding-files
|
||||||
def get_res_path(relative_path):
|
def get_res_path(relative_path):
|
||||||
try:
|
path_a = ""
|
||||||
base_path = sys._MEIPASS
|
|
||||||
except AttributeError:
|
|
||||||
base_path = os.path.abspath(".")
|
|
||||||
|
|
||||||
return os.path.join(base_path, relative_path)
|
try:
|
||||||
|
path_a = os.path.join(sys.__compiled__.containing_dir, relative_path)
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
path_b = os.path.join(os.path.dirname(__file__), relative_path)
|
||||||
|
path_c = os.path.join(os.path.dirname(sys.argv[0]), relative_path)
|
||||||
|
|
||||||
|
if os.path.isfile(path_a):
|
||||||
|
return path_a
|
||||||
|
elif os.path.isfile(path_b):
|
||||||
|
return path_b
|
||||||
|
elif os.path.isfile(path_c):
|
||||||
|
return path_c
|
||||||
|
else:
|
||||||
|
return os.path.join(os.path.abspath("."), relative_path)
|
||||||
|
@@ -7,6 +7,7 @@ class FastEditQDoubleSpinBox(QDoubleSpinBox):
|
|||||||
QTimer.singleShot(0, self.selectAll)
|
QTimer.singleShot(0, self.selectAll)
|
||||||
super(FastEditQDoubleSpinBox, self).focusInEvent(e)
|
super(FastEditQDoubleSpinBox, self).focusInEvent(e)
|
||||||
|
|
||||||
|
|
||||||
class FastEditQSpinBox(QSpinBox):
|
class FastEditQSpinBox(QSpinBox):
|
||||||
def focusInEvent(self, e):
|
def focusInEvent(self, e):
|
||||||
QTimer.singleShot(0, self.selectAll)
|
QTimer.singleShot(0, self.selectAll)
|
||||||
|
@@ -6,7 +6,7 @@ from PyQt6.QtWidgets import QWidget, QVBoxLayout
|
|||||||
class MplWidget(QWidget):
|
class MplWidget(QWidget):
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
self.canvas = FigureCanvasQTAgg(Figure(figsize=(10, 10), dpi=100))
|
self.canvas = FigureCanvasQTAgg(Figure(figsize=(15, 8), dpi=100))
|
||||||
vertical_layout = QVBoxLayout()
|
vertical_layout = QVBoxLayout()
|
||||||
vertical_layout.addWidget(self.canvas)
|
vertical_layout.addWidget(self.canvas)
|
||||||
self.canvas.axes = self.canvas.figure.add_subplot(1, 1, 1)
|
self.canvas.axes = self.canvas.figure.add_subplot(1, 1, 1)
|
||||||
|
Reference in New Issue
Block a user