Building a plugin to extend a Qt application - c++

I decided to switch my hobby project application (a dictionary lookup program) to a plugin architecture to enable all kinds of different dictionaries to be developed for other languages in the future. The application is developed in Visual C++ with Qt (5.0.2). I added this header to the application code to define the interface for the dictionary plugins:
// dict_plugin.h
#ifndef DICT_PLUGIN_H
#define DICT_PLUGIN_H
#include <QtPlugin>
class PluginInterface
{
public:
virtual ~PluginInterface() {}
virtual QString language() const = 0;
virtual class QWidget* ui() const = 0;
};
Q_DECLARE_INTERFACE(PluginInterface, "pl.ksmvision.winona.PluginInterface")
#endif // DICT_PLUGIN_H
Next, I created a new project from the "Qt library" template for the plugin itself (using the Qt Visual Studio add-in) which is used to make dll's. The main header file looks like this:
#ifndef JP_PLUGIN_H
#define JP_PLUGIN_H
// created by the template to define Q_DECL_EXPORT
// and _IMPORT macros but the plugin engine takes
// care of that (I think)
//#include "jp_plugin_global.h"
#include <QObject>
#include <QtPlugin>
#include <dict_plugin.h>
class JpPlugin : public QObject, public PluginInterface
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "pl.ksmvision.winona.JpPlugin")
Q_INTERFACES(PluginInterface)
public:
JpPlugin();
virtual ~JpPlugin();
virtual QString language() const;
virtual QWidget* ui() const;
};
#endif // JP_PLUGIN_H
When I try to build this, I get an error from moc on the Q_INTERFACES line specifying the interfaces my plugin is supposed to be implementing:
3>------ Build started: Project: jp_plugin, Configuration: Debug Win32 ------
3> Moc'ing jp_plugin.h...
3>F:\moje\src\cpp\winona\build\jp_plugin\jp_plugin.h(15): error : Undefined interface
========== Build: 2 succeeded, 1 failed, 2 up-to-date, 0 skipped ==========
It looks like moc'ing takes place before the dict_plugin.h file is included, because when I introduce a typo to the include filename, it doesn't complain that the file doesn't exist, just terminates the build with that same error message about the interface being undefined.
What am I doing wrong? Thanks.

The reason moc failed was because the interface declaration was unavailable. The #include directive failed because the file could not be found. Apparently, moc can process #include directives by itself, but doesn't (by default?) print an error message or halt processing if the file to be included can't be found.
The reason the header file with the interface declaration could not be found is that the custom build settings which cause moc to be invoked that are generated by the Qt VS add-in don't inherit the project's include path. I managed to add the required path manualy to moc's command line by entering the property pages of the plugin's header file, browsing to Custom Build Tool->General->Command Line and adding an extra "-I..." include option at the end. After that, moc processed the header and the build was successfull.

For those who venture down this path like I did. My issue was slightly different and had to do with namespaces. I was getting the exact same "undefined interface" error, but the path resolution had no effect for me.
I had something like the following:
namespace foo {
class Interface
{
// ...
};
} // namespace foo
Q_DECLARE_INTERFACE(foo::Interface, "my.interface/1.0")
Incorrect
namespace foo {
class Derived : public QObject, public Interface
{
Q_OBJECT
Q_INTERFACES(Interface)
};
}
Correct
namespace foo {
class Derived : public QObject, public Interface
{
Q_OBJECT
Q_INTERFACES(foo::Interface) //!< Notice foo:: is still provided
};
}
Reason
From the documentation:
If you want to use Q_DECLARE_INTERFACE with interface classes declared in a namespace then you have to make sure the Q_DECLARE_INTERFACE is not inside a namespace though.
But what they don't mention is that the Q_INTERFACES(), even when inside of that namespace by scope, still requires the namespace be provided as though it were global.

Related

How to Use Same Class Between Plugins and App in Qt?

I created a plugin in Qt. Plugin Class and App Class use a same class named ShareData.
This is ShareData Class:
//sharedata.h
class ShareData
{
public:
ShareData();
};
//sharedata.cpp
ShareData::ShareData()
{
}
But When I create an instance in plugin class as follows:
//plugin.pro
INCLUDEPATH += $PWD/../echowindow/
//echoplugin.h
#include "sharedata.h"
class EchoPlugin : public QObject, EchoInterface
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.Examples.EchoInterface" FILE "echoplugin.json")
Q_INTERFACES(EchoInterface)
public:
EchoPlugin();
ShareData* sharedata;
};
//echoplugin.cpp
EchoPlugin::EchoPlugin()
{
sharedata = new ShareData();
}
Then I run the project and I got an error :
error: undefined reference to `ShareData::ShareData()'
Add this is my class structure:
-echoplugin
-echoplugin.pro
-echopluginwindow
-echowindow.pro
-Headers
-echointerface.h
-echowindow.h
-sharedata.h
-Sources
-echowindow.cpp
-main.cpp
-sharedata.cpp
-Plugin
-plugin.pro
-Headers
-echoplugin.h
-Sources
-echoplugin.cpp
What should I do then I can use ShareData Class in Plugin EchoPlugin Class?
Plugin should be independent of main application, so you cannot use classes defined in it. Your build error is probably beacuse of shareddata.cpp is not compiled during plugin build.
If you really want use such design I would suggest creation of library for SharedData.
INCLUDEPATH only says that you can use headers of shareddata. The problem occurs on the linking stage.

Getting redefinitoin error when adding project as reference

I work on VS2015.
I have C++ project A that has a few files and MyClass.h. It's built with /clr flag.
I have another C++ console project B and is also built with /clr flag. The B has B.cpp with the following content:
//B.cpp
#include "MyClass.h"
void main()
{
MyClass* obj = new MyClass();
}
I succesfully can build project B. But, when I add project A as reference (right click->Add Reference) to project B, I suddenly get redefinition error of a class that's definied in a file in project A.
How adding a reference can cause such error?
UPDATE1
All header files have "header guard".
UPDATE2
I thought ProjectA is console and ProjectB is static lib. But, it was late at night and I didn't notice that I created both ProjectA and ProjectB as console applications.
I know that I need to export MyClass if ProjectB is a dll so I could use it in ProjectA. The example maybe stupid but since I got this by mistake I still want to understand why I get the redifinition error in this case. I've uploaded the test to here.
ProjectA has reference to ProjectB.
//ProjectA.cpp
#include "MyHeader.h"
int main()
{
return 0;
}
//ProjectB.cpp
#include "MyHeader.h"
//MyHeader.h
#pragma once
public enum class MyClass : int
{
};
If I remove the reference to ProjectB or I comment out #include "MyHeader.h" I do not get the redifinition error anymore.

QT Plugin Interdependency

I'm having issues with some of the plugins in my application, I have a main project that loads in a set of plugins such that their dependencies are taken into account. However when I have a plugin that has a dependency on another plugin I seem to be getting some symbolic linkage errors.
The actual loading of a plugin :
QPluginLoader pluginLoader(itor->PluginPath);
QObject *tempPlugin = pluginLoader.instance();
Plugin one (Loaded first):
#include "PluginInterface.h"
class NewWizardPlugin: public PluginInterface
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "Code.PluginInterface" FILE "NewViewPlugin.json")
Q_INTERFACES(PluginInterface)
public:
NewWizardPlugin();
virtual ~NewWizardPlugin();
};
Plugin Two:
#include "PluginInterface.h"
#include "NewWizardPlugin.h"
class newStandardSubwizard: public PluginInterface
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "Code.PluginInterface" FILE "New_standard_subwizard.json")
Q_INTERFACES(PluginInterface)
public:
newStandardSubwizard();
virtual ~newStandardSubwizard();
};
The second plugin links against the library for the first plugin
I have some trace in place and know the order they are being loaded in is correct, but using the errorString() function on the QPluginLoader class i know there is a symbolic link error.
Has any one got any ideas?
Fixed - I had not updated LLD_LIBARY_PATH to include the directory of the dynamic objects such that the linker could not find them.

Qt subdirs incude classes

I'm follow Qt Echo Plugin example and trying to write complex application. My project have following structure:
MainDir \
Main.pro
kernel \
kernel.pro
abstractinterface.h
main.cpp
testplugin \
testplugin.pro
abstractplugin.h
abstractplugin.cpp
Problem is in plugin header file:
#include <QObject>
#include <QtPlugin>
#include "abstractinterface.h"
class AbstractPlugin : public QObject, AbstractInterface
// An error appears here
// expected class-name before '{' token
{
Q_OBJECT
//... plugin initialization code ...
public:
explicit AbstractPlugin(QObject *parent = 0);
};
Also, autocompletion can't find class AbstractInterface.
So, question is: what I'm doing wrong? In testplugin.pro file I have line INCLUDEPATH += ../kernel/.
Any help appreciated.
---- EDIT -----
abstractinterface.h
#include <QtPlugin>
#define INTERFACE_ID "AbstractInterface/1.0"
class AbstractInterface
{
public:
virtual ~AbstractInterface();
virtual void init();
virtual void enable();
virtual void disable();
};
Q_DECLARE_INTERFACE(AbstractInterface, INTERFACE_ID)
Given that your pasted files look correct and work here, I am leaning towards that your problem is this line:
INCLUDEPATH += ../kernel/
You likely execute qmake from the project root where your main project file resides calling qmake recursively to generate the Makefiles. However, at the point of generation, the aforementioned path will extend from the project root rather than from the sub-directory. Please fix your testplugin.pro project file by using this instead:
INCLUDEPATH += $$PWD/../kernel/
However, what is even better design is to not handle it inside that project file, but the other kernel.pro where the header files reside. It is more flexible design to add this in there:
INCLUDEPATH += $$PWD
Edit: Based on your comment which was not in the original question, it seems that you have another problem. You seem to have messed with the include guards called the same in two different files and that is why the second inclusion did not result in providing the accessibility for you.

Error linking Qt based app

We're using a really old version of Qt, 1.1, I think. Circa 2000. It's for in-house use only, so there's little concern to upgrade at this time. Program is built on Windows using Visual Studio 2005. I know very little of Qt, other than what I've been researching over the last couple days.
The (very basic) architecture is:
main() creates a QApplication instance.
main() also creates a pointer to a custom subclass of QWidget called Wizard.
Wizard creates a number of Controller objects, which are subclassed from QThread.
I am trying to implement a new class / thread, launched from main(), the purpose of which is to monitor a service and signal an action to be carried out in the Controller objects / threads.
My new class / Thread definition:
#include "qthread.h"
class ServiceMonitor : public QThread
{
Q_OBJECT
public:
ServiceMonitor(int p) : port(p) {}
~ServiceMonitor() {};
private:
void run();
void TerminateProgram();
signals:
void SomethingBadHappened();
private:
int port;
};
And in my cpp file:
void ServiceMonitor::TerminateProgram()
{
...
emit SomethingBadHappened();
...
}
When I compile the app, I receive a linker error:
error LNK2019: unresolved external symbol "protected: void __thiscall ServiceMonitor::SomethingBadHappened(void)"
(?SomethingBadHappened#ServiceMonitor##IAEXXZ) referenced in function "private: void __thiscall ServiceMonitor::TerminateProgram(void)"
(?TerminateProgram#ServiceMonitor##AAEXXZ) ServiceMonitor.obj
I notice that all of our other objects (which have signals) are not derived from Qthread, so I have no examples to follow. The rest (which do use signals) are derived from QWidget or QObject). I notice those use the moc.exe in a custom compile step to generate an output file which is included in the project. I did try to run moc on the header containing the class above and including the output file, where I received:
Error 1 error C2039: 'className' : is not a member of 'QThread'
Error 2 error C3861: 'badSuperclassWarning': identifier not found
Error 3 error C2039: 'staticMetaObject' : is not a member of 'QThread'
Error 4 error C3861: 'activate_signal': identifier not found
What am I doing wrong?
EDIT:
Tried RA's proposal, worked a treat (Once I remembered to include qobject.h). Thanks!
New definition:
#include "qthread.h"
#include "qobject.h"
class ServiceMonitor : public QObject, public QThread
...
For versions of Qt prior to Qt 4.0, the QThread class did not inherit from QObject. As such, if you want to create a class derived from QThread that has signals, you must inherit from both QObject and QThread:
#include "qobject.h"
#include "qthread.h"
class ServiceMonitor : public QObject, public QThread
{
Q_OBJECT
// Rest of class
};
Note that QObject must be listed as the first class derived from.
Also remember to run moc on the class and to compile the generated moc code.
The missing part is the implementation of all the signals, and of the staticMetaObject structure that's declared as part of Q_OBJECT macro. Those are generated by moc. Moc-ing of the headers would normally be handled by a Qt add-in for Visual Studio. Unfortunately, there are no Qt-5 add-ins for VS 2005.
Your options are, in order of decreasing desirability.
Use qmake to generate a VS 2005 project file that invokes moc for you, and includes the necessary files. That would be the best way for you to use Qt.
Manually run moc on all of the header files that contain the Q_OBJECT macro, and add the generated code to your project.
Upgrade to at least VS 2008 (not Express), so that you can use the Qt 5 add-in.
Try Qt 4, which has an add-in for VS 2005.
Since you can have multiple versions of Qt installed at the same time, you can pursue multiple approaches in parallel. For example, #1 and #4.