templated class in Qt - qt

Is it possible to use a templated class (not based on QObject and doesn't have Q_OBJECT macro) in Qt? I keep getting a linker error when trying to use a templated class. however, when I remove the template from the class, it compiles and links fine. I'm just trying to declare a local variable of type Filter, which uses a template, and I get this linker error:
error: undefined reference to `NumericFilter<int>::NumericFilter(int, int)'
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "filter.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
NumericFilter<int> filter(0, 1);
}
MainWindow::~MainWindow()
{
delete ui;
}
filter.h
template <class T>
class NumericFilter {
public:
NumericFilter (int itemType, int val);
protected:
T m_val;
};
filter.cpp
#include "filter.h"
template <class T>
NumericFilter<T>::NumericFilter (int, int)
{
}
Note that if you remove the template in the declaration and source files and comment out the 'T' member, then it compiles fine.

http://www.parashift.com/c++-faq-lite/separate-template-fn-defn-from-decl.html
If you compile and (try to) link these two .cpp files, most compilers will generate linker errors. There are two solutions for this. The first solution is to physically move the definition of the template function into the .h file, even if it is not an inline function.
This solution may (or may not!) cause significant code bloat, meaning your executable size may increase dramatically (or, if your compiler is smart enough, may not; try it and see).
The other solution is to leave the definition of the template function in the .cpp file and simply add the line template void foo(); to that file:
// File "foo.cpp"
#include <iostream>
#include "foo.h"
template<typename T> void foo()
{
std::cout << "Here I am!\n";
}
template void foo<int>();
So for your case, you would have in your .cpp file:
#include "filter.h"
template <class T>
NumericFilter<T>::NumericFilter(int, int)
{
}
template NumericFilter<int>::NumericFilter<int>(int, int); // added line!!!
Tada! No compile errors!
BTW, parashift's explanations on templates in C++ are the best IMHO.
Hope that helps.

Related

Qt class MyButton when use Q_PRIVATE_SLOT, the .h file compile error: undefined MyButtonPrivate

https://www.dvratil.cz/2019/11/q-private-slot-with-new-connect-syntax/
#include <QPushButton>
#include <memory>
class MyButtonPrivate;
class MyButton : public QPushButton {
Q_OBJECT
public:
explicit MyButton(QWidget* parent);
~MyButton() noexcept override;
private:
std::unique_ptr<MyButtonPrivate> const d_ptr;
Q_DECLARE_PRIVATE(MyButton);
Q_PRIVATE_SLOT(d_func(), void onClicked(bool));
};
this is the .h file, compile error: undefined MyButtonPrivate.
truely, the moc_MyButton.cpp(auto generated by compiler) doesnot include the MyButtonPrivate.
then what's wrong, and how to solve?
I'm not sure why it's an error, but according to https://forum.qt.io/topic/61295/solved-what-is-the-usage-of-q_private_slot/3, with Qt > 5 and C++11 it is a useless macro.
the moc file generated:
#include "../../MyButton.h"
#include <QtCore/qbytearray.h>
#include <QtCore/qmetatype.h>
...
case 0: _t->d_func()->onClicked((*reinterpret_cast< bool(*)>(_a[1]))); break;
...
notice: _t->d_func()->onClicked invoke the private class's func(but no include)
so absolutely result in compile error. the question is moc file doesnt include MyButton_p.h, and manually add the include:
#include "../../MyButton.h"
#include "../../MyButton_p.h"
...
then its ok(but the moc file shouldnt modify manually)

QTextBrowser for Helper

I am trying to make a helper with QTextBrowser. As I understood, home(), backward() and forward() are already implemented in QTextBrowser and required only connections to the buttons. Below there is .h and .cpp files
#ifndef HELPWINDOW_H
#define HELPWINDOW_H
#include <QDialog>
namespace Ui {
class HelpWindow;
}
class HelpWindow : public QDialog
{
Q_OBJECT
public:
explicit HelpWindow(QWidget *parent = 0);
~HelpWindow();
private slots:
private:
Ui::HelpWindow *ui;
};
#endif // HELPWINDOW_H
and
#include "helpwindow.h"
#include "ui_helpwindow.h"
HelpWindow::HelpWindow(QWidget *parent) :
QDialog(parent),
ui(new Ui::HelpWindow)
{
ui->setupUi(this);
// connection
connect(ui->pushButton_home,SIGNAL(clicked()),ui->textBrowser,SLOT(home()));
connect(ui->pushButton_forward,SIGNAL(clicked()),ui->textBrowser,SLOT(forward()));
connect(ui->pushButton_backward,SIGNAL(clicked()),ui->textBrowser,SLOT(backward()));
}
HelpWindow::~HelpWindow()
{
delete ui;
}
There is no any error message. It is possible to read and click the links inside QTextBrowser. Only there are no any actions with buttons. What do I miss here?
You need to call either one or both of the following properties
ui->textBrowser.setOpenLinks(true);
ui->textBrowser.setOpenExternalLinks(true);
and if you want filter or re-route the links at runtime
connect(ui->textBrowser, SIGNAL(sourceChanged(QUrl)), pointerToYourCode, SLOT(slotSourceChanged(QUrl)));
and implement
void YourCode::slotSourceChanged(const QUrl& url) {...}
I found why it did not work. The initial source should be specify:
ui->textBrowser->setSource(QUrl::fromLocalFile("help/index.html"));
Thank you, Jens for spending time.

Qt debug pointer not following expected flow in 2nd and successive iterations

I want to display a "generating image..." kind of modal dialog, other than the main GUI. This "generating image..." dialog should be temporary, and be displayed and disappear without user intervention.
For displaying this dialog, the Qt code should check for existence of a .txt file in a specific location in the PC's hard disk. If the .txt file exists, then the dialog should pop-up.
For making this dialog disappear, the Qt code should check whether that .txt file contains the string "OK" in the first line. The dialog should disappear only when this "OK" is found, until then it should continue to display "generating image..."
A good way to do this is to use signal slot mechanism. I would like to know, what functions should be used as SIGNALS in both the cases, of displaying and removing the dialog.
So far, I could manage a simple code, illustrating a "generating image..." using signal slot mechanism, but with setValue() and pressing a push button(i.e. involving user intervention), and not with the checking of .txt file or the "OK" string inside that .txt file(user non-intervention).
Please advise me, whether my logic can be implemented? If yes, how? Also, what SIGNALs should be used?
************************UPDATED SECTION(as of Feb 24th '14):****************************************************
I have revised the code according to Dmitry Sazonov's suggestions. I am able to display the loading GIF whenever a new file is created/deleted in a designated directory. Now I want to close this loading qDialog, when the usbResponse.txt file has "ok" inside it. I tried using signal slot, to implement hide(), but could not get it.
I do not get errors, but the qDialog window does not close as expected. I tried both, secDialog.close() and secDialog.hide(), but the window didn't close. Perhaps because the secDialog object is not the same in both the SLOTs. So I also made secDialog, a global object, but I received an error as follows:-
QWidget: Must construct a QApplication before a QWidget
I looked it up: https://qt-project.org/forums/viewthread/12838
Changed the build modes, accordingly, but that didn't help either. Please tell me how do I close my qDialogs, when I find the "ok" in usbResponse.txt file.
************************UPDATED SECTION(as of Mar 14th '14):****************************************************
I could close the qDialog containing the GIF using hide(). I have done a total overhaul of the code. As mentioned above, the qDialog containing the GIF should appear whenever a text file called usbResponse.txt exists at a designated location. Also taking #Dmitry Sazonov's advice, I am able to close the GIF whenever that txt file i.e. usbResponse.txt is modified, using FileSystemWatcher.
I'm continuously scanning for the existence of the .txt using threads. When I find the file, I display the loading GIF. When the .txt is modified the GIF should disappear. This works fine for the first iteration, i.e. when
(the following are observations after debugging)
the usbResponse.txt exists => GIF is displayed
when usbResponse.txt is modified => GIF is hidden & the .txt is deleted.
THe problem, in next iteraiton,(i.e. all iterations after the first)
the usbResponse.txt is created => the GIF is displayed.
when usbResponse.txt is modified, the debug pointer continues to remain in
afterFileHasBeenFound()
whereas it should have gone in
closeModified(const QString &str)
What is my mistake here?
Here is my code:
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QFile>
#include <QDebug>
#include <QFileSystemWatcher>
#include "dialog.h"
#include "mythread.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
public slots:
void afterFileHasBeenFound();
void closeModified(const QString &str);
private slots:
private:
Ui::MainWindow *ui;
Dialog *pDialog;
MyThread *mThread;
};
#endif // MAINWINDOW_H
dialog.h
#ifndef DIALOG_H
#define DIALOG_H
#include <QDialog>
#include <QMovie>
#include <QLabel>
#define GIF_PATH "E:\\QT1\\timeStampPopUp\\timeStampPopUp\\loading.gif"
namespace Ui {
class Dialog;
}
class Dialog : public QDialog
{
Q_OBJECT
public:
explicit Dialog(QWidget *parent = 0);
~Dialog();
void displayLoadingGif();
private:
Ui::Dialog *ui;
};
#endif // DIALOG_H
mythread.h
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QThread>
#include <QtCore>
#include <QDebug>
#define FILE_PATH "E:\\QT1\\dialogClose2\\dialogClose2\\usbResponse.txt"
class MyThread : public QThread
{
Q_OBJECT
public:
explicit MyThread(QObject *parent = 0);
void run();
QString name;
int exec();
void checkFile();
signals:
void testSignal(QString message);
void fileFoundDisplayGif();
public slots:
};
#endif // MYTHREAD_H
dialog.cpp
#include "dialog.h"
#include "ui_dialog.h"
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
displayLoadingGif();
}
Dialog::~Dialog()
{
delete ui;
}
void Dialog::displayLoadingGif()
{
QMovie *pMovie = new QMovie(GIF_PATH);
ui->loadingGifLabel->setMovie(pMovie);
pMovie->start();
}
mythread.cpp
#include "mythread.h"
MyThread::MyThread(QObject *parent) :
QThread(parent)
{
}
void MyThread::run()
{
exec();
}
int MyThread::exec()
{
while(1)
{
checkFile();
emit(testSignal("hello world!!"));
sleep(1);
}
}
void MyThread::checkFile()
{
QFile file(FILE_PATH);
if(file.exists())
{
qDebug()<<"exists";
emit(fileFoundDisplayGif());
}
else
qDebug()<<"doesn't exist";
}
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
mThread = new MyThread(this);
mThread->name = "mThread";
connect(mThread, SIGNAL(fileFoundDisplayGif()), this, SLOT(afterFileHasBeenFound()), Qt::QueuedConnection);
mThread->start();
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::afterFileHasBeenFound()
{
if(pDialog != NULL)
return;
pDialog = new Dialog();
pDialog->setModal(true);
pDialog->show();
}
void MainWindow::closeModified(const QString &str)
{
Q_UNUSED(str)
if(pDialog != NULL)
{
pDialog->hide();
}
QFile file(FILE_PATH);
file.remove();
pDialog = NULL;
}
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QFileSystemWatcher fileWatcher;
fileWatcher.addPath(FILE_PATH);
QStringList fileList = fileWatcher.files();
Q_FOREACH(QString file, fileList)
qDebug() << "File name " << file;
MainWindow* mc = new MainWindow;
QObject::connect(&fileWatcher, SIGNAL(fileChanged(QString)), mc, SLOT(closeModified(QString)));
mc->show();
return a.exec();
}
Do not use timers for checking file. Use QFileSystemWatcher for traking file modifications.
Implement a slot that will check file content on modification. And call hide() method, when your "OK" text appeared.
IMHO: your solution is to messy. There are a lot of other syncronization mechanisms between processed. Can you modify code of tool that generates image? Should it really work as another process?

Cross-platform Qt5 project using cmake

I am familiar with Qt4 but we are trying to transition to Qt5 and it's being very difficult. I'm trying to create a very simple application, and I had it working using the Qt PRO file, but we need to base it on cmake to keep the build server happy.
The error I get is "invalid use of incomplete type ‘struct Ui::MainWindow’" in the line in mainwindow.cpp where it constructs "ui(new Ui::MainWindow)".
Here are my files (simplified to shorten this post):
mainwindow.h
#include <QtWidgets/QMainWindow>
namespace Ui { class MainWindow; }
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private:
Ui::MainWindow *ui;
};
mainwindow.cpp
#include "mainwindow.h"
#include "moc_mainwindow.cpp"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{ }
MainWindow::~MainWindow()
{ delete ui; }
cmakelists.txt
CMAKE_MINIMUM_REQUIRED( VERSION 2.8.9 FATAL_ERROR )
PROJECT(Test)
set(CMAKE_AUTOMOC TRUE)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
find_package(Qt5Core REQUIRED)
find_package(Qt5Widgets REQUIRED)
file (GLOB Sources src/*.cpp )
add_executable(Test ${Sources} src/mainwindow.ui )
qt5_use_modules(Test Widgets)
I'm sure I'm missing something obvious, but I've been looking all day and can't figure this one out ...
Looking at the generated moc_mainwindow.cpp (again, some lines truncated to keep this post short):
#include "../src/mainwindow.h"
#include <QtCore/qbytearray.h>
#include <QtCore/qmetatype.h>
QT_BEGIN_MOC_NAMESPACE
void MainWindow::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{
Q_UNUSED(_o);
Q_UNUSED(_id);
Q_UNUSED(_c);
Q_UNUSED(_a);
}
QT_END_MOC_NAMESPACE
I am guessing that MainWindow::qt_static_metacall() is declared in my mainwindow.h in the Q_OBJECT macro. which means I have no idea where this mystical Ui::MainWindow is coming from. Or not.
UPDATE
The problem seems to be that in my mainwindow.cpp, I should be #including "ui_mainwindow.h" instead of "moc_mainwindow.cpp", but there is no "ui_mainwindow.h" being generated, only moc_mainwindow.cpp.
Turns out my understanding of Qt was completely WRONG. moc doesn't generate those ui files, it's doing other magic to make signals & slots work. The solution is to add this link to CMakeLists.txt:
qt5_wrap_ui(uifiles src/mainwindow.ui)

Export QObject-based class to DLL

I'm composing a class which derives from QObject, and I want to export this class into a DLL file so other applications can use it. But I got some mysterious problem here:
The code is shown below:
mydll.h:
#ifndef MYDLL_H
#define MYDLL_H
#include "mydll_global.h"
#include <QObject>
#include <QDebug>
class MYDLLSHARED_EXPORT MyDll : public QObject
{
Q_OBJECT
public:
explicit MyDll(QObject * parent = 0);
void test() const;
};
#endif // MYDLL_H
mydll_global.h:
#ifndef MYDLL_GLOBAL_H
#define MYDLL_GLOBAL_H
#include <QtCore/qglobal.h>
#if defined(MYDLL_LIBRARY)
# define MYDLLSHARED_EXPORT Q_DECL_EXPORT
#else
# define MYDLLSHARED_EXPORT Q_DECL_IMPORT
#endif
#endif // MYDLL_GLOBAL_H
mydll.cpp:
#include "mydll.h"
MyDll::MyDll(QObject * parent) :
QObject(parent)
{
}
void MyDll::test() const {
qDebug() << "Hello from dll!";
}
and the dll is used in another application. The dll is compiled successfully. I've add LIBS += "myDll.dll" in the .pro file of the application using this dll, and I've copied myDll.dll to the working directory of the application.
The compiler reports:
C4273: "MyDll::qt_static_metacall" : inconsistent dll linkage.
C2491: "MyDll::staticMetaObject": definition of dllimport static data member not allowed
What's the problem here?
Your code for mydll_global.h checks whether MYDLL_LIBRARY is defined, but none of the code you have posted defines MYDLL_LIBRARY. Is this declared in a file that you have not shared on the question? If not, you need to add a #define MYDLL_LIBRARY in your build project, or your PCH.

Resources