c++ - 将 QAbstractTableModel 与 QML TableView 连接起来
问题描述
QAbstractItemModel
为了创建一个非常通用的模型,我将其子类化,以下是文件:
cvartablemodel.h
#ifndef CVARTABLEMODEL_H
#define CVARTABLEMODEL_H
#include <QObject>
#include <QAbstractTableModel>
#include <QList>
#include <QVariant>
/**
* @brief Provides a QAbstractTableModel override class that implements the
* API for MDE variables storing, reading and writing.
*/
class CVarTableModel : public QAbstractTableModel
{
public:
/**
* @brief An enumeration class providing the columns and the amount of
* columns as well (use ZCOUNT as always last member).
*/
enum class Columns
{
Name = 0,
Unit,
Value,
ZCOUNT,
};
Q_ENUM(Columns)
CVarTableModel(QObject* parent = nullptr);
~CVarTableModel() override;
// Basic overrides
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role) const override;
// Since its a well behaved model...
QVariant headerData(int section,
Qt::Orientation orientation,
int role) const override;
// Its an editable model
Qt::ItemFlags flags(const QModelIndex &index) const override;
bool setData(const QModelIndex &index,
const QVariant &value,
int role = Qt::EditRole) override;
// Only rows are modificable for now
bool insertRows(int position,
int rows,
const QModelIndex &index = QModelIndex()) override;
bool removeRows(int position,
int rows,
const QModelIndex &index = QModelIndex()) override;
private:
/**
* @brief The local, intermediate storage object.
*/
QList<QList<QVariant>> m_data;
};
#endif // CVARTABLEMODEL_H
可变量模型.cpp
#include <QDebug>
#include "cvartablemodel.h"
/**
* @brief The default constructor, nothing interesting in here.
* @param parent: the parent object.
*/
CVarTableModel::CVarTableModel(QObject* parent) : QAbstractTableModel(parent)
{
}
/**
* @brief Clear the storage and remove connections (if any) at exit
*/
CVarTableModel::~CVarTableModel()
{
foreach (auto row, m_data)
row.clear();
m_data.clear();
}
/**
* @brief Returns the fixed (for now) amount of columns
* @param parent: unused
* @return The amount of available columns
*/
int CVarTableModel::columnCount(const QModelIndex& parent) const
{
if (parent.isValid())
return 0; // https://doc.qt.io/qt-5/qabstractitemmodel.html#columnCount
return static_cast<int>(Columns::ZCOUNT);
}
/**
* @brief Row count is equal to the stored number of variables.
* @param parent: unused.
* @return The amount of stored variables entries.
*/
int CVarTableModel::rowCount(const QModelIndex& parent) const
{
if (parent.isValid())
return 0; // https://doc.qt.io/qt-5/qabstractitemmodel.html#rowCount
return m_data.length();
}
/**
* @brief Reads the cell specified by the \ref index.
* @param index: Stores row/ col data.
* @param role: the display role.
* @return In case of valid \p index, a valid cell value. Otherwise empty
* QVariant object.
*/
QVariant CVarTableModel::data(const QModelIndex& index, int role) const
{
if (role != Qt::DisplayRole)
return QVariant();
if (!index.isValid())
return QVariant();
// check the row
if ((index.row() >= m_data.length()) || (index.row() < 0))
return QVariant();
// check the column
if ((index.row() >= m_data[index.row()].length()) || (index.column() < 0))
return QVariant();
return m_data[index.row()][index.column()];
}
/**
* @brief Obtains the header (columns) names.
* @param section: column number.
* @param orientation: Accepts only horizontal.
* @param role: Accepts only display.
* @return The column header text in case all params are valid.
* Otherwise empty QVariant.
*/
QVariant CVarTableModel::headerData(int section,
Qt::Orientation orientation,
int role) const
{
if (role != Qt::DisplayRole)
return QVariant();
if (orientation != Qt::Horizontal)
return QVariant();
if (section >= static_cast<int>(Columns::ZCOUNT))
return QVariant();
return QVariant::fromValue(static_cast<Columns>(section));
}
/**
* @brief Returns the \p index flags. Only values column is editable for now.
* @param index: model index item.
* @return flags enum val.
*/
Qt::ItemFlags CVarTableModel::flags(const QModelIndex& index) const
{
Qt::ItemFlags flags = Qt::ItemIsEnabled;
if (index.isValid())
{
if (static_cast<Columns>(index.column()) == Columns::Value)
flags |= Qt::ItemIsEditable;
}
return flags;
}
/**
* @brief Cell data writing override.
* @param index: The model index with row/ col.
* @param value: Value to be set in the cell.
* @param role: Only EditRole accepted.
* @return Non zero on succesfull data editing.
*/
bool CVarTableModel::setData(const QModelIndex& index,
const QVariant& value,
int role)
{
if (!index.isValid() || (role != Qt::EditRole))
return false;
// check the row
if ((index.row() >= m_data.length()) || (index.row() < 0))
return false;
// check the column
if ((index.row() >= m_data[index.row()].length()) || (index.column() < 0))
return false;
m_data[index.row()][index.column()] = value;
emit dataChanged(index, index, {role});
return true;
}
/**
* @brief Inserts the \p rows amount of rows. They will start at \p position.
* @param position: position at which the insertion starts (the new 1st item
* will have this index in the end).
* @param rows: amount of rows to insert.
* @param index: unused.
* @return Non zero in case of succesfull row insertion.
*/
bool CVarTableModel::insertRows(int position,
int rows,
const QModelIndex& index)
{
Q_UNUSED(index);
if ((position >= rowCount(QModelIndex())) || (position < 0))
return false;
beginInsertRows(QModelIndex(), position, position + rows - 1);
for (int row = 0; row < rows; row++)
{
QList<QVariant> emptyRow;
for (int i = 0; i < columnCount(QModelIndex()); i++)
emptyRow.append(QVariant());
m_data.insert(position, emptyRow);
}
endInsertRows();
return true;
}
/**
* @brief Removes \p rows amount of rows starting at \p position
* @param position: removing starts at this position.
* @param rows: the amount of rows that will be removed.
* @param index: unused.
* @return Non zero on succesfull rows removal.
*/
bool CVarTableModel::removeRows(int position,
int rows,
const QModelIndex& index)
{
Q_UNUSED(index);
if ((position >= rowCount(QModelIndex())) || (position < 0))
return false;
if (rows > rowCount(QModelIndex()))
return false;
beginRemoveRows(QModelIndex(), position, position + rows - 1);
for (int row = 0; row < rows; row++)
m_data.removeAt(position);
endRemoveRows();
return true;
}
我需要将此对象的一个实例与 QMLTableView
组件连接起来,但我真的不确定如何。
我已经为它创建了实例和吸气剂:
/**
* @brief The API + intermediate storage model for the bad nodes
*/
CVarTableModel m_varTabModel;
/**
* @brief A pointer getter for the whole variable table model.
* @return pointer to the model.
*/
QObject* CVessel::varTabModel()
{
return static_cast<QObject*>(&m_varTabModel);
}
所以最初这需要是一个没有行的 3 列表(可以在构造函数中添加一些行以用于测试目的)。
TableView
QML 端的组件现在如何利用它?我会欣赏一些允许输入和编辑一些值的 TableView 的 QML 示例。
解决方案
QML TableView 使用角色而不是列号。如果您检查模型中传递给方法的列data()
,您将看到它始终为 0。
因此,您必须转换 TableView 中给出的角色和模型中的列号。
您可以使用代理模型来处理角色/列转换。您不必更改当前模型:
该QIdentityProxyModel
课程是一个很好的基础:
class QMLProxy: public QIdentityProxyModel
{
Q_OBJECT
public:
QMLProxy(QObject* parent=nullptr): QIdentityProxyModel(parent)
{}
enum Role
{
NameRole = Qt::UserRole + 1,
UnitRole
};
QHash<int, QByteArray> roleNames() const override {
QHash<int, QByteArray> roles;
roles[NameRole] = "COL1";
roles[UnitRole] = "COL2";
return roles;
}
Q_INVOKABLE QVariant data(const QModelIndex &index, int role) const override
{
QModelIndex newIndex = mapIndex(index, role);
if (role == NameRole || role == UnitRole)
role = Qt::DisplayRole;
return QIdentityProxyModel::data(newIndex, role);
}
Q_INVOKABLE void edit(int row,
const QVariant &value,
QString const& role)
{
if (role == QString(roleNames().value(NameRole)))
setData(createIndex(row, 0), value, Qt::EditRole);
else if (role == QString(roleNames().value(UnitRole)))
setData(createIndex(row, 1), value, Qt::EditRole);
}
private:
QModelIndex mapIndex(QModelIndex const& source, int role) const {
switch(role)
{
case NameRole:
return createIndex(source.row(), 0);
case UnitRole:
return createIndex(source.row(), 1);
}
return source;
}
};
我重写了data()
将角色转换为列号。我创建了一个方法edit
,因为当它从 QML 调用时,签名将与方法不同setData
。
要将模型从 main 传递到 QML:
CVarTableModel* model = new CVarTableModel();
QMLProxy* proxy = new QMLProxy();
proxy->setSourceModel(model);
QQuickView *view = new QQuickView;
view->rootContext()->setContextProperty("myModel", proxy);
view->setSource(QUrl("qrc:/main.qml"));
view->show();
然后,在 QML 中,您需要一个委托来使您的表格可编辑(我使用了 TextInput。但是,您可以使用另一个组件):
TableView {
TableViewColumn {
role: "COL1"
title: "Col 1"
width: 100
}
TableViewColumn {
role: "COL2"
title: "Col 2"
width: 200
}
model: myModel
itemDelegate: Component {
TextInput {
id:textinput
text: styleData.value
onAccepted: {
myModel.edit(styleData.row, text, styleData.role)
}
MouseArea {
anchors.fill: parent
onClicked: textinput.forceActiveFocus()
}
}
}
}
推荐阅读
- plotly - 带有 plotly.express 的 Choropleth 地图不显示地图,只显示图例
- docker - gdbserver 不附加到 docker 容器中正在运行的进程
- vuetify.js - Vuetify 可编辑表
- node.js - 如何在显示图像时调整使用 multer-gridfs-storage 存储的图像大小
- java - 409 [{"errorCode":"BULK_API_ERROR","message":"批处理的错误内容类型 (text/csv; charset=utf-8),作业类型:text/csv"}]
- c# - 如何在代码 c# 中从 Resource 创建 WPF 图像 SVG?
- python - 如何将两个向量之间的相同旋转应用于另一个向量
- node.js - 猫鼬从数组中删除
- spring - Spring thymeleaf:如何显示警报
- react-native - React 本机 axios 无法正常工作