Detecting widget resize done by mouse
-
I'm looking for a way to distinguish when a QMdiSubWindow is resized by code and when it's user doing it by mouse. Is there a way?
@Black-Imp
I would say "no", but it will be interesting to see what any experts say :)Your only hope might be https://doc.qt.io/qt-5/qevent.html#details:
In general, events come from the underlying window system (spontaneous() returns true), but it is also possible to manually send events using QCoreApplication::sendEvent() and QCoreApplication::postEvent() (spontaneous() returns false).
but I'm not at all sure that will distinguish your case, worth a test.
Otherwise, I'm not saying it's nice but you might have to track mouse downs or similar so that you know. I hope someone has a better way than that :)
-
Hi,
Out of curiosity, why do you need to make that distinction ?
-
It's a common requirement among desktop applications:
user creates subwindows, manages their layouts and saves a layout file.
main class needs to know when user intentionally reisizes any of them.
I put an emit signal in every callback or function esplicitally called by my code responding user interactions which is connected to main class where I show user that current layout file has been modified - you know any application like word, photoshop or whatever add an asterisk to the title showing user original file has been modified - then if user closes the application, main class if any modification has been made ask user if he/she wants to save -
What happens instead is as soon as I open my application and from main window constructor I open layout file and make subwindows and resize them basing on saved data, resize callback is called a couple of times per subwindow and this triggers back signals intended to be triggered when user modifies manually any subwindow size, and layout file results modified thou it's not.The problem is that there's no callback to rely to for knowing, in main class - which is a QMainWindow - when children are actually displayed and sized . My idea was to connect signal slot only after everything has been displayed and no resize is called anymore. In some other platform you can add an observer or you have a callback you can rely. So far i have found none. I tracked all events arriving to main window but there's no one which occur AFTER the last subwindow resize.
My last chance is to distinguish between user resizing and resizing made by code and then to initialize some flag which I use to decide whether to trigger or not
-
In that case, why not just block the signals when loading your layout file ?
There's QSignalBlocker for that kind of cases.
-
Probably I didn't explain myself enough clearly. the problem is not blocking/unblocking the signal - or when connecting the signal - but having an event which tells me WHEN doing it. there's nothing which assures me that from that moment no resize will be called again by the system, something that tells me "ok everything has been created, resized and showed.
what I ' found out is that after the main window constructor has ended it's called again a resize for all its children.The invocation of the function which loads layout file where application reads data for building subwindows and sizing them is done when mainwindow constructor is still open on the stack.
For instance:
MainWindow::MainWindow(...)
{
//...
loadLayoutFile(); // this function build subwindows}
-
What system resizing do you have in mind ?
In any case, to differentiate between events from within your application and from the system, you have the QEvent::isSpontaneous method.
-
What system resizing do you have in mind ?
In any case, to differentiate between events from within your application and from the system, you have the QEvent::isSpontaneous method.
-
@SGaist I've just tried. It recognizes all resize events occurring as not spontaneous because they are all originated inside this application
@Black-Imp
Which is what I suspected when I suggested earlier.So if you're looking for a practical, not theoretical, solution, and you can't find anything else, set off a timer when you open the main window and the layout restoration occurs, and treat resize events after that expires as user-input? Ugly? Yes. But if it suffices....
-
@Black-Imp
Which is what I suspected when I suggested earlier.So if you're looking for a practical, not theoretical, solution, and you can't find anything else, set off a timer when you open the main window and the layout restoration occurs, and treat resize events after that expires as user-input? Ugly? Yes. But if it suffices....
-
@JonB thank you i had already considered this idea but.. yes it sounds too much nasty. I'd like to understand how to recognize resize by mouse is going on but I think it's something not accessible
@Black-Imp
Well, I also suggested that if you had to you might monitor mouse downs etc. as the only way of recognising.However, if we both agree that is too flaky and that the
resize
event is simply not going to tell you, then you may say using a timer "sounds too much nasty" but you are limited in your options, aren't you?In a sense it's not that nasty. You are wanting to respond only to user resizing, so coding as "user interaction doesn't start till a second after the main window is shown" is not way off the scale, is it?
BTW, I thought about it: I have not tested it, but since you say you want a signal/event to arrive after the main window has opened/layouts have been restored, if you don't want to use a timer could you call
QCoreApplication::postEvent()
after your layout restoration code and see whether that event does indeed arrive after the lastresize
one? You'd have to test.Then there are other alternatives which come to my mind.
One is: why not accept all resizes as potentially needing to be saved back? It sounds a lot, but if you're using, say,
QSettings
to save the overhead is not too bad. You could improve by buffering changes (by time) a bit in memory before committing them, then if there are several closely-spaced resize events during restoration you would only have to deal with the final state. If the final state is actually the same as the saved layout state, discard changes.Or: on any resize event, don't save size, simply set a flag. Periodically examine the flag: if it's set, get the current geometries of sub-windows and compare against what previously saved. If the result is different from saved, only then save/set your "asterisk" to user.
Or: don't do asterisk for resizes, but do compare & save on window closure/exit. I actually think this is more normal for an app? Although apps do save layout changes, they do not tend to mark them as "asterisk" changes to the user, that only shows if the user changes document content.
Food for thought. It's not what you wanted, but there are several possible ways forward.
-
@Black-Imp
Well, I also suggested that if you had to you might monitor mouse downs etc. as the only way of recognising.However, if we both agree that is too flaky and that the
resize
event is simply not going to tell you, then you may say using a timer "sounds too much nasty" but you are limited in your options, aren't you?In a sense it's not that nasty. You are wanting to respond only to user resizing, so coding as "user interaction doesn't start till a second after the main window is shown" is not way off the scale, is it?
BTW, I thought about it: I have not tested it, but since you say you want a signal/event to arrive after the main window has opened/layouts have been restored, if you don't want to use a timer could you call
QCoreApplication::postEvent()
after your layout restoration code and see whether that event does indeed arrive after the lastresize
one? You'd have to test.Then there are other alternatives which come to my mind.
One is: why not accept all resizes as potentially needing to be saved back? It sounds a lot, but if you're using, say,
QSettings
to save the overhead is not too bad. You could improve by buffering changes (by time) a bit in memory before committing them, then if there are several closely-spaced resize events during restoration you would only have to deal with the final state. If the final state is actually the same as the saved layout state, discard changes.Or: on any resize event, don't save size, simply set a flag. Periodically examine the flag: if it's set, get the current geometries of sub-windows and compare against what previously saved. If the result is different from saved, only then save/set your "asterisk" to user.
Or: don't do asterisk for resizes, but do compare & save on window closure/exit. I actually think this is more normal for an app? Although apps do save layout changes, they do not tend to mark them as "asterisk" changes to the user, that only shows if the user changes document content.
Food for thought. It's not what you wanted, but there are several possible ways forward.
@JonB Solved!
Thank you for your kind reply. It suggested me to check again the moment when the last resize is performed by showing the window. I knew that from inside main window constructor I would never be able to do it because subwindows are resized after constructor ends...
when main window slot is invoked I already check that flag but instead of turning true by some event caught by main window I set it true from mainfile after calling main window show:
in main.cpp:
mainWin.show(); mainWin.detectLayoutModification(true); app.exec();
in main window cpp
ParentWindow::ParentWindow:QMainWIndow(...), mDetectLayoutMods(false) {} void ParentWindow::detectTemplateModification(bool detect) { mDetectLayoutMods = detect; } void ParentWindow::onLayoutModified() // this is a slot connected to subwindows signals { if (!mDetectLayoutMods) { return; } mLayoutModified = true; // used elsewhere // do something to show file has been modified .... }
About your solution: I've had aready considered a timer but there is no way to be sure it works: the time may be too long and user may do some mods as soon as file opens before I can detect it or for some reason time required for loading may be longer than usual - this software is frequently used on notebooks
About asterisks: almost every software shows you somehow if the file you are working on has been modified
thank you all for your time.
-
Hi,
An even better way would be to use a single shot QTimer with a timeout of 0 at the end of your main window constructor.The slot will be called right after the widgets are shown, so the resizing will have already happened.Another possible solution would be to use a single shot QTimer with a timeout of 0 at the end of your main window constructor.
The slot will be called at the first occasion once the event loop has started.
The fact that it's working currently is a bit of luck in the sense that the widgets are show when exec starts its work.
-
Hi,
An even better way would be to use a single shot QTimer with a timeout of 0 at the end of your main window constructor.The slot will be called right after the widgets are shown, so the resizing will have already happened.Another possible solution would be to use a single shot QTimer with a timeout of 0 at the end of your main window constructor.
The slot will be called at the first occasion once the event loop has started.
The fact that it's working currently is a bit of luck in the sense that the widgets are show when exec starts its work.
@SGaist said in Detecting widget resize done by mouse:
The slot will be called right after the widgets are shown, so the resizing will have already happened.
could you explain me better the sequence. from what I've noticed all the subwindows resize actions are performed before app.exec(); and after mainwin.show();
how can you be sure timer triggers right after constructor has ended?
if it triggers after the constructor anyway you are still before mainWin.show() after which resize is called more times.
I really don't understand
-
You have a point (I have edited my previous answer). The technique works well for loading settings when signals and slots are involved. I encourage you to try it for your use case.
As for the sequence of events you have one thing wrong, the show method schedules an event to show the widget however it won't happen until the QApplication event loop starts.
The same goes for the QTimer at the end of the constructor. The timer itself needs an event loop and thus the timeout will only happen after the constructor has ended since the event loop has not yet started.