Welcome, Guest! Log In | Create Account

Plugins

From labplot

Jump to: navigation, search

Contents

Plugins based on Qt's plugin system

This article is meant to explain the inner workings of the plugin system used in the backend to a degree where it completely loses any "mystic aura". "Plugin system" may sound like something complicated, but it really isn't. All it takes is to know how to build and link libraries and to understand two macros (and to know how to use another two for implementing plugins).

How do Qt plugins work ?

A Qt plugin is basically a library which uses a standardized way to access the so-called root component object, a normal C++ object which inherits from QObject. Once you have this root object, you use qobject_cast to check which of your predefined plugin interfaces it implements. See Q_DECLARE_INTERFACE and Q_INTERFACES in the Qt documentation for details. In short, these two macros register your interfaces (i.e., abstract C++ classes) to Qt's meta object system.

Such a library can be statically or dynamically linked into the application.

To understand how the plugin system works internally, one has to look at the involved macros defined in qplugin.h (to be found in /src/corelib/plugin in the official source archive):

Q_EXPORT_PLUGIN2
and
Q_IMPORT_PLUGIN
.

These macros are different based on whether QT_STATICPLUGIN is defined or not. Important to note here is that "static plugin" does not mean statically linked but that there is a static (using the static C++ keyword) object involved. Static plugins can also be linked as dynamic libraries. The difference to a dynamic plugin is that the application will not start if a static plugin is missing, it will run without dynamic plugins though. Since being optional is basically the whole point of plugins, static plugins are usually only used to unify the interface to mandatory and optional components.

Declaring a plugin: Q_EXPORT_PLUGIN2

The Q_EXPORT_PLUGIN2 macro is responsible for declaring the necessary code to access the plugin. It is defined in the code implementing the plugin.

For static plugins the Q_EXPORT_PLUGIN2(PLUGIN, PLUGINCLASS) macro expands to roughly this (somewhat simplified):

QObject *qt_plugin_instance_PLUGIN()
{
  static QPointer<QObject> _instance;
  if (!_instance)
    _instance = new PLUGINCLASS;
  return _instance;
}

So, basically, you have a static instance of the root object and a function named using a standard prefix followed by the plugin name to get a pointer to this instance. BTW: In this case (i.e., with QT_STATICPLUGIN defined), Q_EXPORT_STATIC_PLUGIN2(PLUGIN, PLUGINCLASS) is exactly the same as Q_EXPORT_PLUGIN2(PLUGIN, PLUGINCLASS).

For dynamic plugins, Q_EXPORT_PLUGIN2(PLUGIN, PLUGINCLASS) expands to (also somewhat simplified):

extern "C" {
  QObject * qt_plugin_instance();
  {
    static QPointer<QObject> _instance;
    if (!_instance)
      _instance = new PLUGINCLASS;
    return _instance;
  }
}

So it boils down to defining the root object access function as extern "C". In addition, a function called qt_plugin_query_verification_data() is defined which is used to check whether the plugin is compatible with the application its loaded in (same Qt version etc.). BTW: Q_EXPORT_STATIC_PLUGIN2(PLUGIN, PLUGINCLASS) does nothing if QT_STATICPLUGIN is not defined.

Using static plugins: Q_IMPORT_PLUGIN

The Q_IMPORT_PLUGIN(PLUGIN) macro, which is used in the code using the static plugin, expands to the following:

extern QObject *qt_plugin_instance_##PLUGIN();
class Static##PLUGIN##PluginInstance {
  public:
    Static##PLUGIN##PluginInstance() {
    qRegisterStaticPluginInstanceFunction(qt_plugin_instance_##PLUGIN);
  }
};
static Static##PLUGIN##PluginInstance static##PLUGIN##Instance;

This is the static object I was talking about at the beginning. It is created during the static initialization of the application and registers the plugin root object access function to a global list. This list is used by static QObjectList QPluginLoader::staticInstances() which returns all plugin root component objects.

The simplest way to make your static plugin available via QPluginLoader::staticInstances() therefore is:

#define QT_STATICPLUGIN
Q_EXPORT_PLUGIN2(scidavis_standardcurvesymbolfactory, StandardCurveSymbolFactory)
Q_IMPORT_PLUGIN(scidavis_standardcurvesymbolfactory)

Together with Q_DECLARE_INTERFACE and Q_INTERFACES that's all it takes to define a static plugin.

Using dynamic plugins: QPluginLoader/PluginManager

As mentioned above, static plugins must be present for the application to run. They are completely normal library dependencies of the executable. Dynamic plugins are what plugins really should be: optional functionality which can be added at runtime. They are always compiled as dynamic libraries and then loaded at runtime by QPluginLoader (no Q_IMPORT_PLUGIN involved here). QPluginLoader is well documented in the Qt docs, so I won't go into detail here, there is no need to know it anyway, neither when writing plugins nor building the application.

There exists a class called PluginManager in the backend which encapsulates the whole usage of QPluginLoader. All you need to do is call PluginManager::plugins() which works like QPluginLoader::staticInstances() but also includes all dynamically loaded plugins. You can enable and disable plugins permanently (or at least until someone deletes the application config file because QSettings are used to store the list of enabled plugins) by calling PluginManager::enablePlugin() or PluginManager::disablePlugin(), respectively.