Are all Qt slots called before deleteLater()? - qt

When I run the following code then both slots are called:
auto packet = new Packet(this);
connect(packet, &Packet::packetFinished, this, [packet]()
{
qDebug() << "Slot 1 called.";
packet->deleteLater();
});
connect(packet, &Packet::packetFinished, this, [packet]()
{
qDebug() << "Slot 2 called.";
});
emit packet->packetFinished();
Results in:
"Slot 1 called"
"Slot 2 called"
Is deleteLater() always executed after all pending slots for a given signal have been called?
Regards,

According to deleteLater() documentation.
Schedules this object for deletion.
The object will be deleted when control returns to the event loop.
So, deleteLater() is called right away, but the object is not deleted at that point. If there is an event loop, and there is no event after the slots being called, then the object will be deleted.

Related

Should accept() be used only at the end of a slot?

I see the accept() somewhat similar to a return, so I've been putting it a the end of my slots with no code afterwards. That is, the accept() "finishes" the execution of the dialog.
Nevertheless, I came across the need to close a dialog and open a new one from a slot in the first one. Therefore, what I thought was moving the accept() to the beginning of the slot and initializing the second dialog after it. Something like the following:
void FirstDialog:slotFirstDialog()
{
accept();
// Setup second dialog arguments
// ...
SecondDialog *sd = new SecondDialog();
sd->exec();
}
Is this use of accept() valid? Is it good practice?
I'd avoid it. Calling accept() can trigger a delayed deletion of FirstDialog (say, if it has the Qt::WA_DeleteOnClose flag set)1; in that case, it would be deleted in one of the first events dispatched by the nested event loop (sd->exec()), which would lead to go on executing code in a method of an instance that has been deleted. This is just a sample problem on the top of my head, I'm sure others can be found.
I'd probably just hide the dialog before calling exec() on the other, and call accept() after the end of the nested event loop.
void FirstDialog:slotFirstDialog()
{
// Setup second dialog arguments
// ...
SecondDialog *sd = new SecondDialog();
hide();
sd->exec();
accept();
// NB are we leaking sd?
}
By the way:
SecondDialog *sd = new SecondDialog();
sd->exec();
here you are allocating on the heap a dialog without a parent, so either you set the Qt::WA_DeleteOnClose or explicitly call this->deleteLater() inside its code, or you are leaking the dialog instance.
Notes:
and it is explicitly remarked in the documentation
As with QWidget::close(), done() deletes the dialog if the Qt::WA_DeleteOnClose flag is set.
QDialog::accept calls QDialog::done with a dialog code Accepted. Here is how QDialog::done looks like:
void QDialog::done(int r)
{
Q_D(QDialog);
setResult(r);
hide();
d->close_helper(QWidgetPrivate::CloseNoEvent);
d->resetModalitySetByOpen();
emit finished(r);
if (r == Accepted)
emit accepted();
else if (r == Rejected)
emit rejected();
}
which, according to the documentation:
Hides the modal dialog and sets the result code to Accepted.
With this in mind, I think this is not a question of a good practice, but of what your application logic requires.

Why is my mousePressEvent called twice?

I want to make a QLabel clickable and followed this "how-to". I was not sure how to get this piece of code into my GUI (I am quite newbie to qt). What I did was:
I created a new class (just copy/paste of ClickableLabel from the link, but I changed the signal to clicked(QMouseEvent* event))
I added a QLabel to my GUI and "promoted" it to a ClickableLable
I connected the signal to a slot of my main window where I std::cout some stuff:
connect(this->ui->label,SIGNAL(clicked(QMouseEvent*)),
this,SLOT(on_label_clicked(QMouseEvent*)));
It seems to work. The problem is that each time I click on the label the mousePressedEvent gets called twice. I also tried mouseReleasedEvent but its the same.
Any ideas what could go wrong?
EDIT: Here is my modified ClickableLable:
class MyClickableLabel : public QLabel {
Q_OBJECT
public:
MyClickableLabel(QWidget* parent=0);
~GBoardLabel();
signals:
void clicked(QMouseEvent* event);
protected:
void mouseReleaseEvent(QMouseEvent* event);
};
MyClickableLabel::MyClickableLabel(QWidget* parent) : QLabel(parent) {this->setText("");}
MyClickableLabel::~MyClickableLabel() {}
void MyClickableLabel::mouseReleaseEvent(QMouseEvent *event){
std::cout << "CLICKED R" << std::endl;
std::cout << event->type() << std::endl;
std::cout << event->pos().x() << std::endl;
std::cout << event->pos().y() << std::endl;
emit clicked(event);
}
The couts in the in the above method I added only later and realized that the mouseReleaseEvent is actually only called once. But when I connect the clicked to a slot of my mainwindow, this slot recieves the event twice.
Then I removed the connect statement and to my surprise the signal is still emited and recieved (only once). I am a bit puzzled how this works, because I am pretty sure that I do not mistakenly have a connect anywhere in the code.
My label is working, but I would like to understand what is going on. Actually I am not 100% sure anymore that I didn't use some Qt creator feature make the connection. However, I have no idea where to find such connections. For example, I have a QButton on the same main window. In the gui editor I right clicked it and then "show slots"->"clicked()"->"OK" and automatically a method called on_pushButton_clicked() is created, but I have no idea, where this is called / how the button's signal is connected to this method. On the other hand, I do not get the MyClickableLabel::clicked(QMouseEvent*) listed in the list of slots for my label, thus I don't think I created the connection this way...
I could fix it, but I do not really understand what is going on...
It was not the mousePressEvent that was fired twice, but my on_label_clicked slot recieved the event twice.
I removed the connect statement and now the on_label_clicked is called only once per click. It seems like, there is something going on under the hood. Maybe when the slot is called on_label_clicked it gets automatically ("qtmatically") connected to the mouse events emmited from the child called label ?
EDIT: Still didnt find the official docs, but this blog explains it quite nicely. In summary, one just needs to use the naming convention for the slot
void on_<widget name="">_<signal name="">(<signal parameters="">);
to make use of the auto connect feature.

Qt event loop and unit testing?

I'we started experimenting with unit testing in Qt and would like to hear comments on a scenario that involves unit testing signals and slots.
Here is an example:
The code i would like to test is (m_socket is a pointer to QTcpSocket):
void CommunicationProtocol::connectToCamera()
{
m_socket->connectToHost(m_cameraIp,m_port);
}
Since that is an asynchronous call i can't test a returned value. I would however like to test if the response signal that the socket emits on a successful connection (void connected ()) is in fact emitted.
I've written the test below:
void CommunicationProtocolTest::testConnectToCammera()
{
QSignalSpy spy(communicationProtocol->m_socket, SIGNAL(connected()));
communicationProtocol->connectToCamera();
QTest::qWait(250);
QCOMPARE(spy.count(), 1);
}
My motivation was, if the response doesn't happen in 250ms, something is wrong.
However, the signal is never caught, and I can't say for sure if it's even emitted. But I've noticed that I'm not starting the event loop anywhere in the test project. In the development project, the event loop is started in main with QCoreApplication::exec().
To sum it up, when unit testing a class that depends on signals and slots, where should the
QCoreApplication a(argc, argv);
return a.exec();
be run in the test environment?
I realize this is an old thread but as I hit it and as others will, there is no answer and the answer by peter and other comments still miss the point of using QSignalSpy.
To answer you original question about "where the QCoreApplication exec function is needed", basically the answer is, it isn't. QTest and QSignalSpy already has that built in.
What you really need to do in your test case is "run" the existing event loop.
Assuming you are using Qt 5:
http://doc.qt.io/qt-5/qsignalspy.html#wait
So to modify your example to use the wait function:
void CommunicationProtocolTest::testConnectToCammera()
{
QSignalSpy spy(communicationProtocol->m_socket, SIGNAL(connected()));
communicationProtocol->connectToCamera();
// wait returns true if 1 or more signals was emitted
QCOMPARE(spy.wait(250), true);
// You can be pedantic here and double check if you want
QCOMPARE(spy.count(), 1);
}
That should give you the desired behaviour without having to create another event loop.
Good question. Main issues I've hit are (1) needing to let app do app.exec() yet still close-at-end to not block automated builds and (2) needing to ensure pending events get processed before relying on the result of signal/slot calls.
For (1), you could try commenting out the app.exec() in main(). BUT then if someone has FooWidget.exec() in their class that you're testing, it's going to block/hang. Something like this is handy to force qApp to exit:
int main(int argc, char *argv[]) {
QApplication a( argc, argv );
//prevent hanging if QMenu.exec() got called
smersh().KillAppAfterTimeout(300);
::testing::InitGoogleTest(&argc, argv);
int iReturn = RUN_ALL_TESTS();
qDebug()<<"rcode:"<<iReturn;
smersh().KillAppAfterTimeout(1);
return a.exec();
}
struct smersh {
bool KillAppAfterTimeout(int secs=10) const;
};
bool smersh::KillAppAfterTimeout(int secs) const {
QScopedPointer<QTimer> timer(new QTimer);
timer->setSingleShot(true);
bool ok = timer->connect(timer.data(),SIGNAL(timeout()),qApp,SLOT(quit()),Qt::QueuedConnection);
timer->start(secs * 1000); // N seconds timeout
timer.take()->setParent(qApp);
return ok;
}
For (2), basically you have to coerce QApplication into finishing up the queued events if you're trying to verify things like QEvents from Mouse + Keyboard have expected outcome. This FlushEvents<>() method is helpful:
template <class T=void> struct FlushEvents {
FlushEvents() {
int n = 0;
while(++n<20 && qApp->hasPendingEvents() ) {
QApplication::sendPostedEvents();
QApplication::processEvents(QEventLoop::AllEvents);
YourThread::microsec_wait(100);
}
YourThread::microsec_wait(1*1000);
} };
Usage example below.
"dialog" is instance of MyDialog.
"baz" is instance of Baz.
"dialog" has a member of type Bar.
When a Bar selects a Baz, it emits a signal;
"dialog" is connected to the signal and we need to
make sure the associated slot has gotten the message.
void Bar::select(Baz* baz) {
if( baz->isValid() ) {
m_selected << baz;
emit SelectedBaz();//<- dialog has slot for this
} }
TEST(Dialog,BarBaz) { /*<code>*/
dialog->setGeometry(1,320,400,300);
dialog->repaint();
FlushEvents<>(); // see it on screen (for debugging)
//set state of dialog that has a stacked widget
dialog->setCurrentPage(i);
qDebug()<<"on page: "
<<i; // (we don't see it yet)
FlushEvents<>(); // Now dialog is drawn on page i
dialog->GetBar()->select(baz);
FlushEvents<>(); // *** without this, the next test
// can fail sporadically.
EXPECT_TRUE( dialog->getSelected_Baz_instances()
.contains(baz) );
/*<code>*/
}
I had a similar issue with Qt::QueuedConnection (event is queued automatically if the sender and the receiver belongs to different threads). Without a proper event loop in that situation, the internal state of objects depending on event processing will not be updated. To start an event loop when running QTest, change the macro QTEST_APPLESS_MAIN at the bottom of the file to QTEST_MAIN. Then, calling qApp->processEvents() will actually process events, or you can start another event loop with QEventLoop.
QSignalSpy spy(&foo, SIGNAL(ready()));
connect(&foo, SIGNAL(ready()), &bar, SLOT(work()), Qt::QueuedConnection);
foo.emitReady();
QCOMPARE(spy.count(), 1); // QSignalSpy uses Qt::DirectConnection
QCOMPARE(bar.received, false); // bar did not receive the signal, but that is normal: there is no active event loop
qApp->processEvents(); // Manually trigger event processing ...
QCOMPARE(bar.received, true); // bar receives the signal only if QTEST_MAIN() is used

Qt Signals and slots mechanism blocked by a loop in main

I have a following situatuion.
2 Socket objects are created in the main in a for loop (the original problem has 1000 objects). Upon creation the start() method is invoked.
start() creates a QTcpSocket which tries to connect to some host.
Socket has slots which catch the connected() signal from QTcpSocket and print some debug output
What happens is that chronologically first ALL the Socket objects are created after which the sockets are started. Here is an example output of debug options:
1. Created Socket object 1
2. Invoked Socket object 1 start()
3. Created Socket object 2
4. Invoked Socket object 2 start()
5. Socket object 1 TcpSocket Connected
6. Socket object 2 TcpSocket Connected
Code:
//main.cpp
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
for (int i=0; i<10; i++)
{
Socket *socket = new Socket();
qDebug() << "Socket object created";
socket->Start();
}
return a.exec();
}
//socket.cpp
Socket::Socket(QObject *parent)
: QObject(parent)
{}
void Socket::Start()
{
qDebug()<<"Start method invoked";
socket = new QTcpSocket(this);
connect(socket,SIGNAL(connected()), this, SLOT(on_connect()), Qt::DirectConnection);
socket->connectToHost("192.168.5.5",12345);
}
void Socket::on_connect()
{
QTcpSocket* socket = qobject_cast<QTcpSocket *>(QObject::sender());
qDebug() << socket->socketDescriptor() << " Connected.";
}
This is not the behavior I expected because the documentation states:
When a signal is emitted, the slots connected to it are usually
executed immediately, just like a normal function call. When this
happens, the signals and slots mechanism is totally independent of any
GUI event loop.
Question:
How to ensure the slots are executed "immediately" (not only after the loop in the main finishes) when the signal is emitted?
The only available solution (without introducing new threads) i currently see:
Drop the use of signals and slots in this case, and implement everything in the start method. Something like this:
Socket::start(){
...
if(!tcpsocket->waitForConnected(200)) qDebug() << "Socket object X TcpSocket Connected"
...
}
Your slot is indeed triggered immediately when QTcpSocket's signal connected() is emitted.
However, connected() is not emitted the moment you try to connect that socket to somewhere.
The documentation writes:
This signal is emitted after connectToHost() has been called
and a connection has been successfully established.
The establish of a connection requires an event loop.
establishing the connection happens asynchronously (read connectToHost will return immediately before it even checks whether the connection has already been established) and will notify your code using the signals that are triggered by events
these events are handled only in the event loop or when you call WaitForConnect (which will spin up it's own even loop only handling those events)
this means that the sequence you get is perfectly normal
I don't think you can do that without introducing new threads, only solution is seems your solution.
Or using DirectConnection instead of leaving it empty (Which is AutomaticConnection and which is QueuedConnection in your case) may be a solution. But I don't think that it will work because you need to wait in order to run that slot. I'm not sure, just give it a try.

Qt,how does ProcessState enum works

I don't under stand how to use the ProcessState enums. According to documentation, the ProcessState enum can have the following values:
QProcess::NotRunning- 0 - The process is not running.
QProcess::Starting- -1-The process is starting, but the program has not yet been invoked.
QProcess::Running -2 -The process is running and is ready for reading and writing.
How would I use them?
What you refer to are not functions, simply values. You could assign them to an integer and output its value:
int val = QProcess::Starting;
qDebug() << "the value of QProcess::Starting is" << val;
To check the state of a process, you could do:
QProcess *process;
....
if (process->state() == QProcess::Running) // do something with a running process
Of course, when it comes to a QProcess, you really need to be handling signals that the process emits as it changes state. You do not want to do any sort of busy-waiting, and I should discourage the use of any Qt function called waitFor.... Those functions cause the event loop to be re-entered, and potentially to re-enter your code that you never realized could be re-entered. It's a Pandora's box you do not want to open. About the only valid use of wait-style functions is to wait for QThreads that have been quit() to finish before you return from the main() function.
You can have states for the processes to be run. You can then connect your slot to the state changed signal, even in QML if needed, and act accordingly. Also, not that there is no such a thing as "enum function". It is just a simple enumeration that basically the state "property" holds. You can query and set it the usual way. You can see the documentation for those methods below.
http://qt-project.org/doc/qt-5.0/qtcore/qprocess.html#state
http://qt-project.org/doc/qt-5.0/qtcore/qprocess.html#setProcessState
This looks like a generic Qt examples as your question is, but here you go:
myclass.h
class MyClass : QObject
{
Q_OBJECT
public:
explicit MyClass(QObject *parent);
public Q_SLOTS:
void handleProcessStateChanged(QProcess::ProcessState newProcessState);
....
}
myclass.cpp
...
MyClass::MyClass(QObject *parent)
: QObject(parent)
{
}
MyClass::myProcessInvokeMethod()
{
connect(myprocess, SIGNAL(stateChanged(QProcess::ProcessState), this, SLOT(handleStateChange(QProcess::ProcessState)));
myprocess.start(myprogram, myarguments);
....
}
void MyClass::handleProcessStateChange(QProcess::ProcessState newProcessState)
{
switch (newProcessState) {
case QProcess::NotRunning:
qDebug() << "Here goes the handler code when the process is not yet running";
break;
case QProcess::Starting:
qDebug() << "Here goes the handler code when the process is starting";
break;
case QProcess::Running:
qDebug() << "Here goes the handler code when the process is running";
break;
}
}
...

Resources