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

How to have QTreeWidgetItems of different heights in a QTreeWidget utilizing QStyledItemDelegate?

注意:事实证明问题不是由于 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
     };
    
  2. 创建项目时,根据其类型在其用户数据中设置所需的大小,例如:

     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;
     }
    
  3. 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();
}

注意:此示例根据其索引(奇数或偶数)设置项目的大小。通过实施您需要区分项目的逻辑来随意更改此设置。

结果

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

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