Events:CTK-Hackfest-2010/SlicerQtPluginArchitecture

From NAMIC Wiki
Jump to: navigation, search
Home < Events:CTK-Hackfest-2010 < SlicerQtPluginArchitecture

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;
    }
}