Skip to main content
Version: 2.0.0 (latest)

8.1. Extending features with plug-ins

Plug-ins are the best way to add a specific set of features to the software without changing its core code. pgModeler has a simple structure that provides the means to create third-party plug-ins. The first step in the development of a plug-in that handles pgModeler internals is to study pgModeler's API as well as the Qt plug-in system. You can generate pgModeler's code documentation by using the doxygen utility, running the command doxygen doxygen.conf at pgModeler's source code root. Once generated, the code documentation is stored in the folder docs under the source code directory.

Details about configuring and installing the doxygen application can be seen on its official website. Steps on how to retrieve the source code of pgModeler can be seen on the download page. Once you have the basic information about plug-ins, you can start to create your own extended features.

In the Qt framework, and consequently, in pgModeler, a plug-in is basically a C++ class implemented in a library installed in a path that is accessible by the application. The C++ class that implements the plug-in must follow two basic rules: inherit the class PgModelerGuiPlugin (plug-ins for the graphical interface) or PgModelerCliPlugin (plug-ins for the command-line interface), implementing all its pure virtual methods, and use the macros Q_INTERFACES and Q_PLUGIN_METADATA in its declaration. Another rule is that the plug-ins in pgModeler must have a basic directory structure to be properly loaded when the application starts. In the source code tree, a plug-in is a folder of the subproject plugins that has the following structure:

[PGMODELER_SOURCE_ROOT]/plugins/
|
+ - pluginA/
|
+ ---- CMakeLists.txt (build instructions)
|
+ ---- src/ (sources: pluginA.h, pluginA.cpp)
|
+ ---- res/
| |
| +--- pluginA.qrc (resource file)
| |
| +--- /pluginA (context)
| |
| +--- pluginA.png (icon)
|
+ ---- lang/ (optional, translation dictionaries: [language].ts)

Once built and installed, the directory used by pgModeler to load plug-ins has the following layout:

[PGMODELER_PLUGINS_DIR]/
|
+ - pluginA/
|
+ ---- (lib)pluginA.(so|dylib|dll) (library, with the resources compiled into it)
|
+ ---- lang/ (optional, compiled dictionaries: [language].qm)
|
+ ---- (other files installed by the plug-in: settings, schemas, etc)


On the first schema, the directory named pluginA stores all the assets and components of the extension. Inside that folder, there is a res subdirectory in which the icons used by the plug-in are registered in a resource file (.qrc), the src subdirectory with the plug-in's code, and an optional lang subdirectory with the dictionaries used to translate the plug-in's interface. On the second schema, the folder pluginA is located in the root directory used by pgModeler to load plug-ins, and holds the library that implements all the plug-in's logic, with the resources compiled into it. The name of the library is platform-dependent and can be: libpluginA.so (Linux), pluginA.dll (Windows), and libpluginA.dylib (macOS).

Since the plug-in needs to access the tool's features exposed by its classes, plug-ins are built together with pgModeler, using its build system. The next subsection explains the process.

Building plug-ins​

Since version 2.0, pgModeler is built with CMake, and so are its plug-ins: the plug-ins are subprojects built together with the tool, by the same build process that generates the entire tool. The folder plugins, at the source code root, holds the plug-ins of the Community Edition: sampleplugin, an example of a graphical plug-in, and samplecliplugin, an example of a plug-in for the command-line interface. Each plug-in folder has a CMakeLists.txt that uses the helper functions defined in the file PluginBuildConfs.cmake. To create your own plug-in, follow these steps:

  1. Download the source code, making sure it is the same version as your pgModeler installation, or the build may fail. The complete steps for the tool's build are available in the installation instructions.

    git clone https://github.com/nullptrlabs/pgmodeler.git

    Then check out the tag related to the pgModeler installed on your system. Supposing that the version in your system is 2.0.0, the checkout command must be:

    git checkout v2.0.0
  2. Create the plug-in folder in plugins/ (for instance, by copying sampleplugin) with the structure described above, and write the plug-in's CMakeLists.txt. The one below is a good starting point for a graphical plug-in:

    pgm_add_gui_plugin(pluginA OFF)

    target_sources(${PGM_TARGET} PRIVATE
    src/pluginA.cpp src/pluginA.h)

    pgm_set_plugin_res(${PGM_TARGET})
    pgm_install_plugin(${PGM_TARGET})

    The function pgm_add_gui_plugin (or pgm_add_cli_plugin, for command-line plug-ins) creates the plug-in's target; pgm_set_plugin_res compiles the resource file res/pluginA.qrc into the library; and pgm_install_plugin configures the installation of the library in the plug-ins root directory and of the optional translations found in the lang folder.

  3. Register the new folder in the file plugins/CMakeLists.txt:

    add_subdirectory(pluginA)
  4. Configure, build, and install pgModeler as described in the installation instructions. In short, from the source code root:

    cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
    cmake --build build
    cmake --install build

If the build process succeeds, you will see that some files are installed in the plug-ins root directory of your pgModeler installation. If you don't know where it is, just run pgModeler, open the settings dialog, and locate the Plug-ins settings; the path is presented in the field Plug-ins root directory. Now, just run pgModeler to test if the plug-ins are working properly. If a plug-in has any problem, pgModeler will refuse to load it and present a detailed error dialog. You can use the presented information to ask for help.

warning

pgModeler gives complete access to its classes and doesn't prohibit the plug-ins from reading and writing files outside their paths. Therefore, you must take extreme caution when developing a plug-in or even when installing a plug-in from suspicious sources, since this may harm your computer or cause data loss.

API changes since version 1.2​

If you are porting a plug-in written for the 1.2 series, these are the changes of the plug-in interface and of the GUI library that matter most (the complete list is in the file CHANGELOG.md of the source code):

  • The build system is CMake on all platforms (see the previous section) and the minimum required Qt version is 6.6.
  • PgModelerGuiPlugin::PluginWidgets gained the field extra_wgt, an auxiliary widget for general purposes, and the fields welcome_wgt_btn and w_wgt_btn_id, which let a plug-in install a button in the Welcome view; the position of the button is chosen with the enum PgModelerGuiPlugin::WelcomeWgtBtnId (NewModelBtn, LoadModelBtn, SampleModelsBtn, RecentModelsBtn, LastSessionBtn, SupportBtn, or AppendBtn to add it at the end).
  • The method PgModelerGuiPlugin::getWidgetDockMode() returns a WidgetDockMode that tells where the widgets of the plug-in are installed: DockOnMainWnd (side bar of the main window), DockOnSqlExecWgt (SQL execution panel), or NoDock (not docked, which must be avoided because it can lead to memory leaks).
  • MainWindow has the new signals s_modelClosed(), emitted when a model is closed, and s_modelLoadRequested(filename, model_idx), emitted when a model must be reloaded (see MainWindow::reloadModel()), so plug-ins can react to them or intercept the reload. The signal s_modelSaved is now emitted only when the model file is actually written to disk.
  • FileSelectorWidget was renamed to PathSelectorWidget, since it selects both files and directories; the button members of BaseForm were renamed from apply_ok_btn and cancel_btn to accept_btn and reject_btn; DebugOutputWidget::setLogMessages() is now setLogAppMessages().
  • The user interface is translatable in every executable, so texts that can't call tr() where they are declared must use the helpers PGM_TR2 and UtilsNs::pgmTr.

References​

How to create Qt plug-ins
http://doc.qt.io/qt-6/plugins-howto.html

Doxygen utility
http://www.doxygen.nl