Search code examples
qtqt5qtreeview

QTreeView: resizeRowsToContents equivalent or how to word-wrap text in rows


I have a QTreeView with more than one column (like a table). Columns in the tree have a fixed size. I need to resize row heights and use multi-lined text like QTableView::resizeRowsToContents. How can I do this?

I've tried using a custom QStyledItemDelegate with reimplemented sizeHint, but I don't know how to calculate multiline text block height with known width.


Solution

  • Using QStyledItemDelegate is the right approach. In your sizeHint function, you can use the style options text with the QFontMetrics class:

    QSize MyItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override {
        QSize baseSize = this->QStyledItemDelegate::sizeHint(option, index);
        baseSize.setHeight(10000);//something very high, or the maximum height of your text block
    
        QFontMetrics metrics(option.font);
        QRect outRect = metrics.boundingRect(QRect(QPoint(0, 0), baseSize), Qt::AlignLeft, option.text);
        baseSize.setHeight(outRect.height());
        return baseSize;
    }
    

    Note: Right now I cannot test this, but it should work. You may have to tweak the call to metrics.boundingRect if the output does not fit your needs

    EDIT:
    It seems the sizeHint will be only called once to create the initial layout, but not after resizing the columns.

    A final Idea could be to override the QAbstractItemModel::data function to return the desired size using Qt::SizeHintRole. You could either add it to your existing model or provide a proxy-model to do this:

    QSize MyModel::data(const QModelIndex &index, int role) const override {
        switch(role) {
            //...
        case Qt::SizeHintRole:
        {
            QSize baseSize(getFixedWidth(index.column()), baseSize.setHeight(10000));//something very high, or the maximum height of your text block
    
            QFontMetrics metrics(this->data(index, Qt::FontRole).value<QFont>());
            QRect outRect = metrics.boundingRect(QRect(QPoint(0, 0), baseSize), Qt::AlignLeft, this->data(index, Qt::DisplayRole)));
            baseSize.setHeight(outRect.height());
            return baseSize;
        }
            //...
        }
    }
    

    Important: Everytime your view gets resized, you will have to emit the dataChanged signal for all those items. getFixedWidth is something you will have to implement to return the current width of the given column.