1

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.

enter image description here

I have 3 questions:

  1. Is using QStackWidget the best way of doing this?
  2. 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?
  3. 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
RealRK
  • 315
  • 3
  • 19
  • provide a [mre] – eyllanesc Mar 08 '20 at 00:52
  • Sure bro, I'll do that asap, but it won't be minimal tbh. In code, with all the functions in Data View tab, it'll be at least 600 lines. That's why am seeking advice. – RealRK Mar 08 '20 at 00:56
  • 3) On the other hand, your second and third ask if your implementation is correct or not, or if there is a better one. And I ask what implementation do you mean? what is `self.counter`? What same widgets do you mean?, etc Which reinforces the need for an MRE – eyllanesc Mar 08 '20 at 01:08
  • I understand. It's usually my implementation that's wrong... Have corrected the question. That error was before I was using stackwidget. Now it is working, but I had some doubts. For example in android, we can create a manifest of how the workflow and activities progress along with pages and ui changes. Does pyqt have something similar? I'll post the code in sometime, just boarded a train right now. – RealRK Mar 08 '20 at 01:11
  • 1) PyQt is not android and there is nothing similar. 2) Your correction makes point 1 and 2 of my second comment useless but I still need an MRE to answer your second and third question. Is it so complicated to provide an MRE? I will wait for MRE. – eyllanesc Mar 08 '20 at 01:13
  • Updated. Sorry bro, some people in my office were diagnosed with coronavirus so our office was closed for 2 days for disinfection. – RealRK Mar 10 '20 at 19:35
  • It is not necessary to explain why you did not update your question, the action of closing a question is to indicate to the OP that something is missing to make it a quality question, and if you have done so then it will probably be reopened. – eyllanesc Mar 10 '20 at 19:48
  • Okay, thanks! Let me know if you can help :) – RealRK Mar 10 '20 at 20:18
  • @eyllanesc Any suggestions? To be brief, every new file added needs to have its own page with the same widgets (SW 1) and then we should be able to delete it. I'm not asking for code here, but for program structure inputs. For example, how do I make sure that a new page in stackwidget is created on importing a new file. Also, I found the way of changing the stackwidget page according to selection, but that isn't working for children of item. Deleting is also buggy, removes the child text, but the page is still in memory. – RealRK Mar 12 '20 at 17:13
  • I got some excellent pointers [here](https://stackoverflow.com/questions/47076370/how-to-connect-qtreewidget-and-qstackedwidget-in-pyqt4) – RealRK Mar 12 '20 at 17:23

0 Answers0