Plugins and Signal/Slot mechanism
-
Hi.
I'm currently developing an application with plugins. These plugins should extend the functionality of the MainWinow, such as adding some Buttons in the ToolBar and so on.
I'd like to connect the Signals/Slots of the plugins to the MainWindow an also among each other.Currently I have:
Interface class:
@
class IBasePlugin
{
public:
virtual ~IBasePlugin(){}virtual QString name() = 0; virtual QString version() = 0;
};
class IToolbar : public IModule
{
public:
virtual ~IToolbar(){}
virtual QWidget* getToolbar() = 0;
virtual QObject* getObject() = 0;
};
@Implementation of interface:
@
class plugin_Toolbar : public QObject, public IToolbar
{
Q_OBJECT
Q_INTERFACES(IToolbar IBasePlugin)public:
plugin_Toolbar();QString name() { return "abc"; } QString version() { return "1"; } QWidget* getToolbar(); QObject* getObject();
signals:
void buttonClicked();
@Plugin gets loaded by QPluginLoader and its signal gets connected with:
@
connect(toolbar_plugin->getObject(), SIGNAL(buttonClicked()),
mainWindow, SLOT(close()));
@This works, but doesn't seem to be the best solution.
I'd like to have signals and slots in my interface, but inheriting QObject in the interface and in the implementation of the interface doesn't work, moc is complaining.
How can this be achieved, unfortunately this seems to be an infrequent task, haven't found examples.Thanks for your help!
[edit: Last piece of code fixed / Denis Kormalev]
-
I can't see why that's not a good solution?
If you provide us with your implementation for getToolbar() and getObject() we probably can help further. Do both functions return the same object?
Also: It is definitely NOT possible to declare signals and slots in an interface, as the inheritance from QObjet is forbidden there.
If you declare your interfaces with the proper macros you can use qobject_cast and test if a plugin implements a certain interface.
See the Docs on "qobject_cast":http://doc.qt.nokia.com/4.7/qobject.html#qobject_cast and the "Plug & Paint Example":http://doc.qt.nokia.com/4.7/tools-plugandpaint.html for this.
-
[quote author="Volker" date="1288282846"]
If you provide us with your implementation for getToolbar() and getObject() we probably can help further. Do both functions return the same object?
[/quote]
No, one returns a member, the other the whole class.
@
QWidget* plugin_Toolbar::getToolbar()
{
return(m_toolbar);
}QObject* plugin_Toolbar::getObject()
{
return(this);
}
@[quote author="Volker" date="1288282846"]
Also: It is definitely NOT possible to declare signals and slots in an interface, as the inheritance from QObjet is forbidden there.
[/quote]
Allright, I've tried to do this for quite a while, because I thought of having the signals/slots in the interface would be a nice thing. Knowing this prevents me from further failure.
W/o, the interface isn't fat, so good thing.[quote author="Franzk" date="1288329878"]Besides that, the plugin will always be seen as or even has to be a QObject, so you can connect to those signals anyway. The only downside is that the signal and slot names aren't clear from the interface...[/quote]
Okay, I can live with that.Thank you for your help!
-
You can shorten your connect a bit:
@
connect(toolbar_plugin, SIGNAL(buttonClicked()),
mainWindow, SLOT(close()));
@Calling getObject() on plugin_Toolbar is redundant, as both pointers are identical.
Unfortunately it's not reliable in the client code, that uses the plugin, because the connected signal is not guaranteed to exist in the plugin. You will notice this only at runtime (watch console output for QObjects warnings about failed connections). Alternatively you can use "QMetaObject":http://doc.qt.nokia.com/4.7/qmetaobject.html to look if the object has the wanted signal.
You can get around this limitation like this:
Create an abstract base class (like MyAbstractToolbarPlugin), that inherits from OQbject (and probably from non-QObject IBasePlugin), and declare the signal there.
In your concrete plugin, inherit from MyAbstractToolbarPlugin, then you definitely do have the signal.
Of course, this solution has another, different downside: You can only implement one QObject base class per plugin.
-
Allright, I think I've already tried this by doing the following:
Interface class:
@
class IBasePlugin
{
public:
virtual ~IBasePlugin(){}virtual QString name() = 0; virtual QString version() = 0;
};
class IToolbar : public IModule, public QObject
{
Q_OBJECT
public:
virtual ~IToolbar(){}
virtual QWidget* getToolbar() = 0;
virtual QObject* getObject() = 0;
signals:
void exitClicked();
};
@And changing the implementation to:
@
class plugin_Toolbar : /public QObject,/ public IToolbar
{
Q_OBJECT
Q_INTERFACES(IToolbar IBasePlugin)public:
plugin_Toolbar();QString name() { return "abc"; } QString version() { return "1"; } QWidget* getToolbar(); QObject* getObject();
signals:
void buttonClicked();
@But with this I'm getting errors:
in function
~IToolbar
undefined reference tovtable for IToolbar
-
[quote author="Volker" date="1288343476"]Ah, and I forgot to mention: You can/should omit IToolbar from the Q_INTERFACES macro, as you directly inherit from it.[/quote]
Okay, the reason I'm doing this is is to be able to do a qobject_cast to IToolbar. If I use only this macro
@
Q_INTERFACES(IBasePlugin)
@
the cast to IToolbar fails.Now I finally got it working, in my Plugins .pro I had to add the header file of the interface(s)
@
...
HEADERS +=
plugin_toolbarright.h
../../interfaces.h #!!! important
...
@
So thanks @ all, the cryptic undefined references to vtable were quite confusing...Hints for plugin writers:
Signals can't be declared virtual, but slots can. -
The qobject_cast to IToolbar should work without listing it in the macro. It's the same as if you inherited from QLabel.
But why would you object_cast to IToolbar anyway? That's only needed if you downcast from a QObject pointer to an IToolbar pointer. You can always safely assign a plugin_Toolbar pointer to an IToolbar pointer thanks to inheritance.