如何使用 QStyledItemDelegate 在 QTreeWidget 中拥有不同高度的 QTreeWidgetItems?

Posted

技术标签:

【中文标题】如何使用 QStyledItemDelegate 在 QTreeWidget 中拥有不同高度的 QTreeWidgetItems?【英文标题】:How to have QTreeWidgetItems of different heights in a QTreeWidget utilizing QStyledItemDelegate? 【发布时间】:2021-02-12 10:20:24 【问题描述】:

注意:原来问题不是由于QStyledItemDelegate的实现,而是在MyTreeWidget的构造函数中我调用了setUniformRowHeights(true)。下面的代码和@scopchanov 发布的解决方案有效且有效

QTreeWidget 有一个名为 itemFromIndex() 的受保护方法,这就是我使其可访问的方式:

class MyTreeWidget : public QTreeWidget 
    Q_OBJECT
public:
    MyTreeWidget(QWidget *parent) : QTreeWidget(parent) 
        setItemDelegate(new MyItemDelegate(this));
    

    QTreeWidgetItem treeWidgetItemFromIndex(const QModelIndex& index) 
        return itemFromIndex(index);
    

在我的QStyledItemDelegate 中,我存储了一个指向MyTreeWidget 的指针,然后覆盖它的虚拟sizeHint() 方法并根据QTreeWidgetItem 的类型添加一个填充。

class MyItemDelegate : public QStyledItemDelegate

    Q_OBJECT
public:
    MyItemDelegate(QObject *parent) : QStyledItemDelegate(parent) 
        _myTreeWidget = dynamic_cast<MyTreeWidget*>(parent);
    

    QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const 
        auto treeWidgetItem = _myTreeWidget->treeWidgetItemFromIndex(index);
        QSize padding;
        if (dynamic_cast<MyCustomTreeWidgetItem1*>(treeWidgetItem) 
            padding = 0, 5;
         else if (dynamic_cast<MyCustomTreeWidgetItem2*>(treeWidgetItem) 
            padding = 0, 10;
        

        return QStyledItemDelegate::sizeHint(option, index) + padding;
    

这不起作用,因为代理的sizeHint() 不会被每个QTreeWidgetItem 调用。

所以我在MyCustomTreeWidgetItem1 的构造函数中调用setSizeHint() 的文本选项似乎也没有任何效果。 Qt 是否因为有委托而忽略它?

另一个选项是设置包含在MyCustomTreeWidgetItem 中的QWidget 的最小高度,这可以通过QTreeWidget::setItemWidget() 实现。

所以看起来在我使用委托的那一刻,我只限于大小。我可以选择摆脱委托还是可以尝试其他方法?

我知道很多人会说从QTreeWidget 切换到QTreeView,但目前不可能。

【问题讨论】:

【参考方案1】:

解决方案

我会以不同(更简单)的方式解决这个问题:

    为不同的项目大小定义一个枚举,例如:

     enum ItemType : int 
         IT_ItemWithRegularPadding,
         IT_ItemWithBigPadding
     ;
    

    创建项目时,根据其类型在其用户数据中设置所需的大小,例如:

     switch (type) 
     case IT_ItemWithRegularPadding:
         item->setData(0, Qt::UserRole, QSize(0, 5));
         break;
     case IT_ItemWithBigPadding:
         item->setData(0, Qt::UserRole, QSize(0, 10));
         break;
     
    

    sizeHint 的重新实现中,从索引的数据中检索所需的大小,例如:

     QSize sizeHint(const QStyleOptionViewItem &option,
                    const QModelIndex &index) const override 
         return QStyledItemDelegate::sizeHint(option, index)
                 + index.data(Qt::UserRole).toSize();
     
    

示例

这是我为您编写的示例,用于演示如何实施建议的解决方案:

#include <QApplication>
#include <QStyledItemDelegate>
#include <QTreeWidget>
#include <QBoxLayout>

class Delegate : public QStyledItemDelegate

public:
    explicit Delegate(QObject *parent = nullptr) :
        QStyledItemDelegate(parent)
    

    QSize sizeHint(const QStyleOptionViewItem &option,
                   const QModelIndex &index) const override 
        return QStyledItemDelegate::sizeHint(option, index)
                + index.data(Qt::UserRole).toSize();
    
;

class MainWindow : public QWidget

public:
    enum ItemType : int 
        IT_ItemWithRegularPadding,
        IT_ItemWithBigPadding
    ;

    MainWindow(QWidget *parent = nullptr) :
        QWidget(parent) 
        auto *l = new QVBoxLayout(this);
        auto *treeWidget = new QTreeWidget(this);
        QList<QTreeWidgetItem *> items;

        for (int i = 0; i < 10; ++i)
            items.append(createItem(QString("item: %1").arg(i),
                                    0.5*i == i/2 ? IT_ItemWithRegularPadding
                                                 : IT_ItemWithBigPadding));

        treeWidget->setColumnCount(1);
        treeWidget->setItemDelegate(new Delegate(this));
        treeWidget->insertTopLevelItems(0, items);

        l->addWidget(treeWidget);

        resize(300, 400);
        setWindowTitle(tr("Different Sizes"));
    

private:
    QTreeWidgetItem *createItem(const QString &text, int type) 
        auto *item = new QTreeWidgetItem(QStringList(text));

        switch (type) 
        case IT_ItemWithRegularPadding:
            item->setData(0, Qt::UserRole, QSize(0, 5));
            break;
        case IT_ItemWithBigPadding:
            item->setData(0, Qt::UserRole, QSize(0, 10));
            break;
        

        return item;
    
;

int main(int argc, char *argv[])

    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();

注意:此示例根据其索引设置项目的大小 - 奇数或偶数。随意通过实施区分项目所需的逻辑来改变这一点。

结果

给定的示例产生以下结果:

偶数项和奇数项的高度不同。

【讨论】:

我能够深入了解我的问题,问题是我的QTreeWidgetuniformRowHeights 属性设置为true。把它取下来解决了这个问题。 您的解决方案消除了对dynamic_cast 的需求,所以我赞成它。 @ArmaniStyles,我的解决方案不仅更简单,而且是通过使用委托来获得不同项目高度的正确方法。如果您让代理知道使用它的视图并使用它的方法,就像您所做的那样,这称为紧密耦合,并且会在项目的后期阶段给您带来麻烦。所以我建议你使用我描述的方法。你不妨接受这个答案,因为它为关于达到不同高度的问题提供了一个有效的解决方案。 我同意你的观点,你的解决方案更简单,但我不同意紧耦合部分,因为它只适用于你的样本。事实是我可能有 3 种不同类型的 QTreeWidgetItem 并通过对它们进行子类化来实现。然后我可能有这样的逻辑,即我的QTreeWidget 的子类正在创建这些项目。这意味着QTreeWidget 将完全了解这些类型。一旦它完全意识到,在同一个源文件中调用dynamic_cast 是没有害处的。 @ArmaniStyles,你描述的用例确实很有趣,如果你决定在另一篇文章中更具体地描述它,我很乐意为它寻找合适的解决方案。

以上是关于如何使用 QStyledItemDelegate 在 QTreeWidget 中拥有不同高度的 QTreeWidgetItems?的主要内容,如果未能解决你的问题,请参考以下文章

QStyledItemDelegate中如何根据qStylesheet进行绘制

在带有 QSqlQueryModel 的 QListView 上使用 QStyledItemDelegate

重新实现 QStyledItemDelegate::paint - 如何获取子元素坐标?

如何在 QStyledItemDelegate 中绘制整行的背景?

Qt之QStyledItemDelegate类

Qt表格中的自定义编辑组件---------------自定义代理QStyledItemDelegate