QML ListView delegate with Component Row does't render correctly - qml

And now I have a problem with my qml script. here's the simple code:
import QtQuick 2.0
import QtQuick.Window 2.0
import QtQuick.Controls 1.4
import QtQuick.Layouts 1.0
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Rectangle {
id: root
anchors.fill: parent
color: "green"
SystemPalette { id: activePalette }
ColumnLayout {
id: rightPanel
Layout.fillHeight: true
Layout.fillWidth: true
Layout.rightMargin: 10
anchors.fill: parent
Component.onCompleted: {
console.log(this, width, parent, parent.width)
}
ListView {
spacing: 2
Layout.fillHeight: true
Layout.fillWidth: true
model: ListModel {
ListElement {
name: "nihao"
value: "1"
}
ListElement {
name: "fds"
value: "2"
}
ListElement {
name: "fdssd"
value: "4"
}
}
delegate: Component {
// Rectangle {
// color: "yellow"
// height: 40
// width: parent.width
Row {
anchors.fill: parent
spacing: 2
height: 40
width: 200
Rectangle {
color: activePalette.window
height: 25
width: 100
border.color: "white"
border.width: 3
Text {
anchors.centerIn: parent
text: name
}
}
Rectangle {
color: activePalette.window
height: 25
width: 100
border.color: "white"
border.width: 3
Text {
anchors.centerIn: parent
text: value
}
}
}
//}
}
}
}
}
}
The code does't display correctly when I use qmlscene to render it, it even render nothing if the ListModel is too long.
But, if I uncomment out the "Rectangle" code in delegate component, it works well. So I'm comfused with the difference between Reactangle and Row for that they are all inherited from Item. And what should be placed into the delegate component as its direct child?

Related

TableView is overlapping TabBar

I have this inside of my main.qml:
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import QtQuick.Controls.Imagine
ApplicationWindow {
visible: true
width: Screen.width
height: Screen.height
title: "Portmod"
TabBar {
id: bar
width: parent.width
TabButton {
text: "Manage"
width: implicitWidth
}
TabButton {
text: "Search"
width: implicitWidth
}
}
StackLayout {
width: parent.width
height: parent.height
currentIndex: bar.currentIndex
TableView {
id: manageTab
columnSpacing: 1
rowSpacing: 1
clip: true
model: installed_pkgs_model
selectionModel: ItemSelectionModel {}
delegate: Rectangle {
implicitWidth: 300
implicitHeight: 50
color: selected ? "blue" : "lightgray"
required property bool selected
Text {
text: display
}
}
}
Item {
id: searchTab
}
}
}
It's displaying my TableView and TabBar with two TabButtons fine, but the TableView is overlapping with the TabBar and I have to drag the TableView out of the way to see it.
I'd like the TableView to be moved down so the TabBar is always visible, but I'm not sure how to do this. The layout system is a bit confusing to me.
A simple way would be to move the TabBar into a header:
header: TabBar {
id: bar
width: parent.width
TabButton {
text: "Manage"
width: implicitWidth
}
TabButton {
text: "Search"
width: implicitWidth
}
}
Using layouts, you could do it like this:
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import QtQuick.Controls.Imagine
import Qt.labs.qmlmodels 1.0
ApplicationWindow {
width: Screen.width
height: Screen.height
title: "Portmod"
visible: true
ColumnLayout {
anchors.fill: parent
spacing: 0
TabBar {
id: bar
Layout.fillWidth: true
TabButton {
text: "Manage"
width: implicitWidth
}
TabButton {
text: "Search"
width: implicitWidth
}
}
StackLayout {
currentIndex: bar.currentIndex
Layout.fillWidth: true
Layout.fillHeight: true
TableView {
id: manageTab
columnSpacing: 1
rowSpacing: 1
clip: true
model: TableModel {
TableModelColumn { display: "name" }
TableModelColumn { display: "color" }
rows: [
{
"name": "cat",
"color": "black"
},
{
"name": "dog",
"color": "brown"
},
{
"name": "bird",
"color": "white"
}
]
}
selectionModel: ItemSelectionModel {}
delegate: Rectangle {
implicitWidth: 300
implicitHeight: 50
color: selected ? "blue" : "lightgray"
required property bool selected
required property string display
Text {
text: display
}
}
}
Item {
id: searchTab
}
}
}
}

How can I make a custom delegate for my ListView?

I'm trying to make a custom ListView (AppDataListView) that can be used for data manipulation (essentially having a bunch of inputs). The problem I'm having is finding a way to tell AppDataListView what input types to use in the delegate of the ListView.
I currently try and do this by creating a loader in the ListView delegate, setting its source to a property of type Component, and then when I create an instance of AppDataListView, I specify the Component... However, I don't have any access to the model data, so it's kind of pointless.
Does anyone know how I can accomplish this?
Main.qml
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick .Controls 2.12
import QtQuick.Layouts 1.12
Window {
width: 640
height: 480
visible: true
ListModel {
id: sampleData
ListElement {
itemId: 1
name: "Name1"
}
ListElement {
itemId: 2
name: "Name2"
}
ListElement {
itemId: 3
name: "Name3"
}
ListElement {
itemId: 4
name: "Name4"
}
}
AppDataListView {
anchors.fill: parent
headers: ["ID", "Name"]
model: sampleData
delegate: Component {
RowLayout {
anchors.fill: parent
TextArea {
// I can't access itemId from here even though this is loaded into the delegate.
text: itemId
}
TextArea {
// I can't access name from here even though this is loaded into the delegate.
text: name
}
}
}
}
}
AppDataListView.qml
import QtQuick 2.0
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
Rectangle {
property var headers: []
property alias model: listView.model
property alias listView: listView
required property Component delegate;
id: root
color: "#bdbdbd"
ColumnLayout {
anchors.fill: parent
anchors.margins: 1
spacing:0
RowLayout {
spacing: 1
Layout.fillWidth: true
Layout.preferredHeight: childrenRect.height
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
Item {
Layout.minimumWidth: 30
Layout.maximumWidth: 30
}
Repeater {
id: headerRepeater
model: headers
delegate: Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: childrenRect.height
gradient: Gradient {
GradientStop { position: 0.0; color: "#FFFFFF" }
GradientStop { position: 0.5; color: "#F1F1F1" }
GradientStop { position: 1.0; color: "#FFFFFF" }
}
Label {
text: modelData
padding: 5
anchors.centerIn: parent
}
}
}
}
ListView {
id: listView
Layout.fillWidth: true
Layout.fillHeight: true
interactive: true
clip: true
boundsBehavior: Flickable.StopAtBounds
spacing: 1
ScrollBar.vertical: ScrollBar {
active: true
policy: ScrollBar.AlwaysOn
}
delegate: RowLayout {
width: parent.width
spacing: 1
Button {
id: rowBtn
Layout.minimumWidth: 30
Layout.maximumWidth: 30
background: Rectangle {
gradient: Gradient {
GradientStop { position: 0.0; color: (rowBtn.down ? "#56aff5" : rowBtn.hovered ? "#d9ebf9" : "#FFFFFF") }
GradientStop { position: 0.5; color: (rowBtn.down ? "#1b93f1" : rowBtn.hovered ? "#a4b2bd" : "#F1F1F1") }
GradientStop { position: 1.0; color: (rowBtn.down ? "#56aff5" : rowBtn.hovered ? "#d9ebf9" : "#FFFFFF") }
}
}
}
Loader {
sourceComponent: root.delegate
}
}
}
}
}
Capturing #Amfasis excellent comments to the question and adding some more detail....
Change your Loader reference to this:
Loader {
sourceComponent: root.delegate
property int itemId: model.itemId
property string name: model.name
}
fixes it. Here's why....
A Component declaration is put into the QML namespace hierarchy where it is declared not where it is instantiated at runtime. In other words, by declaring the delegate within main.qml, it can only see that namespace regardless of where it is instantiated at (in this case in AppDataListView).
More info here:
https://doc.qt.io/qt-5/qml-qtqml-component.html#creation-context
As #Amfasis pointed out, the workaround in this case is to declare properties on the Loader to pass in the model references you need. In this case the Loader acts as a bridge of sorts from the ListView namespace over to the delegate Component's namespace in main.qml.
More info on that here:
https://doc.qt.io/qt-5/qml-qtquick-loader.html#using-a-loader-within-a-view-delegate

GridLayout Arrangement

Following is my main.qml:
Window {
id: window
visible: true
width: 800
height: 480
title: qsTr("Hello World")
ListModel {
id: _listModel
ListElement {
textData: "E1"
isEnabled: false
}
ListElement {
textData: "E2"
isEnabled: false
}
ListElement {
textData: "E3"
isEnabled: false
}
ListElement {
textData: "E4"
isEnabled: false
}
ListElement {
textData: "E5"
isEnabled: false
}
ListElement {
textData: "E6"
isEnabled: false
}
}
ListView {
id: _listview
model: _listModel
width: 100
height: parent.height
anchors.right: parent.right
delegate: Item {
width: parent.width
height: 50
anchors.right: parent.right
Component.onCompleted:
{
if (isEnabled)
visibleRecs++;
}
RowLayout {
Text {
id: itemText
text: qsTr(textData)
}
CheckBox {
height: 30
width: height
checked: isEnabled
onCheckedChanged: {
isEnabled = checked
}
}
}
}
}
ScrollView {
id: _scrollView
width: parent.width / 2
height: parent.height
clip: true
GridLayout {
id: _gridLayout
anchors.fill: parent
anchors.horizontalCenter: parent.horizontalCenter
columnSpacing: 10
rowSpacing: 10
columns: 2
Repeater {
model: _listModel
Loader {
id: _loader
sourceComponent: isEnabled ? _recComponent : null
onLoaded: {
item.text = textData
}
}
}
}
}
Component {
id: _recComponent
Rectangle {
property alias text : _txt.text
id: _rec
width: 100
height: 50
radius: 5
color: "yellow"
Text {
id: _txt
anchors.centerIn: parent
}
}
}
}
The above code creates following (when all check boxes are ticked):
When all checkboxes are ticked:
When checkbox E3 is unchecked:
I want the items to be rearranged in the gridlayout if any of the item goes invisible.
E.g. in the above case when E3 is unchecked, I want my view to be something like this:
Please let me know, if it is possible to achieve. Thanks in advance.
The problem is that you still instantiate the Loader, you just set the sourceComponent to null. You have to make the item invisible to not use space in the GridLayout (or put width/height to 0)
ScrollView {
id: _scrollView
width: parent.width / 2
height: parent.height
clip: true
GridLayout {
id: _gridLayout
anchors.fill: parent
anchors.horizontalCenter: parent.horizontalCenter
columnSpacing: 10
rowSpacing: 10
columns: 2
Repeater {
model: _listModel
Loader {
id: _loader
visible: isEnabled
sourceComponent: isEnabled ? _recComponent : null
onLoaded: {
item.text = textData
}
}
}
}
}

ReferenceError: ueUserInfoListView is not defined

I am working on QML/Qt app and I have following ListView, names ueUserInfoListView:
ListView
{
id: ueUserInfoListView
antialiasing: true
Layout.alignment: Qt.AlignHCenter|Qt.AlignVCenter
Layout.fillWidth: true
Layout.fillHeight: true
Layout.margins: 8
clip: true
spacing: 64
orientation: ListView.Horizontal
highlightFollowsCurrentItem: true
delegate: Image
{
id: ueUserInfoListViewDelegate
source: "image://uePeopleModel/"+model.ueRoleImage
opacity: 0.3
fillMode: Image.PreserveAspectFit
function ueDoOpacity()
{
if(ueUserInfoListViewDelegate===ueUserInfoListView.currentItem)
opacity=1.0
else
opacity=0.3
}
Behavior on opacity
{
NumberAnimation
{
duration: 300
} // NumberAnimation
} // Behavior
Component.onCompleted:
{
ueUserInfoListViewDelegate.focusChanged.connect(ueDoOpacity)
} // Component.onCompleted
} // delegate
Component.onCompleted:
{
model=uePeopleModel
} // Component.onCompleted
preferredHighlightBegin: width/2-70
preferredHighlightEnd: width/2+70
highlightRangeMode: ListView.StrictlyEnforceRange
currentIndex: count/2
} // ListView
I am tryigin to call ueUserInfoListView from another separated (in terms of file) Item, named ueProductSelector:
import QtQuick 2.5
import QtQuick.Layouts 1.2
import si.mikroelektronika 1.0
Item
{
id: ueProductSelector
antialiasing: true
clip: true
Rectangle
{
id: ueProductSelectorWrapper
radius: 16
gradient: Gradient
{
GradientStop
{
position: 0
color: "#ffffff"
} // GradientStop
GradientStop
{
position: 1
color: "#000000"
} // GradientStop
} // Gradient
border.color: "#4682b4"
border.width: 1
antialiasing: true
anchors.centerIn: parent
anchors.fill: parent
ColumnLayout
{
anchors.margins: parent.radius/2
spacing: 0
antialiasing: true
anchors.fill: parent
anchors.centerIn: parent
GridView
{
id: ueProductGridView
antialiasing: true
clip: true
Layout.fillWidth: true
Layout.fillHeight: true
cellWidth: 144
cellHeight: 144
model: ueProductsModel
delegate: Rectangle
{
radius: 16
clip: true
width: ueProductGridView.cellWidth-8
height: ueProductGridView.cellHeight-8
border.color: "#4682b4"
antialiasing: true
gradient: Gradient
{
GradientStop
{
position: 0
color: "#000000"
ParallelAnimation on color
{
id: ueProductSelectorDelegateMouseAreaAnimation
loops: 1
running: false//ueDelegateMouseArea.pressed
ColorAnimation
{
from: "#4682b4"
to: "#000000"
duration: 100
} // ColorAnimation
} // ParallelAnimation
} // GradientStop
GradientStop
{
position: 1
color: "#ffffff"
} // GradientStop
} // Gradient
MouseArea
{
id: ueDelegateMouseArea
anchors.fill: parent
onClicked:
{
var selectedIndex=ueProductGridView.currentIndex=index;
ueProductSelectorDelegateMouseAreaAnimation.running=true;
ueProductGridView.currentIndex=index;
ueOrdersModel.ueSetRecordValues(ueUserInfoListView.model.get(ueUserInfoListView.currentIndex).ueRoleId,
uePlacesListView.model.get(uePlacesListView.currentIndex).ueRoleId,
ueProductGridView.model.get(selectedIndex).ueRoleProductId,
1);
} // onClicked
} // MouseArea
ColumnLayout
{
anchors.centerIn: parent
anchors.fill: parent
antialiasing: true
spacing: 8
Image
{
Layout.fillWidth: true
Layout.fillHeight: false
Layout.alignment: Qt.AlignCenter|Qt.AlignTop
Layout.topMargin: ueProductSelectorWrapper.radius+4
fillMode: Image.PreserveAspectFit
horizontalAlignment: Image.AlignHCenter
verticalAlignment: Image.AlignVCenter
antialiasing: true
source: "image://ueProductsModel/"+model.ueRoleImage
} // Image
Text
{
Layout.fillWidth: true
Layout.fillHeight: true
Layout.alignment: Qt.AlignCenter|Qt.AlignBottom
Layout.bottomMargin: ueProductSelectorWrapper.radius+4
color: "#000000"
text: model.ueRoleProductName
wrapMode: Text.WordWrap
font.family: "Courier"
textFormat: Text.RichText
font.bold: true
font.pointSize: 10
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
} // Text
} // ColumnLayout
} // delegate
Component.onCompleted:
{
ueProductSelectorOpacityAnimator.running=true
}
} // GridView
} // ColumnLayot
} // Rectangle
} // Item
and I get following error:
qrc:/gui/items/UeProductSelector.qml:126: ReferenceError:
ueUserInfoListView is not defined
Why?
Well, you should not do this. Using an ID of another component in a seperate QML component file is evil!
Try to avoid this at all costs. It will couple your QML components and they are not really reuseable.
So, to solve your problem you should pass the ListView component as a property to your ueProductSelector component:
import QtQuick 2.5
import QtQuick.Layouts 1.2
import si.mikroelektronika 1.0
Item
{
id: ueProductSelector
antialiasing: true
clip: true
property ListView ueUserInfoListView
// [...]
}
Then you should be able to call
ueOrdersModel.ueSetRecordValues(ueUserInfoListView.model.get(ueUserInfoListView.currentIndex).ueRoleId,
uePlacesListView.model.get(uePlacesListView.currentIndex).ueRoleId,
ueProductGridView.model.get(selectedIndex).ueRoleProductId,
1);
in your second component.
The ListView component is passed as a reference of (QObject*) and should not be performance critical.
If the MouseArea was declared in UeDelegate.qml, and UeListView.qml (imaginary file names for the sake of the example) contained a ListView that specifies a UeDelegate as its delegate, this would work. With the information you've given us, we can only assume that the ListView is not an ancestor of the MouseArea.
To elaborate on this concept:
The component instance scope hierarchy extends to out-of-line components, too. In the following example, the TitlePage.qml component creates two TitleText instances. Even though the TitleText type is in a separate file, it still has access to the title property when it is used from within the TitlePage. QML is a dynamically scoped language - depending on where it is used, the title property may resolve differently.
// TitlePage.qml
import QtQuick 2.0
Item {
property string title
TitleText {
size: 22
anchors.top: parent.top
}
TitleText {
size: 18
anchors.bottom: parent.bottom
}
}
// TitleText.qml
import QtQuick 2.0
Text {
property int size
text: "<b>" + title + "</b>"
font.pixelSize: size
}

Custom Button with conditional image and conditional text

I have the following custom QML Button:
import QtQuick.Controls 1.4
import QtQuick 2.5
import QtQuick.Layouts 1.2
import QtMultimedia 5.5
Rectangle {
id: ueButton
property string ueText: ""
property string ueIconPath: ""
width: 256
height: 128
signal ueSignalButtonClicked
gradient: Gradient {
GradientStop {
position: 0
color: "#ffffff"
SequentialAnimation on color {
id: ueButtonClickAnimation
loops: 1
running: false
ColorAnimation {
from: "#ffffff"
to: "#000000"
duration: 25
} // ColourAnimation
ColorAnimation {
from: "#000000"
to: "#ffffff"
duration: 25
} // ColorAnimation
} // SequentialAnimation
} // GradientStop
GradientStop {
position: 0.418
color: "#000000"
} // GradientStop
} // Gradient
border.color: "steelblue"
border.width: 1
radius: 4
antialiasing: true
smooth: true
ColumnLayout {
anchors.fill: parent
antialiasing: true
Image {
Layout.fillWidth: true
Layout.fillHeight: true
Layout.alignment: Qt.AlignHCenter|Qt.AlignTop
Layout.margins: 8
asynchronous: true
horizontalAlignment: Image.AlignHCenter
verticalAlignment: Image.AlignVCenter
fillMode: Image.PreserveAspectFit
smooth: true
source: ueIconPath
} // Image
Text {
text: ueText
Layout.fillWidth: true
Layout.fillHeight: true
Layout.alignment: Qt.AlignHCenter|Qt.AlignVCenter
Layout.topMargin: 8
Layout.leftMargin: 8
Layout.rightMargin: 8
Layout.bottomMargin: 16
color: "#ffffff"
font.bold: true
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
font.pointSize: 16
textFormat: Text.RichText
} // Text
} // ColumnLayout
MouseArea {
id: ueButtonMouseArea
antialiasing: true
anchors.fill: parent
onClicked: {
ueButtonClickAnimation.running=true
ueSignalButtonClicked()
}
} // MouseArea
} // ueButton
This works fine, but what I want to do now is to solve the following two specular issues:
If urtext is empty the corresponding text is ignored and whole ColumnLayout place is reserved for Image
If ueIconPath is empty, the corresponding image is ingnored and the whole ColumnLayout is reserved for Text
How can I achieve that?
As #BaCaRoZzo already suggested, using styles is preferable solution in this case since the item itself is still Button and so you can use all its properties and signals, including text property to pass your ueText text.
As for hiding elements inside, you can use visible property as suggested below:
UeButton.qml
Button {
id: ueButton
style: ButtonStyle {
background: Rectangle {
color: control.pressed ? "#CCC" : "#DEDEDE"
border.width: 1
border.color: "#999"
radius: 3
}
label: ColumnLayout {
id: layout
spacing: 10
Image {
Layout.alignment: Qt.AlignHCenter
source: control.iconSource
visible: control.iconSource !== ""
}
Text {
text: control.text
visible: control.text !== ""
}
}
}
}
Now using of this component is similar to regular Button:
UeButton {
anchors.centerIn: parent
text: "Some text"
iconSource: "data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7"
}
Commenting out text or iconSource will change the button view