Do not repaint window during resize

一个人想着一个人 提交于 2020-08-22 05:25:17

问题


My QML application (Qt 5.4) is based on a Window item. The application can be resized by the user. When the application is resized the content of the application is being resized respectively (with onWidthChanged and onHeightChanged).

This is all fine.

But to avoid flickering I don't want to update the content of the application while the applicaiton is beeing resized. Is there a possibility in QML to detect when the user is actually resizing the window (holding the mouse button down over the border of the window) and don't recalculate the content before the resize is finished (the mouse button is released)?


回答1:


EDIT : What Kuba Ober suggested is infinitely simpler and more robust, I'll still leave my answer here as I found it somewhat interesting (and the C++ custom component approach can be modified to filter the window events as suggested).


Pardon me but I have written a quick and ugly hack to see if it was possible, it only covers the second part of your question (not updating the content). My solution blocks the repainting of an Item, but also hide it as soon as an update is requested on it (which may not be a problem for you).

After reading the QQuickItem::updatePaintNode documentation and especially this phrase

The function is called as a result of QQuickItem::update(), if the user has set the QQuickItem::ItemHasContents flag on the item.

I created a C++ class to set/unset this flag on an abitrary 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

next step is to register this class so that it can be used in QML :

qmlRegisterType<ItemUpdateBlocker>("com.mycompany.qmlcomponents", 1, 0, "ItemUpdateBlocker");

And you can use it in QML like this :

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
            }
        }
    }
}

You can of course add an active property to the blocker to simplify it's use (prettier than using a null target to disable it), but I'll leave that as an exercise.

Maybe you can use that with a timer started whenever the width or the height of your Window is changed, I have not yet found a direct way to find if a window is resized.




回答2:


But to avoid flickering I don't want to update the content of the application while the applicaiton is beeing resized.

I had the same intention and then found that in my case it's already enough to avoid flickering and high CPU load when the window does not repaint as often. This can be implemented with a timer:

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
    }
}

The code above creates a window that is repainted only when no resize event has been received in the last 100 ms. This way, there is a small but hardly noticeable delay between the end of resizing and the repainting, and similarly between a pause of mouse movement during resizing and the repainting.

This approached works esp. well with elements that have a fast, low-qualit and a slow, high-quality way of repainting, such as the Qt QML Image type used with a SVG source. In that case, raster graphics based scaling is fast and can be done during window resizing with the QML layout or anchors mechanism, creating a "preview". Repainting the SVG by setting Image#sourceSize is slow and would only be done when triggered by the timer as shown above.



来源:https://stackoverflow.com/questions/29169121/do-not-repaint-window-during-resize

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!