创建Qt Creator插件

Qt Creator可以通过多种方式进行扩展。Qt Creator架构基于插件加载器,这意味着插件加载之外的所有功能都在插件中实现。本文要说明的是Qt Creator插件的开发内容。

注意事项:

  1. Qt Creator自带Qt Creator插件向导,可以为您创建一个可运行的最小插件。强烈建议使用两个不同的Qt Creator实例来开发和测试您的插件。否则,您的插件也将加载到您的开发环境中,这可能会使开发环境不稳定,而您的插件仍然不稳定。你可以创建一个Qt Creator的副本,一个用于实际开发,另一个用于测试你的插件。

  2. 确保要开发的插件的Qt Creator版本与要使用的Qt Creator版本要相同。由于Qt Creator的二进制和源代码兼容性规则,Qt Creator插件向导创建的插件只能在创建它时使用的相同Qt Creator版本下编译和运行。

文件结构

文件 角色
README.md 描述如何构建和运行插件
Example.json.in 插件元数据模板。CMake创建一个Example.Json,它被作为元数据编译到插件中。元数据由Qt Creator读取,以找出有关插件的信息。
CMakeLists.txt 项目文件,由CMake用来生成构建文件和构建插件。
example_global.h 包含导出宏定义,当此插件应该将符号导出到其他插件
exampleconstants.h 头文件定义插件代码使用的常量
example.h, example.cpp 定义插件类的c++头文件和源文件,这些插件类将由Qt Creator的插件管理器实例化和运行
build_cmake.yml 添加了一个GitHub动作和工作流,可以随时将提交推送到Windows, Linux和macOS上的GitHub。有关更多信息,请参阅.github\workflow\README.md

Plugin Meta Data 模板

插件的元数据文件是一个JSON文件,其中包含加载插件库所需的所有信息,决定插件是否加载以及以何种顺序加载(取决于依赖项)。此外,它还包含了谁创建插件的文本描述,它的用途,以及在哪里可以找到更多关于它的信息。当编译插件时,该文件必须位于包含搜索路径之一,并且必须具有.json扩展名。JSON文件作为元数据编译到插件中,然后在加载插件时由Qt Creator读取。

用于识别插件和定义默认加载行为的主键,包括必选键Name和Version,以及可选键CompatVersion, Experimental, DisabledByDefault, Required和 Platform。

Key 值类型 意义
Name String 它被用作插件的标识符,可以在其他插件的依赖项中引用
Version String x.y.z_n形式的版本字符串,用于标识插件。
CompatVersion String 可选的。如果没有给出,默认其设置为与Version相同的值
Experimental Boolean 可选的。默认为false。实验插件默认不加载,但必须由用户显式启用。对于有可能对用户体验产生负面影响的新插件,应该启用此属性
DisabledByDefault Boolean 可选的。默认为false。如果设置,则默认情况下不加载相应的插件,由用户显式启用。对于那些不希望被这么多人使用的插件,应该这样做
Required Boolean 可选的。默认为false。被用作关于插件的提示…对话框,用户不能手动禁用此插件。仅用于核心插件。
Platform String 可选的。一个正则表达式,与插件工作的平台名称相匹配。省略标记意味着该插件可以在所有平台上加载。

Example Test.json

{
    "Name" : "Test",
    "Version" : "1.0.1",
    "CompatVersion" : "1.0.0",
    "Vendor" : "My Company",
    "Copyright" : "(C) 2016 MyCompany",
    "License" : [
        "This is a default license bla",
        "blubbblubb",
        "end of terms"
    ],
    "Category" : "My Company Additions",
    "Description" : [
        "This plugin is just a test.",
        "It demonstrates the great use of the plugin meta data."
    ],
    "Url" : "http://www.mycompany-online.com/products/greatplugin",
    "Arguments" : [
        {
            "Name" : "-variant",
            "Parameter" : "fancy|boring",
            "Description" : "Brings up the fancy or boring user interface"
        }
    ],
    "Dependencies" : [
        { "Name" : "SomeOtherPlugin", "Version" : "2.3.0_2" },
        { "Name" : "EvenOther", "Version" : "1.0.0" }
    ]
}

Plugin Class

所有Qt Creator插件必须从ExtensionSystem::IPlugin派生和QObjects。Q_PLUGIN_METADATA宏用于声明是有效的Qt插件,其中IID必须是org.qt-project.Qt.QtCreatorPlugin用于确定它是一个QtCreator插件,FILE指向插件的元数据.json。

class ExamplePlugin : public ExtensionSystem::IPlugin
{
    Q_OBJECT
    Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "Example.json")

Plugin Life Cycle

当你启动Qt Creator时,插件管理器会做以下工作:

  1. 在其搜索路径中查找所有动态库,并读取它们的元数据。没有元数据的所有库和没有org.qt-project.Qt.QtCreatorPlugin IID的所有库会被忽略
  2. 为每个插件创建一个ExtensionSystem::PluginSpec类的实例。这个类是来自插件规范的所有信息的容器,并且还跟踪插件的状态。你可以通过插件管理器的plugins()函数获得ExtensionSystem::PluginSpec实例。
  3. 将插件设置为Read状态。
  4. 验证每个插件的依赖项是否存在并兼容。有关插件依赖项的更多信息,请参见插件元数据。
  5. 将插件设置为Resolved 状态。
  6. 将所有插件排序到一个列表中,我们称之为加载队列,其中插件的依赖项位于插件之后(但不一定直接位于插件之后)。它将确保我们以适当的顺序加载和初始化插件。
  7. 加载插件的库,并按照加载队列的顺序创建它们的IPlugin实例。此时调用插件构造函数。其插件依赖的插件首先被创建。
  8. 将插件设置为Loaded 状态。
  9. 按照加载队列的顺序调用所有插件的initialize()函数。在initialize函数中,插件应该确保所有导出的接口都已设置并可供其他插件使用。插件可以假设它们所依赖的插件已经设置了它们的导出接口。例如,Core插件设置Core::ActionManager, Core::EditorManager和所有其他公开可用的接口,以便其他插件可以请求和使用它们。
    插件的initialize()函数是执行以下操作的好地方:
  • 在插件管理器的对象池中注册对象
  • 加载设置
  • 向菜单添加新菜单和新动作
  • 连接到其他插件的信号
  1. 将插件设置为Initialized 状态。
  2. 以与加载队列相反的顺序调用所有插件的extensionsInitialized()函数。在使用extensionsInitialized函数之后,插件应该被完全初始化、设置并运行。一个插件可以假设依赖于它的插件已经完全设置好了,并且可以完成可以被其他插件扩展的部分的初始化。例如,Core插件假设所有插件都注册了它们的动作,并完成动作管理器的初始化。
  3. 将插件设置为Running 状态。

在setup结束时,Core插件的Core::ICore发送两个信号。在Qt Creator UI显示之前,它发送coreAboutToOpen(),之后发送coreOpened()。

启动后,当Qt Creator的事件循环运行时,插件管理器按加载队列的相反顺序调用所有插件的delayedInitialize()函数。这些调用在主线程上完成,但间隔了几毫秒的延迟,以确保Qt Creator的响应性。在delayedInitialize函数中,插件可以执行非关键初始化,如果在启动期间执行,可能会不必要地延迟显示Qt Creator UI。

所有延迟初始化完成后,PluginManager发送initializationDone()信号。

在关闭之前,Core插件Core::ICore发送coreAboutToClose()信号。之后,插件管理器开始关闭程序:

  1. 按照加载队列的顺序调用所有插件的aboutToShutdown()函数。插件应该执行加速实际关闭的措施,比如断开本来不必要调用的信号。如果一个插件需要延迟真正的关机一段时间,例如,如果它需要等待外部进程完成干净关机,插件可以从这个函数返回ExtensionSystem::IPlugin::AsynchronousShutdown。这将使插件管理器等待下一步,并保持主事件循环运行,直到所有请求AsynchronousShutdown的插件都发送了asynchronousShutdownFinished()信号。
  2. 通过以与加载队列相反的顺序删除它们的ExtensionSystem::IPlugin实例来销毁所有插件。此时调用插件析构函数。插件应该通过释放内存和其他资源来清理自己。

转载请注明出处,以上只代表个人观点,引用不当或侵权请联系删除。

posted @ 2023-09-12 19:06  yg1990  阅读(174)  评论(0编辑  收藏  举报