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

时间:2015-03-20 14:38:34

标签: qt qml qt-quick qtquick2

我的QML应用程序(Qt 5.4)基于Window项。用户可以调整应用程序的大小。调整应用程序大小时,应用程序的内容将分别调整大小(使用onWidthChangedonHeightChanged)。

这一切都很好。

但为了避免闪烁,我不希望在应用程序调整大小时更新应用程序的内容。 QML中是否有可能检测用户何时实际调整窗口大小(将鼠标按钮按住窗口边框)并且在调整大小完成之前不重新计算内容(释放鼠标按钮)? / p>

2 个答案:

答案 0 :(得分:1)

编辑:Kuba Ober建议的内容更加简单和强大,我仍然会在这里留下我的答案,因为我发现它有点有趣(并且可以修改C ​​++自定义组件方法以按照建议过滤窗口事件)


请原谅我,但我写了一个快速而丑陋的黑客,看它是否可能,它只涵盖你问题的第二部分(不更新内容)。 我的解决方案阻止重新绘制一个项目,但一旦请求更新就会隐藏它(这可能不是你的问题)。

阅读QQuickItem::updatePaintNode文档后,特别是这句话

  

如果用户在项目上设置了QQuickItem :: ItemHasContents标志,则该函数将作为QQuickItem :: update()的结果调用。

我创建了一个C ++类来设置/取消设置这个标志QQuickItem:

#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更漂亮来禁用它),但是我会离开作为一项练习。

也许您可以在Window的宽度或高度发生变化时使用计时器启动,我还没有找到直接查找窗口大小的方法。

答案 1 :(得分:0)

但是为了避免闪烁,我不想在调整应用程序大小时更新应用程序的内容。

我有相同的意图,然后发现就我而言,当窗口不经常重新绘制时,足以避免闪烁和高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的速度很慢,并且只有在如上所示由计时器触发时才能完成。