I'm trying to find a better approach than my current one to manage the storage of my PyQt application.
I have a QTreeWidget where I can add and delete rows. Clicking on these QTreeWidgetItems shows the data linked to it.
Each of that linked data consists of a graph with x-y values, and some additional metadata.
Currently in the __init__
method I have two empty lists defined:
self.xData = []
self.yData = []
When I load data the x and y values are np.ndarray type, and they get appended to self.xData
and self.yData
. The metadata is written to a dictionary, the key is the index of the data, value is the metadata. Later I access all of them by index.
I've tried to use dictionary the following way:
in __init__
:
self._dict = {}
The keys were the current index of the row, value is the (x, y) tuple (pseudocode):
self._dict['index of the row'] = (x_values, y_values)
I think this is still not an elegant way of dealing with the data.
There is the code you can try, but I had to disable and delete huge amount of functionality (including metadata) to reduce its length and to clean it up.
logic.py
import numpy as np
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import QAbstractItemView, QFileDialog, QTreeWidgetItem
from ui import Ui_SPP
class SPPWindow(QtWidgets.QMainWindow, Ui_SPP):
def __init__(self, parent=None):
super(SPPWindow, self).__init__(parent)
self.setupUi(self)
self.xData = []
self.yData = []
self.treeWidget.setSelectionMode(QAbstractItemView.SingleSelection)
self.loadButton.clicked.connect(self.loadData)
self.treeWidget.itemSelectionChanged.connect(self.previewData)
def previewData(self):
""" Function to update the plot."""
curr = self.treeWidget.currentIndex().row()
if curr == -1:
pass
else:
Ydata = self.yData[curr]
Xdata = self.xData[curr]
self.widget.canvas.axes.clear()
self.widget.canvas.axes.plot(Xdata, Ydata, 'r')
self.widget.canvas.axes.grid()
self.widget.canvas.draw()
def loadData(self):
""" Function to load file into QTreeWidget"""
options = QFileDialog.Options()
fileName, _ = QFileDialog.getOpenFileName(None,"Load interferogram", "","All Files (*);;Text Files (*.txt)", options=options)
try:
if fileName:
x, y = np.loadtxt(fileName, unpack=True, delimiter=',', usecols=(0, 1))
self.xData.append(x)
self.yData.append(y)
l1 = QTreeWidgetItem([fileName.split('/')[-1]])
self.treeWidget.addTopLevelItem(l1)
self.previewData()
except Exception as e:
print(e)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
application = SPPWindow()
application.show()
sys.exit(app.exec_())
ui.py
# -*- coding: utf-8 -*-
from PyQt5 import QtCore, QtGui, QtWidgets
import numpy as np
from plotwidget import PlotWidget
class Ui_SPP(object):
def setupUi(self, SPP):
SPP.setObjectName("SPP")
SPP.resize(1302, 832)
self.centralwidget = QtWidgets.QWidget(SPP)
self.centralwidget.setObjectName("centralwidget")
self.gridLayout = QtWidgets.QGridLayout(self.centralwidget)
self.gridLayout.setObjectName("gridLayout")
self.verticalLayout_3 = QtWidgets.QVBoxLayout()
self.verticalLayout_3.setObjectName("verticalLayout_3")
self.GDSPP = QtWidgets.QLineEdit(self.centralwidget)
self.GDSPP.setFocusPolicy(QtCore.Qt.NoFocus)
self.GDSPP.setAlignment(QtCore.Qt.AlignCenter)
self.GDSPP.setObjectName("GDSPP")
self.verticalLayout_3.addWidget(self.GDSPP)
self.GDDSPP = QtWidgets.QLineEdit(self.centralwidget)
self.GDDSPP.setFocusPolicy(QtCore.Qt.NoFocus)
self.GDDSPP.setAlignment(QtCore.Qt.AlignCenter)
self.GDDSPP.setObjectName("GDDSPP")
self.verticalLayout_3.addWidget(self.GDDSPP)
self.TODSPP = QtWidgets.QLineEdit(self.centralwidget)
self.TODSPP.setFocusPolicy(QtCore.Qt.NoFocus)
self.TODSPP.setAlignment(QtCore.Qt.AlignCenter)
self.TODSPP.setObjectName("TODSPP")
self.verticalLayout_3.addWidget(self.TODSPP)
self.FODSPP = QtWidgets.QLineEdit(self.centralwidget)
self.FODSPP.setFocusPolicy(QtCore.Qt.NoFocus)
self.FODSPP.setAlignment(QtCore.Qt.AlignCenter)
self.FODSPP.setObjectName("FODSPP")
self.verticalLayout_3.addWidget(self.FODSPP)
self.QODSPP = QtWidgets.QLineEdit(self.centralwidget)
self.QODSPP.setFocusPolicy(QtCore.Qt.NoFocus)
self.QODSPP.setAlignment(QtCore.Qt.AlignCenter)
self.QODSPP.setObjectName("QODSPP")
self.verticalLayout_3.addWidget(self.QODSPP)
self.messageBox = QtWidgets.QTextEdit(self.centralwidget)
self.messageBox.setFocusPolicy(QtCore.Qt.NoFocus)
self.messageBox.setReadOnly(True)
self.messageBox.setObjectName("messageBox")
self.verticalLayout_3.addWidget(self.messageBox)
self.gridLayout.addLayout(self.verticalLayout_3, 1, 3, 2, 1)
self.verticalLayout_4 = QtWidgets.QVBoxLayout()
self.verticalLayout_4.setObjectName("verticalLayout_4")
self.verticalLayout_2 = QtWidgets.QVBoxLayout()
self.verticalLayout_2.setObjectName("verticalLayout_2")
self.horizontalLayout = QtWidgets.QHBoxLayout()
self.horizontalLayout.setObjectName("horizontalLayout")
self.delayLine = QtWidgets.QLineEdit(self.centralwidget)
self.delayLine.setMinimumSize(QtCore.QSize(0, 30))
self.delayLine.setFocusPolicy(QtCore.Qt.ClickFocus)
self.delayLine.setAlignment(QtCore.Qt.AlignCenter)
self.delayLine.setObjectName("delayLine")
self.horizontalLayout.addWidget(self.delayLine)
self.verticalLayout_2.addLayout(self.horizontalLayout)
self.SPP1 = QtWidgets.QLineEdit(self.centralwidget)
self.SPP1.setMinimumSize(QtCore.QSize(0, 30))
self.SPP1.setFocusPolicy(QtCore.Qt.ClickFocus)
self.SPP1.setText("")
self.SPP1.setAlignment(QtCore.Qt.AlignCenter)
self.SPP1.setObjectName("SPP1")
self.SPP1.setStyleSheet("""QLineEdit { color: blue }""")
self.verticalLayout_2.addWidget(self.SPP1)
self.SPP2 = QtWidgets.QLineEdit(self.centralwidget)
self.SPP2.setMinimumSize(QtCore.QSize(0, 30))
self.SPP2.setFocusPolicy(QtCore.Qt.ClickFocus)
self.SPP2.setAlignment(QtCore.Qt.AlignCenter)
self.SPP2.setObjectName("SPP2")
self.verticalLayout_2.addWidget(self.SPP2)
self.SPP3 = QtWidgets.QLineEdit(self.centralwidget)
self.SPP3.setMinimumSize(QtCore.QSize(0, 30))
self.SPP3.setFocusPolicy(QtCore.Qt.ClickFocus)
self.SPP3.setAlignment(QtCore.Qt.AlignCenter)
self.SPP3.setObjectName("SPP3")
self.verticalLayout_2.addWidget(self.SPP3)
self.SPP4 = QtWidgets.QLineEdit(self.centralwidget)
self.SPP4.setMinimumSize(QtCore.QSize(0, 30))
self.SPP4.setFocusPolicy(QtCore.Qt.ClickFocus)
self.SPP4.setAlignment(QtCore.Qt.AlignCenter)
self.SPP4.setObjectName("SPP4")
self.SPP2.setStyleSheet("""QLineEdit { color: orange }""")
self.SPP3.setStyleSheet("""QLineEdit { color: green }""")
self.SPP4.setStyleSheet("""QLineEdit { color: purple }""")
self.verticalLayout_2.addWidget(self.SPP4)
self.pushButton_4 = QtWidgets.QPushButton(self.centralwidget)
self.pushButton_4.setFocusPolicy(QtCore.Qt.ClickFocus)
self.pushButton_4.setObjectName("pushButton_4")
self.verticalLayout_2.addWidget(self.pushButton_4)
self.verticalLayout_4.addLayout(self.verticalLayout_2)
self.gridLayout.addLayout(self.verticalLayout_4, 1, 1, 2, 1)
self.verticalLayout = QtWidgets.QVBoxLayout()
self.verticalLayout.setObjectName("verticalLayout")
self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
self.loadButton = QtWidgets.QPushButton(self.centralwidget)
self.loadButton.setMinimumSize(QtCore.QSize(150, 30))
self.loadButton.setObjectName("loadButton")
self.horizontalLayout_2.addWidget(self.loadButton)
self.pushButton_7 = QtWidgets.QPushButton(self.centralwidget)
self.pushButton_7.setObjectName("pushButton_7")
self.horizontalLayout_2.addWidget(self.pushButton_7)
self.verticalLayout.addLayout(self.horizontalLayout_2)
self.treeWidget = QtWidgets.QTreeWidget(self.centralwidget)
self.treeWidget.setExpandsOnDoubleClick(False)
self.treeWidget.setObjectName("treeWidget")
font = QtGui.QFont()
font.setBold(False)
font.setUnderline(True)
font.setWeight(50)
font.setStrikeOut(False)
self.treeWidget.headerItem().setFont(0, font)
self.verticalLayout.addWidget(self.treeWidget)
self.gridLayout.addLayout(self.verticalLayout, 0, 0, 3, 1)
self.horizontalLayout_4 = QtWidgets.QHBoxLayout()
self.horizontalLayout_4.setObjectName("horizontalLayout_4")
self.pushButton_2 = QtWidgets.QPushButton(self.centralwidget)
self.pushButton_2.setFocusPolicy(QtCore.Qt.ClickFocus)
self.pushButton_2.setObjectName("pushButton_2")
self.horizontalLayout_4.addWidget(self.pushButton_2)
self.pushButton_3 = QtWidgets.QPushButton(self.centralwidget)
self.pushButton_3.setFocusPolicy(QtCore.Qt.ClickFocus)
self.pushButton_3.setObjectName("pushButton_3")
self.horizontalLayout_4.addWidget(self.pushButton_3)
self.gridLayout.addLayout(self.horizontalLayout_4, 1, 2, 1, 1)
self.widget = PlotWidget(self.centralwidget)
self.widget.setMinimumSize(QtCore.QSize(500, 450))
self.widget.setObjectName("widget")
self.gridLayout.addWidget(self.widget, 0, 1, 1, 3)
self.horizontalLayout_3 = QtWidgets.QHBoxLayout()
self.horizontalLayout_3.setObjectName("horizontalLayout_3")
self.verticalLayout_8 = QtWidgets.QVBoxLayout()
self.verticalLayout_8.setObjectName("verticalLayout_8")
self.spp_ref = QtWidgets.QLineEdit(self.centralwidget)
self.spp_ref.setMinimumSize(QtCore.QSize(0, 30))
self.spp_ref.setFocusPolicy(QtCore.Qt.StrongFocus)
self.spp_ref.setAlignment(QtCore.Qt.AlignCenter)
self.spp_ref.setObjectName("spp_ref")
self.verticalLayout_8.addWidget(self.spp_ref)
self.fitOrderLine = QtWidgets.QLineEdit(self.centralwidget)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.fitOrderLine.sizePolicy().hasHeightForWidth())
self.fitOrderLine.setSizePolicy(sizePolicy)
self.fitOrderLine.setMinimumSize(QtCore.QSize(0, 30))
self.fitOrderLine.setMaximumSize(QtCore.QSize(651651, 16777215))
self.fitOrderLine.setFocusPolicy(QtCore.Qt.StrongFocus)
self.fitOrderLine.setAlignment(QtCore.Qt.AlignCenter)
self.fitOrderLine.setObjectName("fitOrderLine")
self.verticalLayout_8.addWidget(self.fitOrderLine)
self.pushButton_5 = QtWidgets.QPushButton(self.centralwidget)
self.pushButton_5.setFocusPolicy(QtCore.Qt.ClickFocus)
self.pushButton_5.setObjectName("pushButton_5")
self.verticalLayout_8.addWidget(self.pushButton_5)
self.horizontalLayout_3.addLayout(self.verticalLayout_8)
self.pushButton_6 = QtWidgets.QPushButton(self.centralwidget)
self.pushButton_6.setMinimumSize(QtCore.QSize(120, 30))
self.pushButton_6.setObjectName("pushButton_6")
self.horizontalLayout_3.addWidget(self.pushButton_6)
self.horizontalLayout_6 = QtWidgets.QHBoxLayout()
self.horizontalLayout_6.setObjectName("horizontalLayout_6")
self.horizontalLayout_3.addLayout(self.horizontalLayout_6)
self.gridLayout.addLayout(self.horizontalLayout_3, 2, 2, 1, 1)
SPP.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(SPP)
self.menubar.setGeometry(QtCore.QRect(0, 0, 1302, 26))
self.menubar.setObjectName("menubar")
SPP.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(SPP)
self.statusbar.setObjectName("statusbar")
SPP.setStatusBar(self.statusbar)
self.retranslateUi(SPP)
QtCore.QMetaObject.connectSlotsByName(SPP)
def retranslateUi(self, SPP):
_translate = QtCore.QCoreApplication.translate
SPP.setWindowTitle(_translate("SPP", "SPP Panel"))
self.GDSPP.setPlaceholderText(_translate("SPP", "GD "))
self.GDDSPP.setPlaceholderText(_translate("SPP", "GDD"))
self.TODSPP.setPlaceholderText(_translate("SPP", "TOD"))
self.FODSPP.setPlaceholderText(_translate("SPP", "FOD"))
self.QODSPP.setPlaceholderText(_translate("SPP", "QOD"))
__sortingEnabled = self.treeWidget.isSortingEnabled()
self.treeWidget.setSortingEnabled(False)
self.treeWidget.setSortingEnabled(__sortingEnabled)
self.delayLine.setPlaceholderText(_translate("SPP", "delay (fs)"))
self.SPP1.setPlaceholderText(_translate("SPP", "SPP1"))
self.SPP2.setPlaceholderText(_translate("SPP", "SPP2"))
self.SPP3.setPlaceholderText(_translate("SPP", "SPP3"))
self.SPP4.setPlaceholderText(_translate("SPP", "SPP4"))
self.pushButton_4.setText(_translate("SPP", "Set SPP\'s"))
self.loadButton.setText(_translate("SPP", "Load item"))
self.pushButton_7.setText(_translate("SPP", "Delete item"))
self.treeWidget.headerItem().setText(0, _translate("SPP", "Loaded files"))
self.pushButton_2.setText(_translate("SPP", "Clickable SPP"))
self.pushButton_3.setText(_translate("SPP", "Stop clicks"))
self.spp_ref.setPlaceholderText(_translate("SPP", "Reference point"))
self.fitOrderLine.setPlaceholderText(_translate("SPP", "Fit order "))
self.pushButton_5.setText(_translate("SPP", "Reset all"))
self.pushButton_6.setText(_translate("SPP", "Fit and report"))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
SPP = QtWidgets.QMainWindow()
ui = Ui_SPP()
ui.setupUi(SPP)
SPP.show()
sys.exit(app.exec_())
plotwidget.py
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
from PyQt5.QtWidgets import QWidget, QVBoxLayout
class PlotWidget(QWidget):
def __init__(self, parent=None):
QWidget.__init__(self, parent)
self.canvas = FigureCanvas(Figure())
vertical_layout = QVBoxLayout()
vertical_layout.addWidget(self.canvas)
self.canvas.axes = self.canvas.figure.add_subplot(111)
self.setLayout(vertical_layout)
My question mainly aims at finding a better data structure, but every little improvement is appreciated. Note that I intentionally didn't follow PEP8 there, because PyQt's naming convention is against that, and I'd rather keep the code coherent. Basically you can load any x-y dataset from a txt file which looks like this:
1,1
2,4
3,9
4,16
..