Qt: setting an override cursor from a non-GUI thread - qt

A while ago I wrote a little RAII class to wrap the setOverrideCursor() and restoreOverrideCursor() methods on QApplication. Constructing this class would set the cursor and the destructor would restore it. Since the override cursor is a stack, this worked quite well, as in:
{
CursorSentry sentry;
// code that takes some time to process
}
Later on, I found that in some cases, the processing code would sometimes take a perceptible time to process (say more than half a second) and other times it would be near instantaneous (because of caching). It is difficult to determine before hand which case will happen, so it still always sets the wait cursor by making a CursorSentry object. But this could cause an unpleasant "flicker" where the cursor would quickly turn from the wait cursor to the normal cursor.
So I thought I'd be smart and I added a separate thread to manage the cursor override. Now, when a CursorSentry is made, it puts in a request to the cursor thread to go to the wait state. When it is destroyed it tells the thread to return to the normal state. If the CursorSentry lives longer than some amount of time (50 milliseconds), then the cursor change is processed and the override cursor is set. Otherwise, the change request is discarded.
The problem is, the cursor thread can't technically change the cursor because it's not the GUI thread. In most cases, it does happen to work, but sometimes, if I'm really unlucky, the call to change the cursor happens when the GUI thread gets mixed in with some other X11 calls, and the whole application gets deadlocked. This usually only happens if the GUI thread finishes processing at nearly the exact moment the cursor thread decides to set the override cursor.
So, does anyone know of a safe way to set the override cursor from a non-GUI thread. Keep in mind that most of the time, the GUI thread is going to be busy processing stuff (that's why the wait cursor is needed after all), so I can't just put an event into the GUI thread queue, because it won't be processed until its too late. Also, it is impractical to move the processing I'm talking about to a separate thread, because this is happening during a paint event and it needs to do GUI work when its done (figuring out what to draw).
Any other ideas for adding a delay to setting the override cursor would be good, too.

I don't think there is any other way besides a Signal-Slot connection going to the GUI thread followed by a qApp->processEvents() call, but like you said, this would probably not work well when the GUI thread is tied up.
The documentation for QCoreApplication::processEvents also has some recommended usages with long event processing:
This function overloads processEvents(). Processes pending events for
the calling thread for maxtime milliseconds or until there are no more
events to process, whichever is shorter.
You can call this function
occasionally when you program is busy doing a long operation (e.g.
copying a file).
Calling this function processes events only for the
calling thread.
If possible break up the long calls in the paint event and have it periodically check to see how long it has been taking. And in any of those checks, have it set the override cursor then from in the GUI Thread.
Often a QProgressBar can go a long way to convey the same information to the user.
Another option that could help quite a bit would be to render outside of the GUI thread onto a QImage buffer and then post it to the GUI when it is done.

Related

Can I delay/bundle reactions to QPlainTextEditor.textChanged events?

I have a small IDE for a modeling language I wrote, implemented in PyQt/PySide, and am trying to implement a code navigator that let's you jump to different sections in the file being edited.
The current implementation is: (1) connect to QPlainTextEditor.textChanged, (2) any time a change is made, (sloppily) parse the file and update the navigator pane
It seems to work OK, but I'm worried this could cause major performance issues for large files on slower systems, in particular if more stuff is connected to textChanged in the future.
My question: Has anybody here implemented a delayed reaction to events, so that multiple events (i.e. keystrokes) within a short period only trigger a single update (say once per second)? And is there a proper QT way of doing this?
Thanks,
Michael
You can try using timers if you want some "delay".
There would be 2 ways to use them (with different results).
One is only parse after no input has been done for a certain amount of time
NOTE: I only know C++ Qt but I assume the same things are valid for pyqt so this is kind of "pseudocode" I hope you get the concept though.
QTimer timer; //somewhere
timer.setSingleShot(true); //only fire once
connect(timer,QTimer::timeout(),OnTimerDone(...);
OnTextChanged(...)
{
timer.start(500); //wait 500ms
}
OnTimerDone(...)
{
DoStuff(...);
}
This will restart the timer every input, so when you call that and the timer is not done the timeout signal is not emitted. When no input is done for an amount of time the timer timeouts and you parse the file.
The second option would be to have a periodic timer running (singleShot(false)).
Just start the timer for like each second. and timeout will be called once a second. You can combine that with a variable which you set to true when the input changes and to false when the file is parsed. So you avoid parsing when nothing has changed.
In C++Qt you won't have to worry about multi-threading because the slot gets called in the GUI thread. I assume it is the same for python but you should probably check this.

QTcpSocket readyRead() signal emits multiple times

I'm new to Qt and currently learning to code with QTcpServer and QTcpSocket.
My code to process data is like
Myclass()
{
connect(&socket, &QTcpSocket::readyRead, this, &MyClass::processData);
}
void MyClass::processData()
{
/* Process the data which may be time-consuming */
}
Is it the correct way to use the signal like that? As I'm reading the documentation that in the same thread the slot is invoked immediately, which means if my processing work hasn't finished and new data comes, Qt will pause on the current work and enter the processData() again. That is not exactly what I want to do, So should I QueueConnection in the signal/slot connection?
Or could you please provide some good methods that I should adopt in this case?
Qt will not pause your current work when data comes in, it will call processData() only when the event loop is free and waiting for new events.
so, when your application is busy executing your code, the application will appear to be unresponsive, because it can't respond to external events, so processData() won't get called if some data is received on the socket until the current function (that may contain your heavy code) returns, and the control is back in the event loop, that has to process the queued events (these events may contain the received data on the socket, or the user clicks on some QPushButton, etc) .
in short, that's why you always have to make your code as short and optimized as possible, in order not to block the event loop for a long time.
With the event delivery stuck, widgets won't update themselves (QPaintEvent objects will sit in the queue), no further interaction with widgets is possible (for the same reason), timers won't fire and networking communications will slow down and stop. Moreover, many window managers will detect that your application is not handling events any more and tell the user that your application isn't responding. That's why is so important to quickly react to events and return to the event loop as soon as possible!
see https://wiki.qt.io/Threads_Events_QObjects

QT progress dialog cancel button not highlighted

I have an application which makes use of 20 different classes. The program execution starts in mainwindow. I start a progress dialog. I will make use of different classes for different purposes. After each function call which the execution goes to the particular class and does the required and come back to the mainwindow class, I will update the progress dialog. Now, the issue is the progress dialog freezes when the execution goes away from the mainwindow class. The cancel button is unable to accessed and so, the execution could not be stopped at the required time.
mainclass::mainclass()
{
ProgressDialog->exec();
x->add();
updateProgressDialog();
y->do();
updateProgressDialog();
zz->bring();
updateProgressDialog();
}
// x, y, z are three different classes.
This is how the execution goes. As soon as I enter the function in the main class, I will start the progress dialog. and call functions from different classes. The functions take considerable amount of time. I have invoked a thread to do the execution part, but I am unable to cancel the progress diaolog. I want the program execution to be stopped as and when the cancel button is pressed on the proggress dialog.
Please let me know how to get away with this issue. Hope I am clear here.
Without knowing exactly what calculations are being preformed in your threads its hard to isolate the problem. Maybe this can help: Keeping the GUI Responsive
Excerpt from: Performing Long Operations (by: Witold Wysota)
During long calculations (regardless of any usage of signals and slots) all event processing gets halted. As a result, the GUI is not refreshed, user input is not processed, network activity stops and timers don't fireā€”the application looks like it's frozen and, in fact, the part of it not related to the time-intensive task is frozen.
The functions you are calling are not processing the Qt events loop. You are using a modal progress bar, since you are calling exec(). This means that Qt only gets control at the times where you update the dialog.
The only way that I know of to work around this is to code the dialog as modeless, but you will also have to provide an opportunity for the events loop to process.
This is explained in a fair amount of detail in the Qt docs: QProgressDialog

QNetworkAccessManager handling asynchronous thread

I am new to QT. I have created object class QNetworkAccessManager main window as parent. Also registered to SIGNAL finished. It is working fine.
But I want to know in which thread it will run. Will it block the main thread. If i need to perform sequence of get operation how should I need to write the code.
Please give me some sample to understand concept properly.
It certainly does not run in the main thread, the calls to get() are asynchronous.
For example this would keep firing get requests:
while (condition) {
QNetworkRequest request;
request.setUrl(QUrl(m_ServerURL);
m_httpGetUpdatedFile->get(request);
}
You then have the slot for the finished signal which is processing the QNetworkReply. That slot basically should be getting called for each get request you make (even if it fails). If you need to keep track of when all your get requests have finished, you need to keep a track of how many you posted and then have your own finished flag or signal.
QNAM does use threads in the background, but this is completely invisible for your application code. Everything you see will run in the main thread.
QNAM works in the usual Qt way, it will emit signals when things happen, and you connect these signals to slots in your own code, which do things as much as they can. If they don't for example have enough data, then your slots must not block to wait for new data, instead they must return. And then they will be called again when/if there's more data (or you'll get another signal if for example connection was terminated).
Some links, in case you have not read these:
Qt signal & slot documentation
Qt networking documentation

Can I use QwaitCondition.wait() in a slot called by the main thread?

if the maximum wait time is 10 ms can i use qwaitcondition in Qt's main thread?
Nothing stops you from using QWaitCondition in the main thread. If you are setting the wait time to 10ms, and it passes without unlocking you will probably not get the desired effects you want. The default is to wait indefinitely.
However, using a wait condition in the main thread will cause the GUI to become unresponsive while it waits. This is almost always undesired.

Resources