C++ 和 Qt/QML 数据交换的问题

huangapple go评论89阅读模式
英文:

Issues with C++ and Qt/QML data exchange

问题

以下是您要翻译的内容:

QML代码中为什么会出现这个错误:ReferenceError: m_jsonDocStr is not defined,即使在C++代码中已经定义了m_jsonDocStr

Q_PROPERTY(QString m_jsonDocStr READ jsonDocToStr WRITE setJsonDocStr NOTIFY jsonDocStrChanged)

为什么无法从QML中打开C++函数QmlLink.open()?(既不是作为普通成员函数,也不是作为槽函数)

TypeError: Property 'open' of object [object Object] is not a function

接下来,通过“<--”注释,我指出了代码中关键的行,并列出了错误消息。

这是QML文件:

/** Main.qml */

import QtCore
import QtQuick
import QtQuick.Window
import QtQuick.Controls
import QtQuick.Dialogs
import QtQuick.Layouts
import main

ApplicationWindow {
    id: window
    width: 640
    height: 480
    visible: true
    title: qsTr("Data input widget")

    property string dataFilename: ""
    property string dataFile: ""

    menuBar: MenuBar {
        Menu {
            title: qsTr("&File")
            Action {
                text: qsTr("&Open...")
                onTriggered: fileDialog.open()
            }
        }
    }

    ScrollView {
        id: fileContents
        anchors.fill: parent
        property string dataFile: m_jsonDocStr    // <-- ReferenceError: m_jsonDocStr is not defined
        TextArea {
            text: "File: " + dataFilename + "\n" + dataFile + "\nLength: " + dataFile.length
        }
    }

    FileDialog {
        id: fileDialog
        title: "Open data file"
        nameFilters: ["JSON files (*.json)"]
        onAccepted: {
            dataFilename = selectedFile
            console.log("Open file: " + dataFilename)
            QmlLink.open(dataFilename)        // <-- TypeError: Property 'open' of object [object Object] is not a function
            console.log("File contents: " + m_jsonDocStr)
        }
        onRejected: {
            console.log("No file selected.")
        }
    }
}

以下是头文件:

/** QmlLink.hpp */

#ifndef QMLLINK_HPP
#define QMLLINK_HPP

#include <QObject>
#include <QJsonDocument>
#include <QtQml/qqmlregistration.h>

class QmlLink : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QJsonDocument m_jsonDoc READ jsonDoc WRITE setJsonDoc NOTIFY jsonDocChanged)
    Q_PROPERTY(QString m_jsonDocStr READ jsonDocToStr WRITE setJsonDocStr NOTIFY jsonDocStrChanged)
    QML_ELEMENT

public:
    explicit QmlLink(QObject *parent = nullptr);

public slots:
    QJsonDocument jsonDoc() const { return m_jsonDoc; }
    void setJsonDoc(const QJsonDocument &jsonDoc) { m_jsonDoc = jsonDoc; }
    QString jsonDocToStr() const { return m_jsonDoc.toJson(); }
    void setJsonDocStr(const QString &jsonDoc) { m_jsonDoc = QJsonDocument::fromJson(jsonDoc.toUtf8()); }

signals:
    void jsonDocChanged();
    void jsonDocStrChanged();

public slots:
    void menuOpen();
    int open(QString filePath);

private:
    QJsonDocument m_jsonDoc;
    QString m_jsonDocStr;

    QString m_jsonFileName = "default.json";
};

#endif // QMLLINK_HPP

以下是实现文件:

/** QmlLink.cpp */

#include "QmlLink.hpp"
#include <iostream>
#include <QDebug>
#include <QFile>
#include <QQmlContext>
#include <QQmlEngine>
#include <QQuickView>

QmlLink::QmlLink(QObject *parent) : QObject{parent} {}

void QmlLink::menuOpen() {
    open(m_jsonFileName);
}

int QmlLink::open(QString filePath)
{
    m_jsonFileName = filePath;

    QUrl url(filePath);
    QFile jsonFile;
    jsonFile.setFileName(url.toLocalFile());

    if(jsonFile.open(QIODevice::ReadOnly) == false) {
        return -1;
    }

    const QByteArray data = jsonFile.readAll();
    m_jsonDoc = QJsonDocument::fromJson(data);

    const QString strDoc(m_jsonDoc.toJson(QJsonDocument::Compact));

    std::cout << "Data file contents: " << strDoc.toStdString() << std::endl;

    return 0;
}

这是主函数:

/** main.cpp */

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>

#include "QmlLink.hpp"

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);

    QmlLink qmlLink;

    QQmlApplicationEngine engine;
    const QUrl url(u"qrc:/main/Main.qml");
    QObject::connect(&engine, &QQmlApplicationEngine::objectCreationFailed,
        &app, []() { QCoreApplication::exit(-1); },
        Qt::QueuedConnection);
    engine.load(url);

    QQmlContext *rootContext = engine.rootContext();
    rootContext->setContextProperty("QmlLink", &qmlLink);

    return app.exec();
}

这是简单的JSON文件:

{
    "Array": [
        true,
        999,
        "string"
    ],
    "Key": "Value",
    "null": null
}

工具版本:

  • Qt 6.5
  • 操作系统:Linux
  • gcc 13.1
  • clang 15
英文:

Why do I get this error in the QML code: ReferenceError: m_jsonDocStr is not defined,
even if m_jsonDocStr is specified here in the C++ code:

Q_PROPERTY(QString m_jsonDocStr READ jsonDocToStr WRITE setJsonDocStr NOTIFY jsonDocStrChanged)

And why the QmlLink.open() C++ function is not opened from QML? (neither as a normal member function nor as a slot)

TypeError: Property &#39;open&#39; of object [object Object] is not a function

Next, with the "<--" comment I point to the critical lines of the code, with the error messages I get.

This is the QML file:

/** Main.qml */

import QtCore
import QtQuick
import QtQuick.Window
import QtQuick.Controls
import QtQuick.Dialogs
import QtQuick.Layouts
import main

ApplicationWindow {
    id: window
    width: 640
    height: 480
    visible: true
    title: qsTr(&quot;Data input widget&quot;)
    
    property string dataFilename: &quot;&quot;
    property string dataFile: &quot;&quot;
    
    menuBar: MenuBar {
        Menu {
            title: qsTr(&quot;&amp;File&quot;)
            Action {
                text: qsTr(&quot;&amp;Open...&quot;)
                onTriggered: fileDialog.open()
            }
        }
    }

    ScrollView {
        id: fileContents
        anchors.fill: parent
        property string dataFile: m_jsonDocStr	// &lt;-- ReferenceError: m_jsonDocStr is not defined
        TextArea {
            text: &quot;File: &quot; + dataFilename + &quot;\n&quot; + dataFile + &quot;\nLength: &quot; + dataFile.length
        }
    }

    FileDialog {
        id: fileDialog
        title: &quot;Open data file&quot;
        nameFilters: [&quot;JSON files (*.json)&quot;]
        onAccepted: {
            dataFilename = selectedFile
            console.log(&quot;Open file: &quot; + dataFilename)
            QmlLink.open(dataFilename)		// &lt;-- TypeError: Property &#39;open&#39; of object [object Object] is not a function
            console.log(&quot;File contents: &quot; + m_jsonDocStr)
        }
        onRejected: {
            console.log(&quot;No file selected.&quot;)
        }
    }
}

Here is the header file:

/** QmlLink.hpp */

#ifndef QMLLINK_HPP
#define QMLLINK_HPP

#include &lt;QObject&gt;
#include &lt;QJsonDocument&gt;
#include &lt;QtQml/qqmlregistration.h&gt;

class QmlLink : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QJsonDocument m_jsonDoc READ jsonDoc WRITE setJsonDoc NOTIFY jsonDocChanged)
    Q_PROPERTY(QString m_jsonDocStr READ jsonDocToStr WRITE setJsonDocStr NOTIFY jsonDocStrChanged)
    QML_ELEMENT

public:
    explicit QmlLink(QObject *parent = nullptr);

public slots:
    QJsonDocument jsonDoc() const { return m_jsonDoc; }
    void          setJsonDoc(const QJsonDocument &amp;jsonDoc) { m_jsonDoc = jsonDoc; }
    QString       jsonDocToStr() const { return m_jsonDoc.toJson(); }
    void          setJsonDocStr(const QString &amp;jsonDoc) { m_jsonDoc = QJsonDocument::fromJson(jsonDoc.toUtf8()); }

signals:
    void jsonDocChanged();
    void jsonDocStrChanged();

public slots:
    void menuOpen();
    int  open(QString filePath);

private:
    QJsonDocument m_jsonDoc;
    QString m_jsonDocStr;

    QString m_jsonFileName = &quot;default.json&quot;;
};

#endif // QMLLINK_HPP

This is the implementation file:

/** QmlLink.cpp */

#include &quot;QmlLink.hpp&quot;
#include &lt;iostream&gt;	
#include &lt;QDebug&gt;
#include &lt;QFile&gt;
#include &lt;QQmlContext&gt;
#include &lt;QQmlEngine&gt;
#include &lt;QQuickView&gt;

QmlLink::QmlLink(QObject *parent) : QObject{parent} {}

void QmlLink::menuOpen() {
    open(m_jsonFileName);
}

int QmlLink::open(QString filePath)
{
    m_jsonFileName = filePath;

    QUrl url(filePath);
    QFile jsonFile;
    jsonFile.setFileName(url.toLocalFile());

    if(jsonFile.open(QIODevice::ReadOnly) == false) {
        return -1;
    }

    const QByteArray data = jsonFile.readAll();
    m_jsonDoc = QJsonDocument::fromJson(data);

    const QString strDoc(m_jsonDoc.toJson(QJsonDocument::Compact));

    std::cout &lt;&lt; &quot;Data file contents: &quot; &lt;&lt; strDoc.toStdString() &lt;&lt; std::endl;

    return 0;
}

This is a classic main() function:

/** main.cpp */

#include &lt;QGuiApplication&gt;
#include &lt;QQmlApplicationEngine&gt;
#include &lt;QQmlContext&gt;

#include &quot;QmlLink.hpp&quot;

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);

    QmlLink qmlLink;

    QQmlApplicationEngine engine;
    const QUrl url(u&quot;qrc:/main/Main.qml&quot;_qs);
    QObject::connect(&amp;engine, &amp;QQmlApplicationEngine::objectCreationFailed,
        &amp;app, []() { QCoreApplication::exit(-1); },
        Qt::QueuedConnection);
    engine.load(url);

    QQmlContext *rootContext = engine.rootContext();
    rootContext-&gt;setContextProperty(&quot;QmlLink&quot;, &amp;qmlLink);

    return app.exec();
}

This is the trivial JSON file:

{
    &quot;Array&quot;: [
        true,
        999,
        &quot;string&quot;
    ],
    &quot;Key&quot;: &quot;Value&quot;,
    &quot;null&quot;: null
}

Tools versions:

  • Qt 6.5
  • OS: Linux
  • gcc 13.1
  • clang 15

答案1

得分: 2

你应该在加载URL之前设置上下文属性:

QGuiApplication app(argc, argv);

QmlLink qmlLink;

QQmlApplicationEngine engine;
// 在这里设置上下文属性
QQmlContext *rootContext = engine.rootContext();
rootContext->setContextProperty("QmlLink", &qmlLink);

const QUrl url("qrc:/main/Main.qml");
QObject::connect(&engine, &QQmlApplicationEngine::objectCreationFailed,
    &app, []() { QCoreApplication::exit(-1); },
    Qt::QueuedConnection);
engine.load(url);

然后修复 Main.qml

/** Main.qml */

import QtCore
import QtQuick
import QtQuick.Window
import QtQuick.Controls
import QtQuick.Dialogs
import QtQuick.Layouts

ApplicationWindow {
    id: window
    width: 640
    height: 480
    visible: true
    title: qsTr("Data input widget")

    property string dataFilename: ""

    menuBar: MenuBar {
        Menu {
            title: qsTr("&File")
            Action {
                text: qsTr("&Open...")
                onTriggered: fileDialog.open()
            }
        }
    }

    ScrollView {
        id: fileContents
        anchors.fill: parent
        property string dataFile: QmlLink.m_jsonDocStr // 添加上下文属性
        TextArea {
            // 添加 ScrollView 的 id 以访问 dataFile
            text: "File: " + dataFilename + "\n" + fileContents.dataFile + "\nLength: " + fileContents.dataFile.length
        } 
    }

    FileDialog {
        id: fileDialog
        title: "Open data file"
        nameFilters: ["JSON files (*.json)"]
        onAccepted: {
            dataFilename = selectedFile
            console.log("Open file: " + dataFilename)
            QmlLink.open(dataFilename)
            // 添加上下文属性
            console.log("File contents: " + QmlLink.m_jsonDocStr)
        }
        onRejected: {
            console.log("No file selected.")
        }
    }
}

为了在 ScrollView 中显示文件内容,还需要对 QmlLink.cpp 进行一些更改:

int QmlLink::open(QString filePath)
{
    m_jsonFileName = filePath;

    QUrl url(filePath);
    QFile jsonFile;
    jsonFile.setFileName(url.toLocalFile());

    if(jsonFile.open(QIODevice::ReadOnly) == false) {
        return -1;
    }

    const QByteArray data = jsonFile.readAll();
    m_jsonDoc = QJsonDocument::fromJson(data);

    const QString strDoc(m_jsonDoc.toJson(QJsonDocument::Compact));

    std::cout << "Data file contents: " << strDoc.toStdString() << std::endl;

    emit jsonDocStrChanged(); // 更新用于 QML 的属性

    return 0;
}

以及在 QmlLink.hpp 中:

void setJsonDocStr(const QString &jsonDoc) 
{ 
  m_jsonDoc = QJsonDocument::fromJson(jsonDoc.toUtf8());
  emit jsonDocStrChanged(); // 更新用于 QML 的属性
}
英文:

You should set the context property before loading the url :

QGuiApplication app(argc, argv);

QmlLink qmlLink;

QQmlApplicationEngine engine;
// set the context property here
QQmlContext *rootContext = engine.rootContext();
rootContext-&gt;setContextProperty(&quot;QmlLink&quot;, &amp;qmlLink);

const QUrl url(u&quot;qrc:/main/Main.qml&quot;_qs);
QObject::connect(&amp;engine, &amp;QQmlApplicationEngine::objectCreationFailed,
    &amp;app, []() { QCoreApplication::exit(-1); },
    Qt::QueuedConnection);
engine.load(url);

and fix the Main.qml :

/** Main.qml */

import QtCore
import QtQuick
import QtQuick.Window
import QtQuick.Controls
import QtQuick.Dialogs
import QtQuick.Layouts

ApplicationWindow {
    id: window
    width: 640
    height: 480
    visible: true
    title: qsTr(&quot;Data input widget&quot;)
    
    property string dataFilename: &quot;&quot;
    
    menuBar: MenuBar {
        Menu {
            title: qsTr(&quot;&amp;File&quot;)
            Action {
                text: qsTr(&quot;&amp;Open...&quot;)
                onTriggered: fileDialog.open()
            }
        }
    }

    ScrollView {
        id: fileContents
        anchors.fill: parent
        property string dataFile: QmlLink.m_jsonDocStr // add context property
        TextArea {
            // add id of the ScrollView to access dataFile
            text: &quot;File: &quot; + dataFilename + &quot;\n&quot; + fileContents.dataFile + &quot;\nLength: &quot; + fileContents.dataFile.length
        } 
    }

    FileDialog {
        id: fileDialog
        title: &quot;Open data file&quot;
        nameFilters: [&quot;JSON files (*.json)&quot;]
        onAccepted: {
            dataFilename = selectedFile
            console.log(&quot;Open file: &quot; + dataFilename)
            QmlLink.open(dataFilename)
            // add context property 
            console.log(&quot;File contents: &quot; + QmlLink.m_jsonDocStr)
        }
        onRejected: {
            console.log(&quot;No file selected.&quot;)
        }
    }
}

To display the content of the file in the ScrollView, there is also a little change needed in QmlLink.cpp :

int QmlLink::open(QString filePath)
{
    m_jsonFileName = filePath;

    QUrl url(filePath);
    QFile jsonFile;
    jsonFile.setFileName(url.toLocalFile());

    if(jsonFile.open(QIODevice::ReadOnly) == false) {
        return -1;
    }

    const QByteArray data = jsonFile.readAll();
    m_jsonDoc = QJsonDocument::fromJson(data);

    const QString strDoc(m_jsonDoc.toJson(QJsonDocument::Compact));

    std::cout &lt;&lt; &quot;Data file contents: &quot; &lt;&lt; strDoc.toStdString() &lt;&lt; std::endl;

    emit jsonDocStrChanged(); // update the property for QML

    return 0;
}

and in QmlLink.hpp :

void setJsonDocStr(const QString &amp;jsonDoc) 
{ 
  m_jsonDoc = QJsonDocument::fromJson(jsonDoc.toUtf8());
  emit jsonDocStrChanged(); // update the property for QML
}

答案2

得分: 1

I think you can't pass QJsonDocument from C++ to QML.
As you mentioned, you will get an "Unable to assign [undefined] to QString" error.
But my idea is to pass QByteArray.

Here is my example:

In main.cpp:

#include <QApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QFileDialog>
#include <QJsonDocument>
#include <QJsonObject>
#include <QDebug>

class FileLoader : public QObject
{
    Q_OBJECT

public:
    explicit FileLoader(QObject *parent = nullptr) : QObject(parent) {}

public slots:
    void openFile()
    {
        QString filePath = QFileDialog::getOpenFileName(nullptr, "Open JSON File", "", "JSON Files (*.json)");

        if (!filePath.isEmpty()) {
            QFile file(filePath);
            if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
                QByteArray data = file.readAll();
                file.close();

                QJsonDocument jsonDoc = QJsonDocument::fromJson(data);
                qDebug() << "JSON File Loaded:" << filePath;
                emit fileLoaded(jsonDoc.toJson(QJsonDocument::Indented));
            }
        }
    }

signals:
    void fileLoaded(const QByteArray &jsonData);
};

int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    QApplication app(argc, argv);

    QQmlApplicationEngine engine;

    // Create an instance of the FileLoader
    FileLoader fileLoader;

    // Register the FileLoader as a context property
    engine.rootContext()->setContextProperty("fileLoader", &fileLoader);

    // Load the QML file
    const QUrl url(QStringLiteral("qrc:/QmlJson/Main.qml"));
    engine.load(url);

    return app.exec();
}

#include "main.moc"

In Main.qml:

import QtQuick 2.15
import QtQuick.Controls 2.15

ApplicationWindow {
    visible: true
    width: 400
    height: 300
    title: "JSON Viewer"

    Text {
        id: jsonDataText
        anchors.centerIn: parent
    }

    Button {
        text: "Open JSON"
        onClicked: fileLoader.openFile()
    }

    Connections {
        target: fileLoader
        function onFileLoaded(jsonData) {
            jsonDataText.text = jsonData
        }
    }
}

This is my result:

C++ 和 Qt/QML 数据交换的问题

英文:

I think you can't pass QJsonDocument from C++ to QML.
As you mentioned you will get an "Unable to assign [undefined] to QString" error.
But My Idea is to pass QByteArray.

Here is my example:

In main.cpp:

#include &lt;QApplication&gt;
#include &lt;QQmlApplicationEngine&gt;
#include &lt;QQmlContext&gt;
#include &lt;QFileDialog&gt;
#include &lt;QJsonDocument&gt;
#include &lt;QJsonObject&gt;
#include &lt;QDebug&gt;

class FileLoader : public QObject
{
    Q_OBJECT

public:
    explicit FileLoader(QObject *parent = nullptr) : QObject(parent) {}

public slots:
    void openFile()
    {
        QString filePath = QFileDialog::getOpenFileName(nullptr, &quot;Open JSON File&quot;, &quot;&quot;, &quot;JSON Files (*.json)&quot;);

        if (!filePath.isEmpty()) {
            QFile file(filePath);
            if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
                QByteArray data = file.readAll();
                file.close();

                QJsonDocument jsonDoc = QJsonDocument::fromJson(data);
                qDebug() &lt;&lt; &quot;JSON File Loaded:&quot; &lt;&lt; filePath;
                emit fileLoaded(jsonDoc.toJson(QJsonDocument::Indented));
            }
        }
    }

signals:
    void fileLoaded(const QByteArray &amp;jsonData);
};

int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    QApplication app(argc, argv);

    QQmlApplicationEngine engine;

    // Create an instance of the FileLoader
    FileLoader fileLoader;

    // Register the FileLoader as a context property
    engine.rootContext()-&gt;setContextProperty(&quot;fileLoader&quot;, &amp;fileLoader);

    // Load the QML file
    const QUrl url(QStringLiteral(&quot;qrc:/QmlJson/Main.qml&quot;));
    engine.load(url);

    return app.exec();
}

#include &quot;main.moc&quot;


in Main.qml

import QtQuick 2.15
import QtQuick.Controls 2.15

ApplicationWindow {
    visible: true
    width: 400
    height: 300
    title: &quot;JSON Viewer&quot;

    Text {
        id: jsonDataText
        anchors.centerIn: parent
    }

    Button {
        text: &quot;Open JSON&quot;
        onClicked: fileLoader.openFile()
    }

    Connections {
        target: fileLoader
        function onFileLoaded(jsonData) {
            jsonDataText.text = jsonData
        }
    }
}

This is my result:

C++ 和 Qt/QML 数据交换的问题

huangapple
  • 本文由 发表于 2023年6月13日 18:23:37
  • 转载请务必保留本文链接:https://go.coder-hub.com/76463921.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定