Problems with if statement in QML - qt

I am trying to learn Qt and QML and I have hitten a wall.
I want to be able to determine the screen factor of the display and choose a background image accordingly, but I cannot.
This is my code:
import QtQuick 2.2
import QtQuick.Window 2.2
Image {
id: root
width: screenGeometry.width
height: screenGeometry.height
property real ratio: root.width / root.height
source: {
source = "../components/artwork/background.png"
if (ratio == 16.0 / 9.0) {
source = "../components/artwork/background_169.png"
}
else if (ratio == 16.0 / 10.0) {
source = "../components/artwork/background_1610.png"
}
else if (ratio == 4.0 / 3.0) {
source = "../components/artwork/background_43.png"
}
}
fillMode: Image.PreserveAspectFit
}
Independently from the screen size, it always choose the generic one.
From what I could see trying to debug the problem, it seems that the value of variable ratio is not understood within the if statement.
Searching for similar topics, I have found this one, but I can't still see the error.
Your help is much appreciated!

Related

what is the maximum size for an array in QML?

It's troubling me building an array of objects with a lot of data in qml using the Windows platform (it's the only one that the crash happens). For some reason the application is crashing if the processing function takes too long!
I'm going to illustrate with a portion of code what i want to do:
main.qml
import QtQuick 2.0
import QtQuick.Controls 1.4
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.3
ApplicationWindow {
id: window
title: "Stack"
visible: true
width: 1400
Page {
id: page
anchors.fill: parent
property int responsiveWidth: 1000
property int maximumWidth: 900
ScrollView {
id:configScroll
anchors.fill: parent
GridLayout {
columns: 2
width: page.width > page.responsiveWidth ? page.maximumWidth : page.width
anchors.top: parent.top
anchors.left: parent.left
anchors.leftMargin: page.width > page.responsiveWidth ? (page.width - childrenRect.width)/2 : 10
anchors.rightMargin: page.width > page.responsiveWidth ? 0 : 10
Button {
property bool test: true
text: "array func"
onClicked: {
var panelModes=[], rows=[],groupsModes=[], panelLabel="panel", sounderLabel="sounder",soundersModeLabel="sounder mode",sounderGroupLabel="sounder group"
for(var gSndrModeAdd=0; gSndrModeAdd<1000;gSndrModeAdd++) {
panelModes = []
for(var pSndrModeAdd=0; pSndrModeAdd<32; pSndrModeAdd++) {
rows = []
rows.push(["C1",1])
rows.push(["C2",1])
for(var sSndrModeAdd=0; sSndrModeAdd<32; sSndrModeAdd++) {
rows.push(["L1S"+(sSndrModeAdd+1),1])
rows.push(["L2S"+(sSndrModeAdd+1),1])
rows.push(["L3S"+(sSndrModeAdd+1),1])
rows.push(["L4S"+(sSndrModeAdd+1),1])
}
panelModes.push({"label":panelLabel, "value": 1, "headers":[sounderLabel,soundersModeLabel],"rows":rows})
}
groupsModes.push({"label":sounderGroupLabel,"value":1,"nested":panelModes})
}
console.log("the array is: ")
console.log(groupsModes)
}
}
}
}
}
}
This crashes.
If this is not specifically an array problem, is this memory related problem? something like that?
This is necessary to build the structure i want. Is there a way to solve this?
The error i get on console:
09:45:07: The program has unexpectedly finished.
09:45:07: The process was ended forcefully.
09:45:07: C:/Dev/QT/build-array-crash-limit-Desktop_Qt_5_11_1_MinGW_32bit-Debug/debug/array-crash-limit.exe crashed.
I managed to catch Memory usage with QML Profiler if i set the first for cycle to 300.
you have a bug here
...
for(var pSndrModeAdd=0; 32; pSndrModeAdd++) {
...
and the loop will run forever. Change to
...
for(var pSndrModeAdd=0; pSndrModeAdd<32; pSndrModeAdd++) {
...
Anyway, arrays in QML are just JavaScript arrays, as such have (practically) no maximum size.
It turns out the crashing of the application only happens on the windows platform.
In MACOS and LINUX it only blocks for the time of the function processing.
Maybe it really is a memory problem after all, that happens on windows using versions of QT that use MinGW 32-bit.
The solution to solve this problem?
The usage of MinGW 64-bit.

QML Signal for Orientation Change Complete

I am working on an ESRI AppStudio app (AppStudio 3.1, Qt 5.11) for iPad and need to do some resizing of a QML control when the orientation changes. I found this page which seems to describe the official way to do this: https://wiki.qt.io/QML_orientation_observer
import QtQuick.Window 2.2
Rectangle {
property bool isPortrait: Screen.primaryOrientation === Qt.PortraitOrientation || Screen.primaryOrientation === Qt.InvertedPortraitOrientation
onIsPortraitChanged: console.log("isPortrait", isPortrait)
}
However, I have found the statement on that page that the binding will be fired after the height and width changes are completed to be incorrect. What I saw when I implemented this is that onIsPortraitChanged does indeed fire when the orientation changes but it does so before the orientation change animation completes and before the width of the app is resized. Is there a way I can trigger my code after the width is finished changing?
Here's a solution that I found but it will only work for devices where the app is full screen and there might be a cleaner way to do this.
import QtQuick.Window 2.2
Window {
id: app
visible: true
width: 640
height: 480
Rectangle {
anchors.fill: parent
onWidthChanged: {
if(app.width === Screen.width || app.width === Screen.height) {
//calculate new size
}
}
}
}
I have no problem getting new width with correct signal for orientationchanged
import QtQuick.Window 2.12
Window {
id: app
visible: true
width: 640
height: 480
Screen.orientationUpdateMask: Qt.LandscapeOrientation | Qt.PortraitOrientation
...
...
Connections{
target: my_object
Screen.onPrimaryOrientationChanged:{
console.log("orinetation changed, width: " + width )
}
}
}

Weird canvas behavior in QT/QML

I have a strange issue in QML when trying to draw relatively simple graphics.
I'm using PyQt 5 with QT 5.11 and Ubuntu 18.04.
Since the graphics part I'm writing is mostly stationnary (changes about each 1 secs), I found out canvas would be a convenient way to draw graphics without using QPainter. But it's been noting but a nightmare with fonts.
For example, when drawing this simple QML component:
import QtQuick 2.11
import QtQuick.Controls 2.4
import QtQuick.Layouts 1.11
import QtGraphicalEffects 1.0
Rectangle {
id: cont_rect
Button {
text: "Repaint"
onClicked: {
cv.clearcv()
cv.requestPaint()
}
}
property string cvfont: "13px Ubuntu"
height: 80
width: 300
color: "#132931"
Canvas {
anchors.fill: parent
antialiasing: true
contextType: '2d'
id: cv
width: cont_rect.width
height: cont_rect.height
function clearcv() {
var ctx = cv.context
ctx.clearRect(0, 0, width, height)
ctx.fillStyle = "black"
}
function drawStateLine() {
var ctx = (cv.context == null) ? getContext("2d", {alpha: false}) : cv.context
var x = width/2, y = height - 10
var txt_angle = -45 * 2*Math.PI / 360
ctx.save()
ctx.translate(x,y)
ctx.rotate(txt_angle)
ctx.fillStyle = "royalblue"
ctx.textAlign = "left"
ctx.font = cvfont
ctx.textBaseline = "middle"
ctx.globalAlpha = 1
ctx.fillText("Detected", 0, 0)
ctx.restore()
}
onPaint: {
drawStateLine();
}
}
}
I end up with extremely weird graphics behavior.
The FIRST time my component is rendered, I have good looking text:
Then, when I click my specially crafted button (for the sake of isolating the problem down to a simple component), I have:
Basically, whenever I am trying to repaint my damn canvas, first clearing it (I also tried ctx.reset(), ctx.fillRect with my background color, etc.), I end up with font rendering that is much less readable and that bothers me.
Is there anyone here who has an idea on how to avoid this?
I don't know if it's useful, but I use a 27in display with 1080p resolution.
Thanks!

How to Animate an Image with more than 256 colors (i.e. not a GIF) using AnimatedImage or otherwise

I can use AnimatedImage in Qt 5.9 which works on GIFs like this;
import QtQuick 2.7
import QtQuick.Controls 2.2
ApplicationWindow
{
visible: true
width: 640
height: 480
Rectangle
{
width: animation.width;
height: animation.height + 8
AnimatedImage
{
id: animation;
source: "myanimation.gif"
}
Rectangle {
height: 8
width: animation.currentFrame/animation.frameCount * animation.width
y: animation.height
color: "red"
Component.onCompleted: console.log("framecount ", animation.frameCount);
}
}
}
I get a lot of error messages too. This is printed over and over;
QQmlExpression: Expression qrc:/main.qml:26:20 depends on non-NOTIFYable properties:
QQuickAnimatedImage::frameCount
I took my example code from here; http://doc.qt.io/qt-5/qml-qtquick-animatedimage.html
which doen't work at all, something wrong with putting frameCount into a property, so i changed that in my version.
I would like to animate a png like an apng. Apparently there used to be mng support, but it is not there anymore.
So i call QMovie::supportedFormats which returns only GIF (on Windows).
My question:
How can i either use AnimatedImage on a format that supports non-palettised color (eg png etc) or is there another way to animate an image that would work?
Thanks for any info.
It seems like those image formats are not shipped with the standard installation of Qt/QML. You can however install them as a plugin.
See:
Documentation: Qt Image Formats
Code/GitRepository

Recursion trick in ShaderEffectSource fails when I destroy the source object in onStateChanged

I want a ShaderEffectSource called snapshotter that updates on every change of a source item, but when the source item is destroyed, I want snapshotter to retain the last state of the source item.
In my code I use a workaround for a Qt deficiency - setting snapshotter.sourceItem = snapshotter when the previous source item gets destroyed. This works fine when I destroy the old source item e.g. on key press. But when I do it in an onStateChanged handler, I get this error:
ShaderEffectSource: 'recursive' must be set to true when rendering recursively.
But I don't want to set recursive: true, because then snapshotter would start repainting as fast as possible, wasting a lot of processing power.
Any idea why that problem happens considering it works fine when using key press, and/or a workaround?
I realize I'm asking why a hacky solution fails in some cases, but since it works in some cases, I'd like to use the solution anyway as it's very useful.
My code: (main.qml)
import QtQuick 2.6
import QtQuick.Window 2.2
Window {
visible: true
width: 640
height: 480
Loader {
active: true
id: loader
sourceComponent:
Rectangle {
color: "red"
border.color: "white"
width: 100
height: 100
parent: row
states: [
State {
// the condition is always true, in this example code
name: "theOnlyState"; when: width === 100
}
]
onStateChanged: {
if(state === "theOnlyState") {
loader.active = false;
}
}
}
}
Row {
id: row
ShaderEffectSource {
id: snapshotItem
sourceItem: {
if(loader.status === Loader.Ready) {
return loader.item;
} else {
return snapshotItem;
}
}
live: loader.status === Loader.Ready
width: 100
height: 100
}
}
}
Note: I just had an idea: maybe setting recursive: true will not create the problem I mentioned, considering I'd only set it when live == false. Maybe Qt is smart enough not constantly redraw in that case. But I'm not sure how to check if that's true.
Ok, I found an 99%-authorative answer.
The worry about recursive: true that I expressed in the question was provoked by my vague memory of reading something like that in the Qt docs.
I now went ahead and looked up the relevant passage again, and here's what it says:
Setting both this property and live to true will cause the scene graph to render continuously. Since the ShaderEffectSource depends on itself, updating it means that it immediately becomes dirty again.
Note how they say that for the bad scenario to occur, live has to be true.
So the solution is to simply use recursive: true, which makes the solution much less hacky.
Note: I'm not gonna mark this answer accepted, because I'd like people to still go over it now and then and maybe, just maybe, prove me wrong (e.g. show that I'm misinterpreting the text).
More evidence in favor of the above conclusion:
I set the env var QSG_VISUALIZE to changes and ran this trivival test app:
import QtQuick 2.6
import QtQuick.Window 2.2
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
ShaderEffectSource {
width: 100
height: 100
id: shaderEffectSource
live: false
sourceItem: shaderEffectSource
recursive: true
}
}
It showed an unchanging colored square. But when I changed live to true in this code, it started flickering in random colors.
As you found out yourself, when you have live = false and recursive = true it won't be redrawn all the time.
I think the problem of yours might arise due to the magic, QML uses for its state machine, going back and forth and so on...
As far as I understand your problem, you want to create an object, take a snapshot, and delete it right after that again.
This is easier achieved by using methods and signals than by some declarative state changes and so on.
Specifically you might be looking for scheduleUpdate() to render the next frame, without the need of setting live = true. The next thing you will be interested in is the signal: scheduledUpdateCompleted to delete your object again.
Something like this:
import QtQuick 2.0
import QtQuick.Controls 2.0
import QtGraphicalEffects 1.0
ApplicationWindow {
width: 1024
height: 800
visible: true
Button {
text: 'Next'
onClicked: {
ses.sourceItem = prototype.createObject(ses)
}
}
ShaderEffectSource {
id: ses
y: 100
width: 50
height: 50
live: false
onSourceItemChanged: if (sourceItem !== this) scheduleUpdate()
onScheduledUpdateCompleted: {
sourceItem.destroy()
sourceItem = this
}
}
Component {
id: prototype
Rectangle {
width: 50
height: 50
color: Qt.rgba(Math.random(-1), Math.random(0), Math.random(1))
visible: false
Component.onCompleted: console.log('Created new Rectangle with color', color)
Component.onDestruction: console.log('Destroy Rectangle with color', color)
}
}
}
Remember: The sourceItem does not have to be visible to be rendered into an ShaderEffectSource. If it is not necessary for other reasons, I would keep it invisible, so it is not rendered twice.

Resources