Using GridLayout with QAbstractModelList and Repeaters - qml

I have following problem in QtQuick (QML). I want to display a table with data from QAbstractListModel in a table layout. I use GridlĹayout and repeaters for it:
ScrollView {
id: scrollView
width: parent.width
anchors.fill: parent
clip: true
ScrollBar.horizontal.policy: ScrollBar.AlwaysOn
ScrollBar.vertical.policy: ScrollBar.AlwaysOn
Repeater{
model: _network.qqjourneyslist.length
Item {
GridLayout {
id: inr
columns: 5
width: rect2.width
layoutDirection: "RightToLeft"
anchors.left: parent.left
anchors.top: parent.bottom
Repeater{
model: _network.qqjourneyslist[index]
Item {
Text{
id:t1
//width: 100
text: model.startstop
//Layout.fillWidth: true
}
Text{
id:t2
//width: 100
text: model.starttime
//Layout.fillWidth: true
//anchors.left: t1.right
}
Text{
id:t3
//width: 100
text: model.lineno
//Layout.fillWidth: true
//anchors.left: t2.right
}
Text{
id: t4
//width: 100
text: model.endstop
// Layout.fillWidth: true
//anchors.left: t3.right
}
Text{
id: t5
//width: 100
text: model.endtime
//Layout.fillWidth: true
//anchors.left: t4.right
}
}
}
}
}
}
}
The problem is, that when I insert a repeater in the GridLayout the flow is destroyed and the Texts are overwriting each other. I tried a lot of things like insert width for the texts or use Layout.row, Layout.columns, but nothing works.
I also looked at Populate GridLayout with Repeater but this topic did not help in my case or at least I did not find a way how to modify it for my purpose. _network.qqjourneislist is a QList wiht QAbstractListModel which I would like to use and outside of the Layout there are working fine.
Could you please help me how to use a repeater and QAbstractListModel?
I do not keen on using GridLayout I just want to create a table with row and columns for my model. I know there are lot of another table objects but I do not know which to use and trying all out is a long way for me.
I am glad of every advice I receive and thank you for it!
EDIT....
The solution with ColumnLayout and RowLayout is working until I use a Repeater in a Repeater which yields to that the text lines are not produced.
The code is following:
ColumnLayout {
id: inr
// anchors.fill: parent
width: parent.width
layoutDirection: "LeftToRight"
anchors.left: parent.left
anchors.margins: 15
//anchors.horizontalCenter: parent
anchors.top: parent.top
anchors.right: parent.right
Repeater{
model: _network.qqjourneyslist.length
Repeater{
id: rep1
model: _network.qqjourneyslist[index]
//width: inr.width
//height: 50
RowLayout{
height: 20
Layout.preferredHeight: 20
spacing: 10
Text{
id:t1
width: wind.implicitWidth/3
//anchors.left: parent.left
//anchors.top: parent.top
text: model.startstop
font.pointSize: 12
Layout.preferredWidth: wind.implicitWidth/3
}
Text{
id:t2
//width: 100
text: model.starttime
font.pointSize: 12
font.bold: true
//Layout.fillWidth: true
//anchors.left: t1.right
}
Text{
id:t3
//width: 100
text: model.lineno
font.pointSize: 12
color: "red"
//Layout.alignment: Qt.AlignVCenter
horizontalAlignment: Text.AlignHCenter
//Layout.preferredWidth: parent.implicitWidth/3
Layout.fillWidth: true
}
Text{
x:wind.implicitWidth/12*9
id: t4
text: model.endstop
Layout.alignment: Qt.AlignLeft
font.pointSize: 12
Layout.preferredWidth: parent.implicitWidth/3
//anchors.left: t3.right
}
Text{
id: t5
//width: 100
text: model.endtime
Layout.alignment: Qt.AlignRight
font.pointSize: 12
font.bold: true
//Layout.fillWidth: true
//anchors.left: t4.right
}
}
}
RowLayout{
height: 12
//Layout.preferredHeight: 12
//anchors.top: rep1.bottom
Rectangle{
//width: parent.width
//Layout.
Layout.row: 3*index
Layout.preferredHeight: 12
Layout.fillWidth: true
color: "grey"
//Layout.alignment: Qt.AlignLeft
//Layout.fillWidth: parent
//Layout.alignment: Qt.Right
radius: 5
}
}}}
I want to produce something like that (the lower rectangle)
right version
but I get only the rectangles and no text. If I remove the last RowLayout then the texrows are displayed properly.
something is wrong
The _network.qqjournieslist is a QList of QObejcts* - QAbstractListModel - so it should be possible to assign it to model.
Thanks for every help!

The Text objects overwrite themselves because their parent is an Item, which doesn't tell them where to be positioned. You don't really describe how you want the output to look, so I'll make an assumption that you want those 5 text strings to appear as a single row in your grid. Then you can just use a Column with a Row, (or a ColumnLayout with a RowLayout if you prefer).
Column {
Row {
Repeater {
model: _network.qqjourneyslist
Text {
width: 100 // Column 1 width
text: model.startstop
}
Text {
width: 100 // Column 2 width
text: model.starttime
}
...
}
}
}
EDIT:
Your updated code doesn't work because you seem to misunderstand how Repeaters work. The model of a Repeater can either be a count of how many times it should repeat, or a list of some sort that tells the Repeater how many items to create.
Your first Repeater uses the length of your list as a model, which is ok. Let's say its value is 10. That means it will create 10 of whatever is inside it. And what's inside that Repeater? Another Repeater. The model of that second Repeater though is the contents of a single item in your list. That's a problem. Unless that item is also a list of some sort, the Repeater won't know how to use it as a count. That's why in my code above, I changed your example to only use a single Repeater. That's all that's needed for your case. The model for each row needs to be repeated, but not your columns.

Related

transferring the same images logic to another file

Is it possible to transfer the same code logic in another page, in which these two images are taken as alias or something similar and they behave in the same pattern of "if statement" logic on the other QML file(2) when this button is pressed from the first QML file(1)?
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Studio.Components 1.0
import QtQuick.Timeline 1.0
Item {
id: root
width: 500
height: 500
property alias locking: locking
Text {
id: confirmSign
text: qsTr("")
anchors.left: parent.left
anchors.top: parent.top
font.styleName: "Bold"
anchors.leftMargin: 70
anchors.topMargin: 55
color: "white"
font.pointSize: 10
font.family: "Tahoma"
}
RoundButton {
id: locking
y: 70
width: 90
height: 90
anchors.left: password_field.right
anchors.leftMargin: 15
Image {
id: unlck
anchors.centerIn: parent
source: "Images/Unlocked.png"
visible: false
}
Image {
id: lcked
anchors.centerIn: parent
source: "Images/Locked.png"
visible: true
}
onClicked: {
passBlocker.visible = true
confirmSign.text = qsTr("The ToolBar is 'Locked' ");
confirmSign.color = "#ffffff";
lcked.visible = true
unlck.visible = false
}
}
}
You can create a new file just for the lock image and use it wherever you want.
Something like this:
File LockImage.qml
Item {
property bool locked: false
Image {
id: lcked
anchors.centerIn: parent
source: locked? "Images/Locked.png": "Images/Unlocked.png"
visible: true
}
}
Side note: you don’t need to change properties using clicked handler. Prefer using the checked property of Button

Error when customizing Controls 2 SpinBox

When customizing a QtQuickControls2 SpinBox, I get this error in the console when closing my application :
"There are still \"2\" items in the process of being created at engine destruction."
The number varies depending on how many Spinboxes are in the windows, it goes up for every indicator that was customized (two times per customized SpinBox : one for the up indicator, one for the down indicator). I tried commenting out each part of my custom code, as well as using the example code provided here, so I am positive that this is where the error comes from.
Does anyone know how to get rid of this error ?
Main window code :
import QtQuick 2.7
import QtQuick.Controls 2.3
ApplicationWindow{
width: 1600
height: 900
visible: true
SpinBox_custom{
}
}
Custom SpinBox_custom code :
import QtQuick 2.11
import QtQuick.Controls 2.4
SpinBox {
id: control
value: 50
editable: true
contentItem: TextInput {
anchors.fill: parent
anchors.rightMargin : up.indicator.width
anchors.leftMargin : down.indicator.width
z: 2
text: control.textFromValue(control.value, control.locale)
font.pointSize: Style.textPointSize-2
color: '#7e8d9e'
selectionColor: '#7e8d9e'
selectedTextColor: "white"
horizontalAlignment: Qt.AlignHCenter
verticalAlignment: Qt.AlignVCenter
readOnly: !control.editable
validator: control.validator
inputMethodHints: Qt.ImhFormattedNumbersOnly
}
up.indicator: Rectangle {
x: control.mirrored ? 0 : parent.width - width
height: parent.height
implicitWidth: 20
implicitHeight: 30
color: control.up.pressed ? '#dee2e6' : '#bec6ce'
border.color: enabled ? '#bec6ce' : '#dee2e6'
Text {
text: "+"
font.pixelSize: control.font.pixelSize * 2
color: '#428AC9'
anchors.fill: parent
fontSizeMode: Text.Fit
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
}
down.indicator: Rectangle {
x: control.mirrored ? parent.width - width : 0
height: parent.height
implicitWidth: 20
implicitHeight: 30
color: control.down.pressed ? '#dee2e6' : '#bec6ce'
border.color: enabled ? '#bec6ce' : '#dee2e6'
Text {
text: "-"
font.pixelSize: control.font.pixelSize * 2
color: '#428AC9'
anchors.fill: parent
fontSizeMode: Text.Fit
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
}
background: Rectangle {
implicitWidth: 90
}
}
I've just faced the same problem and after some experiments seemed to find that it's somewhat connected to assigning literal values to the value property of SpinBox.
Something similar is also discussed in this report, and these are other things asking for trouble when used in SpinBox mentioned there:
references between internal elements of SpinBox (contentItem, background, [up,down].indicator's)
references to parent in internal elements
assigning id to internal elements
After getting rid of all of that and also changing value: 0 to value: from it automagically stopped warning me with the message in the original post.
Just as a reference, here's the snippet which works for me without the warning:
SpinBox
{
id: control
value: from
from: 0
to: 600
editable: true
enabled: true
leftPadding: topPadding
rightPadding: height
contentItem: Item
{
anchors.left: control.left
anchors.top: control.top
anchors.bottom: control.bottom
implicitWidth: input.implicitWidth
implicitHeight: input.implicitHeight
width: control.width - control.height
height: control.height
TextInput
{
id: input
anchors.fill: parent
text: control.textFromValue(control.value, control.locale)
color: "black"
horizontalAlignment: Qt.AlignHCenter
verticalAlignment: Qt.AlignVCenter
readOnly: !control.editable
validator: control.validator
inputMethodHints: Qt.ImhFormattedNumbersOnly
}
}
up.indicator: Rectangle
{
anchors.top: control.top
anchors.right: control.right
anchors.topMargin: 1
anchors.rightMargin: 1
height: control.height / 2
width: control.height
color: "gold"
}
down.indicator: Rectangle
{
anchors.bottom: control.bottom
anchors.right: control.right
anchors.bottomMargin: 1
anchors.rightMargin: 1
height: control.height / 2
width: control.height
color: "orange"
}
background: Rectangle
{
implicitWidth: 100
border.color: "red"
border.width: 1
radius: 2
}
}

QML: Center variable text and image

I need to horizonally center a variable-length text (red box) and an image (yellow box) in a big box (green box). The text shall wrap if it does not fit the box.
Existing code:
Item {
id: bigBox
x: 255
y: 0
width: 800
height: 100
Image {
id: imageBox
source: "image.png"
width: 52
height: 46
anchors.left: parent.left
anchors.leftMargin: 12
anchors.verticalCenter: parent.verticalCenter
horizontalAlignment: Image.AlignLeft
verticalAlignment: Image.AlignVCenter
fillMode: Image.Pad
}
Text {
id: textBox
anchors.left: symbol.right
anchors.leftMargin: 12
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
text: qsTr("heading text")
font.pixelSize: 36
font.bold: true
horizontalAlignment: Text.AlignCenter
}
}
Update:
Actual running code and a real screenshot showing the problem:
import QtQuick 2.0
Rectangle {
id: mask
x: 0
y: 0
width: 800
height: 430
color: "#FFFFFF"
property int pageState: 0
Rectangle {
x: 0
y: 0
width: 111
height: 100
color: "#0000FF"
}
Item {
id: whitespace
x: 117
y: 0
width: 800-x
height: 100
Row {
anchors.centerIn: parent
Image {
id: symbol
source: "../img/pepper.png"
width: 52
height: 46
anchors.verticalCenter: parent.verticalCenter
fillMode: Image.Pad
}
Text {
id: heading
property var texts: ["Active Blabla","Active Blaaaaaaah Blaaaah ","Active Blabla and Blaaaaaaah Blaaaah"]
anchors.verticalCenter: parent.verticalCenter
color: "#333191"
text: texts[pageState]
font.family: "Liberation Sans"
font.pixelSize: 36
font.bold: true
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignVCenter
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
textFormat: Text.PlainText
width: Math.min(150,contentWidth)
}
}
}
Rectangle {
id: stage
x: 0
y: 106
width: parent.width
height: parent.height-y
color: "#FFFF00"
}
Timer {
interval: 1000 // milliseconds
triggeredOnStart: true
repeat: true
running: true
onTriggered: {
pageState=(pageState+1)%3;
}
}
}
The white area top right is the green box in the first picture. The timer simply runs through the three texts, like the real application would do.
Changing the first parameter of Math.min() to 600 does not change anything.
My set of hacks to solve that problem:
Item {
// ...
Text {
function escapeHTML(text)
{
return text.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""");
}
function toHTML(imageUrl,text)
{
var textlines=escapeHTML(text).replace(/\r?\n/,"\n").split("\n");
var retval="<center>";
retval=retval+'<table><tr>';
if (imageUrl!="") {
retval=retval+'<td rowspan="'+(textlines.length)+'"><img src="'+imageUrl+'"></td>';
retval=retval+'<td rowspan="'+(textlines.length)+'"> </td>';
}
for (var i=0; i<textlines.length; i++) {
if (i>0) {
retval=retval+"<tr>";
}
retval=retval+'<td>'+textlines[i]+'</td></tr>';
}
retval=retval+'</table>';
retval=retval+"</center>";
return retval;
}
anchors.fill: parent
verticalAlignment: Text.AlignVCenter
textFormat: Text.RichText
text: toHtml("../img/pepper.png",qsTr("heading"))
}
}
<center> centers the text in the available space, but not the image
(it stays left - bug!).
<table> can be centered by <center>, and it can contain the
image.
Wrapping does not work as expected, so the translated text returned
by qsTr() has to contain linebreaks at the right positions.
toHTML() splits at the linebreaks and generates table rows from that.
The image needs a table cell with rowspan, or else the image is placed too high relative to the text.
And finally, verticalAlignment: Text.AlignVCenter places all of that vertically centered -- except for the table borders that I luckily don't need. (If you are bored, add border=1 to the <table> tag.)
And no, the HTML and CSS subset supported by Text.RichText does not support vertical alignment. The rendering engine behaves like ancient browsers, you have to stack hacks and workarounds as if it was still 1996.
import QtQuick 2.3
import QtQuick.Controls 1.2
import QtQuick.Layouts 1.2
Item {
id: bigBox
width: 800
height: 100
Row {
anchors.centerIn: parent
Image {
anchors.verticalCenter: parent.verticalCenter
source: "blue.png"
}
Text {
anchors.verticalCenter: parent.verticalCenter
// maximum width of the text
width: Math.min(150, contentWidth)
text: qsTr("heading")
wrapMode: Text.WordWrap
horizontalAlignment: Text.AlignHCenter
}
}
}

RowLayout ignores Layout.minimumWidth

The text and text2 should be properly elided (show three dots when space is not enough to cover whole string) when the window is too small. The Layout.preferredWidth is set to be text.implicitWidth with some addition for padding, but i even set the Layout.minimumWidth, so the text can be ellided when the space is not enough. It seems that the RowLayout is ignoring the Layout.minimumWidth and keeps the Layout.preferredWidth, so the text has still width of Layout.preferredWidth and is never elided. What's wrong?
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Layouts 1.1
ApplicationWindow {
id: window
visible: true
width: 640
height: 480
title: qsTr("Hello World")
contentItem.minimumWidth: 50
RowLayout {
id: ntoolbarline
anchors.fill: parent
Rectangle {
color: "red"
Layout.preferredWidth: text.implicitWidth + 40
Layout.preferredHeight: 25
Layout.minimumWidth: 25
Layout.minimumHeight: 25
Layout.maximumWidth: text.implicitWidth + 40
Text {
id: text
anchors.centerIn: parent
text: "Foooooooo"
elide: Text.ElideRight
width: parent.width - 40
renderType: Text.NativeRendering
}
}
Rectangle {
color: "red"
Layout.preferredWidth: text2.implicitWidth + 40
Layout.preferredHeight: 25
Layout.minimumWidth: 25
Layout.minimumHeight: 25
Layout.maximumWidth: text2.implicitWidth + 40
onWidthChanged: console.log("layout item width: " + width) // only one output when app started
Text {
id: text2
anchors.centerIn: parent
text: "Baaaaaaaar"
elide: Text.ElideRight
width: parent.width - 40
renderType: Text.NativeRendering
}
}
Item {
id: spacer
Layout.fillWidth: true
onWidthChanged: console.log("spacer width: " + width) // outputs whenever the window is resized
}
}
}
You have two main problems here:
fillWidth is not defined
Text is centerIn the parent Rectangle
Without 1. the Rectangles cannot grow or shrink inside the desired constraints. By making use of 2. the Text element as no specific constraints on its size and borders and thus Text cannot correctly check that an elide is required. The correct way to go is to use fill and setting alignments via the verticalAlignment and horizontalAlignment of Text.
The final revisited code looks like the following:
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Layouts 1.1
ApplicationWindow {
id: window
visible: true
width: 640
height: 480
minimumWidth: 50
title: qsTr("Hello World")
RowLayout {
id: ntoolbarline
anchors.fill: parent
Rectangle {
color: "red"
Layout.fillWidth: true
Layout.preferredWidth: text.implicitWidth + 40
Layout.preferredHeight: 25
Layout.minimumWidth: 25
Layout.maximumWidth: text.implicitWidth + 40
Text {
id: text
anchors.fill: parent
text: "Foooooooo"
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
renderType: Text.NativeRendering
}
}
Rectangle {
color: "red"
Layout.fillWidth: true
Layout.preferredWidth: text.implicitWidth + 40
Layout.preferredHeight: 25
Layout.minimumWidth: 25
Layout.maximumWidth: text.implicitWidth + 40
Text {
id: text2
anchors.fill: parent
text: "Baaaaaaaar"
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
renderType: Text.NativeRendering
}
}
Item {
id: spacer
Layout.fillWidth: true
}
}
}

QML Row element refuses to work

I know I should be using Row, Column etc. rather than items anchored by ID to make my code simpler and easier to read. But they refuse to work most of the time. For example, in this case:
import QtQuick 2.4
import QtQuick.Controls 1.3
import QtQuick.Layouts 1.1
ListView {
id: listView
anchors.fill: parent
topMargin: spacing
anchors.leftMargin: spacing
anchors.rightMargin: spacing
clip: true
spacing: 0.5 * pxPermm
model: SqlQueryModel {}
delegate: Rectangle {
id: delegateItem
color: "white"
height: 14 * pxPermm
width: listView.width
clip: true
Row {
id: row
anchors.fill: delegateItem
spacing: pxPermm
Image {
height: row.height
width: height
source: "qrc:/resources/ryba.jpg"
fillMode: Image.PreserveAspectCrop
}
Item {
id: textItem
height: row.height
Label {
anchors.left: textItem.left
anchors.top: textItem.top
text: nazov
font.bold: true
}
Label {
anchors.left: textItem.left
anchors.bottom: textItem.bottom
text: cas
}
}
}
}
}
This shows two Labels on the top of an Image in the delegate of list view. Not two labels to the right of the image, as you would expect. However, this code works:
import QtQuick 2.4
import QtQuick.Controls 1.3
import QtQuick.Layouts 1.1
ListView {
id: listView
anchors.fill: parent
topMargin: spacing
anchors.leftMargin: spacing
anchors.rightMargin: spacing
clip: true
spacing: 0.5 * pxPermm
model: SqlQueryModel {}
delegate: Rectangle {
id: delegateItem
color: "white"
height: 14 * pxPermm
width: listView.width
clip: true
Row {
id: row
anchors.fill: delegateItem
spacing: pxPermm
Image {
height: row.height
width: height
source: "qrc:/resources/ryba.jpg"
fillMode: Image.PreserveAspectCrop
}
Label {
text: nazov
font.bold: true
}
}
}
}
Of course I need to show more than one label in the delegate. What am I missing here?
It turns out that Item has zero width by default. The code works properly after the width is set:
Item {
id: textItem
height: row.height
width: childrenRect.width
// labels etc
}