本页内容

QRangeModel Class

QRangeModel 为任何 C++ 范围实现了 `QAbstractItemModel ` 接口。更多内容...

头文件: #include <QRangeModel>
CMake: find_package(Qt6 REQUIRED COMPONENTS Core)
target_link_libraries(mytarget PRIVATE Qt6::Core)
qmake: QT += core
自: Qt 6.10
继承自: QAbstractItemModel

注:本类中的所有函数均为可重入函数。

公共类型

(since 6.11) struct ItemAccess
(since 6.10) struct RowOptions
(since 6.11) enum class AutoConnectPolicy { None, Full, OnRead }
(since 6.12) enum class DropOperation { DontDrop, Automatic, OverwriteAndIgnore, OverwriteAndExtend, InsertAsSiblings, InsertAsChildren }
enum class RowCategory { Default, MultiRoleItem }

属性

公共函数

QRangeModel(Range &&range, QObject *parent = nullptr)
QRangeModel(Range &&range, Protocol &&protocol, QObject *parent = nullptr)
virtual ~QRangeModel() override
QRangeModel::AutoConnectPolicy autoConnectPolicy() const
void resetRoleNames()
void resetSortCollator()
void resetSortRole()
void resetSupportedDragActions()
void resetSupportedDropActions()
void setAutoConnectPolicy(QRangeModel::AutoConnectPolicy policy)
void setRoleNames(const QHash<int, QByteArray> &names)
void setSortCollator(const QCollator &collator)
void setSortRole(int role)
void setSupportedDragActions(Qt::DropActions actions)
void setSupportedDropActions(Qt::DropActions actions)
QCollator sortCollator() const
int sortRole() const

重新实现的公共函数

virtual QModelIndex buddy(const QModelIndex &index) const override
virtual bool canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) const override
virtual bool canFetchMore(const QModelIndex &parent) const override
virtual bool clearItemData(const QModelIndex &index) override
virtual int columnCount(const QModelIndex &parent = {}) const override
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override
virtual bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override
virtual void fetchMore(const QModelIndex &parent) override
virtual Qt::ItemFlags flags(const QModelIndex &index) const override
virtual bool hasChildren(const QModelIndex &parent = QModelIndex()) const override
virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override
virtual QModelIndex index(int row, int column, const QModelIndex &parent = {}) const override
virtual bool insertColumns(int column, int count, const QModelIndex &parent = {}) override
virtual bool insertRows(int row, int count, const QModelIndex &parent = {}) override
virtual QMap<int, QVariant> itemData(const QModelIndex &index) const override
virtual QModelIndexList match(const QModelIndex &start, int role, const QVariant &value, int hits, Qt::MatchFlags flags) const override
virtual QMimeData *mimeData(const QModelIndexList &indexes) const override
virtual QStringList mimeTypes() const override
virtual bool moveColumns(const QModelIndex &sourceParent, int sourceColumn, int count, const QModelIndex &destinationParent, int destinationColumn) override
virtual bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationRow) override
virtual void multiData(const QModelIndex &index, QModelRoleDataSpan roleDataSpan) const override
virtual QModelIndex parent(const QModelIndex &child) const override
virtual bool removeColumns(int column, int count, const QModelIndex &parent = {}) override
virtual bool removeRows(int row, int count, const QModelIndex &parent = {}) override
virtual QHash<int, QByteArray> roleNames() const override
virtual int rowCount(const QModelIndex &parent = {}) const override
virtual bool setData(const QModelIndex &index, const QVariant &data, int role = Qt::EditRole) override
virtual bool setHeaderData(int section, Qt::Orientation orientation, const QVariant &data, int role = Qt::EditRole) override
virtual bool setItemData(const QModelIndex &index, const QMap<int, QVariant> &data) override
virtual QModelIndex sibling(int row, int column, const QModelIndex &index) const override
virtual void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override
virtual QSize span(const QModelIndex &index) const override
virtual Qt::DropActions supportedDragActions() const override
virtual Qt::DropActions supportedDropActions() const override

信号

void autoConnectPolicyChanged(QRangeModel::AutoConnectPolicy policy)
void roleNamesChanged()
void sortCollatorChanged(const QCollator &collator)
void sortRoleChanged(int role)
void supportedDragActionsChanged(Qt::DropActions actions)
void supportedDropActionsChanged(Qt::DropActions actions)

重新实现的受保护函数

virtual bool event(QEvent *event) override
virtual bool eventFilter(QObject *object, QEvent *event) override

受保护的插槽

virtual void resetInternalData() override

详细说明

QRangeModel 可以将任何可顺序迭代的 C++ 类型中的数据提供给 Qt 的模型/视图框架。这使得在Qt Widgets 和Qt Quick 的项视图中轻松显示现有数据结构成为可能,并允许应用程序的用户通过图形用户界面操作数据。

要使用 QRangeModel,请使用 C++ 范围对其进行实例化,并将其设置为一个或多个视图的模型:

std::array<int, 5> numbers = {1, 2, 3, 4, 5};
QRangeModel model(numbers);
listView.setModel(&model);

构建模型

该范围可以是任何已实现标准方法std::begin 和std::end 的 C++ 类型,且其返回的迭代器类型需满足std::forward_iterator 。如果支持std::size ,且迭代器满足std::random_access_iterator ,则某些模型操作的性能会更佳。

在构建模型时必须提供范围,该范围可以通过值、引用包装器或指针提供。模型的构建方式决定了通过模型 API 进行的更改是否会修改原始数据。使用QRangeModelAdapter 可隐式构建模型,同时还能以范围的形式直接、类型安全且便捷地访问该模型。

当按值构建时,模型会复制该范围,而用于修改模型的QAbstractItemModel API(例如setData() 或insertRows())不会对原始范围产生影响。

QRangeModel model(numbers);

可以通过连接到模型发出的信号(例如dataChanged() )来监视对数据所做的更改。

若要使对模型的修改影响原始范围,请通过指针提供该范围:

QRangeModel model(&numbers);

或通过引用封装器:

QRangeModel model(std::ref(numbers));

在这种情况下,QAbstractItemModel 中用于修改模型的 API 也会同时修改该范围。那些修改范围结构的方法(例如insertRows() 或removeColumns()),除了通过解引用可变迭代器来设置或清空数据外,还会使用标准的 C++ 容器 APIresize() 、insert() 、erase() 。

注意:一旦 模型构建完成并传递给视图,模型所操作的范围就不得再被直接修改。模型的视图将无法获知这些更改,且结构上的更改很可能导致模型维护的 `QPersistentModelIndex ` 实例损坏。请使用 `QRangeModelAdapter ` 来安全地与底层范围交互,同时保持模型的更新。

调用方必须确保该范围的生命周期长于模型的生命周期。

请使用智能指针,以确保只有在所有客户端都已完成对该范围的使用后,该范围才会被删除。

auto shared_numbers = std::make_shared<std::vector<int>>(numbers);
QRangeModel model(shared_numbers);

QRangeModel 同时支持共享指针和唯一指针。

只读或可变

对于 const 对象范围(其访问始终返回常量值)或无法使用所需容器 API 的范围,QRangeModel 实现了写入访问 API,该 API 不执行任何操作并返回false 。在使用std::array 的示例中,由于 C++ 数组中的条目数量是固定的,因此模型无法添加或删除行。 但可以通过setData() 修改值,用户还可以在列表视图中触发对值的编辑。通过将数组设为 const,其值也会变为只读。

const std::array<int, 5> numbers = {1, 2, 3, 4, 5};

如果元素类型为 const,则这些值也是只读的,例如在

std::array<const int, 5> numbers = {1, 2, 3, 4, 5};

在上述使用 `std::vector` 的示例中,模型可以添加或删除行,数据也可以被修改。若将范围作为常量引用传递,则模型将变为只读。

QRangeModel model(std::cref(numbers));

注意:如果 范围内的值是 const,则无法通过 `QAbstractItemModel ` API 删除或插入列和行。若需更精细的控制,请实现 `the C++ tuple protocol`。

行与列

范围中的元素被解释为模型的行。根据这些行元素的类型,QRangeModel 会将范围呈现为列表、表格或树结构。

如果行元素是简单值,则该范围将表示为列表。

QList<int> numbers = {1, 2, 3, 4, 5};
QRangeModel model(numbers); // columnCount() == 1
QListView listView;
listView.setModel(&model);

如果行元素的类型是可迭代范围(例如向量、列表或数组),则该范围将表示为表格。

std::vector<std::vector<int>> gridOfNumbers = {
    {1, 2, 3, 4, 5},
    {6, 7, 8, 9, 10},
    {11, 12, 13, 14, 15},
};
QRangeModel model(&gridOfNumbers); // columnCount() == 5
QTableView tableView;
tableView.setModel(&model);

如果行类型提供了标准的 C++ 容器 APIresize() 、insert() 、erase() ,则可以通过insertColumns() 和removeColumns() 添加和删除列。所有行必须具有相同的列数。

作为行的结构体和小部件

如果行类型实现了the C++ tuple protocol ,则该范围将表示为一个具有固定列数的表格。

using TableRow = std::tuple<int, QString>;
QList<TableRow> numberNames = {
    {1, "one"},
    {2, "two"},
    {3, "three"}
};
QRangeModel model(&numberNames); // columnCount() == 2
QTableView tableView;
tableView.setModel(&model);

对于 C++ 类型而言,比实现元组协议更简单、更灵活的方法是使用 Qt的元对象系统来声明具有属性的类型。这可以是声明为gadget 的值类型,也可以是QObject 的子类。

class Book
{
    Q_GADGET
    Q_PROPERTY(QString title READ title)
    Q_PROPERTY(QString author READ author)
    Q_PROPERTY(QString summary MEMBER m_summary)
    Q_PROPERTY(int rating READ rating WRITE setRating)
public:
    Book(const QString &title, const QString &author);

    // C++ rule of 0: destructor, as well as copy/move operations
    // provided by the compiler.

    // read-only properties
    QString title() const { return m_title; }
    QString author() const { return m_author; }

    // read/writable property with input validation
    int rating() const { return m_rating; }
    void setRating(int rating)
    {
        m_rating = qBound(0, rating, 5);
    }

private:
    QString m_title;
    QString m_author;
    QString m_summary;
    int m_rating = 0;
};

使用QObject 的子类,可以使属性支持绑定,或具备更改通知信号。然而,将QObject 实例用作项目会带来显著的内存开销。

与实现元组协议相比,使用 Qt 小工具或对象更为便捷且更具灵活性。这些类型还可在 QML 中直接访问。不过,通过属性系统进行访问会带来一定的运行时开销。对于对性能要求极高的模型,建议考虑实现元组协议,以便在编译时生成访问代码。

多角色项

data()、setData()、clearItemData() 等方法所操作的项目类型,可以在整个模型中保持一致——如上文的gridOfNumbers 示例所示。但范围也可以针对不同列使用不同的项目类型,例如numberNames 的情况。

默认情况下,该值将用于“Qt::DisplayRole ”和“Qt::EditRole ”角色。大多数视图期望该值为convertible to and from a QString (但自定义委托可能提供更大的灵活性)。

具有多个角色的关联容器

如果该项目是一个关联容器,且使用int 、Qt::ItemDataRole 或QString 作为键类型,同时使用QVariant 作为映射类型,那么QRangeModel会将该容器解释为多个角色的数据存储。data() 和setData() 函数用于返回和修改容器中的映射值;setItemData() 用于修改所有提供的值;itemData() 用于返回所有存储的值;clearItemData() 用于清空整个容器。

using ColorEntry = QMap<Qt::ItemDataRole, QVariant>;

const QStringList colorNames = QColor::colorNames();
QList<ColorEntry> colors;
colors.reserve(colorNames.size());
for (const QString &name : colorNames) {
    const QColor color = QColor::fromString(name);
    colors << ColorEntry{{Qt::DisplayRole, name},
                        {Qt::DecorationRole, color},
                        {Qt::ToolTipRole, color.name()}};
}
QRangeModel colorModel(colors);
QListView list;
list.setModel(&colorModel);

用作键的最有效率的数据类型是Qt::ItemDataRole 或int 。当使用int 时,itemData()会原样返回容器,无需创建数据的副本。

作为多角色项的小工具和对象

小工具和QObject 类型也可以作为多角色项表示。这些项的属性将用于与name of a role 匹配的角色。如果所有项都持有相同类型的小工具或QObject ,那么QRangeModel中的roleNames()实现将返回该类型的属性列表。

class ColorEntry
{
    Q_GADGET
    Q_PROPERTY(QString display READ colorName)
    Q_PROPERTY(QColor decoration READ decoration)
    Q_PROPERTY(QString toolTip READ toolTip)
public:
    ColorEntry(const QString &color = {})
        : m_colorName(color)
    {}

    QString colorName() const
    {
        return m_colorName;
    }
    QColor decoration() const
    {
        return QColor::fromString(m_colorName);
    }
    QString toolTip() const
    {
        return QColor::fromString(m_colorName).name();
    }

private:
    QString m_colorName;
};

在表格中使用时,这是小工具的默认显示方式:

QList<QList<ColorEntry>> colorTable;

// ...

QRangeModel colorModel(colorTable);
QTableView table;
table.setModel(&colorModel);

但在列表中使用时,这些类型默认会以多列行形式呈现,每个属性对应一列。若要强制将小部件在列表中表示为多角色项,请通过特化 QRoleModel::RowOptions 并将static constexpr auto rowCategory 成员变量设置为MultiRoleItem 来声明该小部件为多角色类型。

class ColorEntry
{
    Q_GADGET
    Q_PROPERTY(QString display READ colorName)
    Q_PROPERTY(QColor decoration READ decoration)
    Q_PROPERTY(QString toolTip READ toolTip)
public:
    ...
};
template <>
struct QRangeModel::RowOptions<ColorEntry>
{
    static constexpr auto rowCategory = QRangeModel::RowCategory::MultiRoleItem;
};

您还可以将此类类型包装成单元素元组,从而将列表转换为单列表格:

const QStringList colorNames = QColor::colorNames();
QList<std::tuple<ColorEntry>> colors;

// ...

QRangeModel colorModel(colors);
QListView list;
list.setModel(&colorModel);

在此情况下,请注意,直接访问列表数据中的元素时需要使用 `std::get`:

ColorEntry firstEntry = std::get<0>(colors.at(0));

或者使用结构化绑定:

auto [firstEntry] = colors.at(0);

行作为值或指针

在迄今为止的示例中,我们始终使用 QRangeModel 处理包含值的范围。QRangeModel 也可以对包含指针(包括智能指针)的范围进行操作。这使得 QRangeModel 能够处理多态类型的范围,例如QObject 的子类。

class Entry : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QString display READ display WRITE setDisplay NOTIFY displayChanged)
    Q_PROPERTY(QIcon decoration READ decoration WRITE setDecoration NOTIFY decorationChanged)
    Q_PROPERTY(QString toolTip READ toolTip WRITE setToolTip NOTIFY toolTipChanged)

public:
    Entry() = default;

    QString display() const
    {
        return m_display;
    }

    void setDisplay(const QString &display)
    {
        if (m_display == display)
            return;
        m_display = display;
        emit displayChanged(m_display);
    }

signals:
    void displayChanged(const QString &);
    ...
};
std::vector<std::shared_ptr<Entry>> entries = {
    ...
};
QRangeModel model(std::ref(entries));
QListView listView;
listView.setModel(&model);

与值的情况类似,行的类型决定了该范围是作为列表、表格还是树来表示的。如果是 QObject 类型的行,则会将每个属性呈现为一列,除非对QRangeModel::RowOptions 模板进行了特化,将该类型声明为多角色项。

template <>
struct QRangeModel::RowOptions<Entry>
{
    static constexpr auto rowCategory = QRangeModel::RowCategory::MultiRoleItem;
};
std::vector<std::shared_ptr<Entry>> entries = {
    std::make_shared<Entry>(),
    ...
};

QRangeModel model(std::ref(entries));

注意:如果 范围包含原始指针,则必须通过范围的指针或引用包装器来构造 QRangeModel。否则,数据的所有权将变得模糊,且范围的副本仍会操作相同的实际行数据,从而导致意想不到的副作用。

继承 QRangeModel

通过继承 QRangeModel,可以添加考虑了范围数据类型和结构的便捷 API。

class NumbersModel : public QRangeModel
{
    std::vector<int> m_numbers;

public:
    NumbersModel(const std::vector<int> &numbers)
        : QRangeModel(std::ref(m_numbers))
        , m_numbers(numbers)
    {
    }

操作时,请将该范围作为私有成员添加,并使用该成员的引用包装器或指针调用 QRangeModel 构造函数。这样可以正确封装数据并避免直接访问。

    void setNumber(int idx, int number)
    {
        setData(index(idx, 0), QVariant::fromValue(number));
    }

    int number(int idx) const
    {
        return m_numbers.at(idx);
    }
};

添加成员函数以提供对数据的类型安全访问,并使用QAbstractItemModel API执行任何会修改该范围的操作。只读访问可直接操作数据结构。

数据树

QRangeModel 可以将数据结构表示为树模型。此类树数据结构必须是同构的:在树的各个层级上,子行列表必须使用与树本身完全相同的表示形式。此外,行类型必须是静态大小的:要么是 gadget 或QObject 类型,要么是实现了the C++ tuple protocol 的类型。

为了将此类数据表示为树,QRangeModel 必须能够遍历该数据结构:对于任意给定行,模型需要能够检索父行以及可选的子行范围。这些遍历函数可以通过行类型隐式提供,也可以通过显式的协议类型提供。

隐式树遍历协议

class TreeRow;

using Tree = std::vector<TreeRow>;

树本身是一个由TreeRow 值组成的向量。关于行应使用项的值还是指针,请参阅Tree Rows as pointers or values 中的相关讨论。

class TreeRow
{
    Q_GADGET
    // properties

    TreeRow *m_parent;
    std::optional<Tree> m_children;

public:
    TreeRow() = default;

    // rule of 0: copy, move, and destructor implicitly defaulted

行类可以是上述任何固定大小的类型:实现元组协议的类型、小部件(gadget)或QObject 。在本示例中,我们使用一个小部件。

每个行项都需要维护一个指向父行的指针,以及一个可选的子行范围。该范围必须与树本身所使用的范围结构完全一致。

将行类型设为默认可构造是可选的,这允许模型构造新的行数据元素,例如在insertRow()或moveRows()的实现中。

    // tree traversal protocol implementation
    const TreeRow *parentRow() const { return m_parent; }
    const std::optional<Tree> &childRows() const { return m_children; }

随后,树遍历协议可作为行数据类型的成员函数来实现。constparentRow() 函数必须返回一个指向行项的指针;而childRows() 函数必须返回对 conststd::optional 的引用,该引用可存储可选的子行范围。

仅凭这两个函数,模型就足以将树作为只读数据结构进行导航。为了允许用户编辑视图中的数据,并使模型能够实现诸如insertRows()、removeRows() 和moveRows() 等可变模型 API,我们必须实现额外的写入访问函数:

    void setParentRow(TreeRow *parent) { m_parent = parent; }
    std::optional<Tree> &childRows() { return m_children; }

模型调用setParentRow() 函数及其可变版本childRows() ,用于将行移动或插入到现有树分支中,并在旧值失效时更新父节点指针。childRows() 的非 const 重载版本还提供了对行数据的写访问权限。

注意:该模型 将设置行父节点、从旧父节点中移除该行,以及将其添加到新父节点的子节点列表中,作为独立的步骤分别处理。这使得协议接口保持精简。

    ...
    // Helper to assembly a tree of rows, not used by QRangeModel
    template <typename ...Args>
    TreeRow &addChild(Args &&...args)
    {
        if (!m_children)
            m_children.emplace(Tree{});
        auto &child = m_children->emplace_back(std::forward<Args>(args)...);
        child.m_parent = this;
        return child;
    }
};

类实现的其余部分与该模型无关,但addChild() 辅助函数为我们提供了一种便捷的方式来构建树的初始状态。

Tree tree = {
    {"..."},
    {"..."},
    {"..."},
};

// each toplevel row has three children
tree[0].addChild("...");
tree[0].addChild("...");
tree[0].addChild("...");

tree[1].addChild("...");
tree[1].addChild("...");
tree[1].addChild("...");

tree[2].addChild("...");
tree[2].addChild("...");
tree[2].addChild("...");

使用此类范围的实例实例化的 QRangeModel 将以树的形式表示数据。

// instantiate the model with a pointer to the tree, not a copy!
QRangeModel model(&tree);
QTreeView view;
view.setModel(&model);

在单独类中实现树遍历协议

树遍历协议也可以在单独的类中实现。

struct TreeTraversal
{
    TreeRow newRow() const { return TreeRow{}; }
    const TreeRow *parentRow(const TreeRow &row) const { return row.m_parent; }
    void setParentRow(TreeRow &row, TreeRow *parent) const { row.m_parent = parent; }
    const std::optional<Tree> &childRows(const TreeRow &row) const { return row.m_children; }
    std::optional<Tree> &childRows(TreeRow &row) const { return row.m_children; }
};

将该协议实现的实例传递给 QRangeModel 构造函数:

QRangeModel model(&tree, TreeTraversal{});

将树行作为指针或值

数据范围的行类型可以是值,也可以是指针。在上面的代码中,我们一直将树行作为向量中的值使用,这样就避免了必须进行显式内存管理。 然而,作为连续内存块的向量在需要重新分配存储空间,或插入、删除元素时,会导致所有迭代器和引用失效。这会影响指向父项的指针——即父行在向量中的位置。 确保该父节点(以及引用其中项的QPersistentModelIndex 实例)保持有效,可能会带来相当大的性能开销。QRangeModel的实现必须假设,在修改范围时,所有指向该范围的引用都会失效。

作为替代方案,我们也可以使用一组行指针作为树类型:

struct TreeRow;
using Tree = std::vector<TreeRow *>;

在这种情况下,我们必须使用new 运算符显式地分配所有TreeRow实例,并实现析构函数,以对子节点向量中的所有项调用delete 方法。

structTreeRow
{
    Q_GADGET
public:
    TreeRow(constQString&value ={})
        : m_value(value)
    {}
    ~TreeRow()
    {
        if(m_children)
            qDeleteAll(*m_children);
    }

    // 仅支持移动
    TreeRow(TreeRow&&)= default;
    TreeRow&operator=(TreeRow&&)= default;

   // 填充辅助函数
    template <typename...Args>
    TreeRow*addChild(Args&&...args)
    {
        if(!m_children)
            m_children.emplace(Tree{});
       auto *child = m_children->emplace_back(newTreeRow(std::forward<Args>(args)...));
        child->m_parent= this;
        returnchild;
    }

private:
    friend structTreeTraversal;
    QString m_value;
    std::optional<Tree>m_children;
    TreeRow*m_parent =nullptr;
};
Tree tree={
    newTreeRow("1"),
    newTreeRow("2"),
    newTreeRow("3"),
    newTreeRow("4"),
};
tree[0]->addChild("1.1");
tree[1]->addChild("2.1");
tree[2]->addChild("3.1")->addChild("3.1.1");
tree[3]->addChild("4.1");

在构建一个将这些数据表示为树的模型之前,我们还需要实现树遍历协议。

struct TreeTraversal
{
    TreeRow *newRow() const { return new TreeRow; }
    void deleteRow(TreeRow *row) { delete row; }

    const TreeRow *parentRow(const TreeRow &row) const { return row.m_parent; }
    void setParentRow(TreeRow &row, TreeRow *parent) { row.m_parent = parent; }
    const std::optional<Tree> &childRows(const TreeRow &row) const { return row.m_children; }
    std::optional<Tree> &childRows(TreeRow &row) { return row.m_children; }
};

针对可变指针树的显式协议实现必须提供两个额外的成员函数:newRow() 和deleteRow(RowType *) 。

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

    Tree tree = make_tree_of_pointers();

    QRangeModel model(std::move(tree), TreeTraversal{});
    QTreeView treeView;
    treeView.setModel(&model);
    treeView.show();

    return app.exec();
}

当在insertRows() 中创建新行,以及在removeRows() 中删除行时,模型会调用这些函数。此外,如果模型拥有数据的所有权,那么在销毁时,它还会删除所有顶级行。 请注意,在此示例中,我们将树结构移入模型,因此不再需要对其进行任何操作。当通过将包含行指针的树数据移入其中来构造 QRangeModel 时,该模型将获得数据的所有权,并在其析构函数中删除其中的行指针。

使用指针作为行会带来一些内存分配和管理开销。然而,即使行项在范围内部移动,或者范围进行内存重新分配,对行项的引用仍然保持稳定。这可以显著降低在使用insertRows()、removeRows() 或moveRows() 时修改模型结构所需的开销。

每种选择在性能和内存开销方面都有不同的权衡。最佳选项取决于具体的使用场景和所用的数据结构。

通过模板特化进行定制

QRangeModel 声明了两个嵌套的模板类型,您可以通过特化来覆盖默认行为。

上文已介绍过的RowOptions 模板用于自定义行类型特有的功能(如表头数据),或应统一应用于行中所有项的功能(如默认标记)。ItemAccess 模板则允许自定义项类型特有的行为,例如读取或写入角色数据。

这两个模板始终需要针对底层类型进行特化,因此即使范围中包含类型为Row 的行(作为std::unique_ptr<Row> ),或类型为Item 的项(作为std::shared_ptr<Item> ),特化也必须针对Row 和Item 。

template <>
struct QRangeModel::ItemAccess<ColorEntry>
{
    ...
};
std::vector<std::shared_ptr<ColorEntry>> colors;
// ...
QRangeModel model(colors);

注意:在 C++中 ,您只应针对自己拥有的类型进行模板特化,而不应针对标准类型或 Qt 类型进行特化。若需自定义其他类型的行为,请为这些类型创建子类或聚合体。 请注意,类型别名并非独立类型,因此不应针对别名类型进行模板特化。不过,允许使用您拥有的类型(例如std::vector<MyGadget> )来创建标准或 Qt 容器的特化版本。

行和项的特定标志

flags() 的默认实现会返回Qt::ItemIsSelectable 、Qt::ItemIsEnabled 以及——树模型第 0 列的项除外——Qt::ItemNeverHasChildren 的组合。对于非只读模型中的所有项,都会设置Qt::ItemIsEditable 和Qt::ItemIsDropEnabled 标志;只要模型支持至少一个mime type ,就会设置Qt::ItemIsDragEnabled 标志。

若要自定义行中所有项的默认行为,请特化RowOptions 模板。若要访问单个项的标志数据,请特化ItemAccess 模板。您可以同时进行这两项操作:RowOptions 可设置行中所有项共有的标志,而ItemAccess 可为由特定类型支持的项设置标志。

拖放处理

自 Qt 6.12 起,QRangeModel 实现了flags() 虚拟函数,用于为模型中所有有效的项目设置Qt::ItemIsDragEnabled 标志,前提是该模型至少支持一个mime type 。这是默认行为,因为QAbstractItemModel 提供了 Qt 内部的“application/x-qabstractitemmodeldatalist”MIME 类型。 此外,只要该模型不是只读的,就会为该模型中的项目设置Qt::ItemIsDropEnabled 标志。这包括无效索引处的不存在项目,因此用户可以将数据拖放到视图的空白区域以追加数据。

无需子类化 QRangeModel 并重写相应的虚函数,即可实现对其他 MIME 类型的支持。 QRangeModel 将检测并使用ItemAccess 和RowOptions 自定义模板中的函数,将行或项目编码为mime data ,并将其decode mime data 为行或项目的序列。这些自定义函数可以直接操作行和项目类型,无需通过QModelIndex 和QVariant 进行中间处理。

这样,数据序列化的代码就能实现类型安全,并且可以与用于存储数据的类型的实现保持一致。

此外,supportedDragActions 和supportedDropActions 是可针对每个QRangeModel实例进行配置的属性。

C++ 高级主题

C++ 元组协议

如上文numberNames 示例所示,行类型可以是元组,实际上也可以是任何实现了元组协议的类型。该协议的实现方式是特化std::tuple_size 和std::tuple_element ,并重载未限定的get 函数。请为您的自定义行类型进行上述操作,以便让Qt中的模型/视图框架能够使用现有的结构化数据。

struct Book
{
    QString title;
    QString author;
    QString summary;
    int rating = 0;

    template <size_t I, typename T>
        requires ((I <= 3) && std::is_same_v<std::remove_cvref_t<T>, Book>)
    friend inline decltype(auto) get(T &&book)
    {
        if constexpr (I == 0)
            return std::as_const(book.title);
        else if constexpr (I == 1)
            return std::as_const(book.author);
        else if constexpr (I == 2)
            return std::forward_like<T>(book.summary);
        else if constexpr (I == 3)
            return std::forward_like<T>(book.rating);
    }
};

namespace std {
    template <> struct tuple_size<Book> : std::integral_constant<size_t, 4> {};
    template <size_t I> struct tuple_element<I, Book>
    { using type = decltype(get<I>(std::declval<Book>())); };
}

在上述实现中,Book 类型的title 和author 值被作为const 返回,因此模型会将这两列中的项目标记为只读。用户将无法触发编辑操作,且setData() 不会执行任何操作并返回 false。 对于summary 和rating ,其实现返回的值类别与书相同,因此当使用指向Book 的可变引用调用get 时,它将返回该变量的可变引用。模型会将这些列设为可编辑状态,无论是用户操作还是程序化访问均适用。

注意: 上述get 的实现需要 C++23。

二进制兼容性注意事项

QRangeModel 不是模板类。通过库 API 传递 QRangeModel 实例(与所有QObject 类一样,可通过指针或引用传递),或者在库的公共类中按值存储 QRangeModel,都是安全的。

然而,QRangeModel 的构造函数是一个模板且为内联的,并且该构造函数会在内部实例化针对模型所操作的范围类型进行特化的实现。 您不应在库 API 的内联实现中调用该构造函数。这会导致 ODR 违规,并且如果构建该库所基于的 Qt 版本与使用该库的应用程序所基于的 Qt 版本不同,可能会破坏该库的二进制兼容性。

如果 QRangeModel 检测到实现是针对较旧版本的 Qt 编译的,那么 Qt 后期版本中引入的虚拟函数的新实现和优化实现也可能无法被使用。 例如,sort() 和match() 的实现是 Qt 6.12 中的新增功能,但对于针对 Qt 6.11 编译的应用程序,即使所使用的 Qt 库是 Qt 6.12,这些实现也不会被调用。若要利用这些新的重写功能,请重新编译您的应用程序。

另请参阅 QRangeModelAdapter 、QAbstractItemModel 、QStandardItemModel 以及模型/视图编程。

成员类型文档

[since 6.11] enum class QRangeModel::AutoConnectPolicy

此枚举定义了QRangeModel 是否以及在何时会将属性的“changed-signals”自动连接到模型的dataChanged()信号。只有与role names 中的任一项匹配的属性才会被连接。

常量值描述
QRangeModel::AutoConnectPolicy::None0不会自动建立任何连接。
QRangeModel::AutoConnectPolicy::Full1所有相关属性的信号都会自动连接,适用于所有QObject 项。这包括添加到新插入的行和列中的QObject 项。
QRangeModel::AutoConnectPolicy::OnRead2相关属性的信号会在模型首次读取该属性时进行连接。

建立自动连接的内存开销可能相当大。完全自动连接除了连接本身之外不需要任何记录工作,但每个连接都会占用内存,连接所有对象的所有属性可能非常耗费资源,特别是如果只有对象子集中的少数属性会发生变化时。

“OnRead”连接策略不会连接到从未被读取(例如,从未在视图中渲染)的对象或属性,但记住哪些连接已建立需要一定的维护开销,且随时间推移会导致不可预测的内存增长。例如,向下滚动一个长列表时,很容易导致建立数千个新的连接。

该枚举在 Qt 6.11 中引入。

另请参阅 autoConnectPolicy 和roleNames()。

[since 6.12] enum class QRangeModel::DropOperation

此枚举定义了在dropMimeData()自定义方法中解码后的数据如何写入模型。

常量值描述
QRangeModel::DropOperation::DontDrop0数据不应添加到模型中。
QRangeModel::DropOperation::Automatic1Qt 会确定数据如何添加到模型中。
QRangeModel::DropOperation::OverwriteAndIgnore2覆盖被拖放到的项目及其后面的项目,并丢弃任何无法容纳的拖放数据。
QRangeModel::DropOperation::OverwriteAndExtend3覆盖被拖放到的项目及其后面的项目,并扩展模型以容纳所有被拖放的数据。
QRangeModel::DropOperation::InsertAsSiblings4将所有拖放的数据作为目标项的同级节点插入。
QRangeModel::DropOperation::InsertAsChildren5将所有拖放数据作为拖放目标项的子项插入。

dropMimeData() 的自定义实现可以返回一个布尔值,在这种情况下,false 映射到 DontDrop 值,而true 映射到 Automatic。

该枚举在 Qt 6.12 中引入。

另请参阅 canDropMimeData() 和dropMimeData()。

enum class QRangeModel::RowCategory

此枚举描述了QRangeModel 应如何呈现其构建所基于的范围中的元素。

常量值描述
QRangeModel::RowCategory::Default0QRangeModel 决定如何呈现行。
QRangeModel::RowCategory::MultiRoleItem1QRangeModel 将带有元对象的项目作为多角色项目呈现,即使在用于一维范围时也是如此。

为您的类型特化RowOptions 模板,并添加一个公共成员变量static constexpr auto rowCategory ,其值为该枚举中的一个值。

另请参阅 RowOptions 。

属性文档

[since 6.11] autoConnectPolicy : AutoConnectPolicy

如果且当模型自动连接到属性更改通知时。

如果QRangeModel 操作的数据结构所包含的QObject 子类类型与其行或项类型相同,则它会自动将QObject的属性连接到dataChanged()信号。对于QObject 行,该操作针对每一列进行,映射至Qt::DisplayRole 属性;对于项,则针对那些与role names 中任一项匹配的属性进行连接。

默认情况下,该属性的值为None ,因此不会建立此类连接。更改该属性的值将始终断开所有现有连接。

注意: 如果QRangeModel 所操作的数据结构中的 QObject 被替换,则不会断开或建立连接 。

该枚举类型在 Qt 6.11 中引入。

访问函数:

QRangeModel::AutoConnectPolicy autoConnectPolicy() const
void setAutoConnectPolicy(QRangeModel::AutoConnectPolicy policy)

通知器信号:

void autoConnectPolicyChanged(QRangeModel::AutoConnectPolicy policy)

另请参阅 roleNames()。

roleNames : QHash<int, QByteArray>

该属性存储了模型的角色名称。

如果该范围内的所有列类型相同,且该类型提供了元对象(即,它是小部件,或为QObject 的子类),则该属性存储该类型属性的名称,这些名称映射到Qt::ItemDataRole 的值,其范围从Qt::UserRole 开始。 此外,一个名为“modelData”的角色可提供对小部件或QObject 实例的访问。

通过将此属性显式设置为非空映射来覆盖此默认行为。将此属性设置为空映射,或使用 resetRoleNames(),可恢复默认行为。

访问函数:

virtual QHash<int, QByteArray> roleNames() const override
void setRoleNames(const QHash<int, QByteArray> &names)
void resetRoleNames()

通知器信号:

void roleNamesChanged()

另请参阅 QAbstractItemModel::roleNames()。

[since 6.12] sortCollator : QCollator

该属性存储在对模型进行排序时将使用的排序器

该属性的默认值是 C 语言区域设置下的QCollator 。排序操作不考虑区域设置,且区分大小写。设置排序器后,排序将考虑区域设置。

此枚举类型在 Qt 6.12 中引入。

访问函数:

QCollator sortCollator() const
void setSortCollator(const QCollator &collator)
void resetSortCollator()

通知器信号:

void sortCollatorChanged(const QCollator &collator)

另请参阅 sort()、sortRole 以及QSortFilterProxyModel 。

[since 6.12] sortRole : int

该属性存储在对项目进行排序时使用的数据角色。

默认值为Qt::DisplayRole 。

该枚举在 Qt 6.12 中引入。

访问函数:

int sortRole() const
void setSortRole(int role)
void resetSortRole()

通知器信号:

void sortRoleChanged(int role)

另请参阅 sort()、sortCollator 以及QSortFilterProxyModel 。

[since 6.12] supportedDragActions : Qt::DropActions

该属性存储了该模型支持的拖拽操作。

模型无法支持的操作(例如将数据移出只读模型)将在设置操作时被移除。

注意: 可以在QRangeModel 子类中重写 此函数,但这可能会破坏该属性的行为。

该枚举在 Qt 6.12 中引入。

访问函数:

virtual Qt::DropActions supportedDragActions() const override
void setSupportedDragActions(Qt::DropActions actions)
void resetSupportedDragActions()

通知器信号:

void supportedDragActionsChanged(Qt::DropActions actions)

另请参阅 supportedDropActions 。

[since 6.12] supportedDropActions : Qt::DropActions

该属性存储了该模型支持的拖放操作。

只读模型不支持任何拖放操作。

注意: 可以在QRangeModel 的子类中重写 此函数,但这可能会破坏该属性的行为。

该枚举在 Qt 6.12 中引入。

访问函数:

virtual Qt::DropActions supportedDropActions() const override
void setSupportedDropActions(Qt::DropActions actions)
void resetSupportedDropActions()

通知器信号:

void supportedDropActionsChanged(Qt::DropActions actions)

另请参阅 supportedDragActions 。

成员函数文档

[explicit] template <typename Range, int = true> QRangeModel::QRangeModel(Range &&range, QObject *parent = nullptr)

[explicit] template <typename Range, int = true> QRangeModel::QRangeModel(Range &&range, QObject *parent = nullptr)

[explicit] template < typename Range, typename Protocol, int = true > QRangeModel::QRangeModel(Range &&range, Protocol &&protocol, QObject *parent = nullptr)

构建一个QRangeModel 实例,该实例对range 中的数据进行操作。range 必须是一个顺序范围,且编译器能够通过参数依赖查找为其找到begin 和end 的重载,或者已为其实现了std::begin 和std::end 。如果提供了protocol ,则该模型将使用协议实现将该范围表示为一棵树。该模型实例将成为parent 的子节点。

range 可以是指针或引用包装器,在这种情况下,修改模型 API(例如setData() 或insertRow()) 将修改被引用的范围实例中的数据。如果range 是一个值(或已移动到模型中),则应连接到模型发出的信号以响应数据的变化。

QRangeModel 在构造过程中不会访问range 。这使得向该构造函数传递尚未完全构造完成的范围对象的指针或引用是合法的,例如在subclassing QRangeModel 时。

如果range 已被移入模型中,那么当模型被销毁时,该范围及其中的所有数据也将被销毁。

注意:虽然 模型在其他情况下不会获取范围对象的所有权,但 一旦模型被构造并传递给视图,您就不得直接修改该range 。此类修改将不会发出必要的信号,无法使模型用户(其他模型或视图)与模型保持同步,从而导致结果不一致、未定义行为以及程序崩溃。 请使用 `QRangeModelAdapter ` 安全地与底层范围交互,同时保持模型更新。

另请参阅 QRangeModelAdapter 。

[override virtual noexcept] QRangeModel::~QRangeModel()

销毁“QRangeModel ”。

不会访问用于构建该模型的范围,仅当该模型是从已移动的范围构建而来的时才会被销毁。

[override virtual] QModelIndex QRangeModel::buddy(const QModelIndex &index) const

重写了:QAbstractItemModel::buddy(const QModelIndex &index) const。

[override virtual] bool QRangeModel::canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) const

重写:QAbstractItemModel::canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) const。

如果范围中存储的项目类型有ItemAccess 的自定义实现,并且该自定义实现了合适的canDropMimeData()类成员函数,则此实现将调用该函数并返回结果。

template <>
struct QRangeModel::ItemAccess<ColorEntry>
{
    static bool canDropMimeData(const QMimeData *mimeData)
    {
        // ...
        return true;
    }
};

否则,如果该范围中的行类型有RowOptions 的定制版本,且该定制版本具有合适的canDropMimeData()类成员函数,则此实现将返回调用该函数的结果。

template <>
struct QRangeModel::RowOptions<ColorEntry>
{
    static bool canDropMimeData(const QMimeData *mimeData)
    {
        // ...
        return true;
    }
};

如果两种自定义实现均不可用,则返回默认QAbstractItemModel 实现的结果。

另请参阅 Drag'n'drop handling 、RowOptions::canDropMimeData()、ItemAccess::canDropMimeData()、dropMimeData()、mimeTypes() 以及mimeData()。

[override virtual] bool QRangeModel::canFetchMore(const QModelIndex &parent) const

重写了:QAbstractItemModel::canFetchMore(const QModelIndex &parent) const。

[override virtual] bool QRangeModel::clearItemData(const QModelIndex &index)

重写:QAbstractItemModel::clearItemData(const QModelIndex &index)。

将index 中该范围所存储的值替换为默认构造的值。

对于操作只读范围的模型,或者操作在实现the C++ tuple protocol 的行类型中的只读列的模型,此实现会立即返回false 。

[override virtual] int QRangeModel::columnCount(const QModelIndex &parent = {}) const

重写:QAbstractItemModel::columnCount(const QModelIndex &parent) const。

返回模型的列数。此函数对于所有parent 索引均返回相同的值。

对于操作静态行长度的模型,该返回值在模型生命周期内始终保持不变。对于操作动态行长度的模型,该函数返回第一行中的项目数;若模型无行,则返回 0。

另请参阅 rowCount 和insertColumns()。

[override virtual] QVariant QRangeModel::data(const QModelIndex &index, int role = Qt::DisplayRole) const

重写了:QAbstractItemModel::data(const QModelIndex &index, int role) const。

返回存储在给定role 下的、位于index 所指范围内的值的数据。

如果该索引的项类型是一个关联容器,该容器将int 、Qt::ItemDataRole 或QString 映射到QVariant ,则会在该容器中查找角色数据并返回。

如果该项目是小工具或QObject ,则实现将返回该项目中与roleNames()映射中role 条目匹配的属性的值。

否则,实现将返回一个通过QVariant::fromValue() 基于该项构建的QVariant ,用于Qt::DisplayRole 或Qt::EditRole 。对于其他角色,实现将返回一个无效的(默认构造的)QVariant 。

另请参阅 Qt::ItemDataRole 、setData() 以及headerData()。

[override virtual] bool QRangeModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)

重写:QAbstractItemModel::dropMimeData (const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)。

如果范围中存储的项目类型有ItemAccess 的自定义实现,且该自定义实现了合适的dropMimeData()类成员函数,则此实现将调用该函数并返回结果。

template <>
struct QRangeModel::ItemAccess<ColorEntry>
{
    static bool dropMimeData(const QMimeData *mimeData, auto inserter)
    {
        QByteArray data = mimeData->data(mimeTypes().first());
        if (data.isEmpty())
            return false;
        QTextStream stream(&data, QIODevice::ReadOnly);
        while (!stream.atEnd()) {
            QString colorName;
            stream >> colorName;
            inserter = ColorEntry(colorName);
        }
        return true;
    }
};

否则,如果范围中的行类型有RowOptions 的定制版本,且该定制版本具有合适的dropMimeData()类成员函数,则此实现将返回调用该函数的结果。

template <>
struct QRangeModel::RowOptions<ColorEntry>
{
    static bool dropMimeData(const QMimeData *mimeData, auto inserter)
    {
        const QByteArray data = mimeData->data(mimeTypes().first());
        if (data.isEmpty())
            return false;
        QXmlStreamReader stream(data);
        while (!stream.atEnd()) {
            stream.readNext();
            if (stream.isStartElement() && stream.name() == u"li"_s)
                inserter = ColorEntry(stream.readElementText());
        }

        return true;
    }
};

如果两种自定义实现均不可用,则返回默认QAbstractItemModel 实现的结果。

另请参阅 Drag'n'drop handling 、RowOptions::dropMimeData()、ItemAccess::dropMimeData()、canDropMimeData()、mimeTypes() 和mimeData()。

[override virtual protected] bool QRangeModel::event(QEvent *event)

重写了:QObject::event(QEvent *e)。

[override virtual protected] bool QRangeModel::eventFilter(QObject *object, QEvent *event)

重写了:QObject::eventFilter(QObject *watched, QEvent *event)。

[override virtual] void QRangeModel::fetchMore(const QModelIndex &parent)

重写了:QAbstractItemModel::fetchMore(const QModelIndex &parent)。

[override virtual] Qt::ItemFlags QRangeModel::flags(const QModelIndex &index) const

重写:QAbstractItemModel::flags(const QModelIndex &index) const。

返回给定index 的项目标志。

该实现返回一组标志,这些标志既启用该项(ItemIsEnabled ),又允许其被选中(ItemIsSelectable )。对于操作可变数据范围的模型,它还会设置允许该项可编辑的标志(ItemIsEditable )。

返回非空 `mimeTypes` 列表的模型也会设置 `Qt::ItemIsDragEnabled`,并且(除非是只读的)还会设置 `Qt::ItemIsDropEnabled ` 标志。

扁平模型会为所有项目设置Qt::ItemNeverHasChildren ,而分层模型则仅为位于 0 以上列中的所有项目设置该标志。

若要针对您自己的数据类型自定义这些标志,请为您的行或项类型提供RowOptions 和/或ItemAccess 的特化实现,并实现flags() 静态成员函数:

template <>
struct QRangeModel::ItemAccess<ColorEntry>
{
    static Qt::ItemFlags flags(const ColorEntry &entry)
    {
        return entry.flags();
    }
};

另请参阅 Qt::ItemFlags 、RowOptions 和ItemAccess 。

[override virtual] bool QRangeModel::hasChildren(const QModelIndex &parent = QModelIndex()) const

重写了:QAbstractItemModel::hasChildren(const QModelIndex &parent) const。

[override virtual] QVariant QRangeModel::headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const

重写:QAbstractItemModel::headerData(int section, Qt::Orientation orientation, int role) const。

返回标题中指定role 和section 对应的数据,其orientation 为指定值。

对于水平标题,部分编号对应于列号。同样,对于垂直标题,部分编号对应于行号。

对于水平标题和Qt::DisplayRole role ,对使用数组作为行类型的范围进行操作的模型将返回section 。如果行类型是元组,则该实现将返回section 处该类型的名称。对于属于gadget或QObject 类型的行,该函数将返回section 索引处该属性的名称。

若要自定义特定范围的水平标题数据,请针对您的行类型提供RowOptions 的特化版本,并实现headerData() 类成员函数:

template <>
struct QRangeModel::RowOptions<ColorEntry>
{
    static QVariant headerData(int section, int role)
    {
        switch (section) {
            // ...
        }
        return {};
    }
};

对于垂直标题,该函数始终返回QAbstractItemModel 中默认实现的结果。

另请参阅 Qt::ItemDataRole 、setHeaderData()、QHeaderView 以及RowOptions 。

[override virtual] QModelIndex QRangeModel::index(int row, int column, const QModelIndex &parent = {}) const

重写了:QAbstractItemModel::index(int row, int column, const QModelIndex &parent) const。

返回parent 中位于row 和column 处的模型项的索引。

对于操作列表和表格范围的模型,传递一个有效的父项会产生一个无效的索引。

另请参阅 parent()。

[override virtual] bool QRangeModel::insertColumns(int column, int count, const QModelIndex &parent = {})

重写:QAbstractItemModel::insertColumns(int column, int count, const QModelIndex &parent)。

在范围parent 中的所有行中,于坐标column 的项之前插入count 个空列。若操作成功,则返回true ;否则返回false 。

注意: 动态大小的行类型需要提供一个insert(const_iterator, size_t, value_type) 成员函数。

对于在只读范围上操作,或在具有静态行类型的范围(如元组、数组或结构体)上操作的模型,此实现不执行任何操作,并立即返回false 。对于树模型,情况总是如此。

[override virtual] bool QRangeModel::insertRows(int row, int count, const QModelIndex &parent = {})

重写了:QAbstractItemModel::insertRows(int row, int count, const QModelIndex &parent)。

在parent 处指定的范围内,将count 个空行插入到row 之前。成功时返回true ;否则返回false 。

注意:该 范围必须为动态大小,并提供insert(const_iterator, size_t, value_type) 成员函数。

对于操作只读或静态大小的范围(如数组)的模型,此实现不执行任何操作,并立即返回false 。

注意:对于 具有动态大小列类型的范围,该列必须提供一个resize(size_t) 成员函数。

[override virtual] QMap<int, QVariant> QRangeModel::itemData(const QModelIndex &index) const

重写:QAbstractItemModel::itemData(const QModelIndex &index) const。

返回一个映射,其中包含模型中给定 `index` 所指项的所有预定义角色的值。

如果该index 的项类型是一个关联容器,该容器将int 、Qt::ItemDataRole 或QString 映射到QVariant ,则返回该容器中的数据。

如果项类型是小部件或QObject 的子类,则返回与role name 匹配的那些属性的值。

如果该项既不是关联容器、小工具,也不是QObject 的子类,则调用基类的实现。

另请参阅 setItemData()、Qt::ItemDataRole 以及data()。

[override virtual] QModelIndexList QRangeModel::match(const QModelIndex &start, int role, const QVariant &value, int hits, Qt::MatchFlags flags) const

重写了:QAbstractItemModel::match(const QModelIndex &start, int role, const QVariant &value, int hits, Qt::MatchFlags flags) const。

返回一个索引列表,其中包含start 列中那些数据(存储于role )与value 匹配的项,匹配依据由flags 定义。若使用hits = -1,则查找所有匹配项。

注意:此 实现直接从底层 C++ 范围中读取数据,不会通过data() 的重写进行分派。

[override virtual] QMimeData *QRangeModel::mimeData(const QModelIndexList &indexes) const

重写:QAbstractItemModel::mimeData(const QModelIndexList &indexes) const。

如果该范围中存储的项目类型有ItemAccess 的自定义实现,且该自定义实现了合适的mimeData()类成员函数,则此实现将调用该函数并返回结果。

template <>
struct QRangeModel::ItemAccess<ColorEntry>
{
    static QMimeData *mimeData(const auto &range)
    {
        QByteArray data;
        QTextStream stream(&data, QIODevice::WriteOnly);
        for (const auto &[item, index] : range)
            stream << item.colorName() << Qt::endl;

        QMimeData *result = new QMimeData;
        result->setData(mimeTypes().first(), data);
        // Qt handles encoding into the default mime type
        return result;
    }
};

否则,如果该范围中的行类型有可用的 `RowOptions ` 定制版本,且该定制版本具有合适的 `mimeData()` 类成员函数,则此实现将返回调用该函数的结果。

template <>
struct QRangeModel::RowOptions<ColorEntry>
{
    template <typename Items>
    static QMimeData *mimeData(const Items &items)
    {
        if (items.isEmpty())
            return nullptr;
        QByteArray data;
        QXmlStreamWriter stream(&data);
        stream.writeStartElement("ul");
        for (const auto &[item, index] : items)
            stream.writeTextElement("li", item.colorName());
        stream.writeEndElement();

        QMimeData *mimeData = new QMimeData;
        mimeData->setData(mimeTypes().first(), data);
        return mimeData;
    }
};

如果两种自定义实现均不可用,则返回默认QAbstractItemModel 实现的结果。

另请参阅 Drag'n'drop handling 、RowOptions::mimeData()、ItemAccess::mimeData()、canDropMimeData()、dropMimeData() 以及mimeTypes()。

[override virtual] QStringList QRangeModel::mimeTypes() const

重新实现了:QAbstractItemModel::mimeTypes() const。

如果范围中存储的项目类型有ItemAccess 的自定义实现,且该自定义实现实现了合适的mimeTypes()类成员函数,则此实现将调用该函数并返回结果。

template <>
struct QRangeModel::ItemAccess<ColorEntry>
{
    static QStringList mimeTypes()
    {
        return {
            u"text/plain"_s,
            u"application/x-qabstractitemmodeldatalist"_s
        };
    }
};

否则,如果范围中的行类型有可用的 `RowOptions ` 定制版本,且该定制版本具有合适的 `mimeTypes()` 类成员函数,则此实现将返回调用该函数的结果。

template <>
struct QRangeModel::RowOptions<ColorEntry>
{
    static QStringList mimeTypes()
    {
        return {
            u"text/html"_s
        };
    }
};

如果两种自定义实现均不存在,则返回默认QAbstractItemModel 实现的结果。

另请参见 Drag'n'drop handling 、RowOptions::mimeTypes()、ItemAccess::mimeTypes()、canDropMimeData()、dropMimeData() 以及mimeData()。

[override virtual] bool QRangeModel::moveColumns(const QModelIndex &sourceParent, int sourceColumn, int count, const QModelIndex &destinationParent, int destinationColumn)

重写:QAbstractItemModel::moveColumns (const QModelIndex &sourceParent, int sourceColumn, int count, const QModelIndex &destinationParent, int destinationChild)。

将父节点sourceParent 下的、从给定sourceColumn 开始的count 列,移动到父节点destinationParent 下的destinationColumn 列。

如果列移动成功,则返回true ;否则返回false 。

[override virtual] bool QRangeModel::moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationRow)

重写了:QAbstractItemModel::moveRows (const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild)。

count 将父节点sourceParent 下的第 sourceRow 行(从给定的sourceRow 开始)移动到父节点destinationParent 下的第destinationRow 行。

如果行已成功移动,则返回true ;否则返回false 。

[override virtual] void QRangeModel::multiData(const QModelIndex &index, QModelRoleDataSpan roleDataSpan) const

重写了:QAbstractItemModel::multiData(const QModelIndex &index, QModelRoleDataSpan roleDataSpan) const。

[override virtual] QModelIndex QRangeModel::parent(const QModelIndex &child) const

重写:QAbstractItemModel::parent(const QModelIndex &index) const。

返回位于child 索引处的项的父项。

对于操作列表和表格范围的模型,此函数始终会返回一个无效索引。对于操作树的模型,此函数返回树遍历协议中 parent() 实现所返回的行项的索引。

另请参阅 index() 和hasChildren()。

[override virtual] bool QRangeModel::removeColumns(int column, int count, const QModelIndex &parent = {})

重写:QAbstractItemModel::removeColumns(int column, int count, const QModelIndex &parent)。

从范围parent 中的所有行中,移除位于column 的项目中的count 列。成功时返回true ,否则返回false 。

注意: 动态大小的行类型需要提供一个erase(const_iterator, size_t) 成员函数。

对于操作只读范围,或操作具有静态行类型的范围(如元组、数组或结构体)的模型,此实现不执行任何操作,并立即返回false 。对于树模型,情况总是如此。

[override virtual] bool QRangeModel::removeRows(int row, int count, const QModelIndex &parent = {})

重写自:QAbstractItemModel::removeRows(int row, int count, const QModelIndex &parent)。

从parent 中的范围中移除count 行,从给定的row 开始。成功时返回true ,否则返回false 。

注意:该 范围必须为动态大小,并提供erase(const_iterator, size_t) 成员函数。

对于在只读或静态大小范围(如数组)上运行的模型,此实现不执行任何操作,并立即返回false 。

[override virtual protected slot] void QRangeModel::resetInternalData()

重写了:QAbstractItemModel::resetInternalData()。

[override virtual] QHash<int, QByteArray> QRangeModel::roleNames() const

重写了:QAbstractItemModel::roleNames() const。

注意: 可以在QRangeModel 的子类中重写 此函数,但这可能会破坏该属性的行为。

注意: 属性 roleNames的获取 函数。

另请参阅 setRoleNames()。

[override virtual] int QRangeModel::rowCount(const QModelIndex &parent = {}) const

重写:QAbstractItemModel::rowCount(const QModelIndex &parent) const。

返回给定parent 下的行数。对于无效的parent 索引,此值即为根范围中的项目数。

如果parent 索引有效,则对于操作列表和表格范围的模型,此函数始终返回 0。对于树,此函数返回树遍历协议中 childRows() 实现所返回的范围大小。

另请参阅 columnCount()、insertRows() 和hasChildren()。

[override virtual] bool QRangeModel::setData(const QModelIndex &index, const QVariant &data, int role = Qt::EditRole)

重写了:QAbstractItemModel::setData(const QModelIndex &index, const QVariant &value, int role)。

将位于index 的项的role 数据设置为data 。

如果该index 的项类型是一个关联容器,且该容器将int 、Qt::ItemDataRole 或QString 映射到QVariant ,则data 将作为由role 指定的键存储在该容器中。

如果该项是一个小工具或QObject ,则data 将写入该项中与roleNames() 映射中role 条目匹配的属性。如果找到了该属性,且data 中存储的值可转换为所需类型,则该函数返回true ;否则返回false 。

否则,该实现会将data 中的值赋给范围中index 位置的项(针对Qt::DisplayRole 和Qt::EditRole ),并返回true 。对于其他角色,该实现返回false 。

对于操作只读范围的模型,或操作实现the C++ tuple protocol 的行类型中只读列的模型,此实现会立即返回false 。

另请参阅 data()。

[override virtual] bool QRangeModel::setHeaderData(int section, Qt::Orientation orientation, const QVariant &data, int role = Qt::EditRole)

重写了:QAbstractItemModel::setHeaderData (int section, Qt::Orientation orientation, const QVariant &value, int role)。

另请参阅 headerData()。

[override virtual] bool QRangeModel::setItemData(const QModelIndex &index, const QMap<int, QVariant> &data)

重写:QAbstractItemModel::setItemData(const QModelIndex &index, const QMap<int, QVariant> &roles)。

如果该index 的项类型是一个关联容器,且该容器将int 或Qt::ItemDataRole 映射到QVariant ,则data 中的条目将存储在该容器中。如果该关联容器将QString 映射到QVariant ,则仅存储data 中那些在role names 表中存在映射关系的值。

如果项目类型是小工具或QObject 的子类,则那些与role name 匹配的属性将被设置为data 中的相应值。

在data 中没有条目的角色将不会被修改。

对于可复制的项目类型,此实现具有事务性,如果能将data 中的所有条目成功存储,则返回true。如果任何条目无法更新,则原始容器完全保持不变,且函数返回false。

如果该项目不是关联容器、小部件或QObject 的子类,则会调用基类的实现,该实现会针对data 中的每个条目调用setData()。

另请参见 itemData()、setData() 和Qt::ItemDataRole 。

[override virtual] QModelIndex QRangeModel::sibling(int row, int column, const QModelIndex &index) const

重写:QAbstractItemModel::sibling(int row, int column, const QModelIndex &index) const。

返回位于row 和column 处、与位于index 的项目相对应的兄弟项;如果该位置没有兄弟项,则返回无效的QModelIndex 。

此实现比通过parent() 调用index 的方式快得多。

另请参阅 index()、QModelIndex::row() 和QModelIndex::column()。

[override virtual] void QRangeModel::sort(int column, Qt::SortOrder order = Qt::AscendingOrder)

重写了:QAbstractItemModel::sort(int column, Qt::SortOrder order)。

根据column 中各项在sortRole (默认值为Qt::DisplayRole )中的数据,按给定的order 对底层范围进行排序。

注意:此 实现会优先使用 C++ 范围的成员函数sort(Compare comp) (如果可用,例如在std::list 中),否则当范围提供随机访问迭代器时使用std::stable_sort() 。如果两者均不可用,则该实现不执行任何操作并立即返回。

注意:访问 该项不会通过data() 的重写来分派数据读取操作。

另请参阅 sortRole 和QSortFilterProxyModel 。

[override virtual] QSize QRangeModel::span(const QModelIndex &index) const

重写了:QAbstractItemModel::span(const QModelIndex &index) const。

© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.