I'm making a vehicle's instrument cluster in QML and wanted to align the speed labels to the curve in such a way that they are all the same distance away from it. To do this, I used pathview to set up an arc, then used the text delegate to have the text appear in the right places. The reason it's not working exactly as intended is that some text is larger than other (for example '0' takes less space than '100') which causes some variability in distance between the text and the circle I'm trying to align it with. How can this be done better?
Here's the snippet of what I have got working so far:
Component {
id: spdLabel
Text {
font.weight: Font.Normal
font.pixelSize: 28
color: "white"
text: model.index
visible: model.index % spdMarkInterval == 0
}
}
PathView {
anchors.fill: parent
z: 0
model: topSpeed + 1
delegate: spdLabel
path: Path {
PathAngleArc {
centerX: (dialGauge.x + dialGauge.width)/2
centerY: (dialGauge.y + dialGauge.height)/2
radiusX: 140
radiusY: 140
sweepAngle: 270
startAngle: 135
}
}
}
Here's what the result looks like:
Since it is the center of text elements that's anchored a constant distance away from the arc, the distance between the text edge and arc varies.
Thanks for any help!
I'm using Qml 5.12 and basically trying to set an implicitWidth to a GridLayout.
For that, I have a purple rectangle and set the rectangle's width to the GridLayout.
The red rectangle fit with the GridLayout so I can see the width of my GridLayout.
Here's my code:
Rectangle { anchors.fill: gl; color: "red"; opacity: 0.22 }
Rectangle { id: rect; width: 350; height: 30; color: "purple"; }
GridLayout
{
id: gl
y: 35
implicitWidth: rect.width
columns: 2
Label { text: "This is a test" }
SpinBox { Layout.alignment: Qt.AlignRight }
}
If I run the code, I expect to have my both rectangle with the same width.
But the actual result is that the red rectangle is smaller. So the implicitWidth was not considerate.
Can anybody tell my why ?
Thank's !
The GridLayout compute its own implicitWidth based on its children's implicitWidth. So the value you set gets overwritten by the computed one.
implicitWidth is the width an Item wants to have (and the one it would have if no width is explicitely set). Setting it based on something else than its children or some internal value makes little sense.
Here you want the GridLayout to be the exact size of your Rectangle so just set its width property.
I'm trying to get something in a ScrollView to expand in width to fit the screen. The ScrollView is anchored to the main window.
For example purposes, a Rectangle:
ScrollView {
anchors.fill: parent //mainWindow
Rectangle {
color: "light grey"
height: 1000
width: mainWindow.width
}
}
But when the vertical scrollbar appears, it obscures the Rectangle. I can sort of fix it by using a magic constant:
width: mainWindow.width - 20
But what if somebody has bigger scrollbars on their computer? Also it leaves an ugly empty space on the right when the vertical scrollbar is invisible.
Is there a way to automatically learn what the available space is inside of a ScrollView?
There is no need to explicitly adjust to scroll bar. You can just make it to fill the entire available parent space or so. And if you want specify margins:
ScrollView {
id: scrollView
anchors.fill: parent // mainWindow ?
anchors.centerIn: parent // anchoring as asked
anchors.margins: 20
contentItem:
Rectangle {
id: rectScroll
width: scrollView.viewport.width // set as viewport
height: 1000 // set to what you need
}
}
The original issue was solved mainly due to the width property of Rectangle set to parent.parent.width or scrollView.viewport.width as it is more adequate. The latter is definitely better, as long as the width of precisely viewport of scroll area and not the parent width (which in general not guaranteed to contain only this ScrollView).
I want to create a custom drop-down box with text inside. The problem is, when I resize my Rectangle to fold it the Text stays on screen.
Rectangle {
id: dropdown
height: 200
width: 200
color: "red"
Behavior on height {
NumberAnimation {
duration: 1000;
easing.type: Easing.InQuad
}
}
Text {
id: text
anchors.left: parent.left
anchors.top: parent.top
text: "foobar"
}
}
How to solve this?
Ok. I have it thanks to jbache.
I need to put clip:true inside dropdown. According to the documentation of clip:
This property holds whether clipping is enabled. The default clip value is false.
If clipping is enabled, an item will clip its own painting, as well as the painting of its children, to its bounding rectangle.
Hence, by setting the property to true, I can ensure that also the child Text will be correctly hidden on drop-down dismiss.
To my surprise, the Image component has no radius property. I tried emulating the rounded corners by putting the image in a rounded Rectangle, but it does not clip the corners.
Rectangle {
anchors.right: rectContentBg.left
anchors.top: rectContentBg.top
anchors.margins: 8
radius: 8
width: 64
height: 64
Image {
id: imgAuthor
opacity: 1
smooth: false
anchors.fill: parent
source: "qrc:/res/sample_avatar.jpg"
}
}
How can I create an image with rounded corners properly?
A built-in official solution exists as of Qt 5 thanks to the QtGraphicalEffects module and I'm quite surprised to find out that no one provided such a simple solution. If you are targeting Qt 6.x QtGraphicalEffects is unfortunately deprecated so jump to the second part of the answer which proposes a solution independent of QtGraphicalEffects.
QtGraphicalEffects solution
Among the other effects OpacityMask is the type to be exploited for this purpose. The idea is to mask the source Image with a Rectangle that has a correctly set radius. Here goes the simplest example using layering:
Image {
id: img
property bool rounded: true
property bool adapt: true
layer.enabled: rounded
layer.effect: OpacityMask {
maskSource: Item {
width: img.width
height: img.height
Rectangle {
anchors.centerIn: parent
width: img.adapt ? img.width : Math.min(img.width, img.height)
height: img.adapt ? img.height : width
radius: Math.min(width, height)
}
}
}
}
This minimum code produces a nice result for square images but
it also takes in account non-square images via the adapt variable. By setting the flag to false the produced mask will always be a circle, regardless of the image size. That is possible due to the usage of an external Item which fills the source and allows the real mask (the inner Rectangle) to be sized at please. You can obviously get rid of the external Item, if you simply aim to a mask that fills the source, regardless of the its aspect ratio.
Here is a cute cat image with a square format (left), a non-square format with adapt: true (center) and finally a non-square format and adapt: false (right):
The implementation details of this solution are very similar to those of the shader-based answer in the other nice answer (cfr. the QML source code for OpacityMask that can be found here - SourceProxy simply returns a well-formed ShaderEffectSource to feed the effect).
No-dep solution
If you don't want to - or can't - depend on the QtGraphicalEffects module (well, on the presence of OpacityMask.qml actually), you can reimplement the effect with shaders. Apart from the already provided solution another approach is to use step, smoothstep and fwidth functions. Here is the code:
import QtQuick 2.5
Image {
id: image
property bool rounded: true
property bool adapt: true
layer.enabled: rounded
layer.effect: ShaderEffect {
property real adjustX: image.adapt ? Math.max(width / height, 1) : 1
property real adjustY: image.adapt ? Math.max(1 / (width / height), 1) : 1
fragmentShader: "
#ifdef GL_ES
precision lowp float;
#endif // GL_ES
varying highp vec2 qt_TexCoord0;
uniform highp float qt_Opacity;
uniform lowp sampler2D source;
uniform lowp float adjustX;
uniform lowp float adjustY;
void main(void) {
lowp float x, y;
x = (qt_TexCoord0.x - 0.5) * adjustX;
y = (qt_TexCoord0.y - 0.5) * adjustY;
float delta = adjustX != 1.0 ? fwidth(y) / 2.0 : fwidth(x) / 2.0;
gl_FragColor = texture2D(source, qt_TexCoord0).rgba
* step(x * x + y * y, 0.25)
* smoothstep((x * x + y * y) , 0.25 + delta, 0.25)
* qt_Opacity;
}"
}
}
Similarly to the first approach, rounded and adapt properties are added to control the visual appearance of the effect as discussed above.
This code would help you
Rectangle {
width: 200
height: 200
color: "transparent"
//this Rectangle is needed to keep the source image's fillMode
Rectangle {
id: imageSource
anchors.fill: parent
Image {
anchors.fill: parent
source: "your_image_file_path"
fillMode: Image.PreserveAspectCrop
}
visible: false
layer.enabled: true
}
Rectangle {
id: maskLayer
anchors.fill: parent
radius: parent.width / 2
color: "red"
border.color: "black"
layer.enabled: true
layer.samplerName: "maskSource"
layer.effect: ShaderEffect {
property var colorSource: imageSource
fragmentShader: "
uniform lowp sampler2D colorSource;
uniform lowp sampler2D maskSource;
uniform lowp float qt_Opacity;
varying highp vec2 qt_TexCoord0;
void main() {
gl_FragColor =
texture2D(colorSource, qt_TexCoord0)
* texture2D(maskSource, qt_TexCoord0).a
* qt_Opacity;
}
"
}
}
// only draw border line
Rectangle {
anchors.fill: parent
radius: parent.width / 2
border.color: "black"
border.width: 2
color: "transparent"
}
}
When your background is a solid color or when you're never moving the image, a fast way to make rounded corners is to overlap your Image with another one (or with a BorderImage) that only draws the corners.
When this is not an option, but you are using OpenGL, then another way is to apply a mask to the image through a pixel shader. See http://blog.qt.digia.com/blog/2011/05/03/qml-shadereffectitem-on-qgraphicsview/ for a plugin that works on top of Qt 4.
Finally, it's also possible to write a QDeclarativeImageProvider that preprocesses your image to make the corners rounded.
If you have a unicolor background, you can draw with the border of a rounded rectangle on top.
Image{
id:img
}
Rectangle { // rounded corners for img
anchors.fill: img
color: "transparent"
border.color: "blue" // color of background
border.width: 4
radius: 4
}
While both the accepted answer and the one from #fury worked equally well for me (Qt 5.9.3), they both left some aberrations in the corners when applied to raster images (didn't have those with SVG). What worked best for me in all cases was to apply the OpacityMask to a surrounding item, e.g. like the rectangle in the original post.
Rectangle {
id: root;
anchors.right: rectContentBg.left
anchors.top: rectContentBg.top
anchors.margins: 8
radius: 8
width: 64
height: 64
// apply rounded corners mask
layer.enabled: true
layer.effect: OpacityMask {
maskSource: Rectangle {
x: root.x; y: root.y
width: root.width
height: root.height
radius: root.radius
}
}
Image {
id: imgAuthor
opacity: 1
smooth: false
anchors.fill: parent
source: "qrc:/res/sample_avatar.jpg"
}
}
QML currently supports only rectangular clipping, but you might want to take a look at DeclarativeMaskedImage in qt-components project:
http://qt.gitorious.org/qt-components/qt-components/blobs/master/src/symbian/sdeclarativemaskedimage.h
I know I'm a little late to the party, but I got here by googling, so thought I'd help future generations :) QtGraphicalEffects OpacityMask should do this a bit more simply (I had issues with the layer effect approach)
Image {
id: imgAuthor
width: 64
height: 64
source: "qrc:/res/sample_avatar.jpg"
visible: false // this is needed or the corners of the image will be visible underneath the opacity mask
}
OpacityMask {
anchors.fill: imgAuthor
source: imgAuthor
maskSource: Rectangle {
width: imgAuthor.width
height: imgAuthor.height
radius: 8
visible: false // this also needs to be invisible or it will cover up the image
}
}
If you have a unicolor background, you can draw with the border of a rounded rectangle on top.
Item {
property int radius: 0
property color bgColor: "#000000"
property int drawRadius: radius > 0 ? radius : width/2
Image {
anchors.fill: parent
sourceSize: Qt.size(width, height)
asynchronous: true
}
Canvas {
anchors.fill: parent
antialiasing: true
onPaint: {
var ctx = getContext("2d")
ctx.fillStyle = bgColor
ctx.beginPath()
ctx.rect(0, 0, width, height)
ctx.fill()
ctx.beginPath()
ctx.globalCompositeOperation = 'source-out'
ctx.roundedRect(0, 0, width, height, drawRadius, drawRadius)
ctx.fill()
}
}
}