I have a QWidget and i need to do some actions (refresh a picture in widget) when resize event ends. How can i catch this action? I need to catch moment when user ENDs all his r
I've do it in this way:
void MapLoader::resizeEvent(QResizeEvent *){
if (timerId){
killTimer(timerId);
timerId = 0;
}
timerId = startTimer(5000/*delay beetween ends of resize and your action*/);
}
void MapLoader::timerEvent(QTimerEvent *te){
/*your actions here*/
killTimer(te->timerId());
timerId = 0;
}
My Qt app uses image windows and does complex layered rebuilds, which can take some time even on a very fast machine. So not having the window redraw with every change in the window frame size was important to me so the response to the window frame resize would not be laggy.
So I solved it this way:
In my image window, I have enabled mouse tracking:
setMouseTracking(true);
Then, in the window class, I have a boolean, puntme
; this is set when a resize event is caught:
bool puntme;
Then, in the mousemove event:
void imgWindow::mouseMoveEvent(QMouseEvent* event) {
if (puntme)
{
puntme = false;
needRebuild = true;
update();
}
...
Basically, what this does is as soon as the user moves the mouse over the window -- which is a pretty natural thing for them to do if they were just resizing it -- then the window redraws with the new size. It doesn't happen during the resize, because Qt isn't forwarding move moves then.
Instead, during the resize, I just scale up an already existing bitmap, which gives a crude approximation of the change in scale with necessarily handling the actual newly more-or-less available resolution.
Worst case, user resizes, moves away from the window, and leaves the crudely scaled bitmap in place until the come back to it, at which point it will duly update to the actual new displayed bitmap == scale/size conditions.
There's no perfect way - what is really needed here is for Qt to provide (user has stopped resizing window" message, but in lieu of that, this has been working well for me.
Another way would be to install an event filter for the app and get all the events of the application, trap mouse press and mouse release and do not update the window in between .
"Installing an event filter on QCoreApplication::instance(). Such an event filter is able to process all events for all widgets, so it's just as powerful as reimplementing notify(); furthermore, it's possible to have more than one application-global event filter. Global event filters even see mouse events for disabled widgets. Note that application event filters are only called for objects that live in the main thread."
Mouse events on windows decoration are managed by the underlying window system, this is why you can't catch them as you tried. I had the same issue once, the solution I chose was to (re)start a singleshot QTimer on each resize event, and only process the update after the timer interval elapsed. Not very sexy but I did not find any other workaround..
The timeout method is a decent idea, but if the user is resizing and then pauses for longer than the timer's interval then you end up not getting the true "user is done resizing the window" event. Setting the interval longer makes that situation less likely, but by doing that, you end up having a long delay between the time the user finished resizing and the time your function gets called. In my search for a solution, I found quite a few people solving it using the timer method, so apparently it's reliable enough for some use cases, but I think it's a bit hacky.
I like mhstnsc's idea, so after implementing it, I decided to add some code here that might be of use to someone trying to do something similar. You could easily adapt it to catch the "user is done moving the window" event by making a m_bUserIsMoving flag and overriding "void MainWindow::moveEvent(QMoveEvent* pEvent)". I'm using it to save a config file whenever the user finishes resizing or moving the window, so that the last position will always be saved even if the app is killed in an unclean manner.
// constructor
MainWindow::MainWindow(QWidget* pParent, Qt::WindowFlags flags) : QMainWindow(pParent, flags)
{
m_bUserIsResizing = false;
qApp->installEventFilter(this);
}
// this will be called when any event in the application occurs
bool MainWindow::eventFilter(QObject* pObj, QEvent* pEvent)
{
// We need to check for both types of mouse release, because it can vary on which type happens when resizing.
if ((pEvent->type() == QEvent::MouseButtonRelease) || (pEvent->type() == QEvent::NonClientAreaMouseButtonRelease)) {
QMouseEvent* pMouseEvent = dynamic_cast<QMouseEvent*>(pEvent);
if ((pMouseEvent->button() == Qt::MouseButton::LeftButton) && m_bUserIsResizing) {
printf("Gotcha!\n");
m_bUserIsResizing = false; // reset user resizing flag
}
}
return QObject::eventFilter(pObj, pEvent); // pass it on without eating it
}
// override from QWidget that triggers whenever the user resizes the window
void MainWindow::resizeEvent(QResizeEvent* pEvent) { m_bUserIsResizing = true; }
It's slightly more complicated than the timer, but more robust.