QML C++ 集成:查看包含其他 类 数组的动态 类 模型
QML C++ integration: View Models for dynamic classes containing arrays of other classes
我来自 C++,但我很难让我的 classes 按我希望的方式工作。我正在构建的程序相当简单。用户创建一两个相册并可以用卡片填充该相册。这些相册是在 运行 时间动态创建的。
这是我最初的 class 后端 C++ 内容图:
class diagram
下面是 UI 的样子:
QML UI
问题的关键:
必须对 classes 进行一系列更改才能使它们对 QML 可用。
- 继承:类需要根据角色继承自QObject或QAbstractListModel。
- 容器:对象中动态实例化的对象,无论是否通过指针,都需要放在 QList 或类似的东西中。
- 根上下文属性:模型需要注册,以便可以在列表视图中使用。
我已经成功地设置了一个专辑 class 的实例,并对其进行了一些更改,作为根上下文 属性。该模型工作正常,相册中的卡片在 ListView 中正确显示。
这里的问题是我需要跳出画面另一个层次,我应该设置一个包含相册的class作为根上下文属性并让用户创建,并且在 运行 时间内向其中添加专辑。我还需要通过不同的视图显示相册和其中的卡片。
我想知道我是否应该为相册和卡片实现一个 table/tree 数据结构,并以某种方式将列和行传递给模型,或者是否有我不知道的更简洁的方法。
这是我目前的class:
album.h
#ifndef ALBUM_H
#define ALBUM_H
#include <QObject>
#include <QString>
#include <QAbstractListModel>
#include <QQmlListProperty>
#include "card.h"
class Album : public QAbstractListModel
{
Q_OBJECT
public:
enum AlbumRoles {
CardIDRole = Qt::UserRole + 1,
NameRole,
ImageURLRole,
SubtypeRole,
SupertypeRole,
NumberRole,
ArtistRole,
RarityRole,
SeriesRole,
SetRole,
SetCodeRole,
ConditionRole,
StatusRole
};
Album(QObject *parent = 0);
QString name() const;
void addCard(const Card &card);
int rowCount(const QModelIndex & parent = QModelIndex()) const;
QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const;
protected:
QHash<int, QByteArray> roleNames() const;
private:
QList<Card> m_cards;
QString m_name;
};
album.cpp
#include "album.h"
Album::Album(QObject *parent)
: QAbstractListModel(parent)
{
}
QString Album::name() const
{
return m_name;
}
void Album::addCard(const Card &card)
{
beginInsertRows(QModelIndex(), rowCount(), rowCount());
m_cards << card;
endInsertRows();
}
int Album::rowCount(const QModelIndex & parent) const {
Q_UNUSED(parent);
return m_cards.count();
}
QVariant Album::data(const QModelIndex & index, int role) const {
if (index.row() < 0 || index.row() >= m_cards.count())
return QVariant();
const Card &card = m_cards[index.row()];
if (role == CardIDRole)
return card.cardID();
else if (role == NameRole)
return card.name();
else if (role == ImageURLRole)
return card.imageURL();
else if (role == SubtypeRole)
return card.subtype();
else if (role == SupertypeRole)
return card.supertype();
else if (role == NumberRole)
return card.number();
else if (role == ArtistRole)
return card.artist();
else if (role == RarityRole)
return card.rarity();
else if (role == SeriesRole)
return card.series();
else if (role == SetRole)
return card.set();
else if (role == SetCodeRole)
return card.setCode();
else if (role == ConditionRole)
return card.condition();
else if (role == StatusRole)
return card.status();
return QVariant();
}
QHash<int, QByteArray> Album::roleNames() const {
QHash<int, QByteArray> roles;
roles[CardIDRole] = "cardID";
roles[NameRole] = "name";
roles[ImageURLRole] = "imageURL";
roles[SubtypeRole] = "subtype";
roles[SupertypeRole] = "supertype";
roles[NumberRole] = "number";
roles[ArtistRole] = "artist";
roles[RarityRole] = "rarity";
roles[SeriesRole] = "series";
roles[SetRole] = "set";
roles[SetCodeRole] = "setCode";
roles[ConditionRole] = "condition";
roles[StatusRole] = "status";
return roles;
}
card.h
#ifndef CARD_H
#define CARD_H
#include <QString>
class Card
{
public:
// Standard Qt constructor with parent for memory management
Card(const QString &cardID, const QString &name, const QString &imageURL, const QString &subtype, const QString &supertype, const int &number, const QString &artist, const QString &rarity, const QString &series, const QString &set, const QString &setCode, const QString &condition, const QString &status);
QString cardID() const;
QString name() const;
QString imageURL() const;
QString subtype() const;
QString supertype() const;
int number() const;
QString artist() const;
QString rarity() const;
QString series() const;
QString set() const;
QString setCode() const;
QString condition() const;
QString status() const;
private:
// private members
QString m_cardID;
QString m_name;
QString m_imageURL;
QString m_subtype;
QString m_supertype;
int m_number;
QString m_artist;
QString m_rarity;
QString m_series;
QString m_set;
QString m_setCode;
QString m_condition;
QString m_status;
};
#endif //CARD_H
card.cpp
#include "card.h"
// Standard Qt constructor with parent for memory management
Card::Card(const QString &cardID, const QString &name, const QString &imageURL, const QString &subtype, const QString &supertype, const int &number, const QString &artist, const QString &rarity, const QString &series, const QString &set, const QString &setCode, const QString &condition, const QString &status): m_cardID(cardID), m_name(name), m_imageURL(imageURL), m_subtype(subtype), m_supertype(supertype), m_number(number), m_artist(artist), m_rarity(rarity), m_series(series), m_set(set), m_setCode(setCode), m_condition(condition), m_status(status)
{
}
QString Card::cardID() const
{
return m_cardID;
}
QString Card::name() const
{
return m_name;
}
QString Card::imageURL() const
{
return m_imageURL;
}
QString Card::subtype() const
{
return m_subtype;
}
QString Card::supertype() const
{
return m_supertype;
}
int Card::number() const
{
return m_number;
}
QString Card::artist() const
{
return m_artist;
}
QString Card::rarity() const
{
return m_rarity;
}
QString Card::series() const
{
return m_series;
}
QString Card::set() const
{
return m_set;
}
QString Card::setCode() const
{
return m_setCode;
}
QString Card::condition() const
{
return m_condition;
}
QString Card::status() const
{
return m_status;
}
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QtQml>
#include "app.h"
#include "album.h"
#include "card.h"
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
qmlRegisterType<Album>("Classes.PokemonApp", 1, 0, "Album");
qmlRegisterType<Card>("Classes.PokemonApp", 1, 0, "Card");
App pokeApp;
Album myAlbumModel;
Card cardOne("xy7-2","Gloom","http://s3.amazonaws.com/pokemontcg/xy7/2.png","Stage 1","Pokémon",2,"Masakazu Fukuda","Uncommon","XY","Ancient Origins","xy7","Mint","In my collection");
Card cardTwo("xy7-7","Sceptile-EX","https://s3.amazonaws.com/pokemontcg/xy7/7.png","EX","Pokémon",7,"Eske Yoshinob","Rare Holo EX","XY","Ancient Origins","xy7","Used","Duplicate");
myAlbumModel.addCard(cardOne);
myAlbumModel.addCard(cardTwo);
pokeApp.addAlbum(myAlbumModel);
QQmlApplicationEngine engine;
engine.load(QUrl(QLatin1String("qrc:/main.qml")));
QQmlContext *ctxt = engine.rootContext();
ctxt->setContextProperty("pokeApp", &pokeApp);
return app.exec();
}
CardsView.qml
import QtQuick 2.0
import Classes.PokemonApp 1.0
// Cards Deligate
ListView {
width: 200; height: 250
model: myAlbumModel
delegate: Text { text: "Card:"
+ "\n" + "ID: " + cardID
+ "\n" + "Name: " + name
+ "\n" + "Image URL: " + imageURL
+ "\n" + "Subtype: " + subtype
+ "\n" + "Supertype: " + supertype
+ "\n" + "Number: " + number
+ "\n" + "Artist: " + artist
+ "\n" + "Rarity: " + rarity
+ "\n" + "Series: " + series
+ "\n" + "Set: " + set
+ "\n" + "Set code: " + setCode
+ "\n" + "Condition: " + condition
+ "\n" + "Status: " + status }
}
明确地说,这是我需要帮助的:
让用户能够在 运行 时间内创建相册,让用户将卡片添加到相册,并让模型以不同的视图显示相册和其中的卡片。
假设您的 App
class 有一个 Album
对象列表,可以作为 属性 访问,添加相册只是一个插槽或可调用方法class.
类似
public slots:
void addAlbum(const QString &name);
这将创建一个 Album 实例,将其放入列表中并发出 属性 的通知信号。
要添加 Card
,您可以向 Album
class 添加类似的插槽,例如
pulic slots:
void addCard(const QString &cardID, const QString &name, const QString &imageURL, const QString &subtype, const QString &supertype, const int &number, const QString &artist, const QString &rarity, const QString &series, const QString &set, const QString &setCode, const QString &condition, const QString &status);
这将简单地创建一个 Card
添加然后调用 addCard
重载。
我现在完成了这个项目,我已经完成了我的所有目标并克服了我在这里寻求帮助的问题以及后来出现的其他一些问题。
对于那些在搜索他们遇到的类似问题的解决方案时可能会找到此页面的人,您可以在我的 Github 页面上找到我完整项目的所有源代码:https://github.com/Nizars/PokeApp
我想指出我所做的一些更改:
我已经切换到 SQL 存储名片、相册和名片图像的方法。
我针对不同的视图使用了不同的模型。 table 视图使用的关系查询代理模型和其他功能的两个 sql 模型。
我还使用自定义图像提供程序首先检查数据库中从 qml 端请求的图像,然后再通过 https 从 API 请求图像,如果不是,则将其存储在数据库中起初在那里发现。
我还切换到无边框 window 以摆脱 windows 默认 window 边框,并创建了我自己的关闭、最小化和最大化按钮。我还使用了鼠标位置提供程序来使拖动 window 成为可能且无缝。
我还在主页上添加了一个有效的 RSS 提要,并对应用程序中的不同页面使用了页面滑动视图,我使用连接在不同的 qml 文档之间发出消息信号,以便可以在它们之间发送数据。
最后说明:您需要包含自己的 OpenSSL 库,以便网络请求正常工作,并在本地服务器中创建 sql tables,并将应用程序连接到它pokeapp.cpp。
如果您需要任何帮助,请随时问我任何问题。
我来自 C++,但我很难让我的 classes 按我希望的方式工作。我正在构建的程序相当简单。用户创建一两个相册并可以用卡片填充该相册。这些相册是在 运行 时间动态创建的。
这是我最初的 class 后端 C++ 内容图: class diagram
下面是 UI 的样子: QML UI
问题的关键: 必须对 classes 进行一系列更改才能使它们对 QML 可用。
- 继承:类需要根据角色继承自QObject或QAbstractListModel。
- 容器:对象中动态实例化的对象,无论是否通过指针,都需要放在 QList 或类似的东西中。
- 根上下文属性:模型需要注册,以便可以在列表视图中使用。
我已经成功地设置了一个专辑 class 的实例,并对其进行了一些更改,作为根上下文 属性。该模型工作正常,相册中的卡片在 ListView 中正确显示。
这里的问题是我需要跳出画面另一个层次,我应该设置一个包含相册的class作为根上下文属性并让用户创建,并且在 运行 时间内向其中添加专辑。我还需要通过不同的视图显示相册和其中的卡片。
我想知道我是否应该为相册和卡片实现一个 table/tree 数据结构,并以某种方式将列和行传递给模型,或者是否有我不知道的更简洁的方法。
这是我目前的class:
album.h
#ifndef ALBUM_H
#define ALBUM_H
#include <QObject>
#include <QString>
#include <QAbstractListModel>
#include <QQmlListProperty>
#include "card.h"
class Album : public QAbstractListModel
{
Q_OBJECT
public:
enum AlbumRoles {
CardIDRole = Qt::UserRole + 1,
NameRole,
ImageURLRole,
SubtypeRole,
SupertypeRole,
NumberRole,
ArtistRole,
RarityRole,
SeriesRole,
SetRole,
SetCodeRole,
ConditionRole,
StatusRole
};
Album(QObject *parent = 0);
QString name() const;
void addCard(const Card &card);
int rowCount(const QModelIndex & parent = QModelIndex()) const;
QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const;
protected:
QHash<int, QByteArray> roleNames() const;
private:
QList<Card> m_cards;
QString m_name;
};
album.cpp
#include "album.h"
Album::Album(QObject *parent)
: QAbstractListModel(parent)
{
}
QString Album::name() const
{
return m_name;
}
void Album::addCard(const Card &card)
{
beginInsertRows(QModelIndex(), rowCount(), rowCount());
m_cards << card;
endInsertRows();
}
int Album::rowCount(const QModelIndex & parent) const {
Q_UNUSED(parent);
return m_cards.count();
}
QVariant Album::data(const QModelIndex & index, int role) const {
if (index.row() < 0 || index.row() >= m_cards.count())
return QVariant();
const Card &card = m_cards[index.row()];
if (role == CardIDRole)
return card.cardID();
else if (role == NameRole)
return card.name();
else if (role == ImageURLRole)
return card.imageURL();
else if (role == SubtypeRole)
return card.subtype();
else if (role == SupertypeRole)
return card.supertype();
else if (role == NumberRole)
return card.number();
else if (role == ArtistRole)
return card.artist();
else if (role == RarityRole)
return card.rarity();
else if (role == SeriesRole)
return card.series();
else if (role == SetRole)
return card.set();
else if (role == SetCodeRole)
return card.setCode();
else if (role == ConditionRole)
return card.condition();
else if (role == StatusRole)
return card.status();
return QVariant();
}
QHash<int, QByteArray> Album::roleNames() const {
QHash<int, QByteArray> roles;
roles[CardIDRole] = "cardID";
roles[NameRole] = "name";
roles[ImageURLRole] = "imageURL";
roles[SubtypeRole] = "subtype";
roles[SupertypeRole] = "supertype";
roles[NumberRole] = "number";
roles[ArtistRole] = "artist";
roles[RarityRole] = "rarity";
roles[SeriesRole] = "series";
roles[SetRole] = "set";
roles[SetCodeRole] = "setCode";
roles[ConditionRole] = "condition";
roles[StatusRole] = "status";
return roles;
}
card.h
#ifndef CARD_H
#define CARD_H
#include <QString>
class Card
{
public:
// Standard Qt constructor with parent for memory management
Card(const QString &cardID, const QString &name, const QString &imageURL, const QString &subtype, const QString &supertype, const int &number, const QString &artist, const QString &rarity, const QString &series, const QString &set, const QString &setCode, const QString &condition, const QString &status);
QString cardID() const;
QString name() const;
QString imageURL() const;
QString subtype() const;
QString supertype() const;
int number() const;
QString artist() const;
QString rarity() const;
QString series() const;
QString set() const;
QString setCode() const;
QString condition() const;
QString status() const;
private:
// private members
QString m_cardID;
QString m_name;
QString m_imageURL;
QString m_subtype;
QString m_supertype;
int m_number;
QString m_artist;
QString m_rarity;
QString m_series;
QString m_set;
QString m_setCode;
QString m_condition;
QString m_status;
};
#endif //CARD_H
card.cpp
#include "card.h"
// Standard Qt constructor with parent for memory management
Card::Card(const QString &cardID, const QString &name, const QString &imageURL, const QString &subtype, const QString &supertype, const int &number, const QString &artist, const QString &rarity, const QString &series, const QString &set, const QString &setCode, const QString &condition, const QString &status): m_cardID(cardID), m_name(name), m_imageURL(imageURL), m_subtype(subtype), m_supertype(supertype), m_number(number), m_artist(artist), m_rarity(rarity), m_series(series), m_set(set), m_setCode(setCode), m_condition(condition), m_status(status)
{
}
QString Card::cardID() const
{
return m_cardID;
}
QString Card::name() const
{
return m_name;
}
QString Card::imageURL() const
{
return m_imageURL;
}
QString Card::subtype() const
{
return m_subtype;
}
QString Card::supertype() const
{
return m_supertype;
}
int Card::number() const
{
return m_number;
}
QString Card::artist() const
{
return m_artist;
}
QString Card::rarity() const
{
return m_rarity;
}
QString Card::series() const
{
return m_series;
}
QString Card::set() const
{
return m_set;
}
QString Card::setCode() const
{
return m_setCode;
}
QString Card::condition() const
{
return m_condition;
}
QString Card::status() const
{
return m_status;
}
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QtQml>
#include "app.h"
#include "album.h"
#include "card.h"
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
qmlRegisterType<Album>("Classes.PokemonApp", 1, 0, "Album");
qmlRegisterType<Card>("Classes.PokemonApp", 1, 0, "Card");
App pokeApp;
Album myAlbumModel;
Card cardOne("xy7-2","Gloom","http://s3.amazonaws.com/pokemontcg/xy7/2.png","Stage 1","Pokémon",2,"Masakazu Fukuda","Uncommon","XY","Ancient Origins","xy7","Mint","In my collection");
Card cardTwo("xy7-7","Sceptile-EX","https://s3.amazonaws.com/pokemontcg/xy7/7.png","EX","Pokémon",7,"Eske Yoshinob","Rare Holo EX","XY","Ancient Origins","xy7","Used","Duplicate");
myAlbumModel.addCard(cardOne);
myAlbumModel.addCard(cardTwo);
pokeApp.addAlbum(myAlbumModel);
QQmlApplicationEngine engine;
engine.load(QUrl(QLatin1String("qrc:/main.qml")));
QQmlContext *ctxt = engine.rootContext();
ctxt->setContextProperty("pokeApp", &pokeApp);
return app.exec();
}
CardsView.qml
import QtQuick 2.0
import Classes.PokemonApp 1.0
// Cards Deligate
ListView {
width: 200; height: 250
model: myAlbumModel
delegate: Text { text: "Card:"
+ "\n" + "ID: " + cardID
+ "\n" + "Name: " + name
+ "\n" + "Image URL: " + imageURL
+ "\n" + "Subtype: " + subtype
+ "\n" + "Supertype: " + supertype
+ "\n" + "Number: " + number
+ "\n" + "Artist: " + artist
+ "\n" + "Rarity: " + rarity
+ "\n" + "Series: " + series
+ "\n" + "Set: " + set
+ "\n" + "Set code: " + setCode
+ "\n" + "Condition: " + condition
+ "\n" + "Status: " + status }
}
明确地说,这是我需要帮助的: 让用户能够在 运行 时间内创建相册,让用户将卡片添加到相册,并让模型以不同的视图显示相册和其中的卡片。
假设您的 App
class 有一个 Album
对象列表,可以作为 属性 访问,添加相册只是一个插槽或可调用方法class.
类似
public slots:
void addAlbum(const QString &name);
这将创建一个 Album 实例,将其放入列表中并发出 属性 的通知信号。
要添加 Card
,您可以向 Album
class 添加类似的插槽,例如
pulic slots:
void addCard(const QString &cardID, const QString &name, const QString &imageURL, const QString &subtype, const QString &supertype, const int &number, const QString &artist, const QString &rarity, const QString &series, const QString &set, const QString &setCode, const QString &condition, const QString &status);
这将简单地创建一个 Card
添加然后调用 addCard
重载。
我现在完成了这个项目,我已经完成了我的所有目标并克服了我在这里寻求帮助的问题以及后来出现的其他一些问题。
对于那些在搜索他们遇到的类似问题的解决方案时可能会找到此页面的人,您可以在我的 Github 页面上找到我完整项目的所有源代码:https://github.com/Nizars/PokeApp
我想指出我所做的一些更改:
我已经切换到 SQL 存储名片、相册和名片图像的方法。
我针对不同的视图使用了不同的模型。 table 视图使用的关系查询代理模型和其他功能的两个 sql 模型。
我还使用自定义图像提供程序首先检查数据库中从 qml 端请求的图像,然后再通过 https 从 API 请求图像,如果不是,则将其存储在数据库中起初在那里发现。
我还切换到无边框 window 以摆脱 windows 默认 window 边框,并创建了我自己的关闭、最小化和最大化按钮。我还使用了鼠标位置提供程序来使拖动 window 成为可能且无缝。
我还在主页上添加了一个有效的 RSS 提要,并对应用程序中的不同页面使用了页面滑动视图,我使用连接在不同的 qml 文档之间发出消息信号,以便可以在它们之间发送数据。
最后说明:您需要包含自己的 OpenSSL 库,以便网络请求正常工作,并在本地服务器中创建 sql tables,并将应用程序连接到它pokeapp.cpp。
如果您需要任何帮助,请随时问我任何问题。