python - QTableWidget 中的依赖 QComboBoxes
问题描述
我正在尝试在 qtablewidget 中链接 Pyqt5 中的 3 个不同的组合框。前 2 个链接在一个插槽中,第三个链接到第二个组合框,使用单独定义中的不同插槽。在第一个组合框发生变化时,第二个组合框发生变化,这使得第三个组合框发生变化(类似于选车网站)。
当前的问题是,在更改第一个组合框时,第二个信号也会执行,这使其执行两次并给我字典键错误,因为第一次执行不包括键。尝试访问字典时,错误发生在第 81 行。查看图像,因为它运行索引两次:
试图
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_())
解决方案
在有大量信息的情况下,有必要寻找处理信息的数据结构。在这种情况下,您可以使用 aQAbstractItemModel
来共享同一行的所有 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_())
推荐阅读
- javascript - 如何使用 webpack 复制下一个 js“公共”文件夹行为?
- ruby-on-rails - 在 where 类中使用日期范围
- python - 在 Python 中,我将如何更改文本文件中的时间列表?
- python - 为什么基于 iLocation 的布尔索引不起作用?
- unity3d - 我有两个场景,但是当添加新的 ui 时,它一直将它添加到第一个场景中,而从来没有添加到新的场景中,这是为什么呢?
- python - 返回无的 Python 函数
- node.js - vscode“去定义”可以在node_modules中显示实际的js代码而不是打字稿版本吗?
- android - 从 Flutter 的方法通道访问 Kotlin 中的参数数据
- kubernetes - kubeadmin 用户可以创建超过 ClusterResourceQuota 硬限制的资源吗?
- tensorflow - TensorFlow:整个训练过程中的 CosineDifference ObjFunc 常量