问题描述
我需要制作一个表,其中每个项目都是父项,有两个子项(两个参数,在lineEdit'param 1'和'param 2中映射)。表中的父值应该是参数1和参数2的乘积结果。结果还映射到3d lineEdit中:
我以前曾经使用过QAbstractTableModel,但是由于它不支持父子关系,因此我需要将qabstractitemmodel子类化。
请帮助重新编写代码以实现我的目标
test.ui文件:https://dropmefiles.com/JqSIy
代码:
from PyQt5 import QtWidgets,QtCore,QtGui,uic
import sys
class Model(QtCore.qabstractitemmodel):
def __init__(self,data_list,h_headers,v_headers,parent = None):
super(Model,self).__init__()
self.data_list = data_list
self.h_headers = h_headers
self.v_headers = v_headers
self.parent = parent
def rowCount(self,parent):
return len(self.v_headers)
def columnCount(self,parent):
return len(self.h_headers)
def data(self,index,role):
if role == QtCore.Qt.displayRole:
row = index.row()
column = index.column()
value = self.data_list[row][column]
return value
def setData(self,value,role = QtCore.Qt.EditRole):
if role == QtCore.Qt.EditRole:
column = index.column()
row = index.row()
self.data_list[row][column] = value
self.dataChanged.emit(index,index)
return True
return False
def headerData(self,section,orientation,role):
if role == QtCore.Qt.displayRole:
if orientation == QtCore.Qt.Horizontal:
return self.h_headers[section]
if orientation == QtCore.Qt.Vertical:
return self.v_headers[section]
def flags(self,index):
return QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsDragEnabled | QtCore.Qt.ItemIsDropEnabled
def index(self,row,column,parent):
pass
def test_setup(w):
list = [
[1,2,3],[4,5,6],[7,8,9]
]
h_headers = ['h1','h2','h3']
v_headers = ['v1','v2','v3']
w.model = Model(list,v_headers)
w.tableView.setModel(w.model)
w.widget_mapper = QtWidgets.QDataWidgetMapper()
w.tableView.clicked.connect(lambda: add_mapping(w))
def add_mapping(w):
row = w.tableView.selectionModel().selectedindexes()[0].row()
column = w.tableView.selectionModel().selectedindexes()[0].column()
w.widget_mapper.setModel(w.model)
w.widget_mapper.addMapping(w.lineEdit,column)
w.widget_mapper.setCurrentIndex(row)
app = QtWidgets.QApplication(sys.argv)
test = uic.loadUi("test.ui")
test_setup(test)
test.show()
sys.exit(app.exec())
解决方法
模型不必是树型的,因为具有角色的类型表的模型就足够了。也不应使用QDataWidgetMapper,因为要使用QDataWidgetMapper需要某种在这种情况下显然不满足的结构,因此解决方案是实现数据更新逻辑。
from dataclasses import dataclass,field
import random
from PyQt5 import QtCore,QtGui,QtWidgets
Param1Role = QtCore.Qt.UserRole
Param2Role = QtCore.Qt.UserRole + 1
ResultRole = QtCore.Qt.UserRole + 2
@dataclass
class Item:
param1: float
param2: float
result: float = field(init=False)
def __post_init__(self):
self.recalculate()
def recalculate(self):
self.result = self.param1 * self.param2
class TableModel(QtCore.QAbstractTableModel):
def __init__(self,data_list,h_headers,v_headers,parent=None):
super().__init__(parent)
self.data_list = data_list
self.h_headers = h_headers
self.v_headers = v_headers
def rowCount(self,parent):
return len(self.v_headers)
def columnCount(self,parent):
return len(self.h_headers)
def data(self,index,role):
row = index.row()
column = index.column()
item = self.data_list[row][column]
if role in (QtCore.Qt.DisplayRole,ResultRole):
return item.result
elif role == Param1Role:
return item.param1
elif role == Param2Role:
return item.param2
def setData(self,value,role=QtCore.Qt.EditRole):
row = index.row()
column = index.column()
item = self.data_list[row][column]
if role == Param1Role:
item.param1 = value
item.recalculate()
self.dataChanged.emit(
index,(ResultRole,QtCore.Qt.DisplayRole,role)
)
return True
elif role == Param2Role:
item.param2 = value
item.recalculate()
self.dataChanged.emit(
index,role)
)
return True
return False
def headerData(self,section,orientation,role):
if role == QtCore.Qt.DisplayRole:
if orientation == QtCore.Qt.Horizontal:
return self.h_headers[section]
if orientation == QtCore.Qt.Vertical:
return self.v_headers[section]
def flags(self,index):
return (
QtCore.Qt.ItemIsEditable
| QtCore.Qt.ItemIsEnabled
| QtCore.Qt.ItemIsSelectable
| QtCore.Qt.ItemIsDragEnabled
| QtCore.Qt.ItemIsDropEnabled
)
class ReadOnlyDelegate(QtWidgets.QStyledItemDelegate):
def createEditor(self,parent,option,index):
pass
class Widget(QtWidgets.QWidget):
def __init__(self,parent=None):
super().__init__(parent)
data = []
for i in range(3):
row_items = []
for i in range(3):
item = Item(*random.sample(range(100),2))
row_items.append(item)
data.append(row_items)
h_headers = ["h1","h2","h3"]
v_headers = ["v1","v2","v3"]
self.view = QtWidgets.QTableView()
delegate = ReadOnlyDelegate(self.view)
self.view.setItemDelegate(delegate)
model = TableModel(data,v_headers)
self.view.setModel(model)
self.param1_spinbox = QtWidgets.QDoubleSpinBox()
self.param2_spinbox = QtWidgets.QDoubleSpinBox()
self.result_label = QtWidgets.QLabel()
self.view.selectionModel().currentChanged.connect(self.update_from_model)
self.view.model().dataChanged.connect(self.update_from_model)
self.param1_spinbox.valueChanged.connect(self.update_to_model)
self.param2_spinbox.valueChanged.connect(self.update_to_model)
lay = QtWidgets.QFormLayout(self)
lay.addRow(self.view)
lay.addRow("Param 1",self.param1_spinbox)
lay.addRow("Param 2",self.param2_spinbox)
lay.addRow("Result",self.result_label)
self.resize(640,480)
def update_from_model(self):
index = self.view.selectionModel().currentIndex()
param1 = index.data(Param1Role)
param2 = index.data(Param2Role)
result = index.data(ResultRole)
self.param1_spinbox.blockSignals(True)
self.param1_spinbox.setValue(param1)
self.param1_spinbox.blockSignals(False)
self.param2_spinbox.blockSignals(True)
self.param2_spinbox.setValue(param2)
self.param2_spinbox.blockSignals(False)
self.result_label.setNum(result)
def update_to_model(self):
index = self.view.selectionModel().currentIndex()
param1 = self.param1_spinbox.value()
param2 = self.param2_spinbox.value()
self.view.model().setData(index,param1,Param1Role)
self.view.model().setData(index,param2,Param2Role)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())