How to create a Qt Creator plugin in C++, a working example explained

This article will show you all you need to know to create a Qt Creator plugin in C++.

Qt Creator is the official IDE of Qt and one of the most used by C++ developers. Creating a plugin for Qt Creator is the best way to extend its functionalities and it’s pretty easy to do as you’re going to learn here.

Before we start, you need to know that everything you will find here is based and tested on Qt 5.14 and Qt Creator 4.11, which are the latest versions at the time of writing. Things might change a bit in the future, but hopefully key concepts will stay the same.

Some theory

Before moving to something practical, it’s good to understand few concepts behind plugin design and implementation.

Plugin Structure

From a software design point of view, a Qt Creator plugin is extremely simple. In the simplest case, the only class you really need is one that implements the ExtensionSystem::IPlugin interface.

More realistically, you probably need to handle user settings as well, hence you might also need to implement the following classes:

Something that stores your user settings during execution (like the class Settings).

An implementation the Core::IOptionsPage interface for every option page you want in the Qt Creator options dialog (Tools > Options).

A class extending a QWidget to populate the content of an option page.

Don’t worry if things seem a bit abstract now, they will become more clear after going trough the code later.

Plugin Lifecycle

Qt Creator plugins have a well defined lifecycle for both startup and shutdown and it’s important you know how things work before creating one.

When the application starts, all plugins are put in a loading queue according to their dependencies (i.e.: what other plugins they depend on).

After that, the first part of the initialization process can begin and for each plugin:

Plugin() constructor is called following queue order

queue order initialize() is called following queue order – everything that requires dependencies can be done here

queue order extensionsInitialized() is called in reverse queue order – use it to access initialized dependencies

reverse queue order – use it to access initialized dependencies Core::ICore sends the signal coreAboutToOpen()

Then the Qt Creator UI is shown on screen and the second part of the inizialization process begins:

Core::ICore sends the signal coreOpened()

delayedInitialize() is called in reverse queue order and with some delay between each call – this is for non critical initialization to minimize loading time of Qt Creator

reverse queue order and with some delay between each call PluginManager sends the initializationDone() signal

At this stage every plugin should be initialized and fully operational.

A similar process happens when the application is about to close:

Core::ICore sends the coreAboutToClose() signal

aboutToShutdown() is called following queue order and what happens next depends on the value returned: Return SynchronousShutdown to notify termination Return AsynchronousShutdown to make Qt Creator wait until the plugin sends the signal asynchronousShutdownFinished()

queue order and what happens next depends on the value returned: ~Plugin() destructor is called in reverse queue order

An example Qt Creator plugin: QtC Timer

The plugin we’re going to create here is pretty simple. It shows a message to suggest the user to take a break after using Qt Creator for a certain amount of time.

To make things a bit more interesting, the plugin also has a dedicated options page in the Options dialog accessible from the Tools menu of Qt Creator. The only option available is the time between 2 messages.

Keep in mind that you don’t have to use the Options dialog for your settings. You could create a sub-menu under the Tools menu or you could handle everything in a context menu that appears only when needed. There’s no right way to do this, it’s entirely up to you and your project.

I decided to keep this plugin as simple as possible to focus on the key concepts, but it’s not hard to extend it with more complex features. For example:

Better time handling (consider inactivity, when the window is minimized, etc..)

Different messages at different times (intervals or absolute)

Allow to accept/cancel notification

Usage tracking (session length, session times, user response, etc…)

Play sounds when showing a message

More options to handle all the new features

As you can see, even something this simple can be extended quite a lot. Your imagination is the limit.

Let’s create a Qt Creator plugin

After seeing the basic concepts and defining what we are going to create, it’s time to write some code!

Setting up

The first step to create a Qt Creator plugin is to download and to install the latest versions of Qt and Qt Creator from the Qt website. The easiest way to do that is with the online installer. You will also need to install the Qt Script package which is required to build Qt Creator (even if it’s deprecated).

The second step is cloning the repository of Qt Creator to get its source code:

$ git clone http://code.qt.io/qt-creator/qt-creator.git

Then, you want to make sure you are going to build the code of a specific release. To do that just create a branch which points to the most recent tag:

$ cd qt-creator $ git checkout -b 4.11 v4.11.0

Finally, you want to initialize all the submodules of the repository:

$ git submodule init $ git submodule update --recursive

Now you have all you need to build the source code and to do that just launch Qt Creator and open qtcreator.pro, which you can find in the root directory of the repository. After a bit of loading the project will be ready and you can start to build. I recommend to build it in debug and release mode using shadow builds.

Something to remember when building Qt Creator from code is that ideally you need to use the same compiler and the same version of Qt used by its developers to build the official binaries. You can find this information in the About Qt Creator dialog in the Help menu. For example, Qt Creator 4.11 has been built on Linux using gcc 5.3.1 and Qt 5.14. Using similar compilers (like gcc 5.4) is usually fine, but changing things too much (for example Clang) is probably not going to work.

Create a Qt Creator plugin project

After launching Qt Creator, you can create a plugin using the New Project wizard.

To find the right wizard, navigate these menus in the toolbar: File > New File or Project > Projects > Library > Qt Creator Plugin

The first step in the wizard will let you choose a location for your files and a project name.

Then you select a kit to use to build the plugin, make sure to match compiler and Qt version with the ones used to build Qt Creator from code.

Finally, you can enter plugin information that will be shown in the plugin details page in Qt Creator. All fields are pretty self-explanatory.

The only field that is not so obvious is “Deploy into”, which offers 2 options:

Local user settings – for installing your plugin in the system directory. This makes it available to every instance of Qt Creator you launch. Qt Creator build – for installing your plugin only for your local build of Qt Creator (the one you use to compile the plugin).



Anyway, don’t worry too much about anything in this wizard page as you will be able to change things later.

Plugin project

After finishing the new project wizard you have a basic default project with the following files in it:

qtctimer.pro – qmake project file

qtctimerplugin.h/.cpp – plugin class files

qtctimer_global.h – header with macros to export symbols of your plugin. In most cases you can delete this and all the related #includes as well

qtctimerconstants.h – constants used by your plugin. Eventually you can delete this as well

QtCTimer.json.in – this stores the plugin information (and, for some reason, it’s the only file which is not all lowercase)

The next step after creating a plugin project is setting the following variables in the build environment for both Debug and Release modes:

QTC_SOURCE – path to the source code of Qt Creator (same for debug and release)

QTC_BUILD – path to the build directory of Qt Creator (this should be different for debug and release)

Eventually at some point you will also need to add plugin dependencies. You can do that by editing the QTC_PLUGIN_DEPENDS variable in your .pro file. Simply add the name of the plugins you depend on when needed.

The default project creates a simple plugin that adds a menu to the toolbar and shows an info dialog when its entry is clicked. You can build it to test that everything is fine, but after that delete all the code inside the functions in the cpp file to start from scratch.

Plugin source code

It’s time to dive into the code of our plugin, which contains the following classes:

QtCTimerPlugin – the plugin class that inherits from ExtensionSystem::IPlugin

– the plugin class that inherits from ExtensionSystem::IPlugin Settings – the class handling the options

– the class handling the options OptPageMain – the options page that inherits from Core::IOptionsPage

– the options page that inherits from Core::IOptionsPage OptPageMainWidget – a QWidget that defines the content of the options page

In the following paragraphs I am going to describe in detail the first 3 classes. The QWidget is not described here because it’s simple Qt code to define a basic UI.

For simplicity I have also omitted some less important parts of the code. Don’t worry about them for now, you can read and experiment with the full source code later.

The plugin class – header

First of all, you need a class that handles all the logic of the plugin and acts like mediator to connect the other ones. This class is the one that inherits from ExtensionSystem::IPlugin.

#include <extensionsystem/iplugin.h> class QtCTimerPlugin : public ExtensionSystem::IPlugin { Q_OBJECT Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "QtCTimer.json")

This class is automatically created by the project wizard and you don’t need to take care of implementation details like adding the right macros. You will only need to implement its pure virtual functions like initialize() and extensionsInitialized(), but also any other virtual member you might need like aboutToShutdown().

public: bool initialize(const QStringList & arguments, QString * errorString) override; void extensionsInitialized() override; ShutdownFlag aboutToShutdown() override; }

The plugin class – implementation

The first member to implement is initialize(), which does 2 things:

First, it creates the Settings object to store and handle user settings. It also create the option page that will allow the user to change such settings.

bool QtCTimerPlugin::initialize(const QStringList & arguments, QString * errorString) { // -- OPTIONS PAGE -- mSettings = new Settings; mSettings->Load(); mOptionsPage = new OptPageMain(mSettings, this); connect(mOptionsPage, &OptPageMain::SettingsChanged, this, &QtCTimerPlugin::UpdateTimer);

When the settings change, we execute the slot UpdateTimer, which updates the timer we are about to create.

The second part of this method creates and initializes the timer that controls when to show a message on screen.

// -- TIMING -- mTimer = new QTimer; UpdateTimer(); connect(mTimer, &QTimer::timeout, this, &QtCTimerPlugin::OnTimeout); mTimer->start(); return true; }

Every time the timeout signal is emitted, we execute the slot OnTimeout, which uses a QMessageBox to show a dialog with a message.

As we don’t really need to do anything with extensionsInitialized(), we can leave it empty.

In aboutToShutdown() we disconnect all the signals and stop the timer as we are about to exit.

ExtensionSystem::IPlugin::ShutdownFlag QtCTimerPlugin::aboutToShutdown() { mTimer->stop(); mTimer->disconnect(); mOptionsPage->disconnect(); return SynchronousShutdown; }

All the objects created with new are destroyed in the destructor, but I am not going to show the code here as it’s nothing special or complicated.

The Settings class – header

This class stores the user settings during execution. It also takes care of loading and saving them.

The header file is pretty simple and it can be divided in 3 parts:

Setters and getters for the data (omitted here for simplicity)

2 functions to load and save data

2 operators to compare a pair of Settings objects

class Settings { public: // ...DATA getters and setters... // -- I/O -- void Load(); void Save(); // -- OPERATORS -- bool operator==(const Settings & other) const; bool operator!=(const Settings & other) const; };

This is also the only class of this plugin which is not a QObject.

The Settings class – implementation

The Load() member handles only 1 option (mTimeLimit), but it shows all you need to handle any number.

The first thing you need to do when working with Qt Creator settings is to get access to a QSettings object to read/write user settings. You can do that with a single static call:

void Settings::Load() { QSettings * s = Core::ICore::settings();

Now you can use the QSettings object to access your options. You do that with the function beginGroup(), which takes as parameter a QString that identifies your group of settings.

Once you are inside your group of settings you can get the value of any option by calling the value() function. This function takes a QString (OPT_TIME_LIMIT) that identifies the specific setting and a default value (DEF_TIME_LIMIT) that is returned when the wanted setting is missing. This function returns a QVariant, so you have to call the right conversion function (toInt() in this case) to get a proper value,

s->beginGroup(GROUP); mTimeLimit = s->value(OPT_TIME_LIMIT, DEF_TIME_LIMIT).toInt(); s->endGroup(); }

When you are done loading all the settings you can close the group with endGroup().

Saving is specular to loading. The only difference is that this time you call the function setValue() to store each setting. The function takes a QString (OPT_TIME_LIMIT) that identifies the specific setting and a QVariant that contains the data to save. Remember that QVariant has many implicit constructors, so unless you need to save some complex data, you can just rely on that.

void Settings::Save() { QSettings * s = Core::ICore::settings(); s->beginGroup(GROUP); s->setValue(OPT_TIME_LIMIT, mTimeLimit); s->endGroup(); }

The 2 operators (== and !=) are pretty straightforward as they only compare data members, so I will not discuss them here, but you can check their code later.

Options page – header

For every page you need in the Options dialog of Qt Creator, you need to implement a Core::IOptionsPage, which contains the meta information necessary to populate the dialog.

#include <coreplugin/dialogs/ioptionspage.h> class OptPageMain : public Core::IOptionsPage { Q_OBJECT public: OptPageMain(Settings * settings, QObject * parent = nullptr);

Every option page should have access to the Settings object as that’s needed for reading and writing settings. Hence, it’s good practice to pass it to its constructor.

Then it’s necessary to override the following 3 members as they are pure virtual in the interface class.

QWidget * widget() override; void apply() override; void finish() override;

Finally, it’s good to have a signal to notify the plugin class when things change.

signals: void SettingsChanged(); };

Options page – implementation

The class constructor sets all the meta data needed by Qt Creator to handle the page properly.

OptPageMain::OptPageMain(Settings * settings, QObject * parent) : IOptionsPage(parent) , mSettings(settings) { setId("QtCTimerSettings"); // page ID setDisplayName("General"); // page label in tabs setCategory("QtC Timer"); // category ID setDisplayCategory("QtC Timer"); // category label in options - set once setCategoryIcon(Utils::Icon(":/imgs/icon_timer.png")); // category icon - set once }

The setId() and setDisplayName() functions define the ID and the visible name of the page, whereas the setCategory(), setDisplayCategory() and setCategoryIcon() define the ID, visible name and icon of the group of pages used by our plugin. You need to call the last 2 functions only once, and you can do that in the first page you create.

The widget() functions simply creates the widget that defines all the content of the page and returns it.

QWidget * OptPageMain::widget() { if(nullptr == mWidget) mWidget = new OptPageMainWidget(mSettings); return mWidget; }

Eventually you could create a widget only once, for example in the constructor, but usually it’s better to create it when needed and to destroy it when the dialog is closed to save memory.

The apply() method is called when the settings are accepted by the user, which is when they push the buttons “Ok” or “Apply” of the options dialog.

void OptPageMain::apply() { const Settings newSettings = mWidget->GenerateSettings(); if(newSettings != *mSettings) { *mSettings = newSettings; mSettings->Save(); emit SettingsChanged(); } }

This member gets a new Settings object from the widget, which eventually replaces the existing one if something has changed.

Things might be more elaborated when you have to handle complex settings and the Settings object gets bigger, but in our case (and in many others) this is enough.

The finish() method is called when the options dialog is closed.

void OptPageMain::finish() { delete mWidget; mWidget = nullptr; }

It simply deletes the widget to save memory, as discussed before.

Resources and references

Get the source code

The full source code of this plugin is available on GitHub and released under the Unlicense license.

Remember that you will also need the source code of Qt Creator to build this example.

A more advanced plugin: SIGBUILD

In case you wanted to experiment with something more advanced you can check out my plugin SIGBUILD, available on GitHub as well.

SIGBUILD notifies you when your builds terminate and tracks their stats. It offers several types of notifications and it comes with a nice systray icon you can use to monitor your builds without even opening Qt Creator.

Support this blog

If you find this post useful you might consider to sponsor me on GitHub.

With your contribution I will be able to spend more time writing posts about Qt like this one and working on my other open source project like SIGBUILD.

Stay connected

Don’t forget to subscribe to the blog newsletter to get notified of future posts.

You can also get updates following me on Github, LinkedIn and Twitter.