PDA

View Full Version : Problem with plugin signals/slots



DiamonDogX
4th June 2009, 13:58
I am developing under Windows and I have a Qt plugin project that produces a dll, and I have another app that can load that dll dynamically as a plugin via the usual Qt methods. Basically what I wanted to do was have the plugin report its status back to this app that's using it (i.e. the plugin emits a signal, the app receives it via a slot). So after I successfully detect and read in the plugin (i.e. the dll file), I do the usual connect call to set up the signal/slot (which returns true), but the connection doesn't seem to actually be working, as my slot code never gets executed in the app. I tried to strip down the pertinent code below.


UpgradeInterface.h (this is the interface a plugin extends):


class UpgradeInterface : public QObject
{
public:

virtual ~UpgradeInterface() {}
virtual bool performUpgrade() = 0;

signals:
void statusUpdate(QString statusMsg);
};

Q_DECLARE_INTERFACE(UpgradeInterface, "com.UpgradeInterface/1.0")

//----------------------------------------------------------

UpgradePlugin.h:


class UpgradePlugin : public QObject, public UpgradeInterface
{
Q_OBJECT
Q_INTERFACES(UpgradeInterface)

public:
bool performUpgrade();

signals:
void statusUpdate(QString statusMsg);
};

//----------------------------------------------------

UpgradePlugin.cpp:


bool UpgradePlugin::performUpgrade()
{
emit statusUpdate("Upgrade started!");
return true;
}

Q_EXPORT_PLUGIN2(UpgradePlugin, UpgradePlugin)

//-------------------------------------------------

The following is part of the app code that detects/loads the plugin.

MyDialog.h:


class MyDialog : public QDialog
{
Q_OBJECT
//...
//...
protected slots:
void statusUpdateFromUpgradePlugin(QString statusMsg);
};

//------------------------------------------------------

MyDialog.cpp:


void MyDialog::loadPlugins(QDir dir)
{
// Load dynamic plugins

foreach (QString fileName, dir.entryList(QDir::Files))
{
QPluginLoader loader(dir.absoluteFilePath(fileName));
QObject *plugin = loader.instance();

if (plugin)
{
UpgradeInterface * iUpgrade = qobject_cast<UpgradeInterface *>(plugin);
if (iUpgrade)
{
bool success = connect(iUpgrade, SIGNAL(statusUpdate(QString)), this, SLOT(statusUpdateFromUpgradePlugin(QString))); // returns true
iUpgrade->performUpgrade();
}
}
}
}

void MyDialog::statusUpdateFromUpgradePlugin(QString statusMsg)
{
cout << statusMsg; // Never gets here!
}

faldzip
4th June 2009, 18:51
AFAIK your code won't compile because for me code like this:


QString str("asdasd");
std::cout << str;
result in this:

error C2679: binary '<<' : no operator found which takes a right-hand operand of type 'QString' (or there is no acceptable conversion)
so I don't know how do you achieved compilation, but you can tell me how to do it :]
next thing is that maybe you forgot CONFIG += console or sth. Try using:


#include <QDebug>

...

qDebug() << statusMsg;
except for that everything should work in your code in my opinion

DiamonDogX
4th June 2009, 19:36
I don't actually have the "cout" statement in my code... it was there just as a generic line of code. So my code does actually compile :). Put anything you want there... the point I was trying to illustrate is that it simply doesn't ever get there. I put break points, try to pop up message dialogs, etc. so I know it does not actually execute... thx.

seim
4th June 2009, 21:51
Hi,

try to add Q_OBJECT to the UpgradeInterface declaration. It works written in that way in my code, I hope it will help you.

have a nice day

faldzip
4th June 2009, 22:06
ok, I've done some research and I think, like my preposter, that you should add Q_OBJECT macro in interface declaration, and you should not redefine signal in implementation, just emit it.

DiamonDogX
4th June 2009, 22:14
Hmm when I add Q_OBJECT, now when I compile my UpgradePlugin project, I get:

Error 3 error LNK2001: unresolved external symbol "public: virtual struct QMetaObject const * __thiscall UpgradeInterface::metaObject(void)const " (?metaObject@UpgradeInterface@@UBEPBUQMetaObject@@ XZ) UpgradePlugin.obj UpgradePlugin
Error 4 error LNK2001: unresolved external symbol "public: virtual void * __thiscall UpgradeInterface::qt_metacast(char const *)" (?qt_metacast@UpgradeInterface@@UAEPAXPBD@Z) UpgradePlugin.obj UpgradePlugin
Error 5 error LNK2001: unresolved external symbol "public: virtual int __thiscall UpgradeInterface::qt_metacall(enum QMetaObject::Call,int,void * *)" (?qt_metacall@UpgradeInterface@@UAEHW4Call@QMetaOb ject@@HPAPAX@Z) UpgradePlugin.obj UpgradePlugin
Error 6 fatal error LNK1120: 3 unresolved externals ..\UpgradePlugin.dll UpgradePlugin

DiamonDogX
4th June 2009, 22:18
Hmm when I call emit from the class that implements UpgradeInterface (UpgradePlugin), no luck :(. Keep in mind UpgradePlugin is its own separate project (and produces a dll) and references the same UpgradeInterface.h file as the app (exe) project does... not that that helps, just reiterating...

Error 3 error LNK2019: unresolved external symbol "protected: void __thiscall UpgradeInterface::statusUpdate(class QString)" (?statusUpdate@UpgradeInterface@@IAEXVQString@@@Z) referenced in function "public: virtual bool __thiscall UpgradePlugin::performUpgrade(void)" (?performUpgrade@UpgradePlugin@@UAE_NXZ) UpgradePlugin.obj UpgradePlugin
Error 4 error LNK2001: unresolved external symbol "public: virtual struct QMetaObject const * __thiscall UpgradeInterface::metaObject(void)const " (?metaObject@UpgradeInterface@@UBEPBUQMetaObject@@ XZ) UpgradePlugin.obj UpgradePlugin
Error 5 error LNK2001: unresolved external symbol "public: virtual void * __thiscall UpgradeInterface::qt_metacast(char const *)" (?qt_metacast@UpgradeInterface@@UAEPAXPBD@Z) UpgradePlugin.obj UpgradePlugin
Error 6 error LNK2001: unresolved external symbol "public: virtual int __thiscall UpgradeInterface::qt_metacall(enum QMetaObject::Call,int,void * *)" (?qt_metacall@UpgradeInterface@@UAEHW4Call@QMetaOb ject@@HPAPAX@Z) UpgradePlugin.obj UpgradePlugin
Error 7 fatal error LNK1120: 4 unresolved externals ..\UpgradePlugin.dll UpgradePlugin

faldzip
4th June 2009, 23:05
nmake clean + qmake again :]
this works for me:
interface.h:


#ifndef INTERFACE_H
#define INTERFACE_H

#include <QtPlugin>

class PluginInterface : public QObject
{
Q_OBJECT
public:
PluginInterface(QObject *parent) : QObject(parent) {}
virtual void someMethod() = 0;
signals:
void someSignal();
};

Q_DECLARE_INTERFACE(PluginInterface, "pl.faldzip.PluginInterface")
#endif // INTERFACE_H
myplugin.h:


#ifndef MYPLUGIN_H
#define MYPLUGIN_H

#include "plugin1_global.h"
#include "interface.h"

class MyPlugin : public PluginInterface
{
Q_OBJECT
Q_INTERFACES(PluginInterface)
public:
MyPlugin(QObject *parent = 0);
void someMethod();
};

#endif // MYPLUGIN_H
myplugin.cpp:


#include "myplugin.h"

MyPlugin::MyPlugin(QObject *parent)
: PluginInterface(parent)
{
}

void MyPlugin::someMethod()
{
emit someSignal();
}

Q_EXPORT_PLUGIN2(myplugin, MyPlugin);


and this was one project (with pro file containing TEMPLATE = lib and CONFIG += plugin),
and second project - app project:
main.cpp:


#include <QtGui>
#include <QtCore>
#include <QtPlugin>
#include "interface.h"

class SomeClass : public QObject
{
Q_OBJECT
public:
SomeClass(PluginInterface *pi, QObject *parent = 0) : QObject(parent), m_pi(pi) { connect(m_pi, SIGNAL(someSignal()), SLOT(someSlot2())); }
public slots:
void someSlot() { m_pi->someMethod(); }
void someSlot2() { qDebug("someSlot2"); }
private:
PluginInterface *m_pi;
};

int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QPluginLoader loader(a.applicationDirPath() + QDir::separator() + "plugin1.dll");
QObject *obj = loader.instance();
PluginInterface *pi = qobject_cast<PluginInterface *>(obj);
if (!pi)
{
qDebug("plugin error");
return 1;
}
QPushButton pb("Button");
SomeClass sc(pi, &pb);
pb.connect(&pb, SIGNAL(clicked()), &sc, SLOT(someSlot()));
pb.show();
return a.exec();
}

#include "main.moc"

so it shows a button, and when I click on it, it's callingSomeClass::someSlot() which is calling PluginInterface::someMethod(), whis is emitting someSignal() connected to someSlot2() so the "someSlot2" appears on the screen.

DiamonDogX
5th June 2009, 16:01
Thanks for the help... finally got it!

One problem was the Q_OBJECT thing... but after adding that I got some link errors, as I mentioned. Basically I reorganized my projects so that the UpgradeInterface was in its own separate project (lib), and I had the plugin project and app project both reference that project. Works :)