2017.5.8 更新:Record類要用指針,QObject 不能有拷貝函數。
我有一個C++中自定義的ReaderModel,繼承自QAbstractListModel類,傳遞給了QML。
它的me成員是一個Reader指針,Reader有個成員是RecordModel。
通過reader獲取的recordModel,在qml中類型是QVariant(RecordModel),我沒法把它作為一個ListView的model。
要怎么讓它綁定給view呢?
我嘗試者把數據拷貝到一個直接傳給qml的recordModel,但是當數據之后發生了變化時,視圖就不會更新,除非再次拷貝,這樣效率不可觀。
通過艱難地google查找相關問題,我最后的解決方案是:
取消這個RecordModel成員,用QVariantList來儲存所有record。
簡單地說就是傳遞自定義類中的自定義結構體數組。
作為解決方案的代碼(如果不需要,完全可以不用ReaderModel,但是要用setContextProperty把reader變量傳給qml):
record.h
#ifndef RECORD_H
#define RECORD_H
#include <QObject>
class Record: public QObject
{
Q_OBJECT
Q_PROPERTY(QString bookId READ bookId WRITE setBookId NOTIFY bookIdChanged)
Q_PROPERTY(int state READ state WRITE setState NOTIFY stateChanged)
public:
Record(const QString &bookId="",int state=0):
bookId_(bookId),state_(state){}
Record(const Record &r){
bookId_ = r.bookId_;
state_ = r.state_;
}
QString bookId() const;
int state() const;
public slots:
void setBookId(const QString &);
void setState(int);
private:
QString bookId_;
int state_;
signals:
void bookIdChanged();
void stateChanged();
};
Q_DECLARE_METATYPE(Record*)//元類型注冊
#endif // RECORD_H
reader.h
#ifndef READER_H
#define READER_H
#include <QObject>
#include <QVariantList>
class Reader: public QObject
{
Q_OBJECT
Q_PROPERTY(QString id READ id WRITE setId NOTIFY idChanged)
Q_PROPERTY(QString password READ password WRITE setPassword NOTIFY passwordChanged)
Q_PROPERTY(QVariantList record READ record WRITE setRecord NOTIFY recordChanged)
public:
Reader(const QString &id = "",
const QString &password = "")
: id_(id),password_(password){}
QString id() const; //id
QString password() const; //密碼
QVariantList record() const; //記錄
Q_INVOKABLE void doSomething()const;
public slots:
void setId(const QString &);
void setPassword(const QString &);
void setRecord(const QVariantList &);
private:
QString id_;
QString password_;
QVariantList record_;//借書記錄
signals:
void idChanged();
void passwordChanged();
void recordChanged();
};
#endif // READER_H
readerModel.h
#ifndef READERMODEL_H
#define READERMODEL_H
#include <QAbstractListModel>
#include <QJsonValueRef>
#include <QVariant>
#include "reader.h"
#include "record.h"
class ReaderModel : public QAbstractListModel
{
Q_OBJECT
Q_PROPERTY(Reader* me READ me WRITE setMe NOTIFY meChanged)
public:
enum ReaderRole {
IdRole = Qt::DisplayRole, //0
PasswordRole = Qt::UserRole,
RecordRole
};
Q_ENUM(ReaderRole)
ReaderModel(QObject *parent = nullptr){Q_UNUSED(parent);}
int rowCount(const QModelIndex & = QModelIndex()) const;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
QHash<int, QByteArray> roleNames() const;
Q_INVOKABLE QVariantMap get(int row) const;
Q_INVOKABLE void append(const QString &id,
const QString &password);
Q_INVOKABLE void set(int row, const QString &id,
const QString &password);
Q_INVOKABLE void remove(int row);
Q_INVOKABLE Reader *me() const;//當前登錄的用戶對象指針
Q_INVOKABLE void setMe(Reader *r);
private:
QList<Reader*> m_readers;
Reader *me_;
signals:
void meChanged();
};
#endif // READERMODEL_H
reader.cpp
#include "reader.h"
#include "record.h"
#include <QVariant>
QString Reader::id() const
{
return id_;
}
QString Reader::password() const
{
return password_;
}
QVariantList Reader::record() const
{
return record_;
}
void Reader::setId(const QString &value)
{
if (id_ == value)
return;
id_ = value;
emit idChanged();
}
void Reader::setPassword(const QString &value)
{
if (password_ == value)
return;
password_ = value;
emit passwordChanged();
}
void Reader::setRecord(const QVariantList &value)
{
if (record_ == value)
return;
record_ = value;
emit recordChanged();
}
record.cpp
#include "record.h"
QString Record::bookId() const
{
return bookId_;
}
int Record::state() const
{
return state_;
}
void Record::setBookId(const QString &value)
{
if(bookId_ == value)
return;
bookId_ = value;
emit bookIdChanged();
}
void Record::setState(int value)
{
if(state_ == value)
return;
state_ = value;
emit stateChanged();
}
readerModel.cpp
#include "readermodel.h"
#include "reader.h"
int ReaderModel::rowCount(const QModelIndex & /*parent*/) const
{
return m_readers.count();
}
QVariant ReaderModel::data(const QModelIndex &index, int role) const
{
if(index.row() < rowCount())
switch(role){
case IdRole: return m_readers.at(index.row())->id();
case PasswordRole: return m_readers.at(index.row())->password();
case RecordRole: return m_readers.at(index.row())->record();
default: return QVariant();
}
return QVariant();
}
QHash<int, QByteArray> ReaderModel::roleNames() const
{
static const QHash<int, QByteArray> roles{
{IdRole, "id"},
{PasswordRole, "password"},
{RecordRole, "record"}
};
return roles;
}
QVariantMap ReaderModel::get(int row) const
{
Reader *reader = m_readers.value(row);
return { {"id", reader->id()},
{"password", reader->password()},
{"record", reader->record()}};
}
void ReaderModel::append(const QString &id, const QString &password)
{
int row = m_readers.count();
beginInsertRows(QModelIndex(), row, row);
Reader *r = new Reader(id, password, name, power, school, credit, money, unback);
m_readers.append(r);
endInsertRows();
}
void ReaderModel::set(int row, const QString &id, const QString &password)
{
if (row < 0 || row >= m_readers.count())
return;
Reader *r = new Reader(id, password == ""? m_readers[row]->password() : password);
m_readers.replace(row, r);
dataChanged(index(row, 0), index(row, 0), { IdRole,PasswordRole});
}
void ReaderModel::remove(int row)
{
if (row < 0 || row >= m_readers.count())
return;
beginRemoveRows(QModelIndex(), row, row);
m_readers.removeAt(row);
endRemoveRows();
}
Reader *ReaderModel::me() const
{
return me_;
}
void ReaderModel::setMe(Reader *r)
{
me_ = r;
emit meChanged();
}
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QtQml>
#include <QQmlContext>
#include "readermodel.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
//向qml注冊類型
qmlRegisterType<ReaderModel>("Backend", 1, 0, "ReaderModel");
ReaderModel *readerModel = new ReaderModel();
...//寫入數據
QQmlApplicationEngine *engine = new QQmlApplicationEngine();
//向qml傳遞變量
engine->rootContext()->setContextProperty("readerModel", readerModel);
engine->load(QUrl(QLatin1String("qrc:/main.qml")));
return app.exec();
}
mail.qml:
ListView {
visible: true
id: recordView
width: parent.width
height: parent.height
model: readerModel.me.record
delegate:Rectangle{
property var record: readerModel.me.record[index]
RowLayout{
spacing: 10
Label {
text: record.bookId
}
Label {
text: record.state
}
}
Component.onCompleted: {
console.log(readerModel.me.record)
console.log("\n",readerModel.me.record[index])
console.log("\n",readerModel.me.record[index].state)
}
}
}
參考:[SOLVED] Cascaded QVariantList exposed to QML causes Error: Cannot assign [undefined] to QString