【问题标题】:How to create and use C++ objects in QML Javascript如何在 QML Javascript 中创建和使用 C++ 对象
【发布时间】:2014-07-18 09:14:15
【问题描述】:

我的应用同时使用 c++ 和 QML。

我在 C++ 部分定义了几个对象来访问 SQL 等。

看起来像:

class MyObject : public QObject
{
    Q_OBJECT
public:
    MyObject(QObject *parent = 0);
    Q_INVOKABLE void someFunction(const QString &query);
};

qmlRegisterType<MyObject>("xxx.xxx", 1, 0, "MyObject");

理想情况下,我只需要在 Javascript 而不是 QML 中使用这些对象。

我尝试了很多示例并阅读了所有文档,但仍然无法解决我的问题。

所以我的问题:

  • 如何在 Javascript 中实例化在 C++ 中定义的对象?我试过var obj = Qt.createComponent("MyObject");,但它似乎不起作用。是否可以以普通 JS 样式定义新对象 - var obj = new MyObject;?
  • 如何在 javascript 中访问这个创建的对象?我尝试了 obj.someFunction("xxx") 但出现了一些错误 - TypeError: Property 'someFunction' of object QQmlComponent(0x3605f5c0) is not a function. 我在这里做错了什么?我的对象派生自 QObject,而不是 QQmlComponent。

【问题讨论】:

  • 有人回答了这个问题,它出现在我的列表中。 :) 在这里使用context property 不是一个好的解决方案?我能看到的唯一限制是它不能随心所欲地实例化,而是在主 QML 文件加载之前。

标签: javascript qml qt5 qt-quick qtquick2


【解决方案1】:

文档很清楚,但混淆看起来像是在等式的 QML 方面。这应该可以帮助您开始:

//C++
class MyObject : public QObject
{
    Q_OBJECT
public:
    MyObject(QObject *parent = 0);
    Q_INVOKABLE void someFunction(const QString &query) { qDebug() << query;}
};
....
qmlRegisterType<MyObject>("foo.bar", 1, 0, "MyObject");

QML 如下:

import foo.bar 1.0 //This is your register type
Item {
  MyObject { //here's the instance, remember it is declarative
    id: myObject;
  }
  MyObject {
    id: myObjectInstance2
  }
  Button {
    onClicked: {
      myObject.someFunction("doSomething"); //here is using a reference
      myObjectInstance2.someFunction("doSomethingElse");
    }
  }
}

单击后,您应该会在输出中看到字符串(我没有编译或测试它)。请务必在您的主类中注册类型。

如果您在移动设备上使用 SQL,则应该检查本地存储对象。这是一个非常简单的回调 API,适用于 SQLite。我将它用于桌面应用程序并且没有太多麻烦。返回列表有点烦人,所以尽量坚持简单的类型以便于 JavaScript 集成。

我希望这会有所帮助。我非常喜欢在 QML 中工作,一旦你学会了它就会很有趣(1-2 周足以熟练地工作)。

【讨论】:

  • 感谢@Daniel B. Chapman,但这并不是我所需要的。我对 QML 语法非常熟悉,所以我问了比 JS 怎么做的问题。我的对象是 ActiveRecord。实际上它适用于 Postgresql。该对象没有视觉表示,因此我看不到将其嵌入到 QML 树中。它可以多次销毁或创建,也可以在某个步骤中更改。我需要它,就像 Qt.someFunction() 或 Math.someFunction() 一样。
  • 您的问题是您需要将您的类实例化为组件实例。使用这种方法,您需要将(据我所知)附加到树上,以便可以引用它。本质上是var comp = Qt.create component('myQmlFile.qml'); var instance = comp.createObject(parent);,它只是动态地声明这一点。我应该注意到静态方法是可能的(这是我上次检查的功能请求),但后来我发现的解决方案是创建一个实例(动态或非动态),然后使用那里的方法。
  • 呃,非常非常糟糕!据我了解,不可能将 c++ 对象作为 JS 库公开给 QML。使用如此原始的技术是个坏主意。我的新项目以此为基础,现在我完全混淆了所有代码的用途。 (
  • 这不是全部,您可以公开它。您需要了解 JavaScript 方面需要一个起点。我一直在 QML 中使用 C++
  • 我有一个很类似的问题,请你看看:stackoverflow.com/questions/39379345/…
【解决方案2】:

您的对象不是Component,但您可以改用Qt.createQmlObject。

【讨论】:

  • 父母应该是谁?还是应该在使用结束时动态删除它?
  • QML 中的无父对象由垃圾收集器管理,除非另有特别标记。请注意,QObject 父项和(可视)父项之间存在差异。
【解决方案3】:

你可以使用

QQmlApplicationEngine engine;
engine.globalObject().setProperty("CppCreator", engine.newQObject(&CppCreator::GetInstance()));

CppCreator 是一个用于创建其他 c++ 对象的 QObject

Q_INVOKABLE QObject* Create(const QString& type_name);

然后就可以像在qml js中创建c++对象了

var test = CppCreator.Create("Your Type");

它并不完美,但满足了我的要求。希望对你有帮助。

【讨论】:

    【解决方案4】:

    这是一个带有想象的TextFile 类的示例。首先,我们需要一个已经建议的工厂类:

    // Factory class
    class Creator : public QObject
    {
        Q_OBJECT
        public:
        Q_INVOKABLE QObject* createObject(const QString& typeName, const QVariantMap& arguments);
    };
    
    QObject* Creator::createObject(const QString& typeName, const QVariantMap& arguments)
    {
        if (typeName == "TextFile")
        {
            QString filePath = arguments.value("filePath").toString();
            TextFile::OpenMode openMode =
                    qvariant_cast<TextFile::OpenMode>(arguments.value("openMode", TextFile::ReadWrite));
            QString codec = arguments.value("codec", "UTF-8").toString();
            return new TextFile(qmlEngine(this), filePath, openMode, codec);
        }
    
        Q_ASSERT(false);
        return nullptr;
    }
    

    注意:这个类比必要的要复杂一些。它应该创建多种类型。现在我们已经有了工厂类,我们需要告诉 QML/QJSEngine 在为 TextFile 调用 new 运算符时要做什么。

        QJSValue creator = engine.newQObject(new Creator());
        engine.globalObject().setProperty("_creator", creator);
        engine.evaluate("function TextFile(path, mode) { return _creator.createObject(\"TextFile\", { filePath: path, openMode: mode }); }");
    

    现在我们可以根据需要实例化我们的TextFile,即使带有参数:

    var myFile = new TextFile("/path/to/file", TextFile.ReadWrite);
    

    致谢this answer 的作者。

    【讨论】:

      猜你喜欢
      • 2019-03-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-06-14
      • 2017-01-16
      相关资源
      最近更新 更多