Qml images and memory releasing - image

I noticed that memory allocated for Image is not released.
Without launched app the system has the following memory values: 423MiB / 1985MiB (checked via nvidia-smi)
When I am launching app and clicking (changing Image source) several times the memory using is increasing (1 click adds 4-5MB): 1950MiB / 1985MiB
Setting "cache" property to false does not help.
I found workaround: change image visibility but in this case a lot Image items are needed.
Does a solution exist to use "source" property not "visible"?
qml source:
Image {
id: trg
anchors.fill: parent
cache: false
states: [
State {
name: "on"
PropertyChanges {
target: trg
source: "qrc:/1.png"
}
},
State {
name: "off"
PropertyChanges {
target: trg
source: "qrc:/2.png"
}
}
]
}
MouseArea {
property bool isOn: false
anchors.fill: parent
onClicked: {
if (isOn) {
trg.state = "on";
}
else {
trg.state = "off";
}
isOn = !isOn;
}
}

Unfortunately, it was a bug (QTBUG-61754 and a few more) which has already been fixed in the qt 5.9.2 snapshot (I used to use 5.9.1 version).

Related

QML Keys.onReleased fire when pressing key

Env:
Qt 5.13 + Mingw32 + Windows 10 64bit
or
Qt 5.11 + Mingw32 + Windows 10 64bit
Demo code:
import QtQuick 2.12
import QtQuick.Window 2.12
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Item {
id: name
anchors.fill: parent
focus: true
Keys.onReleased: console.log("onReleased")
}
}
Problem: QML Keys.onReleased fire when pressing key(Any Key)
From running your example I assume your problem occurs after holding the Key.
To prevent that you can simply check the isAutoRepeat attribute when you catch the KeyEvent:
Keys.onReleased: if (!event.isAutoRepeat) console.log("onReleased")
You can check isAutoRepeat from event
Item {
id: name
anchors.fill: parent
focus: true
Keys.onReleased: {
if (!event.isAutoRepeat)
console.log("released")
else
console.log("repeated like in a text field")
}
}

Map pans and zooms when opening info window

when I click on a marker the map always re-pans and zooms to the initial state. Not such a big deal for a single marker but if I start with a cluster, zoom in and then click on a marker there is the problem. It zooms out, reclusters and then pops up the window. Anybody come across this issue?
This is what it looks like:
Here is the relevant code:
var loc = { latitude: item.Latitude, longitude: item.Longitude };
var marker = { location: loc, label: item.Label, id: item.Id, value: item.Value,showWindow:false };
markers.push(marker);
marker.onClick = function () {
if ($scope.$$phase || $scope.$root.$$phase) {
this.model.showWindow = true;
} else {
var self = this;
$scope.$apply(function() {
self.model.showWindow = true;
});
}
};
And the markup. This is in another directive that allows me to just keep a list of different layer types:
'<markers models="mylayer.locations" coords="\'location\'" doRebuildAll="true" idKey="\'id\'" doCluster="true" fit="true" options="mylayer.options" click="\'onClick\'">' +
'<windows show="\'showWindow\'" doRebuildAll="false" disableAutoPan="true" data-ng-if="mylayer.options.clickable" ng-cloak>' +
'<div>hello</div>' +
'</windows>' +
'</markers>'
Turns out the problem happened when updating from version 1.1.0 of angular-google-maps.js to version 1.1.4. Reverted and it works fine now.

markers appear/disappear a few time when toggleing on

I am having an issue with the markers directive. I start by loading it but keeping visible = false. then I toggle it on and the clusters appear, disappear and then reappear. this sometime repeats a few times. I tried setting doRebuildAll to false but then they don't appear at all when toggle visible to true. Here is my markup for the directive. I have it in another directive so that I can just keep a list of different kinds of layers:
'<markers models="mylayer.locations" coords="\'location\'" doRebuildAll="true" idKey="\'id\'" doCluster="true" fit="true" options="mylayer.options" click="\'onClick\'">' +
'<windows show="\'showWindow\'" doRebuildAll="false" disableAutoPan="true" data-ng-if="mylayer.options.clickable" ng-cloak>' +
'<div>hello</div>' +
'</windows>' +
'</markers>'
And the object:
var loc = { latitude: item.Latitude, longitude: item.Longitude };
var marker = { location: loc, label: item.Label, id: item.Id, value: item.Value,showWindow:false };
markers.push(marker);
var layer = new markersFactory(5,'Top 1000 Locations by TIV', markers, true, { visible: false });
Turns out the problem happened when updating from version 1.1.0 of angular-google-maps.js to version 1.1.4. Reverted and it works fine now.

QML timer combined with transition

import QtQuick 2.0
Rectangle {
id: rec
property int img_in:2
property int img_out:3
Image {
id: imgout
source: "pics/"+img_out+".jpg"
opacity: 1
anchors.fill: parent
}
Image {
id: imgin
source: "pics/"+img_in+".jpg"
opacity: 0
anchors.fill: imgout
}
Timer {
interval: 5000
repeat: true
running: true
onTriggered: {
img_in = img_in+1
img_out = img_out+1
anim.running = true;
}
}
states: State {
name: "state1"
PropertyChanges { target: imgout; opacity: 0}
PropertyChanges { target: imgin; opacity: 1}
}
SequentialAnimation {
id: anim
NumberAnimation { target: imgin; properties: "opacity"; easing.type: Easing.InOutQuad; duration: 1500 }
NumberAnimation { target: imgout; properties: "opacity"; easing.type: Easing.InOutQuad; duration: 1500 }
}
}
I would like to know if the problem is in opacity or in pics number change. Pics do change (suddenly) but there is no animation. Inside onTriggered I have tried to use these options: states, SequentialAnimation plus transition. No help.
So I took your code and customized it a bit. It basically starts at pics/1.jpg fades it out while fading pics/2.jpg in, then continues to the next images. You should be able to use it and change it to suit your needs.
Importantly there is no need to do this with States rather use Behaviour on the Image elements opacity
Hope this answers your question! It does make for a nice slideshow!
import QtQuick 2.0
Rectangle {
id: rec
// [1] First image is to be display is pics/1.jpg,
// followed by 2.jpg, 3.jpg, etc...
property int currentIndex: 1
property int nextIndex: 2
// [2] When swapping the image sources we need
// to block the animation behaviour.
// By default turn it on.
property bool allowBehaviour: true
// [3] When the 'rec' is loaded
// set the current image to fade out
// and the next image to fade in.
Component.onCompleted: {
currentImage.opacity = 0;
nextImage.opacity = 1;
}
Image {
id: currentImage
source: "pics/" + currentIndex + ".jpg"
opacity: 1
anchors.fill: parent
// [4] Here we define that whenever we change the
// opacity we want it to animate. Notice the enable
// is tied to `allowBehaviour`
Behavior on opacity {
enabled: allowBehaviour
NumberAnimation { easing.type: Easing.InOutQuad; duration: 2500 }
}
}
Image {
id: nextImage
source: "pics/" + nextIndex + ".jpg"
opacity: 0
anchors.fill: currentImage
// [5] See [4] above.
Behavior on opacity {
enabled: allowBehaviour
NumberAnimation { easing.type: Easing.InOutQuad; duration: 2500 }
}
}
Timer {
interval: 2500
repeat: true
running: true
onTriggered: {
// [6] Block the Behaviour animation.
allowBehaviour = false;
// [7] Advance the indices.
currentIndex = nextIndex;
++nextIndex;
// [8] This is key, set the current
// image to visible and the next
// image to invisible. This happens
// instantly as the Behaviour is off.
currentImage.opacity = 1;
nextImage.opacity = 0;
// [9] Turn the behaviour so the
// opacity change at [10] will
// cause an animation.
allowBehaviour = true;
// [10] Like [3] set the current
// image to fade out and the
// next image to fade in.
currentImage.opacity = 0;
nextImage.opacity = 1;
}
}
}

QML Loader not shows changes on .qml file

I have main.qml and dynamic.qml files that i want to load dynamic.qml on main.qml using Loader {}.
Content of dynamic.qml file is dynamic and another program may change its content and overwrite it.
So i wrote some C++ code for detecting changes on file and fires Signal.
My problem is that I don't know how can i force Loader to reload file.
This is my current work:
MainController {
id: mainController
onInstallationHelpChanged: {
helpLoader.source = "";
helpLoader.source = "../dynamic.qml";
}
}
Loader {
id: helpLoader
anchors.fill: parent
anchors.margins: 60
source: "../dynamic.qml"
}
I think that QML Engine caches dynamic.qml file. So whenever I want to reload Loader, it shows old content. Any suggestion?
You need to call trimComponentCache() on QQmlEngine after you have set the Loaders source property to an empty string. In other words:
helpLoader.source = "";
// call trimComponentCache() here!!!
helpLoader.source = "../dynamic.qml";
In order to do that, you'll need to expose some C++ object to QML which has a reference to your QQmlEngine (lots of examples in Qt and on StackOverflow to help with that).
trimComponentCache tells QML to forget about all the components it's not current using and does just what you want.
Update - explaining in a bit more detail:
For example, somewhere you define a class that takes a pointer to your QQmlEngine and exposes the trimComponentCache method:
class ComponentCacheManager : public QObject {
Q_OBJECT
public:
ComponentCacheManager(QQmlEngine *engine) : engine(engine) { }
Q_INVOKABLE void trim() { engine->trimComponentCache(); }
private:
QQmlEngine *engine;
};
Then when you create your QQuickView, bind one of the above as a context property:
QQuickView *view = new QQuickView(...);
...
view->rootContext()->setContextProperty(QStringLiteral("componentCache", new ComponentCacheManager(view->engine());
Then in your QML you can do something like:
helpLoader.source = "";
componentCache.trim();
helpLoader.source = "../dynamic.qml";
I was hoping for a pure QML solution. I noticed that loader.source is a url (file:///) and remembered how with HTML, you can avoid HTTP caching using ?t=Date.now() in your requests. Tried adding ?t=1234 to the end of loader.source, and sure enough, it works.
import QtQuick 2.0
Item {
Loader {
id: loader
anchors.fill: parent
property string filename: "User.qml"
source: filename
function reload() {
source = filename + "?t=" + Date.now()
}
}
Timer {
id: reloadTimer
interval: 2000
repeat: true
running: true
onTriggered: {
loader.reload();
}
}
}
I also wrote another example that will check for changes in the file contents before triggering a reload using an XMLHttpRequest.
import QtQuick 2.0
Item {
Loader {
id: loader
anchors.fill: parent
property string filename: "AppletUser.qml"
property string fileContents: ""
source: ""
function reload() {
source = filename + "?t=" + Date.now()
}
function checkForChange() {
var req = new XMLHttpRequest();
req.onreadystatechange = function() {
if (req.readyState === 4) {
if (loader.fileContents != req.responseText) {
loader.fileContents = req.responseText;
loader.reload();
}
}
}
req.open("GET", loader.filename, true);
req.send();
}
onLoaded: {
console.log(source)
}
Timer {
id: reloadTimer
interval: 2000
repeat: true
running: true
onTriggered: loader.checkForChange()
}
Component.onCompleted: {
loader.checkForChange()
}
}
}

Resources