position center of Text on a point
-
Hi
Example: positioning numerals around a clock face.
Using sin and cos to get the x and y is the easy bit.
But the text is appearing below the desired point, rather than centered over it.
Even with
verticalAlignment: Text.AlignVCenterWhat's the correct way to tell Qt that the x and y must be the center of the item/text, rather than the top left of the item?
import QtQuick Item { anchors.fill: parent Repeater { model: 12 Text { property int radius: parent.height / 2 text: index x: radius + radius * Math.cos(2 * Math.PI * index / 12) y: radius + radius * Math.sin(2 * Math.PI * index / 12) verticalAlignment: Text.AlignVCenter } } }
QML Qt 6.5.0 Qt Creator 10.0.1 Ubuntu 22.04.2
Thank you
-- Peter -
Hi
Example: positioning numerals around a clock face.
Using sin and cos to get the x and y is the easy bit.
But the text is appearing below the desired point, rather than centered over it.
Even with
verticalAlignment: Text.AlignVCenterWhat's the correct way to tell Qt that the x and y must be the center of the item/text, rather than the top left of the item?
import QtQuick Item { anchors.fill: parent Repeater { model: 12 Text { property int radius: parent.height / 2 text: index x: radius + radius * Math.cos(2 * Math.PI * index / 12) y: radius + radius * Math.sin(2 * Math.PI * index / 12) verticalAlignment: Text.AlignVCenter } } }
QML Qt 6.5.0 Qt Creator 10.0.1 Ubuntu 22.04.2
Thank you
-- Peter@petero3
I have played with this a bit. My first thought is to rotate an invisible Item and then counter rotate the text:Rectangle { anchors.right: parent.right anchors.bottom: parent.bottom width: 200 height: 200 Repeater { id: clock_repeater model: 12 Item { anchors.horizontalCenter: parent.horizontalCenter property real rotinc: 360/clock_repeater.model rotation: rotinc * (index + 1) height: parent.height width: number_text.width Text { id: number_text rotation: -parent.rotation text: "%1".arg(index + 1) } } } }
This might be expensive. Maybe not if just matrix calculation.
Then I thought about taking a position of rotated object to position the text:
Rectangle { anchors.right: parent.right anchors.bottom: parent.bottom width: 200 height: 200 Repeater { id: clock_repeater model: 12 Item { id: rotate_container anchors.horizontalCenter: parent.horizontalCenter height: parent.height width: number_text.width Item { anchors.fill: parent Rectangle { id: rotate_anchor width: 2 height: 2 border.width: 1 anchors.top: parent.top anchors.topMargin: 10 anchors.horizontalCenter: parent.horizontalCenter } property real rotinc: 360/clock_repeater.model rotation: rotinc * (index + 1) } Item { width: 16 height: 16 property var cpoint: rotate_anchor.mapToItem(rotate_container, rotate_anchor.x, rotate_anchor.y) x: cpoint.x - width/2 y: cpoint.y - height/2 Text { id: number_text anchors.centerIn: parent text: "%1".arg(index + 1) } } } } }
But this seems to have turned into a mess. So I think the counter rotate is probably simpler.
-
@petero3
I have played with this a bit. My first thought is to rotate an invisible Item and then counter rotate the text:Rectangle { anchors.right: parent.right anchors.bottom: parent.bottom width: 200 height: 200 Repeater { id: clock_repeater model: 12 Item { anchors.horizontalCenter: parent.horizontalCenter property real rotinc: 360/clock_repeater.model rotation: rotinc * (index + 1) height: parent.height width: number_text.width Text { id: number_text rotation: -parent.rotation text: "%1".arg(index + 1) } } } }
This might be expensive. Maybe not if just matrix calculation.
Then I thought about taking a position of rotated object to position the text:
Rectangle { anchors.right: parent.right anchors.bottom: parent.bottom width: 200 height: 200 Repeater { id: clock_repeater model: 12 Item { id: rotate_container anchors.horizontalCenter: parent.horizontalCenter height: parent.height width: number_text.width Item { anchors.fill: parent Rectangle { id: rotate_anchor width: 2 height: 2 border.width: 1 anchors.top: parent.top anchors.topMargin: 10 anchors.horizontalCenter: parent.horizontalCenter } property real rotinc: 360/clock_repeater.model rotation: rotinc * (index + 1) } Item { width: 16 height: 16 property var cpoint: rotate_anchor.mapToItem(rotate_container, rotate_anchor.x, rotate_anchor.y) x: cpoint.x - width/2 y: cpoint.y - height/2 Text { id: number_text anchors.centerIn: parent text: "%1".arg(index + 1) } } } } }
But this seems to have turned into a mess. So I think the counter rotate is probably simpler.
Thank you very much!
Ok I can visualise how that works, after temporarily changing Item to Rectangle and adding semi-transparent color to it.
So the anchors.horizontalCenter and width: number_text.width (of the wrapper Item) gets the center of the text in the right place.
And then counter-rotate is exploiting that "The default transform origin is Item.Center." (of the text, so it stays in the right place).
Effective!So I guess there isn't a direct way to tell QML to center an item/text over a specific point?
So rotate/counter-rotate is one workaround. But if the points were along an ellipse or other non-circular curve, then rotate/counter-rotate would be trickier? But I could borrow your idea of a wrapper item, and remember that "The default clip value is false". Then create a wrapper item of size 0 in the desired location and center the text relative to that:
import QtQuick Item { width: 400 height: 400 Repeater { id: clock_repeater model: 12 Item { property int radius: parent.height / 2 x: radius + radius * Math.cos(2 * Math.PI * index / clock_repeater.model) y: radius + radius * Math.sin(2 * Math.PI * index / clock_repeater.model) width: 0 height: 0 Text { text: index anchors.centerIn: parent } } } }
-
Hi
Example: positioning numerals around a clock face.
Using sin and cos to get the x and y is the easy bit.
But the text is appearing below the desired point, rather than centered over it.
Even with
verticalAlignment: Text.AlignVCenterWhat's the correct way to tell Qt that the x and y must be the center of the item/text, rather than the top left of the item?
import QtQuick Item { anchors.fill: parent Repeater { model: 12 Text { property int radius: parent.height / 2 text: index x: radius + radius * Math.cos(2 * Math.PI * index / 12) y: radius + radius * Math.sin(2 * Math.PI * index / 12) verticalAlignment: Text.AlignVCenter } } }
QML Qt 6.5.0 Qt Creator 10.0.1 Ubuntu 22.04.2
Thank you
-- Peter@petero3
I got to thinking about the second attempt I made. I need to set the origin of the object to the center of the object. Then using 0 sized anchor object I can precisely place them without counter rotation. No idea which way is faster though:Rectangle { anchors.right: parent.right anchors.bottom: parent.bottom width: 200 height: 200 Repeater { id: clock_repeater model: 12 Item { id: rotate_container anchors.horizontalCenter: parent.horizontalCenter height: parent.height width: number_text.width Item { anchors.fill: parent Item { id: rotate_anchor anchors.top: parent.top anchors.topMargin: 10 anchors.horizontalCenter: parent.horizontalCenter } property real rotinc: 360/clock_repeater.model rotation: rotinc * (index + 1) } Text { id: number_text transformOrigin: Item.Center property var cpoint: rotate_anchor.mapFromItem(rotate_container, rotate_anchor.x, rotate_anchor.y) x: cpoint.x y: cpoint.y text: "%1".arg(index + 1) } } } }
I try to avoid a lot of Javascript math and get the QML C++ objects to do the math. This is written in C++ and should be faster. I have found some algorithms in Javascript to be slow.
-
Hi
Example: positioning numerals around a clock face.
Using sin and cos to get the x and y is the easy bit.
But the text is appearing below the desired point, rather than centered over it.
Even with
verticalAlignment: Text.AlignVCenterWhat's the correct way to tell Qt that the x and y must be the center of the item/text, rather than the top left of the item?
import QtQuick Item { anchors.fill: parent Repeater { model: 12 Text { property int radius: parent.height / 2 text: index x: radius + radius * Math.cos(2 * Math.PI * index / 12) y: radius + radius * Math.sin(2 * Math.PI * index / 12) verticalAlignment: Text.AlignVCenter } } }
QML Qt 6.5.0 Qt Creator 10.0.1 Ubuntu 22.04.2
Thank you
-- Peter@petero3 You can also simply translate the text item itself.
Or add the translation to the x/y calculation.Item { id: wrapper anchors.fill: parent property int pointSize: 16 anchors.margins: pointSize/2 Repeater { model: 12 Text { id: text property int radius: parent.height / 2 text: index font.pointSize: wrapper.pointSize x: radius + radius * Math.cos(2 * Math.PI * index / 12) y: radius + radius * Math.sin(2 * Math.PI * index / 12) verticalAlignment: Text.AlignVCenter transform: Translate { y: -text.height/2 x: -text.width/2 } } } }
Or use a PathView instead, which centers the element on the point automatically:
PathView { id: pathView anchors.fill: parent property int pointSize: 16 anchors.margins: pointSize/2 model: 12 delegate: Text { id: text font.pointSize: pathView.pointSize text: index } path: Path { startX: pathView.width/2; startY: 0 PathArc { x: pathView.width/2; y: pathView.height radiusX: pathView.width/2; radiusY: pathView.height/2 direction: PathArc.Clockwise } PathArc { x: pathView.width/2; y: 0 radiusX: pathView.width/2; radiusY: pathView.height/2 direction: PathArc.Clockwise } } }
-
@petero3 You can also simply translate the text item itself.
Or add the translation to the x/y calculation.Item { id: wrapper anchors.fill: parent property int pointSize: 16 anchors.margins: pointSize/2 Repeater { model: 12 Text { id: text property int radius: parent.height / 2 text: index font.pointSize: wrapper.pointSize x: radius + radius * Math.cos(2 * Math.PI * index / 12) y: radius + radius * Math.sin(2 * Math.PI * index / 12) verticalAlignment: Text.AlignVCenter transform: Translate { y: -text.height/2 x: -text.width/2 } } } }
Or use a PathView instead, which centers the element on the point automatically:
PathView { id: pathView anchors.fill: parent property int pointSize: 16 anchors.margins: pointSize/2 model: 12 delegate: Text { id: text font.pointSize: pathView.pointSize text: index } path: Path { startX: pathView.width/2; startY: 0 PathArc { x: pathView.width/2; y: pathView.height radiusX: pathView.width/2; radiusY: pathView.height/2 direction: PathArc.Clockwise } PathArc { x: pathView.width/2; y: 0 radiusX: pathView.width/2; radiusY: pathView.height/2 direction: PathArc.Clockwise } } }
Hi @lemons
Thank you very much.
Ok, so PathView automatically spaces the delegate out evenly along the path. Thanks!PS. re "-text.width/2" - yes that is how we normally do it. But just wondered if there was a more QML-ish way. Telling Qt to position using the center, rather than a corner. There is anchors.centerIn and there is "The default transform origin is Item.Center"...