How to remove QTreeView indentation
Asked Answered
T

4

6

I want to have a QTreeView without an indentation on the left side increasing at each nesting level. I tried setting QTreeView::setIndentation(0). It removes the indentations just as I want, however it also hides the tree arrows.


Default behavior:

  • With indentations ✗
  • With arrows ✔

Default behavior


After setIndentation(0):

  • Without indentations ✔
  • Without arrows ✗

After setIndentation(0)


Desired behavior:

  • Without indentations ✔
  • With arrows ✔

Desired behavior


So how can I achieve the result shown in the third example? Is there any standard way of doing it, or I will have to reimplement the QTreeView::paintEvent(), QTreeView::drawBranches(), etc.?

Territus answered 3/5, 2019 at 12:33 Comment(3)
You need to reimplement it...Temperament
Just personal opinion but... I think what you're trying to achieve could be very confusing for users. In the image labeled Desired behavior: how do I know if 1 is a subdirectory of Test or a subdirectory of C:?Brimful
@Brimful I agree, in this particular example it would very confusing for users. However, the actual tree structure will have more plain and static nesting. For example, imagine the grouping in property browsers.Territus
M
4

To solve the problem I used a delegate to translate the paint of the items, and paint the arrows.

#include <QtWidgets>

class BranchDelegate: public QStyledItemDelegate
{
public:
    using QStyledItemDelegate::QStyledItemDelegate;
    void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override{
        QStyleOptionViewItem opt(option);
        if(index.column() == 0)
            opt.rect.adjust(opt.rect.height(), 0, 0, 0);
        QStyledItemDelegate::paint(painter, opt, index);
        if(index.column() == 0){
            QStyleOptionViewItem branch;
            branch.rect = QRect(0, opt.rect.y(), opt.rect.height(), opt.rect.height());
            branch.state = option.state;
            const QWidget *widget = option.widget;
            QStyle *style = widget ? widget->style() : QApplication::style();
            style->drawPrimitive(QStyle::PE_IndicatorBranch, &branch, painter, widget);
        }
    }
};

class TreeView: public QTreeView
{
public:
    TreeView(QWidget *parent=nullptr):QTreeView(parent)
    {
        BranchDelegate *delegate = new BranchDelegate(this);
        setItemDelegate(delegate);
        setIndentation(0);
    }
protected:
    void mousePressEvent(QMouseEvent *event) override{
        QModelIndex index = indexAt(event->pos());
        bool last_state = isExpanded(index);
        QTreeView::mousePressEvent(event);
        if(index.isValid() && last_state == isExpanded(index))
            setExpanded(index, !last_state);
    }
};

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    TreeView w;
    QFileSystemModel model;
    model.setRootPath(QDir::rootPath());
    w.setModel(&model);
    w.setRootIndex(model.index(QDir::homePath()));
    /*for (int i = 1; i< model.columnCount() ; ++i) {
        w.hideColumn(i);
    }*/
    w.expandAll();
    w.resize(640, 480);
    w.show();
    return a.exec();
}
Maje answered 4/5, 2019 at 10:23 Comment(0)
D
1

ellyanesc's answer works, but is incorrect by one small detail in the line:

branch.rect = QRect(0, opt.rect.y(), opt.rect.height(), opt.rect.height());

The reason is because when the view is horizontally scrolled, option.rect.x() becomes negative. If branch.rect.x() is 0 (as in ellyanesc's answer), the branch indicator will always be shown, which also causes artifacts during scroll:

To solve this, replace the above line with:

branch.rect = QRect(option.rect.x(), opt.rect.y(), opt.rect.height(), opt.rect.height());

(I would have just pointed that out as a comment in ellyanesc's answer, but I don't have enough reputation for that.)

Dromous answered 26/9, 2022 at 15:31 Comment(0)
S
0

eyllanesc's falls appart if there is horizontal scrolling. Also usually the view only expands/collapses when clicking the branch-indicator, not the index.

My Solution: Only change the Rect of indices which have a parent but no children. Also dont set the indentation to 0. No need to subclass QTreeView.

#include <QtWidgets>
class BranchDelegate: public QStyledItemDelegate
{
public:
    mIndent = 50;
    using QStyledItemDelegate::QStyledItemDelegate;
    void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override
    {
        QStyleOptionViewItem opt(option);
        if(index.parent().isValid && (!index.model() || !index.model()->index(0, 0, index).isValid())) 
        { 
            opt.rect.adjust(-mIndent, 0, 0, 0); 
        }
        QStyledItemDelegate::paint(painter, opt, index);
    }
};

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    QTreeView apView;
    BranchDelegate* const apDelegate = new BranchDelegate(apView);
    apDelegate->mIndent = 50;
    apView->setIndentation(apDelegate->mIndent);
    apView->setItemDelegateForColumn(0, apDelegate);


    QFileSystemModel model;
    model.setRootPath(QDir::rootPath());
    apView.setModel(&model);
    apView.setRootIndex(model.index(QDir::homePath()));
    /*for (int i = 1; i< model.columnCount() ; ++i) {
        apView.hideColumn(i);
    }*/
    apView.expandAll();
    apView.resize(640, 480);
    apView.show();
    return a.exec();
}
Settles answered 23/11, 2021 at 9:37 Comment(0)
I
0

I came here with the same question and I think there's an easier solution that hasn't been mentioned yet, which is to add a button that mimics the tree expansion icon by toggling expansion on click.

Here's how I did it:

            frame = ClickableFrame() # an implementation of QFrame that allows clicking
            toggle_tree = QPushButton()
            spacer = QSpacerItem(5, 1, QSizePolicy.Fixed, QSizePolicy.Fixed)
            label = QLabel(column)

            frame.setLayout(QHBoxLayout())
            frame.setStyleSheet("background-color:  transparent;")

            frame.clicked.connect(
                lambda: self.setExpanded(not self.isExpanded())
            )
            toggle_tree.clicked.connect(
                lambda: self.setExpanded(not self.isExpanded())
            )

            frame.layout().addWidget(toggle_tree)
            frame.layout().addItem(spacer)
            frame.layout().addWidget(label)
            frame.layout().addItem(QSpacerItem(
                5, 1, QSizePolicy.Fixed, QSizePolicy.Fixed
            ))
            self.treeWidget().setItemWidget(
                self, 0, frame
            )
Interscholastic answered 24/10, 2023 at 21:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.