Расширение функционала редактора
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, объявляется следующим образом:
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 в ваш проект) и может быть получена во время выполнения из экземпляра класса 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 — версия, с которой должен быть совместим плагин для заполнения зависимости, в виде x.y.z. Может быть пустым, если версия не имеет значения.
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.Ищет в своих путях поиска все динамические библиотеки и считывает их метаданные. Все библиотеки без метаданных и без IID com.unigine.EditorPlugin игнорируются. Начальное состояние всех плагинов — 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::PluginInfo с помощью функции Editor::PluginManager::plugins().
- 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);