How to space vertically QML objects in a Flickable using anchors?
The code snippet (reproduced below in this message) is supposed to display texts and images supplied by a C++ model (the C++ object is named Global in this snippet).
I get an error message on the line:
anchors { top: _lblMsg1.bottom+8; }
You get the idea of what I'm trying to do: I want the top of the image to be 8 pixels lower than the bottom of the text above it.
I get an error message, though it seems to be working.
Could you tell me, please, how I can do this correctly?
<...>
Flickable {
anchors.fill: parent;
Label {
id: _lblMsg1;
text: Global.dataMessages.byId(Global.currService.msg_text1_id);
anchors { top: parent.top; left:parent.left; right: parent.right; }
}
Image {
id: _imgUri1;
cache: false;
source: (Global.currService && Global.currService.img_uri1 ? Global.currService.img_uri1 : "");
asynchronous: true;
anchors { top: _lblMsg1.bottom+8; left:parent.left; right: parent.right;}
horizontalAlignment: Image.AlignHCenter;
fillMode: (Image.width > 400 ? Image.Stretch : Image.Pad);
}
<...>
}
Anchors only accept other anchor lines for their values. However, you can use a margin to adjust the anchoring like so:
anchors {
top: _lblMsg1.bottom;
topMargin: 8;
left:parent.left;
right: parent.right;
}
Related
I have a ListView in my qml file, but when I am overscrolling it, it gets stuck for a several seconds and then flicks back. Does anyone know what can be the reason of that delay before flicking back?
Also, it's only freezes, when the text inside my delegates is on Russian language. On english it works fine
Upd:
There is nothing specific, but minimal, reproducible example:
ListView {
id: list
anchors {
right: parent.right
top: parent.top
}
height: parent.height
width: 502
interactive: height < contentHeight
spacing: 24
currentIndex: -1
footer: Item {
height: 100
}
delegate: Item {
anchors {
left: list.contentItem.left
right: list.contentItem.right
leftMargin: 40
rightMargin: 40
}
implicitHeight: Math.max(image.height, description.implicitHeight)
// Some other code
}
ScrollBar.vertical: OknaGui.ScrollBar { id: scrollBar } // Just a scrollbar
}
This might not be the proper solution, but you can disable overscroll at all with boundsBehavior: Flickable.StopAtBounds
Toolseparator is not working inside repeater. I need to show the image and text under that one line after each content, so to show the line i have used tool separator but it's not working, it just override all the content by showing only lines. Here is the example
Repeater {
model: 10
Row {
leftPadding: 10
spacing: 10
Rectangle {
height: 100
width: 200
}
Text {
text: "Username"
}
ToolSeparator {
height: 25
width: 335
orientation: Qt.Horizontal
}
}
}
You've done few mistakes, my friend.
If you are going to use Repeater -- typically you should use it inside of some of the positioning components (e.g. Column, Row). So in your case, Repeater should be inside Row. By the way, never neglect to review official Qt documentation: Repeater Detailed Description; Using QML Positioner and Repeater Items. In addition to this there is great learning source QmlBook, and for example its Quick Starter.
If you want to show your components one under another -- you should use Column component, and not Row.
If I understand you correctly you are going to get something like this:
Use this code as a reference:
Column {
spacing: 10
Repeater {
model: 4
Rectangle {
id: background
height: 50
width: 200
color: "bisque"
Text {
id: text
color: "chocolate"
text: "Username " + modelData
anchors.centerIn: parent
}
Rectangle {
id: separator
anchors {
bottom: parent.bottom
left: parent.left
right: parent.right
}
height: 5
color: "goldenrod"
}
}
}
}
Is there any possible way to make grabToImage() buffer different from what's displayed in GUI? I'm using ChartView without legend in GUI and want to export it to PNG with legend. So I try:
chartView.legend.visible = true
chartView.update()
chartView.grabToImage(function(result) {
console.log("grabbed")
var path = filename.toString().replace(/^(file:\/{2})/,"");
console.log(path + result.saveToFile(path));
chartView.legend.visible = false
update();
});
But both those updates happen only after the control comes out of this function, so I don't get legend drawn in PNG. Also I would like the appearance of legend to be unnoticable to user. Are there any ways to do that in QML?
I am unsure, whether I got you quite right.
My suggestion is, to not grab the Item you display, but a copy of it, that you then modify as you like.
Copy here does not mean, that you have the same object twice, but that you render it twice by using a ShaderEffectSource. Before you grab this ShaderEffectSource to image, you can add anything you like.
In my example I display a simple Rectangle with a nice gradient. What I save is the same Rectangle that is extended by the Text 'I am legend'. The user won't see this text apearing in the view at any time.
Rectangle {
id: commonView
width: 200
height: 200
gradient: Gradient {
GradientStop { position: 0; color: 'steelblue' }
GradientStop { position: 1; color: 'orange' }
}
MouseArea {
anchors.fill: parent
// don't grab the rectangle itself.
onClicked: legendView.grabToImage(function(result) {
console.log(result.saveToFile("something.png"));
});
}
}
ShaderEffectSource {
id: legendView
visible: false // Does not need to be shown.
sourceItem: commonView
width: 200
height: 200
Text {
anchors {
right: parent.right
bottom: parent.bottom
}
text: 'I am legend'
}
}
You might optimize the performance by only having the ShaderEffectSource active or even created when needed.
You might use the ShaderEffectSource.live-property to disable updating of it. Then use scheduleUpdate() to trigger the update.
This might look like this:
Rectangle {
id: commonView
width: 200
height: 200
gradient: Gradient {
GradientStop { position: 0; color: 'steelblue' }
GradientStop { id: gs1; position: 1; color: 'orange' }
}
MouseArea {
anchors.fill: parent
onClicked: {
gs1.position -= 0.1
legendView.save()
}
}
}
ShaderEffectSource {
id: legendView
y: 200
visible: false // Do not render it (will only be rendered when called grapToImage()
sourceItem: commonView
width: 200
height: 200
live: false // Will only be updated, when explicitly called for
function save() {
console.log('Schedule Save')
scheduleUpdate() // explicitly update. grabToImage() will force rendering afterwards.
legendView.grabToImage(function(result) {
console.log(result.saveToFile("something" +gs1.position.toFixed(1) + ".png"));
})
}
// Put additional stuff on it.
Text {
anchors {
right: parent.right
bottom: parent.bottom
}
text: 'I am legend!'
}
}
Let's assume I have a component like this:
Rectangle {
id: rec1
Rectangle {
id: rec
x:10;y:100; height:100;
color: 'red'
anchors {
left: parent.left
right: parent.right
}
Loader {
id:loader
width: parent.width
sourceComponent: comp
}
}
Component {
id: comp
Rectangle {
anchors {
left: parent.left
leftMargin: 1 // this line
right: parent.right
}
height:img.height
Image {
id: img
source: '/myimage.png'
width: parent.width
fillMode: Image.PreserveAspectFit
}
}
}
}
the console reports:
QML Image: Binding loop detected for property "width"
But if I change the comp leftMargin to leftMargin: 0 (not 1), everything is ok
Is this a bug or some feature I am missing?
In my opinion you made a typo:
width:100; height:100;
...
anchors {
left: parent.left
right: parent.right
}
By doing this you:
Explicitly set your rectangles width (to 100).
Do explicitly anchoring of left side to parents left side (if this is only one anchoring operation -- it is Ok) and then -- do anchoring of right side to parent's right side (so you are implicitly defining your rectangle's width).
It is contradiction. Try to solve it (for example, by removing right anchoring) and re-run your application.
I need to share equally the horizontal space between all "buttons" in my Row.
I use this code with a Repeater.
Component {
id: buttonComponent
Rectangle {
height: buttonRow.height
width: buttonRow.width / buttonsRepeater.count
color: "#FFDDDD"
Text {
anchors.centerIn: parent
text: model.text
}
}
}
Rectangle {
color: "#DDDDDD"
id: buttonBar
height: 30
anchors {
bottom: parent.bottom
left: parent.left
right: parent.right
}
Row {
id: buttonRow
anchors.fill: parent
Repeater {
id: buttonsRepeater
model: buttonsModel
delegate: buttonComponent
}
}
}
Now, I like to compute the ideal width of the Row such that all my button texts appear correctly.
How can I get this ideal width?
If you don't want to use QtQuick.Layouts as they are not really ready yet, you can use this :
Rectangle {
id: buttonBar;
color: "#DDDDDD";
height: 30;
width: (buttonColumn.width + 20 + buttonRow.spacing) * buttonsRepeater.count;
anchors {
bottom: parent.bottom;
left: parent.left;
}
Column {
id: buttonColumn;
visible: false;
Repeater {
model: buttonsModel;
delegate: Text {
text: model.text;
}
}
}
Row {
id: buttonRow;
anchors.fill: parent;
property real itemWidth : ((width + spacing) / buttonsRepeater.count) - spacing;
Repeater {
id: buttonsRepeater;
model: buttonsModel;
delegate: Component {
id: buttonDelegate;
Rectangle {
height: parent.height;
width: parent.itemWidth;
color: "#FFDDDD";
border.width: 1;
Text {
anchors.centerIn: parent;
text: model.text;
}
}
}
}
}
}
I just used a hidden Column to easily compute max width of Text elements, and added a little padding in the bar width to avoid unspaced text.
The minimum width of a button itself is the implicitWidth property of its Text element.
One solution to your problem might be to add code in the Component.onCompleted handler, i.e. code that is executed after the repeater has created its items, and then sum up these implicitWidth properties of each of the repeater's item (which you can get by using its itemAt(index) function).
These kinds of dynamic layout is a bit cumbersome in QML still, which will get much better in Qt 5.1 with the introduction of Qt Quick Layouts