• qt笔记之qml和C++的交互系列(一):初记


    code review!

    —— 杭州 2023-11-16 夜


    一.qt笔记之qml和C++的交互:官方文档阅读理解

    参考资料:

    官方文档《Exposing Attributes of C++ Types to QML》(将C++类暴露给QML)
    官方文档《Data Type Conversion Between QML and C++》
    官方文档《The Property System》
    官方文档《Defining QML Types from C++》(在QML中定义C++类)
    官方文档《Overview - QML and C++ Integration》

    0.《Overview - QML and C++ Integration》中给出五种QML与C+集成的方法

    • 1.Exposing Attributes of C++ Classes to QML
    • 2.Defining QML Types from C++
    • 3.Embedding C++ Objects into QML with Context Properties
    • 4.Interacting with QML Objects from C++
    • 5.Data Type Conversion Between QML and C++

    1.Q_PROPERTY:将C++类的成员变量暴露给QML

    Q_PROPERTY(…)
    This macro is used for declaring properties in classes that inherit QObject. Properties behave like class data members, but they have additional features accessible through the Meta-Object System.
    翻译:
    这个宏用于在继承 QObject 的类中声明属性。属性的行为类似于类数据成员,但通过元对象系统可以访问额外的功能。

    在这里插入图片描述

    翻译:

    为了与QML最大程度地互操作,任何可写的属性都应该有一个关联的NOTIFY信号,在属性值变化时发出。这允许该属性与属性绑定(property binding)一起使用,属性绑定是QML的一个重要特性,它通过在任何依赖项值变化时自动更新属性来强制属性之间的关系。

    在上述示例中,author属性的关联NOTIFY信号是authorChanged,如Q_PROPERTY()宏调用中所指定的。这意味着每当信号被发出时——比如在Message::setAuthor()中当author发生变化时——它会通知QML引擎需要更新任何涉及author属性的绑定,进而引擎将通过再次调用Message::author()来更新text属性。

    如果author属性可写,但没有关联的NOTIFY信号,text值将使用Message::author()返回的初始值进行初始化,但不会更新为此属性的任何后续更改。此外,任何尝试从QML绑定到该属性的操作都会导致引擎产生运行时警告。

    注意:建议将NOTIFY信号命名为Changed,其中是属性的名称。由QML引擎生成的关联属性更改信号处理程序的形式始终为onChanged,而不管相关的C++信号的名称如何,因此建议信号名称遵循此约定,以避免任何混淆。

    GPT解读:
    在上述代码中,Q_PROPERTY(QString author READ author WRITE setAuthor NOTIFY authorChanged) 声明了一个名为 author 的属性,它具有读取方法 author() 和写入方法 setAuthor(const QString&)。该属性还使用了信号 authorChanged(),以便在属性值更改时发出通知。

    setAuthor函数中修改了m_author的值后,Q_PROPERTY声明的属性author的值也会相应地改变。这是因为在setAuthor函数中,当m_author的值发生变化时,通过emit authorChanged()语句发出了信号authorChanged(),这个信号与Q_PROPERTY中的NOTIFY关键字相关联。

    Q_PROPERTYNOTIFY关键字用于指定一个信号,当属性的值发生变化时,会发出这个信号。在这种情况下,authorChanged()信号与author属性相关联,意味着当m_author的值发生变化时,会发出authorChanged()信号。

    因此,当m_author的值在setAuthor函数中被修改时,会发出authorChanged()信号,这将导致与author属性相关联的代码(例如绑定到该属性的UI元素)能够接收到信号并相应地更新属性的值。

    QString author() constMessage 类的成员函数,用于读取私有成员变量 m_author 的值并返回。

    QString m_authorMessage 类的私有成员变量,用于存储作者的名称。

    main 函数中,通过创建 Message 类的实例 msg,将其设置为上下文属性,使其可以在 QML 文件中访问。

    MyItem.qml 中,通过 msg.author 可以读取 Message 类中的 author 属性值。这将调用 Message::author() 方法。

    Component.onCompleted 中,将 msg.author 设置为 “Jonah”,这将调用 Message::setAuthor() 方法,并且在属性值更改后会发出 authorChanged() 信号。

    在这里插入图片描述

    2.Q_INVOKABLE()或public slots:将C++类的成员函数暴露给QML

    Exposing Methods (Including Qt Slots):
    在这里插入图片描述

    3.on其中第一个字母大写:暴露C++中的信号以及信号中传递的参数给QML

    在这里插入图片描述

    翻译:

    任何继承自QObject类型的公共信号都可以在QML代码中访问。

    QML引擎会自动为在QML中使用的任何继承自QObject类型的信号创建信号处理器。信号处理器的命名规则是on<信号名>,其中<信号名>是信号的名称,首字母大写。通过参数名,可以在信号处理器中访问所有通过信号传递的参数。

    例如,假设MessageBoard类具有一个带有单个参数subject的newMessagePosted()信号:

    class MessageBoard : public QObject
    {
        Q_OBJECT
    public:
       // ...
    signals:
       void newMessagePosted(const QString &subject);
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    如果MessageBoard类型已在QML类型系统中注册,那么在QML中声明的MessageBoard对象可以通过名为onNewMessagePosted的信号处理器接收newMessagePosted()信号,并检查subject参数的值:

    MessageBoard {
        onNewMessagePosted: (subject)=> console.log("New message received:", subject)
    }
    
    • 1
    • 2
    • 3

    二.qt笔记之qml和C++的交互笔记

    原文链接:

    QML进阶(八)实现QML界面与C++类型交互

    在QML工程中,一般QML界面只负责前端交互,而真正的业务逻辑都是C++模块实现的。为了实现前端和后端的顺利衔接,我们需要做好QML界面与C++的交互。这里就介绍一下如何在QML中调用对应的C++模块。在QML中调用C++模块的方法主要有三种,分别是:

    1.设置上下文属性(setContextProperty())
    2.在QML引擎里面注册新类型(qmlRegisterType)
    3.导出对应的QML扩展插件。

    下面介绍一下三个方法的优缺点:
    对于小型应用来说,方法一设置上下文属性是最简单实用的方法。开发者只需要将对应的接口和变量暴露给QML就行。由于设置在QML中的变量是全局的,一定要注意避免名称冲突。

    在QML引擎里面注册新的类型,允许用户在QML文件中控制C++对象的生命周期,这是设置上下文属性这种方法无法实现的。同时注册新类型的方法,不会污染全局命名空间。但是这种方法也有一个缺点,就是QML中的类型都需要提前注册,所有用到的库都需要在程序启动的时候链接,无法动态链接。但在绝大多数情况下,这并不是一个问题。

    QML扩展插件是弹性最好,但也是最复杂的方法。QML允许用户在插件里面注册对应的新类型。这些新类型在QML第一次导入对应的符号的时候被加载。同时,通过使用QML单例引入,我们的新类型不会污染全局命名空间。由于新类型被插件化了,我们可以很轻松的在多个项目中复用我们之前定义的新类型。

    1.设置上下文属性(setContextProperty())

    1.1.fileio.h

    在这里插入图片描述

    1.2.fileio.cpp

    在这里插入图片描述

    1.3.main.cc

    在这里插入图片描述

    1.4.main.qml

    在这里插入图片描述

    代码
    fileio.h

    #ifndef FILEIO_H
    #define FILEIO_H
    
    #include 
    //用来打开的保存对应的文件
    class FileIO : public QObject
    {
        Q_OBJECT
        //定义QML可以访问的属性,定义格式如下
        //Q_PROPERTY(变量类型 访问名称 READ 读方法 WRITE 写方法 NOTIFY 发生变化的通知信号)
        //需要定义在Q_OBJECT之后第一个public之前
        Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged)
        Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged)
    
        //ui_title是在QML使用的别名,m_title_content是对应的变量名称
        //CONSTANT说明是只读的
        Q_PROPERTY(QString ui_title MEMBER m_title_content CONSTANT)
    public:
        FileIO(QObject *parent = 0);
        ~FileIO();
    
        //定义QML可以访问的方法
        Q_INVOKABLE void read();
        Q_INVOKABLE void write();
    
        QUrl source() const;
        QString text() const;
    public slots:
        void setSource(QUrl source);
        void setText(QString text);
    signals:
        void sourceChanged(QUrl arg);
        void textChanged(QString arg);
    private:
        QUrl m_file_source;
        QString m_file_content;
        //用来测试的只读title数据
        QString m_title_content;
    };
    
    #endif // FILEIO_H
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    fileio.cpp

    #include "fileio.h"
    FileIO::FileIO(QObject *parent)
        : QObject(parent),
          m_title_content(QString("fileio"))
    {
    }
    
    FileIO::~FileIO()
    {
    }
    
    void FileIO::read()
    {
        if(m_file_source.isEmpty()) {
            return;
        }
        QFile file(m_file_source.toLocalFile());
        if(!file.exists()) {
            qWarning() << "Does not exits: " << m_file_source.toLocalFile();
            return;
        }
        if(file.open(QIODevice::ReadOnly)) {
            QTextStream stream(&file);
            m_file_content = stream.readAll();
            emit textChanged(m_file_content);
        }
    }
    
    void FileIO::write()
    {
        if(m_file_source.isEmpty()) {
            return;
        }
        QFile file(m_file_source.toLocalFile());
        if(file.open(QIODevice::WriteOnly)) {
            QTextStream stream(&file);
            stream << m_file_content;
        }
    }
    
    QUrl FileIO::source() const
    {
        return m_file_source;
    }
    
    QString FileIO::text() const
    {
        return m_file_content;
    }
    
    void FileIO::setSource(QUrl source)
    {
        if (m_file_source == source)
            return;
    
        m_file_source = source;
        emit sourceChanged(source);
    }
    
    void FileIO::setText(QString text)
    {
        if (m_file_content == text)
            return;
        m_file_content = text;
        emit textChanged(text);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66

    main.cc

    #include 
    #include 
    #include 
    #include 
    #include "fileio.h"
    int main(int argc, char *argv[])
    {
        QGuiApplication app(argc, argv);
        //根据不同的QT版本设置对应的编码
        app.setFont(QFont("Microsoft Yahei", 9));
    #if (QT_VERSION <= QT_VERSION_CHECK(5,0,0))
    #if _MSC_VER
        QTextCodec *codec = QTextCodec::codecForName("gbk");
    #else
        QTextCodec *codec = QTextCodec::codecForName("utf-8");
    #endif
        QTextCodec::setCodecForLocale(codec);
        QTextCodec::setCodecForCStrings(codec);
        QTextCodec::setCodecForTr(codec);
    #else
        QTextCodec *codec = QTextCodec::codecForName("utf-8");
        QTextCodec::setCodecForLocale(codec);
    #endif
    
        //定义对应的类型指针
        QScopedPointer<FileIO> current_file_io(new FileIO());
        QQmlApplicationEngine engine;
        //在加载对应的URL之前, 设置上下文属性
        engine.rootContext()->setContextProperty("qmlfileio",current_file_io.data());
        engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
        if (engine.rootObjects().isEmpty())
            return -1;
        return app.exec();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    main.qml

    import QtQuick 2.8
    import QtQuick.Window 2.2
    import Qt.labs.platform 1.0
    
    Window {
        visible: true
        width: 440
        height: 300
        title: qsTr("Context fileIO")
        Column{
            Rectangle{
                id:contentRect
                x:10; y:10
                width: 400
                height: 150
    
                //用于显示打开的文本文件的内容
                Text{                
                    id:content
                    anchors.top :contentRect.top
                    anchors.bottom: contentRect.bottom
                    anchors.left: contentRect.left
                    anchors.right: contentRect.right
                    text: qmlfileio.text
                }
                border.color: "#CCCCCC"
            }
           
            //点击的按钮用来选择对应的文件
            Rectangle{
                anchors.horizontalCenter:contentRect.horizontalCenter
                color: "#4D9CF8"
                width:200
                height: 30
                Text{
                    anchors.centerIn: parent
                    text:"点击打开文件"
                }
                MouseArea {
                    anchors.fill: parent
                    onClicked: {
                        fileDialog.open();
                    }
                }
            }
        }
        //文件选择窗口,选择需要打开的文件
        //并读取文件中对应的内容
        FileDialog{
            id: fileDialog
            folder: StandardPaths.standardLocations(StandardPaths.PicturesLocation)[0]
            onFileChanged: {
                qmlfileio.source = fileDialog.file;
                qmlfileio.read();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57

    2.在QML引擎里面注册新类型(qmlRegisterType)

    2.1.fileio.h

    在这里插入图片描述

    2.2.fileio.cpp

    在这里插入图片描述

    2.3.main.cc

    在这里插入图片描述

    2.4.main.qml

    在这里插入图片描述

    代码
    fileio.h

    #ifndef FILEIO_H
    #define FILEIO_H
    
    #include 
    //用来打开的保存对应的文件
    class FileIO : public QObject
    {
        Q_OBJECT
        //定义QML可以访问的属性,定义格式如下
        //Q_PROPERTY(变量类型 访问名称 READ 读方法 WRITE 写方法 NOTIFY 发生变化的通知信号)
        //需要定义在Q_OBJECT之后第一个public之前
        Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged)
        Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged)
    
        //ui_title是在QML使用的别名,m_title_content是对应的变量名称
        //CONSTANT说明是只读的
        Q_PROPERTY(QString ui_title MEMBER m_title_content CONSTANT)
    public:
        FileIO(QObject *parent = 0);
        ~FileIO();
    
        //定义QML可以访问的方法
        Q_INVOKABLE void read();
        Q_INVOKABLE void write();
    
        QUrl source() const;
        QString text() const;
    public slots:
        void setSource(QUrl source);
        void setText(QString text);
    signals:
        void sourceChanged(QUrl arg);
        void textChanged(QString arg);
    private:
        QUrl m_file_source;
        QString m_file_content;
        //用来测试的只读title数据
        QString m_title_content;
    };
    
    #endif // FILEIO_H
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    fileio.cpp

    #include "fileio.h"
    FileIO::FileIO(QObject *parent)
        : QObject(parent),
          m_title_content(QString("fileio"))
    {
    }
    
    FileIO::~FileIO()
    {
    }
    
    void FileIO::read()
    {
        if(m_file_source.isEmpty()) {
            return;
        }
        QFile file(m_file_source.toLocalFile());
        if(!file.exists()) {
            qWarning() << "Does not exits: " << m_file_source.toLocalFile();
            return;
        }
        if(file.open(QIODevice::ReadOnly)) {
            QTextStream stream(&file);
            m_file_content = stream.readAll();
            emit textChanged(m_file_content);
        }
    }
    
    void FileIO::write()
    {
        if(m_file_source.isEmpty()) {
            return;
        }
        QFile file(m_file_source.toLocalFile());
        if(file.open(QIODevice::WriteOnly)) {
            QTextStream stream(&file);
            stream << m_file_content;
        }
    }
    
    QUrl FileIO::source() const
    {
        return m_file_source;
    }
    
    QString FileIO::text() const
    {
        return m_file_content;
    }
    
    void FileIO::setSource(QUrl source)
    {
        if (m_file_source == source)
            return;
    
        m_file_source = source;
        emit sourceChanged(source);
    }
    
    void FileIO::setText(QString text)
    {
        if (m_file_content == text)
            return;
        m_file_content = text;
        emit textChanged(text);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66

    main.cc

    #include 
    #include 
    #include 
    #include 
    #include "fileio.h"
    int main(int argc, char *argv[])
    {
        QGuiApplication app(argc, argv);
        //在engine声明之前注册C++类型
        //@1:类在qml中别名 @2:版本主版本号 @3:版本的次版本号 @4类的名称
        qmlRegisterType<FileIO>("org.fileio",1,0,"FileIO");
        QQmlApplicationEngine engine;
        engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
        if (engine.rootObjects().isEmpty())
            return -1;
        return app.exec();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    main.qml:import org.fileio 1.0

    import QtQuick 2.8
    import QtQuick.Window 2.2
    import Qt.labs.platform 1.0
    import org.fileio 1.0
    
    Window {
        visible: true
        width: 440
        height: 300
        title: qsTr("Context fileIO")
        Column{
            Rectangle{
                id:contentRect
                x:10; y:10
                width: 400
                height: 150
    
                //用于显示打开的文本文件的内容
                Text{                
                    id:content
                    anchors.top :contentRect.top
                    anchors.bottom: contentRect.bottom
                    anchors.left: contentRect.left
                    anchors.right: contentRect.right
                    text: fileIO.text
                }
                border.color: "#CCCCCC"
            }
    
            //点击的按钮用来选择对应的文件
            Rectangle{
                anchors.horizontalCenter:contentRect.horizontalCenter
                color: "#4D9CF8"
                width:200
                height: 30
                Text{
                    anchors.centerIn: parent
                    text:"点击打开文件"
                }
                //点击按钮弹出选择文件的对话框
                MouseArea {
                    anchors.fill: parent
                    onClicked: {
                        fileDialog.open();
                    }
                }
            }
        }
        //文件选择窗口,选择需要打开的文件
        //并读取文件中对应的内容
        FileDialog{
            id: fileDialog
            folder: StandardPaths.standardLocations(StandardPaths.PicturesLocation)[0]
            onFileChanged: {
                fileIO.source = fileDialog.file;
                fileIO.read();
            }
        }
       
        //外部导入的C++类型,可以直接定义使用
        //外部通过ID来访问该模块
        FileIO{
            id:fileIO
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65

    3.导出对应的QML扩展插件——略

    在这里插入图片描述

  • 相关阅读:
    Android获取本地文件目录
    微信小程序如何分包管理
    计算机操作系统面试题自用
    珂朵莉树 学习笔记
    Task07|缺失数据|DataWhale组队学习
    HTML模板 宽屏大气的企业官网网站模板
    【线上问题】很抱歉,如果没有 JavaScript 支持,将不能正常工作
    2.7 基本放大电路的派生电路
    【前端】[vue3] [uni-app] 组件样式击穿:deep
    Transformer模型
  • 原文地址:https://blog.csdn.net/weixin_43297891/article/details/134452960