QStyledItemDelegate paint() 的子类实现
QStyledItemDelegate subclass implementation of paint()
我有一个 QStandardItemModel 显示在两个 QTreeView 中。第一个 TreeView 使用标准的 QStyledItemDelegate,没有任何改动,并显示带有复选框、图标及其显示文本的项目。第二个视图应该显示同一棵树,尽管 QStyledItemDelegate 的自定义实现应该执行以下操作:
- 以斜体显示某些项目的显示文本
- 将某些项目的显示文本显示为红色
- 删除每个项目的复选框
- 只选择那些不是斜体的项目
项目是否 italics/not 可点击以及它们是否显示为红色都保存在自定义 DataRole 中并使用 index.data(custom_role) 访问。
我的问题是:我无法让文本显示为红色,而且我不知道如何删除复选框,或者如何使项目可选或不可选。
这是我的:
class PostProcessedDelegate(QtWidgets.QStyledItemDelegate):
def __init__(self):
super().__init__()
def paint(self, painter, option, index):
custom_option = QtWidgets.QStyleOptionViewItem(option)
custom_painter = painter
if not index.data(Item.IS_PROCESSED):
custom_option.font.setItalic(True)
#ALSO MAKE ITEM NON-SELECTABLE HERE
if index.data(Item.HAS_PROCESSING_ERROR):
custom_painter.setPen(QtGui.QColor(255,0,0)) #THIS DOESNT HAVE ANY EFFECT
#REMOVE CHECKBOX BEFORE PAINTING
super().paint(custom_painter, custom_option, index)
此屏幕截图显示左侧的第一个 TreeView(没有任何更改的那个)和右侧的第二个,只有一些必需的更改生效。
不需要重写paint方法,需要修改initStyleOption中的QStyleOptionViewItem,选择必须重写QTreeView的selectionCommand方法:
from enum import IntEnum, auto
import random
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class Item(IntEnum):
IS_PROCESSED = QtCore.Qt.UserRole
HAS_PROCESSING_ERROR = auto()
def create_icon():
color = QtGui.QColor(*random.sample(range(255), 3))
pixmap = QtGui.QPixmap(128, 128)
pixmap.fill(color)
return QtGui.QIcon(pixmap)
class StyledItemDelegate(QtWidgets.QStyledItemDelegate):
def initStyleOption(self, option, index):
super().initStyleOption(option, index)
if not index.data(Item.IS_PROCESSED):
option.font.setItalic(True)
if index.data(Item.HAS_PROCESSING_ERROR):
option.palette.setBrush(QtGui.QPalette.Text, QtGui.QColor(255, 0, 0))
option.features &= ~QtWidgets.QStyleOptionViewItem.HasCheckIndicator
class TreeView(QtWidgets.QTreeView):
def __init__(self, parent=None):
super().__init__(parent)
delegate = StyledItemDelegate(self)
self.setItemDelegate(delegate)
def selectionCommand(self, index, event):
if not index.data(Item.IS_PROCESSED):
return QtCore.QItemSelectionModel.NoUpdate
return super().selectionCommand(index, event)
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
model = QtGui.QStandardItemModel(self)
left_view = QtWidgets.QTreeView()
right_view = TreeView()
lay = QtWidgets.QHBoxLayout(self)
lay.addWidget(left_view)
lay.addWidget(right_view)
left_view.setModel(model)
right_view.setModel(model)
root_item = QtGui.QStandardItem("Root")
model.appendRow(root_item)
self.populate(root_item, 3)
left_view.expandAll()
right_view.expandAll()
def populate(self, root_item, level):
for i in range(random.randint(2, 4)):
it = QtGui.QStandardItem("item {}".format(i))
it.setIcon(create_icon())
it.setCheckable(True)
it.setData(random.choice([True, False]), Item.IS_PROCESSED)
it.setData(random.choice([True, False]), Item.HAS_PROCESSING_ERROR)
root_item.appendRow(it)
next_level = level - 1
if next_level > 0:
self.populate(it, next_level)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())
我有一个 QStandardItemModel 显示在两个 QTreeView 中。第一个 TreeView 使用标准的 QStyledItemDelegate,没有任何改动,并显示带有复选框、图标及其显示文本的项目。第二个视图应该显示同一棵树,尽管 QStyledItemDelegate 的自定义实现应该执行以下操作:
- 以斜体显示某些项目的显示文本
- 将某些项目的显示文本显示为红色
- 删除每个项目的复选框
- 只选择那些不是斜体的项目
项目是否 italics/not 可点击以及它们是否显示为红色都保存在自定义 DataRole 中并使用 index.data(custom_role) 访问。
我的问题是:我无法让文本显示为红色,而且我不知道如何删除复选框,或者如何使项目可选或不可选。
这是我的:
class PostProcessedDelegate(QtWidgets.QStyledItemDelegate):
def __init__(self):
super().__init__()
def paint(self, painter, option, index):
custom_option = QtWidgets.QStyleOptionViewItem(option)
custom_painter = painter
if not index.data(Item.IS_PROCESSED):
custom_option.font.setItalic(True)
#ALSO MAKE ITEM NON-SELECTABLE HERE
if index.data(Item.HAS_PROCESSING_ERROR):
custom_painter.setPen(QtGui.QColor(255,0,0)) #THIS DOESNT HAVE ANY EFFECT
#REMOVE CHECKBOX BEFORE PAINTING
super().paint(custom_painter, custom_option, index)
此屏幕截图显示左侧的第一个 TreeView(没有任何更改的那个)和右侧的第二个,只有一些必需的更改生效。
不需要重写paint方法,需要修改initStyleOption中的QStyleOptionViewItem,选择必须重写QTreeView的selectionCommand方法:
from enum import IntEnum, auto
import random
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class Item(IntEnum):
IS_PROCESSED = QtCore.Qt.UserRole
HAS_PROCESSING_ERROR = auto()
def create_icon():
color = QtGui.QColor(*random.sample(range(255), 3))
pixmap = QtGui.QPixmap(128, 128)
pixmap.fill(color)
return QtGui.QIcon(pixmap)
class StyledItemDelegate(QtWidgets.QStyledItemDelegate):
def initStyleOption(self, option, index):
super().initStyleOption(option, index)
if not index.data(Item.IS_PROCESSED):
option.font.setItalic(True)
if index.data(Item.HAS_PROCESSING_ERROR):
option.palette.setBrush(QtGui.QPalette.Text, QtGui.QColor(255, 0, 0))
option.features &= ~QtWidgets.QStyleOptionViewItem.HasCheckIndicator
class TreeView(QtWidgets.QTreeView):
def __init__(self, parent=None):
super().__init__(parent)
delegate = StyledItemDelegate(self)
self.setItemDelegate(delegate)
def selectionCommand(self, index, event):
if not index.data(Item.IS_PROCESSED):
return QtCore.QItemSelectionModel.NoUpdate
return super().selectionCommand(index, event)
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
model = QtGui.QStandardItemModel(self)
left_view = QtWidgets.QTreeView()
right_view = TreeView()
lay = QtWidgets.QHBoxLayout(self)
lay.addWidget(left_view)
lay.addWidget(right_view)
left_view.setModel(model)
right_view.setModel(model)
root_item = QtGui.QStandardItem("Root")
model.appendRow(root_item)
self.populate(root_item, 3)
left_view.expandAll()
right_view.expandAll()
def populate(self, root_item, level):
for i in range(random.randint(2, 4)):
it = QtGui.QStandardItem("item {}".format(i))
it.setIcon(create_icon())
it.setCheckable(True)
it.setData(random.choice([True, False]), Item.IS_PROCESSED)
it.setData(random.choice([True, False]), Item.HAS_PROCESSING_ERROR)
root_item.appendRow(it)
next_level = level - 1
if next_level > 0:
self.populate(it, next_level)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())