Binding loop on ListView delegate - qt

I have this code , which gives me this error:
file:///home/user/qmltests/bindingheight.qml:29:5: QML Pane: Binding loop detected for property "height"
Why would it produce the loop ? And how do I fix this?
import QtQuick 2.11
import QtQuick.Controls 2.4
import QtQuick.Layouts 1.11
ListView {
width: 300
height: 600
clip: true
model: ListModel {
id: fruitModel
ListElement {
name: "Apple Apple Apple Apple Apple Apple Apple Apple Apple Apple Apple Apple"
cost: 2.45
}
ListElement {
name: "Orange Orange Orange Orange Orange Orange Orange Orange Orange Orange"
cost: 3.25
}
ListElement {
name: "Banana Banana Banana Banana Banana Banana Banana Banana Banana Banana"
cost: 1.95
}
}
delegate: Item {
width: 200
height: childrenRect.height * 1.1
Pane {
height: contentItem.childrenRect.height
anchors.left: parent.left
anchors.right: parent.right
anchors.bottomMargin: 20
padding: 10
background: Rectangle { anchors.fill: parent; color: "gray";}
MouseArea {
anchors.fill: parent
onClicked: {
console.log("Executing action on the item")
}
}
ColumnLayout {
anchors.top: parent.top
anchors.left:parent.left
anchors.right: parent.right
Label {
id: item_to_hide
text: model.name
wrapMode: Label.WordWrap
Layout.maximumWidth: parent.width
}
Label {
text: model.cost
}
Button {
text: " Hide element "
onClicked: {
item_to_hide.visible=false
}
}
Button {
text: " Show element "
onClicked: {
item_to_hide.visible=true
}
}
}
}
}
}
This is with Qt 5.11, testing was done with qmlscene, just copy/paste and feed to the bin/qmlscene to reproduce the error.

Using the same code you wrote just change height of Pane to implicitHeight. That will break the loop created by height.
Pane {
implicitHeight: contentItem.childrenRect.height
anchors.left: parent.left
anchors.right: parent.right
anchors.bottomMargin: 20
padding: 10
background: Rectangle { anchors.fill: parent; color: "gray";}
MouseArea {
anchors.fill: parent
onClicked: {
console.log("Executing action on the item")
}
}
...

Use IDs to identify your components instead of using children*/content* properties. Your code will be easier to understand for you and for QML. This works for me:
import QtQuick 2.11
import QtQuick.Controls 2.4
import QtQuick.Layouts 1.11
ListView {
width: 300
height: 600
clip: true
model: ListModel {
id: fruitModel
ListElement {
name: "Apple Apple Apple Apple Apple Apple Apple Apple Apple Apple Apple Apple"
cost: 2.45
}
ListElement {
name: "Orange Orange Orange Orange Orange Orange Orange Orange Orange Orange"
cost: 3.25
}
ListElement {
name: "Banana Banana Banana Banana Banana Banana Banana Banana Banana Banana"
cost: 1.95
}
}
delegate: Item {
width: 200
height: panneau.height * 1.1
Pane {
id: panneau
height: clayout.height
anchors.left: parent.left
anchors.right: parent.right
anchors.bottomMargin: 20
padding: 10
background: Rectangle {
anchors.fill: parent
color: "gray"
}
MouseArea {
anchors.fill: parent
onClicked: console.log("Executing action on the item")
}
ColumnLayout {
id: clayout
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
Label {
id: item_to_hide
text: model.name
wrapMode: Label.WordWrap
Layout.maximumWidth: parent.width
}
Label {
text: model.cost
}
Button {
text: " Hide element "
onClicked: item_to_hide.visible = false
}
Button {
text: " Show element "
onClicked: item_to_hide.visible = true
}
}
}
}
}

Related

How to position one label at the end of another label and center both of them

Basically I want to display a text telling the user if a switch is turned on or off. The words "on" and "off" have a small background associated with them so I thought maybe making two labels would work. Now I have an issue where I'm hard coding the position of the second label, and it doesn't look clean because both of them are not centered. Here is the code:
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Window 2.0
Window {
visible: true
width: 400
height: 400
property bool switchOn: false
Rectangle {
color: '#D3D3D3'
anchors.fill: parent
Label {
id: sentence
text: qsTr("The switch is currently turned ")
color: 'black'
width: parent.width
wrapMode: Text.Wrap
font.pixelSize: 40
anchors.top: parent.top
Label {
id: endText
text: switchOn ? qsTr(" off ") : qsTr(" on ")
font.pixelSize: 40
color: "white"
anchors {
top: parent.top
left: parent.left
topMargin: 50
leftMargin: 310
}
// #disable-check M16
background: Rectangle {
color: switchOn ? "grey" : "red"
radius: 8
}
}
}
Switch {
id: switch1
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
anchors.topMargin: 150
text: qsTr("Switch")
onToggled: switchOn = !switchOn
}
}
}
How can I have that sentence appear as one sentence and center it within a parent object?
EDIT:
Here I can get them in a row, but if my sentence ends on the second row and my first row is longer than the first, the ending text is placed way further than it needs to be:
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Window 2.0
Window {
visible: true
width: 400
height: 400
property bool switchOn: false
Rectangle {
color: '#D3D3D3'
anchors.fill: parent
Row {
anchors.horizontalCenter: parent.horizontalCenter
Label {
id: sentence
text: qsTr("The switch is currently ")
color: 'black'
width: 300
wrapMode: Text.Wrap
font.pixelSize: 40
anchors.top: parent.top
}
Label {
id: endText
text: switchOn ? qsTr(" off ") : qsTr(" on ")
font.pixelSize: 40
color: "white"
anchors.top: sentence.top
anchors.topMargin: 45
// #disable-check M16
background: Rectangle {
color: switchOn ? "grey" : "red"
radius: 8
}
}
}
Switch {
id: switch1
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
anchors.topMargin: 150
text: qsTr("Switch")
onToggled: switchOn = !switchOn
}
}
}
You can do that by using the lineLaidOut signal of the Label. It gives you information about each line's geometry (you can also modify them if needed):
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Window 2.0
Window {
id: root
visible: true
width: 400
height: 400
property bool switchOn: false
Rectangle {
color: '#D3D3D3'
anchors.fill: parent
Label {
id: sentence
text: qsTr("The switch is currently ")
color: 'black'
anchors.horizontalCenter: parent.horizontalCenter
width: 300
wrapMode: Text.Wrap
font.pixelSize: 40
anchors.top: parent.top
property real lastLineY
property real lastLineWidth
onLineLaidOut: line => {
if (line.isLast) {
lastLineY = line.y;
lastLineWidth = line.implicitWidth
}
}
Label {
id: endText
text: root.switchOn ? qsTr(" off ") : qsTr(" on ")
font.pixelSize: 40
color: "white"
x: sentence.lastLineWidth
y: sentence.lastLineY
background: Rectangle {
color: root.switchOn ? "grey" : "red"
radius: 8
}
}
}
Switch {
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
anchors.topMargin: 150
text: qsTr("Switch")
onToggled: root.switchOn = !root.switchOn
}
}
}
You can put the Labels within a Row, and horizontally center the Row.
Rectangle {
color: '#D3D3D3'
anchors.fill: parent
Row {
anchors.horizontalCenter: parent.horizontalCenter
Label {
id: sentence
...
}
Label {
id: endText
anchors.top: sentence.top
...
}
}
}

Scroll Bar is not working in ScrollView qml

I'm trying to use a scrollbar inside a scrollview. The scrollbar shows up and I can interact with it (hover/pressed), but it doesn't move, and I can't understand why. I wrote my code by following the official documentation and online examples.
Here's the code:
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
import QtGraphicalEffects 1.15
Window {
width: 740
height: 580
visible: true
color: "#00000000"
title: qsTr("Hello World")
Rectangle {
id: rectangle
color: "#40405f"
anchors.fill: parent
Button {
id: button
text: qsTr("Menu")
anchors.left: parent.left
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.leftMargin: 10
anchors.bottomMargin: 466
anchors.topMargin: 74
onClicked: animationMenu.running = true
}
ScrollView {
id: scrollView
width: 0
anchors.left: button.right
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.leftMargin: 10
anchors.bottomMargin: 10
anchors.topMargin: 10
clip: true
Rectangle {
id: rectangle1
color: "#00000000"
border.color: "#00000000"
border.width: 0
anchors.fill: parent
PropertyAnimation {
id: animationMenu
target: scrollView
property: "width"
to: if(scrollView.width == 0) return 240; else return 0
duration: 800
easing.type: Easing.InOutQuint
}
Column {
id: columnMenu
width: 0
anchors.fill: parent
spacing: 10
Button {
id: button1
text: qsTr("Button")
}
Button {
id: button2
text: qsTr("Button")
}
Button {
id: button3
text: qsTr("Button")
}
Button {
id: button4
text: qsTr("Button")
}
}
}
ScrollBar {
id: vbar
hoverEnabled: true
orientation: Qt.Vertical
size: scrollView.height / rectangle1.height
anchors.top: parent.top
anchors.right: parent.right
anchors.bottom: parent.bottom
wheelEnabled: true
pressed: true
active: true
}
}
}
}
Ok, so I edited the code to a smaller version so that it can be run.
Some advices:
Use anchors or Layouts. Do not use fixed values or some kind of treats, no matter if it works. The long term value of your code will be bad.
You should read carefully the (ScrollView documentatio). Also the Size section and the Touch and Mouse Interaction Section.
I am able to modify your example without the animation.
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.12
import QtGraphicalEffects 1.15
Window {
width: 740
height: 580
visible: true
color: "#00000000"
title: qsTr("Hello World")
Rectangle {
id: rectangle
color: "#40405f"
anchors.fill: parent
RowLayout{
anchors.fill: parent
Button {
id: button
text: qsTr("Menu")
width: 100
height: 50
}
ScrollView {
id: scrollView
Layout.fillWidth: true
Layout.fillHeight: true
RowLayout{
implicitHeight: 2000
implicitWidth: 2000
Column {
id: columnMenu
width: 0
anchors.fill: parent
spacing: 10
Repeater{
model: 50
delegate: Button {
text: qsTr("Button")
}
}
}
}
}
}
}
}

How to set visible flag correctly for Text item in QML based on a model count of ListView?

I have the following QML code where I show list view based on a model fetched from a C++ AbstractListModel.
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Controls 1.4
import QtQuick.Layouts 1.12
import QtQuick.Controls.Styles 1.4
import HackNews 1.0
import QtWebEngine 1.8
Item {
width: 640
height: 480
property alias dummyModel: kidsListView.model
ListView {
width: 100;
id: listView
anchors.left: parent.left
anchors.leftMargin: 20
anchors.top: parent.top
anchors.topMargin: 20
anchors.bottom: parent.bottom
anchors.bottomMargin: 20
model: HackNewsModel
delegate: Rectangle
{
width: 100; height: 40
Button {
id: pushButton
anchors.fill: parent
anchors.margins:
{
right: 5
left: 5
top: 5
bottom: 5
}
text: "id "+model.id
style: ButtonStyle{
background: Rectangle {
implicitWidth: 100
implicitHeight: 25
border.width: control.activeFocus ? 2 : 1
border.color: "#888"
radius: 4
gradient: Gradient {
GradientStop { position: 0 ; color: control.pressed ? "#ccc" : "#eee" }
GradientStop { position: 1 ; color: control.pressed ? "#aaa" : "#ccc" }
}
}
}
onClicked:
{
dummyModel = HackNewsModel.get(model.listId).kids
}
}
}
}
Rectangle
{
id: rectangle
anchors.left: listView.right
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.top: parent.top
anchors.margins:
{
left: 20
right: 20
top: 20
bottom: 20
}
Text {
id: kidsHeader
anchors.top: parent.top
anchors.left: parent.left
width: 100
height: 20
visible: (dummyModel.count!==0)?true:false
text: "kids"
}
ListView {
anchors.top: kidsHeader.bottom
anchors.left: parent.left
id: kidsListView
anchors.leftMargin: 10
model: ListModel{}
height: 50
delegate: Text {
text: modelData
}
ScrollBar.vertical: ScrollBar {}
}
}
}
The model can empty sometimes. When it's not empty, I would like to show a Text item otherwise I want to hide it. But somehow it's set visible forever when I have visible: (dummyModel.count!==0)?true:false or never when I have visible: (dummyModel.count>0)?true:false.
When model isn't empty it looks as the following.
But when the model isn't empty the text item doesn't get hidden.
Could you please tell me what am I doing wrong?
Thanks.
There is a property count available on the ListView as well, you should use that:
Text {
id: kidsHeader
anchors.top: parent.top
anchors.left: parent.left
width: 100
height: 20
visible: kids_listview.count !== 0 //Important change here
text: "kids"
}
ListView {
id: kids_listview
anchors.top: kidsHeader.bottom
anchors.left: parent.left
id: kidsListView
anchors.leftMargin: 10
model: ListModel{}
height: 50
delegate: Text {
text: modelData
}
ScrollBar.vertical: ScrollBar {}
}

GridLayout: width and height issues

I have a few questions about GridLayout in QtQuickLayouts 1.3:
How should I make it , so the GridLayout places into CORRECT row/column every element of my grid even if the size of the element is not clearly specified by with or height parameter?
Let me remove the width and height and I will show you the broken layout. I will just comment it out:
Item {
id: test_item
//Layout.fillWidth: true
//height: 20
}
Full source code of the .qml file is here, and can be tested with bin/qmlscene:
import QtQuick 2.7
import QtQuick.Controls 2.1
import QtQuick.Layouts 1.3
ApplicationWindow {
width: 400
height: 500;
ColumnLayout {
anchors.fill: parent
GridLayout {
anchors.fill: parent
columns: 2
Label {
text:"Email:"
}
Rectangle {
width: 80; height: 20
border.color: "magenta"
}
Label {
text: "Full Name:"
Layout.fillWidth: true
}
Item {
id: test_item // the commented out portion, to show the flaw
//Layout.fillWidth: true
//height: 20
}
Label {
text: "Gender:"
}
RowLayout {
Layout.minimumHeight: 20
Layout.fillWidth: true
RadioButton {
text: "Male"
width: parent.width/2
height: 20
}
RadioButton {
text: "Female"
width: parent.width/2
height: 20
}
}
Label {
text: "Mobile phone:"
}
Rectangle {
width: 80; height: 20
border.color: "magenta"
}
}
Row {
Button {
text: "Cancel"
}
Button {
text: " Ok "
}
}
}
}
The 'broken` output looks like this:
While the correct output , with Item's size specified looks like this:
2) The second question. How do I load a component into a GridLayout cell via Loader using Component type? For example, this is my full source with the Component item and the component is missing when rendering:
import QtQuick 2.7
import QtQuick.Controls 2.1
import QtQuick.Layouts 1.3
ApplicationWindow {
width: 400
height: 500;
ColumnLayout {
anchors.fill: parent
GridLayout {
anchors.fill: parent
columns: 2
Label {
text:"Email:"
}
Rectangle {
width: 80; height: 20
border.color: "magenta"
}
Label {
text: "Full Name:"
Layout.fillWidth: true
}
Loader {
id: test_item
sourceComponent: field_template
}
Label {
text: "Gender:"
}
RowLayout {
Layout.minimumHeight: 20
Layout.fillWidth: true
RadioButton {
text: "Male"
width: parent.width/2
height: 20
}
RadioButton {
text: "Female"
width: parent.width/2
height: 20
}
}
Label {
text: "Mobile phone:"
}
Rectangle {
width: 80; height: 20
border.color: "magenta"
}
}
Row {
Button {
text: "Cancel"
}
Button {
text: " Ok "
}
}
}
Component {
id: field_template
Item {
Layout.fillWidth: true
height: 33
Rectangle {
border.color: "blue"
color: "transparent"
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.rightMargin: 10
TextEdit {
anchors.fill: parent
anchors.leftMargin: 5
anchors.topMargin: 3
anchors.rightMargin: 2
clip: true
text: "type here"
}
}
}
}
}
The picture of the output rendered by qmlscene is this:
I have correctly speecified witdht and height, why isn't the component being loaded with Loader ?
For the second question folibis is right in the comment. You just have to check the size. Here, is a working code.
...
Item {
Layout.fillWidth: true
//width: 80
height:20
Loader {
id: test_item
anchors.fill: parent
sourceComponent: field_template
}
}
...
Component {
id: field_template
Item {
Rectangle {
border.color: "blue"
color: "transparent"
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.rightMargin: 10
TextEdit {
anchors.fill: parent
anchors.leftMargin: 5
anchors.topMargin: 3
anchors.rightMargin: 2
clip: true
text: "type here"
}
}
}

How to animate a message that appears on bottom?

I'm showing a message on the bottom:
Msg.qml
import QtQuick 2.4
Item {
property alias text: mf.text
anchors.fill: parent
antialiasing: false
opacity: 0.9
z: 100
MsgForm {
id: mf
width: parent.width
y: parent.height - height - 5
}
}
MsgForm.ui.qml
import QtQuick 2.4
Item {
property alias text: msg.text
width: 200
id: message
height: msg.height+10
Rectangle {
id: rectangle
color: "#fb9191"
anchors.fill: parent
border.color: "#fd6666"
border.width: 2
Text {
id: msg
anchors.top: parent.top
anchors.topMargin: 2
textFormat: Text.PlainText
anchors.right: parent.right
anchors.rightMargin: 4
anchors.left: parent.left
anchors.leftMargin: 4
wrapMode: Text.WordWrap
clip: false
font.bold: true
font.pointSize: 12
font.family: "Tahoma"
}
}
}
How can I animate the form to appear from the bottom smoothly?
After the animation, if the window resizes, the message must stay always on the bottom.
You can play with anchors.bottomMargin property to raise the message item from the bottom.
import QtQuick 2.4
Item {
property alias text: mf.text
anchors.fill: parent
antialiasing: false
opacity: 0.9
z: 100
MsgForm {
id: mf
property bool showing: false
width: parent.width
anchors{
bottom: parent.bottom
bottomMargin: mf.showing ? 0 : -mf.height
Behavior on bottomMargin{
NumberAnimation{ }
}
}
}
}
Thanks everyone. In the end I've solved the issue by following advices received in the qtcentre forum.
The desired effect can be achieved easily by defining a local numerical property that is use to bind to either an anchors.XXXXmargin or the y property expression.
Following this approach a possible solution is the following:
MsgForm {
property bool showing: false
property int position: showing ? height : 0
width: parent.width
y: parent.height - position
Behavior on position {
NumberAnimation {duration: 500}
}
}
You can make an animation on the opacity change:
Msg.qml
import QtQuick 2.4
Item {
property alias text: mf.text
anchors.fill: parent
antialiasing: false
opacity: 0.9
z: 100
MouseArea{
anchors.fill: parent
onClicked: mf.opacity = !mf.opacity
}
MsgForm {
id: mf
//y: parent.height - height - 5
opacity:0
Behavior on opacity {
NumberAnimation{
duration:600
}
}
width: parent.width
anchors.bottom: parent.bottom
}
}
or any other NumberAnimation. I recommand you to create States, within it do some propertyChanges, and on some actions, example button clicked change states.
example in your MsgForm.ui.qml add:
MouseArea{
anchors.fill: parent
onClicked: mf.state= "show"
}
and in the action, example:
in my mouseArea I change the state of mf
MouseArea{
anchors.fill: parent
onClicked: mf.state= "show"
}
If you want an Animation on the y try this:
MsgForm.ui.qml
import QtQuick 2.4
Item {
id: message
property alias text: msg.text
width: parent.width
height: msg.height+10
Rectangle {
id: rectangle
color: "#fb9191"
anchors.fill: parent
border.color: "#fd6666"
border.width: 2
Text {
id: msg
anchors.top: parent.top
anchors.topMargin: 2
textFormat: Text.PlainText
anchors.right: parent.right
anchors.rightMargin: 4
anchors.left: parent.left
anchors.leftMargin: 4
wrapMode: Text.WordWrap
clip: false
font.bold: true
font.pointSize: 12
font.family: "Tahoma"
}
}
Behavior on y {
NumberAnimation{
duration:300
}
}
states: [
State {
name: "show"
PropertyChanges {
target: message
y: parent.height - height
}
},
State {
name: "hide"
PropertyChanges {
target: message
y: parent.height + height + 5
}
}
]
}
Msg.qml
import QtQuick 2.4
Rectangle {
property alias text: mf.text
width:800
height: 480
antialiasing: false
opacity: 0.9
z: 100
MouseArea{
anchors.fill: parent
onClicked: mf.state= "show"
}
MsgForm {
id: mf
//y: parent.height - height - 5
y: parent.height +height + 5
width: parent.width
}
}

Resources