界面开发无外乎是布局和界面的美化,而现代业务显示中最常见的非列表、表格莫属,列表表格的界面在Qt中采用model/view进行开发,model/view能够很好实现业务与界面的分离,对于复杂的业务来说是非常有用的。
Qt QML的model/view相对于widget更简单易用。QML提供有一些model,如数组、键值对等,这些model都相对比较简易,处理复杂的数据和多样的业务就比较费劲了。
所以,对于复杂的数据,Qt提供了另外一种方式来解决,就是使用C++的model,即model的数据和业务处理都是用C++。我们熟知的用C++写业务,QML来写页面,QML使用C++的model来显示,这就是一个很典型的C++和QML分工合作的体现。
下面就具体来看看是如何使用的
C++的model需要继承自QAbstractListModel,并重写QAbstractListModel的虚函数
#include
class DataEntryModel : public QAbstractListModel
{
Q_OBJECT
public:
explicit DataEntryModel(QObject *parent = nullptr);
~DataEntryModel();
public: // QAbstractItemModel interface
int rowCount(const QModelIndex &parent) const override;
QVariant data(const QModelIndex &index, int role) const override;
private:
QList m_data;
};
#include
DataEntryModel::DataEntryModel(QObject *parent):
QAbstractListModel(parent)
{
// initialize our data (QList) with a list of color names
m_data = QColor::colorNames();
}
DataEntryModel::~DataEntryModel()
{
}
int DataEntryModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent);
// return our data count
return m_data.count();
}
QVariant DataEntryModel::data(const QModelIndex &index, int role) const
{
// the index returns the requested row and column information.
// we ignore the column and only use the row information
int row = index.row();
// boundary check for the row
if(row < 0 || row >= m_data.count()) {
return QVariant();
}
// A model can return data for different roles.
// The default role is the display role.
// it can be accesses in QML with "model.display"
switch(role) {
case Qt::DisplayRole:
// Return the color name for the particular row
// Qt automatically converts it to the QVariant type
return m_data.value(row);
}
// The view asked for other data, just return an empty QVariant
return QVariant();
}
该例子只做基本的显示,维护一个QList
注册QML类型:
qmlRegisterType("org.example", 1, 0, "DataEntryModel");
QML调用:
import QtQuick 2.15
import QtQuick.Window 2.15
import org.example 1.0
Window {
width: 640
height: 480
visible: true
title: qsTr("Hello World")
ListView {
id: view
anchors.fill: parent
model: DataEntryModel {}
delegate: Text {
// use the defined model role "display"
text: model.display
}
}
}
运行结果:
#include
class ModelData;
class DataListModel : public QAbstractListModel
{
Q_OBJECT
public:
explicit DataListModel(QObject *parent = nullptr);
enum AnimalRoles {
TypeRole = Qt::UserRole + 1,
SizeRole1
};
int rowCount(const QModelIndex & parent = QModelIndex()) const override;
QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
Q_INVOKABLE void insert(int index, const ModelData &data);
Q_INVOKABLE void append(const ModelData &data);
Q_INVOKABLE void remove(int index);
Q_INVOKABLE void append(const QVariantMap map);
signals:
void countChanged(int arg);
private:
int count() const;
protected:
QHash roleNames() const override;
private:
QList m_list;
};
class ModelData
{
public:
ModelData(const QString &type, const QString &size):
m_type(type), m_size(size)
{
}
QString type() const {return m_type;}
QString size() const {return m_size;}
void setType(const QString &type) {m_type = type;}
void setSize(const QString & size) {m_size = size;}
private:
QString m_type;
QString m_size;
};
代码中可以看到维护了一个自定义类型的list-QList
DataListModel::DataListModel(QObject *parent):
QAbstractListModel(parent)
{
}
int DataListModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent);
return m_list.count();
}
QVariant DataListModel::data(const QModelIndex &index, int role) const
{
if (index.row() < 0 || index.row() >= m_list.count())
return QVariant();
const ModelData &data = m_list[index.row()];
// qDebug() << "row: " << index.row();
if (role == TypeRole)
return data.type();
else if (role == SizeRole1)
return data.size();
return QVariant();
}
bool DataListModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (index.row() < 0 || index.row() >= m_list.count())
return false;
ModelData &data = m_list[index.row()];
if (role == TypeRole)
data.setType(value.toString());
else if (role == SizeRole1)
data.setSize(value.toString());
QVector roles = {role};
emit dataChanged(index, index, roles);
return true;
}
void DataListModel::insert(int index, const ModelData &data)
{
if(index < 0 || index > m_list.count()) {
return;
}
beginInsertRows(QModelIndex(), index, index);
m_list.insert(index, data);
endInsertRows();
emit countChanged(m_list.count());
}
void DataListModel::append(const ModelData &data)
{
insert(count(), data);
}
void DataListModel::remove(int index)
{
if(index < 0 || index >= m_list.count()) {
return;
}
beginRemoveRows(QModelIndex(), index, index);
m_list.removeAt( index );
endRemoveRows();
emit countChanged(m_list.count());
}
void DataListModel::append(const QVariantMap map)
{
QString type = map["type"].toString();
QString size = map["size"].toString();
ModelData data(type, size);
insert(count(), data);
}
int DataListModel::count() const
{
return rowCount(QModelIndex());
}
QHash DataListModel::roleNames() const
{
QHash roles;
roles[TypeRole] = "type";
roles[SizeRole1] = "size";
return roles;
}
QML使用
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
import org.example 1.0
Window {
width: 640
height: 480
visible: true
title: qsTr("Hello World")
DataListModel {
id: myModel
Component.onCompleted: {
//增加数据
myModel.append({"type": "11","size": "222"})
myModel.append({"type": "222","size": "333"})
myModel.append({"type": "333","size": "444"})
}
}
ListView {
id: listview
clip: true
width: parent.width
height: parent.height
model: myModel
delegate: Item {
id: delegate
width: listview.width
height: 30
Row {
spacing: 5
Text {
//取值
text: type
}
Text {
//取值
text: size
color: "red"
}
Button {
height: parent.height
onClicked: {
//修改值
size = "0000"
}
}
}
}
}
}
可以看到取值是通过type、size就能拿到数据,这些关键词是在roleNames()中定义的,猜测应该是通过QHash
以上就是QML显示C++model的基本用法介绍,QML book关于这方面的介绍点这里查看。下一篇将介绍C++model的一些高级用法,如过滤排序撤销。
Demo github地址:GitHub - a137748099/QMLModelView: c++ model with QML view