python - 使用 QAbstractListModel 在默认 QListView 中绘制项目的“图标”空间?
问题描述
考虑这个示例,修改自QStyledItemDelegate 绘制刷新问题:
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class MyElement(object):
def __init__(self, numid):
self.numid = numid
self.strid = "Hello world {}".format(numid)
self.param = 'a' if numid%2==0 else 'b'
def __repr__(self):
return "(numid {}, strid '{}', param '{}')".format(self.numid, self.strid, self.param)
elements = [ MyElement(i) for i in range(20) ]
print(elements)
class ElementListModel(QtCore.QAbstractListModel):
def __init__(self, elements = [], parent = None):
super(ElementListModel, self).__init__()
self.__elements = elements
def rowCount(self, parent):
return len(self.__elements)
def data(self, index, role):
thiselement = self.__elements[index.row()]
if role == QtCore.Qt.DisplayRole:
return str( thiselement.strid )
elif role == QtCore.Qt.DecorationRole:
return QtGui.QColor(thiselement.numid*10,thiselement.numid,0)
class ElementThumbDelegate(QtWidgets.QStyledItemDelegate): #(QtGui.QStyledItemDelegate):
def __init__(self, view, parent=None):
super(ElementThumbDelegate, self).__init__(parent)
def paint(self, painter, options, index):
super(ElementThumbDelegate, self).paint(painter, options, index)
#painter.setRenderHint(QtGui.QPainter.Antialiasing)
#painter.setPen(QtGui.QColor(255, 255, 255))
#painter.setBrush(QtGui.QColor(10, 10, 10))
#painter.drawRect(options.rect)
#painter.drawText(options.rect, QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter, str(index.data()))
#def sizeHint(self, options, index):
# return QtCore.QSize(50, 50)
def main():
app = QtWidgets.QApplication(sys.argv)
viewer = QtWidgets.QListView()
viewModel = ElementListModel(elements)
viewer.setModel(viewModel)
#viewer.setViewMode(QtWidgets.QListView.IconMode)
viewer.setItemDelegate(ElementThumbDelegate(viewer))
viewer.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
结果是这样的:
请注意,默认情况下,项目左侧有一个框,您可以通过ListModelDecorationRole
的data
方法“着色”(显然,如果您返回 QIcon 而不是 QColor,您也可以在那里存储一个图标,但是我从来没有尝试过)。
我的问题是:
- 如何根据某些属性在该图标空间/框周围绘制边框?在上面的示例中,如果
MyElement.param == 'a'
列表中的给定元素,那么我想要在“图标空间/框”周围绘制宽度为 2 像素的浅蓝色(RGB:(38、76、100)或#62c2ff)边框- 就像我在圆圈区域的模型图像中手动完成的一样;否则我不想要边框 - 根据某些属性,我如何在该空间/框的中心另外绘制一个字母?例如,如果
MyElement.param == 'b'
列表中给定元素的 a,那么我希望在“图标空间/框”中间用白色写的字母 'b' - 否则,我不希望在其中写额外的文本空间。
ElementThumbDelegate 的 paint() 方法应该足以说明如何执行此操作;但是如果你取消注释,你会看到整个项目都被改变了——不仅仅是左边的图标框/空间。
解决方案
执行绘制的类是从DecorationRole角色获取信息来创建图标的委托,因此解决方案是根据项目信息创建自定义图标。该创建可以在模型或委托中完成,在这种情况下,我将使用第二个选项,但为此项目必须通过自定义角色(如 Qt.UserRole)公开:
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class MyElement(object):
def __init__(self, numid):
self.numid = numid
self.strid = "Hello world {}".format(numid)
self.param = "a" if numid % 2 == 0 else "b"
def __repr__(self):
return "(numid {}, strid '{}', param '{}')".format(
self.numid, self.strid, self.param
)
elements = [MyElement(i) for i in range(20)]
class ElementListModel(QtCore.QAbstractListModel):
def __init__(self, elements=[], parent=None):
super(ElementListModel, self).__init__()
self.__elements = elements
def rowCount(self, parent=QtCore.QModelIndex()):
return len(self.__elements)
def data(self, index, role):
if not index.isValid() or not (0 <= index.row() < self.rowCount()):
return
thiselement = self.__elements[index.row()]
if role == QtCore.Qt.DisplayRole:
return str(thiselement.strid)
if role == QtCore.Qt.UserRole:
return thiselement
class ElementThumbDelegate(QtWidgets.QStyledItemDelegate):
def initStyleOption(self, option, index):
super().initStyleOption(option, index)
thiselement = index.data(QtCore.Qt.UserRole)
if isinstance(thiselement, MyElement):
if thiselement.param == "a":
option.features |= QtWidgets.QStyleOptionViewItem.HasDecoration
pixmap = QtGui.QPixmap(option.decorationSize)
pixmap.fill(QtGui.QColor("#62c2ff"))
painter = QtGui.QPainter(pixmap)
color = QtGui.QColor(thiselement.numid * 10, thiselement.numid, 0)
painter.fillRect(pixmap.rect().adjusted(2, 2, -2, -2), color)
painter.end()
option.icon = QtGui.QIcon(pixmap)
if thiselement.param == "b":
option.features |= QtWidgets.QStyleOptionViewItem.HasDecoration
pixmap = QtGui.QPixmap(option.decorationSize)
color = QtGui.QColor(thiselement.numid * 10, thiselement.numid, 0)
pixmap.fill(color)
painter = QtGui.QPainter(pixmap)
painter.setPen(QtGui.QColor("white"))
painter.drawText(pixmap.rect(), QtCore.Qt.AlignCenter, "b")
painter.end()
option.icon = QtGui.QIcon(pixmap)
def main():
app = QtWidgets.QApplication(sys.argv)
viewer = QtWidgets.QListView()
viewModel = ElementListModel(elements)
viewer.setModel(viewModel)
viewer.setItemDelegate(ElementThumbDelegate(viewer))
viewer.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
推荐阅读
- javascript - 如何比较 2 个数组中的值以过滤掉元素
- python - 仅提取首字母大写的整个单词
- python - 如何识别字典对于提供的输入没有已知值,而不仅仅是导致错误(KeyError)Python 3.6
- rstudio - blogdown 中的 serve_site() 取消删除旧文件
- batch-file - 用文本替换数字
- c++ - 输出向量
在模板函数中 - c++ - C++ 枚举索引值
- android - 无法将 java.util.HashMap 类型的值转换为 String 并且应用程序崩溃
- spring - 如何使用 Spring WebClient 同时进行多个调用?
- ios - 使用响应式编程将带有 imageURL 的对象转换为带有下载的 UIImage 的对象