【发布时间】:2021-11-26 14:51:14
【问题描述】:
我制作了一个自定义 StyleItemDelegate,由于某种原因,当它绘制复选框指示器时,它与我的样式表中定义的不匹配。我怎样才能解决这个问题?您可以在应用程序的右侧看到样式表正确地影响复选框在默认列表视图绘制事件中的显示方式。
更新 #1
我制作了一个自定义样式项委托来支持富文本 html 渲染,这一切都很好。我需要重新实现复选框,因为我已经覆盖了绘制事件并确保该复选框仍然可用。但是,我的文本与复选框重叠,使其无法使用。结果,当尝试绘制复选框指示器时,ListItem 的突出显示被破坏,仅在左侧显示一个细长的蓝色条带。
截图
代码
################################################################################
# imports
################################################################################
import os
import sys
from PySide2 import QtGui, QtWidgets, QtCore
################################################################################
# QStyledItemDelegate
################################################################################
class MyDelegate(QtWidgets.QStyledItemDelegate):
MARGINS = 10
def __init__(self, parent=None, *args):
QtWidgets.QStyledItemDelegate.__init__(self, parent, *args)
# overrides
def sizeHint(self, option, index):
'''
Description:
Since labels are stacked we will take whichever is the widest
'''
options = QtWidgets.QStyleOptionViewItem(option)
self.initStyleOption(options, index)
# draw rich text
doc = QtGui.QTextDocument()
doc.setHtml(index.data(QtCore.Qt.DisplayRole))
doc.setDocumentMargin(self.MARGINS)
doc.setDefaultFont(options.font)
doc.setTextWidth(option.rect.width())
return QtCore.QSize(doc.idealWidth(), doc.size().height())
# methods
def paint(self, painter, option, index):
painter.save()
painter.setClipping(True)
painter.setClipRect(option.rect)
opts = QtWidgets.QStyleOptionViewItem(option)
self.initStyleOption(opts, index)
style = QtGui.QApplication.style() if opts.widget is None else opts.widget.style()
# Draw background
if option.state & QtWidgets.QStyle.State_Selected:
painter.fillRect(option.rect, option.palette.highlight().color())
else:
painter.fillRect(option.rect, QtGui.QBrush(QtCore.Qt.NoBrush))
# Draw checkbox
if (index.flags() & QtCore.Qt.ItemIsUserCheckable):
cbStyleOption = QtWidgets.QStyleOptionButton()
if index.data(QtCore.Qt.CheckStateRole):
cbStyleOption.state |= QtWidgets.QStyle.State_On
else:
cbStyleOption.state |= QtWidgets.QStyle.State_Off
cbStyleOption.state |= QtWidgets.QStyle.State_Enabled
cbStyleOption.rect = option.rect.translated(self.MARGINS, 0)
style.drawControl(QtWidgets.QStyle.CE_CheckBox, cbStyleOption, painter, option.widget)
# Draw Title
doc = QtGui.QTextDocument()
doc.setHtml(index.data(QtCore.Qt.DisplayRole))
doc.setTextWidth(option.rect.width())
doc.setDocumentMargin(self.MARGINS)
doc.setDefaultFont(opts.font)
ctx = QtGui.QAbstractTextDocumentLayout.PaintContext()
# highlight text
if option.state & QtWidgets.QStyle.State_Selected:
ctx.palette.setColor(option.palette.Text, option.palette.color(option.palette.Active, option.palette.HighlightedText))
else:
ctx.palette.setColor(option.palette.Text, option.palette.color(option.palette.Active, option.palette.Text))
textRect = style.subElementRect(QtWidgets.QStyle.SE_ItemViewItemText, option)
painter.translate(textRect.topLeft())
painter.setClipRect(textRect.translated(-textRect.topLeft()))
doc.documentLayout().draw(painter, ctx)
# end
painter.restore()
################################################################################
# Widgets
################################################################################
class ListViewExample(QtWidgets.QWidget):
'''
Description:
Extension of listview which supports searching
'''
def __init__(self, parent=None):
super(ListViewExample, self).__init__(parent)
self.setStyleSheet('''
QListView {
color: rgb(255,255,255);
background-color: rgb(60,60,60);
}
QCheckBox, QCheckBox:disabled {
background: transparent;
}
QWidget::indicator {
width: 12px;
height: 12px;
border: 2px solid rgb(90,90,90);
border-radius: 3px;
background: rgb(30,30,30);
}
QWidget::indicator:checked {
border: 2px solid rgb(76,175,80);
background: rgb(0,255,40);
}
''')
self.itemModel = QtGui.QStandardItemModel()
self.checkbox = QtWidgets.QCheckBox('Sample')
self.listView = QtWidgets.QListView()
self.listView.setIconSize(QtCore.QSize(128,128))
self.listView.setModel(self.itemModel)
self.listView.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel)
self.checkboxA = QtWidgets.QCheckBox('Sample')
self.listViewA = QtWidgets.QListView()
self.listViewA.setIconSize(QtCore.QSize(128,128))
self.listViewA.setModel(self.itemModel)
self.listViewA.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel)
# layout
self.mainLayout = QtWidgets.QGridLayout()
self.mainLayout.addWidget(self.checkbox,0,0)
self.mainLayout.addWidget(self.listView,1,0)
self.mainLayout.addWidget(self.checkboxA,0,1)
self.mainLayout.addWidget(self.listViewA,1,1)
self.setLayout(self.mainLayout)
################################################################################
# Widgets
################################################################################
def main():
app = QtWidgets.QApplication(sys.argv)
window = ListViewExample()
window.resize(600,400)
window.listView.setItemDelegate(MyDelegate())
window.itemModel.clear()
for i in range(10):
html = '''
<span style="font-size:12px;">
<b> Player <span>•</span> #{}</b>
</span>
<br>
<span style="font-size:11px;">
<b>Status:</b> <span style='color:rgb(255,0,0);'>⬤</span> Active
<br>
<b>Position:</b> WR
<br>
<b>Team:</b> <span style='color:rgb(0,128,255);'>█</span> Wings
</span>
'''.format(i)
item = QtGui.QStandardItem()
item.setData(html, QtCore.Qt.DisplayRole)
item.setCheckable(True)
window.itemModel.appendRow(item)
window.show()
app.exec_()
if __name__ == '__main__':
pass
main()
【问题讨论】: