Environment: Ubuntu, Qt Creator
In my Qt app, I found that sometimes Qt doesn't respond to my key press event immediately, but if I wait a while, it eventually responds.
I think something is blocking the UI.
As I know, if a Qt's component (QWidget etc.) is being destroyed, the Qt UI will be blocked. I have checked my code, there is no component being destroyed at the time I'm pressing the up/down key.
I really want to know is there any other things can block Qt UI.
{
...
connect(webViewWidget, SIGNAL(loadfinished()), this, SLOT(addItem()));
...
}
void addItem()
{
delete webViewWidget; // will this delete block UI?
mListWidget = new ScrollWidget();
mScrollArea = new ScrollArea(this);
for(int i=0; i<Datalen; i++)
{
mListWidget->addSubItem(itemWidget);
}
}
void keyPressEvent(QKeyEvent *event)
{
switch(event->key)
{
case UP_KEY:
scroll up;
break;
case DOWN_KEY:
scroll down;
break;
default:
break;
}
}
In general, your key press event will not be processed before all other events which were put into the application's event queue before pressing your key are processed.
Therefore it could be any kind of event which has not finished processing. Maybe you can figure out if there are any events, e.g. by using QApplication::hasPendingEvents or by inheriting from QApplication and adding debug output whenever an event is added or fully processed.
Destruction of objects is usually not a concern, unless you are doing a lot of work in the destructor. Destroying a webview may take long. You probably should not be destroying it like you do. Instrument that delete (see code below) and see how long it takes.
Your own code may be calling APIs that block. Are you calling any third party libraries? Are you calling any wait... methods in Qt's own API?
If you're unsure, you can instrument every slot and every reimplemented virtual method like xxxEvent(...). You'd need to instrument only slots and reimplemented QObject/QWidget methods, not every method in your code.
You may be producing an event storm, perhaps by posting lots of events in a loop, or by sending a lot of signals that are hooked up to slots connected via a Qt::QueuedConnection. Make sure you're not calling repaint() from within paintEvent() for example.
The instrumentation example below uses RAII and is very easy to apply. Alternatively, you can use a profiler.
#include <QElapsedTimer>
#define INSTRUMENT() Instrument instr__ument(__FUNCTION__)
#define INSTRUMENTLIM(lim) Instrument instr__ument(__FUNCTION__, (lim))
class Instrument {
QElapsedTimer timer;
int limit;
const char * function;
public:
Instrument(const char * name, int timeLimitMs = 20) :
function(name), limit(timeLimitMs) { timer.start(); }
~Instrument() {
if (timer.elapsed() > limit) {
qDebug("%s was slow, took %d ms", function, timer.elapsed());
}
}
}
void slot(...)
{
INSTRUMENT();
...
}
void addItem()
{
INSTRUMENT();
delete webViewWidget; // will this delete block UI?
mListWidget = new ScrollWidget();
mScrollArea = new ScrollArea(this);
for(int i=0; i<Datalen; i++)
{
mListWidget->addSubItem(itemWidget);
}
}
Related
I found time to investigate a bit into QT, and it is very interesting for me. However, right now I am encountering a problem that I am not aware about how to solve it. My aim is actually simple. I have a QCheckBox that I want to activate. If it is activated, I am starting a process (I am opening a file, reading it, taking some values out and change different labels accordingly). This process is repeated until the user is deactivating the QCheckBox. Some small code example to get a better idea of what I am going to do.
void Analyzer::on_actualTemperature_stateChanged(int arg1)
{
// Read data and change labels
if (arg1 != 0)
{
qDebug() << "Start data analysis";
// Infinity loop to get the data and display it
while true
{
// Open file and extract data
const actualTemperature = getData();
// Change any label or do something with the data
ui->anyLabel->setText(actualTemperature);
// Some break
QThread::sleep(1);
// Leave the loop if user deactivate the QCheckBox
// Something like on_actualTemperature_stateChange == 0
}
}
// Stop reading the data
else
{
qDebug() << "Stop data analysis";
}
}
It is obvious that after activating the QCheckBox, the loop will not finish at all and the GUI will not recognize anything anymore. Hence, I guess I have to start some new thread and have to kill it. However, I have no idea how to proceed here. An idea would be:
void Analyzer::on_actualTemperature_stateChanged(int arg1)
{
// Read data and change labels
if (arg1 != 0)
{
// Start reading the file and updating the label using some other thread
startThread(XY);
}
// Stop reading the data
else
{
// Kill thread 1234
killThread(XY);
}
}
Any hint is warmly welcomed and I hope this question is not too basic for you. Thank you for reading, Tobi.
I think killing a running thread is not a decent behavior. Let's be gentle to our threads with a loop control variable. In this example it named keepLoop. Set keepLoop when checkbox checked. Then start thread if it is not running. We are using QtConcurrent::run, and monitoring it by a QFuture in this case.
connect(ui->checkBox, &QCheckBox::toggled,
[&](const bool checked) {
analyzer->keepLoop = checked;
if (checked && !future.isRunning())
future = QtConcurrent::run(analyzer, &Analyzer::on_actualTemperature_stateChanged);
}
);
Don't call user interface slots directly, instead connect them to signals. Connections will be queued connection when signals emitted from another thread. It means slots will be called in event loop of main thread and changes will be shown when the next frame painted.
connect(analyzer, &Analyzer::temperatureCalculated, ui->anyLabel, &QLabel::setText);
Our asynchronous function does not forced to die immediately when user toggle checkbox. Instead we letting it to finish the iteration it already on halfway through.
Analyzer::on_actualTemperature_stateChanged() {
while (keepLoop) {
// Open file and extract data
const QString& actualTemperature = getData();
// send data
emit temperatureCalculated(actualTemperature);
}
}
You can use atomic bool if you want a more precise loop control.
Bonus:
If you don't want to mess with threads, you can avoid GUI freezing by using QTimer to run your loop periodically in main thread.
In QML you can use Animator type to "animate on the scene graph's rendering thread even when the UI thread is blocked."
How can I achieve the same thing for Qt Widgets?
Basically, I want something like:
1) start loading screen / splash-screen
2) start GUI-blocking operation
3) stop loading screen / splash-screen
It is not possible to move the ui-blocking operation to a separate thread (Widgets are being created). I cannot modify this part.
I tried QQuickWidget and QQuickView containing the splash-screen scene with an Animator inside but it didn't work - they got blocked as well.
EDIT: In the separate thread I read the file containing the UI description. Then I recursively create hundreds of Widgets (including QQuickWidgets, QLabels for images, Web views etc.).
Point of the question was to see if there is a "workaround" for that (e.g. displaying the aforementioned QML scene in some separate window with an own event loop). Unfortunately at this point not much more can be done about the overall design of the above.
Probably the widgets you're creating do too much work. You have to specify exactly how many widgets you're creating, and how. Show some example code. In general, the GUI thread is for cooperative multitasking - if you have something that "blocks", break it down into tiny chunks. For example, suppose that you're processing some XML or json file to build the UI. You could have that task do it one widget at a time, and be invoked each time the event loop is about to block (i.e. use a zero-duration "timer" and invert control).
You should also do the maximum possible amount of work outside of the gui thread. I.e. the UI description should be read and converted to an efficient representation that encapsulates the work to be done in the main thread. This conversion has to be done asynchronously.
The simplest way to accomplish that is to encapsulate each widget's creation in a lambda that refers to some context object. Such a lambda would have the signature [...](BatchContext &ctx). The vector of those lambdas would be kept by the CreationContext object as well:
class BatchContext : public QObject {
Q_OBJECT
public:
using Op = std::function<void(CreationContext &)>;
using QObject::QObject;
// useful for Op to keep track of where things go
void push(QWidget *w) { m_stack.push_back(w); }
QWidget *pop() { return m_stack.isEmpty() ? nullptr : m_stack.takeLast(); }
QWidget *top() const { return m_stack.isEmpty() ? nullptr : m_stack.last(); }
int stackSize() const { return m_stack.size(); }
bool stackEmpty() const { return m_stack.isEmpty(); }
Q_SLOT void startExec() {
if (m_execIndex < ops.size())
m_execTimer.start(0, this);
}
template <typename F>
void addOp(F &&op) { m_ops.push_back(std::forward<F>(op)); }
...
private:
QVector<Op> m_ops;
QVector<QWidget *> m_stack;
QBasicTimer m_execTimer;
int m_execIndex = 0;
void timerEvent(QTimerEvent *ev) override {
if (ev->timerId() == m_execTimer.timerId())
if (!exec())
m_execTimer.stop();
}
/// Does a unit of work, returns true if more work is available
bool exec() {
if (m_execIndex < m_ops.size())
m_ops.at(m_execIndex++)(*this);
return m_execIndex < ops.size();
}
};
After the context is created asynchronously, it can be passed to the main thread where you can then invoke startExec() and the widgets will be created one-at-a-time. The stack is but an example of how you might implement one aspect of widget creation process - the tracking of what widget is the "current parent".
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
I have the MainWindow w windows and TestThread testThread as a member of w. I know it i simple, but I cannot run the testThread.foo() method in testThread thread (not in window thread). In another words: I don't understand the QThread behavior.
Please help correct the next test application. There is a QProgressBar *MainWindow::ui::progressBar and QPushButton *MainWindow::ui::startButton (write simply). I want to start (by startButton click) TestThread::foo(int* progress) which will increment int progress each second.
MainWindow:
MainWindow::MainWindow(QWidget *parent) : // ...
{
// ...
ui->progressBar->setRange(0, 5);
progress = 0; // int MainWindow::progress
this->connect(ui->startButton, SIGNAL(clicked()), SLOT(startFoo()));
connect(this, SIGNAL(startFooSignal(int*)), &testThread, SLOT(foo(int*)));
// TestThread MainWindow::testThread
testThread.start();
}
// ...
void MainWindow::timerEvent(QTimerEvent *event)
{
ui->progressBar->setValue(progress);
}
void MainWindow::startFoo() // this is a MainWindow SLOT
{
startTimer(100);
emit startFooSignal(&progress);
// startFooSignal(int*) is a MainWindows SIGNAL
}
TestThread:
void TestThread::foo(int *progress) // this is a TestThread SLOT
{
for (unsigned i = 0; i < 5; ++i) {
sleep(1);
++*progress; // increment MainWindow::progress
}
}
I know, this is simple. I am doing something wrong :)
P.S. I want to run the simpliest (as possible) example to understand the QThread behavior.
Thanks!
The critical issue is to have the object containing the foo()-function be owned by that thread, so that slot calls are dispatched from the right thread's event-loop.
(Note that there's no need to actually have foo() on the TestThread object. You can use separate objects for QThread and WhatEver::foo() function. It might be easier too, I'm not sure..)
IIUC, this is what you have to do:
Use QObject::moveToThread() to assign the object containing the foo-function to TestThread (that means that Qt::AutoConenction (the default) signal/slots calls will run correctly across thread, being dispatched from each thread's own event loop).
By having the object "owned" by the right thread, slots calls will be scheduled on that thread's event loop, rather than executed directly.
Hope it helps. :)
One alternative solution: If you just want to run a function in another thread, and don't insist using QThread, you should check out the QT Concurrent Namespace.
The following example will run the function foo() in separate thread and will not block on the line where calling the function. Of course there are mechanisms to understand when a function ends, to get a result, to wait for it, to control execution.
void foo(int &progress) {...}
int progress;
QtConcurrent::run(foo, progress);
Hope this helps
See QThread::start and QThread::run.
Am I doing it right?
A client of mine has a group where I'm developing Qt-based client-server stuff with a lot of fun widget stuff and sockets.
Another group within the company wants to use a wrapped version of the QTcpSocket-based client data provider classes. (Which does basically what it sounds like, provides data from the server to the client displays)
However, that group has a huge application built mostly with MFC, and that is simply not going to change any time soon. The Qt-based DLL is also delay-loading so that it can be deployed without this feature in certain configurations.
I've got it working, but it's a little hacky. Here's my solution at the moment:
The DLL wrapper class constructor calls QCoreApplication::instance() to see if it's NULL or not. If it's NULL, it assumes it's in a non-Qt app, and creates a QCoreApplication instance of it's own:
if (QCoreApplication::instance() == NULL)
{
int argc = 1;
char* argv[] = { "dummy.exe", NULL };
d->_app = new QCoreApplication(argc, argv); // safe?
}
else
d->_app = NULL;
It then will set up a windows timer to occasionally call processEvents():
if (eventTimerInterval > 0)
{
// STATE: start a timer to occasionally process the Qt events in the event queue
SetTimer(NULL, (UINT_PTR)this, eventTimerInterval, CDatabaseLayer_TimerCallback);
}
The callback simply calls the processEvents() function using the timerID as a pointer to the class instance. The SetTimer() docs say when HWND is NULL it ignores the timerID, so this appears to be perfectly valid.
VOID CALLBACK BLAHBLAH_TimerCallback(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
{
((BLAHBLAH*)idEvent)->processEvents(); // basically just calls d->_app->processEvents();
}
I then destroy the QCoreApplication instance as the very last thing in the destructor.
BLAHBLAH::~BLAHBLAH()
{
.. other stuff
QCoreApplication* app = d->_app;
d->_app = NULL;
delete d;
if (app != NULL)
delete app;
}
If the hosting application wishes to time the calls to processEvents() itself, it can pass 0 in for eventTimerInterval and call BLAHBLAH::processEvents() itself.
Any thoughts on this? Porting that app to Qt is not an option. It's not ours.
It appears to work, but there are probably several assumptions being broken here. Can I just construct a QCoreApplication with dummy arguments like that? Is the event queue safe to operate in this manner?
I don't want this blowing up in my face later. Thoughts?
Studying the Qt code it seems QCoreApplication is needed to dispatch system-wide messages such as timer events. Things like signal/slots and even QThreads do not depend on it unless they are related to those system-wide messages. Here is how I do this in a shared library (in a cross platform way using Qt itself) and I actually do call exec, because processEvents() alone does not process everything.
I have a global namespace:
// Private Qt application
namespace QAppPriv
{
static int argc = 1;
static char * argv[] = {"sharedlib.app", NULL};
static QCoreApplication * pApp = NULL;
static QThread * pThread = NULL;
};
I have an OpenApp method in a QObject (that is moc'ed) like this:
// Initialize the app
if (QAppPriv::pThread == NULL)
{
// Separate thread for application thread
QAppPriv::pThread = new QThread();
// Direct connection is mandatory
connect(QAppPriv::pThread, SIGNAL(started()), this, SLOT(OnExec()), Qt::DirectConnection);
QAppPriv::pThread->start();
}
And here is OnExec slot:
if (QCoreApplication::instance() == NULL)
{
QAppPriv::pApp = new QCoreApplication(QAppPriv::argc, QAppPriv::argv);
QAppPriv::pApp->exec();
if (QAppPriv::pApp)
delete QAppPriv::pApp;
}
So far it seems to be working fine, I am not sure if I need to delete the app at the end, I will update my answer if I find something.
The Qt Documentation for 4.5.2 says that the arguments to QCoreApplication need to have lifetimes as long as the application object - so you shouldn't really use local variables.
Apart from that little thing:
I'm grappling with the same problem, and everything seems to work for me too. I would recommend being very careful at unload / exit time, however, since if you're using the event loop from another application and that event loop is stopped before your library is unloaded then all sorts of crashy nastiness can happen when you try to close() sockets and delete QObjects.