动态分配Q_Property变量

Dynamic assignment of Q_Property variables

本文关键字:变量 Property 动态分配      更新时间:2023-10-16

所以情况是这样的

QQmlApplicationEngine* engine;
QQmlContext* ctxt;
class Fruit: public QObject
{
Q_OBJECT
Q_PROPERTY(QString type MEMBER m_type NOTIFY fruitChanged)
private:
QString m_type;
public:
virtual void initFruit() = 0;
void startEating(){
initFruit();
ctxt->setContextProperty("fruit", this);
engine->load(QUrl(QLatin1String("qrc:/qml/main.qml"))); 
app.exec();
engine->quit();
engine->deleteLater();
}
void setType(QString fruit){
m_type = fruit;
emit fruitChanged();
}
signals:
fruitChanged();
};
class Apple:public Fruit{
public:
explicit Appl(QObject *parent = nullptr);
void initFruit(){
Slice slice = new Slice();    // some other class
ctxt->setContextProperty("slice", slice);
}
};
class Banana:public Fruit{
public:
explicit Banana(QObject *parent = nullptr);
void initFruit(){
Peel peel = new Peel();      // some other class
ctxt->setContextProperty("peel", peel);
}
};
class Peel: public QObject
{
Q_OBJECT
Q_PROPERTY(QString color MEMBER m_peelColor NOTIFY peelChanged)
private:
QString m_peelColor;
signals:
void peelChanged();
public:
void someFunction(QString peelColor){
m_peelColor = peelColor;
emit peelChanged();
}
}
class Slice: public QObject
{
Q_OBJECT
Q_PROPERTY(int number MEMBER m_slices NOTIFY sliceChanged)
private:
QString m_slices;
signals:
void sliceChanged();
public:
void someFunction(QString slices){
m_slices = slices;
emit sliceChanged();
}
}
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
Fruit *fruit;
if (argv[1] == "Apple"){
fruit = new Apple();
}
else if (argv[1] == "Banana"){
fruit = new Banana();
}
engine = new QQmlApplicationEngine();
ctxt = engine->rootContext();
fruit->setType(argv[1].toString());
fruit->startEating();
}

然后在main.qml中

...
Rectangle{
Text {
text: {    // I want to remove this if else statement
if(fruit.type == "Apple")
slice.number
else if(fruit.type == "Banana") 
peel.color
}
}
}

你可以想象,当我增加水果的数量时,我在main.qml中的if-else语句会变得多大。所以我想知道是否有一种不同的方法来处理这种情况,我可以以某种方式为 qml 中的文本分配一个值,然后它动态加载正确的后端属性。

与其在 initFruit 方法中注册新的数据类型并使用全局变量,不如创建一个提供程序类:

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <cstring>
class Fruit: public QObject
{
Q_OBJECT
public:
virtual Q_INVOKABLE QString name() = 0;
};
class Apple: public Fruit{
public:
QString name(){
return "apple";
}
};
class Banana: public Fruit{
public:
QString name(){
return "banana";
}
};
class FruitProvider: public QObject{
Q_OBJECT
Q_PROPERTY(Fruit* fruit READ fruit WRITE setFruit NOTIFY fruitChanged)
public:
FruitProvider(QObject *parent=nullptr):QObject(parent){}
Fruit* fruit() const{
return m_fruit.get();
}
public slots:
void setFruit(Fruit* fruit){
m_fruit.reset(fruit);
emit fruitChanged();
}
Q_SIGNALS:
void fruitChanged();
private:
QScopedPointer<Fruit> m_fruit;
};
int main(int argc, char *argv[]){
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
FruitProvider provider;
if(argc < 1){
return -1;
}
if (std::strcmp(argv[1], "Apple") == 0){
provider.setFruit(new Apple());
}
else if (std::strcmp(argv[1], "Banana") == 0){
provider.setFruit(new Banana());
}
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("provider", &provider);
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
return app.exec();
}
#include "main.moc"
Text{
text: provider.fruit.name()
}

更新:

正如我在评论中指出的那样,消除if-elseif-else的想法是标准化对属性的访问,在水果的情况下,您可以使用提供者。对于"数字"和"颜色"属性,您可以创建一个通用属性,该属性在每次修改其他属性时更新,并使用该属性访问 QML 中的信息。

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <cstring>
class Peel: public QObject{
Q_OBJECT
Q_PROPERTY(QString color MEMBER m_peelColor NOTIFY peelChanged)
private:
QString m_peelColor;
signals:
void peelChanged();
public:
void someFunction(QString peelColor){
m_peelColor = peelColor;
emit peelChanged();
}
};
class Slice: public QObject{
Q_OBJECT
Q_PROPERTY(int number MEMBER m_slices NOTIFY sliceChanged)
private:
int m_slices;
signals:
void sliceChanged();
public:
void someFunction(int slices){
m_slices = slices;
emit sliceChanged();
}
};
class Fruit: public QObject{
Q_OBJECT
Q_PROPERTY(QVariant common MEMBER m_common NOTIFY commonChanged)
public:
void startEating(){
initFruit();
}
QVariant common() const{
return m_common;
}
Q_SIGNALS:
void commonChanged();
protected:
virtual void  initFruit() = 0;
void setCommon(QVariant common){
m_common = common;
emit commonChanged();
}
private:
QVariant m_common;
};
class Apple: public Fruit{
public:
void initFruit(){
slice.reset(new Slice());
slice->someFunction(100);
setCommon(slice->property("number"));
connect(slice.get(), &Slice::sliceChanged, [this](){
setCommon(slice->property("number"));
});
}
private:
QScopedPointer<Slice> slice;
};
class Banana: public Fruit{
public:
void initFruit(){
peel.reset(new Peel());
peel->someFunction("red");
setCommon(peel->property("color"));
connect(peel.get(), &Peel::peelChanged, [this](){
setCommon(peel->property("color"));
});
}
private:
QScopedPointer<Peel> peel;
};
class FruitProvider: public QObject{
Q_OBJECT
Q_PROPERTY(Fruit* fruit READ fruit WRITE setFruit NOTIFY fruitChanged)
public:
Fruit* fruit() const{
return m_fruit.get();
}
public slots:
void setFruit(Fruit* fruit){
m_fruit.reset(fruit);
emit fruitChanged();
}
Q_SIGNALS:
void fruitChanged();
private:
QScopedPointer<Fruit> m_fruit;
};
int main(int argc, char *argv[]){
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
FruitProvider provider;
if(argc < 1){
return -1;
}
if (std::strcmp(argv[1], "Apple") == 0){
provider.setFruit(new Apple());
}
else if (std::strcmp(argv[1], "Banana") == 0){
provider.setFruit(new Banana());
}
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("provider", &provider);
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
provider.fruit()->startEating();
return app.exec();
}
#include "main.moc"
Text{
text: provider.fruit.common
}