I have a simple "dialog" in qml where the user is supposed to select one image. Images are scaled-down and shown in a GridLayout inside ScrollView. Images should be 1/2 width of the scrollview, there will be descriptions next to it.
What I see is that the vertical spacing is huge and it does not change with resizing the window. It actually seems that the spacing corresponds to full-size (unscaled) images.
The issue changes when I set Layout.preferredHeight: 100 (for example), but then again the spacing is there should the images be smaller than that, and don't grow beyond 100px in height. I would like to solve this without supposing any absolute sizes.
How to fix that?
import QtQuick 2.9
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3
ApplicationWindow{
visible: true
id: root
width: 600
height: 1200
ScrollView {
id: scroll
anchors.fill: parent
ListModel{
id: demosModel
ListElement{ src:"01.jpg"; }
ListElement{ src:"02.jpg"; }
ListElement{ src:"03.jpg"; }
ListElement{ src:"04.jpg"; }
}
GridLayout{
anchors.fill: parent
columns: 1
rowSpacing: 10
Repeater{
model: demosModel
Image {
source: "assets/"+src;
Layout.preferredWidth: .5*scroll.width;
fillMode: Image.PreserveAspectFit;
}
}
}
}
}
In your example:
Image { source: "assets/"+src } will use the real size of the images.
Layout.preferredWidth: .5*scroll.width will limit the width of the area in which the image will be displayed. So the big images will be displayed with a width of 300px (.5*scroll.width) but will conserve their height.
fillMode: Image.PreserveAspectFit; will scale the image uniformly to fit in the 300px. The proportions of the images are now visually OK but the value of the height property remains the same and this is this value which will be used by the layout.
So I think the solution is to force the width (the height will be recalculated) of the element via the sourceSize property:
import QtQuick 2.9
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3
ApplicationWindow{
id: root
width: 600
height: 1200
visible: true
ScrollView {
id: scroll
anchors.fill: parent
ListModel{
id: demosModel
ListElement{ src:"01.jpg"; }
ListElement{ src:"02.jpg"; }
ListElement{ src:"03.jpg"; }
ListElement{ src:"04.jpg"; }
}
GridLayout{
anchors.fill: parent
columns: 1
rowSpacing: 10
Repeater{
model: demosModel
Image {
source: "assets/" + src;
Layout.preferredWidth: .5 * scroll.width;
// Qt documentation: "If only one dimension of the size is set to greater
// than 0, the other dimension is set in proportion to preserve the
// source image's aspect ratio. (The fillMode is independent of this.)"
// fillMode: Image.PreserveAspectFit;
sourceSize.width: Layout.preferredWidth
}
}
}
}
}
Related
When I resize a Qt Quick ApplicationWindow that contains a Frame with an InnerShadow, I see flickering and visual artifacts. The same is not true when I either do not replace the default border or if I use a simple rectangle for the Frame object.
I tested this on my laptop that runs a 64-bit Arch Linux. It has an Nvidia GTX 1060 Max Q graphics card and an integrated Intel graphics card. I ran the code both with and without bumblebee.
Any way to work around or eliminate this flickering? It is pretty bad. My code and some screen-grabs are as below
EDIT: I have tried setting AA_ShareOpenGLContexts and AA_UseOpenGLES (and its software/desktop variants) attributes with no luck.
UPDATE: I have created an issue here: https://bugreports.qt.io/browse/QTBUG-81519, but I am still hoping someone can devise a workaround.
test.qml
import QtQuick 2.14
import QtQuick.Controls 2.14
import QtGraphicalEffects 1.14
ApplicationWindow{
id: main
width: 2*screen.width/3
height: 2*screen.height/3
title: "Test ApplicationWindow"
color: activeColorPalette.window
visible:true
SystemPalette {
id: activeColorPalette
colorGroup: SystemPalette.Active
}
Frame{
anchors.fill: parent
anchors.margins: 10
background: Item{
id: root
anchors.fill: parent
Rectangle{
anchors.fill: parent
anchors.margins: 1
radius: 16
color: activeColorPalette.window
}
InnerShadow {
anchors.fill: root
horizontalOffset: 0
verticalOffset: 0
source: root
radius: 16
color: activeColorPalette.shadow
spread: 0.6
samples: 32
cached: true
fast:true
}
}
}
}
Window without flickering or artifacts
Window with flickering/visual artifacts while resizing
I found a workaround to eliminate the visual artifacts during resizing.
In my problem code, the InnerShadow used an Item QML type as the source, which is transparent by default and contained a grey Rectangle that I added within it. The visual distinction between the transparent source Item and the smaller child Rectangle inside it is what the InnerShadow uses to compute the shadow gradient within. The end result was a decorative shadow border. However, resizing the application resulted in ugly visual artifacts that would sometimes stay. Note: Changing the outer Item into a transparent Rectangle had no discernible effect.
But when I encapsulated the grey innermost rectangle into another transparent component like
Item {transparent Rectangle {grey inner Rectangle} }
or like
Rectangle{transparent Rectangle{grey inner Rectangle}}
in addition to setting the middle transparent Rectangle as the source for the InnerShadow, the visual artifacts are eliminated. Below is the working code for test_workaround.qml that you can compare to the test.qml above.
test_workaround.qml
import QtQuick 2.14
import QtQuick.Controls 2.14
import QtGraphicalEffects 1.14
ApplicationWindow{
id: main
width: 2*screen.width/3
height: 2*screen.height/3
title: "Test ApplicationWindow"
color: activeColorPalette.window
visible:true
SystemPalette {
id: activeColorPalette
colorGroup: SystemPalette.Active
}
Frame{
anchors.fill: parent
anchors.margins: 10
background: Item{
id: root
anchors.fill: parent
Rectangle{
id: middleRect
anchors.fill: parent
color: "transparent"
Rectangle{
id: innerRect
anchors.fill: parent
anchors.margins: 1
radius: 16
color: activeColorPalette.window
}
}
InnerShadow {
anchors.fill: root
horizontalOffset: 0
verticalOffset: 0
source: middleRect
radius: 16
color: activeColorPalette.shadow
spread: 0.6
samples: 32
cached: true
fast:true
smooth:true
}
}
}
}
Target's iOS and macOS as well.
I have this very general code to draw an Image and some text:
let dynamicScale = 1.2 // here just static but will be !!calculated!!
...
VStack (alignment: .leading){
Image(user.imageName)
.resizable()
.frame(width: 60, height: 60)
.clipped()
.clipShape(Circle())
.overlay(Circle()
.scale(dynamicScale) // with some calculation
.stroke(Color.red, lineWidth: 2))
Text(user.username).font(.headline)
Text(user.message).font(.subheadline)
}.padding(.init(top: 10, leading: 5, bottom: 10, trailing: 5))
}
for now the size is static, but later, its size will be depend on-screen size and rotation. So will the stroke be depended of that, size and some more settings.
How can I get dynamically the circle size ( frame ) in SwiftUI, so I can us it to draw a second Circle() stroke/border at a offset of the Image.
I want .scale(dynamicScale) dynamically calculated depending of the size of the image
As far as I understand I can't insert any Swift code to read that size, like 'let size = Image().frame.width'. Logical, there are no values there yet.
Somebody has some suggestions?
Here is an interactive example using GeometryReader.
GeometryReader takes up all the space available to it, which seems to be the size of the view it is overlaying if used inside of the overlay modifier.
You can then read the properties of the GeometryProxyobject to get details regarding the size of the space it is taking up and use that to size your views inside of the GeometryReader.
In this case we have a stroke circle that is dynamically sized based on a slider, with another circle overlayed that is always half the size of the first circle.
import SwiftUI
struct RelativeSizeView: View {
#State var dynamicSize: CGFloat = 100
var body: some View {
VStack(spacing: 20) {
Slider(value: $dynamicSize, in: 100...1000)
Circle() // dynamically sized circle
.stroke()
.frame(width: dynamicSize, height: dynamicSize)
.overlay(GeometryReader{ geometry in
Circle() // sized based on first
.frame(width: geometry.size.width*0.5, height: geometry.size.height*0.5)
})
}.padding()
}
}
struct RelativeSizeView_Previews: PreviewProvider {
static var previews: some View {
RelativeSizeView()
}
}
I'm trying to understand how anchors work in QML (Qt Quick 2.0). I've got a simple Item like this:
AddButton.qml:
Item {
Button {
text: "ADD"
width: 100
height: 50
}
}
Which I add to the main QML file like this:
main.qml:
Item {
id: root
width: 800
height: 600
AddButton {
id: addButton
}
}
This works fine. However, as soon as I try to put the button in the bottom right corner using anchors, the button disappears:
main.qml:
Item {
.....
AddButton {
id: addButton
anchors.right: parent.right
anchors.bottom: parent.bottom
}
}
It only comes back if I set a width and height at the main QML file level:
main.qml:
Item {
.....
AddButton {
id: addButton
width: 100
height: 50
anchors.right: parent.right
anchors.bottom: parent.bottom
}
}
So I'm wondering, why does the button disappear when I set the anchors? And is there any way to make it work without setting the width and height in the main QML file (basically to make it use whatever size is set in AddButton.qml?)
The problem is that the encapsulating Item has not an explicit width height. In this case the engine refers to the "natural" witdh/height, i.e. the implicitWidth/implicitHeight properties. Such properties happen to be zero in most cases, even in this specific case. Hence, your custom type has zero dimension.
Therefore the AddButton.anchors.bottom is in fact at the very top of the encapsulated Button, which in turn protrudes the encapsulating Item
There are two things about this:
You don't need to encapsulate the Button with an Item unless you want to hide the internals of the Button.
If the latter is your desire, try this:
Item {
width: 100 //give the object a dimension!
height: 50
Button {
text: "ADD"
anchors.fill: parent
}
}
Now you can anchor it, and it won't be positionated somewhere else.
I've tried finding the solution to my problem but came up with nothing, so forgive me if this has been covered elsewhere.
I'm using Masonry for a client's site that I'm currently developing. In any viewport size that is 768px or wider, there are two element widths: 20% and 40%; when sizing down to mobile, i.e. up to 767px, the element sized at 20% becomes 50% and the one at 40% becomes 100%.
The problem is that, in mobile view, Masonry doesn't always put two of the 50% elements into a row, so the grid becomes broken up.
Here's a link to the dev site and Masonry grid: http://176.32.230.48/maxence.io/#work
Here's my CSS for the two different grid-item sizes:
.grid-item,
.grid-sizer {
width: 20%;
#include media(xs-max) {
width: 50%;
}
}
.grid-item-lg {
width: 40%;
#include media(xs-max) {
width: 100%;
}
}
Here's the relevant block of jQuery:
$container.masonry({
itemSelector: '.grid-item',
columnWidth: '.grid-sizer',
percentPosition: true
});
I'm also using the Masonry layout method on window resize:
var windowWidth = $(window).width();
if (windowWidth < 768) {
$('#grid').masonry('layout');
}
Anybody have any ideas?
I have to perform a very simple task: I want to display a piece of text inside a rectangle and the size of that rectangle should precisely be the width of the text.
In C++, it's fairly easy to do. Just define the QString and apply the QFontMetrics to get its width. Then define the rectangle graphics element to have that size. It's done within five minutes.
I have heard that QML is easier to use. Therefore, I was expecting to solve that problem in less than five minutes. I didn't, and I'm still stuck at it. Here's what I have tried:
Rectangle {
width: myText.contentWidth
height: myText.contentHeight
Text {
anchors.fill:parent
id: myText
font.family: "Helvetica"
font.pointSize: 50
text: qsTr("The string I want to display")
}
}
This doesn't work for some reason I don't understand. I have found a way to do it in a way that doesn't exactly suits my needs:
Rectangle {
width: 100
height: 100
MouseArea {
id: myMouseArea
anchors.fill: parent
onClicked: parent.width=myText.contentWidth
hoverEnabled: true
}
Text {
anchors.fill:parent
id: myText
font.family: "Helvetica"
font.pointSize: 50
text: qsTr("The string I want to display")
}
}
In this case, when I click the rectangle, it gets the correct width. Nevertheless, I am not interested in this solution, because I don't want to have to click to get a rectangle with the correct size.
I want that the rectangle's size gets the correct size whenever myText changes text. The use of onTextChanged in the Text item doesn't work either.
What am I missing here?
As far as I know, Font metrics were made available to developers in Qt 5.4, so they are relatively new, in QML. You got mainly FontMetrics and TextMetrics. A simple usage example:
import QtQuick 2.4
import QtQuick.Window 2.2
Window {
visible: true
width: 280; height: 150
TextMetrics {
id: textMetrics
font.family: "Arial"
font.pixelSize: 50
text: "Hello World"
}
Rectangle {
width: textMetrics.width
height: textMetrics.height
color: "steelblue"
Text {
text: textMetrics.text
font: textMetrics.font
}
}
}
As noted by Phrogz in the comment below, the TextMetrics type does not support measuring wrapped text.
EDIT
For what is worth I've never ever had the need to use metrics in QML. For me content* or painted* properties served the purpose and, as of Qt 5.12, they seem to work fine. Aka the following two solutions generate the correct visual behaviour:
// solution 1
Rectangle {
width: myText.contentWidth
height: myText.contentHeight
Text {
anchors.fill:parent
id: myText
font.family: "Helvetica"
font.pointSize: 50
text: qsTr("The string I want to display")
}
}
// solution 2
Rectangle {
width: myText.paintedWidth
height: myText.paintedHeight
Text {
anchors.fill:parent
id: myText
font.family: "Helvetica"
font.pointSize: 50
text: qsTr("The string I want to display")
}
}
I would prefer those solutions to the usage of metrics for such a simple use case as the one proposed by the OP. For the opposite case - fitting a text in a specific size - a combination of properties can do the trick, e.g.:
Rectangle {
anchors.centerIn: parent
width: 200
height: 30
Text {
anchors.fill: parent
text: "Wonderful Text"
minimumPixelSize: 2
fontSizeMode: Text.Fit
font.pixelSize: 200
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
}
Here the pixel size is simply over the top but the text still fits because a minimum size of 2 is set and the text has a clear fitting policy and clear boundaries, defined by the anchoring.
I'm sure Label component will do the job:
import QtQuick 2.1
import QtQuick.Controls 2.4
ApplicationWindow {
visible: true
Column {
Repeater {
model: [
{"color": "red", "radius": 1},
{"color": "green", "radius": 2},
{"color": "blue", "radius": 3}
]
Label {
padding: 0
text: modelData.color
font.family: "Helvetica"
font.pointSize: 50
background: Rectangle {
color: modelData.color
radius: modelData.radius
}
}
}
}
}
You don't need to use anchors.fill: parent for Text item because size of Text's parent depends on size of Text itself. It's cause binding loop.
This must works fine.
Rectangle {
width: myText.contentWidth
height: myText.contentHeight
Text {
id: myText
font.family: "Helvetica"
font.pointSize: 50
text: qsTr("The string I want to display")
}
}