forked from qt-creator/qt-creator
		
	Signed-off-by: Abhishek Patil <abhishek.patil@vcreatelogic.com> Merge-request: 145 Reviewed-by: con <qtc-committer@nokia.com>
		
			
				
	
	
		
			330 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			330 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
| /*!
 | |
|     \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.
 | |
| */
 |