Построение комплекса GUI с помощью PyQt5 QStackWidget - PullRequest
2 голосов
/ 08 марта 2020

Так что, по сути, я хочу использовать treeView для выбора верхнего уровня в качестве триггера для изменения страницы QStackWidget 1 и дочернего выбора в качестве триггера для изменения страницы QStackWidget 2, как показано на рисунке. Ранее я использовал функцию selectedindexes, чтобы получить выделение, и это работало, но оно перераспределяло данные каждый раз, когда я нажимал на другой файл, и изменения, сделанные на вкладке «Просмотр данных», терялись. Тогда я прочитал о StackedWidgets.

Для каждого нового добавленного файла или фильтра я использовал счетчик (self.counter + = 1) в функции загрузки для добавления новых уровней / потомков и отслеживания количества выполнений определенной функции.

enter image description here

У меня есть 3 вопроса:

  1. Является ли использование QStackWidget лучшим способом сделать это?
  2. Как обработать событие удаления, чтобы при удалении третьего файла он правильно отображал данные в других представлениях файлов? Расширение: Является ли использование self.counter лучшим способом для отслеживания загрузки функций?
  3. Как получить каждую новую страницу с теми же виджетами, что и исходные, но с пустыми данными?
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_())

ОБНОВЛЕНИЕ: загружен код и создан новый (тестовый) способ заполнения treeWidget. До сих пор не знаю, следует ли продолжать с treeWidget или treeView для этого приложения. Вы можете открыть любой CSV-файл с первой строкой в ​​виде заголовков (Xaxis), а остальные строки - с осью Y. Создал contextMenu для удаления, но не знаю, как получить выбранные индексы, чтобы получить потомок, также родительский, в настоящее время они просто показывают родителя. Для этой цели нужна некоторая структура данных, чтобы последовательность файлов / задач возвращалась и оставалась неизменной при удалении или добавлении.

Ссылка для загрузки файла, который может быть нанесен на график

Вот структура, о которой я думаю. Пожалуйста, укажите ваши входные данные:

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
...