QML change screen width in runtime - qt

I try to create application based on current width/height screen size. I have several monitors: 2k and 4k. Then I start application on 2k, it is initially determined width with 4k monitor, but it isn't that.
Window size based on screen size:
Window {
width: 0.42 * Screen.width
height: 0.85 * Screen.height
x: Screen.virtualX + 0.5 * Screen.width - 0.5 * width
y: Screen.virtualY + 0.5 * Screen.height - 0.5 * height
onWidthChanged: console.log("window width: ", width)
}
After startup i received following log:
qml: window width: 1612
qml: window width: 806
And broken animation:

Related

QML Canvas improperly rendered with QT_SCALE_FACTOR set

I have a Qt/QML program that runs on Ubuntu and on a Raspberry Pi 3B+. The program runs fine on both platforms however the Pi has a high DPI screen so I set a QT_SCALE_FACTOR to 1.3 on that device to make controls appear the right size. It works perfectly except for one thing: indicators on ComboBox and SpinBox controls are drawn the same size as if no QT_SCALE_FACTOR was set, and the rest of the rectangle they would fill if the QT_SCALE_FACTOR was applied is filled with random pixels. Setting a QT_SCALE_FACTOR on Ubuntu works without any issues.
I tried replacing the indicators with a custom one but I get the same result.
Leaving QT_SCALE_FACTOR unset produces correctly rendered indicators.
This is with Qt 5.13.2 and QtQuick.Controls 2.5 on Linux pi3 4.19.75-v7+ #1270 SMP Tue Sep 24 18:45:11 BST 2019 armv7l GNU/Linux
The pictures below illustrate the behavior with QT_SCALE_FACTOR=2:
This is the code for the custom indicator in the first picture:
ComboBox {
id: cb
...
width: 200
...
indicator: Canvas {
id: canvas
x: cb.width - width - cb.rightPadding
y: cb.topPadding + (cb.availableHeight - height) / 2
width: 12*2
height: 8*2
contextType: "2d"
Connections {
target: cb
function onPressedChanged() { canvas.requestPaint(); }
}
onPaint: {
var context = getContext("2d");
context.reset();
context.beginPath();
context.moveTo(0, 0);
context.lineTo(width, 0);
context.lineTo(width / 2, height);
context.closePath();
context.fillStyle = cb.pressed ? "#17a81a" : "#21be2b";
context.fill();
}
}

Zooming/Scaling an Image at a specific point within a Flickable

By moving a Slider, I want to be able to scale (zoom in) on an Image and once scaled (zoomed in), be able to move around, ( flick ) I have this example working here below, but my problem is the following.
If I zoom in, scale in, and then flick around to a spot on the image, then I want to zoom (scale) in further on that specific point in the center of the window, when I scale in using the Slider, the zoom (scale) occurs at the center of the image because by default the 'transformOrigin' is Item.Center
Try running the code to understand what I am talking about: ( you will need to change the image source)
import QtQuick 2.12
import QtQuick.Controls 2.3
Item {
id: mainId
width: 1280
height: 720
property int scaleMultiplier: 3
Flickable {
id: flickArea
anchors.fill: parent
focus: true
contentWidth: Math.max(inner.width * slider.value * scaleMultiplier, width)
contentHeight: Math.max(inner.height * slider.value * scaleMultiplier, height)
anchors.centerIn: parent
boundsBehavior: Flickable.StopAtBounds
contentX: contentWidth === width ? 0 : inner.width * slider.value * scaleMultiplier / 2 - flickArea.width / 2
contentY: contentHeight === height ? 0 : inner.height * slider.value * scaleMultiplier / 2 - flickArea.height / 2
Image {
id: inner
scale: slider.value * scaleMultiplier
anchors.centerIn: parent
source: "test_images/1.png"
}
}
Slider {
id: slider
value: .01
orientation: Qt.Vertical
anchors {
bottom: parent.bottom
right: parent.right
top: parent.top
margins: 50
}
from: 0.01
}
}
I tried setting an arbitrary transform origin point like so:
Image {
id: inner
scale: slider.value * scaleMultiplier
anchors.centerIn: parent
source: "test_images/1.png"
transform: Scale {
id: scaleID ;
origin.x: flickArea.contentX + flickArea.width * flickArea.visibleArea.widthRatio / 2
origin.y: flickArea.contentY + flickArea.height * flickArea.visibleArea.heightRatio / 2
}
}
But it seems to have no effect. Any help is appreciated. What am I missing?
Again what I want is when I flick to a specific point on the Image, and scale in, I want the point of origin to scale into be the area in the center of the viewable flickarea. For example let's say I am zoomed in the upper right corner of the image, then I wish to zoom in further, when I zoom in further, the Image is brought back to the center of the image.
Thanks.
Found the answer with help from #sierdzio here:
https://forum.qt.io/topic/105233/zooming-scaling-an-image-at-a-specific-point-within-a-flickable/2
I needed to use resizeContents() method from Flickable
property real zoom: 1;
onZoomChanged: {
var zoomPoint = Qt.point(flickArea.width/2 + flickArea.contentX,
flickArea.height/2 + flickArea.contentY);
flickArea.resizeContent((inner.width * zoom), (inner.height * zoom), zoomPoint);
flickArea.returnToBounds();
}
and
// REMOVE THESE LINES:
contentWidth: Math.max(inner.width * slider.value * scaleMultiplier, width)
contentHeight: Math.max(inner.height * slider.value * scaleMultiplier, height)
contentX: contentWidth === width ? 0 : inner.width * slider.value * scaleMultiplier / 2 - flickArea.width / 2
contentY: contentHeight === height ? 0 : inner.height * slider.value * scaleMultiplier / 2 - flickArea.height / 2
// Instead, do:
contentWidth: inner.width
contentHeight: inner.height

Scaling and positioning in QML

I'm developing a Qt5.11.1 QML application that runs into a QQuickWidget. The resize mode is set to SizeRootObjectToView. Hence, whenever I resize the QMainWindow I see my QML root object that scales too.
Inside I have some images anchored to fill the parent, and they are scaled as expected. Instead I have issues with smaller images or text that should maintain the same relative position and size.
I begin with absolute position and size when ratio is 1:1. Example: the root item has a size of 1920x1080 px, and I must place the other items (mainly images and texts) to given coordinates.
When the root changes its size all the elements should follow it. I tried this:
import QtQuick 2.11
import QtQuick.Controls 2.4
import QtGraphicalEffects 1.0
Rectangle {
id: root
visible: true
color: "black"
property real ratio: root.width / 1920
readonly property real x_CenterGauge: 502
readonly property real y_CenterGauge: 489
Image {
x: x_CenterGauge * ratio
y: y_CenterGauge * ratio
scale: ratio
}
}
but this doesn't work because the root.width property (and in turn ratio) doesn't change when I resize the window. But it actually resize the root element because any anchored item will resize too! I get a change only if I maximize/minimize the window.
I read this article and this question, but I still don't understand how to handle the resising in QML.
In testing it is the property scale that seems to be a problem
By modifying width and height instead it solves the problem
Rectangle {
id: root
visible: true
color: "black"
property real ratio: root.width / 1920
readonly property real x_CenterGauge: 202
readonly property real y_CenterGauge: 489
Image {
x: root.x_CenterGauge * root.ratio
y: root.y_CenterGauge * root.ratio
width: implicitWidth * root.ratio
height: implicitHeight * root.ratio
//scale: ratio
}
}

How to create QML slider with a custom shape?

I have to create a slider with a custom shape like the one shown below (blue is the slider handle):
The red color one is groove and the blue color one is handle.
For the groove, you can use any Item to draw this shape, e.g. a Image
For the handle, you just use a Rectangle and place it according to the Slider.position, e.g. like so:
Slider {
id: slider
width: 400
height: 150
y: 200
handle: Rectangle {
x: slider.leftPadding + slider.visualPosition * (slider.availableWidth - width)
y: Math.max(slider.topPadding, slider.availableHeight - height + slider.topPadding - ((slider.availableHeight - height) * (slider.position * 2)))
width: 15
height: 30
radius: 5
color: 'blue'
}
}
If you have a stranger shape, just change the function for y.
You can use any function that maps a value position (range [0, 1]) to an y-value. You can use every property of the slider, to calculate an appropriate position.
Here another example, where the slider draws the sine function:
Slider {
width: 400
height: 150
id: slider1
y: 200
handle: Rectangle {
x: slider1.leftPadding + slider1.visualPosition * (slider1.availableWidth - width)
y: slider1.topPadding + (slider1.availableHeight - height) / 2 + (slider1.availableHeight - height) / 2 * Math.sin(slider1.position * 10)
width: 15
height: 30
radius: 5
color: 'blue'
}
}
And here for the fun of it: A random function. But I don't think you can draw a fitting groove to it
Slider {
id: slider
width: 400
height: 150
y: 200
onPositionChanged: {
var a = Math.round(Math.random() * 5) - Math.round(Math.random() * 5)
console.log(a)
handle.handleY = handle.y + (a)
console.log(handle.handleY)
}
handle: Rectangle {
property int handleY: slider.topPadding + slider.availableHeight / 2 - height / 2
x: slider.leftPadding + slider.visualPosition * (slider.availableWidth - width)
y: Math.max(slider.topPadding, Math.min(handleY, slider.availableHeight - height + slider.topPadding))
width: 15
height: 30
radius: 5
color: 'blue'
}
}
You can do this directly in QML using Canvas or in Qt C++ (example with a circular slider) and then expose the element to QML. While not a direct copy-and-paste solution check this to see a custom circular progress bar that uses arc() to draw the element. You can use line() if you want straight lines. The behaviour of the slider is also something you will have to do on your own.
You can try to inherit from Slider to try and get some (or even most) of the functionality that your element requires.

How to use QML Scale Element for incremental scaling with different origin

I'm trying to use a QML Scale Element to perform view scaling around a point clicked by the user, but it's not always working as documented.
To reproduce the problem, run the minimal QML example below (I'm using Qt 5.3.1 on Ubuntu 14.04 x86_64) and then:
Click in the center of the blue rectangle at the top left.
See that everything is scaled up, but the center of the blue rectangle remains at your click location. This is as documented in http://doc.qt.io/qt-5/qml-qtquick-scale.html - "[The origin] holds the point that the item is scaled from (that is, the point that stays fixed relative to the parent as the rest of the item grows)."
Now click in the center of the red rectangle.
See that everything is scaled up, but the center of the red rectangle did not remain at your click point, it was translated up and to the left. This is not as documented.
My goal is to have it always zoom correctly maintaining the click point as the origin, as stated in the documentation.
P.S. Interestingly, if you now click again in the center of the red rectangle, it scales up around that point as promised. Clicking again now on the center of the blue rectangle, you see the same unexpected translation behaviour.
P.P.S. I'm working on an application where the user can mouse-wheel / pinch anywhere on the containing rectangle, and everything inside should scale up or down around the mouse / pinch position. Many applications have exactly this behaviour. See for example inkscape.
import QtQuick 2.2
import QtQuick.Controls 1.1
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Rectangle {
x: 100
y: 100
width: 300
height: 300
transform: Scale {
id: tform
}
MouseArea {
anchors.fill: parent
onClicked: {
console.log(mouse.x + " " + mouse.y)
tform.xScale += 0.5
tform.yScale += 0.5
tform.origin.x = mouse.x
tform.origin.y = mouse.y
}
}
Rectangle {
x: 50
y: 50
width: 50
height: 50
color: "blue"
}
Rectangle {
x: 100
y: 100
width: 50
height: 50
color: "red"
}
}
}
(I filed this as a Qt bug, because the behaviour does not follow the documentation. At the moment of writing, the bug seems to have been triaged as "important". https://bugreports.qt.io/browse/QTBUG-40005 - I'm still very much open to suggestions of work-arounds / fixes over here)
In fact it is not a deviant behavior, just a different one from what you may expect after reading the doc.
When you change the Scale transform of your rectangle, the transformation is applied on the original rectangle. The point you click on stay in the same place from the original rectangle point of view.
That's why your rectangle "moves" so much when you click on one corner then the opposite corner.
In order to achieve what you want, you can't rely on transform origin. You have to set the actual x y coordinates of your rectangle.
Here's a working example:
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Rectangle {
id: rect
x: 100
y: 100
width: 300
height: 300
Rectangle {
x: 50
y: 50
width: 50
height: 50
color: "blue"
}
Rectangle {
x: 100
y: 100
width: 50
height: 50
color: "red"
}
transform: Scale {
id: tform
}
MouseArea {
anchors.fill: parent
property double factor: 2.0
onWheel:
{
if(wheel.angleDelta.y > 0) // zoom in
var zoomFactor = factor
else // zoom out
zoomFactor = 1/factor
var realX = wheel.x * tform.xScale
var realY = wheel.y * tform.yScale
rect.x += (1-zoomFactor)*realX
rect.y += (1-zoomFactor)*realY
tform.xScale *=zoomFactor
tform.yScale *=zoomFactor
}
}
}
}

Resources