PDA

View Full Version : Plugin interfaces, signals and slots



QPlace
9th August 2007, 04:55
I have a plugin interface

class TestInterface : public QObject
{
public:
virtual ~TestInterface () { }
virtual QString Description () = 0;
virtual int ID() = 0;
virtual bool setImplementation(QWidget* parent,QString fname) = 0;

signals:
void OnNewConfig (QString string);
};
Q_DECLARE_INTERFACE(TestInterface ,"xxxx/1.0")

In my client I implemented the slot with the same signature as OnNewConfig and I am trying to connect signal of the plugin to a slot at the client. When I retrieve interface from plugin I am doing following:
TestInterface* interface = <retrieve from plugin>
connect(interface, SIGNAL(OnNewConfig(QString)), this, SLOT(OnNewConfig(QString)));

Connect fails, in debug I see that it fails in if (signal_index < 0) {
err_method_notfound(QSIGNAL_CODE, sender, signal, "connect"); of the bool QObject::connect method.

My Questions are: Can Signal/slot mechanism be used in plugins
How to do it correctly (apparently I am doing something wrong but don't see what exactly).

marcel
9th August 2007, 08:20
Try adding the Q_OBJECT macro to the class declaration, but I am not sure if it works for interfaces.... It should work just fine.
When moc compiles the interface header, it should generate the signal method and all relevant methods.
When you connect an object of type TestInterface(this includes subclasses, since it will be inherited) to some other object, the signal should have no problem in getting called.

Otherwise, don't derive the abstract class from QObject. When creating the actual plug-in classes, derive them from QObject and the interface. All plugins will have to declare the OnNewConfig signal, although.

Regards

gri
9th August 2007, 08:22
You can use signal/slots for plugins.
The simplest way would be to add a function "QObject* getQObject()" to your interface. Since interfaces are not derived from QObject you can't use them directly for the QObject::connect() function.

Ok, your's is derived :P So it might work somehow .. *needcoffeeandbelievesmarcel*

QPlace
9th August 2007, 10:59
Thank you guys for the help. I redefined TestInterface to exclude Q_OBJECT and deriving from Object, so it is just a pure abstract clas. I also used the suggestion and added QObject* getObject to the interface. Plugin returns "this" out of the implementation.

I am still getting an error with "connect", which returns false. By debugging it looks as if "connect" cannot find my signal in "signalsource". Any comments/suggestions?
Here is what I do:

QPluginLoader loader(pluginDir.absoluteFilePath(fileName));
if (TestInterface * interface = qobject_cast<TestInterface *>(loader.instance()))
{
QObject* signalsource = interface->getObject(); // it is not null.
connect(signalsource, SIGNAL(OnNewConfig(QString)), this, SLOT(OnNewConfig(QString)));
...
}

marcel
9th August 2007, 13:30
Because QObject itself does not contain that signal(try casting to the plugin instead).

I suggested ADDING the Q_OBJECT macro to the interface( while keep deriving from QObject) and leaving the signal in the interface.
In your example, you missed the Q_OBJECT macro.

Regards

QPlace
9th August 2007, 18:24
Thank you. I fugured out what the problem was. It was not actually Q_Object in the interface. Interface is a pure abstract class, it is the classes that implement interface that should be derived from Object, as you correctly stated.

I think that the solution is worth mentioning here in the forum. May be others will benefit from it.

The problem was that, as I mentioned before, "connect" did not see the signal in the implementation of the interface. Looking further, the returned error code was -1, which is explained as finding the method that matches the signature, but the method was not recognized as signal by "connect". Solution was simple:

interface A
{
public signals:
void Test();
};


class Aimpl : public A, public Object
{
...
public signal: // that's what I was missing in my derived class! When I added this,
//everything started to work.
void Test();
}

Also, for the plugins getting the Object is even simpler, it has the method ->instance(), which returns just what I needed.

Thank you guys for all your help, I really mean it and count for it for my future questions! :)It is the second time in my career that I am exited about the development framework. The first time it was Delphi, the second is now and it is QT.

Gopala Krishna
9th August 2007, 18:38
Actually i think this is nice hack. I am using this in my application where i need an abstract base class to represent view and the derived classes also derive from QTextEdit or QGraphicsView. This is nice way to enforce implementation of signals.

I have a doubt in your code. Shouldn's signals: suffice rather than public signals: ?
I guess signals is defined to be protected.
Also you can make the signal in the abstract base class abstract!

QPlace
9th August 2007, 21:17
My test interface that I typed into the comment was not an actual implementation: I did it just to show where I found a solution :).

Shouldn's signals: suffice rather than public signals: ?
Probably. I will check it and report back here. My intention was to make it public, because I was testing the scheme. Now I will try to make it private.

Also you can make the signal in the abstract base class abstract
Interestingly, compiler did not complain. Indeed, it should be declared with = 0;

marcel
9th August 2007, 21:19
Shouldn's signals: suffice rather than public signals: ?
Signals must not have any visibility modifiers. Only slots.
The signals keyword is defined in qobjectdefs.h as:


# define signals protected