简体   繁体   English

有没有一种方法可以在PySide或PyQt中向QListView添加节?

[英]Is there a way to add sections to QListView in PySide or PyQt?

This question is an exact duplicate of this unanswered question, except that I'm using Python. 除了我使用的是Python之外, 问题与未回答的问题完全相同。

I've got this. 我有这个

在此处输入图片说明

And am looking for this. 并且正在寻找这个。

在此处输入图片说明

I'm looking for hints as to how to approach this. 我正在寻找有关如何解决此问题的提示。 Here's what I've considered so far. 到目前为止,这是我考虑过的内容。

  1. Add "virtual items" to the model itself. 将“虚拟物品”添加到模型本身。 I'd rather not do this, in order to keep the model free of view related data. 我宁愿不这样做,以使模型不包含与视图相关的数据。 I intend to add additional views onto this model. 我打算在此模型上添加其他视图。
  2. Add a proxy model per view. 为每个视图添加代理模型。 The proxy could add additional items and sort them appropriately. 代理可以添加其他项目并对其进行适当排序。 Albeit cleaner than (1), I'm not entirely convinced for the same reasons. 尽管比(1)干净,但出于相同的原因,我并不完全相信。
  3. Subclass QListView, but I'm struggling to understand what to override. 子类QListView,但我正在努力了解要覆盖的内容。
  4. Just write my own view; 写下我自己的观点; with a for-loop and QLabels, and synchronise with the model best I can. 使用for循环和QLabel,并尽我所能与模型同步。 Gotta do what you gotta do. 要做你该做的事。

Help! 救命!

Source. 资源。

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_()


Update 1 - Additional Information 更新1-附加信息

For clarity, this question is about QListView rather than about alternatives to it. 为了清楚起见,这个问题是关于QListView的,而不是它的替代品。 The reason being that the rest of the application is developing in an MVC-like fashion, where one or more views are drawing the unique set of data present in the one model. 原因是该应用程序的其余部分正在以类似于MVC的方式开发,其中一个或多个视图正在绘制一个模型中存在的唯一数据集。

This particular view includes sections, the other views, which aren't necessarily QListView's, shouldn't know anything about sections. 这个特定的视图包括部分,其他视图(不一定是QListView的部分)对这些部分一无所知。 For example, one view may be a counter, listing the number of items available. 例如,一个视图可能是一个计数器,其中列出了可用的项目数。 Another might be a pie, showing the ratio between items starting with the letter 'A'. 另一个可能是馅饼,显示以字母“ A”开头的项目之间的比率。

For further reference, what I'm looking for is exactly what ListView does in QML. 作为进一步的参考,我正在寻找的正是ListView在QML中的功能。

That is, a single model with an additional delegate for optional sections. 即,一个带有附加委托的单个模型用于可选节。 In this case, the view doesn't require the model to contain these added members, but rather draws them based on the existing data. 在这种情况下,视图不需要模型包含这些添加的成员,而是根据现有数据绘制它们。

Example


Update 2 - Work in progress 更新2-进行中

Ok, so I've got the extra items added to the bottom of the view using a QSortFilterProxyModel, but I'm struggling to understand: 好的,所以我已经使用QSortFilterProxyModel将多余的项目添加到视图的底部,但是我一直在努力理解:

  1. How do I assign them their corresponding data? 如何为他们分配相应的数据?
  2. How do I sort them into place, above their "child" items? 如何在“子”项上方将它们分类到位?

在此处输入图片说明

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_()

What you want is a QTreeWidget (or QTreeView if you want separate model/views, but you'll have to create your own model for that to work). 您想要的是一个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])

The only other option would be to use a QListWidget/QListView and use a QItemDelegate to draw your section items differently than your label items. 唯一的其他选择是使用QListWidget/QListView并使用QItemDelegate标签项不同地绘制项。

Ok, you intend to connect the other views (such as the Pie chart) to a QAbstractListModel descendant. 好的,您打算将其他视图(例如饼图)连接到QAbstractListModel后代。 I guess this is possible but it is not that common. 我想这是可能的,但并不普遍。 Hence the confusion. 因此造成混乱。 IHMO the Qt Model classes are not that great and I wouldn't use them if I didn't have a table or tree view. IHMO的Qt Model类不是很好,如果我没有表或树视图,我不会使用它们。 Nothing stops you from making your own model class and populating a QListWidget from it. 没有什么可以阻止您创建自己的模型类并从中填充QListWidget

But let's say you still want to put your data in a QAbstractListModel . 但是,假设您仍然想将数据放入QAbstractListModel I agree that adding the sections as virtual items is a bad idea, so your option 1 is out. 我同意将这些部分添加为虚拟项目不是一个好主意,因此您的选项1已经淘汰。

Using a proxy model is a good option I think. 我认为使用代理模型是一个不错的选择。 You can connect all your QListViews to a proxy and all other views (eg the Pie chart) to the underlying source model. 您可以将所有QListViews连接到代理,将所有其他视图(例如饼图)连接到基础源模型。 The proxy model does then include the sections as items. 然后,代理模型会将这些节作为项目包括在内。 I think you need only one proxy since the sections will be the same for all list views. 我认为您只需要一个代理,因为所有列表视图的部分都是相同的。

Instead of subclassing from a QListView you could use a delegate to customize how the cells are rendered. 代替从QListView进行子类化,您可以使用委托来自定义单元格的呈现方式。 Take a look at the star delegate example from the Qt documentation. 看一下Qt文档中的星形委托示例

Option 4, writing your own view, should really be your last resort. 选择4,发表您自己的看法,实际上应该是您的不得已的选择。 You basically are rolling out your own version QListView. 基本上,您正在推出自己的版本QListView。 It is a lot of work and will never be as good as the Qt original. 这是很多工作,永远不会像Qt原始版本那样出色。

Hope this helps. 希望这可以帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM