setSectionResizeMode () падает, когда столбец с данным индексом еще не создан - PullRequest
0 голосов
/ 19 января 2019

У меня есть QTreeView объект с двумя столбцами. Я хочу, чтобы первый мог растягиваться, а второй иметь фиксированную ширину.

На этот вопрос есть ответ, который я пытался применить к своему делу. Я установил их в заголовок моего дерева:

header->setSectionResizeMode(0, QHeaderView::Stretch);
header->setSectionResizeMode(1, QHeaderView::Fixed);
header->setStretchLastSection(false);

Таким образом, моя программа падает. Я думаю, проблема в том, что когда я звоню setSectionResizeMode(), столбец не существует. Выдержка из документов:

void QHeaderView :: setSectionResizeMode (int logicIndex, QHeaderView :: ResizeMode mode)

Устанавливает ограничения на то, каким образом раздел, указанный логическим индексом в заголовке, может быть изменен до размера, описанного данным режимом. Логический индекс должен существовать во время вызова этой функции.

Теперь, когда я устанавливаю эти свойства для объекта QTreeWidget, все в порядке:

#include <QApplication>
#include <QTreeWidget>
#include <QHeaderView>

int main(int argc, char **argv)
{
    QApplication app(argc, argv);

    QTreeWidget treeWidget;
    treeWidget.setColumnCount(2);

    QList<QTreeWidgetItem *> items;
    for (int i = 0; i < 5; ++i)
        items.append(new QTreeWidgetItem(&treeWidget,
            QStringList{ "item", QString("%1").arg(i) }));
    treeWidget.insertTopLevelItems(0, items);

    auto header = treeWidget.header();
    header->setSectionResizeMode(0, QHeaderView::Stretch);
    header->setSectionResizeMode(1, QHeaderView::Fixed);
    header->setStretchLastSection(false);
    header->setDefaultSectionSize(50);

    treeWidget.show();

    return QApplication::exec();
}

В чем проблема с QTreeView? Когда мне следует звонить setSectionResizeMode(), когда я его использую?

P.S. вот мой код:

main.cpp

#include <QApplication>
#include <QTreeWidget>
#include "TreeModel.h"
#include "TreeItem.h"

int main(int argc, char **argv)
{
    QApplication app(argc, argv);

    TreeModel model;
    NodeDelegate delegate;

    QTreeView treeView;
    treeView.setModel(&model);
    treeView.setItemDelegate(&delegate);

    TreeHeader header(Qt::Horizontal, &treeView);
    header.setStretchLastSection(false);
    header.setSectionResizeMode(0, QHeaderView::Stretch); // <--- crashes, column 0 doesn't exist yet
    header.setSectionResizeMode(1, QHeaderView::Fixed); // <--- crashes, column 1 doesn't exist yet
    header.setDefaultSectionSize(50);

    treeView.setHeader(&header);
    treeView.show();

    return QApplication::exec();
}

treeitem.h

#pragma once

#include <QVariant>
#include <QVector>
#include <QStyleOptionViewItem>
#include <QItemDelegate>
#include <QPainter>

class TreeItem
{
public:
    TreeItem(QVector<QVariant> data, TreeItem *parent = nullptr)
        : m_itemData(std::move(data))
        , m_parentItem(parent)
    {
    }

    ~TreeItem()
    {
        qDeleteAll(m_childItems);
    }

    TreeItem *child(const int row) const
    {
        return m_childItems.value(row);
    }

    int childCount() const
    {
        return m_childItems.count();
    }

    int row() const
    {
        if (m_parentItem)
            return m_parentItem->m_childItems.indexOf(const_cast<TreeItem *>(this));

        return 0;
    }

    int columnCount() const
    {
        return m_itemData.count();
    }

    QVariant data(const int column) const
    {
        return m_itemData.value(column);
    }

    TreeItem *parentItem() const
    {
        return m_parentItem;
    }

    bool setData(const int column, const QVariant &value)
    {
        return column >= 0 && column < m_itemData.size() && (m_itemData[column] = value, true);
    }

    bool insertRows(const int position, const int count, const int columns)
    {
        if (position < 0 || position > m_childItems.size())
            return false;

        for (auto row = 0; row < count; ++row)
        {
            // create a new row with columns number of columns
            const QVector<QVariant> newData(columns);
            m_childItems.insert(position, new TreeItem{ newData, this });
        }

        return true;
    }

    bool removeRows(const int position, const int count)
    {
        if (position < 0 || position > m_itemData.size())
            return false;

        for (auto row = 0; row < count; ++row)
            delete m_childItems.takeAt(position);

        return true;
    }

    bool insertColumns(const int position, const int columns)
    {
        if (position < 0 || position > m_childItems.size())
            return false;

        for (auto column = 0; column < columns; ++column)
            m_itemData.insert(position, QVariant{});

        for (auto child : m_childItems)
            child->insertColumns(position, columns);

        return true;
    }

    bool removeColumns(const int position, const int columns)
    {
        if (position < 0 || position > m_childItems.size())
            return false;

        for (auto column = 0; column < columns; ++column)
            m_itemData.remove(position);

        for (auto child : m_childItems)
            child->removeColumns(position, columns);

        return true;
    }

    bool paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
    {
        QRect rect = option.rect;
        painter->drawText(rect, "data");
        return true;
    }

private:
    QList<TreeItem *> m_childItems;
    QVector<QVariant> m_itemData;
    TreeItem *m_parentItem = nullptr;
};

class NodeDelegate : public QItemDelegate
{
public:
    NodeDelegate(QObject *parent = nullptr)
        : QItemDelegate(parent)
    {
    }

    void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override
    {
        if (!index.isValid()) return;

        auto node = static_cast<TreeItem *>(index.internalPointer());
        if (!node->paint(painter, option, index))
            QItemDelegate::paint(painter, option, index);
    }

};

treemodel.h

#pragma once

#include <QAbstractItemModel>
#include <QHeaderView>
#include "TreeItem.h"

/*
 * A read-only tree model
 */
class TreeModel final : public QAbstractItemModel
{
    Q_OBJECT
public:
    TreeModel(QObject *parent = nullptr)
        : QAbstractItemModel(parent)
    {
        const auto rootData = QVector<QVariant>{} << "Number" << "Data";
        m_pRootItem = new TreeItem{ rootData };
    }

    QModelIndex index(const int row, const int column, const QModelIndex &parent /* = QModelIndex() */) const
    {
        return QModelIndex{};
    }

    QModelIndex parent(const QModelIndex &index) const
    {
        if (!index.isValid())
            return QModelIndex{};

        const auto childItem = getItem(index);
        const auto parentItem = childItem->parentItem();

        // don't return a model index corresponding to the root item
        if (parentItem == m_pRootItem)
            return QModelIndex{};

        return createIndex(parentItem->row(), 0, parentItem);
    }

    int rowCount(const QModelIndex &parent /* = QModelIndex() */) const
    {
        const auto parentItem = getItem(parent);
        return parentItem->childCount();
    }

    int columnCount(const QModelIndex &parent /* = QModelIndex() */) const
    {
        // all items have the same number of columns
        return m_pRootItem->columnCount();
    }

    QVariant data(const QModelIndex &index, const int role /* = Qt::DisplayRole */) const
    {
        if (!index.isValid())
            return QVariant{};

        if (role != Qt::DisplayRole)
            return QVariant{};

        const auto item = static_cast<TreeItem *>(index.internalPointer());
        return item->data(index.column());
    }

    Qt::ItemFlags flags(const QModelIndex &index) const
    {
        if (!index.isValid())
            return Qt::NoItemFlags;

        return QAbstractItemModel::flags(index) | Qt::ItemIsEditable;
    }

    QVariant headerData(const int section, const Qt::Orientation orientation, const int role /* = Qt::DisplayRole */) const
    {
        if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
            return m_pRootItem->data(section);

        return QVariant{};
    }

    bool setData(const QModelIndex &index, const QVariant &value, int role /* = Qt::EditRole */)
    {
        if (role != Qt::EditRole)
            return false;

        auto item = getItem(index);
        const auto result = item->setData(index.column(), value);
        if (result)
            emit dataChanged(index, index);

        return result;
    }

    bool setHeaderData(int section, Qt::Orientation orientation,
        const QVariant &value, int role)
    {
        if (role != Qt::EditRole || orientation != Qt::Horizontal)
            return false;

        const auto result = m_pRootItem->setData(section, value);
        if (result)
            emit headerDataChanged(orientation, section, section);

        return result;
    }

    TreeItem *getItem(const QModelIndex &index) const
    {
        if (index.isValid())
        {
            if (const auto item = static_cast<TreeItem *>(index.internalPointer()))
                return item;
        }

        return m_pRootItem;
    }


    TreeItem *m_pRootItem{};
};

class TreeHeader : public QHeaderView
{
public:
    TreeHeader(Qt::Orientation orientation, QWidget *parent = nullptr)
        : QHeaderView(orientation, parent)
    {
    }

protected:
    void paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const override
    {
        if (!rect.isValid()) return;

        QStyleOptionHeader option;
        option.initFrom(this);

        option.text = logicalIndex == 0 ? "Header text" : "#";
        if (logicalIndex == 1)
            option.textAlignment = Qt::AlignLeft;
    }
};

1 Ответ

0 голосов
/ 21 января 2019

Вы должны установить заголовок в QTreeView, так как количество столбцов обрабатывается моделью, то есть заголовок использует модель, которая была установлена ​​в представлении:

TreeHeader header(Qt::Horizontal, &treeView);
treeView.setHeader(&header); // <---
header.setStretchLastSection(false);
// ...
...