python - QTableWidget 中的从属 QComboBox

标签 python pyqt pyqt5

我试图在 qtablewidget 内链接 Pyqt5 中的 3 个不同的组合框。前两个链接在一个插槽中,第三个使用单独定义中的不同插槽链接到第二个组合框。在第一个组合框发生变化时,第二个组合框发生变化,从而导致第三个组合框发生变化(类似于汽车选择网站)。

当前的问题是,在更改第一个组合框时,第二个信号也会执行,这使其执行两次,并给我字典键错误,因为第一次执行不包含该键。尝试访问字典时,错误发生在第 81 行。 查看图像,因为它运行索引两次:

enter image description here

尝试

from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import QMessageBox, QMainWindow, QApplication, QFileDialog


class MainWindow(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)

        self.m_tablewidget = QtWidgets.QTableWidget(0, 3)
        self.m_tablewidget.setHorizontalHeaderLabels(
            ["Col 1", "Col 2", "Col 3"]
        )
        self.m_button = QtWidgets.QPushButton("Add Row", clicked=self.onClicked)

        central_widget = QtWidgets.QWidget()
        self.setCentralWidget(central_widget)
        lay = QtWidgets.QVBoxLayout(central_widget)
        lay.addWidget(self.m_tablewidget)
        lay.addWidget(self.m_button, alignment=QtCore.Qt.AlignLeft)
        self.vehicleHardware_dict = {'k': ['LiftSource_Wing_ChordConstantChordTapered', 'TPS_Active_Coolant'], 'X51': ['TPS_Passive_Tile', 'ThrustSource_Airbreathing_ScramjetTwoD']}
        self.procesVeh_dict = {'k': ['Aerodynamics', 'Optimization', 'Propulsion', 'Weight_Balance'], 'X51': ['Aerodynamics', 'Optimization', 'Propulsion', 'Weight_Balance']}
        self.processMethod_dict = {'Aerodynamics': ['kia', 'T1_case2_outNoIn'], 'Optimization': ['thomas'], 'Propulsion': ['test', 'rocket', 'T1_case3_InOutSame_inLess'], 'Weight_Balance': ['wing weight', 'T1_case1_inNoOut', 'T1_case3_inOutEq_inMore']}
        self.methodInput_dict = {'T1_case1_inNoOut': ['T'], 'T1_case2_outNoIn': [], 'T1_case3_InOutSame_inLess': ['T'], 'T1_case3_inOutEq_inMore': ['THRUST_REF'], 'kia': ['ACS', 'AEXIT', 'AE_AT', 'AIP', 'AISP', 'AISP_AVAIL_V', 'AISP_EFF', 'AISP_EFF_V', 'THETA2_N'], 'rocket': ['AIP', 'AISP', 'AISP_AVAIL_V', 'AISP_EFF', 'AISP_EFF_V', 'AISP_HW', 'AISP_REF'], 'test': ['ACS', 'Y_V'], 'thomas': ['ACS', 'AEXIT', 'AE_AT', 'AIP', 'AISP', 'AISP_AVAIL_V', 'CS', 'DIA_BODY'], 'wing weight': ['A', 'ABASE', 'ACAP', 'ACAP_SPLN', 'ACS', 'AEXIT', 'AE_AT']}

    @QtCore.pyqtSlot()
    def onClicked(self):
        combobox1_vehicle = QtWidgets.QComboBox()
        combobox2_hardware = QtWidgets.QComboBox()
        # combo_dummy = QtWidgets.QComboBox()
        for k, v in self.processMethod_dict.items():
            combobox1_vehicle.addItem(k, v)
            for kk, vv in self.methodInput_dict.items():
                combobox2_hardware.addItem(kk, vv)

        combobox3 = QtWidgets.QComboBox()
        combobox3.addItems(combobox2_hardware.currentData())

        combobox1_vehicle.currentIndexChanged.connect(self.onCurrentTextChanged1)
        combobox2_hardware.currentIndexChanged.connect(self.onCurrentTextChanged2)

        rc = self.m_tablewidget.rowCount()
        self.m_tablewidget.insertRow(rc)

        for i, combo in enumerate((combobox1_vehicle, combobox2_hardware, combobox3)):
            self.m_tablewidget.setCellWidget(rc, i, combo)

    @QtCore.pyqtSlot()
    def onCurrentTextChanged1(self):
        combobox1_vehicle = self.sender()
        if not isinstance(combobox1_vehicle, QtWidgets.QComboBox):
            return
        p = combobox1_vehicle.mapTo(self.m_tablewidget.viewport(), QtCore.QPoint())
        ix = self.m_tablewidget.indexAt(p)
        if not ix.isValid() or ix.column() != 0:
            return
        r = ix.row()
        data = combobox1_vehicle.currentData()
        combobox2_hardware = self.m_tablewidget.cellWidget(r, 1)
        if not isinstance(combobox2_hardware, QtWidgets.QComboBox):
            return
        combobox2_hardware.clear()
        combobox2_hardware.addItems(data)


    @QtCore.pyqtSlot()
    def onCurrentTextChanged2(self):
        combobox2_hardware = self.sender()
        if not isinstance(combobox2_hardware, QtWidgets.QComboBox):
            return
        p = combobox2_hardware.mapTo(self.m_tablewidget.viewport(), QtCore.QPoint())
        ix = self.m_tablewidget.indexAt(p)
        if not ix.isValid() or ix.column() != 1:
            return
        r = ix.row()
        # data = combobox2_hardware.currentData()
        valueOfKey = combobox2_hardware.currentText()
        print(combobox2_hardware)
        print(p)
        print(ix)

        data = self.methodInput_dict[valueOfKey]
        combobox3 = self.m_tablewidget.cellWidget(r, 2)
        if not isinstance(combobox3, QtWidgets.QComboBox):
            return
        combobox3.clear()
        if data == None:
            combobox3.addItem("")
        else:
            combobox3.addItems(data)



if __name__ == "__main__":
    import sys

    app = QtWidgets.QApplication(sys.argv)

    w = MainWindow()
    w.resize(640, 480)
    w.show()
    sys.exit(app.exec_())

最佳答案

在存在大量信息的情况下,有必要寻找处理这些信息的数据结构。在这种情况下,您可以使用 QAbstractItemModel 来共享同一行的所有 QComboBox,并且要更改显示的内容,您应该使用 setRootModelIndex()方法。在下面的示例中,我添加了一个 QTreeView,以便您可以看到模型是如何分布的。

from PyQt5 import QtCore, QtGui, QtWidgets

d = {
    "Aerodynamics": {
        "kia": [
            "ACS",
            "AEXIT",
            "AE_AT",
            "AIP",
            "AISP",
            "AISP_AVAIL_V",
            "AISP_EFF",
            "AISP_EFF_V",
            "THETA2_N",
        ],
        "T1_case2_outNoIn": [],
    },
    "Optimization": {
        "thomas": [
            "ACS",
            "AEXIT",
            "AE_AT",
            "AIP",
            "AISP",
            "AISP_AVAIL_V",
            "CS",
            "DIA_BODY",
        ]
    },
    "Propulsion": {
        "test": ["ACS", "Y_V"],
        "rocket": [
            "AIP",
            "AISP",
            "AISP_AVAIL_V",
            "AISP_EFF",
            "AISP_EFF_V",
            "AISP_HW",
            "AISP_REF",
        ],
        "T1_case3_InOutSame_inLess": ["T"],
    },
    "Weight_Balance": {
        "wing weight": [
            "A",
            "ABASE",
            "ACAP",
            "ACAP_SPLN",
            "ACS",
            "AEXIT",
            "AE_AT",
        ],
        "T1_case1_inNoOut": ["T"],
        "T1_case3_inOutEq_inMore": ["THRUST_REF"],
    },
}


def dict_to_model(item, d):
    if isinstance(d, dict):
        for k, v in d.items():
            it = QtGui.QStandardItem(k)
            item.appendRow(it)
            dict_to_model(it, v)
    elif isinstance(d, list):
        for v in d:
            dict_to_model(item, v)
    else:
        item.appendRow(QtGui.QStandardItem(str(d)))


class MainWindow(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)

        self.m_tablewidget = QtWidgets.QTableWidget(0, 3)
        self.m_tablewidget.setHorizontalHeaderLabels(
            ["Col 1", "Col 2", "Col 3"]
        )
        self.m_button = QtWidgets.QPushButton("Add Row", clicked=self.onClicked)
        self.m_treeview = QtWidgets.QTreeView()
        model = QtGui.QStandardItemModel(self)
        dict_to_model(model.invisibleRootItem(), d)
        self.m_treeview.setModel(model)
        self.m_treeview.expandAll()

        central_widget = QtWidgets.QWidget()
        self.setCentralWidget(central_widget)
        lay = QtWidgets.QVBoxLayout(central_widget)
        lay.addWidget(self.m_tablewidget)
        lay.addWidget(self.m_treeview)
        lay.addWidget(self.m_button, alignment=QtCore.Qt.AlignLeft)

    @QtCore.pyqtSlot()
    def onClicked(self):
        rc = self.m_tablewidget.rowCount()
        self.m_tablewidget.insertRow(rc)

        model = QtGui.QStandardItemModel(self)
        dict_to_model(model.invisibleRootItem(), d)

        it = model.invisibleRootItem()
        combos = []
        for i in range(3):
            combo = QtWidgets.QComboBox()
            combo.setModel(model)
            ix = model.indexFromItem(it)
            combo.setRootModelIndex(ix)
            combo.setCurrentIndex(0)
            it = it.child(0)
            self.m_tablewidget.setCellWidget(rc, i, combo)
            combos.append(combo)

        for combo in combos:
            combo.currentIndexChanged[int].connect(self.onCurrentIndexChanged)

    @QtCore.pyqtSlot(int)
    def onCurrentIndexChanged(self, index):
        combo = self.sender()
        if not isinstance(combo, QtWidgets.QComboBox):
            return
        p = combo.mapTo(self.m_tablewidget.viewport(), QtCore.QPoint())
        ix = self.m_tablewidget.indexAt(p)
        if not ix.isValid():
            return
        r, c = ix.row(), ix.column()
        if c == (self.m_tablewidget.columnCount() - 1):
            return
        model = combo.model()
        combo2 = self.m_tablewidget.cellWidget(r, c + 1)
        ix = combo.rootModelIndex()
        child_ix = model.index(index, 0, ix)
        combo2.setRootModelIndex(child_ix)
        combo2.setCurrentIndex(0)


if __name__ == "__main__":
    import sys

    app = QtWidgets.QApplication(sys.argv)

    w = MainWindow()
    w.resize(640, 480)
    w.show()
    sys.exit(app.exec_())

关于python - QTableWidget 中的从属 QComboBox,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56782805/

相关文章:

python - 如何将非分组列保留在 Pandas 的 groupBy 中

Android 命名空间 lxml python

python - 如何保留包含特定字符串的行并从 .txt 文件中删除其他行?

python - 在子类中重写 PyQt 信号

python - 初始化后扩大QPaintDevice的大小

python - 为什么在运行 Locust 时出现 403 错误?

python - Qt QFormLayout 标签向左对齐,值向右对齐

python - Linux : Display names of all currently attached USB sticks on PyQt5 GUI

python - 如何通过将光标放在框架内来调整框架大小?

python - QtSql 连接多个数据库