使用委托向 QTableView 单元格中的文本添加复选框
Add a checkbox to text in a QTableView cell using delegate
我想在我的 QTableView 中向带有文本的单元格添加一个复选框:
- 当一个单元格未激活时,我们应该看到一个文本和一个灰色的复选框,就像这样
- 当我们打开委托编辑器时,我们应该能够编辑文本并更改复选框的状态
仅供参考 - 我还打算下一步做:激活复选框时 - 单元格将拒绝更新其中文本的信号,如果未选中复选框 - 发出特殊信号时将更新文本。
我知道我应该子类化 QStyledItemDelegate 并重新实现 paint 方法,但作为一个初学者,我发现很难弄清楚这个任务。
您可以使用此示例编写和测试代码:
from PyQt5 import QtCore, QtGui, QtWidgets
import sys
import pickle
class Mainwindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.table = QtWidgets.QTableView()
self.setCentralWidget(self.table)
self.list = ['item_1', 'item_2','item_3']
self.data = [
[1, 'Blocks γ=500 GOST 31359-2007', self.list[0], 0.18, 0.22],
[2, 'Blocks γ=600 GOST 31359-2008', self.list[0], 0.25, 0.27],
[3, 'Insulation', self.list[0], 0.041, 0.042],
[3, 'Insulation', self.list[0], 0.041, 0.042]
]
self.model = Materials(self.data)
self.table.setModel(self.model)
self.table.setSelectionBehavior(self.table.SelectRows)
self.table.setSelectionMode(self.table.SingleSelection)
for row in range(len(self.model.materials)):
index = self.table.model().index(row,2)
self.table.setIndexWidget(index, self.setting_combobox(index))
def setting_combobox(self, index):
widget = QtWidgets.QComboBox()
list = self.list
widget.addItems(list)
widget.setCurrentIndex(0)
widget.currentTextChanged.connect(lambda value: self.model.setData(index, value))
return widget
class Materials(QtCore.QAbstractTableModel):
def __init__(self, materials = [[]], parent = None):
super(Materials, self).__init__()
self.materials = materials
def rowCount(self, parent):
return len(self.materials)
def columnCount(self, parent):
return len(self.materials[0])
def data(self, index, role):
if role == QtCore.Qt.DisplayRole:
row = index.row()
column = index.column()
value = self.materials[row][column]
return value
if role == QtCore.Qt.EditRole:
row = index.row()
column = index.column()
value = self.materials[row][column]
return value
if role == QtCore.Qt.FontRole:
if index.column() == 0:
boldfont = QtGui.QFont()
boldfont.setBold(True)
return boldfont
def setData(self, index, value, role = QtCore.Qt.EditRole):
if role == QtCore.Qt.EditRole:
row = index.row()
column = index.column()
self.materials[row][column] = value
self.dataChanged.emit(index, index)
return True
return False
def flags(self, index):
return QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable
if __name__ == '__main__':
app = QtWidgets.QApplication([])
application = Mainwindow()
application.show()
sys.exit(app.exec())
一个可能的解决方案是启用角色 Qt.CheckStateRole 并使用委托修改复选框的位置。为此,还必须修改模型以存储复选框的状态。
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class CustomDelegate(QtWidgets.QStyledItemDelegate):
def initStyleOption(self, option, index):
value = index.data(QtCore.Qt.CheckStateRole)
if value is None:
model = index.model()
model.setData(index, QtCore.Qt.Unchecked, QtCore.Qt.CheckStateRole)
super().initStyleOption(option, index)
option.direction = QtCore.Qt.RightToLeft
option.displayAlignment = QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter
class Mainwindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.table = QtWidgets.QTableView()
self.setCentralWidget(self.table)
self.list = ["item_1", "item_2", "item_3"]
self.data = [
[1, "Blocks γ=500 GOST 31359-2007", self.list[0], 0.18, 0.22],
[2, "Blocks γ=600 GOST 31359-2008", self.list[0], 0.25, 0.27],
[3, "Insulation", self.list[0], 0.041, 0.042],
[3, "Insulation", self.list[0], 0.041, 0.042],
]
self.model = Materials(self.data)
self.table.setModel(self.model)
self.table.setSelectionBehavior(self.table.SelectRows)
self.table.setSelectionMode(self.table.SingleSelection)
for row in range(len(self.model.materials)):
index = self.table.model().index(row, 2)
self.table.setIndexWidget(index, self.setting_combobox(index))
delegate = CustomDelegate(self.table)
self.table.setItemDelegateForColumn(4, delegate)
self.resize(640, 480)
def setting_combobox(self, index):
widget = QtWidgets.QComboBox()
list = self.list
widget.addItems(list)
widget.setCurrentIndex(0)
widget.currentTextChanged.connect(
lambda value: self.model.setData(index, value)
)
return widget
class Materials(QtCore.QAbstractTableModel):
def __init__(self, materials=[[]], parent=None):
super(Materials, self).__init__()
self.materials = materials
self.check_states = dict()
def rowCount(self, parent):
return len(self.materials)
def columnCount(self, parent):
return len(self.materials[0])
def data(self, index, role):
if role == QtCore.Qt.DisplayRole:
row = index.row()
column = index.column()
value = self.materials[row][column]
return value
if role == QtCore.Qt.EditRole:
row = index.row()
column = index.column()
value = self.materials[row][column]
return value
if role == QtCore.Qt.FontRole:
if index.column() == 0:
boldfont = QtGui.QFont()
boldfont.setBold(True)
return boldfont
if role == QtCore.Qt.CheckStateRole:
value = self.check_states.get(QtCore.QPersistentModelIndex(index))
if value is not None:
return value
def setData(self, index, value, role=QtCore.Qt.EditRole):
if role == QtCore.Qt.EditRole:
row = index.row()
column = index.column()
self.materials[row][column] = value
self.dataChanged.emit(index, index, (role,))
return True
if role == QtCore.Qt.CheckStateRole:
self.check_states[QtCore.QPersistentModelIndex(index)] = value
self.dataChanged.emit(index, index, (role,))
return True
return False
def flags(self, index):
return (
QtCore.Qt.ItemIsEditable
| QtCore.Qt.ItemIsEnabled
| QtCore.Qt.ItemIsSelectable
| QtCore.Qt.ItemIsUserCheckable
)
if __name__ == "__main__":
app = QtWidgets.QApplication([])
application = Mainwindow()
application.show()
sys.exit(app.exec())
我想在我的 QTableView 中向带有文本的单元格添加一个复选框:
- 当一个单元格未激活时,我们应该看到一个文本和一个灰色的复选框,就像这样
- 当我们打开委托编辑器时,我们应该能够编辑文本并更改复选框的状态
仅供参考 - 我还打算下一步做:激活复选框时 - 单元格将拒绝更新其中文本的信号,如果未选中复选框 - 发出特殊信号时将更新文本。
我知道我应该子类化 QStyledItemDelegate 并重新实现 paint 方法,但作为一个初学者,我发现很难弄清楚这个任务。
您可以使用此示例编写和测试代码:
from PyQt5 import QtCore, QtGui, QtWidgets
import sys
import pickle
class Mainwindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.table = QtWidgets.QTableView()
self.setCentralWidget(self.table)
self.list = ['item_1', 'item_2','item_3']
self.data = [
[1, 'Blocks γ=500 GOST 31359-2007', self.list[0], 0.18, 0.22],
[2, 'Blocks γ=600 GOST 31359-2008', self.list[0], 0.25, 0.27],
[3, 'Insulation', self.list[0], 0.041, 0.042],
[3, 'Insulation', self.list[0], 0.041, 0.042]
]
self.model = Materials(self.data)
self.table.setModel(self.model)
self.table.setSelectionBehavior(self.table.SelectRows)
self.table.setSelectionMode(self.table.SingleSelection)
for row in range(len(self.model.materials)):
index = self.table.model().index(row,2)
self.table.setIndexWidget(index, self.setting_combobox(index))
def setting_combobox(self, index):
widget = QtWidgets.QComboBox()
list = self.list
widget.addItems(list)
widget.setCurrentIndex(0)
widget.currentTextChanged.connect(lambda value: self.model.setData(index, value))
return widget
class Materials(QtCore.QAbstractTableModel):
def __init__(self, materials = [[]], parent = None):
super(Materials, self).__init__()
self.materials = materials
def rowCount(self, parent):
return len(self.materials)
def columnCount(self, parent):
return len(self.materials[0])
def data(self, index, role):
if role == QtCore.Qt.DisplayRole:
row = index.row()
column = index.column()
value = self.materials[row][column]
return value
if role == QtCore.Qt.EditRole:
row = index.row()
column = index.column()
value = self.materials[row][column]
return value
if role == QtCore.Qt.FontRole:
if index.column() == 0:
boldfont = QtGui.QFont()
boldfont.setBold(True)
return boldfont
def setData(self, index, value, role = QtCore.Qt.EditRole):
if role == QtCore.Qt.EditRole:
row = index.row()
column = index.column()
self.materials[row][column] = value
self.dataChanged.emit(index, index)
return True
return False
def flags(self, index):
return QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable
if __name__ == '__main__':
app = QtWidgets.QApplication([])
application = Mainwindow()
application.show()
sys.exit(app.exec())
一个可能的解决方案是启用角色 Qt.CheckStateRole 并使用委托修改复选框的位置。为此,还必须修改模型以存储复选框的状态。
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class CustomDelegate(QtWidgets.QStyledItemDelegate):
def initStyleOption(self, option, index):
value = index.data(QtCore.Qt.CheckStateRole)
if value is None:
model = index.model()
model.setData(index, QtCore.Qt.Unchecked, QtCore.Qt.CheckStateRole)
super().initStyleOption(option, index)
option.direction = QtCore.Qt.RightToLeft
option.displayAlignment = QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter
class Mainwindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.table = QtWidgets.QTableView()
self.setCentralWidget(self.table)
self.list = ["item_1", "item_2", "item_3"]
self.data = [
[1, "Blocks γ=500 GOST 31359-2007", self.list[0], 0.18, 0.22],
[2, "Blocks γ=600 GOST 31359-2008", self.list[0], 0.25, 0.27],
[3, "Insulation", self.list[0], 0.041, 0.042],
[3, "Insulation", self.list[0], 0.041, 0.042],
]
self.model = Materials(self.data)
self.table.setModel(self.model)
self.table.setSelectionBehavior(self.table.SelectRows)
self.table.setSelectionMode(self.table.SingleSelection)
for row in range(len(self.model.materials)):
index = self.table.model().index(row, 2)
self.table.setIndexWidget(index, self.setting_combobox(index))
delegate = CustomDelegate(self.table)
self.table.setItemDelegateForColumn(4, delegate)
self.resize(640, 480)
def setting_combobox(self, index):
widget = QtWidgets.QComboBox()
list = self.list
widget.addItems(list)
widget.setCurrentIndex(0)
widget.currentTextChanged.connect(
lambda value: self.model.setData(index, value)
)
return widget
class Materials(QtCore.QAbstractTableModel):
def __init__(self, materials=[[]], parent=None):
super(Materials, self).__init__()
self.materials = materials
self.check_states = dict()
def rowCount(self, parent):
return len(self.materials)
def columnCount(self, parent):
return len(self.materials[0])
def data(self, index, role):
if role == QtCore.Qt.DisplayRole:
row = index.row()
column = index.column()
value = self.materials[row][column]
return value
if role == QtCore.Qt.EditRole:
row = index.row()
column = index.column()
value = self.materials[row][column]
return value
if role == QtCore.Qt.FontRole:
if index.column() == 0:
boldfont = QtGui.QFont()
boldfont.setBold(True)
return boldfont
if role == QtCore.Qt.CheckStateRole:
value = self.check_states.get(QtCore.QPersistentModelIndex(index))
if value is not None:
return value
def setData(self, index, value, role=QtCore.Qt.EditRole):
if role == QtCore.Qt.EditRole:
row = index.row()
column = index.column()
self.materials[row][column] = value
self.dataChanged.emit(index, index, (role,))
return True
if role == QtCore.Qt.CheckStateRole:
self.check_states[QtCore.QPersistentModelIndex(index)] = value
self.dataChanged.emit(index, index, (role,))
return True
return False
def flags(self, index):
return (
QtCore.Qt.ItemIsEditable
| QtCore.Qt.ItemIsEnabled
| QtCore.Qt.ItemIsSelectable
| QtCore.Qt.ItemIsUserCheckable
)
if __name__ == "__main__":
app = QtWidgets.QApplication([])
application = Mainwindow()
application.show()
sys.exit(app.exec())