【发布时间】:2016-12-26 20:02:15
【问题描述】:
我想使用方括号通过点表示法从 QML 访问我的 App 的 C++ 类,例如:
myapp.object1.child_object2["key1"].grandchild_object1.property56
myapp.object2.child_object8[291045].grandchild_object4.property14
我希望能够将这些 (C++) 对象绑定到 QML 属性或在 Javascript 代码中使用它们。什么是快速而正确的方法来做到这一点?
例如,假设 MyApp 有一个 Users 对象,它是一个 QAbstractItemModel 类,它有一个用户的 QMap()。此 QMap 中的每个条目都有一个电子邮件地址作为键,以及一个包含用户属性(如电子邮件、姓名等)的 User() 对象......因此,在这种情况下,点符号将是:
myapp.users["johnsmith@domain.com"].name
myapp.users["johnsmith@domain.com"].password
稍后,我会让它变得更复杂,例如为 JohnSmith 的任务添加带有 QMap() 对象的第二级层次结构:
myapp.users["johnsmith@domain.com"].tasks[1].task_name
myapp.users["johnsmith@domain.com"].tasks[2].task_name
我应该使用什么机制以 HIERARCHICAL 点符号形式公开我的 C++ 对象?我了解 PROPERTY 宏在 Qt 中的工作原理,但如何使其适用于分层点符号?我应该在 QAbstractItemModel 类的子类中使用其他方法还是应该重载 [] 运算符?这是否可能(完全)在 QML 引擎中重载 [] 运算符?而且,最后一个问题,如果此时 Qt 中的分层点表示法是不可能的,是否很难通过修改源代码来实现?
这是我的源代码,到目前为止:
// File: main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include "myapp.h"
#include "users.h"
#include "user.h"
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
qmlRegisterType<MyApp>("com.myapp",1,0,"MyApp");
qmlRegisterType<Users>("com.myapp.users",1,0,"Users");
qmlRegisterType<User>("com.myapp.user",1,0,"User");
QQmlApplicationEngine engine;
engine.load(QUrl(QLatin1String("qrc:/main.qml")));
return app.exec();
}
MyApp 类。 标题:
//File: myapp.h
#ifndef MYAPP_H
#define MYAPP_H
#include <QObject>
#include <QQmlListProperty>
#include "users.h"
class MyApp : public QObject
{
Q_OBJECT
Q_PROPERTY(Users *users READ get_users WRITE set_users NOTIFY usersChanged);
private:
Users m_users;
public:
explicit MyApp(QObject *parent = 0);
Q_INVOKABLE Users *get_users();
void set_users(Users *data);
signals:
void usersChanged();
};
#endif // MYAPP_H
C源码:
//File: myapp.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include "myapp.h"
#include "users.h"
#include "user.h"
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
qmlRegisterType<MyApp>("com.myapp",1,0,"MyApp");
qmlRegisterType<Users>("com.myapp.users",1,0,"Users");
qmlRegisterType<User>("com.myapp.user",1,0,"User");
QQmlApplicationEngine engine;
engine.load(QUrl(QLatin1String("qrc:/main.qml")));
return app.exec();
}
用户文件。 标题:
//File: users.h
#ifndef USERS_H
#define USERS_H
#include <QObject>
#include <QAbstractItemModel>
#include <QMap>
#include "user.h"
class Users : public QAbstractItemModel
{
Q_OBJECT
enum UserRoles {
EmailRole = Qt::UserRole + 1,
NameRole,
PasswordRole
};
private:
QMap<QString,User*> users_map;
public:
explicit Users(QAbstractItemModel *parent = 0);
Q_INVOKABLE QModelIndex index(int row, int column,const QModelIndex &parent = QModelIndex()) const;
Q_INVOKABLE QModelIndex parent(const QModelIndex &child) const;
Q_INVOKABLE int rowCount(const QModelIndex &parent = QModelIndex()) const;
Q_INVOKABLE int columnCount(const QModelIndex &parent = QModelIndex()) const;
Q_INVOKABLE QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
QHash<int, QByteArray> roleNames() const;
signals:
public slots:
};
#endif // USERS_H
来源:
//File: users.cpp
#include <QDebug>
#include "users.h"
Users::Users(QAbstractItemModel *parent) : QAbstractItemModel(parent)
{
User *u;
u=new User();
u->set_email("johnsmith@domain.com");
u->set_name("John Smith");
u->set_password("123");
users_map.insert(u->get_email(),u);
u=new User();
u->set_email("juliepage@domain.com");
u->set_name("Julie Page");
u->set_password("321");
users_map.insert(u->get_email(),u);
}
QModelIndex Users::parent(const QModelIndex &child) const {
return QModelIndex();
}
QModelIndex Users::index(int row, int column,const QModelIndex &parent) const {
QList <QString> qlist;
qlist=users_map.keys();
if (row>=qlist.size()) return QModelIndex();
User *user=users_map[qlist.at(row)];
return createIndex(row,column,user);
}
int Users::rowCount(const QModelIndex &parent) const {
return users_map.size();
}
int Users::columnCount(const QModelIndex &parent) const {
return 1;
}
QVariant Users::data(const QModelIndex &index, int role) const {
int row_num;
row_num=index.row();
if (role==EmailRole) {
QList <QString> qlist;
qlist=users_map.keys();
if (row_num>=qlist.size()) return (QVariant());
return QVariant(qlist.at(row_num));
}
if (role==NameRole) {
QList <QString> qlist;
qlist=users_map.keys();
if (row_num>=qlist.size()) return (QVariant());
User *user=users_map.value(qlist.at(row_num));
return QVariant(user->get_name());
}
if (role==PasswordRole) {
QList <QString> qlist;
qlist=users_map.keys();
if (row_num>=qlist.size()) return (QVariant());
User *user=users_map[qlist.at(row_num)];
return QVariant(user->get_password());
}
if (role==Qt::DisplayRole) {
return(QVariant());
}
return (QVariant());
}
QHash<int, QByteArray> Users::roleNames() const {
QHash<int, QByteArray> roles;
roles[EmailRole] = "email";
roles[NameRole] = "name";
roles[PasswordRole] = "password";
return roles;
}
用户文件。 标题:
//File user.h
#ifndef USER_H
#define USER_H
#include <QObject>
class User : public QObject
{
Q_OBJECT
Q_PROPERTY(QString email READ get_email WRITE set_email NOTIFY emailChanged);
Q_PROPERTY(QString name READ get_name WRITE set_name NOTIFY nameChanged);
Q_PROPERTY(QString password READ get_password WRITE set_password NOTIFY passwordChanged);
private:
QString email;
QString name;
QString password;
public:
explicit User(QObject *parent = 0);
const QString get_email();
void set_email(QString data);
const QString get_name();
void set_name(QString data);
const QString get_password();
void set_password(QString data);
signals:
void emailChanged();
void nameChanged();
void passwordChanged();
};
#endif // USER_H
来源:
//File: user.cpp
#include "user.h"
User::User(QObject *parent) : QObject(parent)
{
}
const QString User::get_email() {
return email;
}
void User::set_email(QString data) {
if (email!=data) {
email=data;
emit emailChanged();
}
}
const QString User::get_name() {
return name;
}
void User::set_name(QString data) {
if (name!=data) {
name=data;
emit nameChanged();
}
}
const QString User::get_password() {
return password;
}
void User::set_password(QString data) {
if (password!=data) {
password=data;
emit passwordChanged();
}
}
QML 文件。
//File: main.qml
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.0
import com.myapp 1.0;
import com.myapp.users 1.0;
import com.myapp.user 1.0;
ApplicationWindow {
visible: true; width:640; height: 480;
MyApp {
id:myapp
}
ListView {
model: myapp.users
width: 300; height: 300
delegate: ItemDelegate {
Text {
text: model.email
}
}
}
Component.onCompleted: {
var users=myapp.users;
var user=users["johnsmith@domain.com"];
console.log("users object=" + users);
console.log("user object=" + user);
console.log("user's name="+user.name);
}
}
项目文件。 //文件:QML1.pro
QT += qml quick
CONFIG += c++11
SOURCES += main.cpp \
myapp.cpp \
users.cpp \
user.cpp
RESOURCES += qml.qrc
# Additional import path used to resolve QML modules in Qt Creator's code model
QML_IMPORT_PATH =
# Default rules for deployment.
include(deployment.pri)
HEADERS += \
myapp.h \
users.h \
user.h
还有调试输出,当我运行程序时:
qml: users object=Users(0x15cd1b0)
qml: user object=undefined
qrc:/main.qml:28: TypeError: Cannot read property 'name' of undefined
如您所见,我无法越过 [] 方括号,QML 未检索到 User 对象。
【问题讨论】:
-
文字的圣墙!!!当提出问题时,尽量用最少的代码来呈现你的问题,这只是一个重现问题的简单例子,而不是你的整个生产代码,对那些试图提供帮助的人有怜悯之心。 NO - 你不能在 QML 中重载运算符,在 JS 中没有运算符重载,如果你想从 QML 访问功能,请使用插槽或可调用函数。
-
@ddriver,呵呵,这不是生产代码,它是一个示例,生产代码>25k 行。太糟糕了,重载 [] 运算符将是一个不错的功能。也许 var somevar=object.method().submethod().subsubmethod() 会起作用?
-
是的,只要所有对象都是
QObject派生的或通过Q_GADGET生成的元数据,并且成员是可调用的函数或属性。 QML 必须知道这些东西,而这些知识来自元信息的生成。顺便说一句,您代码中的大部分内容与主题完全无关。这对任何人都没有帮助,因此不是一个好例子。 -
@ddriver,但我必须复制 Users.cpp 和 User.cpp,否则您将无法理解我的对象是如何相互链接的。并且刚刚添加了其他文件以使其成为 100% 可编译的示例。
-
您可以轻松地用 20 行代码重现该问题。此外,在提供此类琐碎示例时,最好在现场实现函数,而不是拆分声明和定义,这样人们就可以一目了然地看到正在发生的事情,而不是四处滚动。