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.
Related
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.
Essentially I want to trigger some code after the my QFileDiag has 1 or more files selected and is accepted ("open" button clicked), the problem is that I can't seem to actually trigger my code in the slot.
Here is the code in my main widget
file_select_diag = new QFileDiag(this)
connect(file_select_diag, &QFileDialog::fileSelected, this,
&MainWidget::connect_test);
auto files = file_select_diag->getOpenFileName(
this,
tr("test"),
QDir::homePath(),
tr("text (*.txt)");
void MainWidget::connect_test(QString str)
{
cout << str.toStdString();
}
And here is the header declaration
{
Q_OBJECT
public:
explicit MainWidget(QWidget *parent = 0); //Constructor
~MainWidget(); // Destructor
private slots:
void connect_test(QString str);
void connect_test2(); //like above but cout << "HIT" << end;
private:
QFileDialog *file_select_diag;
I've tried connecting to both connect_test and connect_test2, when I run my app and select files, hit open, nothing happens.
Solution (copied from G.M.'s comment below)
Note that QFileDialog::getOpenFileName is a static member of
QFileDialog so the call file_select_diag->getOpenFileName(...)
effectively creates a QFileDialog instance independent of
file_select_diagand calls getOpenFileName against that.
So effectively the two approaches here are either go entirely with the static method getOpenFileName and do not initialize file_select_diag or go entirely for the instance approach, configure the file_select_diag then use file_select_diag->show(), in which case the signal will work.
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);
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.
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.