PyQt5 动态将 QFormLayouts 添加到 QTabWidget 的选项卡中

Posted

技术标签:

【中文标题】PyQt5 动态将 QFormLayouts 添加到 QTabWidget 的选项卡中【英文标题】:PyQt5 dynamic add QFormLayouts into tabs of QTabWidget 【发布时间】:2019-02-13 16:30:41 【问题描述】:

我完全是 Python 和 PyQT5 的菜鸟。还有我在 *** 上的第一篇文章。请原谅我可能缺少的任何协议。我正在创建一个读取 excel 文件的程序。此 XL 文件将有多个工作表。该程序首先创建一个主窗口,在其中收集一些信息。用户按下按钮后,程序会打开一个带有 QTabWidget 的新窗口。

这个 QTabWidget 是动态创建的,因此选项卡名称由 XL 工作表名称列表设置。在每个选项卡中,我想放入标签和组合框的 QFormLayout。每个选项卡的 qformlayout 都相同,但我想为每个选项卡上的每个组合框动态设置选择。

这是我的代码:

class TabWindow(QWidget):

     def __init__(self):
        super().__init__()
        self.title = 'Tape Column Mapping'
        self.left = 100
        self.top = 100
        self.width = 640
        self.height = 570
        self.initUI()

    def initUI(self):
        self.setWindowTitle(self.title)
        self.setGeometry(self.left, self.top, self.width, self.height)

        self.lbl = QLabel('For each worksheet you want to analyze, please associate each field with the appropriate worksheet column:',self)
        self.lbl.setGeometry(10,10,621,31)
        self.lbl.setFont(QFont("MS Shell Dlg 2", 8, QFont.Bold))

    def initFormLayout(self):
        self.formLayout = QFormLayout(self)
        self.formLayout.addRow(QLabel("Address"),QComboBox())
        self.formLayout.addRow(QLabel("City"),QComboBox())
        self.formLayout.addRow(QLabel("State"),QComboBox())
        self.formLayout.addRow(QLabel("Zip"),QComboBox())
        self.formLayout.addRow(QLabel("UPB"),QComboBox())
        self.formLayout.addRow(QLabel("Interest Rate"),QComboBox())
        self.formLayout.addRow(QLabel("P&I"),QComboBox())
        self.formLayout.addRow(QLabel("Term"),QComboBox())
        self.formLayout.addRow(QLabel("Original Balance"),QComboBox())
        self.formLayout.addRow(QLabel("Note Date"),QComboBox())
        self.formLayout.addRow(QLabel("Last Paid To"),QComboBox())
        self.formLayout.addRow(QLabel("Next Due Date"),QComboBox())
        self.formLayout.addRow(QLabel("Maturity Date"),QComboBox())
        self.formLayout.addRow(QLabel("Asset Type"),QComboBox())
        self.formLayout.addRow(QLabel("Note Status"),QComboBox())
        self.setLayout(formLayout)

class MainWindow(QWidget):

    def __init__(self):
        super().__init__()
        self.title = 'NPN Tape Analyzer'
        self.left = 100
        self.top = 100
        self.width = 640
        self.height = 480
        self.initUI()

    def initUI(self):
        self.setWindowTitle(self.title)
        self.setGeometry(self.left, self.top, self.width, self.height)

        self.lbl = QLabel('NPN Tape Analyzer',self)
        self.lbl.setGeometry(20,10,231,31)
        self.lbl.setFont(QFont("MS Shell Dlg 2", 16, QFont.Bold))


        self.lbl1 = QLabel('(Open Tape Excel File)',self)
        self.lbl1.setGeometry(250,100,140,20)
        self.lbl1.setFont(QFont("MS Shell Dlg 2", 8))
        self.lbl1.setAlignment(Qt.AlignCenter)


        self.lbl2 = QLabel('Select Worksheets to analyze (ctrl + click for multiple selections):',self)
        self.lbl2.setGeometry(50,190,420,20)
        self.lbl2.setFont(QFont("MS Shell Dlg 2", 9, QFont.Bold))


        btn = QPushButton('START ANALYSIS',self)
        btn.setGeometry(260,60,121,41)
        btn.setFont(QFont("Arial", 8, QFont.Bold))
        btn.clicked.connect(self.openXLworksheetDialog)

        btn1 = QPushButton('NEXT >>',self)
        btn1.setGeometry(500,280,75,23)
        btn1.setFont(QFont("Arial", 8))
        btn1.clicked.connect(self.collectSelectedSheets)

        btn2 = QPushButton('Exit Application',self)
        btn2.setGeometry(210,380,220,40)
        btn2.setFont(QFont("Arial", 10, QFont.Bold))
        btn2.clicked.connect(self.close)

        self.fnametextBox = QLineEdit(self)
        self.fnametextBox.setGeometry(35,150,571,20)
        self.fnametextBox.setText("<No File>")
        self.fnametextBox.setAlignment(Qt.AlignCenter)
        self.fnametextBox.setStyleSheet("color: rgb(128, 128, 128);")
        self.fnametextBox.setReadOnly(True)

        self.sheetListWidg = QListWidget(self)
        self.sheetListWidg.setGeometry(200,220,241,151)
        self.sheetListWidg.setStyleSheet("color: rgb(0, 0, 255);")
        self.sheetListWidg.setSelectionMode(QAbstractItemView.ExtendedSelection)

    def openXLworksheetDialog(self):              
        options = QFileDialog.Options()
        options |= QFileDialog.DontUseNativeDialog
        XLfilename, _ = QFileDialog.getOpenFileName(self,"Open MS Excel file","","Newer Excel files (*.xlsx);;Older Excel files (*.xls)",options=options)
        if XLfilename:
            self.fnametextBox.setText(XLfilename)
            self.WBdict = pd.read_excel(XLfilename, sheet_name=None)
            for key in self.WBdict:
                self.sheetListWidg.addItem(key)

    def collectSelectedSheets(self):
        tmpselSheets = self.sheetListWidg.selectedItems()
        self.selSheetList = []
        for i in range(len(tmpselSheets)):
            self.selSheetList.append(str(self.sheetListWidg.selectedItems()[i].text()))
        print(self.selSheetList)


    def closeEvent(self, event):
            """Generate 'question' dialog on clicking 'X' button in title bar.

            Reimplement the closeEvent() event handler to include a 'Question'
            dialog with options on how to proceed - Close or Cancel buttons
            """
            reply = QMessageBox.question(
                self, "Message",
                "You are exiting the application. Are you sure you want to quit?",
                QMessageBox.Close | QMessageBox.Cancel,
                QMessageBox.Close)

            if reply == QMessageBox.Close:
                event.accept()
            else:
                event.ignore()


if __name__ == '__main__':

    app = QApplication(sys.argv)
    mainwindow = MainWindow()
    mainwindow.show()
    sys.exit(app.exec_())

但我不认为 TabWindow 类部分是正确的,尤其是“self”的使用。我还没有尝试实例化 TabWindow 部分。但到目前为止,MainWindow 部分仍然有效。以下是我的问题:

1) 如果这是第二个窗口,我应该在第二个窗口类中使用 self 吗?

2) 在 TabWidget 中创建 QComboBoxes 后,我将如何访问它们?某种列表?

【问题讨论】:

【参考方案1】:

试试看:

import sys
import pandas as pd
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *


class TabDialog(QDialog):                                     # +++       
    def __init__(self, sheetList):
        super().__init__()

        self.setWindowTitle("Tab Widget Application")
        self.setWindowIcon(QIcon("D:/_Qt/img/pyqt.jpg"))

        tabwidget = QTabWidget()
        for nameTab in sheetList:
            tabwidget.addTab(TabWindow(), nameTab)

        vboxLayout = QVBoxLayout()
        vboxLayout.addWidget(tabwidget)
        self.setLayout(vboxLayout)


class TabWindow(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle('Tape Column Mapping')
        self.lbl = QLabel('For each worksheet you want to analyze, please associate each field with the appropriate worksheet column:',self)
        self.lbl.setFont(QFont("MS Shell Dlg 2", 8, QFont.Bold))

        self.initFormLayout()               # +++

        layout = QVBoxLayout(self)          # +++
        layout.addWidget(self.lbl)          # +++
        layout.addLayout(self.formLayout)   # +++
        layout.addStretch(1)              

    def initFormLayout(self):               # +++
        formList = [
            ["Address", ['A1', 'A2', 'A3', 'A4',]],
            ["City",    ['C1', 'C2', 'C3', 'C4', 'C5', 'C6',]],
            ["State",   ['S1', 'S2', 'S3', 'S4',]],
            ["Zip",     ['Z1', 'Z2', 'Z3',]],
            ["UPB",     ['U1', 'U2',]],
            ["Interest Rate", ['IR1', 'IR2', 'IR3', 'IR4',]],
            ["P&I",     ['PI1', 'PI2', 'PI3', 'PI4',]],
            ["Term",    ['T1', 'T2', 'T3', 'T4',]],
            ["Original Balance", ['OB1', 'OB2', 'OB3', 'OB4',]],
            ["Note Date",     ['ND1', 'ND2', 'ND3', 'ND4',]],
            ["Last Paid To",  ['LPT1', 'LPT2', 'LPT3', 'LPT4',]],
            ["Next Due Date", ['NDD1', 'NDD2', 'NDD3', 'NDD4',]],
            ["Maturity Date", ['MD1', 'MD2', 'MD3', 'MD4',]],
            ["Asset Type",    ['AT1', 'AT2', 'AT3', 'AT4',]],
            ["Note Status",   ['NS1', 'NS2', 'NS3', 'NS4',]],
        ]

        self.formLayout = QFormLayout()
        for item in formList:
            combo = QComboBox()
            combo.addItems(item[1])
            combo.activated[str].connect(self.comboActivate) # 2) How would I go about accessing the QComboBoxes 
            self.formLayout.addRow(QLabel(item[0]), combo)

    def comboActivate(self, text):                           # 2) How would I go about accessing the QComboBoxes 
        print("activated->``".format(text))


class MainWindow(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle('NPN Tape Analyzer')
        self.setGeometry(100, 100, 640, 480)
        self.initUI()

    def initUI(self):
        self.lbl = QLabel('NPN Tape Analyzer', self)
        self.lbl.setGeometry(20, 10, 231, 31)
        self.lbl.setFont(QFont("MS Shell Dlg 2", 16, QFont.Bold))

        self.lbl1 = QLabel('(Open Tape Excel File)',self, alignment=Qt.AlignCenter)
        self.lbl1.setGeometry(250, 100, 140, 20)
        self.lbl1.setFont(QFont("MS Shell Dlg 2", 8))

        self.lbl2 = QLabel('Select Worksheets to analyze (ctrl + click for multiple selections):',self)
        self.lbl2.setGeometry(50, 190, 420, 20)
        self.lbl2.setFont(QFont("MS Shell Dlg 2", 9, QFont.Bold))

        btn = QPushButton('START ANALYSIS', self)
        btn.setGeometry(260, 60, 121, 41)
        btn.setFont(QFont("Arial", 8, QFont.Bold))
        btn.clicked.connect(self.openXLworksheetDialog)

        btn1 = QPushButton('NEXT >>', self)
        btn1.setGeometry(500, 280, 75, 23)
        btn1.setFont(QFont("Arial", 8))
        btn1.clicked.connect(self.collectSelectedSheets)

        btn2 = QPushButton('Exit Application',self)
        btn2.setGeometry(210, 380, 220, 40)
        btn2.setFont(QFont("Arial", 10, QFont.Bold))
        btn2.clicked.connect(self.close)

        self.fnametextBox = QLineEdit(self, alignment=Qt.AlignCenter)
        self.fnametextBox.setGeometry(35, 150, 571, 20)
        self.fnametextBox.setText("<No File>")
        self.fnametextBox.setStyleSheet("color: rgb(128, 128, 128);")
        self.fnametextBox.setReadOnly(True)

        self.sheetListWidg = QListWidget(self)
        self.sheetListWidg.setGeometry(200, 220, 241, 151)
        self.sheetListWidg.setStyleSheet("color: rgb(0, 0, 255);")
        self.sheetListWidg.setSelectionMode(QAbstractItemView.ExtendedSelection)

    def openXLworksheetDialog(self):              
        options = QFileDialog.Options()
        options |= QFileDialog.DontUseNativeDialog
        XLfilename, _ = QFileDialog.getOpenFileName(self,"Open MS Excel file","","Newer Excel files (*.xlsx);;Older Excel files (*.xls)",options=options)
        if XLfilename:
            self.fnametextBox.setText(XLfilename)
            self.WBdict = pd.read_excel(XLfilename, sheet_name=None)
            for key in self.WBdict:
                self.sheetListWidg.addItem(key)

    def collectSelectedSheets(self):
        tmpselSheets = self.sheetListWidg.selectedItems()
        self.selSheetList = []
        for i in range(len(tmpselSheets)):
            self.selSheetList.append(str(self.sheetListWidg.selectedItems()[i].text()))
        print(self.selSheetList)


        if self.selSheetList:                             # +++
            self.tabDialog = TabDialog(self.selSheetList) # +++    1) If this is a 2nd window,
            self.tabDialog.show()                         # +++    1) ...
        else:                                             # +++
            QMessageBox.information(self, 
                'Information', 
                'Select Worksheets to analyze' )

    def closeEvent(self, event):
            """Generate 'question' dialog on clicking 'X' button in title bar.
            Reimplement the closeEvent() event handler to include a 'Question'
            dialog with options on how to proceed - Close or Cancel buttons
            """
            reply = QMessageBox.question(
                self, "Message",
                "You are exiting the application. Are you sure you want to quit?",
                QMessageBox.Close | QMessageBox.Cancel,
                QMessageBox.Close)

            if reply == QMessageBox.Close:
                event.accept()
            else:
                event.ignore()


if __name__ == '__main__':
    app = QApplication(sys.argv)
    mainwindow = MainWindow()
    mainwindow.show()
    sys.exit(app.exec_())

【讨论】:

谢谢 S. Nick。这回答了我未来的一些问题。不过,我应该清楚我的意图。当我说“我想为每个选项卡上的每个组合框动态设置选择”时,我的意思是我想使用每个工作表中的列名并将它们放在每个组合框中。因此,如果工作表 A 有 12 列,我会将这些名称放在选项卡 A 的组合框中。如果工作表 B 有 16 列,我会将这些名称放在选项卡 B 的组合框中。依此类推。然后,我想稍后在程序中使用这些组合中所做的选择。

以上是关于PyQt5 动态将 QFormLayouts 添加到 QTabWidget 的选项卡中的主要内容,如果未能解决你的问题,请参考以下文章

在 PyQt5/Pyside2 中动态添加小部件到流布局

pyqt5动态添加控件及导入图片

通过 pyqt5 动态添加和删除小部件

如何检测在 PyQt5 中按下了动态添加的按钮之一? [复制]

Pyqt5 addStretch 在小部件之间?

将动态解决方案应用于 Pyqt5 连接