Qt用户调整大小事件结束

8
我有一个QWidget,并且需要在resize事件结束时执行一些操作(刷新小部件中的图片)。我如何捕捉到这个动作呢?我需要捕捉用户释放鼠标按钮时的所有调整大小操作的时刻。在我的应用程序中,每像素调整大小都刷新图像并不是一个好的做法。它应该仅在鼠标释放和调整大小操作结束时调用。
我试图重新实现QMouseReleaseEvent来捕捉它,但当用户按下小部件边框进行调整大小时,它不起作用。这意味着它在我们的情况下不起作用。
然后我尝试创建自己的QSizeGrip并将其插入到我的小部件底部,但是再次重新实现的QMouseReleaseEvent事件在其中也没有工作。事件在用户释放鼠标时没有生成。我不知道为什么。
有人能帮我解决这个问题吗?
提前感谢您。
5个回答

7
超时方法是个不错的想法,但如果用户调整大小后暂停时间超过计时器间隔,则无法获得真正的“用户完成窗口调整大小”事件。将间隔设置得更长可以减少这种情况的发生,但这样做会导致用户完成调整大小的时间和您的函数被调用的时间之间出现较长的延迟。在解决这个问题时,我发现有很多人使用计时器方法来解决,因此对于某些用例而言,它似乎足够可靠,但我认为这有点hacky。
我喜欢mhstnsc的想法,所以在实现它后,我决定在这里添加一些代码,可能对试图做类似事情的人有用。您可以轻松地通过设置m_bUserIsMoving标志并重写“void MainWindow :: moveEvent(QMoveEvent * pEvent)”来适应捕捉“用户完成移动窗口”事件。我正在使用它来保存配置文件,每当用户完成调整大小或移动窗口时,都会保存最后一个位置,即使应用程序以不干净的方式终止,也是如此。
// 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; }

这比计时器稍微复杂一些,但更加健壮可靠。


我不确定你是如何使其工作的。就像这个帖子中的那个人一样 https://www.qtcentre.org/threads/54968-how-to-detect-window-resize-event 当我手动调整窗口大小时,我没有收到任何鼠标事件。 - UVV
玩弄这个实现时,我发现在窗口边框上检测鼠标按下和释放的可靠性非常不稳定。有时它能正常工作,有时则不能。 - Alex

3
我是这样做的:
  1. 让我的类继承自QWidget
  2. 定义一个私有变量int timerId = 0
  3. 重载QWidget::resizeEvent和QObject::timerEvent
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;
}

2

窗口装饰上的鼠标事件由底层窗口系统管理,这就是为什么你无法像你尝试的那样捕获它们。

我曾经遇到过同样的问题,我选择的解决方案是在每个调整大小事件上重新启动一个单次定时器,并且只在定时器间隔时间过去后处理更新。虽然不太美观,但我没有找到其他解决方法。


1

另一种方法是安装应用程序的事件过滤器,并获取应用程序的所有事件,陷阱鼠标按下和松开并在两者之间不更新窗口。

"在QCoreApplication :: instance()上安装事件过滤器。这样的事件过滤器能够处理所有小部件的所有事件,因此它与重新实现notify()一样强大; 此外,可以拥有多个应用程序全局事件过滤器。全局事件过滤器甚至可以看到禁用小部件的鼠标事件。请注意,只对主线程中存在的对象调用应用程序事件过滤器。"


0
我的Qt应用程序使用图像窗口并进行复杂的分层重建,即使在非常快的机器上也可能需要一些时间。因此,对我来说,不要求每次窗口框架大小发生变化时都重新绘制窗口是很重要的,这样可以避免窗口框架调整响应缓慢。
所以我是这样解决的:
在我的图像窗口中,我启用了鼠标跟踪:
setMouseTracking(true);

然后,在窗口类中,我有一个布尔值puntme;当捕获到调整大小事件时,它会被设置:

bool puntme;

然后,在mousemove事件中:
void imgWindow :: mouseMoveEvent(QMouseEvent * event) {
if (puntme)
{
    puntme = false;
    needRebuild = true;
    update();
}

...

基本上,这会在用户将鼠标移动到窗口上方时执行操作——如果他们只是调整窗口大小,这是一种非常自然的行为——然后窗口将以新的大小重新绘制。它不会在调整大小期间发生,因为Qt并没有传递移动动作。
相反,在调整大小期间,我只需缩放已经存在的位图,这给出了一种关于比例变化的粗略近似,而不需要处理实际的新分辨率。
最坏的情况是,用户调整大小后,从窗口中移开,并保留粗略缩放的位图,直到再次回到它,在此时它将按照实际新显示的位图==比例/大小条件进行更新。
没有完美方法——真正需要的是Qt提供“用户停止调整窗口大小”消息,但在缺少之下,这对我来说运行良好。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接