QAbstractItemModel:创建 table 个具有映射子元素的父元素
QAbstractItemModel: create a table of parent elements with mapped children
我需要制作一个 table,其中每个项目都是父项,有两个子项(两个参数,映射到 lineEdit 'param 1' 和'param 2)。 table 中的父值应该是参数 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, index, 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, h_headers, 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())
模型不必是树类型,因为具有角色的 table 类型模型就足够了。也不应该使用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, index, 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, index, (ResultRole, QtCore.Qt.DisplayRole, role)
)
return True
elif role == Param2Role:
item.param2 = value
item.recalculate()
self.dataChanged.emit(
index, index, (ResultRole, QtCore.Qt.DisplayRole, 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, h_headers, 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_())
我需要制作一个 table,其中每个项目都是父项,有两个子项(两个参数,映射到 lineEdit 'param 1' 和'param 2)。 table 中的父值应该是参数 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, index, 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, h_headers, 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())
模型不必是树类型,因为具有角色的 table 类型模型就足够了。也不应该使用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, index, 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, index, (ResultRole, QtCore.Qt.DisplayRole, role)
)
return True
elif role == Param2Role:
item.param2 = value
item.recalculate()
self.dataChanged.emit(
index, index, (ResultRole, QtCore.Qt.DisplayRole, 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, h_headers, 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_())