为萌梦动作编辑器写插件(C++/QML混合插件篇)



  • 0_1523956824130_人人人人.jpg
    上次给大家介绍了萌梦动作编辑器是可以支持插件的,同时也简单介绍了制作一个简单的QML插件以便和萌梦动作编辑器结合起来。那么这次我们介绍一下萌梦动作编辑器支持的另外一种插件格式,那就是C++/QML混合插件,以下我们简称混合插件。

    C++/QML混合插件的介绍

    混合插件是萌梦动作编辑器的一种新型的插件格式,它利用C++强大的功能来处理复杂的业务,以便支持QML层,让插件变得更加强大。事实上,萌梦动作编辑器就是由若干个插件组合而成,插件的组合赋予了动作编辑器强大的能力,使其工作起来更有效。

    此外,萌梦动作编辑器支持插件之间的相互交互,让数据传递在插件之间变得简单有效。其中的秘诀就是萌梦独家研制的插件模块(名称为:QtDream.Plugin)。下面就让我们来看一个例子吧。

    如何使用混合插件

    首先请转到这里查看一个混合插件工程。或者在浏览器访问:

    https://gitee.com/jiangcaiyang/QtDreamTestNotepadPlugin-master
    

    一个混合插件通常包含以下几个文件

    • .qmake.conf # 规定了Qt版本等信息
    • 插件名称.pro # 规定了插件名称等信息
    • 插件名称Plugin.h
    • 插件名称Plugin.cpp # 规定了插件的类型继承自QQmlExtensionPlugin而写的类
    • 插件名称plugin.json # json描述插件的一些信息
    • 插件名称plugin.qrc # 用来包裹qmldir的qrc文件
    • qmldir # 描述了插件的一些信息
      此外还有若干qml文件,目的是和萌梦动作编辑器整合起来。

    文件看起来很多的样子……但是别慌,我们一点一点来分析,慢慢就会知道其中的用处的。



  • 首先是qmldir文件。Qt的定义式语言引擎会读取它然后获取到qml路径的信息。我们的Notepad插件的qmldir内容是这样的:

    module QtDream.Notepad
    Main 0.3 qrc:///QtDream/Notepad/qml/Main.qml
    NotepadPage 0.3 qrc:///QtDream/Notepad/qml/NotepadPage.qml
    typeinfo NotepadPlugin.qmltypes
    
    depends QtQuick 2.5
    depends QtQuick.Controls 1.4
    depends QtQuick.Dialog 1.2
    depends QtDream.Core 3.3
    depends QtDream.Plugin 1.0
    depends QtDream.Project 1.0
    designersupported
    
    plugin qtdreamnotepad
    classname NotepadPlugin
    

    首先module后面跟随的是模块的uri,这里我们写成了QtDream.Notepad。然后我们需要将主要的QML文件暴露出来啦,所以我们将Main.qml文件以及版本和路径在此写了出来。同样的我们也需要暴露NotepadPage这个类。然后呢,为了让qmlplugindump生成类型信息,我还写了typeinfo,并且将此类型信息的名称写在了后面。然后就是一系列依赖的声明了,QtDream.Notepad依赖QtQuick 2.5、QtQuick.Controls 1.4等等。最后让其得到Qt Quick设计器的支持,这样就可以在设计器中设计这样的界面了。最后两行则是声明插件的名称以及具体包含插件的名称。一个插件可能包含0个或者更多的plugin,但是classname这一栏只可能有一个,表示的是包含定义QQmlExtensionPlugin子类的类的名称。QML引擎就会根据这个名称来读取插件的实现。



  • 随后就是插件名称.pro文件了,在我们的QtDream.Notepad这个插件中,pro文件可以说是起到了组织的作用。为了以后的插件都兼容Qt和萌梦动作编辑器,我们建议插件的pro文件都按照标准的方式写。比如说QtDream.Notepad文件的内容如下:

    # notepad.pro
    QT += qml quick
    DEFINES += NOTEPAD_LIB
    
    OTHER_FILES += notepadplugin.json
    
    QMAKE_TARGET_PRODUCT = "QtDream Notepad Module (Qt $$QT_VERSION)"
    QMAKE_TARGET_DESCRIPTION = "QtDream notepad module, tekes a notepad easily."
    
    lupdate_only {
        SOURCES =   qml/*.qml \
                    qml/*.js
    }
    
    qtHaveModule( qml ) {
    
        HEADERS += notepadplugin.h
        SOURCES += notepadplugin.cpp
        DISTFILES += qmldir
    
        !greaterThan( QT_MINOR_VERSION, 6 ) {
            RESOURCES += notepadplugin.qrc
        }
    
        CXX_MODULE = dreamnotepad
        TARGET  = qtdreamnotepad
        TARGETPATH = QtDream/Notepad
        IMPORT_VERSION = 0.3
        load( qml_plugin )
    }
    
    RESOURCES +=    notepadassets.qrc
    
    # 这些是文档的部分
    #QMAKE_DOCS = $$PWD/doc/notepad.qdocconf
    #OTHER_FILES += $$QMAKE_DOCS doc/src/*.qdoc
    
    HEADERS +=  notepadglobal.h \
        notepadio.h
    SOURCES +=  notepadglobal.cpp \
        notepadio.cpp
    

    书写萌梦动作编辑器的插件pro文件和其它pro文件差别不大,但是要注意的几点是:

    1. QMAKE_TARGET_PRODUCTQMAKE_TARGET_DESCRIPTION是设定插件的名称和插件的描述的属性。以此可以告诉用户插件的作用如何;
    2. qtHaveModule( qml ) { }表示Qt只有包含了qml插件的情况下,才会生成qml插件。
    3. !greaterThan( QT_MINOR_VERSION, 6 ) { }这是兼容Qt 5.6以及之前的Qt版本而设计的方法,通过这个方法,Qt 5.6以及以前的版本无论是否能够静态编译,都能够运行起插件。
    4. load( qml_plugin )表示使用Qt内部spec的函数来载入qml的插件。qml_plugin则代表着要载入qml插件的方法来处理项目。
      最后您如果想要为插件生成文档,那么可以取消其中的注释,这样文档也能够顺利的生成了。


  • 紧接着介绍的是notepadplugin.hnodepadplugin.cpp了。这些也是生成一个混合插件必要的文件了。下面是notepadplugin.h的文件内容:

    // notepadplugin.h
    #ifndef NOTEPADPLUGIN_H
    #define NOTEPADPLUGIN_H
    
    #include <QQmlExtensionPlugin>
    
    class NotepadPlugin: public QQmlExtensionPlugin
    {
        Q_OBJECT
        Q_PLUGIN_METADATA( IID QQmlExtensionInterface_iid FILE "notepadplugin.json" )
    public:
        NotepadPlugin( QObject* parent = Q_NULLPTR );
        void registerTypes( const char* uri );
    };
    
    #endif // NOTEPADPLUGIN_H
    

    这个类是继承了QQmlExtensionPlugin类,然后覆盖了registerTypes方法。其中notepadplugin.json的内容就是文件系统的notepadplugin.json文件。接下来介绍的是notepadplugin.cpp文件的内容:

    // notepadplugin.cpp
    #include <qqml.h>
    #include <QQmlEngine>
    #include "notepadplugin.h"
    #include "notepadio.h"
    
    // 注册单例函数
    static QObject* NotepadIOCreateCallback(
            QQmlEngine* qmlEngine,
            QJSEngine* jsEngine )
    {
        Q_UNUSED( qmlEngine );
        Q_UNUSED( jsEngine );
    
        NotepadIO* io = NotepadIO::instance( );
        qmlEngine->setObjectOwnership( io, QQmlEngine::CppOwnership );
        return io;
    }
    
    
    NotepadPlugin::NotepadPlugin( QObject* parent ):
        QQmlExtensionPlugin( parent )
    {
    #if defined( QT_STATIC )
    #if QT_VERSION >= QT_VERSION_CHECK( 5, 7, 0 )
        Q_INIT_RESOURCE( qmake_QtDream_Notepad );
    #else
        Q_INIT_RESOURCE( notepadplugin );
    #endif
    #endif
        Q_INIT_RESOURCE( notepadassets );// 所有资源一定要打在一个包中,要不然程序会发现还是找不到相应的资源
    }
    
    void NotepadPlugin::registerTypes( const char* uri )
    {
        Q_UNUSED( uri );
        qmlRegisterSingletonType<NotepadIO>( uri, 0, 2, "NotepadIO",
                                             NotepadIOCreateCallback );
    }
    

    以上是NotepadPlugin类的实现。其中#ifdef #endif的内容表示兼容静态编译以及Qt5.6以及以前的初始化。最后就是注册一个名为NotepadIO的单例,其中是创建Notepad类需要的回调。回调的实现不是本文探讨的内容,所以暂且不介绍。



  • 最后介绍的是notepad.json.qmake.conf。其中json文件是可选的,

    {
        "Keys": [ "NotepadPlugin" ],
        "uri": [ "QtDream.Notepad" ]
    }
    

    notepad.json包含的是一个简单的字符串,其中包含的是Keys和uri。Keys就是我们的QQmlExtensionPlugin子类,而uri则是我们使用qml进行import时候的的uri。

    而.qmake.conf则是使用Qt的方式构建必要的文件。它的内容也极其简单。

    load(qt_build_config)
    MODULE_VERSION = 5.10.1
    

    除了第一行载入Qt构建的配置外,还需要设置的就是模块编译时候的版本。这里我们用Qt当前的版本5.10.1。



  • 好了,一个包含C++和QML混合插件就介绍完了。不过等下,你们还没有看过效果嘛?很简单,从gitee上下载或者复制项目到本地,进行编译,然后呢,放在和萌梦动作编辑器文件夹中的qml/QtDream子文件夹,再在萌梦动作编辑器中设定合适的路径,就可以顺利地载入插件的内容了!
    0_1523956789574_屏幕快照 2018-04-17 下午5.19.14.png



  • @jiangcaiyang 正确使用插件,补充一步,需要将工程下【qmldir】文件拷贝到插件目录

    0_1523969432953_TIM截图20180417205016.png



  • @青山白云 谢谢,我做得疏漏了。这方面没有介绍清楚~


 

走马观花~

最近的回复

关注我们

微博
QQ群











召唤伊斯特瓦尔