如何擴展編輯器功能
UNIGINE enables you to extend the core UnigineEditor functionality, e.g., add new menus, windows, tool bar commands, sub-modes, define how properties are displayed, and even create your custom Editor. This is made possible by UnigineEditor's Plugin System via adding custom user plugins.UNIGINE 使您能够扩展 UnigineEditor 的核心功能,例如,添加新的菜单、窗口、工具栏命令、子模式,定义属性的显示方式,甚至创建您的自定义编辑器。 UnigineEditor 使这成为可能插件系统通过添加自定义用户插件.
UnigineEditor is an application written entirely in C++ relying a lot on the Qt5 framework infrastructure. So, in order to extend its functionality not only C++ programming skills are required, but you should also be familiar with the Qt5 framework, CMake build system.UnigineEditor 是一个完全用 C++ 编写的应用程序,很大程度上依赖于 Qt5 框架基础架构。因此,为了扩展其功能,不仅需要 C++ 编程技能,还应该熟悉 Qt5 框架、CMake 构建系统。
See Also也可以看看#
- Creating Your First Editor Plugin article to learn how to create a custom Editor plugin.创建您的第一个编辑器插件文章了解如何创建自定义编辑器插件。
- Editor API Reference for more information on all available classes.编辑器 API 参考以获取有关所有可用类的更多信息。
Editor Plugin System编辑器插件系统#
Plugins are collections of code and data that developers can easily enable or disable within UnigineEditor on a per-project basis. Each plugin is compiled into a dynamic library that is shipped separately, it is detected and loaded at run time by UnigineEditor.插件是代码和数据的集合,开发人员可以在 UnigineEditor 中根据每个项目轻松启用或禁用它们。每个插件都被编译成一个单独发布的动态库,UnigineEditor 在运行时检测并加载它。
Basically the structure of the Plugin System is as follows:插件系统的基本结构如下:
Here is a brief description of the main components:以下是主要组件的简要说明:
- QPluginLoader is responsible for plugin loading and providing meta data.QPluginLoader 负责插件加载和提供元数据。
- Editor::PluginInfo — plugin meta data parsing and storage, interaction with plugin interface, it also contains the current plugin state and information on a possible plugin initialization error.Editor::PluginInfo — 插件元数据解析和存储,与插件接口的交互,它还包含当前插件状态和可能的插件初始化错误的信息。
- Editor::PluginManager — a manager class responsible for locating plugins, building plugin loading queue, as well as loading and removing plugins.Editor::PluginManager — 一个管理器类,负责定位插件、构建插件加载队列以及加载和删除插件。
-
Editor::Plugin — the basic class all Editor plugins inherit from, it has the following declaration:Editor::Plugin — 所有编辑器插件继承的基本类,它具有以下声明:
Editor::Plugin Class DeclarationEditor::Plugin 类声明
namespace Editor { class EDITOR_API Plugin { public: Plugin(); virtual ~Plugin(); // Plugin's life cycle. virtual bool init() = 0; virtual void shutdown() = 0; }; } // namespace Editor // Associates the given Identifier (a string literal) to the interface class called Editor::Plugin. Q_DECLARE_INTERFACE(Editor::Plugin, "com.unigine.EditorPlugin")
It has two abstract methods defining the plugin's life cycle, you should override them for your custom plugin:它有两个定义插件的抽象方法生命周期,您应该为您的自定义插件覆盖它们:
- init() — plugin initialization (returns initialization result)init() — 插件初始化(返回初始化结果)
- shutdown() — plugin shutdownshutdown() — 插件关闭
The Q_DECLARE_INTERFACE macro adds the interface to Qt's metasystem.Q_DECLARE_INTERFACE 宏将接口添加到 Qt 的元系统。
Locating Plugins定位插件#
Plugins having their libraries located in the directory given below, are automatically loaded and added to the list of available UnigineEditor plugins, no specific code is required:其库位于下面给出的目录中的插件会自动加载并添加到可用的 UnigineEditor 插件列表中,不需要特定代码:
- %project%/bin/plugins/ — for Debug and Release builds and any SDK build.%project%/bin/plugins/ — 用于 Debug, Release 构建和任何 SDK 构建。
To see the list of all currently loaded plugins in UnigineEditor, choose Windows -> Editor Plugin Manager.要查看 UnigineEditor 中所有当前加载的插件的列表,请选择 Windows -> Editor Plugin Manager。
You can view any plugin's description by selecting it in the list and clicking Details.您可以通过在列表中选择并单击 Details 查看任何插件的描述。
Plugin Meta Data插件元数据#
Each plugin must have additional information required by the plugin manager to find your plugin and resolve its dependencies before actually loading your plugin's library file. This information is stored in a meta file in JSON format (myplugin.json file automatically generated when adding an Editor plugin template to your project) and can be obtained at run time from an instance of the Editor::PluginInfo class. This meta file may look as follows: 每个插件都必须有插件管理器需要的附加信息才能找到您的插件并解决它依赖关系在实际加载插件的库文件之前。 此信息以 JSON 格式存储在元文件中(将编辑器插件模板添加到项目时自动生成 myplugin.json 文件),并且可以在运行时从 Editor::PluginInfo 类的实例中获取。此元文件可能如下所示:
{
"Name" : "MyPlugin",
"Vendor" : "Unigine",
"Description" : "The plugin's description text." ,
"Version" : "@PLUGIN_VERSION@",
"CompatVersion" : "@PLUGIN_COMPAT_VERSION@"
"Dependencies" : [
{
"Name": "RequiredPlugin",
"Type": "required",
"Version" : "2.9.0.0"
},
{
"Name": "OptionalPlugin",
"Type": "optional",
"Version" : "2.8.0"
}
]
}
Here is a brief overview of the basic elements:以下是基本要素的简要概述:
- Name — plugin name displayed in UnigineEditor's Plugins List, and used as reference when describing other plugins dependencies.Name — UnigineEditor 的插件列表中显示的插件名称,在描述其他插件依赖项时用作参考。
- Vendor, Description — additional information (optional).Vendor, Description — 附加信息(可选)。
- Version — current plugin version.Version — 当前插件版本。
- CompatVersion — last binary compatible plugin version, defines which version of this plugin the current version is binary backward compatible with and is used to resolve dependencies on this plugin.CompatVersion — 最后一个二进制兼容插件版本,定义当前版本与该插件的哪个版本二进制向后兼容,并用于解决对该插件的依赖关系。
- Dependencies — list of objects describing dependencies on other plugins.Dependencies — 描述对其他插件的依赖关系的对象列表。
- Name — name of the plugin, on which this plugin relies.Name — 该插件所依赖的插件名称。
- Type — dependency type, can be either required or optional.Type — 依赖类型,可以是 required 或 optional。
- Version — version with which the plugin must be compatible to fill the dependency, in the form x.y.z. Can be empty if the version does not matter.Version — 插件必须兼容的版本才能填充依赖,格式为 xyz 如果版本无关紧要可以为空。
Actually, the author creates a .json.in file, which is then used by CMake to generate the actual plugin .json meta data file, replacing variables like EDITOR_VERSION with their actual values.实际上,作者创建了一个 .json.in 文件,然后 CMake 使用该文件生成实际的插件 .json 元数据文件,将 EDITOR_VERSION 等变量替换为其实际值.
Plugin Dependencies插件依赖#
A plugin can rely on other plugins. Such dependencies are specified in the plugin meta data, to ensure that these other plugins are loaded before this one.一个插件可以依赖于其他插件。插件元数据中指定了此类依赖项,以确保在此之前加载这些其他插件。
Dependencies are declared with the key Dependency that contains an array of JSON objects with required keys Name and Version, and the optional key Type.依赖项使用键 Dependency 声明,该键包含 JSON 对象数组,所需键为 Name 和 Version,可选键为 Type。
The following formulas illustrate how the dependency information is matched. In the formulas the name of the required plugin (as defined in Name of the dependency object) is denoted as DependencyName and the required version of the plugin is denoted as DependencyVersion. A plugin with given Name, Version, and CompatVersion as defined in the plugin meta data matches the dependency if the following conditions are met:下面的公式说明了依赖信息是如何匹配的。在公式中,所需插件的名称(在依赖对象的 Name 中定义)表示为 DependencyName,所需的插件版本表示为 DependencyVersion。如果满足以下条件,则插件元数据中定义的具有给定 Name, Version 和 CompatVersion 的插件与依赖项匹配:
- Its Name matches DependencyName.它的 Name 匹配 DependencyName。
- CompatVersion <= DependencyVersion <= Version
For example, a dependency例如,一个依赖
{
"Name" : "SomeOtherPlugin",
"Version" : "2.4.1"
}
would be matched by a plugin with将由一个插件匹配
{
"Name" : "SomeOtherPlugin",
"Version" : "3.1.0",
"CompatVersion" : "2.2.0",
...
}
since the name matches, and the version 2.4.1 given in the dependency tag lies between 2.2.0 and 3.1.0.因为名称匹配,并且依赖标记中给出的版本 2.4.1 位于 2.2.0 和 3.1.0 之间。
Plugin Life Cycle插件生命周期#
To be able to write Editor plugins, you must understand the steps that the plugin manager takes when you start or shut down UnigineEditor.为了能够编写编辑器插件,您必须了解插件管理器在您启动或关闭 UnigineEditor. 时所采取的步骤
When you start UnigineEditor, the plugin manager does the following:当您启动 UnigineEditor时,插件管理器会执行以下操作:
- Looks in its search paths for all dynamic libraries, and reads their meta data. All libraries without meta data and the ones without the com.unigine.EditorPlugin IID are ignored. The initial state of all plugins is INVALID as this is the first point where loading a plugin can fail in the worst case of malformed meta data.在其搜索路径中查找所有动态库,并读取它们的元数据。所有没有元数据的库和没有 com.unigine.EditorPlugin IID 的库都将被忽略。所有插件的初始状态是 INVALID,因为这是在元数据格式错误的最坏情况下加载插件可能失败的第一个点。
- Creates an instance of the Editor::PluginInfo class for each plugin. Being a container for the plugin meta data, this class also tracks the current plugin state. You can get the Editor::PluginInfo instances via the Editor::PluginManager::plugins() function.为每个插件创建一个 Editor::PluginInfo 类的实例。作为插件元数据的容器,此类还跟踪当前插件状态。您可以通过 Editor::PluginManager::plugins() 函数获取 Editor::PluginInfo 实例。
- Sets the plugins to the READ state.将插件设置为 READ 状态。
- Verifies that the dependencies of each plugin exist and are compatible.验证该依赖关系每个插件都存在并且兼容。
- Sets the plugins to the RESOLVED state.将插件设置为 RESOLVED 状态。
- Sorts all plugins into a list called the "loading queue", where the dependencies of a plugin are positioned after the plugin (but not necessarily directly after it). It ensures that plugins are loaded and initialized in proper order.将所有插件排序到一个称为“加载队列”的列表中,其中插件的依赖项位于插件之后(但不一定直接在它之后)。它确保插件以正确的顺序加载和初始化。
- Loads the plugins' libraries, and creates their Editor::Plugin instances in the order of the loading queue. At this point the plugin constructors are called. Plugins that other plugins depend on are created first.加载插件的库,并按照加载队列的顺序创建它们的 Editor::Plugin 实例。此时调用插件构造函数。首先创建其他插件依赖的插件。
- Sets the plugins to the LOADED state.将插件设置为 LOADED 状态。
-
Calls the init() functions of all plugins in accordance with the loading queue. In the init() function, a plugin should make sure that all exported interfaces are set up and available to other plugins. As each plugin assumes that plugins it depends on have set up their exported interfaces.按照加载队列调用所有插件的init()函数。在 init() 函数中,插件应确保所有导出的接口都已设置并可用于其他插件。由于每个插件都假定它所依赖的插件已经设置了它们的导出接口。
The plugin init() function is a good place for:- Creating new objectsCreating new objects
- Loading settingsLoading settings
- Adding new menus, and new actions to themAdding new menus, and new actions to them
- Connecting to other plugin's signalsConnecting to other plugin's signals
- Creating new objects创建新对象
- Loading settings加载设置
- Adding new menus, and new actions to them添加新菜单和新操作
- Connecting to other plugin's signals连接到其他插件的信号
- Sets the plugins to the RUNNING state.将插件设置为 RUNNING 状态。
On UnigineEditor shutdown the plugin manager starts its shutdown sequence:在UnigineEditor 关闭时,插件管理器开始其关闭序列:
- Calls the shutdown() functions of all plugins in the order of the loading queue. Plugins should perform measures for speeding up the actual shutdown here, like disconnecting signals that would otherwise needlessly be called.按照加载队列的顺序调用所有插件的 shutdown() 函数。插件应该在此处执行加速实际关闭的措施,例如断开否则会不必要地调用的信号。
- Destroys all plugins by deleting their Editor::Plugin instances in reverse order of the loading queue. At this point the plugin destructors are called. Plugins should clean up after themselves by freeing memory and other resources.通过以加载队列的相反顺序删除它们的 Editor::Plugin 实例来销毁所有插件。此时会调用插件析构函数。插件应该通过释放内存和其他资源自行清理。
Hot Reload热重载#
Editor plugins support hot reloading, which can be useful in cases, when you need to perform actions like:编辑器插件支持热重新加载,这在您需要执行以下操作时很有用:
- Unload a plugin.卸载插件。
- Do something.做一点事。
- Reload the plugin.重新加载插件。
You can unload and Load your plugin again when necessary via the corresponding button in the HotReload column (accessible via the Editor Plugin Manager window: Windows -> Editor Plugin Manager).您可以在必要时通过 HotReload 列中的相应按钮再次卸载和加载插件(可通过 Editor Plugin Manager 窗口访问:Windows -> Editor Plugin Manager)。
You can also do the same via code:您也可以通过代码执行相同的操作:
Unigine::Vector<Editor::PluginInfo *> plugin_infos = Editor::PluginManager::plugins();
// choose your `PluginInfo*`:
Editor::PluginInfo *required_plugin_info = ..;
// Unload a plugin:
bool unloaded = Editor::PluginManager::unloadPlugin(required_plugin_info);
// Do something
// ...
// Reload the plugin
bool loaded = Editor::PluginManager::loadPlugin(required_plugin_info);