Avoid having to qRegisterMetaType (pointer vs reference), concern about const - qt

Given the signal:
void dbConnected(const QSqlDatabase &db);
I learned (from Qt Communication betwen threads, app design) how to avoid having to use
qRegisterMetaType<QSqlDatabase>("QSqlDatabase");
Just changing the signal to this form:
void dbConnected(QSqlDatabase *db);
And, in the slot side I'll use something like this:
void onDBConnected(QSqlDatabase * const db);
I'm concerned with the usage of db (as in the beginning I've made the reference const), so I make it const here (in the slot side). I've tried to do the same in the signal side with
void dbConnected(QSqlDatabase * const db);
But doing so I have the runtime error (mentioned in Qt Communication betwen threads, app design) back. So I tried another form, which seems to do the job:
void dbConnected(QSqlDatabase *db) const;
Am I in the right direction?

Am I in the right direction?
Maybe. First, make sure that you know that you cannot pass the database to an object living in another thread. That was one of the major mistakes you did in the code in the other question. Don't do that anymore.
If you're passing an object via reference, it must be copyable. A QSqlDatabase is copyable after you open it. So you're OK here, too.
But you might not need to pass the database reference at all. QSqlDatabase assigns a name to each connection. Instead of passing the databases by value or by pointer, you can pass their connection names instead, and use QSqlDatabase::database to get a database object representing a given connection.
For example:
class Opener : public QObject {
Q_OBJECT
QSqlDatabase m_db;
public:
Q_SIGNAL void dbOpened(const QString &);
void open() {
m_db.addDatabase("FOO", "cats");
...
if (m_db.open()) emit dbOpened(m_db.connectionName());
}
};
class DbUser : public QObject {
Q_OBJECT
QSqlDatabase m_db;
public:
Q_SLOT void onDbOpened(const QString & conn) {
m_db = QSqlDatabase::database(conn);
}
...
};
As you can see, the dbOpened signal emits not a database, but a database connection name, and then the various objects that wish to use that connection can retrieve the database object (handle) by name.

Related

How can I find from which key the timeout signal sent from Qtimer objects in Qmap comes from?

In a function, how to understand the signal sent from Qtimer objects that I created in Qmap, how to find from which object the signal comes from in the slot function.
I created Qmap above code
SQL.h
public slots:
void experiment();
void run();
private:
QMap<QString,QTimer*> job;
I create QMap value and key with Qtimer.
SQL.cpp
void SQL::experiment()
{
QTimer *timer=new Qtimer();
job.insert("dd",timer);
QTimer *timer1=new Qtimer();
job.insert("ss",timer1);
job.value("dd")->start();
job.value("dd")->setInterval)(5000);
job.value("ss")->start();
job.value("ss")->setInterval)(10000);
connect(job.value("dd"),SIGNAL(timeout()),this,SLOT(run()));
connect(job.value("ss"),SIGNAL(timeout()),this,SLOT(run()));
}
In this slot, how can I understand which of the Qtimer in the Qmap receives a signal at that time?
void SQL::run()
{
//job.value(key) // how to understand key
}
I thought I could use sender() with Qmapiterator, but I couldn't find out how. can you help?
Old way is using QSignalMapper. You could also set the key as dynamic property of the timer object, so you could access it through QObject::sender(). But, today you should probably just use a lambda.
First, change the run slot to take any parameters you want:
void SQL::run(const QString &key)
{
QTimer *timer = job.value(key);
}
Then, just use lambda to easily pass the required parameters
QString name="ff";
connect(job.value(name), &QTimer::timeout, this, [this, name]() {
run(name);
});
// name is capture by value above,
// so changing name variable later does not
// affect the value captured by the lambda
As a side note, you shouldn't use the old SIGNAL() and SLOT() macros unless you really have to for some reason. Use the "new" (10 years old) connect syntax.

QObject: Cannot create children for a parent that is in a different thread & QProcess [duplicate]

I am using Qt 4.6.0 (32 bit) under Windows 7 Ultimate. Consider the following QThread:
Interface
class ResultThread : public QThread
{
Q_OBJECT
QString _post_data;
QNetworkAccessManager _net_acc_mgr;
signals:
void onFinished(QNetworkReply* net_reply);
private slots:
void onReplyFinished(QNetworkReply* net_reply);
public:
ResultThread();
void run();
void setPostData(const QString& post_data);
};
Implementation
ResultThread::ResultThread() : _net_acc_mgr(this)
{
connect(&_net_acc_mgr, SIGNAL(finished(QNetworkReply*)),
this, SLOT(onReplyFinished(QNetworkReply*)));
}
void ResultThread::onReplyFinished(QNetworkReply* net_reply)
{
emit onFinished(net_reply);
}
void ResultThread::setPostData(const QString& post_data)
{
_post_data = post_data;
}
void ResultThread::run()
{
_net_acc_mgr.post(QNetworkRequest(QUrl("http://[omitted]")),
QByteArray(_post_data.toStdString().c_str()));
}
Whenever _net_acc_mgr.post() is executed in ResultThread::run(), I got the following Application Output in Qt Creator:
QObject: Cannot create children for a parent that is in a different thread.
(Parent is QNetworkAccessManager(0x22fe58), parent's thread is QThread(0x9284190), current thread is ResultThread(0x22fe48)
What does this mean? How to solve it?
The run() member function is executed in a different thread, rather than the thread where QNetworkRequestManager object was created.
This kind of different-thread problems happen all the time with Qt when you use multiple threads. The canonical way to solve this problem is to use signals and slots.
Create a slot in the object where QNetworkRequestManager belongs to, create a signal in ResultThread and connect both of the somewhere, the constructor of ResultThread would be a good place.
The code which is currently in ResultThread::run() goes to the new slot, and is replaced by a emit(yourSignal()). If necessary send a pointer to your ResultThread as a parameter with your emit function to gain access to member functions/variables.
I received this error message when I forgot to set the QNetworkRequestManager's parent.
nam = new QNetworkAccessManager(this);

Emitting signal from callback

I am using RtMidi library to handle midi message in my Qt application and I am facing problem with slot trigger:
My PhMidiInput object is emiting signal from the RtMidi callback upon specific midi message but the slots are not always triggered.
Here is a part of the PhMidiInput class:
class PhMidiInput : QObject
{
Q_OBJECT
public:
void PhMidiInput() {}
signals:
void quarterFrame(unsigned char data);
private:
static void callback(double, std::vector< unsigned char > *message, void *userData ) {
PhMidiInput *midiInput = (PhMidiInput*)userData;
if(midiInput)
midiInput->onMessage(message);
}
void onMessage(std::vector<unsigned char> *message) {
...
emit quarterFrame(data);
...
}
}
Connecting to a lambda functor works:
PhMidiInput midiIn;
int quarterFrameCount;
connect(&midiIn, &PhMidiInput::quarterFrame, [&](unsigned char data) {
quarterFrameCount++;
});
Connecting to my application window works to:
// MyWindow inherits from QMainWindow
connect(_midiIn, &PhMidiInput::quarterFrame, this, &MyWindow::onQuarterFrame);
When trying to connect to a custom class (MidiTest) inheriting from QObject it does'nt trigger:
connect(_midiIn, &PhMidiInput::quarterFrame, this, &MidiTest::onQuarterFrame);
I was wondering if there was something around QObject::moveToThread() but since I don't create the thread myself (the signal is sent from a callback) I don't know if I need to use it or not.
It is as simple as calling emit obj->quarterFrame(data); from the callback. If the connection type is default then this will be perfectly thread safe.
Though you should create a QByteArray from data to pass around as data will likely not be valid by the time the slots get called.
void callback(..., void* user){
//user is the standard void* in most callbacks passed as reinterpret_cast<void*>(this)
unsigned char* data = ...;
QByteArray bytes(data);
emit reinterpret_cast<PhMidiInput>(user)->quarterFrame(bytes);//calling the signal which will behave as you'd expect
}
In the last connect() call you pass this and MidiTest::onQuarterFrame as the receiver object and method. I bet this is not an instance of MidiTest, is it?
The problem here is that you're passing SLOT method from MidiTest, while the receiver object is this, which is not instance of MidiTest. Change receiver from this to some instance of MidiTest.
I'm surprised this code doesn't crash your application when running.

Error while inheriting QObject

I've got a class applicationManager that ought to emit signal when there is an info message or an error occurs, while I'm suppose to log that messages consuming it by QXmppLogger class object. There is a QXmppLoggable class that got methods like info() and warning() that emits a signal logmessage() of internal QXmppLogger. So, in order to emit a signal I've inherited QXmppLogable class which inherits QObject itself, hence to be able to use info() and warning() and connected emitted SIGNAL(logmessage()) by info() and warning() methods, to SLOT (log()) of QXmppLogger object. Here is the code snippet:
header "imApplicationManager.h"
class applicationManagement: public QXmppLoggable
{
//Q_OBJECT
public:
bool createDataBaseConnection();
applicationManagement();
~applicationManagement();
void setLogger(QXmppLogger *logger);
private:
QXmppLogger *logger;
};
and related "imApplicationManager.cpp"
applicationManagement::applicationManagement()
{
QObject::connect(this, SIGNAL(logMessage(QXmppLogger::MessageType,QString)),
logger, SLOT(log(QXmppLogger::MessageType,QString)));
}
applicationManagement::~applicationManagement()
{
// db..closing
}
bool applicationManagement::createDataBaseConnection(){
//...database conncetion...
if (!dataBase.open()){
warning(QString("Cannot open database %1").arg(dataBase.lastError().text()));
qDebug()<< "Cannot open database " << dataBase.lastError();
return false;
}
info(QString("Database succeeded!"));
return true;
}
void applicationManagement::setLogger(QXmppLogger *logger)
{
if(this->logger != logger)
this->logger = logger;
}
in the main.cpp
#include "imApplicationManager"
#incLude "QXmppLogger"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
applicationManagement aa;
QXmppLogger log;
aa.setLogger(&log);
return a.exec();
}
Compilation informs of no error but when luanched there is a Sigmentation fault.
How can I fix that?
OK, decided to post a full answer:
First of all, you do NEED the Q_OBJECT macro. The errors you get from it will likely disappear when you Clean All, Run qmake and Rebuild All from the Build menu of QtCreator. Qt relies on a LOT of generated boilerplate code you never have to type, and without Q_OBJECTthat code will not be generated. That is the reason you sometimes need to clean up after you made changes, run qmake (which generates the code) and rebuild with the now up-to-date generated code.
Second - in the constructor of applicationManagement you connect to an uninitialized pointer logger. This is probably why you get segmentation fault. You can use two approaches to fix that:
Pass the pointer to the logger in the constructor of applicationManagement, this way you have something to connect to in the constructor. This way you cannot instantiate an applicationManagement before a logger, unless you use logger(new QXmppLogger) in the constructor initialization list.
Move the connection from the applicationManagement constructor to the setLogger()method. Do not forget to disconnect a previous connection if any.
It has been mentioned that the Q_OBJECT macro is needed, but I think a little explanation would be useful too, as understanding why it's needed helps to remember to use it.
Qt adds to C++, amongst other features, the functionality of signals and slots and this is done with the Meta-Object Compiler (or moc for short). When compiling Qt code, the moc parses the header files and creates source code for you, which can be seen in the moc files. Occasionally these get out of sync and cleaning these files will fix this issue.
If you look more closely at the Q_OBJECT macro, it is defined as this: -
#define Q_OBJECT \
public: \
Q_OBJECT_CHECK \
static const QMetaObject staticMetaObject; \
virtual const QMetaObject *metaObject() const; \
virtual void *qt_metacast(const char *); \
QT_TR_FUNCTIONS \
virtual int qt_metacall(QMetaObject::Call, int, void **); \
private: \
Q_DECL_HIDDEN_STATIC_METACALL static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **); \
struct QPrivateSignal {};
As can be seen here, the Q_OBJECT macro adds function definitions to the class and its the implementation of these in the QObject parent class that provides the added C++ features such as signals / slots, properties, RTTI etc. When calling signals and slots, internally Qt uses the qt_metacall function. There's an interesting explanation of the implementation of these here. Therefore without the Q_OBJECT macro in a class derived from QObject, signals and slots won't work.
You use logger but it is not initialized before:
QObject::connect(this, SIGNAL(logMessage(QXmppLogger::MessageType,QString)),
logger, SLOT(log(QXmppLogger::MessageType,QString)));
try to rewrite your constructor like that:
applicationManagement::applicationManagement(QXmppLogger *logger_):
logger(logger_)
{
QObject::connect(this, SIGNAL(logMessage(QXmppLogger::MessageType,QString)),
logger, SLOT(log(QXmppLogger::MessageType,QString)));
}
or connect that signal after initializing logger.

How to delete a QProcess instance correctly?

I have a class looking like this:
class FakeRunner : public QObject
{
Q_OBJECT
private:
QProcess* proc;
public:
FakeRunner();
int run()
{
if (proc)
return -1;
proc = new QProcess();
QStringList args;
QString programName = "fake.exe";
connect(comp, SIGNAL(started()), this, SLOT(procStarted()));
connect(comp, SIGNAL(error(QProcess::ProcessError)), this,
SLOT(procError(QProcess::ProcessError)));
connect(comp, SIGNAL(finished(int, QProcess::ExitStatus)), this,
SLOT(procFinished(int, QProcess::ExitStatus)));
proc->start(programName, args);
return 0;
};
private slots:
void procStarted() {};
void procFinished(int, QProcess::ExitStatus) {};
void procError(QProcess::ProcessError);
}
Since "fake.exe" does not exist on my system, proc emits the error() signal. If I handle it like following, my program crashes:
void FakeRunner::procError(QProcess::ProcessError rc)
{
delete proc;
proc = 0;
}
It works well, though, if I don't delete the pointer. So, the question is how (and when) should I delete the pointer to QProcess? I believe I have to delete it to avoid a memory leak. FakeRunner::run() can be invoked many times, so the leak, if there is one, will grow.
Thanks!
You can't delete QObject instance inside slot which is connected to a signal in this instance using normal delete operator. This is due to the fact that if signal and slot connected using direct connection then the slot actually called from the signal implementation made by moc. This is like attempt to delete this; from inside the member of a class. There is a solution QObject::deleteLater(). Object will be deleted by Qt event loop inside events processing function. So you need to call proc->deleteLater() in your case.
And you don't need to disconnect signal from slot since Qt do it automatically when QObject is deleted.

Resources