mirror of
https://github.com/RolandWH/SIPPCompare.git
synced 2025-05-10 00:31:49 +01:00
Merge pull request #3 from RolandWH/db_testing
implement sqlite database and graphed results
This commit is contained in:
commit
ca8f4409c1
BIN
SIPPCompare.db
Normal file
BIN
SIPPCompare.db
Normal file
Binary file not shown.
@ -5,7 +5,7 @@ a = Analysis(
|
||||
['src\\main.py'],
|
||||
pathex=[],
|
||||
binaries=[],
|
||||
datas=[('gui/*.ui', 'gui'), ('icon2.ico', '.')],
|
||||
datas=[('gui/*.ui', 'gui'), ('gui/dialogs/*.ui', 'gui/dialogs'), ('icon2.ico', '.')],
|
||||
hiddenimports=[],
|
||||
hookspath=[],
|
||||
hooksconfig={},
|
||||
|
99
gui/dialogs/platform_rename.ui
Normal file
99
gui/dialogs/platform_rename.ui
Normal file
@ -0,0 +1,99 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>PlatformRename</class>
|
||||
<widget class="QDialog" name="PlatformRename">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>300</width>
|
||||
<height>100</height>
|
||||
</rect>
|
||||
</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">
|
||||
<string>Name Platform</string>
|
||||
</property>
|
||||
<widget class="QLineEdit" name="rename_plat_box">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>7</x>
|
||||
<y>41</y>
|
||||
<width>287</width>
|
||||
<height>22</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>11</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="rename_plat_lab">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>35</x>
|
||||
<y>9</y>
|
||||
<width>241</width>
|
||||
<height>20</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>11</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Enter a new name for the platform</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="rename_plat_ok_but">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>220</x>
|
||||
<y>70</y>
|
||||
<width>75</width>
|
||||
<height>24</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>10</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>OK</string>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>rename_plat_ok_but</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>PlatformRename</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>257</x>
|
||||
<y>71</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>149</x>
|
||||
<y>44</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
@ -220,13 +220,13 @@
|
||||
<property name="title">
|
||||
<string>File</string>
|
||||
</property>
|
||||
<addaction name="actionEdit_Platforms"/>
|
||||
<addaction name="actionList_Platforms"/>
|
||||
</widget>
|
||||
<addaction name="menuPlatforms"/>
|
||||
</widget>
|
||||
<action name="actionEdit_Platforms">
|
||||
<action name="actionList_Platforms">
|
||||
<property name="text">
|
||||
<string>Edit Platforms</string>
|
||||
<string>Platform List</string>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
|
@ -1,37 +1,108 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ResultsWindow</class>
|
||||
<widget class="QWidget" name="ResultsWindow">
|
||||
<class>OutputWindow</class>
|
||||
<widget class="QWidget" name="OutputWindow">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>355</height>
|
||||
<width>1330</width>
|
||||
<height>685</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>400</width>
|
||||
<height>355</height>
|
||||
<width>1330</width>
|
||||
<height>685</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>400</width>
|
||||
<height>355</height>
|
||||
<width>1330</width>
|
||||
<height>685</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Results</string>
|
||||
</property>
|
||||
<widget class="QTextEdit" name="output">
|
||||
<widget class="MplWidget" name="graphWidget" native="true">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>-10</x>
|
||||
<y>-10</y>
|
||||
<width>1350</width>
|
||||
<height>650</height>
|
||||
</rect>
|
||||
</property>
|
||||
</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>10</y>
|
||||
<width>381</width>
|
||||
<height>301</height>
|
||||
<y>640</y>
|
||||
<width>341</width>
|
||||
<height>31</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="font">
|
||||
@ -39,66 +110,19 @@
|
||||
<pointsize>11</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="res_ok_but">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>318</x>
|
||||
<y>323</y>
|
||||
<width>75</width>
|
||||
<height>24</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>10</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>OK</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="res_save_but">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>238</x>
|
||||
<y>323</y>
|
||||
<width>75</width>
|
||||
<height>24</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>10</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Save</string>
|
||||
<string>Fees over 1 year(s) (assuming no change in value)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
<tabstops>
|
||||
<tabstop>output</tabstop>
|
||||
<tabstop>res_save_but</tabstop>
|
||||
<tabstop>res_ok_but</tabstop>
|
||||
</tabstops>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>MplWidget</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>widgets/mpl_widget</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>res_ok_but</sender>
|
||||
<signal>clicked(bool)</signal>
|
||||
<receiver>ResultsWindow</receiver>
|
||||
<slot>close()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>357</x>
|
||||
<y>281</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>199</x>
|
||||
<y>149</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
@ -2,6 +2,9 @@
|
||||
<ui version="4.0">
|
||||
<class>PlatformEdit</class>
|
||||
<widget class="QWidget" name="PlatformEdit">
|
||||
<property name="windowModality">
|
||||
<enum>Qt::WindowModality::ApplicationModal</enum>
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
@ -10,6 +13,18 @@
|
||||
<height>567</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>630</width>
|
||||
<height>567</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>630</width>
|
||||
<height>567</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Platform Editor</string>
|
||||
</property>
|
||||
@ -215,7 +230,7 @@
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Fund dealing fee*</string>
|
||||
<string>Fund dealing fee</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -339,7 +354,7 @@
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>8</x>
|
||||
<y>262</y>
|
||||
<y>540</y>
|
||||
<width>191</width>
|
||||
<height>21</height>
|
||||
</rect>
|
||||
@ -505,6 +520,24 @@
|
||||
<string>on the value above £ there is no charge</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>284</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>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
|
153
gui/platform_list.ui
Normal file
153
gui/platform_list.ui
Normal file
@ -0,0 +1,153 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>PlatformList</class>
|
||||
<widget class="QWidget" name="PlatformList">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>263</width>
|
||||
<height>473</height>
|
||||
</rect>
|
||||
</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">
|
||||
<string>Platform List</string>
|
||||
</property>
|
||||
<widget class="QListWidget" name="platListWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>34</y>
|
||||
<width>243</width>
|
||||
<height>371</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>11</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="add_plat_but">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>11</x>
|
||||
<y>413</y>
|
||||
<width>121</width>
|
||||
<height>24</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>10</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Add platform</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="del_plat_but">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>135</x>
|
||||
<y>413</y>
|
||||
<width>121</width>
|
||||
<height>24</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>10</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Remove platform</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="plist_save_but">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>136</x>
|
||||
<y>442</y>
|
||||
<width>121</width>
|
||||
<height>24</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>10</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Save</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="edit_plat_but">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>11</x>
|
||||
<y>442</y>
|
||||
<width>121</width>
|
||||
<height>24</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>10</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Edit platform</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QCheckBox" name="plat_enabled_check">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>12</x>
|
||||
<y>8</y>
|
||||
<width>231</width>
|
||||
<height>20</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>11</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Platform enabled?</string>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>plist_save_but</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>PlatformList</receiver>
|
||||
<slot>close()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>196</x>
|
||||
<y>453</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>131</x>
|
||||
<y>236</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
16
src/data_struct.py
Normal file
16
src/data_struct.py
Normal file
@ -0,0 +1,16 @@
|
||||
class Platform:
|
||||
def __init__(self, plat_id, fund_plat_fee, plat_name, enabled, fund_deal_fee,
|
||||
share_plat_fee, share_plat_max_fee, share_deal_fee,
|
||||
share_deal_reduce_trades, share_deal_reduce_amount,
|
||||
):
|
||||
|
||||
self.plat_id = plat_id
|
||||
self.fund_plat_fee = fund_plat_fee
|
||||
self.plat_name = plat_name
|
||||
self.enabled = enabled
|
||||
self.fund_deal_fee = fund_deal_fee
|
||||
self.share_plat_fee = share_plat_fee
|
||||
self.share_plat_max_fee = share_plat_max_fee
|
||||
self.share_deal_fee = share_deal_fee
|
||||
self.share_deal_reduce_trades = share_deal_reduce_trades
|
||||
self.share_deal_reduce_amount = share_deal_reduce_amount
|
264
src/db_handler.py
Normal file
264
src/db_handler.py
Normal file
@ -0,0 +1,264 @@
|
||||
import os
|
||||
import sqlite3
|
||||
|
||||
import resource_finder
|
||||
from data_struct import Platform
|
||||
|
||||
|
||||
class DBHandler:
|
||||
def __init__(self):
|
||||
# Function to create all necessary database tables if the DB file doesn't exist
|
||||
def create_tables():
|
||||
self.cur.execute("""
|
||||
CREATE TABLE "tblPlatforms" (
|
||||
"PlatformID" INTEGER NOT NULL UNIQUE,
|
||||
"PlatformName" TEXT,
|
||||
"IsEnabled" INTEGER NOT NULL,
|
||||
PRIMARY KEY("PlatformID")
|
||||
)
|
||||
""")
|
||||
|
||||
self.cur.execute("""
|
||||
CREATE TABLE "tblFlatPlatFees" (
|
||||
"PlatformID" INTEGER NOT NULL UNIQUE,
|
||||
"SharePlatFee" REAL NOT NULL,
|
||||
"SharePlatMaxFee" REAL,
|
||||
PRIMARY KEY("PlatformID"),
|
||||
FOREIGN KEY("PlatformID") REFERENCES "tblPlatforms"("PlatformID")
|
||||
)
|
||||
""")
|
||||
|
||||
self.cur.execute("""
|
||||
CREATE TABLE "tblFlatDealFees" (
|
||||
"PlatformID" INTEGER NOT NULL UNIQUE,
|
||||
"FundDealFee" REAL,
|
||||
"ShareDealFee" REAL NOT NULL,
|
||||
"ShareDealReduceTrades" REAL,
|
||||
"ShareDealReduceAmount" REAL,
|
||||
PRIMARY KEY("PlatformID"),
|
||||
FOREIGN KEY("PlatformID") REFERENCES "tblPlatforms"("PlatformID")
|
||||
)
|
||||
""")
|
||||
|
||||
self.cur.execute("""
|
||||
CREATE TABLE "tblFundPlatFee" (
|
||||
"PlatformID" INTEGER NOT NULL,
|
||||
"Band" REAL NOT NULL,
|
||||
"Fee" REAL NOT NULL,
|
||||
PRIMARY KEY("PlatformID","Band","Fee"),
|
||||
FOREIGN KEY("PlatformID") REFERENCES "tblPlatforms"("PlatformID")
|
||||
)
|
||||
""")
|
||||
|
||||
self.cur.execute("""
|
||||
CREATE TABLE "tblUserDetails" (
|
||||
"UserID" INTEGER NOT NULL UNIQUE,
|
||||
"PensionValue" REAL NOT NULL,
|
||||
"SliderValue" INTEGER NOT NULL,
|
||||
"ShareTrades" INTEGER NOT NULL,
|
||||
"FundTrades" INTEGER NOT NULL,
|
||||
PRIMARY KEY("UserID")
|
||||
)
|
||||
""")
|
||||
|
||||
if not os.path.exists(resource_finder.get_res_path("SIPPCompare.db")):
|
||||
db_exists = False
|
||||
else:
|
||||
db_exists = True
|
||||
|
||||
self.conn = sqlite3.connect(resource_finder.get_res_path("SIPPCompare.db"))
|
||||
self.cur = self.conn.cursor()
|
||||
if not db_exists:
|
||||
create_tables()
|
||||
|
||||
# Retrieve the list of platform names for list population
|
||||
def retrieve_plat_list(self) -> list:
|
||||
res = self.cur.execute("SELECT PlatformName FROM tblPlatforms").fetchall()
|
||||
plat_name_list = []
|
||||
for platform in res:
|
||||
plat_name_list.append(platform[0])
|
||||
|
||||
return plat_name_list
|
||||
|
||||
def write_platforms(self, plat_list: list[Platform]):
|
||||
for i in range(len(plat_list)):
|
||||
platforms_data = [
|
||||
plat_list[i].plat_id,
|
||||
plat_list[i].plat_name,
|
||||
plat_list[i].enabled
|
||||
]
|
||||
|
||||
flat_plat_fees_data = [
|
||||
plat_list[i].plat_id,
|
||||
plat_list[i].share_plat_fee,
|
||||
plat_list[i].share_plat_max_fee
|
||||
]
|
||||
|
||||
flat_deal_fees_data = [
|
||||
plat_list[i].plat_id,
|
||||
plat_list[i].fund_deal_fee,
|
||||
plat_list[i].share_deal_fee,
|
||||
plat_list[i].share_deal_reduce_trades,
|
||||
plat_list[i].share_deal_reduce_amount
|
||||
]
|
||||
|
||||
res = self.cur.execute(f"""
|
||||
SELECT EXISTS(
|
||||
SELECT PlatformID FROM tblPlatforms
|
||||
WHERE PlatformID = {i}
|
||||
)""").fetchall()
|
||||
|
||||
if res[0][0] == 1:
|
||||
self.cur.execute(f"""
|
||||
UPDATE tblPlatforms SET
|
||||
PlatformID = ?,
|
||||
PlatformName = ?,
|
||||
IsEnabled = ?
|
||||
WHERE PlatformID = {i}
|
||||
""", platforms_data)
|
||||
|
||||
self.cur.execute(f"""
|
||||
UPDATE tblFlatPlatFees SET
|
||||
PlatformID = ?,
|
||||
SharePlatFee = ?,
|
||||
SharePlatMaxFee = ?
|
||||
WHERE PlatformID = {i}
|
||||
""", flat_plat_fees_data)
|
||||
|
||||
self.cur.execute(f"""
|
||||
UPDATE tblFlatDealFees SET
|
||||
PlatformID = ?,
|
||||
FundDealFee = ?,
|
||||
ShareDealFee = ?,
|
||||
ShareDealReduceTrades = ?,
|
||||
ShareDealReduceAmount = ?
|
||||
WHERE PlatformID = {i}
|
||||
""", flat_deal_fees_data)
|
||||
|
||||
self.cur.execute(f"DELETE FROM tblFundPlatFee WHERE PlatformID = {i}")
|
||||
else:
|
||||
self.cur.execute("INSERT INTO tblPlatforms VALUES (?, ?, ?)", platforms_data)
|
||||
self.cur.execute("INSERT INTO tblFlatPlatFees VALUES (?, ?, ?)", flat_plat_fees_data)
|
||||
self.cur.execute("INSERT INTO tblFlatDealFees VALUES (?, ?, ?, ?, ?)", flat_deal_fees_data)
|
||||
|
||||
exec_str = f"INSERT INTO tblFundPlatFee VALUES\n"
|
||||
for x in range(len(plat_list[i].fund_plat_fee[0])):
|
||||
band = plat_list[i].fund_plat_fee[0][x]
|
||||
fee = plat_list[i].fund_plat_fee[1][x]
|
||||
exec_str += f"({i}, {band}, {fee}),\n"
|
||||
exec_str = exec_str[:-2]
|
||||
self.cur.execute(exec_str)
|
||||
|
||||
self.conn.commit()
|
||||
|
||||
# Retrieve all info about all platforms in DB and initialise Platform objects
|
||||
def retrieve_platforms(self) -> list[Platform]:
|
||||
# Retrieve all one-to-one relations
|
||||
platforms_res = self.cur.execute("""
|
||||
SELECT
|
||||
-- tblPlatforms
|
||||
tblPlatforms.PlatformID, PlatformName, IsEnabled,
|
||||
-- tblFlatPlatFees
|
||||
SharePlatFee, SharePlatMaxFee,
|
||||
-- tblFlatDealFees
|
||||
FundDealFee,
|
||||
ShareDealFee,
|
||||
ShareDealReduceTrades,
|
||||
ShareDealReduceAmount
|
||||
FROM tblPlatforms
|
||||
INNER JOIN tblFlatPlatFees ON
|
||||
tblPlatforms.PlatformID = tblFlatPlatFees.PlatformID
|
||||
INNER JOIN tblFlatDealFees ON
|
||||
tblPlatforms.PlatformID = tblFlatDealFees.PlatformID
|
||||
""").fetchall()
|
||||
|
||||
platforms = []
|
||||
for platform in platforms_res:
|
||||
# plat_id, plat_name, enabled, share_plat_fee, share_plat_max_fee, fund_deal_fee,
|
||||
# share_deal_fee, share_deal_reduce_trades, share_deal_reduce_amount
|
||||
this_platform = [platform[0], platform[1], platform[2], platform[3], platform[4],
|
||||
platform[5], platform[6], platform[7], platform[8]]
|
||||
platforms.append(this_platform)
|
||||
|
||||
# Insert 2D array into each platform data in preparation for fund_plat_fee retrival
|
||||
for platform in platforms:
|
||||
platform.insert(1, [[], []])
|
||||
|
||||
# Get all records from tblFundPlatFee, add them to the platforms list based on ID
|
||||
# WARNING: This code is dependent on PlatformID being sequential from 0 in DB records
|
||||
fund_plat_fee_res = self.cur.execute("SELECT * FROM tblFundPlatFee ORDER BY PlatformID ASC").fetchall()
|
||||
for i in range(len(fund_plat_fee_res)):
|
||||
plat_id = fund_plat_fee_res[i][0]
|
||||
platforms[plat_id][1][0].append(fund_plat_fee_res[i][1])
|
||||
platforms[plat_id][1][1].append(fund_plat_fee_res[i][2])
|
||||
|
||||
platform_obj_list: list[Platform] = []
|
||||
for platform in platforms:
|
||||
platform_obj_list.append(Platform(
|
||||
platform[0], platform[1], platform[2], platform[3],
|
||||
platform[6], platform[4], platform[5], platform[7],
|
||||
platform[8], platform[9]
|
||||
))
|
||||
|
||||
return platform_obj_list
|
||||
|
||||
# This function writes the details the user entered this session to the DB
|
||||
def write_user_details(self, pension_val: float, slider_val: int, share_trades: int, fund_trades: int):
|
||||
# Hardcode UserID as 0
|
||||
user_details_data = (0, pension_val, slider_val, share_trades, fund_trades)
|
||||
|
||||
# Check if there is already a record in tblUserDetails
|
||||
res = self.cur.execute("SELECT EXISTS(SELECT 1 FROM tblUserDetails)").fetchone()
|
||||
if res[0] == 0:
|
||||
# If there isn't then insert a new record
|
||||
self.cur.execute("INSERT INTO tblUserDetails VALUES (?, ?, ?, ?, ?)", user_details_data)
|
||||
else:
|
||||
# If there is then update the existing record (only ever one record as of now)
|
||||
self.cur.execute("""
|
||||
UPDATE tblUserDetails SET
|
||||
UserID = ?,
|
||||
PensionValue = ?,
|
||||
SliderValue = ?,
|
||||
ShareTrades = ?,
|
||||
FundTrades = ?
|
||||
""", user_details_data)
|
||||
self.conn.commit()
|
||||
|
||||
# Function to retrieve details entered by the user in prev session from DB
|
||||
def retrieve_user_details(self) -> dict:
|
||||
res = self.cur.execute("SELECT EXISTS(SELECT 1 FROM tblUserDetails)").fetchone()
|
||||
if res[0] == 0:
|
||||
return {"NO_RECORD": None}
|
||||
|
||||
res = self.cur.execute("SELECT * FROM tblUserDetails")
|
||||
res_tuple: tuple = res.fetchone()
|
||||
user_details_dict: dict[str, float | int] = {
|
||||
"user_id": res_tuple[0],
|
||||
"pension_val": res_tuple[1],
|
||||
"slider_val": res_tuple[2],
|
||||
"share_trades": res_tuple[3],
|
||||
"fund_trades": res_tuple[4]
|
||||
}
|
||||
|
||||
return user_details_dict
|
||||
|
||||
def toggle_platform_state(self, index: int, state: bool):
|
||||
self.cur.execute("UPDATE tblPlatforms SET IsEnabled = ? WHERE PlatformID = ?", [state, index])
|
||||
self.conn.commit()
|
||||
|
||||
def remove_platform(self, index: int):
|
||||
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()
|
17
src/main.py
17
src/main.py
@ -1,20 +1,11 @@
|
||||
from PyQt6.QtWidgets import QApplication
|
||||
|
||||
import sys
|
||||
|
||||
import platform_edit
|
||||
from PyQt6.QtWidgets import QApplication
|
||||
|
||||
from main_window import SIPPCompare
|
||||
|
||||
|
||||
app = QApplication(sys.argv)
|
||||
# Show platform edit window first, before main win
|
||||
# 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 = SIPPCompare()
|
||||
window.show()
|
||||
app.exec()
|
||||
|
@ -1,48 +1,41 @@
|
||||
from PyQt6.QtGui import QIntValidator, QIcon
|
||||
from PyQt6.QtWidgets import QMainWindow, QWidget
|
||||
from PyQt6 import uic
|
||||
from PyQt6.QtGui import QIntValidator, QIcon
|
||||
from PyQt6.QtWidgets import QMainWindow, QApplication
|
||||
|
||||
import output_window
|
||||
import resource_finder
|
||||
from db_handler import DBHandler
|
||||
from platform_list import PlatformList
|
||||
from output_window import OutputWindow
|
||||
|
||||
|
||||
class SIPPCompare(QMainWindow):
|
||||
# Receive instance of PlatformEdit() as parameter
|
||||
def __init__(self, plat_edit_win: QWidget):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
# Import Qt Designer UI XML file
|
||||
uic.loadUi(resource_finder.get_res_path("gui/main_gui.ui"), self)
|
||||
self.setWindowIcon(QIcon(resource_finder.get_res_path("icon2.ico")))
|
||||
|
||||
# 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
|
||||
self.fund_plat_fees = 0.0
|
||||
self.fund_deal_fees = 0.0
|
||||
self.share_plat_fees = 0.0
|
||||
self.share_deal_fees = 0.0
|
||||
self.results = []
|
||||
|
||||
# Create window objects
|
||||
self.platform_win = plat_edit_win
|
||||
self.output_win = output_window.OutputWindow()
|
||||
self.db = DBHandler()
|
||||
self.platform_list_win = PlatformList(self.db)
|
||||
self.output_win = OutputWindow()
|
||||
|
||||
# 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)
|
||||
# Menu bar entry (File -> Platform List)
|
||||
self.actionList_Platforms.triggered.connect(self.show_platform_list)
|
||||
# Update percentage mix label when slider moved
|
||||
self.mix_slider.valueChanged.connect(self.update_slider_lab)
|
||||
self.value_input.valueChanged.connect(self.check_valid)
|
||||
# Validate input
|
||||
self.share_trades_combo.currentTextChanged.connect(self.check_valid)
|
||||
self.fund_trades_combo.currentTextChanged.connect(self.check_valid)
|
||||
|
||||
@ -50,12 +43,22 @@ class SIPPCompare(QMainWindow):
|
||||
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()
|
||||
if "NO_RECORD" not in prev_session_data:
|
||||
self.value_input.setValue(prev_session_data["pension_val"])
|
||||
self.mix_slider.setValue(prev_session_data["slider_val"])
|
||||
self.share_trades_combo.setCurrentText(str(prev_session_data["share_trades"]))
|
||||
self.fund_trades_combo.setCurrentText(str(prev_session_data["fund_trades"]))
|
||||
self.calc_but.setFocus()
|
||||
|
||||
# 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)
|
||||
|
||||
# Ensure that trade fields aren't blank and pension value > 0
|
||||
def check_valid(self):
|
||||
if self.share_trades_combo.currentText() != "" \
|
||||
and self.fund_trades_combo.currentText() != "" \
|
||||
@ -64,90 +67,71 @@ class SIPPCompare(QMainWindow):
|
||||
else:
|
||||
self.calc_but.setEnabled(False)
|
||||
|
||||
# Get variables from platform editor input fields
|
||||
def init_variables(self):
|
||||
self.optional_boxes = self.platform_win.get_optional_boxes()
|
||||
self.fund_plat_fee = self.platform_win.get_fund_plat_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.fund_deal_fee = self.platform_win.get_fund_deal_fee()
|
||||
else:
|
||||
self.fund_deal_fee = None
|
||||
|
||||
if self.optional_boxes[2]:
|
||||
self.share_plat_max_fee = self.platform_win.get_share_plat_max_fee()
|
||||
else:
|
||||
self.share_plat_max_fee = None
|
||||
|
||||
if self.optional_boxes[3]:
|
||||
self.share_deal_reduce_trades = self.platform_win.get_share_deal_reduce_trades()
|
||||
else:
|
||||
self.share_deal_reduce_trades = None
|
||||
|
||||
if self.optional_boxes[4]:
|
||||
self.share_deal_reduce_amount = self.platform_win.get_share_deal_reduce_amount()
|
||||
else:
|
||||
self.share_deal_reduce_amount = None
|
||||
|
||||
# Calculate fees
|
||||
# Calculate fees for all active platforms
|
||||
def calculate_fees(self):
|
||||
self.init_variables()
|
||||
# Set to zero each time to avoid persistence
|
||||
self.fund_plat_fees = 0
|
||||
# Set to empty list each time to avoid persistence
|
||||
self.results = []
|
||||
|
||||
# Get user input
|
||||
value_num = float(self.value_input.value())
|
||||
# Funds/shares mix
|
||||
slider_val: int = self.mix_slider.value()
|
||||
funds_value = (slider_val / 100) * value_num
|
||||
fund_trades_num = int(self.fund_trades_combo.currentText())
|
||||
if self.fund_deal_fee is not None:
|
||||
self.fund_deal_fees = fund_trades_num * self.fund_deal_fee
|
||||
|
||||
for i in range(1, len(self.fund_plat_fee[0])):
|
||||
band = self.fund_plat_fee[0][i]
|
||||
prev_band = self.fund_plat_fee[0][i - 1]
|
||||
fee = self.fund_plat_fee[1][i]
|
||||
gap = (band - prev_band)
|
||||
|
||||
if funds_value > gap:
|
||||
self.fund_plat_fees += gap * (fee / 100)
|
||||
funds_value -= gap
|
||||
else:
|
||||
self.fund_plat_fees += funds_value * (fee / 100)
|
||||
break
|
||||
|
||||
shares_value = (1 - (slider_val / 100)) * value_num
|
||||
if self.share_plat_max_fee is not None:
|
||||
if (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 self.share_deal_reduce_trades is not None:
|
||||
if (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
|
||||
shares_value = (1 - (slider_val / 100)) * value_num
|
||||
|
||||
for platform in self.platform_list_win.plat_list:
|
||||
if not platform.enabled:
|
||||
continue
|
||||
|
||||
fund_plat_fees = 0.0
|
||||
fund_deal_fees = 0.0
|
||||
share_plat_fees = 0.0
|
||||
share_deal_fees = 0.0
|
||||
plat_name = platform.plat_name
|
||||
|
||||
if platform.fund_deal_fee is not None:
|
||||
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])):
|
||||
band = platform.fund_plat_fee[0][i]
|
||||
prev_band = platform.fund_plat_fee[0][i - 1]
|
||||
fee = platform.fund_plat_fee[1][i]
|
||||
gap = (band - prev_band)
|
||||
|
||||
if funds_value > gap:
|
||||
fund_plat_fees += gap * (fee / 100)
|
||||
funds_value -= gap
|
||||
else:
|
||||
fund_plat_fees += funds_value * (fee / 100)
|
||||
break
|
||||
|
||||
if platform.share_plat_max_fee is not None:
|
||||
if (platform.share_plat_fee * shares_value / 12) > platform.share_plat_max_fee:
|
||||
share_plat_fees = platform.share_plat_max_fee * 12
|
||||
else:
|
||||
share_plat_fees = platform.share_plat_fee * shares_value
|
||||
|
||||
if platform.share_deal_reduce_trades is not None:
|
||||
if (share_trades_num / 12) >= platform.share_deal_reduce_trades:
|
||||
share_deal_fees = platform.share_deal_reduce_amount * share_trades_num
|
||||
else:
|
||||
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])
|
||||
|
||||
# Save details entered by user for next session
|
||||
self.db.write_user_details(value_num, slider_val, share_trades_num, fund_trades_num)
|
||||
self.show_output_win()
|
||||
|
||||
# Show the output window - this func is called from calculate_fee()
|
||||
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.output_win.display_output(self.results, 1)
|
||||
self.output_win.activateWindow()
|
||||
self.output_win.raise_()
|
||||
self.output_win.show()
|
||||
QApplication.alert(self.output_win)
|
||||
|
||||
# Show the platform editor window (currently run-time only)
|
||||
def show_platform_edit(self):
|
||||
self.platform_win.show()
|
||||
def show_platform_list(self):
|
||||
self.platform_list_win.show()
|
||||
|
@ -1,11 +1,32 @@
|
||||
from PyQt6.QtGui import QIcon
|
||||
from PyQt6.QtWidgets import QWidget
|
||||
from PyQt6 import uic
|
||||
|
||||
import datetime
|
||||
import os
|
||||
from datetime import datetime
|
||||
|
||||
from PyQt6 import uic
|
||||
from PyQt6.QtGui import QIcon, QFont
|
||||
from PyQt6.QtWidgets import QWidget, QFileDialog, QMessageBox, QDialogButtonBox
|
||||
|
||||
import resource_finder
|
||||
from widgets.mpl_widget import MplWidget
|
||||
|
||||
|
||||
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):
|
||||
@ -15,50 +36,103 @@ class OutputWindow(QWidget):
|
||||
uic.loadUi(resource_finder.get_res_path("gui/output_window.ui"), self)
|
||||
self.setWindowIcon(QIcon(resource_finder.get_res_path("icon2.ico")))
|
||||
|
||||
# Initialise class variables
|
||||
self.results_str = ""
|
||||
self.platform_name = ""
|
||||
# Define class variables
|
||||
self.save_err_dialog = SaveFailure()
|
||||
self.canvas = self.graphWidget.canvas
|
||||
self.ax = self.canvas.axes
|
||||
self.fig = self.canvas.figure
|
||||
self.results = []
|
||||
|
||||
# Handle events
|
||||
self.res_save_but.clicked.connect(self.save_results)
|
||||
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 save_results(self):
|
||||
# Use datatime for txt filename
|
||||
cur_time = datetime.datetime.now()
|
||||
if not os.path.exists("output"):
|
||||
os.makedirs("output")
|
||||
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)
|
||||
def display_output(self, results: list, years: int):
|
||||
self.results = results
|
||||
self.ax.clear()
|
||||
self.ax.cla()
|
||||
self.canvas.draw_idle()
|
||||
|
||||
# Display fees in output window as plaintext readout
|
||||
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
|
||||
if self.platform_name is not None:
|
||||
self.results_str = f"Fees breakdown (Platform \"{self.platform_name}\"):"
|
||||
else:
|
||||
self.results_str = f"Fees breakdown:"
|
||||
names = []
|
||||
values = []
|
||||
for result in results:
|
||||
names.append(result[4])
|
||||
values.append(sum(result[:4]) * years)
|
||||
|
||||
self.results_str += "\n\nPlatform fees:"
|
||||
# :.2f is used in order to display 2 decimal places (currency form)
|
||||
self.results_str += f"\n\tFund platform fees: £{round(fund_plat_fees, 2):.2f}"
|
||||
self.results_str += f"\n\tShare platform fees: £{round(share_plat_fees, 2):.2f}"
|
||||
total_plat_fees = fund_plat_fees + share_plat_fees
|
||||
self.results_str += f"\n\tTotal platform fees: £{round(total_plat_fees, 2):.2f}"
|
||||
names = sorted(names, key=lambda x: values[names.index(x)], reverse=True)
|
||||
values = sorted(values, reverse=True)
|
||||
|
||||
self.results_str += "\n\nDealing fees:"
|
||||
self.results_str += f"\n\tFund dealing fees: £{round(fund_deal_fees, 2):.2f}"
|
||||
self.results_str += f"\n\tShare dealing fees: £{round(share_deal_fees, 2):.2f}"
|
||||
total_deal_fees = fund_deal_fees + share_deal_fees
|
||||
self.results_str += f"\n\tTotal dealing fees: £{round(total_deal_fees, 2):.2f}"
|
||||
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])
|
||||
|
||||
total_fees = total_plat_fees + total_deal_fees
|
||||
self.results_str += f"\n\nTotal fees: £{round(total_fees, 2):.2f}"
|
||||
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]
|
||||
|
||||
self.output.setText(self.results_str)
|
||||
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,39 +1,29 @@
|
||||
from PyQt6.QtCore import QRegularExpression
|
||||
from PyQt6 import uic
|
||||
from PyQt6.QtCore import QRegularExpression, QRect
|
||||
from PyQt6.QtGui import QRegularExpressionValidator, QFont, QIcon
|
||||
from PyQt6.QtWidgets import QWidget, QLabel
|
||||
from PyQt6 import uic
|
||||
|
||||
from widgets.fastedit_spinbox import FastEditQDoubleSpinBox
|
||||
import main_window
|
||||
import resource_finder
|
||||
from db_handler import DBHandler
|
||||
from data_struct import Platform
|
||||
from widgets.fastedit_spinbox import FastEditQDoubleSpinBox
|
||||
|
||||
|
||||
class PlatformEdit(QWidget):
|
||||
def __init__(self, autofill: bool):
|
||||
def __init__(self, plat: Platform):
|
||||
super().__init__()
|
||||
# Import Qt Designer UI XML file
|
||||
uic.loadUi(resource_finder.get_res_path("gui/platform_edit.ui"), self)
|
||||
self.setWindowIcon(QIcon(resource_finder.get_res_path("icon2.ico")))
|
||||
|
||||
# Initialise class variables
|
||||
# Create main window object, passing this instance as param
|
||||
self.main_win = main_window.SIPPCompare(self)
|
||||
|
||||
self.fund_plat_fee = []
|
||||
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
|
||||
self.plat = plat
|
||||
self.fund_plat_fee = self.plat.fund_plat_fee
|
||||
self.widgets_list_list = []
|
||||
|
||||
self.fund_fee_rows = 1
|
||||
# Debugging feature: set with "--DEBUG_AUTOFILL" cmd argument
|
||||
self.autofill = autofill
|
||||
if autofill:
|
||||
self.save_but.setEnabled(True)
|
||||
if len(self.plat.fund_plat_fee[0]) > 1:
|
||||
self.fund_fee_rows = len(self.plat.fund_plat_fee[0]) - 1
|
||||
else:
|
||||
self.fund_fee_rows = 1
|
||||
|
||||
self.required_fields = [
|
||||
self.share_plat_fee_box,
|
||||
@ -64,6 +54,57 @@ class PlatformEdit(QWidget):
|
||||
False
|
||||
]
|
||||
|
||||
# Set optional checkboxes based on DB storage
|
||||
if self.plat.plat_name is None:
|
||||
self.check_boxes_ticked[0] = False
|
||||
self.plat_name_check.setChecked(False)
|
||||
else:
|
||||
self.check_boxes_ticked[0] = True
|
||||
self.plat_name_check.setChecked(True)
|
||||
self.plat_name_box.setText(self.plat.plat_name)
|
||||
|
||||
if self.plat.fund_deal_fee is None:
|
||||
self.check_boxes_ticked[1] = False
|
||||
self.fund_deal_fee_check.setChecked(False)
|
||||
else:
|
||||
self.check_boxes_ticked[1] = True
|
||||
self.fund_deal_fee_check.setChecked(True)
|
||||
self.fund_deal_fee_box.setValue(self.plat.fund_deal_fee)
|
||||
|
||||
self.share_plat_fee_box.setValue(self.plat.share_plat_fee * 100)
|
||||
|
||||
if self.plat.share_plat_max_fee is None:
|
||||
self.check_boxes_ticked[2] = False
|
||||
self.share_plat_max_fee_check.setChecked(False)
|
||||
else:
|
||||
self.check_boxes_ticked[2] = True
|
||||
self.share_plat_max_fee_check.setChecked(True)
|
||||
self.share_plat_max_fee_box.setValue(self.plat.share_plat_max_fee)
|
||||
|
||||
self.share_deal_fee_box.setValue(self.plat.share_deal_fee)
|
||||
|
||||
if self.plat.share_deal_reduce_trades is None:
|
||||
self.check_boxes_ticked[3] = False
|
||||
self.share_deal_reduce_trades_check.setChecked(False)
|
||||
else:
|
||||
self.check_boxes_ticked[3] = True
|
||||
self.share_deal_reduce_trades_check.setChecked(True)
|
||||
self.share_deal_reduce_trades_box.setValue(int(self.plat.share_deal_reduce_trades))
|
||||
|
||||
if self.plat.share_deal_reduce_trades is None:
|
||||
self.check_boxes_ticked[4] = False
|
||||
self.share_deal_reduce_amount_check.setChecked(False)
|
||||
else:
|
||||
self.check_boxes_ticked[4] = True
|
||||
self.share_deal_reduce_amount_check.setChecked(True)
|
||||
self.share_deal_reduce_amount_box.setValue(self.plat.share_deal_reduce_amount)
|
||||
|
||||
# Populate fund platform fee rows from DB
|
||||
if len(self.plat.fund_plat_fee[0]) > 1:
|
||||
self.first_tier_box.setValue(self.plat.fund_plat_fee[0][1])
|
||||
self.first_tier_fee_box.setValue(self.plat.fund_plat_fee[1][1])
|
||||
self.add_row(loading=True)
|
||||
|
||||
# Handle events
|
||||
for field in self.required_fields:
|
||||
field.valueChanged.connect(self.check_valid)
|
||||
@ -93,7 +134,7 @@ class PlatformEdit(QWidget):
|
||||
QRegularExpressionValidator(QRegularExpression("\\w*"))
|
||||
)
|
||||
|
||||
def create_plat_fee_struct(self):
|
||||
def create_plat_fee_struct(self) -> list:
|
||||
plat_fee_struct = [[0], [0]]
|
||||
plat_fee_struct[0].append(self.first_tier_box.value())
|
||||
plat_fee_struct[1].append(self.first_tier_fee_box.value())
|
||||
@ -108,32 +149,34 @@ class PlatformEdit(QWidget):
|
||||
|
||||
# Get fee structure variables from user input when "Save" clicked
|
||||
def init_variables(self):
|
||||
# If debugging, save time by hardcoding
|
||||
if self.autofill:
|
||||
self.plat_name = "AJBell"
|
||||
self.fund_plat_fee = [
|
||||
[0, 250000, 1000000, 2000000],
|
||||
[0, 0.25, 0.1, 0.05]
|
||||
]
|
||||
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
|
||||
self.check_boxes_ticked = [True, True, True, True, True]
|
||||
else:
|
||||
self.plat_name = self.plat_name_box.text()
|
||||
self.fund_plat_fee = self.create_plat_fee_struct()
|
||||
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())
|
||||
self.plat.fund_plat_fee = self.create_plat_fee_struct()
|
||||
self.plat.share_plat_fee = float(self.share_plat_fee_box.value()) / 100
|
||||
self.plat.share_deal_fee = float(self.share_deal_fee_box.value())
|
||||
|
||||
# Once user input is received show main window
|
||||
self.main_win.show()
|
||||
if self.check_boxes_ticked[0]:
|
||||
self.plat.plat_name = self.plat_name_box.text()
|
||||
else:
|
||||
self.plat.plat_name = None
|
||||
|
||||
if self.check_boxes_ticked[1]:
|
||||
self.plat.fund_deal_fee = float(self.fund_deal_fee_box.value())
|
||||
else:
|
||||
self.plat.fund_deal_fee = None
|
||||
|
||||
if self.check_boxes_ticked[2]:
|
||||
self.plat.share_plat_max_fee = float(self.share_plat_max_fee_box.value())
|
||||
else:
|
||||
self.plat.share_plat_max_fee = None
|
||||
|
||||
if self.check_boxes_ticked[3]:
|
||||
self.plat.share_deal_reduce_trades = int(self.share_deal_reduce_trades_box.value())
|
||||
else:
|
||||
self.plat.share_deal_reduce_trades = None
|
||||
|
||||
if self.check_boxes_ticked[4]:
|
||||
self.plat.share_deal_reduce_amount = float(self.share_deal_reduce_amount_box.value())
|
||||
else:
|
||||
self.plat.share_deal_reduce_amount = None
|
||||
|
||||
# This method does multiple things in order to validate the user's inputs:
|
||||
# 1) Check all required fields have a non-zero value
|
||||
@ -215,50 +258,67 @@ class PlatformEdit(QWidget):
|
||||
max_band = self.first_tier_box.value()
|
||||
self.val_above_lab.setText(f"on the value above £{int(max_band)} there is no charge")
|
||||
|
||||
def add_row(self):
|
||||
widgets = []
|
||||
font = QFont()
|
||||
font.setPointSize(11)
|
||||
def add_row(self, loading: bool = False):
|
||||
if loading:
|
||||
rows_needed = self.fund_fee_rows - 1
|
||||
else:
|
||||
rows_needed = 1
|
||||
|
||||
widgets.append(QLabel(self.gridLayoutWidget_2))
|
||||
widgets[0].setFont(font)
|
||||
for x in range(rows_needed):
|
||||
widgets = []
|
||||
font = QFont()
|
||||
font.setPointSize(11)
|
||||
|
||||
widgets.append(FastEditQDoubleSpinBox(self.gridLayoutWidget_2))
|
||||
widgets[1].setPrefix("£")
|
||||
widgets[1].setMaximum(9999999)
|
||||
widgets[1].setButtonSymbols(FastEditQDoubleSpinBox.ButtonSymbols.NoButtons)
|
||||
widgets[1].setFont(font)
|
||||
widgets[1].valueChanged.connect(self.check_valid)
|
||||
widgets[1].valueChanged.connect(self.update_tier_labels)
|
||||
widgets.append(QLabel(self.gridLayoutWidget_2))
|
||||
widgets[0].setFont(font)
|
||||
|
||||
widgets.append(QLabel(self.gridLayoutWidget_2))
|
||||
widgets[2].setText(f"the fee is")
|
||||
widgets[2].setFont(font)
|
||||
widgets.append(FastEditQDoubleSpinBox(self.gridLayoutWidget_2))
|
||||
widgets[1].setPrefix("£")
|
||||
widgets[1].setMaximum(9999999)
|
||||
widgets[1].setButtonSymbols(FastEditQDoubleSpinBox.ButtonSymbols.NoButtons)
|
||||
widgets[1].setFont(font)
|
||||
if loading:
|
||||
widgets[1].setValue(self.plat.fund_plat_fee[0][x+2])
|
||||
widgets[1].valueChanged.connect(self.check_valid)
|
||||
widgets[1].valueChanged.connect(self.update_tier_labels)
|
||||
|
||||
widgets.append(FastEditQDoubleSpinBox(self.gridLayoutWidget_2))
|
||||
widgets[3].setSuffix("%")
|
||||
widgets[3].setMaximum(100)
|
||||
widgets[3].setButtonSymbols(FastEditQDoubleSpinBox.ButtonSymbols.NoButtons)
|
||||
widgets[3].setFont(font)
|
||||
widgets[3].valueChanged.connect(self.check_valid)
|
||||
widgets.append(QLabel(self.gridLayoutWidget_2))
|
||||
widgets[2].setText(f"the fee is")
|
||||
widgets[2].setFont(font)
|
||||
|
||||
# TODO: why 28.5?
|
||||
self.gridLayoutWidget_2.setGeometry(11, 309, 611, int(round(28.5 * (self.fund_fee_rows + 1), 0)))
|
||||
for i in range(len(widgets)):
|
||||
self.gridLayout_2.addWidget(widgets[i], self.fund_fee_rows, i, 1, 1)
|
||||
widgets.append(FastEditQDoubleSpinBox(self.gridLayoutWidget_2))
|
||||
widgets[3].setSuffix("%")
|
||||
widgets[3].setMaximum(100)
|
||||
widgets[3].setButtonSymbols(FastEditQDoubleSpinBox.ButtonSymbols.NoButtons)
|
||||
widgets[3].setFont(font)
|
||||
if loading:
|
||||
widgets[3].setValue(self.plat.fund_plat_fee[1][x+2])
|
||||
widgets[3].valueChanged.connect(self.check_valid)
|
||||
|
||||
self.fund_fee_rows += 1
|
||||
if loading:
|
||||
grid_height = int(round(28.5 * self.fund_fee_rows))
|
||||
else:
|
||||
grid_height = int(round(28.5 * (self.fund_fee_rows + 1)))
|
||||
self.gridLayoutWidget_2.setGeometry(QRect(11, 309, 611, grid_height))
|
||||
for i in range(len(widgets)):
|
||||
if loading:
|
||||
self.gridLayout_2.addWidget(widgets[i], x + 1, i, 1, 1)
|
||||
else:
|
||||
self.gridLayout_2.addWidget(widgets[i], self.fund_fee_rows, i, 1, 1)
|
||||
|
||||
self.widgets_list_list.append(widgets)
|
||||
cur_label_idx = self.gridLayout_2.indexOf(widgets[0])
|
||||
cur_box_idx = self.gridLayout_2.indexOf(widgets[1])
|
||||
cur_label_pos = list(self.gridLayout_2.getItemPosition(cur_label_idx))[:2]
|
||||
cur_box_pos = list(self.gridLayout_2.getItemPosition(cur_box_idx))[:2]
|
||||
if not loading:
|
||||
self.fund_fee_rows += 1
|
||||
|
||||
prev_box_row = cur_box_pos[0] - 1
|
||||
prev_box_item = self.gridLayout_2.itemAtPosition(prev_box_row, cur_box_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")
|
||||
self.widgets_list_list.append(widgets)
|
||||
cur_label_idx = self.gridLayout_2.indexOf(widgets[0])
|
||||
cur_box_idx = self.gridLayout_2.indexOf(widgets[1])
|
||||
cur_label_pos = list(self.gridLayout_2.getItemPosition(cur_label_idx))[:2]
|
||||
cur_box_pos = list(self.gridLayout_2.getItemPosition(cur_box_idx))[:2]
|
||||
|
||||
prev_box_row = cur_box_pos[0] - 1
|
||||
prev_box_item = self.gridLayout_2.itemAtPosition(prev_box_row, cur_box_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")
|
||||
|
||||
if self.fund_fee_rows > 1:
|
||||
self.del_row_but.setEnabled(True)
|
||||
@ -267,8 +327,9 @@ class PlatformEdit(QWidget):
|
||||
self.add_row_but.setEnabled(False)
|
||||
|
||||
self.check_valid()
|
||||
self.update_tier_labels()
|
||||
|
||||
# TODO: Tab order
|
||||
# TODO: Tab/focus order
|
||||
|
||||
def remove_row(self):
|
||||
for widget in self.widgets_list_list[self.fund_fee_rows - 2]:
|
||||
@ -286,31 +347,3 @@ class PlatformEdit(QWidget):
|
||||
|
||||
self.check_valid()
|
||||
self.update_tier_labels()
|
||||
|
||||
# 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
|
||||
|
||||
def get_fund_plat_fee(self):
|
||||
return self.fund_plat_fee
|
||||
|
||||
def get_fund_deal_fee(self):
|
||||
return self.fund_deal_fee
|
||||
|
||||
def get_share_plat_fee(self):
|
||||
return self.share_plat_fee
|
||||
|
||||
def get_share_plat_max_fee(self):
|
||||
return self.share_plat_max_fee
|
||||
|
||||
def get_share_deal_fee(self):
|
||||
return self.share_deal_fee
|
||||
|
||||
def get_share_deal_reduce_trades(self):
|
||||
return self.share_deal_reduce_trades
|
||||
|
||||
def get_share_deal_reduce_amount(self):
|
||||
return self.share_deal_reduce_amount
|
||||
|
148
src/platform_list.py
Normal file
148
src/platform_list.py
Normal file
@ -0,0 +1,148 @@
|
||||
from PyQt6 import uic
|
||||
from PyQt6.QtCore import QRegularExpression
|
||||
from PyQt6.QtGui import QIcon, QRegularExpressionValidator, QFont
|
||||
from PyQt6.QtWidgets import QWidget, QListWidgetItem, QDialog, QDialogButtonBox, QMessageBox
|
||||
|
||||
import resource_finder
|
||||
from db_handler import DBHandler
|
||||
from data_struct import Platform
|
||||
from platform_edit import PlatformEdit
|
||||
|
||||
|
||||
class PlatformRename(QDialog):
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
# Import Qt Designer UI XML file
|
||||
uic.loadUi(resource_finder.get_res_path("gui/dialogs/platform_rename.ui"), self)
|
||||
self.setWindowIcon(QIcon(resource_finder.get_res_path("icon2.ico")))
|
||||
|
||||
self.rename_plat_box.setFocus()
|
||||
self.new_name = ""
|
||||
|
||||
# Set validators
|
||||
# Regex accepts any characters that match [a-Z], [0-9] or _
|
||||
self.rename_plat_box.setValidator(
|
||||
QRegularExpressionValidator(QRegularExpression("\\w*"))
|
||||
)
|
||||
|
||||
self.rename_plat_ok_but.clicked.connect(self.store_new_name)
|
||||
|
||||
def store_new_name(self):
|
||||
self.new_name = self.rename_plat_box.text()
|
||||
|
||||
def closeEvent(self, event):
|
||||
event.ignore()
|
||||
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):
|
||||
def __init__(self, db: DBHandler):
|
||||
super().__init__()
|
||||
# Import Qt Designer UI XML file
|
||||
uic.loadUi(resource_finder.get_res_path("gui/platform_list.ui"), self)
|
||||
self.setWindowIcon(QIcon(resource_finder.get_res_path("icon2.ico")))
|
||||
|
||||
# Initialise class variables
|
||||
self.db = db
|
||||
self.plat_edit_win = None
|
||||
self.plat_list_dialog = PlatformRename()
|
||||
self.del_plat_dialog = RemoveConfirm()
|
||||
self.plat_list = []
|
||||
self.plat_name_list = []
|
||||
self.new_plat_name = ""
|
||||
self.update_plat_list()
|
||||
self.db_indices = [x for x in range(len(self.plat_name_list))]
|
||||
|
||||
# Handle events
|
||||
self.add_plat_but.clicked.connect(self.add_platform)
|
||||
self.del_plat_but.clicked.connect(self.remove_platform)
|
||||
self.edit_plat_but.clicked.connect(self.edit_platform)
|
||||
self.plist_save_but.clicked.connect(self.save_platforms)
|
||||
self.plat_enabled_check.checkStateChanged.connect(self.toggle_platform_state)
|
||||
self.platListWidget.currentRowChanged.connect(self.get_enabled_state)
|
||||
|
||||
def update_plat_list(self):
|
||||
self.plat_name_list = self.db.retrieve_plat_list()
|
||||
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):
|
||||
name_dialog_res = self.plat_list_dialog.exec()
|
||||
if name_dialog_res == QDialog.DialogCode.Accepted:
|
||||
name = self.plat_list_dialog.new_name
|
||||
index = self.platListWidget.count()
|
||||
if name != "":
|
||||
self.platListWidget.addItem(name)
|
||||
name_param = name
|
||||
else:
|
||||
self.platListWidget.addItem(f"Unnamed [ID: {index}]")
|
||||
name_param = None
|
||||
|
||||
self.plat_list.append(Platform(
|
||||
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.show()
|
||||
|
||||
def get_enabled_state(self):
|
||||
index = self.platListWidget.currentRow()
|
||||
is_enabled = self.plat_list[index].enabled
|
||||
if is_enabled:
|
||||
self.plat_enabled_check.setChecked(True)
|
||||
else:
|
||||
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):
|
||||
index = self.platListWidget.currentRow()
|
||||
self.plat_edit_win = PlatformEdit(self.plat_list[index])
|
||||
self.plat_edit_win.show()
|
||||
|
||||
def save_platforms(self):
|
||||
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):
|
||||
index = self.platListWidget.currentRow()
|
||||
state = self.plat_enabled_check.isChecked()
|
||||
self.plat_list[index].enabled = state
|
||||
|
||||
def remove_platform(self):
|
||||
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()
|
@ -7,6 +7,7 @@ class FastEditQDoubleSpinBox(QDoubleSpinBox):
|
||||
QTimer.singleShot(0, self.selectAll)
|
||||
super(FastEditQDoubleSpinBox, self).focusInEvent(e)
|
||||
|
||||
|
||||
class FastEditQSpinBox(QSpinBox):
|
||||
def focusInEvent(self, e):
|
||||
QTimer.singleShot(0, self.selectAll)
|
||||
|
13
src/widgets/mpl_widget.py
Normal file
13
src/widgets/mpl_widget.py
Normal file
@ -0,0 +1,13 @@
|
||||
from matplotlib.backends.backend_qtagg import FigureCanvasQTAgg
|
||||
from matplotlib.figure import Figure
|
||||
from PyQt6.QtWidgets import QWidget, QVBoxLayout
|
||||
|
||||
|
||||
class MplWidget(QWidget):
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.canvas = FigureCanvasQTAgg(Figure(figsize=(15, 8), dpi=100))
|
||||
vertical_layout = QVBoxLayout()
|
||||
vertical_layout.addWidget(self.canvas)
|
||||
self.canvas.axes = self.canvas.figure.add_subplot(1, 1, 1)
|
||||
self.setLayout(vertical_layout)
|
Loading…
x
Reference in New Issue
Block a user