首页 > 解决方案 > 使用 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()

结果是这样的:

pyqt5-listview.png

请注意,默认情况下,项目左侧有一个框,您可以通过ListModelDecorationRoledata方法“着色”(显然,如果您返回 QIcon 而不是 QColor,您也可以在那里存储一个图标,但是我从来没有尝试过)。

我的问题是:

ElementThumbDelegate 的 paint() 方法应该足以说明如何执行此操作;但是如果你取消注释,你会看到整个项目都被改变了——不仅仅是左边的图标框/空间。

标签: pythonpyqt5

解决方案


执行绘制的类是从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()

在此处输入图像描述


推荐阅读