The purpose of this plugin is to extend our Sample plugin by making it possible for the user to add two numbers through a dockable window.
i18n
└─ SampleWindow_fr.ts
res
└─ SampleWindow_i18n.qrc.in
src
├─ samplewindowplugin.cpp
├─ samplewindowplugin.h
├─ samplewindowplugin.json
├─ samplewindowwindow.cpp
├─ samplewindowwindow.h
└─ samplewindowwindow.ui
CMakeLists.txt
Our plugin is part of the Sample category, which means that its code can be found under [OpenCOR]/src/plugins/sample/SampleWindow/
.
Unlike for the Sample plugin, we want our plugin to interact with OpenCOR. This means that it needs to implement some interfaces.
More specifically, we want our plugin to be a dockable window, so we need to implement both the Plugin and Window interfaces. While we are at it, we might also internationalise our plugin, which means also implementing the Internationalisation interface.
As for the Sample plugin, our plugin has a CMakeLists.txt
file, which contents is:
1project(SampleWindowPlugin)
2
3# Add the plugin
4
5add_plugin(SampleWindow
6 SOURCES
7 ../../i18ninterface.cpp
8 ../../plugininfo.cpp
9 ../../plugininterface.cpp
10 ../../windowinterface.cpp
11
12 src/samplewindowplugin.cpp
13 src/samplewindowwindow.cpp
14 UIS
15 src/samplewindowwindow.ui
16 PLUGINS
17 Core
18 Sample
19)
The interfaces our plugin implements come with a .cpp
file, so we reference them (lines 7, 9 and 10).
Then, our plugin needs both the Core and Sample plugins (the latter, to be able to use its add()
function), so they are referenced (lines 17 and 18) using the PLUGINS
keyword (line 16).
Our plugin comes with a dockable window, which is implemented using various files (lines 13 and 15).
One of those files is a .ui
file, which is referenced using the UIS
keyword (line 14).
Our plugin information can be found in samplewindowplugin.cpp
, samplewindowplugin.h
and samplewindowplugin.json
. Starting with samplewindowplugin.h
, its contents is:
28#include "i18ninterface.h"
29#include "plugininfo.h"
30#include "plugininterface.h"
31#include "windowinterface.h"
32
33//==============================================================================
34
35namespace OpenCOR {
36namespace SampleWindow {
37
38//==============================================================================
39
40PLUGININFO_FUNC SampleWindowPluginInfo();
41
42//==============================================================================
43
44class SampleWindowWindow;
45
46//==============================================================================
47
48class SampleWindowPlugin : public QObject, public I18nInterface,
49 public PluginInterface, public WindowInterface
50{
51 Q_OBJECT
52
53 Q_PLUGIN_METADATA(IID "OpenCOR.SampleWindowPlugin" FILE "samplewindowplugin.json")
54
55 Q_INTERFACES(OpenCOR::I18nInterface)
56 Q_INTERFACES(OpenCOR::PluginInterface)
57 Q_INTERFACES(OpenCOR::WindowInterface)
58
59public:
60#include "i18ninterface.inl"
61#include "plugininterface.inl"
62#include "windowinterface.inl"
63
64private:
65 QAction *mSampleWindowAction = nullptr;
66
67 SampleWindowWindow *mSampleWindowWindow = nullptr;
68};
69
70//==============================================================================
71
72} // namespace SampleWindow
73} // namespace OpenCOR
As mentioned above, our plugin implements some interfaces, which means that their header file is included (lines 28, 30 and 31).
It also means that our plugin class inherits from those interfaces (lines 48 and 49), as well as makes calls to the Q_INTERFACES()
macro to let Qt know which interfaces it implements (lines 55-57).
Finally, we include the inline files (lines 60-62) that declare various methods that must be implemented by our plugin (see below).
(The rest of the class definition is specific to our plugin and is discussed below.)
The C function that is used by OpenCOR to retrieve some basic information about our plugin can be found in samplewindowplugin.cpp
:
39PLUGININFO_FUNC SampleWindowPluginInfo()
40{
41 static const Descriptions descriptions = {
42 { "en", QString::fromUtf8("a plugin that provides an addition window.") },
43 { "fr", QString::fromUtf8("une extension qui fournit une fenêtre d'addition.") }
44 };
45
46 return new PluginInfo(PluginInfo::Category::Sample, true, false,
47 { "Core", "Sample" },
48 descriptions);
49}
As can be seen, our plugin is selectable by the user, but it does not offer CLI support (line 46). It also has a direct dependency on both the Core and Sample plugins (line 47).
The implementation of the interfaces’ various methods can be found in samplewindowplugin.cpp
.
The methods are grouped by interface and are ordered alphabetically.
We start with the Internationalisation interface:
51//==============================================================================
52// I18n interface
53//==============================================================================
54
55void SampleWindowPlugin::retranslateUi()
56{
57 // Retranslate our Sample window action
58
59 retranslateAction(mSampleWindowAction,
60 tr("Sample"),
61 tr("Show/hide the Sample window"));
62}
63
64//==============================================================================
All that we need to do is (re)translate mSampleWindowAction
with the actual (French) translations in SampleWindow_fr.ts
(together with some other translations needed below).
Next, we have the Plugin interface:
64//==============================================================================
65// Plugin interface
66//==============================================================================
67
68bool SampleWindowPlugin::definesPluginInterfaces()
69{
70 // We don't handle this interface...
71
72 return false;
73}
74
75//==============================================================================
76
77bool SampleWindowPlugin::pluginInterfacesOk(const QString &pFileName,
78 QObject *pInstance)
79{
80 Q_UNUSED(pFileName)
81 Q_UNUSED(pInstance)
82
83 // We don't handle this interface...
84
85 return false;
86}
87
88//==============================================================================
89
90void SampleWindowPlugin::initializePlugin()
91{
92 // Create an action to show/hide our Sample window
93
94 mSampleWindowAction = Core::newAction(true, Core::mainWindow());
95
96 // Create our Sample window
97
98 mSampleWindowWindow = new SampleWindowWindow(Core::mainWindow());
99}
100
101//==============================================================================
102
103void SampleWindowPlugin::finalizePlugin()
104{
105 // We don't handle this interface...
106}
107
108//==============================================================================
109
110void SampleWindowPlugin::pluginsInitialized(const Plugins &pLoadedPlugins)
111{
112 Q_UNUSED(pLoadedPlugins)
113
114 // We don't handle this interface...
115}
116
117//==============================================================================
118
119void SampleWindowPlugin::loadSettings(QSettings &pSettings)
120{
121 Q_UNUSED(pSettings)
122
123 // We don't handle this interface...
124}
125
126//==============================================================================
127
128void SampleWindowPlugin::saveSettings(QSettings &pSettings) const
129{
130 Q_UNUSED(pSettings)
131
132 // We don't handle this interface...
133}
134
135//==============================================================================
136
137void SampleWindowPlugin::handleUrl(const QUrl &pUrl)
138{
139 Q_UNUSED(pUrl)
140
141 // We don't handle this interface...
142}
143
144//==============================================================================
The only method of interest to our plugin is initializePlugin()
(lines 90-99), which is where we initialise both mSampleWindowAction
and mSampleWindowWindow
.
All the other methods (definesPluginInterfaces()
, pluginInterfacesOk()
, finalizePlugin()
, pluginsInitialized()
, loadSettings()
, saveSettings()
, and handleUrl()
) are left empty.
Finally, we have the Window interface:
144//==============================================================================
145// Window interface
146//==============================================================================
147
148Qt::DockWidgetArea SampleWindowPlugin::windowDefaultDockArea() const
149{
150 // Return our default dock area
151
152 return Qt::TopDockWidgetArea;
153}
154
155//==============================================================================
156
157QAction * SampleWindowPlugin::windowAction() const
158{
159 // Return our window action
160
161 return mSampleWindowAction;
162}
163
164//==============================================================================
165
166QDockWidget * SampleWindowPlugin::windowWidget() const
167{
168 // Return our window widget
169
170 return mSampleWindowWindow;
171}
172
173//==============================================================================
All three methods are implemented since they tell OpenCOR the default dock area for our plugin window (see windowDefaultDockArea()
; lines 148-153), as well as provide the pointer to our action (see windowAction()
; lines 157-162) and window (see windowWidget()
; lines 166-171).
Some extra work is needed to get our plugin to do what we want, and this is done via the SampleWindowWindow
class in samplewindowwindow.h
:
28#include "windowwidget.h"
29
30//==============================================================================
31
32namespace Ui {
33 class SampleWindowWindow;
34} // namespace Ui
35
36//==============================================================================
37
38namespace OpenCOR {
39namespace SampleWindow {
40
41//==============================================================================
42
43class SampleWindowWindow : public Core::WindowWidget
44{
45 Q_OBJECT
46
47public:
48 explicit SampleWindowWindow(QWidget *pParent);
49 ~SampleWindowWindow() override;
50
51private:
52 Ui::SampleWindowWindow *mGui;
53
54private slots:
55 void updateSum();
56};
57
58//==============================================================================
59
60} // namespace SampleWindow
61} // namespace OpenCOR
SampleWindowWindow
inherits from Core::WindowWidget
, which is defined in the Core plugin and is an extended version of Qt’s QDockWidget
class (line 43).
It also comes with a GUI file, which describes the layout of our plugin window (samplewindowwindow.ui
).
The implementation of SampleWindowWindow
can be found in samplewindowwindow.cpp
:
24#include "sampleutilities.h"
25#include "samplewindowwindow.h"
26
27//==============================================================================
28
29#include "ui_samplewindowwindow.h"
30
31//==============================================================================
32
33namespace OpenCOR {
34namespace SampleWindow {
35
36//==============================================================================
37
38SampleWindowWindow::SampleWindowWindow(QWidget *pParent) :
39 Core::WindowWidget(pParent),
40 mGui(new Ui::SampleWindowWindow)
41{
42 // Set up the GUI
43
44 mGui->setupUi(this);
45
46 // A couple of connections to update our sum whenever one of the value of
47 // one of our numbers is updated
48
49 connect(mGui->nb1DoubleSpinBox, QOverload<double>::of(&QDoubleSpinBox::valueChanged),
50 this, &SampleWindowWindow::updateSum);
51 connect(mGui->nb2DoubleSpinBox, QOverload<double>::of(&QDoubleSpinBox::valueChanged),
52 this, &SampleWindowWindow::updateSum);
53
54 // Initialise our sum
55
56 updateSum();
57}
58
59//==============================================================================
60
61SampleWindowWindow::~SampleWindowWindow()
62{
63 // Delete the GUI
64
65 delete mGui;
66}
67
68//==============================================================================
69
70void SampleWindowWindow::updateSum()
71{
72 // Update our sum
73
74 mGui->sumLabel->setText(QString::number(Sample::add(mGui->nb1DoubleSpinBox->value(), mGui->nb2DoubleSpinBox->value())));
75}
76
77//==============================================================================
78
79} // namespace SampleWindow
80} // namespace OpenCOR
SampleWindowWindow()
(lines 38-57) initialises the SampleWindowWindow
object, as well as creates a couple of connections (lines 49-52) and initialises our sum by calling updateSum()
(line 56).
As can be seen, updateSum()
calls the add()
method from the Sample plugin (lines 70-75).