1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
|
/*!
\page qtc-arch.html
\title 4. Qt Creator Architecture
Every large system has a well defined "system architecture" which if understood well makes it easy for us to find out
way in it. Qt Creator is no different. In this chapter we will understand the basic architecture of Qt Creator so that we
can continue our understanding of writing plugins.
\section1 4.1 Nuts and Bolts of Qt Creator
Qt Creator is Nokia's cross-platform IDE. Currently Qt Creator is mainly used for writing Qt/C++ code.
The core of Qt Creator is basically only a "plugin loader" All functionality is implemented in plugins.
\inlineimage qtc-pluginmanager-4.png
The core "personality"of Qt Creator is implemented in the \bold {Core Plugin (Core::ICore)}. We have already had a brush
with the core plugin in the previous chapter. In the rest of this document we will refer to "core plugin" as Core.
The plugin manager \bold{(ExtensionSystem::PluginManager)} provides simple means for plugin cooperation that
allow plugins to provide hooks for other plugin's extensions.
\section1 4.2 What exactly is a plugin?
At the most fundamental level plugin is a shared library (DLL file on Windows, SO file on Linux, DYLIB file on Mac). From
a developer's point of view plugin is a module that
\list 1
\o Implements the ExtensionSystem::IPlugin interface in a class. This class will be referred to as "Plugin Class" in the
rest of the document.
\o Exports the Plugin Class using the Q_EXPORT_PLUGIN macro
\o Provides a pluginspec file that provides some meta information about the plugin
\o Registers one or more objects that might be of some interest to other plugins
\o Searches for the availability of one or more objects registered by other plugins.
\endlist
We have already had some experience with the first three aspects listed above, but we have not touched upon the last
two.
\section2 4.2.1 What are registered objects?
objects are those that land up in the \bold {PluginManager's} object pool. The \bold {allObjects()} method in
\bold {PluginManager} returns the object pool as a list of QObject pointers. Shown below is the code that we can use to list
all objects in the object-pool in a \bold {QListWidget}.
\code
#include <extensionsystem/pluginmanager.h>
ExtensionSystem::PluginManager* pm = ExtensionSystem::PluginManager::instance();
QList<QObject*> objects = pm->allObjects();
QListWidget* listWidget = new QListWidget;
Q_FOREACH(QObject* obj, objects)
{
QString objInfo = QString("%1 (%2)").arg(obj->objectName()).arg(obj->metaObject()->className());
listWidget->addItem(objInfo);
}
listWidget->show();
\endcode
When such a list widget is constructed and shown; you will see a window as shown below.
\inlineimage qtc-objectlist-4.png
From the class names it is easy to picture the fact that each of those objects came from different plugins.
\bold {\underline {A registered object is an instance of QObject (or one of its subclasses) registered by a plugin and is available in the
object-pool for other plugins to make use of}}.
\section2 4.2.2 How to "register" an object from a plugin?
There are three ways to register an object from a plugin:
\list
\o \bold {IPlugin::addAutoReleasedObject(QObject*)}
\o \bold {IPlugin::addObject(QObject*)}
\o \bold {PluginManager::addObject(QObject*)}
\endlist
The \bold {IPlugin::addObject()} and \bold {IPlugin::addAutoReleasedObject()} essentially call the
\bold {PluginManager::addObject()} method. The \bold {IPlugin} methods are only provided for convenience. It is
recommended that plugins make use of the \bold {IPlugin} methods for adding objects.
The only difference between \bold {addAutoReleasedObject()} and \bold {addObject()} is that objects added using the
former method are automatically removed and deleted in the reverse order of registration when the plugin is destroyed.
At anytime plugins can make use of the \bold {IPlugin::removeObject(QObject*)} method to remove its object from
the object pool.
\section2 4.2.3 What objects to register?
Plugins can register just about any object. Normally objects that provide some sort of functionality used by other
plugin(s) are registered. Functionalities in Qt Creator are defined by means of interfaces. Listed below are some interfaces
\list
\o \bold {Core::INavigationWidgetFactory}
\o \bold {Core::IEditor}
\o \bold {Core::IOptionsPage}
\o \bold {Core::IWizard}
\endlist
\bold{\underline { C++ developers normally assume interfaces to be classes with all its functions are public pure
virtual functions. In Qt Creator interfaces are subclasses of QObject that offer one or more
methods are pure virtual}}.
If a plugin has objects that implement an interface, then such an object has to be registered. For example if a plugin
implements the \bold{INavigationWidgetFactory} interface in an object and registered it, the Core will automatically use that
object to show the widget provided by it as navigation widget. Take a look at the code snippet below. We provide a
simple \bold{QTableWidget} as navigation widget via an implementation of \bold {Core::INavigationWidgetFactory}.
\code
#include <coreplugin/inavigationwidgetfactory.h>
class NavWidgetFactory : public Core::INavigationWidgetFactory
{
public:
NavWidgetFactory();
~NavWidgetFactory();
Core::NavigationView createWidget();
QString displayName();
};
#include <QTableWidget>
NavWidgetFactory::NavWidgetFactory() { }
NavWidgetFactory::~NavWidgetFactory() { }
Core::NavigationView NavWidgetFactory::createWidget()
{
Core::NavigationView view;
view.widget = new QTableWidget(50, 3);
}
QString NavWidgetFactory::displayName()
{
return "Spreadsheet";
}
bool MyPlugin::initialize(const QStringList& args, QString *errMsg)
{
Q_UNUSED(args);
Q_UNUSED(errMsg);
// Provide a navigation widget factory.
// Qt Creator's navigation widget will automatically
// hook to our INavigationWidgetFactory implementation, which
// is the NavWidgetFactory class, and show the QTableWidget
// created by it in the navigation panel.
addAutoReleasedObject(new NavWidgetFactory);
return true;
}
\endcode
The effect of the above code is
\inlineimage qtc-codeeffect-4.png
\section2 4.2.4 Becoming aware of registered objects
Whenever the \bold {PluginManager::addObject()} is used to add an object, it \bold{(PluginManager)} emits the
\bold {objectAdded(QObject*)} signal. This signal can be used within our applications to figure out the objects that got
added.
Obviously a plugin will begin receiving the signal only after it makes a connection to it. That happens only after the
plugin is initialized; which also means that the plugin will receive the \bold {objectAdded()} signal only for objects added
after the plugin was initialized.
Usually the slot that is connected to the objectAdded() signal will look for one or more known interfaces. Suppose that
your plugin is looking for the INavigationWidgetFactory interface, the slot connected to objectAdded() will be like the
one shown below.
\code
void Plugin::slotObjectAdded(QObject * obj)
{
INavigationWidgetFactory *factory = Aggregation::query<INavigationWidgetFactory>(obj);
if(factory)
{
// use it here...
}
}
\endcode
\section2 4.2.5 Searching for objects
Sometimes a plugin might want to search for an object in the application that offers some functionality. We already
know by now that
\list
\o \bold {PluginManager::allObjects()} returns the object pool as a \bold {QList<QObject*>}
\o Connecting to \bold {PluginManager::objectAdded()} signal helps in known objects as they get registered
\endlist
Using both of the above mentioned methods you can look for objects. Lets now understand yet another way to find
objects.
Suppose that you wanted to look for objects that implement the \bold {INavigationWidgetFactory} interface and show it in a
\bold {QListWidget}. You can make use of the \bold {PluginManager::getObjects<T>()} method for this purpose. The following code
snippet explains this
\code
ExtensionSystem::PluginManager* pm = ExtensionSystem::PluginManager::instance();
QList<Core::INavigationWidgetFactory*> objects = pm->getObjects<Core::INavigationWidgetFactory>();
QListWidget* listWidget = new QListWidget();
Q_FOREACH(Core::INavigationWidgetFactory* obj, objects)
{
QString objInfo = QString("%1 (%2)").arg(obj->displayName()).arg(obj->metaObject()->className());
listWidget->addItem(objInfo);
}
\endcode
When the list widget is shown you will notice that the navigation widgets are shown in the same order as they are
shown in the navigation combo box. Take a look at the screenshot below.
\inlineimage qtc-nevigationwidget-4.png
\section1 4.3 Aggregations
Aggregations are provided by the \bold {Aggregation} namespace. It adds functionality for "glueing" \bold {QObjects} of different
types together, so you can "cast" between them. Using the classes and methods in this namespace you can bundle
related objects into a single entity. Objects that are bundled into an aggregate can be "cast" from the aggregate into the
object class type.
\section2 4.3.1 Aggregations - the old fashioned way
Suppose that you wanted an object that provided implementations of two interfaces. Normally we would go about
coding the object like this.
\code
class Interface1
{
....
};
Q_DECLARE_INTERFACE("Interface1", "Interface1");
class Interface2
{
....
};
Q_DECLARE_INTERFACE("Interface2", "Interface2");
class Bundle : public QObject,public Interface1,public Interface2
{
Q_OBJECT(Interface1 Interface2)
....
};
Bundle bundle;
\endcode
Now we can think of \bold {bundle} as an object that provides \bold {Interface1} and \bold {Interface2} implementations. We can
make use of casting operators on the bundle object to extract \bold{Interface1} and \bold {Interface2}.
\code
Interface1* iface1Ptr = qobject_cast<Interface1*>(&bundle);
Interface2* iface2Ptr = qobject_cast<Interface2*>(&bundle);
\endcode
\section2 4.3.2 Aggregations - the Qt Creator way
Qt Creator's Aggregation library offers a cleaner way to define interfaces and bundle them into a single object. Instances
of Aggregation::Aggregate can be created and objects can be added to it. Each of the objects added to the aggregation
can implement an interface. The following code snippet shows how to create an aggregation.
\code
#include <aggregation/aggregate.h>
class Interface1 : public QObject
{
Q_OBJECT
public:
Interface1() { }
~Interface1() { }
};
class Interface2 : public QObject
{
Q_OBJECT
public:
Interface2() { }
~Interface2() { }
};
Aggregation::Aggregate bundle;
bundle.add(new Interface1);
bundle.add(new Interface2);
\endcode
The aggregation instance "bundle" now conceptually contains implementations of two interfaces. To extract the
interfaces we can make use of the following code
\code
Interface1* iface1Ptr = Aggregation::query<Interface1>( &bundle );
Interface2* iface2Ptr = Aggregation::query<Interface2>( &bundle );
\endcode
With aggregation you can also several objects of the same interface into a single bundle. For example
\code
Aggregation::Aggregate bundle;
bundle.add(new Interface1);
bundle.add(new Interface2);
bundle.add(new Interface1);
bundle.add(new Interface1);
QList<Interface1*> iface1Ptrs = Aggregation::query_all<Interface1>( &bundle );
\endcode
Another key advantage of Aggregation is that, you can delete any one of the objects in the bundle to delete the whole
bundle. Example
\code
Aggregation::Aggregate* bundle = new Aggregation::Aggregate;
bundle->add(new Interface1);
bundle->add(new Interface2);
Interface1* iface1Ptr = Aggregation::query<Interface1>(bundle);
delete iface1Ptr;
// deletes the bundle and all objects in it
// same as delete bundle
\endcode
The use of aggregation will become clearer when we deal with real plugin examples in the coming chapters.
*/
|