[英]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.
到目前为止,这是我考虑过的内容。
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_()
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.
在这种情况下,视图不需要模型包含这些添加的成员,而是根据现有数据绘制它们。
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将多余的项目添加到视图的底部,但是我一直在努力理解:
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.