/*! \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* pm = ExtensionSystem::PluginManager::instance(); QList 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 class NavWidgetFactory : public Core::INavigationWidgetFactory { public: NavWidgetFactory(); ~NavWidgetFactory(); Core::NavigationView createWidget(); QString displayName(); }; #include 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(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} \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()} method for this purpose. The following code snippet explains this \code ExtensionSystem::PluginManager* pm = ExtensionSystem::PluginManager::instance(); QList objects = pm->getObjects(); 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(&bundle); Interface2* iface2Ptr = qobject_cast(&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 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( &bundle ); Interface2* iface2Ptr = Aggregation::query( &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 iface1Ptrs = Aggregation::query_all( &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(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. */