c++ - 当给定索引处的列尚未创建时,setSectionResizeMode() 崩溃

标签 c++ qt qt5 qtreeview qtreewidget

我有一个包含两列的 QTreeView 对象。我希望第一个能够拉伸(stretch),第二个具有固定宽度。

This question有一个答案,我试图将其应用于我的案例。我将这些设置为我的树头:

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

这样我的程序就崩溃了。我想问题是当我调用 setSectionResizeMode() 时,该列不存在。文档摘录:

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

Sets the constraints on how the section specified by logicalIndex in the header can be resized to those described by the given mode. The logical index should exist at the time this function is called.

现在,当我将这些属性设置为 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()

附言这是我的代码:

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

};

树模型.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;
    }
};

最佳答案

由于列数是通过模型处理的,因此必须在 QTreeView 中设置表头,即表头使用在 View 中建立的模型:

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

关于c++ - 当给定索引处的列尚未创建时,setSectionResizeMode() 崩溃,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54270794/

相关文章:

c++ - 施放 ssize_t 或 size_t

c++ - 确保给定图像与另一图像翻转

c++ - 双击QWidget

c++ - 这是否完美地模仿了函数模板特化?

c++ - 如何将使用 malloc() 的 C 语句转换为 C++?

c++ - Clang 不为 x86 以外的平台编译代码

c++ - 在 Windows 10 上的 Qt5 中围绕屏幕区域绘制高亮边框

c++ - QTabWidget - 如何将 "include"指向每个选项卡的指针?

c++ - 在 QChart 上画一个点

qt - 如何获取系统的默认浏览器?