I want to make a view that looks something like (I left off a column for brevity)
__________
| text |
|__________|
| |headr|
|____|_____|
|text|item1|
| |item2|
| | |
|text|item3|
| |item4|
| |item5|
| |item6|
|____|_____|
and have been trying to do this with a GridLayout. The problem is that I could have potentially many rows. It just depends on my model. So, I want to be able to have a repeater that will repeat the same n elements. However, it seems to only take one component. I would love to repeat one element but that is not how the GridLayout figures out spacing. So, it seems as if this is impossible except using dynamic object creation.
My code for the actual item trying to be repeated is this
Text {
Layout.alignment: Qt.AlignHCenter
text: abbr
color: "#545454"
}
VerticalRule {
Layout.fillHeight: true
}
ColumnLayout {
Repeater {
model: getModel()
Image {}
}
}
VerticalRule {
Layout.fillHeight: true
}
ColumnLayout {
Repeater {
model: getModel()
Image {}
}
}
So, is there any way to do this easily in qml or am I kind of on my own when it comes to this kind of super specific table ish format.
You can do it with the GridLayout; use multiple Repeaters, one for each column in the Gridlayout. The following snippet reproduces your text layout:
GridLayout {
columns: 2
Text {
Layout.columnSpan: 2
Layout.alignment: Qt.AlignLeft
text: 'text'
}
Rectangle {
height: 2
color: "black"
Layout.row: 1
Layout.columnSpan: 2
Layout.fillWidth: true
}
Text {
Layout.column: 1
Layout.row: 2
text: 'header'
}
Rectangle {
height: 2
color: "black"
Layout.row: 3
Layout.columnSpan: 2
Layout.fillWidth: true
}
Repeater {
model: list
Text {
Layout.column: 0
Layout.row: index + 4
Layout.alignment: Qt.AlignTop
text: label
}
}
Repeater {
model: list
Column {
Layout.column: 1
Layout.row: index + 4
Repeater {
model: items
Text { text: item }
}
}
}
ListModel {
id: list
ListElement {
label: 'text1'
items: [
ListElement { item: 'item1' },
ListElement { item: 'item2' }
]
}
ListElement {
label: 'text2'
items: [
ListElement { item: 'item3' },
ListElement { item: 'item4' },
ListElement { item: 'item5' },
ListElement { item: 'item6' },
ListElement { item: 'item7' }
]
}
ListElement {
label: 'text3'
items: [
ListElement { item: 'item8' },
ListElement { item: 'item9' }
]
}
}
}
Unless I find a way, You cant. You can use a ListView to make rows that they themselves have GridViews .... I think.
Add multiple Items as children to a delegate Item and use JavaScript to reparent them to the GridLayout when the delegate Item is completed.
The OP requests a solution that avoids using a wrapping Item, but a wrapping Item affords the cleanest solution to this problem and supports an arbitrary number of columns. Also, I have never seen a solution using a "wrapping Item" -- this is the page that comes up whenever I search for one.
GridLayout {
id: grid
anchors.centerIn: parent
columns: 2
Repeater {
model: [
{ column1: "text1", column2: ["item1","item2"] },
{ column1: "text2", column2: ["item3","item4","item5","item6","item7"] },
{ column1: "text3", column2: ["item8","item9"] },
]
delegate: Item {
objectName: "wrapping_item"
property int rowOffset: 4
Component.onCompleted: {
// reparent all child elements into the grid
while (children.length)
children[0].parent = grid;
}
Text {
objectName: "first_child"
Layout.alignment: Qt.AlignRight | Qt.AlignTop
Layout.column: 0
Layout.row: index + rowOffset
text: modelData.column1
}
Column {
objectName: "second_child_containing_more_children"
Layout.alignment: Qt.AlignLeft
Layout.column: 1
Layout.row: index + rowOffset
Repeater {
model: modelData.column2
delegate: Text { text: modelData }
}
}
}
}
// same header code as in other answer, just moved out of the way
Text {
text: "text"
Layout.columnSpan: 2
}
Rectangle {
height: 2
color: "blue"
Layout.row: 1
Layout.columnSpan: 2
Layout.fillWidth: true
}
Text {
text: "header"
Layout.row: 2
Layout.column: 1
}
Rectangle {
height: 2
color: "blue"
Layout.row: 3
Layout.columnSpan: 2
Layout.fillWidth: true
}
}
Related
On Qt 5.9.4, I have a ListView with model and delegate like below:
Rectangle {
Column {
Image {
id: img0
source: "images/img0.png"
}
ListView {
model: ListModel {
ListElement {
dimName: "D top"
}
ListElement {
dimName: "D mid"
}
ListElement {
dimName: "D bot"
}
ListElement {
dimName: "H top"
}
ListElement {
dimName: "H bot"
}
}
delegate: RowLayout {
StyledLabel {
text: dimName
enabled: true
}
Rectangle {
}
SpinBox {
editable: true
enabled: true
from: 0
to: 1000
stepSize: 1
}
}
}
}
}
The ListView doesn't show the ListElements correctly. I mean all the elements are shown on top of each other, and on top of the Image item in the code, like this:
Can anybody give me a hint on how to show ListElements in a vertical line, one below the other.
UPDATE
as suggested by #Amfasis in a comment, I explicitly determined the height of ListView item:
height: parent.height + 100
anchors.top: img0.bottom
After above modification, now the list looks better:
You have to set the height of the RowLayout, but to set it explicitly is of course not nice. So you can use the height of the first child, or a different one if you wish (actually in your case this would be the SpinBox probably)
delegate: RowLayout {
anchors.left: parent.left
anchors.right: parent.right
//height: children[0].height
height: spinbox.height //actually better in your case
StyledLabel {
text: dimName
enabled: true
}
Rectangle { }
SpinBox {
id: spinbox
editable: true
enabled: true
from: 0
to: 1000
stepSize: 1
}
}
Even better would be to take the max of all children, but I'm not sure how that would be done best
I have an model that consist of objects with some properties, e.g.:
ListModel {
id: fruitModel
ListElement {
name: "Apple"
color: "green"
cost: 2.45
}
ListElement {
name: "Orange"
color: "orange"
cost: 3.25
}
ListElement {
name: "Banana"
color: "yellow"
cost: 1.95
}
}
And now I want to display this model using GridLayout. For each property I want to use one element inside GridLayout, e.g:
GridLayout {
columns: 3
Text { text: "Apple" }
Rectangle { color: "green" }
SpinBox { value: 2.45 }
Text { text: "Orange" }
Rectangle { color: "orange" }
SpinBox { value: 3.25 }
Text { text: "Banana" }
Rectangle { color: "yellow" }
SpinBox { value: 1.95 }
}
The point is that I can easily change the columns property of GridLayout and make my layout narrower (e.g. to fit small screens). I can use Repeater to fill GridLayout. However such approach will fill my GridLayout in wrong order:
GridLayout {
columns: 3
Repeater {
model: fruitModel
Text { text: name }
}
Repeater {
model: fruitModel
Rectangle { color: color }
}
Repeater {
model: fruitModel
SpinBox { value: value }
}
}
And it is waste to use Layout.column and Layout.row attached properties since I want to easily change number of columns in GridLayout.
Is there any way to fill GridLayout with data from model row by row?
UPD1:
Behaviour that I want to get:
GridLayout {
columns: parent.width > 235 ? 3 : 1
Text { text: "Apple" }
Rectangle { color: "green"; width: 40; height: 40 }
SpinBox { value: 2 }
Text { text: "Orange" }
Rectangle { color: "orange"; width: 40; height: 40 }
SpinBox { value: 3 }
Text { text: "Banana" }
Rectangle { color: "yellow"; width: 40; height: 40 }
SpinBox { value: 1 }
}
UPD2:
Modified variant from #m7913d:
GridLayout {
id: layout
property int maxColumns: 3
columns: parent.width > 235 ? maxColumns : 1
Repeater {
model: fruitModel
Text {
text: name
Layout.row: layout.columns == maxColumns ? index : (maxColumns * index)
Layout.column: 0
}
}
Repeater {
model: fruitModel
Rectangle {
Layout.preferredWidth: 30
Layout.preferredHeight: 30
color: col
Layout.row: layout.columns == maxColumns ? index : (maxColumns * index + 1)
Layout.column: layout.columns == maxColumns ? 1 : 0
}
}
Repeater {
model: fruitModel
SpinBox {
value: val
Layout.row: layout.columns == maxColumns ? index : (maxColumns * index + 2)
Layout.column: layout.columns == maxColumns ? 2 : 0
}
}
}
It's working but not easy-to-modify solution plus sometimes during layout resize there is messages QGridLayoutEngine::addItem: Cell (1, 0) already taken.
I would use a Column filled by either Rows or Columns (or any other arrangement) as delegates.
Column {
id: rootCol
anchors.fill: parent
Repeater {
model: fruitModel
delegate: rootCol.width > 300 ? rowDel : colDel
}
Component {
id: rowDel
Row {
Text { width: 100; height: 50; text: model.name }
Rectangle { width: 50; height: 50; color: model.color }
SpinBox { width: 150; height: 50; value: model.cost }
}
}
Component {
id: colDel
Column {
Text { width: 100; height: 50; text: model.name }
Rectangle { width: 50; height: 50; color: model.color }
SpinBox { width: 150; height: 50; value: model.cost}
}
}
}
Or maybe:
Column {
id: rootCol
anchors.fill: parent
Repeater {
model: fruitModel
delegate: Flow {
anchors {
left: parent.left
right: parent.right
}
Text { width: 100; height: 50; text: model.name }
Rectangle { width: 50; height: 50; color: model.color }
SpinBox { width: 150; height: 50; value: model.value }
}
}
}
Here is yet another solution:
The Idea: add the children in the right order
For that we will need to make sure that all Items for a model entry.
GridLayout {
id: gl
columns: parent.width > 235 ? 3 : 1
}
Instantiator {
model: fruitModel
delegate: QtObject {
property Item text: Text { parent: gl; text: model.name }
property Item rect: Rectangle { parent: gl; color: model.color; width: 50; height: 50; }
property Item spin: SpinBox { parent: gl; value: model.cost; }
}
}
Note: This will fail when entries in the model will be inserted or reordered, as in this solution the entries are always appended.
Other than that, it will automatically support column changes
A possible approach is to extend this method with condition column/row indexes:
GridLayout {
id:gridLayout
columns: parent.width > 235 ? 3 : 1
Repeater {
model: fruitModel
implicitHeight: 50
Text {
text: name
Layout.row: gridLayout.columns == 3 ? index : 3 * index
Layout.column: 0
}
}
Repeater {
model: fruitModel
Rectangle {
color: model.color
implicitWidth: 50
implicitHeight: 50
Layout.row: gridLayout.columns == 3 ? index : 3 * index + 1
Layout.column: gridLayout.columns == 3 ? 1 : 0
}
}
Repeater {
model: fruitModel
SpinBox {
value: value
Layout.row: gridLayout.columns == 3 ? index : 3 * index + 2
Layout.column: gridLayout.columns == 3 ? 2 : 0
}
}
}
I need to create nested list view and as shown below, and highlight the main list and sub-list with different color
I have tried with ListView highlight but there are issue like the highlight showing for child as well as parent as shown
below image.
I am using the code from here with some minor modification.
Here is the full code
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick 2.2
import QtQuick.Controls 1.2
import QtQuick.Controls.Styles 1.1
ApplicationWindow {
id: loginWindow
//visibility: "Maximized"
visible: true
width: 720
height: 720
Item {
width: 200
height: 720
ListView {
id: list
anchors.fill: parent
model: nestedModel
delegate: categoryDelegate
highlight: Rectangle {
color: "#FF00AAFF" //"#FF59ACFF";
radius: 2
}
}
ListModel {
id: nestedModel
ListElement {
categoryName: "Veggies"
collapsed: true
// A ListElement can't contain child elements, but it can contain
// a list of elements. A list of ListElements can be used as a model
// just like any other model type.
subItems: [
ListElement {
itemName: "Tomato"
},
ListElement {
itemName: "Cucumber"
},
ListElement {
itemName: "Onion"
},
ListElement {
itemName: "Brains"
}
]
}
ListElement {
categoryName: "Fruits"
collapsed: true
subItems: [
ListElement {
itemName: "Orange"
},
ListElement {
itemName: "Apple"
},
ListElement {
itemName: "Pear"
},
ListElement {
itemName: "Lemon"
}
]
}
ListElement {
categoryName: "Cars"
collapsed: true
subItems: [
ListElement {
itemName: "Nissan"
},
ListElement {
itemName: "Toyota"
},
ListElement {
itemName: "Chevy"
},
ListElement {
itemName: "Audi"
}
]
}
}
Component {
id: categoryDelegate
Column {
width: 200
Rectangle {
id: categoryItem
border.color: "black"
border.width: 5
color: "#33FF5225"
height: 50
width: 200
Text {
anchors.verticalCenter: parent.verticalCenter
x: 15
font.pixelSize: 24
text: categoryName
}
Rectangle {
color: "red"
width: 30
height: 30
anchors.right: parent.right
anchors.rightMargin: 15
anchors.verticalCenter: parent.verticalCenter
MouseArea {
anchors.fill: parent
// Toggle the 'collapsed' property
onClicked: {
list.currentIndex = index
nestedModel.setProperty(index, "collapsed",
!collapsed)
}
}
}
}
Loader {
id: subItemLoader
// This is a workaround for a bug/feature in the Loader element. If sourceComponent is set to null
// the Loader element retains the same height it had when sourceComponent was set. Setting visible
// to false makes the parent Column treat it as if it's height was 0.
visible: !collapsed
property variant subItemModel: subItems
sourceComponent: collapsed ? null : subItemColumnDelegate
onStatusChanged: if (status == Loader.Ready) item.model = subItemModel
}
}
}
Component {
id: subItemColumnDelegate
Column {
property alias model: subItemRepeater.model
width: 200
Repeater {
id: subItemRepeater
delegate: Rectangle {
x: 10
color: "#33FF5225"
height: 40
width: 190
border.color: "black"
border.width: 2
Text {
anchors.verticalCenter: parent.verticalCenter
x: 30
font.pixelSize: 18
text: itemName
}
}
}
}
}
}
}
How can I overcome this issue. Basically I need to highlight Parent and child Item with different color.
Edit:
I can highlight parent list using the code
color:categoryDelegate.ListView.isCurrentItem ? "#FF00AAFF" : "#CCBBBBBB"
but coud'nt a find similar way to change the color of child list (sub list) on click.
Change the color property of the delegate in subItemRepeater to your choice.
Example
Component {
id: subItemColumnDelegate
Column {
...
Repeater {
id: subItemRepeater
delegate: Rectangle {
...
color: "purple"
...
}
}
}
}
Similarly change the color property categoryItem in the categoryDelegate.
Example
Component {
id: categoryDelegate
Column {
...
Rectangle {
id: categoryItem
...
color: "blue"
...
}
}
}
EDIT:
In that case the the overall concept is wrong. In the comment of the original code the author has written A ListElement can't contain child elements, but it can contain a list of elements. So we can't highlight the child item. But the following approach will give good result to you.
import QtQuick 2.0
Rectangle {
width: 360
height: 360
ListModel {
id: model1
ListElement {
name: "name"
}
ListElement {
name: "name"
}
ListElement {
name: "name"
}
}
ListModel {
id: model2
ListElement {
name: "inside"
}
ListElement {
name: "inside"
}
ListElement {
name: "inside"
}
}
ListView {
id: outer
model: model1
delegate: listdelegate
anchors.fill: parent
}
Component {
id: listdelegate
Item {
width: 100
height: col.childrenRect.height
Column {
id: col
anchors.left: parent.left
anchors.right: parent.right
Text {
id: t1
text: name
}
ListView {
id: insidelist
model: model2
property int collapseHeightFlag: childrenRect.height
delegate: Component {
id: delegate2
Item {
width: 100
height: col2.childrenRect.height
Column {
id: col2
anchors.left: parent.left
anchors.right: parent.right
Text {
id: name1
text: name
}
}
MouseArea {
anchors.fill: parent
onClicked: {
insidelist.currentIndex = index;
}
}
}
}
contentHeight: contentItem.childrenRect.height
height: 0
anchors.left: parent.left
anchors.right: parent.right
clip: true
highlight: Rectangle {
color: "pink"
radius: 2
}
focus: true
}
}
Rectangle {
color: "red"
width: 10
height: 10
anchors.right: parent.right
anchors.rightMargin: 5
anchors.top: parent.top
anchors.topMargin: 5
MouseArea {
anchors.fill: parent
onClicked: {
if(insidelist.height === insidelist.collapseHeightFlag) {
insidelist.height = 0;
}
else
insidelist.height = insidelist.collapseHeightFlag;
}
}
}
}
}
}
In my ui.qml I have:
RowLayout {
id: rowLayout2
x: 0
y: 397
width: 640
height: 50
clip: false
Text {
id: text4
width: 105
height: 32
text: qsTr("房间类型")
font.pixelSize: 17
wrapMode: Text.WordWrap
}
ComboBox {
id: comboBox1
activeFocusOnPress: false
model: ListModel {
id: cbItems
ListElement { text: "标准间"; color: "Yellow" }
ListElement { text: "三人间"; color: "Green" }
ListElement { text: "大床房"; color: "Brown" }
ListElement { text: "豪华套房"; color: "Blue" }
}
}
}
And I want to make a button that when clicked on duplicates this RowLayout below the original one, how do I do that?
Put it inside a Repeater and increment the model count when the button is clicked.
Button {
onClicked: {
repeater.model += 1;
}
}
...
Column {
Repeater {
model: 1
// Your rowLayout2 code
}
}
I'm using Qt5.2 anc C++ to implement an application and need to display a list with sections similar to the example image below:
(source: ngo-hung.com)
Please note I'm not implementing a mobile app and I don't need the alphabet index on the right. Any suggestions how I can achieve this other than implementing a QTreeView?
Thanks.
ListView in QML has built-in support for sections. In the following example, I have a ListModel where the section is defined via a "sec" role. The ListModel defines a section delegate as well as a contact delegate:
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
Page {
id: page
header: Frame {
background: Rectangle {
color: "red"
}
RowLayout {
width: parent.width
Item {
Layout.preferredWidth: 32
Layout.preferredHeight: 32
Button {
anchors.centerIn: parent
background: Item {}
icon.source: "https://raw.githubusercontent.com/Esri/calcite-ui-icons/master/icons/address-book-32.svg"
icon.width: 32
icon.height: 32
icon.color: "white"
}
}
Text {
Layout.fillWidth: true
text: qsTr("All Contacts")
color: "white"
}
}
}
anchors.fill: parent
ListView {
anchors.fill: parent
model: contacts
delegate: Frame {
width: ListView.view.width
RowLayout {
width: parent.width
Item {
Layout.preferredWidth: 32
Layout.preferredHeight: 32
Button {
anchors.centerIn: parent
background: Item {}
icon.source: "https://raw.githubusercontent.com/Esri/calcite-ui-icons/master/icons/user-32.svg"
icon.width: 32
icon.height: 32
icon.color: "green"
}
}
ColumnLayout {
Layout.fillWidth: true
Text {
Layout.fillWidth: true
text: fn
}
Text {
Layout.fillWidth: true
text: qsTr("Full name: %1 %2").arg(fn).arg(sn)
}
}
}
}
section.property: "sec"
section.delegate: Frame {
width: ListView.view.width
background: Rectangle {
color: "lightsteelblue"
}
Text {
text: section
color: "white"
}
}
}
ListModel {
id: contacts
ListElement {
sec: "B"; fn: "Branda"; sn: "Respass"
}
ListElement {
sec: "C"; fn: "Chana"; sn: "Hollin"
}
ListElement {
sec: "D"; fn: "Delisa"; sn: "Deak"
}
ListElement {
sec: "D"; fn: "Demetrius"; sn: "Zona"
}
ListElement {
sec: "D"; fn: "Dwain"; sn: "Mark"
}
}
}
You can Try it Online!