So essentially, I want to use treeView toplevel selection as trigger for changing QStackWidget 1 page and child selection as trigger for changing QStackWidget 2 page as shown in the image. Earlier, I used selectedindexes function to get selection and that was working, but it was re-plotting data every time I clicked on another file and the changes I made in "Data View" tab were lost. Then I read about StackedWidgets.
For every new file or filter added, I used a counter (self.counter += 1) in the load function to append new toplevels/children and keep track of number of executions of a particular function.
I have 3 questions:
- Is using QStackWidget the best way of doing this?
- How do I handle delete event so that if some 3rd file is deleted it correctly shows the data inside other file views? Extension: Is using self.counter the best way to track function loading?
- How do I get every new page with the same widgets as the initial but with blank data?
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
import ntpath
import matplotlib
import matplotlib.pylab as plt
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
import pandas as pd
from scipy.signal import savgol_filter
class Ui_MainWindow(object):
fileCounter = -1
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(800, 600)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.gridLayout_2 = QtWidgets.QGridLayout(self.centralwidget)
self.gridLayout_2.setObjectName("gridLayout_2")
self.horizontalLayout = QtWidgets.QHBoxLayout()
self.horizontalLayout.setObjectName("horizontalLayout")
self.groupBox = QtWidgets.QGroupBox(self.centralwidget)
self.groupBox.setObjectName("groupBox")
self.gridLayout = QtWidgets.QGridLayout(self.groupBox)
self.gridLayout.setObjectName("gridLayout")
self.treeWidget = QtWidgets.QTreeWidget(self.groupBox)
self.treeWidget.setObjectName("treeWidget")
self.gridLayout.addWidget(self.treeWidget, 0, 0, 1, 1)
self.horizontalLayout.addWidget(self.groupBox)
self.stackedWidget = QtWidgets.QStackedWidget(self.centralwidget)
self.stackedWidget.setObjectName("stackedWidget")
self.page = QtWidgets.QWidget()
self.page.setObjectName("page")
self.gridLayout_3 = QtWidgets.QGridLayout(self.page)
self.gridLayout_3.setObjectName("gridLayout_3")
self.tabWidget = QtWidgets.QTabWidget(self.page)
self.tabWidget.setObjectName("tabWidget")
self.tab = QtWidgets.QWidget()
self.tab.setObjectName("tab")
self.gridLayout_4 = QtWidgets.QGridLayout(self.tab)
self.gridLayout_4.setObjectName("gridLayout_4")
self.stackedWidget_2 = QtWidgets.QStackedWidget(self.tab)
self.stackedWidget_2.setObjectName("stackedWidget_2")
self.page_3 = QtWidgets.QWidget()
self.page_3.setObjectName("page_3")
self.gridLayout_5 = QtWidgets.QGridLayout(self.page_3)
self.gridLayout_5.setObjectName("gridLayout_5")
# Added Matplotlib
self.plotter = plt.figure()
self.showPlot = FigureCanvas(self.plotter)
self.gridLayout_5.addWidget(self.showPlot, 0, 0, 1, 1)
self.stackedWidget_2.addWidget(self.page_3)
self.page_4 = QtWidgets.QWidget()
self.page_4.setObjectName("page_4")
self.stackedWidget_2.addWidget(self.page_4)
self.gridLayout_4.addWidget(self.stackedWidget_2, 0, 0, 1, 1)
self.tabWidget.addTab(self.tab, "")
self.tab_2 = QtWidgets.QWidget()
self.tab_2.setObjectName("tab_2")
self.gridLayout_6 = QtWidgets.QGridLayout(self.tab_2)
self.gridLayout_6.setObjectName("gridLayout_6")
self.checkBox_2 = QtWidgets.QCheckBox(self.tab_2)
self.checkBox_2.setObjectName("checkBox_2")
self.gridLayout_6.addWidget(self.checkBox_2, 0, 0, 1, 1)
self.tabWidget.addTab(self.tab_2, "")
self.gridLayout_3.addWidget(self.tabWidget, 0, 0, 1, 1)
self.stackedWidget.addWidget(self.page)
self.page_2 = QtWidgets.QWidget()
self.page_2.setObjectName("page_2")
self.stackedWidget.addWidget(self.page_2)
self.horizontalLayout.addWidget(self.stackedWidget)
self.gridLayout_2.addLayout(self.horizontalLayout, 0, 0, 1, 1)
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 26))
self.menubar.setObjectName("menubar")
self.menuFile = QtWidgets.QMenu(self.menubar)
self.menuFile.setObjectName("menuFile")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.actionOpen_File = QtWidgets.QAction(MainWindow)
self.actionOpen_File.setObjectName("actionOpen_File")
self.menuFile.addAction(self.actionOpen_File)
self.menubar.addAction(self.menuFile.menuAction())
self.retranslateUi(MainWindow)
self.tabWidget.setCurrentIndex(0)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.groupBox.setTitle(_translate("MainWindow", "Files"))
self.treeWidget.headerItem().setText(0, _translate("MainWindow", "File Name"))
self.treeWidget.headerItem().setText(1, _translate("MainWindow", "Last Updated"))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab), _translate("MainWindow", "Plot"))
self.checkBox_2.setText(_translate("MainWindow", "Derivative"))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_2), _translate("MainWindow", "Data"))
self.menuFile.setTitle(_translate("MainWindow", "File"))
self.actionOpen_File.setText(_translate("MainWindow", "Open File"))
self.actionOpen_File.triggered.connect(self.addnewFile)
self.checkBox_2.stateChanged.connect(self.state_changed)
# TreeWidget Specifications
self.treeWidget.setContextMenuPolicy(Qt.CustomContextMenu)
self.treeWidget.customContextMenuRequested.connect(self.openMenu)
self.treeWidget.setColumnCount(2)
self.treedata = QTreeWidgetItem(self.treeWidget)
self.fileseqList = []
self.taskseqList = []
def addfileName(self, fileName):
self.treedata.setText(self.fileCounter,fileName)
self.fileseqList.append(fileName) # This is for creating a list which stores sequence of fileName
def addtaskName(self, taskName):
self.childItem = QTreeWidgetItem(self.treedata)
self.childItem.setText(self.fileCounter,taskName)
def delete(self):
print(self.treeWidget.currentIndex())
selectrows = []
selectcols = []
# if child, if parent delete that and change the initial list, update the model rendering
for i in self.treeWidget.selectionModel().selectedIndexes():
selectrows.append(i.row())
selectcols.append(i.column())
# Implement function for deleting selected parent,child
def addnewFile(self):
self.fileName, _ = QFileDialog.getOpenFileName(None, 'Open File', ".", "Files (*.*)", options=QFileDialog.DontUseNativeDialog)
self.FILENAMETREE = ntpath.basename(self.fileName)
self.fileCounter += 1
self.df = pd.read_csv(self.fileName)
self.addfileName(self.FILENAMETREE)
self.plotFunction(self.df.transpose())
def openMenu(self, position):
indexes = self.treeWidget.selectedIndexes()
if len(indexes) > 0:
level = 0
index = indexes[0]
while index.parent().isValid():
index = index.parent()
level += 1
menu = QMenu()
if level == 0:
menu.addAction("Delete File")
elif level == 1:
menu.addAction("Delete Task")
menu.exec_(self.treeWidget.viewport().mapToGlobal(position))
def plotFunction(self,df):
self.plotter.clear()
ax = self.plotter.add_subplot(111)
df.T.plot(ax=ax)
ax.get_legend().remove()
self.showPlot.draw()
def state_changed(self, int): # List of tasks
if self.checkBox_2.isChecked():
X = savgol_filter(self.df, 11, polyorder = 2, deriv=2)
self.plotFunction(pd.DataFrame(X))
self.addtaskName("Derivative")
else:
pass
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
UPDATE: Uploaded Code and created new (testing) way of populating treeWidget. Still don't know whether to continue with treeWidget or treeView for this application. You can open any csv file which has the first line as headers (Xaxis) and the rest of the lines as Y Axis. Have created a contextMenu for deleting, but don't know how to get the selectedindexes to get the child,parent also, currently they just show the parent. Need some data structure for this purpose so that the sequence of files/tasks is returned and remains the same when something is deleted or added.
Link for downloading file which can be plotted
Here is the structure I'm thinking of. Please provide your inputs:
Structure for populating StackWidget and QTreeWidget
class NewFileStructure():
def __init__(self,parent=None):
initialize function
open qfiledialog and import file
check extension and read the file
def loadFileinTreeWidget(self):
load the file in treewidget
increment file counter
add treeroot as fileName
return fileName, treeroot index and filecounter
def loadnewStackWidget1Page(self):
every new file is loaded in a new instance(page) of QStackWidget
all pages in this stackwidget contain same widget and layouts
def plot(self):
plot the file in stackwidget2Page1
class NewTaskStructure():
def __init__(self):
initialize function
user selects some filter/task
def addtasktoTreeWidget(self):
the user has selected the treeroot
we get the treeroot index
the task is added as a child to the treeroot by referencing index
increment taskcounter
return taskname, index, taskcounter and treeroot to which it is added
def loadNewStackWidget2Page(self):
the UI for the new stackwidget is loaded
shown in stackwidget 2
def
def openMenu(self):
This contains options for deleting the selected index (fileName or task)
Updates the filecounter and taskcounter by removing the values
or subtracts 1 from the point where the file/task was deleted
updates treeWidget data accordingly
def selectionchangedTreeWidget(self):
This method determines what index to put the task in
it returns selected filename,index
it returns selected taskname,index
it changes the page of stackwidget 1 (new file) when treeroot filename selected
it changes the page of stackwidget 2 (new task) when treeroot child task selected
whenever a new task is added, this function gets called