QT-QML: How to create image (line) and work on it - qt

I need to create and move an image on Qt but i cannot understand how to do that.
The image is just a line like the following:
The result that I'd like to obtain is to have a line which I can shift right or left (it should continue even outside the canvas for some cm) and color the line(or glowing it white with another background) when I call a function.
To do that im using a qml file in qt and I'm creating this line by using the Canvas object, but it is far away from the image showed before:
Canvas {
id: mycanvas
width: 1000; height: 600
contextType: "2d"
property var l_mez: 273
property var pp: 245
Path {
id: myPath
startX: 100; startY: 300
PathCurve { x: (100+(mycanvas.l_mez/2)); y: (300+mycanvas.pp/2) }
PathCurve { x: 100+(mycanvas.l_mez*3/2)-40; y: 300-mycanvas.pp/2+15 }
PathCurve { x: 100+(mycanvas.l_mez*3/2)+40; y: 300-mycanvas.pp/2+15 }
PathCurve { x: 100+(mycanvas.l_mez*2); y: 300}
PathCurve { x: 100+(mycanvas.l_mez*5/2); y: 300+mycanvas.pp/2 }
PathCurve { x: 100+(mycanvas.l_mez*7/2); y: 300-mycanvas.pp/2 }
}
onPaint: {
var ctx = getContext("2d");
ctx.strokeStyle = "grey";
ctx.path = myPath;
ctx.lineWidth =9
ctx.stroke();
}
The result is :
The question is, how can i properly draw the line on the first image?
I was thinking I could just create the image and import it but then I could not shift it and change the color of the line as I prefer.
Thanks for the help

PathCurve is really overspecifying the curve in your example which is where the waviness is coming from. I would instead use alternating PathLine and PathArc to achieve that look.
Note, you could do this with an image also if you were willing to give up on changing the line color. You can load your sample image above twice and place them next to each other so that they seamless connect. You would place them both inside of one Item and turn on clipping.
Then you translate them both left or right to give the sense of movement. When you get to the far end of the images in either direction you just jump the translation back by the image width and keep going.
It will look seamless and smooth to the user. Especially if you use an XAnimator as those run in a separate thread.

Related

Converting 2d coordinates to 3d coordinates in Qml qt3d

I have a window size width: 1500 height: 780, I would like to render the 3d object with 2d co-ordinates, giving z=1 as dummy value. Following is the qml code.(If i have to render at x=0,y=0 3d should object should render exactly the same place where the Rectangle in qml renders i.e window coordinates)
Entity {
id: root
property real x: 2.0
property real y: 0.0
property real z: 0.0
property real scale: 1
property var mainCam
property var forwardRenderer
property Material material
components: [ trefoilMeshTransform, mesh, root.material ]
Transform {
id: trefoilMeshTransform
property real userAngle: 900.0
translation:Qt.vector3d(0,1,1)
property real theta: 0.0
property real phi:0.0
property real roll: 0.0
rotation: fromEulerAngles(theta, phi, roll)
scale: root.scale
}
Mesh {
id: mesh
source: "assets/obj/cube.obj"
}
}
The code is exactly the same as wireframe example, including the camera settings. I tried to use the qt3d unproject api in qml but ended up unsuccessfully . Can you please help me with the clue?
Please read this document about How to convert world to screen coordinates and vice versa this is good for understanding some logics.
and also this.
In qt3d I get 3d coordinate by using RayCaster and ScreenRayCaster
it gives you the local position and world position and screen x , y
see this site from kdab and its example
RenderSettings{
//all components that you need like viewport , InputSettings ,...
ScreenRayCaster {
id: screenRayCaster
onHitsChanged: printHits("Screen hits", hits)
}
}
MouseHandler {
id: mouseHandler
sourceDevice: MouseDevice {}
onReleased: { screenRayCaster.trigger(Qt.point(mouse.x, mouse.y)) }
}
function printHits(desc, hits) {
console.log(desc, hits.length)
for (var i=0; i<hits.length; i++) {
console.log(" " + hits[i].entity.objectName, hits[i].distance,
hits[i].worldIntersection.x, hits[i].worldIntersection.y, hits[i].worldIntersection.z)
}
}

How to create a shape with a hole in QML?

I want to create a kind of masking layer which consists of a black area with holes in it. Through the holes it should be possible to see the background. In the simplest version I it's just an rectangle covering the whole screen with an opening in the middle. As shown in the sample picture below.
My first approach was to use QML's Context2D feature: https://doc.qt.io/qt-5/qml-qtquick-context2d.html. Maybe it's totally wrong to do it this way, but maybe it's a good starting point. I tried to create an rectangle (which works) and a clipping area (which doesn't work). Besides the fact my implementation of the clipping doesn't work I would have the problem that the clip() command erases the area outside its path but not inside (at least that's what I understood from the docs: https://doc.qt.io/qt-5/qml-qtquick-context2d.html#clip-method).
Canvas {
anchors.fill: parent
onPaint: {
var ctx = getContext("2d");
ctx.fillStyle = "black"
ctx.beginPath();
ctx.fillRect(0, 0, Sizes.rootWindow.width, Sizes.rootWindow.height);
ctx.fill();
}
if the hole is exactly centered and the borders are all have the same size, you could use a transparent rectangle with borders:
Rectangle {
anchors.fill: parent
color: "transparent"
border.color: "black"
border.width: 50
}
I found the solution myself by rereading the Qt docs. There is a function to create transparent rectangles inside a given canvas which is called "clearRect(...)": https://doc.qt.io/qt-5/qml-qtquick-context2d.html#clearRect-method
Canvas {
anchors.fill: parent
onPaint: {
var ctx = getContext("2d");
ctx.fillStyle = "black"
ctx.beginPath();
ctx.fillRect(0, 0, 1000, 600);
ctx.fill();
ctx.clearRect(10, 10, 600, 100)
}
For more advanced shapes OpacityMask suggested by Frank Osterfeld is definitely the way to solve this. But for rectangle I think my solution is more straight forward.

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

QML NumberAnimation.duration behaviour

I'm trying to make a player move smoothly towards a destination in QML. I'm using a NumberAnimation to animate the x,y position changes. The NumberAnimation's duration should be proportional to the distance the player has to travel, so that the player moves at the same speed regardless of how far away they are from their destination.
import QtQuick 1.1
Item {
width: 720
height: 720
MouseArea {
anchors.fill: parent
onClicked: {
var newXDest = mouse.x - player.width / 2;
var newYDest = mouse.y - player.height / 2;
var dist = player.distanceFrom(newXDest, newYDest);
// Make duration proportional to distance.
player.xMovementDuration = dist; // 1 msec per pixel
player.yMovementDuration = dist; // 1 msec per pixel
console.log("dist = " + dist);
player.xDest = newXDest;
player.yDest = newYDest;
}
}
Rectangle {
id: player
x: xDest
y: yDest
width: 32
height: 32
color: "blue"
property int xDest: 0
property int yDest: 0
property int xMovementDuration: 0
property int yMovementDuration: 0
function distanceFrom(x, y) {
var xDist = x - player.x;
var yDist = y - player.y;
return Math.sqrt(xDist * xDist + yDist * yDist);
}
Behavior on x {
NumberAnimation {
duration: player.xMovementDuration
// duration: 1000
}
}
Behavior on y {
NumberAnimation {
duration: player.yMovementDuration
// duration: 1000
}
}
}
Rectangle {
x: player.xDest
y: player.yDest
width: player.width
height: player.height
color: "transparent"
border.color: "red"
}
}
My problem can be demonstrated by running the application above and following these steps:
Click on the bottom right hand corner of the screen.
Immediately click in the centre (or closer towards the top left) of the screen.
On the second click (while the rectangle is still moving), it seems that the rectangle's number animation is stopped (which is what I want) but it assumes the position of the destination (not what I want). Instead, I want the animation to stop and the rectangle to assume the position at which it was stopped, then to continue on to the new destination.
The correct behaviour - ignoring that the movement speed becomes disproportional - can be seen by setting both of the NumberAnimation.durations to 1000.
I think that you are looking for SmoothedAnimation. There are only two types of animation that deal nicely with the destination changing before the animation is completed. That is SmoothedAnimation and SpringAnimation. Both of these use the current position and velocity to determine the position in the next frame. Other animation types move the position along a predetermined curve.
Simply changing NumberAnimation to SmoothedAnimation makes your example look correct to me.

Resources