Wednesday, November 24, 2010

Qt Plugin and Signals

If is often required to emit signals from Plug-in created using Qt Plug-in Framework.

In such case most people, connect to signal from loaded plug-in which is derived from QObject and defined plug-in interface.

Like following sample plug-in,

Plug-in interface.
class Worker
{
public:
    virtual ~Worker() {}
    virtual void doWork() = 0;
};
Q_DECLARE_INTERFACE(Worker,"Worker");
Plug-in that implements required interface.
class SampleWorker: public QObject,public Worker
{
Q_OBJECT
public:
    void doWork();
signals:
    void workDone();
};
Plug-in loader code,
QPluginLoader pluginLoader(fileName);
QObject *plugin = pluginLoader.instance();
if (plugin) {
    Worker* worker = qobject_cast<Worker*>(plugin);
    if (worker) {        
        connect(worker,SIGNAL(workDone()),this,SLOT(workDone()));
        worker->doWork();
    }
}
In Plug-in loader code, We are creating plug-in object and if plug-in creation is successfully, we are connecting signal workDone() with some slot. Above code will work fine, because SampleWorker has defined workDone signal.

But problem here is, workDone signal is not part of Worker interface, it is defined in implemented plug-in. If plug-in defines required signal than all works well but we are not forcing plug-in to abide with interface.

Proper solution to this problem could be to create plug-in that return Factory, This factory then creates and returns proper object which is derived from Interface or base class that has defined required method and signals.

like shown in my following sample,
class Worker: public QObject
{
Q_OBJECT
public:
    virtual ~Worker() {}
    virtual void doWork() = 0;

signals:
    void workDone();
};

class WorkerFactory
{
public:
    virtual ~WorkerFactory() {}
    virtual Worker* newWorker() = 0;
};
Q_DECLARE_INTERFACE(WorkerFactory,"WorkerFactory");
Here Worker is actual interface that we need, It defines required method and signals so all subclass will have required method and signals.

Sample plug-in implementation.
class SampleWorker: public Worker
{
Q_OBJECT
public:
// it emits workDone signal, when its done
void doWork();
};

// Plug-in implementation
class SampleWorkerFactory: public QObject, public WorkerFactory
{
Q_OBJECT
Q_INTERFACES(WorkerFactory)
public:
    Worker* newWorker() {
        new SampleWorker();
    }
};
Plug-in loader implementation,
QPluginLoader pluginLoader(fileName);
QObject *plugin = pluginLoader.instance();
if (plugin) {
    WorkerFactory* workerFactory = qobject_cast(plugin);
    if (workerFactory) {
        Worker* worker = workerFactory->newWorker();
        connect(worker,SIGNAL(workDone()),this,SLOT(workDone()));
        worker->doWork();
    }
}
Here plug-in loader code is loading plug-in implementing WorkerFactory interface and asking it to create object that implements Worker interface which define all required method and signals. Now we can connect required signal slots with this worker object.

2 comments:

  1. A critical bit of information is not mentioned here: in your plugin project file you must include the interface header file in HEADERS. If it is not there, the plugin will fail to load complaining of an undefined symbol which will be the name of a pure virtual function from the interface file.

    I was having this very problem, and so I created a trivial application and plugin (with factory). Everything worked fine until I added signals to the plugin. A quick Google search for "qt plugin factory interface" took me to this page: http://qt-project.org/forums/viewthread/13950 describing my problem exactly, and providing the solution.

    ReplyDelete
  2. Thank you for the HEADERS tip!

    ReplyDelete