Events:CTK-Hackfest-2010/SlicerQtPluginArchitecture
From NAMIC Wiki
Home < Events:CTK-Hackfest-2010 < SlicerQtPluginArchitecture
Contents
Goal
- Provide a cross-platform plugin architecture
- Modular and extensible
Big picture
- a Factory manages a collection of Factory items
- Factory Item provides load() and instanciate() methods
- Factory provides registerItems() and load(key) methods
- Factory are templated
Under the hood
The Qt layer
- QPlugin [1] [2] (from Qt docmentation ... )
- QPluginLoader checks that a plugin is linked against the same version of Qt as the application.
- QPluginLoader provides direct access to a root component object (instance()), instead of forcing you to resolve a C function manually.
- In order to speed up loading and validation of plugins, some of the information that is collected during loading is cached in persistent memory (through QSettings).
- QLibrary [3]
- QObject / Object
The CTK layer
- Base class: qCTKAbstractFactory
template<typename BaseClassType> class qCTKAbstractFactory { ... public: explicit qCTKAbstractFactory(); virtual ~qCTKAbstractFactory(); virtual void printAdditionalInfo(); /// Return factory name virtual QString name()const = 0; /// Create an instance of the object virtual BaseClassType * instantiate(const QString& itemKey); /// Uninstanciate the object void uninstantiate(const QString& itemKey); /// Get list of all registered item names QStringList names() const; /// Register items with the factory /// Method provided for convenience - Should be overloaded in subclasse virtual void registerItems(){} protected: /// Call the load method associated with the item. /// If succesfully loaded, add it to the internal map. bool registerItem(const QSharedPointer<qCTKAbstractFactoryItem<BaseClassType> > & item); /// Get a Factory item given its itemKey. Return 0 if any. qCTKAbstractFactoryItem<BaseClassType> * item(const QString& itemKey)const; private: ... };
- 3 kind of factory:
- qCTKAbstractLibraryFactory
- qCTKAbstractObjectFactory
- qCTKAbstractPluginFactory
- A factory manage a collection of qCTKAbstractFactoryItem.
template<typename BaseClassType> class qCTKAbstractFactoryItem { public: explicit qCTKAbstractFactoryItem(const QString& key); virtual QString loadErrorString()const; virtual bool load() = 0; BaseClassType* instantiate(); bool instantiated(); QString key(); virtual void uninstantiate(); protected: virtual BaseClassType* instanciator() = 0; BaseClassType* Instance; private: QString Key; };
Concrete example(s)
- All modules are based on qSlicerAbstractModule which provides:
- logic(): This method allows to get a pointer to the ModuleLogic. If no moduleLogic already exists, one will be created calling 'createLogic' method
- widgetRepresentation(): This method allows to get a pointer to the WidgetRepresentation. If no WidgetRepresentation already exists, one will be created calling 'createWidgetRepresentation' method.
- qSlicerCoreModuleFactory:
- registerItems -> registerCoreModule -> N * registerCoreModule -> extractName -> registerObject -> registerItem(moduleName)
void qSlicerCoreModuleFactory::registerItems() { QCTK_D(qSlicerCoreModuleFactory); d->registerCoreModule<qSlicerTransformsModule>(); d->registerCoreModule<qSlicerCamerasModule>(); }
- qSlicerLoadableModuleFactory
- registerItems -> use QDirIterator -> extractName -> registerPlugin(path, moduleName)
template<typename BaseClassType> bool qCTKFactoryPluginItem<BaseClassType>::load() { this->Loader.setFileName(this->path()); return this->Loader.load(); }
template<typename BaseClassType> BaseClassType* qCTKFactoryPluginItem<BaseClassType>::instanciator() { //qDebug() << "PluginItem::instantiate - name:" << this->path(); QObject * object = this->Loader.instance(); if (!object) { qWarning() << "Failed to instantiate plugin:" << this->path(); return 0; } BaseClassType* castedObject = qobject_cast<BaseClassType*>(object); if (!castedObject) { qWarning() << "Failed to access interface [" << BaseClassType::staticMetaObject.className() << "] in plugin:" << this->path(); delete object; // Clean memory return 0; } return castedObject; }
- qSlicerCLILoadableModuleFactory
- registerItems -> use QDirIterator -> extractName -> registerLibrary(path, moduleName)
void qCTKFactoryLibraryItem<BaseClassType>::resolve() { foreach(const QString& symbol, this->Symbols) { // Sanity checks if (symbol.isEmpty()) { continue; } // Make sure the symbols haven't been registered if (this->ResolvedSymbols.contains(symbol)) { qWarning() << "Symbol '" << symbol << "' already resolved - Path:" << this->Path; continue; } void * resolvedSymbol = this->Library.resolve(symbol.toLatin1()); if (!resolvedSymbol) { qWarning() << "Failed to resolve symbol '" << symbol << "' - Path:" << this->Path; } this->ResolvedSymbols[symbol] = resolvedSymbol; } }