带有自定义项和自定义项小部件的QListView/QListWidget

37
我正在编写一款PyQt应用程序,但在创建自定义列表视图方面遇到了一些问题。我希望该列表包含任意小部件(特别是一个自定义小部件)。请问我该如何做?
似乎另一种选择是创建包含滚动条的表格或网格视图。然而,我想要利用模型/视图方法以及内置处理嵌套(树形视图)的支持。
澄清一下,自定义小部件是交互式的(包含按钮),因此解决方案需要更多的工作而不仅仅是绘制一个小部件。
5个回答

32

我认为你需要创建QItemDelegate子类。

QItemDelegate可用于为基于QAbstractItemView子类的项视图提供自定义显示功能和编辑器小部件。使用代理实现此目的允许自定义和开发显示和编辑机制,而与模型和视图独立。

这段代码来自Qt示例中的Torrent应用程序。

class TorrentViewDelegate : public QItemDelegate
{
    Q_OBJECT
public:
    inline TorrentViewDelegate(MainWindow *mainWindow) : QItemDelegate(mainWindow) {}

    inline void paint(QPainter *painter, const QStyleOptionViewItem &option,
                      const QModelIndex &index ) const
    {
        if (index.column() != 2) {
            QItemDelegate::paint(painter, option, index);
            return;
        }

        // Set up a QStyleOptionProgressBar to precisely mimic the
        // environment of a progress bar.
        QStyleOptionProgressBar progressBarOption;
        progressBarOption.state = QStyle::State_Enabled;
        progressBarOption.direction = QApplication::layoutDirection();
        progressBarOption.rect = option.rect;
        progressBarOption.fontMetrics = QApplication::fontMetrics();
        progressBarOption.minimum = 0;
        progressBarOption.maximum = 100;
        progressBarOption.textAlignment = Qt::AlignCenter;
        progressBarOption.textVisible = true;

        // Set the progress and text values of the style option.
        int progress = qobject_cast<MainWindow *>(parent())->clientForRow(index.row())->progress();
        progressBarOption.progress = progress < 0 ? 0 : progress;
        progressBarOption.text = QString().sprintf("%d%%", progressBarOption.progress);

        // Draw the progress bar onto the view.
        QApplication::style()->drawControl(QStyle::CE_ProgressBar, &progressBarOption, painter);
    }
};

基本上,您可以看到它检查要绘制的列是否为特定索引,如果是,则绘制进度条。我认为您可以进行一些调整,而不是使用QStyleOption,您可以使用自己的小部件。
编辑:不要忘记使用setItemDelegate设置您的项目委托与您的QListView配合使用。
在调查您的问题时,我偶然发现了this线程,它详细说明了如何使用QItemDelegate绘制自定义小部件,我相信它包含了您可能需要的所有信息。

谢谢您的帖子,我明天会更深入地研究它。您能详细说明一下如何使用自定义小部件替换QStyleOption绘图吗? - Daniel Naab
好的,这只是在QItemDelegate的paint方法中使用提供的QPainter来绘制小部件的问题。我已经编辑了我的帖子,包括一个有用的链接。 - Idan K
好的,我可以使用这种技术得到一些粗略的工作成果。但问题在于“任意小部件”的方面——我有一个包含按钮等交互元素的复合小部件,因此它们需要被实际放置在列表中,否则用户无法点击它们。如果我错了,请纠正我,但这只会绘制一个不活动的小部件。 - Daniel Naab
那是正确的。但这就是为什么你有“编辑模式”,那时列表应该是互动的,而不是显示静态数据。 - Idan K
5
我并不需要为自己辩护,但我有理由希望以这种方式进行。这些控件并非用于编辑数据,而是基于数据生成操作。许多良好的用户界面都是这样做的...比如打开浏览器中的“下载”窗口。 - Daniel Naab
构造函数不应该接受并传递一个 QObject 吗? - Sebastian Mach

22

如果我正确理解您的问题,您想要类似于以下内容:

enter image description here

每行都包含自定义小部件。

为实现此目标,需要两个步骤。

使用自定义小部件实现行

首先,实现一个自定义小部件,其中包含每个列表行所需的所有小部件。

这里我使用每行一个标签和两个按钮,水平布局。

class MyCustomWidget(QWidget):
    def __init__(self, name, parent=None):
        super(MyCustomWidget, self).__init__(parent)

        self.row = QHBoxLayout()

        self.row.addWidget(QLabel(name))
        self.row.addWidget(QPushButton("view"))
        self.row.addWidget(QPushButton("select"))

        self.setLayout(self.row)

添加行到列表

实例化多行只需创建小部件项,并将自定义小部件关联到该项的行即可。

# Create the list
mylist = QListWidget()

# Add to list a new item (item is simply an entry in your list)
item = QListWidgetItem(mylist)
mylist.addItem(item)

# Instanciate a custom widget 
row = MyCustomWidget()
item.setSizeHint(row.minimumSizeHint())

# Associate the custom widget to the list entry
mylist.setItemWidget(item, row)

如果我设置Movement.Free并尝试重新排列项目,这将会出现错误。 - Danon

5
@Idan的答案很好,但我会发一个更简单的例子。这个item代理只为每个item绘制一个黑色矩形。
class ItemDelegate : public QItemDelegate
{
public:
    explicit ItemDelegate(QObject *parent = 0) : QItemDelegate(parent) {}
    void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
    {
        painter->fillRect(option.rect.adjusted(1, 1, -1, -1), Qt::SolidPattern);
    }
};

然后您只需为列表小部件设置它即可:

ui->listWidget->setItemDelegate(new ItemDelegate(ui->listWidget));

2

助手说:

void QTableWidget::setCellWidget (int row, int column, QWidget * widget)  

将给定的小部件设置为显示在给定行和列中的单元格中,并将小部件的所有权传递给表格。如果单元格小部件A被替换为单元格小部件B,则单元格小部件A将被删除。
大多数QAbstractItemView后代都有类似的方法。
只有当您希望编辑器小部件仅在按下EditTrigger时出现在视图中,然后消失并让委托以某种方式呈现视图项时,您才需要子类化Q *** Delegate。
如果我理解正确,您想要在项目视图中始终显示控件,并能够在不需要进入编辑模式并等待委托创建编辑器并设置其状态的情况下点击控件,因此您不需要制作特定的委托,只需将小部件设置为视图的项目即可。

不幸的是,这些功能仅适用于 QTableWidgetQTreeWidgetQListWidget。在使用具有自定义模型的视图时,它们是不可用的。 - Thorbjørn Lindeijer
1
文档中可以看到:“此函数仅应用于在列表小部件项的位置显示静态内容。如果您想要显示自定义动态内容或实现自定义编辑器小部件,请使用QListView并子类化QItemDelegate。” - Trilarion

1

另一种在listWidget中添加自定义项的方法。为此,您需要添加一个新类。将其命名为您想要的名称,我只是将其命名为“myList”,希望您知道如何在项目中添加新项。 :)

然后在这个新框架中,按照您想要的方式添加控件。例如QLabels、QlineEdit等

然后在主类中,您需要添加一个名为“list”或其他您想要的名称的listWidget,并编写以下代码

MyList *myList = new MyList();
QListWidgetItem *item = new QListWidgetItem();
ui->list->insertItem(ui->list->size().height(),item);
item->setSizeHint(QSize(50,30));
ui->list->setItemWidget(item,myList);

后者,您还可以使用信号/槽更改项目属性。

希望这能帮到您..!


2
谁在项目销毁时删除了 myList?我在这个示例中看不到任何与所有权相关的代码。 - Allex
我是Java开发人员,JAVA负责垃圾回收 :) 这就是我对C++的一点了解。 - Muhammad Suleman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接