问题
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