【问题标题】:How to know when the user has finished resizing the window? [closed]如何知道用户何时完成了窗口大小的调整? [关闭]
【发布时间】:2021-02-08 17:17:28
【问题描述】:

我正在为自己编写一个 PDF 阅读器来获得乐趣和咯咯笑声,但是在调整窗口大小时我遇到了麻烦。问题如下:在加载 PDF 时调整窗口大小时,将再次渲染 PDF 以匹配新窗口大小,但是 resizeEvent 被多次调用,因此渲染函数被多次调用,这效率极低并导致滞后。有没有办法让程序在 resizeEvent 完全完成然后调用渲染函数时通知?欢迎使用 Python 和 C++ 的解决方案

【问题讨论】:

  • 请添加到目前为止您尝试过的任何内容
  • 当您收到 resizeEvent 时,调整大小已经发生,问题是某些小部件可能会由于其内容而重复(甚至递归)接收它(这通常发生在滚动条正在播放时)。你是如何展示文件的?可以分享minimal, reproducible example吗?

标签: python c++ qt pyqt5 qt5


【解决方案1】:

免责声明

下面的解决方案是对如何知道用户何时完成窗口大小调整?这个问题的回答,这个问题很清楚,可以回答。

但是,我相信您遇到了 XY 问题。您遇到的渲染缓慢可能是由于使用了错误的技术,但不看代码就无法判断。我建议你在另一个问题中描述你的渲染问题,用Minimal, Reproducible Example 支持它。

解决方案

我解决此类问题的方法是:

  1. 在调整大小之前采取行动

正如@musicamante 在 cmets 中所写:

当您收到 resizeEvent 时,调整大小已经发生

超越QWidget::resizeEvent 的方法是使用QObject::event 并注意QEvent::type QResizeEvent

  1. 尝试区分程序调整大小,即调用QWidget::resize用户调整大小,即通过拖动窗口的边缘

QEvent::spontaneous 可以用于此目的,因为它:

如果事件源自应用程序外部(系统事件),则返回 true;否则返回 false。

如果事件是非自发的(程序调整大小),允许生成resizeEvent 来设置复杂小部件的初始大小。对于自发事件(用户调整大小),阻止resizeEvent 的生成并手动处理复杂小部件的大小

  1. 获取用户完成窗口大小调整的准确时刻,而不是像@G.M.那样使用计时器。 (顺便说一句,我非常尊重)建议

NonClientAreaMouseButtonRelease 事件类型在这里可能会派上用场。

这并不一定意味着用户正在调整窗口大小。这种事件类型也是由拖动窗口生成的。但是,在这种情况下,这不会影响解决方案。

注意:不要将复杂的小部件添加到任何布局中,因为它会破坏手动大小处理。

示例

以下是我为您编写的 C++ 示例,目的是演示如何实施建议的解决方案:

#include <QApplication>
#include <QResizeEvent>
#include <QFormLayout>
#include <QLineEdit>

class MainWindow : public QWidget
{
    QWidget *m_complexWidget;
public:
    MainWindow(QWidget *parent = nullptr) :
        QWidget(parent),
        m_complexWidget(new QWidget(this)) {
        auto *form = new QFormLayout(m_complexWidget);

        for (int n = 0; n < 20; n++)
            form->addRow(tr("Property ") + QString::number(n),
                         new QLineEdit(this));

        resize(400, 400);
    }

    bool event(QEvent *event) override {
        switch (event->type()) {
        case QEvent::NonClientAreaMouseButtonRelease:
            // The widget is resized at the end of a resize
            m_complexWidget->resize(size());
            break;
        case QEvent::Resize:
            // Prevent generation of a resize event
            if (event->spontaneous())
                return false;
            break;
        default:
            break;
        }

        return QWidget::event(event);
    }

protected:
    void resizeEvent(QResizeEvent *event) override {
        // The widget's size is set to the initial size of the window
        m_complexWidget->resize(event->size());
    }
};

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;

    w.show();

    return a.exec();
}

【讨论】:

  • 非常感谢您提供的示例和说明。今天晚些时候我会试试这个。关于如何防止 resizeEvent 被多次调用,我有另一个想法:停用调整大小动画。
  • 我的渲染还没有优化,我稍后会做。我目前的渲染问题是,我要同时渲染整个 PDF(这显然很愚蠢),但我可能会在本周末解决这个问题
  • @Mettigel4_1 正如我所说,您真正的问题不在于调整大小。请按照我的回答中的建议并发布另一个具有足够详细信息的问题,以便可以确定真正的问题并提出解决方案。至于调整大小,如果您尝试该示例,它会完美运行,因此我建议您考虑接受和/或支持我的回答。
  • 我会努力的,谢谢
  • 问题的部分原因在于调整大小,因为我点击了调整窗口大小按钮,因此没有必要将 pdf 渲染 3 次。我很清楚我的渲染问题,我已经在修复它
【解决方案2】:

假设您的班级名为PDF_reader,您可以使用单次QTimer 在特定时间长度(例如1 秒)内未调整小部件大小时通知您,并且仅更新小部件如果该计时器处于非活动状态。

基于QLabel 的简单示例可能类似于...

class PDF_reader: public QLabel {
  using super      = QLabel;
  using this_class = PDF_reader;
public:
  PDF_reader ()
    : super("Some text goes here")
    {
      m_timer.setSingleShot(true);
      m_timer.setInterval(1000);
      connect(&m_timer, &QTimer::timeout, this, [this]{ update(); });
    }
  virtual void resizeEvent (QResizeEvent *event) override
    {
      m_timer.stop();
      m_timer.start();
    }
  virtual void paintEvent (QPaintEvent *event) override
    {
      if (!m_timer.isActive()) {
        super::paintEvent(event);
      }
      event->accept();
    }
private:
  QTimer m_timer;
};

【讨论】:

  • 我宁愿不设置计时器,在我看来,这是一种解决方法而不是解决方案。但是谢谢你的例子
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-04-04
  • 1970-01-01
  • 2019-05-28
  • 1970-01-01
  • 2023-03-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多