【发布时间】:2021-01-24 10:04:57
【问题描述】:
我正在使用子类 QItemDelegate 来显示和编辑 QTableView 中的单元格。编辑部分完美运行。
我遇到的问题是,对于每个可编辑的单元格,在选择行时背景保持白色(我使用行选择行为)。
I have tried playing with the paint() method but whatever I do the background for those cells does not change when the row is selected.
我的互联网研究使我认为,因为用于编辑单元格的不同小部件占据了整个单元格矩形(因为这是在 updateEditorGeometry() 方法中定义的方式),所以如果隐藏单元格的背景。
我该如何解决这个问题,以便用于编辑单元格的小部件的背景与行的其余部分获得相同的选定背景。
这是显示问题的图像:
仍然具有白色背景的每个单元格都是可编辑的,并且在 QItemDelegate 类中定义。
这是委托类的代码:
class SqlAlchemyItemDelegate(QtWidgets.QItemDelegate):
def __init__(self):
super().__init__()
def paint(self, painter: QtGui.QPainter, option: 'QStyleOptionViewItem', index: QtCore.QModelIndex) -> None:
# Checkboxes
check_state = index.data(QtCore.Qt.CheckStateRole)
if isinstance(check_state, int):
self.drawCheck(painter, option, option.rect, check_state)
self.drawFocus(painter, option, option.rect)
return
super().paint(painter, option, index)
def editorEvent(self, event, model, option, index):
if event.type() == QtCore.QEvent.MouseButtonRelease and isinstance(index.data(QtCore.Qt.DisplayRole), bool):
value = bool(model.data(index, QtCore.Qt.CheckStateRole))
model.setData(index, not value)
event.accept()
return super().editorEvent(event, model, option, index)
def createEditor(self, parent: QtWidgets.QWidget, option: 'QStyleOptionViewItem', index: QtCore.QModelIndex) -> QtWidgets.QWidget:
data = index.model().data(index, QtCore.Qt.EditRole)
if isinstance(data, datetime.datetime):
return QtWidgets.QDateTimeEdit(parent)
if isinstance(data, str):
return QtWidgets.QLineEdit(parent)
def setEditorData(self, editor: QtWidgets.QWidget, index: QtCore.QModelIndex) -> None:
data = index.model().data(index, QtCore.Qt.EditRole)
if isinstance(editor, QtWidgets.QDateTimeEdit):
editor.setDateTime(data)
if isinstance(editor, QtWidgets.QLineEdit):
editor.setText(data)
def setModelData(self, editor: QtWidgets.QWidget, model: QtCore.QAbstractItemModel, index: QtCore.QModelIndex) -> None:
if isinstance(editor, QtWidgets.QDateTimeEdit):
model.setData(index, editor.dateTime().toPyDateTime(), QtCore.Qt.EditRole)
if isinstance(editor, QtWidgets.QLineEdit):
model.setData(index, editor.text(), QtCore.Qt.EditRole)
def updateEditorGeometry(self, editor: QtWidgets.QWidget, option: 'QStyleOptionViewItem', index: QtCore.QModelIndex) -> None:
editor.setGeometry(option.rect)
编辑
这是一个重现该问题的最小工作示例:
from PyQt5 import QtWidgets, QtCore, QtGui
import datetime
class Gui(QtWidgets.QDialog):
def __init__(self):
super().__init__()
self.setMinimumWidth(750)
self.table_view = QtWidgets.QTableView()
self.table_view.setSelectionBehavior(self.table_view.SelectRows)
self.table_model = SqlAlchemyTableModel()
self.table_model.from_complex_query(
table_data=[
[1, 'Alex', 'Knight', '28', '0123456789', True, datetime.datetime(year=2021, month=5, day=12, hour=2, minute=30, second=30)],
[2, 'Alex', 'Knight', '28', '0123456789', True, datetime.datetime(year=2021, month=5, day=12, hour=2, minute=30, second=30)],
[3, 'Alex', 'Knight', '28', '0123456789', True, datetime.datetime(year=2021, month=5, day=12, hour=2, minute=30, second=30)]
],
headers=['id', 'first', 'last', 'age', 'phone', 'checked', 'date'],
editable_columns=[4, 5, 6]
)
self.table_view.setModel(self.table_model)
self.delegate = SqlAlchemyItemDelegate()
self.table_view.setItemDelegate(self.delegate)
self.layout = QtWidgets.QVBoxLayout(self)
self.layout.addWidget(self.table_view)
self.setLayout(self.layout)
self.show()
class SqlAlchemyItemDelegate(QtWidgets.QItemDelegate):
def __init__(self):
super().__init__()
def paint(self, painter: QtGui.QPainter, option: 'QStyleOptionViewItem', index: QtCore.QModelIndex) -> None:
# Checkboxes
check_state = index.data(QtCore.Qt.CheckStateRole)
if isinstance(check_state, int):
self.drawCheck(painter, option, option.rect, check_state)
self.drawFocus(painter, option, option.rect)
return
super().paint(painter, option, index)
def editorEvent(self, event, model, option, index):
if event.type() == QtCore.QEvent.MouseButtonRelease and isinstance(index.data(QtCore.Qt.DisplayRole), bool):
value = bool(model.data(index, QtCore.Qt.CheckStateRole))
model.setData(index, not value)
event.accept()
return super().editorEvent(event, model, option, index)
def createEditor(self, parent: QtWidgets.QWidget, option: 'QStyleOptionViewItem', index: QtCore.QModelIndex) -> QtWidgets.QWidget:
data = index.model().data(index, QtCore.Qt.EditRole)
if isinstance(data, datetime.datetime):
return QtWidgets.QDateTimeEdit(parent)
if isinstance(data, str):
return QtWidgets.QLineEdit(parent)
def setEditorData(self, editor: QtWidgets.QWidget, index: QtCore.QModelIndex) -> None:
data = index.model().data(index, QtCore.Qt.EditRole)
if isinstance(editor, QtWidgets.QDateTimeEdit):
editor.setDateTime(data)
if isinstance(editor, QtWidgets.QLineEdit):
editor.setText(data)
def setModelData(self, editor: QtWidgets.QWidget, model: QtCore.QAbstractItemModel, index: QtCore.QModelIndex) -> None:
if isinstance(editor, QtWidgets.QDateTimeEdit):
model.setData(index, editor.dateTime().toPyDateTime(), QtCore.Qt.EditRole)
if isinstance(editor, QtWidgets.QLineEdit):
model.setData(index, editor.text(), QtCore.Qt.EditRole)
def updateEditorGeometry(self, editor: QtWidgets.QWidget, option: 'QStyleOptionViewItem', index: QtCore.QModelIndex) -> None:
editor.setGeometry(option.rect)
class SqlAlchemyTableModel(QtCore.QAbstractTableModel):
def __init__(self, **kwargs):
super().__init__()
def rowCount(self, *args):
return len(self.table_data)
def columnCount(self, *args):
return len(self.headers)
def data(self, index, role):
row = index.row()
col = index.column()
# EditRole is handled by SqlAlchemyItemDelegate
if role == QtCore.Qt.EditRole:
return self.table_data[row][col]
# For Booleans, display checkbox
if isinstance(self.table_data[row][col], bool):
if role == QtCore.Qt.CheckStateRole:
if self.table_data[row][col]:
return QtCore.QVariant(QtCore.Qt.Checked)
else:
return QtCore.QVariant(QtCore.Qt.Unchecked)
return self.table_data[row][col]
# For DateTime, display QDateTime
if isinstance(self.table_data[row][col], datetime.datetime) and role == QtCore.Qt.DisplayRole:
return QtCore.QDateTime(self.table_data[row][col])
# For other data, display itself
if role == QtCore.Qt.DisplayRole:
return self.table_data[row][col]
def headerData(self, section, orientation, role):
if role == QtCore.Qt.DisplayRole and orientation == QtCore.Qt.Horizontal:
return self.headers[section]
def flags(self, index):
if self.editable_columns and index.column() in self.editable_columns and isinstance(self.table_data[index.row()][index.column()], bool):
return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsUserCheckable
if self.editable_columns and index.column() in self.editable_columns and isinstance(self.table_data[index.row()][index.column()], (str, datetime.datetime)):
return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsEditable
return super().flags(index)
def setData(self, index, value, role):
row = index.row()
col = index.column()
if role == QtCore.Qt.EditRole:
self.table_data[row][col] = value
self.dataChanged.emit(index, index)
return True
return super().setData(index, value, role)
def from_complex_query(self, table_data, headers, editable_columns=None):
self.table_data = [list(row) for row in table_data]
self.headers = headers
self.editable_columns = editable_columns
if __name__ == '__main__':
app = QtWidgets.QApplication([])
win = Gui()
app.exec()
【问题讨论】:
-
在显示可检查项目时,绘制肯定缺少
drawBackground方法,并且通常建议使用QStyledItemDelegate,而不是更基本和有限的QItemDelegate。也就是说,请提供minimal, reproducible example。 -
@musicamante 谢谢你的回答,我用一个完整的例子更新了这个问题。我使用 QItemDelegate 因为它有 drawCheck() 方法(QStyledItemDelegate 没有)允许我将复选框居中。
标签: python python-3.x qt pyqt5