PDA

View Full Version : Qt4 Plugins How-to



Chaid
7th July 2006, 23:19
Hi, men!

I have one question and proposition:
there are many people who want to do the plugin support in their applications, but many of them don't know how to do this (such as I am).

Documentation from Trolltech is not enough, but many people in this forum were talking, asking about plugins.

So, if you know how to do the plugin support, or have good documentation or want (and have time) to write it - please help!

I think that many people will say THANKS later!

thanks in advance!

jacek
7th July 2006, 23:30
But do you have problems with plugins in Qt or maybe you don't know how to write an application that will use plugins?

Chaid
7th July 2006, 23:37
Yes I don't know how to write an application that will use plugins.
Also I don't how to write these plugins.

I have some ideas, but...

jacek
8th July 2006, 00:54
Yes I don't know how to write an application that will use plugins.
Also I don't how to write these plugins.
You must start from designing interfaces --- one for each kind of a plugin. You will also need a set of interfaces through which the plugins will communicate with your application.

For example if you want to have plugins that will allow users to export the document to some kind of a format. You would need something like this:
class DocumentExporterInterface
{
public:
// returns default extension for the file format known to plugin
virtual QString defaultExtension() const = 0;

// returns a string that can be used in QFileDialog as a filter
virtual QString filter() const = 0;

// stores document in a given file, returns true on success[1]
virtual bool export( const Document& document, const QString& fileName ) const = 0;
};

Q_DECLARE_INTERFACE( DocumentExporterInterface,
"org.qtcentre.examples.DocumentExporterInterface/1.0" )
All of the plugins should implement this interface and when your application will want to use a plugin, it will have to use one of those three methods.

On other hand plugins will see the application's data through Document interface --- it should define all methods required for successful export (but not more).

Now when you have all interfaces, another things you need are hooks. Application must be aware that there user might want to use a plugin and it must be prepared for this. You must leave special places in your code in which you will pass the control to plugins (if they are present).

In the above case, you will have to create a list of DocumentExporterInterface * that contains pointers to all loaded plugins that implement that interface and when user wants to export document, you will have to:
query all elements of that list and collect filter strings,
open QFileDialog and let user choose a file where document should be saved,
then you have to check which filter was chosen, find the corresponding plugin and invoke export() --- that's all.

How to load plugins? You will need something like:
QDir dir( QCoreApplication::applicationDirPath() + QDir::separator() + "plugins" );
// or
// QDir dir( QLibraryInfo::location( QLibraryInfo::PluginsPath );

QPluginLoader loader;
QStringList files( dir.entryList( QDir::Files | QDir::Executable ) ); // get a list of files
foreach( const QString& file, files ) {
if( QLibrary::isLibrary( file ) ) { // if it looks like library, try to load it
loader.setFileName( dir.absoluteFilePath( file ) );
QObject *plugin = loader.instance();
if( plugin != 0 ) { // if was loaded successfully, register the plugin
registerPlugin( plugin );
}
}
}
...
void PluginManager::registerPlugin( QObject *plugin )
{
DocumentExporterInterface *exporter = qobject_cast< DocumentExporterInterface * >( plugin );
if( exporter != 0 ) {
// store exporter pointer somewhere
}
// check for another interface (plugins might implement multiple interfaces, for example exporter and importer)
...
}


The last thing left is how to implement plugins. Well... it's pretty straightforward:

#include "DocumentExporterInterface.h"
#include "Document.h"

#include <QtPlugin>
...
#incluse <QString>

class CsvExporterPlugin : public QObject, public DocumentExporterInterface // QObject must be first!
{
Q_OBJECT
Q_INTERFACES( DocumentExporterInterface )
public:
QString defaultExtension() const;
QString filter() const;
bool export( const Document& document, const QString& fileName ) const;
};

#ifdef QT_NO_DEBUG
Q_EXPORT_PLUGIN2( csvexporter, CsvExporterPlugin )
#else
Q_EXPORT_PLUGIN2( csvexporter_debug, CsvExporterPlugin )
#endif

QString CsvExporterPlugin::defaultExtension() const
{
return "csv";
}

QString CsvExporterPlugin::filter() const
{
return "Comma separated values (*.csv)";
}

bool CsvExporterPlugin::export( const Document& document, const QString& fileName ) const
{
...
}

// this line is needed, because we have a Q_OBJECT macro in a .cpp file
#include "csvexporter.moc"
Important: First argument of Q_EXPORT_PLUGIN2 must match the name of a file with plugin (without extension).


[1] Of course in real life it might be wiser to use exceptions to report problems, since there are a lot of things that might go wrong during export, but it's just an example.

fullmetalcoder
8th July 2006, 09:32
Sometimes it can be good to do the low-level yourself and not let Qt do it for you...

If you want your plugins to interact with your app deeply you'll have to split it up into one wrapper executable and one core library which contains the classes definition. With such a structure you can drop the interface system recommended by the docs. Indeed you're not forced to use as pure virtual classes as interface since their definition will be present in the core library and thus accessible to the plugins.

Another interesting thing I've tried with plugins is doing the loading/verification myself using QLibrary. The main advantage is that if an error occurs you know precisely which one (symbol lookup error, no instance function found, ...)

In the example given by Jacek, going so deep would be needed if, and only if, the Document class is not pure virtual...