Как иметь QTreeWidgetItems разной высоты в QTreeWidget, используя QStyledItemDelegate?

#c #qt #qwidget #qtreewidget #qtreewidgetitem

#c #qt #qwidget #qtreewidget #qtreewidgetitem

Вопрос:

ПРИМЕЧАНИЕ: оказалось, что проблема была связана не с реализацией 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 QModelIndexamp; 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 QStyleOptionViewItemamp; option, const QModelIndexamp; 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 .

Итак, мои текстовые параметры для вызова setSizeHint() в конструкторе MyCustomTreeWidgetItem1 , и это, похоже, тоже не имело никакого эффекта. Qt Игнорирует ли это, потому что есть делегат?

Другим вариантом было установить минимальную высоту a QWidget , которая содержится в MyCustomTreeWidgetItem , что стало возможным благодаря QTreeWidget::setItemWidget() .

Итак, похоже, что в тот момент, когда я использую делегат, я ограничен только размером. Есть ли у меня возможность избавиться от делегата или есть что-то еще, что я могу попробовать?

Я знаю, что многие люди сказали бы переключиться с a QTreeWidget на a QTreeView , но на данный момент это невозможно.

Ответ №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 amp;option,
                    const QModelIndex amp;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 amp;option,
                   const QModelIndex amp;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 amp;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();
}
  

Примечание: В этом примере размер элемента устанавливается в зависимости от его индекса — четного или нечетного. Не стесняйтесь изменять это, реализуя логику, необходимую для различения элементов.

Результат

Приведенный пример дает следующий результат:

Древовидный виджет с элементами разных размеров

Четные и нечетные элементы имеют разную высоту.

Комментарии:

1. Я смог разобраться в своей проблеме, проблема заключалась в том, что мое QTreeWidget uniformRowHeights свойство устанавливало значение true. Удаление этого решило проблему.

2. Однако ваше решение устраняет необходимость dynamic_cast , поэтому я поддержал его.

3. @ArmaniStyles, мое решение не только проще, но и является правильным способом иметь разные высоты элементов с помощью делегата. Если вы сообщите делегату о представлении, которое его использует, и используете его методы, как вы это делаете, это называется тесной связью и может вызвать проблемы на более позднем этапе вашего проекта. Поэтому я предлагаю вам использовать подход, который я описал. Вы могли бы также принять ответ, поскольку он дает правильное решение вопроса о достижении разных высот.

4. Я согласен с вами в том, что ваше решение проще, но я не согласен с частью жесткой связи, поскольку она применима только к вашему образцу. Дело в том, что у меня может быть 3 разных типа QTreeWidgetItem и достичь этого путем их подклассификации. Тогда у меня может быть такая логика, что мой подкласс QTreeWidget создает эти элементы. Это означает, что QTreeWidget они будут полностью знать об этих типах. Как только он полностью осознает, нет никакого вреда в вызове dynamic_cast того же исходного файла.

5. @ArmaniStyles, описанный вами вариант использования действительно интересен, и я был бы очень рад найти для него подходящее решение, если вы решите описать его более конкретно в другом сообщении.