有没有办法在PySide或PyQt中向QListView添加节?

时间:2016-06-05 10:53:16

标签: python qt pyqt pyside

此问题与this未回答的问题完全相同,只是我使用的是Python。

我有这个。

enter image description here

我正在寻找这个。

enter image description here

我正在寻找如何解决这个问题的提示。这是我到目前为止所考虑的内容。

  1. 将“虚拟物品”添加到模型本身。我宁愿不这样做,以保持模型免于查看相关数据。我打算在此模型上添加其他视图。
  2. 为每个视图添加代理模型。代理可以添加其他项目并对其进行适当排序。虽然比(1)更清洁,但我并不完全相信原因。
  3. 子类QListView,但我很难理解要覆盖的内容。
  4. 只写自己的观点;使用for-loop和QLabel,并尽可能地与模型同步。要做你必须做的事。
  5. 帮助!

    源。

    import sys
    from PySide import QtCore, QtGui
    
    
    Label = QtCore.Qt.DisplayRole
    Section = QtCore.Qt.UserRole + 1
    
    
    class Model(QtCore.QAbstractListModel):
        def __init__(self, parent=None):
            super(Model, self).__init__(parent)
            self.items = list()
    
        def data(self, index, role):
            item = self.items[index.row()]
            if role == Label:
                return item["label"]
    
            if role == Section:
                return item["section"]
    
        def append(self, item):
            """Append item to end of model"""
            self.beginInsertRows(QtCore.QModelIndex(),
                                 self.rowCount(),
                                 self.rowCount())
    
            self.items.append(item)
            self.endInsertRows()
    
        def rowCount(self, parent=None):
            return len(self.items)
    
    app = QtGui.QApplication(sys.argv)
    model = Model()
    
    for item in ({"label": "Ben", "section": "Human"},
                 {"label": "Steve", "section": "Human"},
                 {"label": "Alpha12", "section": "Robot"},
                 {"label": "Mike", "section": "Toaster"}):
        model.append(item)
    
    view = QtGui.QListView()
    view.setWindowTitle("My View")
    view.setModel(model)
    view.show()
    
    app.exec_()
    


    更新1 - 附加信息

    为清楚起见,这个问题是关于QListView而不是关于它的替代品。原因是应用程序的其余部分正在以类似MVC的方式开发,其中一个或多个视图正在绘制一个模型中存在的唯一数据集。

    这个特殊的视图包括部分,其他视图,不一定是QListView的,不应该对部分有所了解。例如,一个视图可以是计数器,列出可用项目的数量。另一个可能是馅饼,显示以字母“A”开头的项目之间的比率。

    为了进一步参考,我正在寻找的是ListView在QML中的作用。

    也就是说,单个模型带有可选节的附加委托。在这种情况下,视图不需要模型包含这些添加的成员,而是根据现有数据绘制它们。

    实施例


    更新2 - 正在进行的工作

    好的,所以我使用QSortFilterProxyModel将额外的项目添加到视图的底部,但我很难理解:

    1. 如何为其分配相应的数据?
    2. 如何将它们分类到“儿童”项目之上?
    3. enter image description here

      import sys
      from PySide import QtCore, QtGui
      
      
      Label = QtCore.Qt.DisplayRole
      Section = QtCore.Qt.UserRole + 1
      IsSection = QtCore.Qt.UserRole + 2
      
      
      class Item(object):
          @classmethod
          def paint(cls, painter, option, index):
              rect = QtCore.QRectF(option.rect)
      
              painter.save()
      
              if option.state & QtGui.QStyle.State_MouseOver:
                  painter.fillRect(rect, QtGui.QColor("#DEE"))
      
              if option.state & QtGui.QStyle.State_Selected:
                  painter.fillRect(rect, QtGui.QColor("#CDD"))
      
              painter.drawText(rect.adjusted(20, 0, 0, 0),
                               index.data(Label))
      
              painter.restore()
      
          @classmethod
          def sizeHint(cls, option, index):
              return QtCore.QSize(option.rect.width(), 20)
      
      
      class Section(object):
          @classmethod
          def paint(self, painter, option, index):
              painter.save()
              painter.setPen(QtGui.QPen(QtGui.QColor("#666")))
              painter.drawText(QtCore.QRectF(option.rect), index.data(Label))
              painter.restore()
      
          @classmethod
          def sizeHint(self, option, index):
              return QtCore.QSize(option.rect.width(), 20)
      
      
      class Delegate(QtGui.QStyledItemDelegate):
          def paint(self, painter, option, index):
              if index.data(IsSection):
                  return Section.paint(painter, option, index)
              else:
                  return Item.paint(painter, option, index)
      
          def sizeHint(self, option, index):
              if index.data(IsSection):
                  return Section.sizeHint(option, index)
              else:
                  return Item.sizeHint(option, index)
      
      
      class Model(QtCore.QAbstractListModel):
          def __init__(self, parent=None):
              super(Model, self).__init__(parent)
              self.items = list()
      
          def data(self, index, role):
              item = self.items[index.row()]
      
              return {
                  Label: item["label"],
                  Section: item["section"],
                  IsSection: False
              }.get(role)
      
          def append(self, item):
              self.beginInsertRows(QtCore.QModelIndex(),
                                   self.rowCount(),
                                   self.rowCount())
      
              self.items.append(item)
              self.endInsertRows()
      
          def rowCount(self, parent=None):
              return len(self.items)
      
      
      class Proxy(QtGui.QSortFilterProxyModel):
          def data(self, index, role):
              if index.row() >= self.sourceModel().rowCount():
      
                  return {
                      Label: "Virtual Label",
                      Section: "Virtual Section",
                      IsSection: True
                  }.get(role)
      
              return self.sourceModel().data(index, role)
      
          def rowCount(self, parent):
              sections = 0
      
              prev = None
              for item in self.sourceModel().items:
                  cur = item["section"]
      
                  if cur != prev:
                      sections += 1
      
                  prev = cur
      
              # Note: This includes 1 additional, duplicate, section
              # for the bottom item. Ordering of items in model is important.
              return self.sourceModel().rowCount() + sections
      
          def index(self, row, column, parent):
              return self.createIndex(row, column, parent)
      
          def mapToSource(self, index):
              if not index.isValid():
                  return QtCore.QModelIndex()
      
              return self.sourceModel().createIndex(index.row(),
                                                    index.column(),
                                                    QtCore.QModelIndex())
      
          def parent(self, index):
              return QtCore.QModelIndex()
      
      
      app = QtGui.QApplication(sys.argv)
      model = Model()
      
      for item in ({"label": "Ben", "section": "Human"},
                   {"label": "Steve", "section": "Human"},
                   {"label": "Alpha12", "section": "Robot"},
                   {"label": "Mike", "section": "Toaster"},
                   {"label": "Steve", "section": "Human"},
                   ):
          model.append(item)
      
      proxy = Proxy()
      proxy.setSourceModel(model)
      
      delegate = Delegate()
      
      view = QtGui.QListView()
      view.setWindowTitle("My View")
      view.setModel(proxy)
      view.setItemDelegate(delegate)
      view.show()
      
      app.exec_()
      

2 个答案:

答案 0 :(得分:2)

你想要的是QTreeWidget(或QTreeView,如果你想要单独的模型/视图,但是你必须创建自己的模型才能工作)。

tree = QtGui.QTreeWidget()
tree.setHeaderLabels(['Name'])

data = ({"label": "Ben", "section": "Human"},
        {"label": "Steve", "section": "Human"},
        {"label": "Alpha12", "section": "Robot"},
        {"label": "Mike", "section": "Toaster"})

sections = {}
for d in data:
    sections.setdefault(d['section'], []).append(d['label'])

for section, labels in sections.items():
    section_item = QtGui.QTreeWidgetItem(tree, [section])
    for label in labels:
        QtGui.QTreeWidgetItem(section_item, [label])

唯一的选择是使用QListWidget/QListView并使用QItemDelegate来绘制部分项目,而不是标签项目。

答案 1 :(得分:1)

好的,您打算将其他视图(例如饼图)连接到QAbstractListModel后代。我想这是可能的,但这并不常见。因此混乱。 IHMO的Qt模型类并不是那么好,如果我没有表或树视图,我也不会使用它们。没有什么能阻止你制作自己的模型类并从中填充QListWidget

但是,我们仍然希望您将数据放在QAbstractListModel中。我同意将这些部分添加为虚拟物品是一个坏主意,因此您的选项1已经用完了。

我认为使用代理模型是一个不错的选择。您可以将所有QListViews连接到代理,将所有其他视图(例如饼图)连接到基础源模型。代理模型 然后将这些部分包含为项目。我认为您只需要一个代理,因为所有列表视图的部分都是相同的。

您可以使用委托来自定义单元格的呈现方式,而不是从QListView继承子类。请查看Qt文档中的star delegate example

选项4,编写自己的视图,应该是您的最后选择。你基本上推出了自己的版本QListView。这是很多工作,永远不会像Qt原版那么好。

希望这有帮助。