【问题标题】:How to have QTreeWidgetItems of different heights in a QTreeWidget utilizing QStyledItemDelegate?如何使用 QStyledItemDelegate 在 QTreeWidget 中拥有不同高度的 QTreeWidgetItems?
【发布时间】: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,但目前不可能。

【问题讨论】:

    标签: c++ qt qwidget qtreewidget qtreewidgetitem


    【解决方案1】:

    解决方案

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

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

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

    结果

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

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

    【讨论】:

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