How to get cartesian coordinates about element inside of ColumnLayout - qt

Having a ColumnLayout that have inside 4 different items, positioned using Layout, how can I get cartesian coordinates from one of those item?
Example:
import QtQuick 2.11
import QtQuick.Layouts 1.3
ColumnLayout {
spacing: 0
Layout.topMargin: 10
Layout.bottomMargin: 10
Image {
id: item1
Layout.alignment: Qt.AlignHCenter
sourceSize: Qt.size(204, 96)
}
Text {
id: item2
Layout.topMargin: 15
Layout.fillWidth: true
horizontalAlignment: Text.AlignHCenter
text: "hello"
}
Text {
id: item3
Layout.alignment: Qt.AlignHCenter
text: "world"
}
}
What is the position based in cartesian coordinates of element with id "item3"? How to get that?

If you want to obtain the position of the item3 with respect to the ColumnLayout you must use the properties x, y:
// ...
Text {
id: item3
Layout.alignment: Qt.AlignHCenter
text: "world"
Component.onCompleted: console.log(x, y)
}
If you want respect to the screen you must use the mapToGlobal() function:
Component.onCompleted: console.log(mapToGlobal(x, y))

Related

How to use own objects in ColumnLayout in QML?

I would like to produce a table where in every Row is a Text() and a ComboBox(). I want the comboboxes are aligned right and also the left side of the text labels something like that:
I have the main qml:
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
Window {
id: wind
width: 640
height: 480
visible: true
title: qsTr("Public Transport Searcher")
property string oz: "Odjezdová zastávka"
property string pz: "Příjezdová zastávka"
property string co: "Čas odjezdu"
Rectangle{
id: rect
x: wind.width/16
implicitHeight: parent.height/3*2
implicitWidth: wind.width/8*7
anchors.right: parent.right
anchors.left: parent.left
anchors.margins: 20
radius: 5
color: "lightblue"
ColumnLayout{
id: cl
spacing: 30
width: parent.width
anchors.top: parent.top
anchors.left: parent.left
anchors.margins: 20
anchors.fill: parent
Row{
id: r1
Layout.alignment: Qt.AlignTop
//Layout.alignment: Qt.AlignTop
Layout.margins: 15
Text{
id: r1t1
text: "Vyhledávač jízdních řádů"
font.pointSize: 16
font.bold: true
}
}
Line{
id: r2
tt2: oz
Layout.alignment: Qt.AlignLeft
//anchors.top: r1.bottom
}
Line{
id: r3
tt2: pz
Layout.alignment: Qt.AlignLeft
}
Line{
id: r4
tt2: co
Layout.alignment: Qt.AlignLeft
}
}
}
}
And a separate Line.qml item:
import QtQuick 2.15
import QtQuick.Controls 2.12
Item {
property string tt2: "v"
Text{
id: txt
text: tt2
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
}
ComboBox{
id: cb
anchors.verticalCenter: parent.verticalCenter
//anchors.fill: parent
anchors.left: txt.right
}
}
Now it runs but the all Rows are overwritting in the top left corner.
I know that I could use the GridLayout but I want to take advantage from the own objects for one row (in case I have much such rows) to avoid copy paste technic and initialise every object of the row separate.
Could you show me please how to do that in an elagant way?
The problem is that your Line items do not have any implicit size which the ColumnLayout can read from. That is, the Line's base is just an Item whose default implicitHeight and implicitWidth are both 0 - so the ColumnLayout renders them improperly. You have several options.
One option is to add implicitHeight/implicitWidth (or Layout.preferredWidth/Layout.preferredHeight) to your Line's Item:
import QtQuick 2.12
import QtQuick.Controls 2.12
Item {
implicitHeight: 30
implicitWidth: parent.width
property string tt2: "v"
Text{
id: txt
text: tt2
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
}
ComboBox{
id: cb
anchors.verticalCenter: parent.verticalCenter
//anchors.fill: parent
anchors.left: txt.right
}
}
A different way of accomplishing this would be to change the base of the Line to a RowLayout (and remove the interior anchors which would conflict):
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
RowLayout {
property string tt2: "v"
Text {
id: txt
text: tt2
Layout.preferredWidth: 200 // to align ComboBoxes, all text must have same width
// Layout.fillWidth: true // a different option for aligning ComboBoxes
}
ComboBox {
id: cb
}
}

qml: How to format column of a listview

I have a list view but all text elements start printing at x position 0.
Is it possible to format the column width? I can set the text element x position by simply
x: 100
But that seems to be the wrong way. How can I set the row width to see the content like a table?
What I currently have prints all elements in the first row.
import QtQuick 2.9
import QtQuick.Controls 1.4
import MyTypes 1.0
ListView {
id: listView
implicitWidth: contentItem.childrenRect.width
anchors.fill: parent
//model: mymodel
model: ExportedListModel {}
delegate: Item {
implicitHeight: text1.height
TextEdit {
id: text1
text: model.heading
Keys.onReturnPressed: model.heading = text
}
TextEdit {
id: text2
text: model.description
Keys.onReturnPressed: model.description = text
}
TextEdit {
id: text3
text: model.quantity
Keys.onReturnPressed: model.quantity = text
}
TextEdit {
id: text4
text: model.someEnum
Keys.onReturnPressed: model.someEnum = text
}
}
}
I think you should specify the width property in your List View and not the implicitWidth. The latter is just a hint.
Besides, what is referenced by your "contentItem" ?
You can specify your delegate's item width as it is done in the second example here :
https://doc.qt.io/qt-5/qml-qtquick-listview.html
EDIT :
Whether it is "width" or "implicitWidth" properties, the example will work.
To use the column, it is important to provide a sufficient height value to your delegate's root item in order to be able to display all the rows in your column.
Here is a working example inspired by yours, you can copy paste it in an empty QtQuick project :
import QtQuick 2.9
import QtQuick.Window 2.2
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
ListModel {
id: mymodel
ListElement {
TEXT: "text_1"
ID: 1
COMMENT: "Comment1"
}
ListElement {
TEXT: "text_2"
ID: 2
COMMENT: "Comment2"
}
ListElement {
TEXT: "text_3"
ID: 3
COMMENT: "Comment3"
}
ListElement {
TEXT: "text_4"
ID: 4
COMMENT: "Comment4"
}
}
ListView {
id: listView
anchors.fill: parent
model: mymodel
delegate:
Rectangle
{
implicitWidth: contentItem.childrenRect.width
implicitHeight: textEdit.height*2
border.color: "black"
border.width: 1
color: "red"
Column{
TextEdit {
id: textEdit
color: "white"
text: TEXT + " number is " + ID
}
TextEdit {
id: textEdit2
color: "white"
text: COMMENT
}
}
}
}
}
Here is a picture of the result :
https://imgur.com/4yoj5gU

Qt QML binding child property to parent property

I want to set the ApplicationWindow to minimum width and height by the minimum width and height of the child element "mainLayout". I am having trouble to use the property of "mainLayout" in the parent QML ApplicationWindow. I tried to make the property viewable by making an alias. Not sure if it is the right solution. It does not work. But there is also no Error when I run.
My code looks like this:
main.qml
import QtQuick 2.2
import QtQuick.Controls 1.2
import QtQuick.Layouts 1.3
import QtQuick.Window 2.2
ApplicationWindow {
visible: true
width: 1500
height: 1200
property int margin: 11
minimumWidth: serial.mainLayout.minimumWidth + 2 * margin //this one is not working
minimumHeight: serial.mainLayout.minimumHeight + 2 * margin //this one is not working
Serial {
id: serial
anchors.fill: parent
}
}
Serial.qml
import QtQuick 2.7
import QtQuick.Controls 1.4
import QtQuick.Layouts 1.3
import io.qt.serialComm 1.0
Item {
anchors.fill: parent
id: item
property alias mainLayout: mainLayout
ColumnLayout {
id: wrapper
width: parent.width/2
height: parent.height/2
ColumnLayout {
id: mainLayout
anchors.fill: parent
anchors.margins: margin
GroupBox {
id: rowBox
title: "Row layout"
Layout.fillWidth: true
RowLayout {
id: rowLayout
anchors.fill: parent
TextField {
placeholderText: "This wants to grow horizontally"
Layout.fillWidth: true
}
Button {
text: "Button"
}
}
}
GroupBox {
id: gridBox
title: "Grid layout"
Layout.fillWidth: true
GridLayout {
id: gridLayout
rows: 3
flow: GridLayout.TopToBottom
anchors.fill: parent
Label { text: "Line 1" }
Label { text: "Line 2" }
Label { text: "Line 3" }
TextField { }
TextField { }
TextField { }
TextArea {
text: "This widget spans over three rows in the GridLayout.\n"
+ "All items in the GridLayout are implicitly positioned from top to bottom."
Layout.rowSpan: 3
Layout.fillHeight: true
Layout.fillWidth: true
}
}
}
TextArea {
id: t3
text: "This fills the whole cell"
Layout.minimumHeight: 30
Layout.fillHeight: true
Layout.fillWidth: true
}
GroupBox {
id: stackBox
title: "Stack layout"
implicitWidth: 200
implicitHeight: 60
Layout.fillWidth: true
Layout.fillHeight: true
StackLayout {
id: stackLayout
anchors.fill: parent
function advance() { currentIndex = (currentIndex + 1) % count }
Repeater {
id: stackRepeater
model: 5
Rectangle {
color: Qt.hsla((0.5 + index)/stackRepeater.count, 0.3, 0.7, 1)
Button { anchors.centerIn: parent; text: "Page " + (index + 1); onClicked: { stackLayout.advance() } }
}
}
}
}
}
}
}
When I put the code in one file, it works and the ApplicationWindow does not get smaller than the minimum height and width of the child element "mainLayout". But splitting into 2 files does not work..
The reason why you are not able to use the property minimumWidth of your QML element with the id mainLayout like serial.mainLayout.minimumWidth is that it doesn't have one.
However, the QML element in question does have an attached property Layout.minimumWidth because it's an item in a ColumnLayout with the id wrapper. You already found out that you could access it through serial.mainLayout.Layout.minimumWidth.
Attached property mechanism that enables the minimumWidth for mainLayout is not the easiest one to understand. In short, it enables objects to be annotated with extra properties that are otherwise unavailable to the object but are relevant in certain circumstances. In this case minimumWidth is considered relevant for child items of ColumnLayout. Items in a ColumnLayout support these attached properties.

How to put attached properties to child item

Let's assume I have a component like this
RowLayout {
MyItem {
Layout.fillWidth: true
Layout.fillHeight: true
... // other properties
}
MyItem {
Layout.fillWidth: true
Layout.fillHeight: true
... // other properties
}
}
in which MyItem.qml is defined like this
Rectangle {
... // other properties
// Layout.fillWidth: true
// Layout.fillHeight: true
}
Can I put Layout.fillWidth to MyItem, so that I don't need to repeat it in RowLayout ?
Can I put Layout.fillWidth to MyItem, so I don't need to repeat it in RowLayout ?
I think the question has the answer in it: if you don't want to repeat, just use the Repeater type. The documentation states that
Items instantiated by the Repeater are inserted, in order, as children of the Repeater's parent. The insertion starts immediately after the repeater's position in its parent stacking list. This allows a Repeater to be used inside a layout.
The example which follows in the documentation uses Row but the very same approach can be applied to other layouts, e.g. RowLayout. Actually, it works for any type with attached properties as per the Repeater nature ("insert items inside parent").
Here is an example. Assume we have defined an Example type.
import QtQuick 2.5
Rectangle {
property alias text: inner.text
color: "steelblue"
Text {
id: inner
anchors.centerIn: parent
font.pixelSize: 30
}
}
We can add the layout properties to our Example type inside the Repeater, for instance like this:
import QtQuick 2.5
import QtQuick.Window 2.2
import QtQuick.Layouts 1.1
Window {
id: window
width: 600
height: 400
visible: true
RowLayout {
id: row
anchors.fill: parent
Repeater {
model: 6
delegate : Example {
text: index
Layout.fillWidth: true // layout options added in the delegate
Layout.fillHeight: true
Layout.alignment: Qt.AlignCenter
Layout.maximumWidth: parent.width / model.length
}
}
}
}
The model property of the Repeater can be either an array of strings or another model, as usual.
This approach is flexible enough to combine several Repeaters to create more complex structures. See for instance the following example in which Text is used to fill the screen inside a GridLayout:
import QtQuick 2.5
import QtQuick.Window 2.2
import QtQuick.Layouts 1.1
Window {
id: window
width: 600
height: 400
visible: true
GridLayout {
id: grid
anchors.fill: parent
rows: 2
columns: 6
Repeater {
model: grid.columns
Text {
Layout.fillWidth: true
Layout.fillHeight: true
Layout.row: 0
Layout.column: index
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
text: index + 1 // index of the repeater as text
}
}
Repeater {
model: grid.columns
Text {
Layout.fillWidth: true
Layout.fillHeight: true
Layout.row: 1
Layout.column: index
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
text: index + 7
}
}
}
}
Yes, you can do that, but it will end in an error whenever you decide to use that component in a context where it has no attached property named Layout.fillWidth or, more in general, whenever you decide not to use it as a top element within a layout.

QML ListView with sections representing first letter of model's name role - sections are not visible

I have QML ListView, which shows customers from database via UeCustomerModel. Customers are sorted in model via MySQL statement. All works fine, but now I want to add sections into ListView, so the customers are sorted in sections by first letter criteria. Here is my ListView:
ListView
{
id: ueCustomersListView
Layout.fillWidth: true
Layout.fillHeight: true
Layout.alignment: Qt.AlignHCenter|Qt.AlignBottom
Layout.margins: 8
clip: true
spacing: 8
delegate: UeCustomerSelectorDelegate
{
ueParamWidth: ueCustomersListView.width
ueParamHeight: 128
ueParamImage: "image://ueCustomerModel/"+model.ueRoleImage
ueParamName: model.ueRoleCustomerName
ueParamTaxId: model.ueRoleCustomerTaxId
ueParamCurrentDebt: model.ueRoleCustomerCurrDebt
ueParamMaxDebt: model.ueRoleCustomerMaxDebt
} // delegate
section.property: model.ueRoleCustomerName // HERE RUNTIME ERROR OCCURS
section.criteria: ViewSection.FirstCharacter
section.delegate: UeCustomerSelectorSectionDelegate
{
ueParamWidth: ueCustomersListView.width
ueParamHeight: 128
ueParamName: section
} // section.delegate
Component.onCompleted:
{
model=ueCustomerModel;
} // Component.onCompleted
} // ListView
and here is section delegate:
import QtQuick 2.5
import QtQuick.Layouts 1.1
Rectangle
{
property int ueParamWidth
property int ueParamHeight
property string ueParamName
width: ueParamWidth
height: ueParamHeight
color: "#4682b4"
antialiasing: true
smooth: true
RowLayout
{
anchors.fill: parent
anchors.margins: 8
spacing: 8
Text
{
color: "#ffffff"
Layout.fillWidth: true
Layout.fillHeight: true
Layout.alignment: Qt.AlignHCenter|Qt.AlignVCenter
antialiasing: true
text: ueParamName
font.family: "Courier"
font.bold: true
font.pointSize: 12
clip: true
textFormat: Text.RichText
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
} // Text
} // RowLayout
} // Rectangle
Now, when ListView is shown, there are no sections visible. What did I miss, i.e., how do I correct section.property: model.ueRoleCustomerName statement to get alphabetical letters from customers (A, B, C, ...). I want ListView to section delegates according to Customer Name (delivered from database via model) first letter. Also, in mentioned line of code, I get QML runtime error Unable to assign [undefined] to QString. Why?
P.S.: model works perfectly (is fast, can search accross customers using TextField), I've tested it 5 times.
According to user sk2212's hint, I've upgraded the code with:
section.property: "ueParamName"
section.criteria: ViewSection.FirstCharacter
section.delegate: UeCustomerSelectorSectionDelegate
{
ueParamWidth: ueCustomersListView.width/2
ueParamHeight: ueCustomersListView.height/4
ueParamName: model.ueRoleCustomerName.subString(0,1)
} // section.delegate
and here is Section delegate:
import QtQuick 2.5
import QtQuick.Layouts 1.1
Rectangle
{
property int ueParamWidth
property int ueParamHeight
property string ueParamName
width: ueParamWidth
height: ueParamHeight
color: "#4682b4"
antialiasing: true
smooth: true
RowLayout
{
anchors.fill: parent
anchors.margins: 8
spacing: 8
Text
{
color: "#ffffff"
Layout.fillWidth: true
Layout.fillHeight: true
Layout.alignment: Qt.AlignHCenter|Qt.AlignVCenter
antialiasing: true
text: ueParamName
font.family: "Courier"
font.bold: true
font.pointSize: 12
clip: true
textFormat: Text.RichText
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
} // Text
} // RowLayout
} // Rectangle
Still does not work.
You have to use the section.property as a model data element:
delegate: UeCustomerSelectorDelegate
{
ueParamWidth: ueCustomersListView.width
ueParamHeight: 128
ueParamImage: "image://ueCustomerModel/"+model.ueRoleImage
ueParamName: model.ueRoleCustomerName
ueParamTaxId: model.ueRoleCustomerTaxId
ueParamCurrentDebt: model.ueRoleCustomerCurrDebt
ueParamMaxDebt: model.ueRoleCustomerMaxDebt
alphabet: model.ueRoleCustomerName.substring(0,1)
} // delegate
section.property: "alphabet"

Resources