调整大小时不要重新绘制窗口。

8
我的QML应用程序(Qt 5.4)基于一个“Window”项目。用户可以调整应用程序的大小。当应用程序被调整大小时,应用程序的内容也会相应地被调整大小(使用“onWidthChanged”和“onHeightChanged”)。
这一切都很好。
但为了避免闪烁,我不想在调整大小时更新应用程序的内容。在QML中是否有可能检测到用户实际上正在调整窗口大小(按住鼠标按钮在窗口边框上),并且在调整大小完成之前(释放鼠标按钮)不重新计算内容?

6
直到鼠标按钮被释放之前,请过滤掉相关事件。具体来说,在按住鼠标按钮时“吞噬”大小调整事件,然后在鼠标释放时合成最终的大小调整事件。您可以在附加到显示QML界面的窗口/小部件对象的事件过滤器中完成所有操作。 - Kuba hasn't forgotten Monica
我尝试了这个方法,它可以工作,但问题是当调整窗口大小时,你无法获取鼠标按下或释放事件,而且 QGuiApplication::mouseButtons 返回没有任何按钮被按下。 - GrecKo
2个回答

1

编辑:Kuba Ober建议的方法更加简单和稳健,我仍然保留我的答案,因为我认为它有些有趣(可以修改C++自定义组件方法来过滤窗口事件,如建议所述)。


抱歉,我写了一个快速且不太美观的黑客来测试是否可能,它仅涵盖了您问题的第二部分(不更新内容)。我的解决方案阻止了Item的重绘,但是也在请求更新时立即隐藏了它(这对您可能不是问题)。
阅读QQuickItem :: updatePaintNode文档,特别是这句话后,我创建了一个C++类来设置/取消设置此标志在任意QQuickItem上:
“如果用户在项目上设置了QQuickItem :: ItemHasContents标志,则将调用该函数作为QQuickItem :: update()的结果。”
#ifndef ITEMUPDATEBLOCKER_H
#define ITEMUPDATEBLOCKER_H

#include <QObject>
#include <QQuickItem>


class ItemUpdateBlocker : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QQuickItem* target READ target WRITE setTarget NOTIFY targetChanged)
    QQuickItem* m_target;

public:
    explicit ItemUpdateBlocker(QObject *parent = 0) : QObject(parent), m_target(nullptr) {  }
    QQuickItem* target() const { return m_target; }

signals:
    void targetChanged();

private:
    static void blockUpdate(QQuickItem* target)
    {
        if (target)
            target->setFlag(QQuickItem::ItemHasContents, false);
    }

    static void unblockUpdate(QQuickItem* target)
    {
        if (target)
        {
            target->setFlag(QQuickItem::ItemHasContents, true);
            target->update();
        }
    }


public slots:
    void setTarget(QQuickItem* target)
    {
        if (m_target == target)
            return;
        unblockUpdate(m_target);
        blockUpdate(target);
        m_target = target;
        emit targetChanged();
    }
};

#endif // ITEMUPDATEBLOCKER_H

下一步是注册这个类,以便在QML中使用:
qmlRegisterType<ItemUpdateBlocker>("com.mycompany.qmlcomponents", 1, 0, "ItemUpdateBlocker");

而且您可以在QML中像这样使用它:

import QtQuick 2.4
import QtQuick.Controls 1.3
import QtQuick.Window 2.2
import com.mycompany.qmlcomponents 1.0

ApplicationWindow {

    width: 640
    height: 480
    visible: true

    Rectangle {
        color: "red"
        id: root
        anchors.fill: parent

        Text {
            text: blocker.target ? "Blocked" : "Not Blocked"
        }

        Rectangle {
            color: "white"
            anchors.centerIn: parent
            width: parent.width/2
            height: parent.height/2

            ItemUpdateBlocker {
                id: blocker;
            }

            MouseArea {
                anchors.fill: parent
                onClicked: blocker.target = blocker.target ? null : parent
            }
        }
    }
}

你当然可以给阻止器添加一个“active”属性来简化它的使用(比使用空“target”禁用它更美观),但我会把这留作练习。
也许你可以在窗口的宽度或高度改变时启动一个定时器,我还没有找到直接找到窗口是否被调整大小的方法。

1
但为了避免闪烁,我不想在调整应用程序大小时更新应用程序内容。 我有同样的意图,后来发现在我的情况下,当窗口不经常重绘时,已经足以避免闪烁和高CPU负载。这可以通过计时器实现:
import QtQuick 2.12
import QtQuick.Controls 2.12

Window {
    id: root

    onWidthChanged: {
        redrawTimer.running ? redrawTimer.restart() : redrawTimer.start()
    }

    onHeightChanged: {
        redrawTimer.running ? redrawTimer.restart() : redrawTimer.start()
    }

    Timer {
        id: redrawTimer
        interval: 100
        onTriggered: {
            content.width  = ...
            content.height = ...
        }
    }

    // Actual content of your window, here assumed to be in a custom QML type.
    WindowContent {
        id: content
    }
}

上面的代码创建了一个窗口,只有在过去100毫秒内没有接收到调整大小事件时才重新绘制。这样,调整大小结束和重绘之间存在一个小但几乎不可察觉的延迟,类似于调整大小期间鼠标移动的暂停和重绘之间的延迟。
这种方法特别适用于具有快速、低质量和慢速、高质量的重绘方式的元素,例如使用SVG源的Qt QML Image类型。在这种情况下,基于光栅图形的缩放是快速的,并且可以通过QML布局或锚定机制在窗口调整大小期间连续进行,创建“预览”。通过设置Image#sourceSize来重新绘制SVG是缓慢的,只有在像上面展示的100毫秒计时器触发时才会执行。

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