PDA

View Full Version : Getting Qt to recognize plugins...



KShots
20th April 2007, 15:39
Hello all,

I've been following the docs for developing a plugin for my Qt application, using the low-level approach (http://doc.trolltech.com/4.2/plugins-howto.html#the-lower-level-api-extending-qt-applications), as well as the plug & paint example (http://doc.trolltech.com/4.2/tools-plugandpaint.html). For whatever reason, when I run my application, it detects the plugin files but cannot generate an instance from them. Here's what I've got (using a single plugin as an example, and the relevant sections of my application).

First, I'll start with my interface, input.h:
#ifndef INPUT_H
#define INPUT_H

# include <QObject>

class QPoint;

class input
{
Q_OBJECT
public:
input() : allow(true){}
virtual ~input(){}

virtual const QString getName()const = 0;
virtual const QString getDescription()const = 0;
public slots:
virtual void setAllow(bool allow) = 0;
signals:
void menuUp();
void menuDown();
void menuLeft();
void menuRight();
void tap(const QPoint & p);
protected:
bool allow;
};

Q_DECLARE_INTERFACE(input,
"com.warfaresdl.evilcpc.input/1.0.0")

#endifAnd below, a sample input plugin, inputvoice.h:
#ifndef INPUTVOICE_H
#define INPUTVOICE_H

# include <input.h>

class inputVoice : public input, public QObject
{
Q_OBJECT
Q_INTERFACES(input)
public:
inputVoice(QObject * p = 0);
virtual ~inputVoice();

virtual const QString getName()const;
virtual const QString getDescription()const;
public slots:
virtual void setAllow(const bool allow);
};

#endif... and its implementation, inputVoice.cpp:
#include "inputVoice.h"
#include <QtPlugin>

inputVoice::inputVoice(QObject * p) : input(), QObject(p)
{
}

inputVoice::~inputVoice()
{
}

const QString inputVoice::getName()const
{
return tr("Voice");
}

const QString inputVoice::getDescription()const
{
return tr("Voice input plugin");
}

void inputVoice::setAllow(const bool a)
{
allow = a;
}

Q_EXPORT_PLUGIN2(inputVoice, inputVoice)The qmake .pro file for this plugin (voice):
TEMPLATE = lib
HEADERS = inputVoice.h
SOURCES = inputVoice.cpp
VERSION = 1.0.0
CONFIG = plugin debug_and_release qt
QT = core
INCLUDEPATH = ../interface

CONFIG(debug, debug|release) {
TARGET = inputVoice_debug
} else {
TARGET = inputVoice
}This generates a file, libinputVoice.so, which I sym-link to my application's project directory under a "plugins" sub-folder. My application uses the following qmake project file:
CONFIG = debug_and_release qt warn_on thread
QT = core
TEMPLATE = app
HEADERS =
SOURCES = main.cpp
INCLUDEPATH = ../plugins/menu/interface \
../plugins/menuItem/interface \
../plugins/input/interface
LIBS += -lmenu \
-lpanel \
-lncurses

CONFIG(debug, debug|release) {
TARGET = evilcpcconf-debug
} else {
TARGET = evilcpcconf
}... and in my application code, I use the following to load the plugin (stderr has been redirected to error.log via freopen):
unsigned int index;
//load plugins at this point...
QStringList lPluginName;
QStringList lPluginDescription;
QStringList lPluginFile;
QDir pluginsDir = QDir(QCoreApplication::applicationDirPath());
#if defined(Q_OS_WIN)
if(pluginsDir.dirName().toLower() == "debug" || pluginsDir.dirName().toLower() == "release")
{
pluginsDir.cdUp();
}
#elif defined(Q_OS_MAC)
if(pluginsDir.dirName() == "MacOS")
{
pluginsDir.cdUp();
pluginsDir.cdUp();
pluginsDir.cdUp();
}
#endif
cerr << "Using plugin path: " << pluginsDir.absolutePath().toAscii().data() << endl;
pluginsDir.cd("plugins");
cerr << "Changed to: " << pluginsDir.absolutePath().toAscii().data() << endl;
foreach(QString fileName, pluginsDir.entryList(QDir::Files))
{
cerr << "Found a file: " << pluginsDir.absoluteFilePath(fileName).toAscii().da ta() << endl;
QPluginLoader loader(pluginsDir.absoluteFilePath(fileName));
QObject * plugin = loader.instance();
if(!plugin)
{ // This is where each plugin is ending up
cerr << fileName.toAscii().data() << " is not a valid plugin..." << endl;
}
else
{
cerr << fileName.toAscii().data() << " is a valid plugin" << endl;
if(qobject_cast<input *>(plugin))
{
cerr << fileName.toAscii().data() << " is an input plugin" << endl;
input * i = qobject_cast<input *>(plugin);
lPluginFile << fileName;
lPluginName << i->getName();
lPluginDescription << i->getDescription();
}
}
}
if(!lPluginName.size())
{
return;
}... and the cerr output:
Using plugin path: /home/rich/svn/evilcpc/trunk/evilcpcconf
Changed to: /home/rich/svn/evilcpc/trunk/evilcpcconf/plugins
Found a file: /home/rich/svn/evilcpc/trunk/evilcpcconf/plugins/libinputVoice.so
libinputVoice.so is not a valid plugin...
Found a file: /home/rich/svn/evilcpc/trunk/evilcpcconf/plugins/libmenuAeon.so
libmenuAeon.so is not a valid plugin...
Found a file: /home/rich/svn/evilcpc/trunk/evilcpcconf/plugins/libmenuBasic.so
libmenuBasic.so is not a valid plugin...
Found a file: /home/rich/svn/evilcpc/trunk/evilcpcconf/plugins/libmenuItemGps.so
libmenuItemGps.so is not a valid plugin...What am I doing incorrectly in my plugin code? :(

KShots
20th April 2007, 15:57
Ok, I think I found the problem. The issue is that the docs don't explain very well what needs to go into the qmake project file, and that it also claims that the interface should not inherit from QObject.

Including the interface in my project file for my plugin cause Qt to actually care about the content of input.h... which then told me that because I used the Q_OBJECT macro, I need to inherit from QObject. Once that was done, the plugin was recognized. Here's the changes I needed to make.

The plugin project file:
TEMPLATE = lib
HEADERS = inputVoice.h \
../interface/input.h <--------Added this line
SOURCES = inputVoice.cpp
VERSION = 1.0.0
CONFIG = plugin debug_and_release qt
QT = core
INCLUDEPATH = ../interface

CONFIG(debug, debug|release) {
TARGET = inputVoice_debug
} else {
TARGET = inputVoice
}The interface header:
#ifndef INPUT_H
#define INPUT_H

# include <QObject>

class QPoint;

class input : public QObject // <----- This line changed
{
Q_OBJECT
public:
input(QObject * p = 0) : QObject(p), allow(true){} // <----- This line changed
virtual ~input(){}

virtual const QString getName()const = 0;
virtual const QString getDescription()const = 0;
public slots:
virtual void setAllow(bool allow) = 0;
signals:
void menuUp();
void menuDown();
void menuLeft();
void menuRight();
void tap(const QPoint & p);
protected:
bool allow;
};

Q_DECLARE_INTERFACE(input,
"com.warfaresdl.evilcpc.input/1.0.0")

#endifThe plugin header:
#ifndef INPUTVOICE_H
#define INPUTVOICE_H

# include <input.h>

class inputVoice : public input // <----- This line changed (don't inherit QObject here)
{
Q_OBJECT
Q_INTERFACES(input)
public:
inputVoice(QObject * p = 0);
virtual ~inputVoice();

virtual const QString getName()const;
virtual const QString getDescription()const;
public slots:
virtual void setAllow(const bool allow);
};

#endif... and finally the plugin implementation:
#include "inputVoice.h"
#include <QtPlugin>

inputVoice::inputVoice(QObject * p) : input(p) // <-----This line changed
{
}

inputVoice::~inputVoice()
{
}

const QString inputVoice::getName()const
{
return tr("Voice");
}

const QString inputVoice::getDescription()const
{
return tr("Voice input plugin");
}

void inputVoice::setAllow(const bool a)
{
allow = a;
}

Q_EXPORT_PLUGIN2(inputVoice, inputVoice)

fullmetalcoder
20th April 2007, 18:23
If you want to use plugins through QPluginLoader, as the docs state, you must respect some basic rules (apart from the Q_DECLARE_INTERFACE and Q_EXPORT_PLUGIN2 macros stuff) :


Your interface MUST be an interface, i.e an abstract class with only pure virtual methods and a virtual destructor defined in the header
The first rule implies a second one : no inheritance from non-abstract classes
The second rule implies that plugin interfaces can't make use of the meta object system, i.e. no signals, no slots no events, ... (note : this does not mean that implementations can't...)
as with every QObject classes, the implementation of an interface must place QObject first in its inheritance chain and multiple inheritance from QObject is impossible due to the way the meta object system works internally
All the methods declared by your plugin implementation MUST be implemented, even those not declared in the interface, otherwise a "symbol lookup error" is likely to happen which will prevent the loading of your plugin
Alternatively, if these rules are not compatible with what you want to achieve you might consider writing your own plugin loading code. This is not real hard : all you need is to use QLibrary and to create an extra function in your plugins which create an instance of your object (just like Qt plugin loading is done with QPluginLoader...). Additionnaly, if the inheritance limit is especially important you'll have to split your code in at list an executable and one library which would contain the implementation of plugin interfaces (thus allowing the use of meta object system and other similar niceties :D but bringing some architectural difficulties and extra run-time tricks under Nix :()

Hope this helps.:)

KShots
20th April 2007, 19:43
Hmm... I was able to successfully compile and link to my plugin when my interface inheritted from QObject, and had a default constructor and a variable. I was able to access both of my functions that way.

However, I think I see what you're saying as well... if I want my plugin to do something beyond simple QObject stuff (like QWidget or similar), I may have to find another solution. Also, as I'm bending the rules you listed above a bit, I'm not sure how much further I could get away with what I'm doing anyways.

fullmetalcoder
21st April 2007, 10:05
Hmm... I was able to successfully compile and link to my plugin when my interface inheritted from QObject, and had a default constructor and a variable. I was able to access both of my functions that way.

Well, I might have made a mistake... Actually it seems that you can inherit your interface from any Qt class BUT you shouldn't anyway be able to access, for instance, your custom signals, or any other method that is IMPLEMENTED in the base class... Does your plugin implementation successfully emit signals defined in the base class???