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.
Related
I have a library that uses QwtPlotMagnifier, amongst other Qwt classes. I decided to subclass QwtPlotMagnifier so that I can emit a signal when the plot is rescaled.
The library (mylib.lib) compiles, but the application using it is now complaining about an unresolved external related to the moc output of QwtPlotMagnifier.
I am linking qwt statically; so the requirement to have the preprocessor directive QWT_DLL in the lowest level library doesn't apply here.
Here's the error (the subclass is called PlotMagnifier):
mylib.lib(moc_PlotMagnifier.obj) : error LNK2001: unresolved external symbol "public: static struct QMetaObject const QwtPlotMagnifier::staticMetaObject" (?staticMetaObject#QwtPlotMagnifier##2UQMetaObject##B)
Nothing special about the subclass declaration:
#pragma once
#include "qwt_plot_magnifier.h"
/**
subclass of QwtPlotMagnifier to provide a signal when zooming is complete
*/
class PlotMagnifier : public QwtPlotMagnifier
{
Q_OBJECT
public:
explicit PlotMagnifier(QWidget *w);
virtual ~PlotMagnifier();
signals:
void rescaled();
protected:
virtual void rescale(double factor);
};
I'm on visual studio 2013 fwiw. My application still includes qwtd.lib as it always has. This has got to be a stupid mistake on my part.. kickstart my brain please, someone!
Add this line to .pro file to give the compiler a hint for an external symbol:
DEFINES += QWT_DLL
In the file qwt_global.h have the macro. Without this macro, the compiler will think this is an internal symbol.
Check, if you have all needed includes in you Visual Studio project.
C/C++ / Additional Include Directories
Here should be a path to <qwt_dir\include> be
Linker / General / Additional Library Directories
Here should be a path to <qwt_dir\lib> be
Linker / Input
Should include qwtd.lib (for debug configuration) and qwt.lib (for release)
Also, check that you have those entries in Release and Debug configurations, it is easy to configure only Debug, while working on Release configuration.
Also, check that you have moc_* file (something like moc_plotmagnifier.cpp) for your PlotMagnifier under Generated Files in your project view, sometimes Qt addin fails to add them.
I'm running a simulation program in Visual Studio 2013 for which I wanted a simple GUI to output/input data.
Since I know some Qt I decided to write a small Qt5 program in Qt Creator, build it as a .dll and link this .dll in my program. The program then calls an initialization function to start the GUI.
Overall this works quite well. The GUI works just like a stand-alone Qt program would. However, once I added a custom signal to my Qt .dll like this:
//File.h
class MainGui : public QMainWindow
{
Q_OBJECT
public:
explicit MainGui(QWidget *parent = 0);
~MainGui();
signals:
void addItemSignal(QGraphicsView* it);
private slots:
void addItemImpl(QGraphicsView* it);
private:
Ui::MainGui *ui;
};
//File.cpp
void MainGui::addItemImpl(QGraphicsView *it)
{
//do anything
}
MainGui::MainGui(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainGui)
{
QObject::connect(this, &MainGui::addItemSignal,
this, &MainGui::addItemImpl);
}
MainGui::~MainGui()
{
delete ui;
}
I ended up getting the following error message when trying to start my main program:
The procedure entry point could not be located in the dynamic link library e:\...\...\MyQtLibrary.dll.
The two spaces between "point" and "could" are not a mistake - it seems like, for some reason, the entry point is not set to anything at all.
After some testing I discovered that the issue lies with using Qt classes within the signals. The following works fine:
//File.h
class MainGui : public QMainWindow
{
Q_OBJECT
public:
explicit MainGui(QWidget *parent = 0);
~MainGui();
signals:
void addItemSignal(void* it);
private slots:
void addItemImpl(void* it);
private:
Ui::MainGui *ui;
};
//File.cpp
void MainGui::addItemImpl(void*it)
{
//do anything
}
MainGui::MainGui(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainGui)
{
QObject::connect(this, &MainGui::addItemSignal,
this, &MainGui::addItemImpl);
}
MainGui::~MainGui()
{
delete ui;
}
This doesn't just affect custom signals but built-in ones too:
QObject::connect(models, &QStandardItemModel::dataChanged,[this](a b, x y){
//do something
});
This breaks the .dll as well.
I also noticed that I had to completely rebuild the .dll in Qt Creator to fix the problem. Removing any problematic signals and just building didn't fix the issue.
The error only happens when the Qt .dll is built in Debug configuration. Release config works well. Whether the MSVC program is Debug or Release seems to have no effect. I haven't changed any of the default settings for either of those configurations (other than a few things that definitely have nothing to do with it).
The only difference I found in Qt Creator between the two configurations is the call of qmake:
qmake.exe "D:\Dev\Qt Workspace\ArduGui\ArduGui.pro" -r -spec win32-msvc2013 "CONFIG+=debug" "CONFIG+=qml_debug"
This is the call for the Debug configuration. In Release the two debug config flags are missing. But when I messed around with the qmake arguments the result did not change. Debug config would always cause the entry point error, regardless of the presence of CONFIG+=debug or CONFIG+=qml_debug. Likewise, Release would always work even if the two flags are added.
So at this point I'm running against a wall. Does anyone have experience with this or can suggest options on how to debug the problem?
Some more info:
I'm using Windows 10, MSVC 2013 and Qt Creator 3.6 with Qt 5.5.1. Both the .exe and .dll are compiled with the MSVC++ 12.0 compiler through their respective IDEs.
If it works fine with void* but doesn't work with QT class pointer passed as argument, I guess you have not registered the class to be able to use it in signal/slot mechanism:
qRegisterMetaType<QGraphicsView *>("QGraphicsView*");
Be sure to call this function before your first use of this signal.
Though I'm not sure this is the cause of all your issues.
I am currently trying integrate the library Qt in my C++ project. First I thought I could use it like any other library. I downloaded it, added the include path and libs to my VS project and tried a small sample of code that just creates a simple window with a text edit and a button. This worked and the small user interface was displayed.
Now I started to try implementing a class derived from a QWidget and realised, that it might not be that simple. The class I tried creating looked like this:
#include <QtWidgets\qstylepainter.h>
#include <QtWidgets\qwidget.h>
class MapRenderer : public QWidget{
Q_OBJECT
public:
MapRenderer(QWidget *parent = 0);
protected:
void paintEvent(QPaintEvent* event);
};
And the corresponding cpp file:
MapRenderer::MapRenderer(QWidget *parent) : QWidget(parent){
}
void MapRenderer::paintEvent(QPaintEvent *event){
QPainter p(this);
p.setPen(QPen(Qt::black, 3));
p.drawPoint(QPointF(10, 20));
}
Now as I tried compiling this I started getting linker three linker errors that looked like this:
error LNK2001: unresolved external symbol "public: virtual struct QMetaObject const * __cdecl MapRenderer::metaObject(void)const " (?metaObject#MapRenderer##UEBAPEBUQMetaObject##XZ) G:\Documents\C++ Projects\HistoricalBorders\MapRenderer.obj
error LNK2001: unresolved external symbol "public: virtual void * __cdecl MapRenderer::qt_metacast(char const *)" (?qt_metacast#MapRenderer##UEAAPEAXPEBD#Z)
error LNK2001: unresolved external symbol "public: virtual int __cdecl MapRenderer::qt_metacall(enum QMetaObject::Call,int,void * *)" (?qt_metacall#MapRenderer##UEAAHW4Call#QMetaObject##HPEAPEAX#Z)
So I had a look around on the internet and found out that Qt apparently requires a far more complicated build process than a 'normal' c++ project with 'normal' libraries. I read of qmake and a Visual Studio Qt Add-In that can be used to create Qt-Projects in Visual Studio.
So I gave it a try and installed the Visual Studio Add-In and specified my Qt-version's root directory in its settings. Afterwards my project still wouldn't compile, but the only error message then was:
error LNK1112: module machine type 'x64' conflicts with target machine type 'X86'
Now I don't really get this, because my Qt library is 64bit and my project configuration also is 64bit as well as all the other libraries I am using. Furthermore, before installing the add-in I didn't have this problem. I have to say, my project is a 'normal' Visual Studio C++ console application. I saw that since I installed the VS Add-in it is also possible to choose 'Qt-console application' when creating a new project in Visual Studio. I also gave this a try but it didn't even compile after creating it. I also don't really know what the difference of a "Qt-project" to a normal VS-project is, except that the Qt libraries are included by default.
The other thing I have to mention is that I am using cmake to create my VS project file because I use version control (mercurial) and multiple people shall work on the project.
At the moment I am totally confused on how to get this library to work.
Now what is my question? Honestly, I don't really know. What I'm asking myself is:
Where does the architecture mismatch linker error come from?
Can I create a normal VS console application and get it to work with Qt?
What's the difference of a 'Qt-console application" to a normal VS project?
Is it possible to generate a Qt-compatible VS project with cmake?
EDIT: Now I uninstalled the Add-In again, reinstalled it but am now getting the old linker errors again (no longer the architecture mismatch). I have no idea why.
EDIT: Ok, some clarification. Yes, I want to use cmake. I also already have a cmakelists.txt and a findQt.cmake that I wrote myself and that asks you for your Qt root directory and then adds the necessary include paths and library paths to the project. This also works so far. E.g. the following code compiles without problems and shows an interface:
QApplication app(argc, argv);
QTextEdit *textEdit = new QTextEdit;
QPushButton *quitButton = new QPushButton("&Quit");
QObject::connect(quitButton, SIGNAL(clicked()), qApp, SLOT(quit()));
QVBoxLayout *layout = new QVBoxLayout;
layout->addWidget(textEdit);
layout->addWidget(quitButton);
QWidget window;
window.setLayout(layout);
window.show();
app.exec();
Now if I try to create a class as shown above (the MapRenderer) I get the linker errors also shown above. Now I thought I am doing something wrong. My only question is what. From your comment, drescherjm, I see that I problably missed the moc-step (sorry but I have no idea what exactly that is).
If you have Qt project file (.pro) (or can get it somehow), you can run
cd <pro_folder>
qmake -tp vc
to (re-)generate Visual Studio project file with Qt build process properly set up, including
moc (meta object compiler; that's what you are missing now),
uic (processing designer's files)
rcc (resource compiler).
All generated files will be compiled and linked properly.
You don't even need AddIn for that.
I created a Visual C++ Project using MSVS and I just made another project in MSVS so that the same code can be built using boost build. I can currently build my project using a Visual C++ project as well as a Makefile project that uses boost build.
There is a difference between the two builds though concerning QT Signals and Slots.For the following code,when I call MyThread::Start() the onTimeout() slot is called when the project is built in visual studio, but not called when built using boost build.
class MyThread: public QObject{
Q_OBJECT
public:
bool start();
public Q_SLOTS:
void onTimeout();
private:
QThread m_thread;
QTimer m_timer;
};
void MyThread::start()
{
m_timer.start(1000);
m_thread.setObjectName(QString("GigeControl"));
m_thread.start();
m_timer.moveToThread(&m_thread);
}
void MyThread::onTimeout()
{
//Do Stuff
}
I am really having a hard time trying to figure out the differences between the two
builds,especially since I need those signals and slots to work.
One major difference that I have noticed so far is that the Visual C++ project is built using /Zp1 so all structures are 1 byte alligned. This is not done in the boost project as I don't know how to. I've seen people on the internet mentioning that structure allignments (especially using #pragma pack can cause problems with QT).
If anyone has some experience they might be able to share it would be greatly appreciated.
It looks like the problem was in fact the structure packing. I am writing code for a couple of code bases that are being merged and there coincidentally happens to be some packing conflicts.
To fix the packing for building with boost-build all I had to to was add this to the lib being generated in the Jamfile:
lib foo
:
...
:
...
<target-os>linux:<cxxflags>-fpack-struct=1
<target-os>windows:<cxxflags>-Zp1
;
This worked for me so hopefully it will help someone else out if they get stuck here too
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.