Qt on Mac and QWidget's closeEvent - qt

Silly question, but can I know at the time of the window's "CloseEvent" event if the window is being closed because it received a cmd-q keystroke, or a cmd-w keystroke?

If you need to understand how an user has closed your application you can install filter on QApplication and check every keystrokes here. If you encounter one of those you can do something: log it, send signal, set global flag etc. With global flag you can know for sure in close event if some keystroke has been used.

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.

How to emit a delayed signal in PyQt GUI?

In my PyQt GUI app, there are a lot of spinBoxes that trigger a lengthy setup routine when their values are changed.
My problem is this:
When typing in (large) numbers the spinBox.valueChanged() signal is emitted every time I input a single digit. That leads to the setup function being called many more times than necessary.
Is there a way to delay the trigger until I'm done typing the number and then fire the signal only once?
How do you usually take care of that issue in your GUIs?
I found this but I think it would involve creating an extra timer for every unique function I want to call upon emitting. That doesn't seem very elegant.
For spinboxes there is the keyboardTracking property.
If keyboard tracking is disabled, the spinbox doesn't emit the valueChanged() signal while typing. It emits the signal later, when the return key is pressed, when keyboard focus is lost, or when other spinbox functionality is used, e.g. pressing an arrow key.
I believe this solves your issue.
You can also connect to the editingFinished signal, which isn't emitted until the user presses enter or the spinbox loses focus. However, you'll get this signal even if the value doesn't change, so you may want to check it against the last value to prevent having to run the lengthy routine unnecessarily.

Using QNetworkAccessManager::authenticationRequired with own input widget / asynchronously

I'm currently developing a browser with Qt which has a vim-like input bar:
This is basically just a QHBoxLayout with a QLineEdit and some QLabels in it.
Now I'd like to handle HTTP authentication. The usual thing I see in other projects is opening a modal QDialog and then calling exec() on it inside the slot connected to the authenticationRequired signal.
Now I'd like to use the same statusbar to ask the user for authentication information, i.e. displaying some prompt and then using the QLineEdit to enter the information.
My problem is the authenticationRequired slot blocks, I can't simply continue to run in the mainloop and continue the request with authentication information added when the user is done.
I've thought about two solutions:
Implementing some function which gets the values from the statusbar while calling QCoreApplication::processEvents while there's no reply from the user yet. However I'm not sure if that's a good idea, and if the application will hog a lot of CPU until I'm back to the real eventloop.
Somehow saving and aborting the request, asking the user for authentication, and then re-creating the request as soon as the authentication information is added. But it seems I can't simply clone a QNetworkReply and then call abort() on the original reply and resume it later.
Looking at how QDialog::exec() is implemented it seems they create a new QEventLoop with an undocumented value of QEventLoop::DialogExec passed. I wonder if I could do the same, but then I'm not sure how I'd quit the event loop once the user input is there.
Which of these ideas sounds like the most sane one?
You can just use a QEventLoop without any special undocumented values. Instead you'll have something like:
QEventLoop loop;
connect(editBox, SIGNAL(finishedEditing()), &loop, SLOT(quit()));
loop.exec();
And that will start a new event loop that blocks, waiting for your input (without hogging as much cpu as processEvents)

How to detect if QDialog.exec() is active

Is there a way to detect whether execution is currently in the middle of QDialog.exec()?
I'm the author of DreamPie, a Python shell, and it lets you run Python code while Qt GUI is being displayed. It does that by running the Qt event loop for 1/10 of a second, and then checking if any Python commands need to be executed. The event loop is stopped by a QTimer which calls QApplication.quit() after the timeout.
If a QDialog.exec() is active, however, I don't want to call QApplication.quit(), because it will break the code. The current solution is to check whether there's a modal dialog active, by checking if QApplication.activeModalWidget() is None. However, I currently have a modal dialog which is not run with QDialog.exec(), and it's blocking Python commands for no reason.
Is there a way to exit the event loop only if it's not called recursively by QDialog.exec()?
Thanks!
You can check whether your dialog is visible with QDialog.isVisible. Normally, a modal dialog is visible only while it is being executed.

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

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.

Resources