I am new to QML so kindly help me out as why QML throws error when trying to build this one
import QtQuick
import QtQuick.Window
import QtQuick.Controls 2.4
Window {
width: 640
height: 480
visible: true
title: qsTr("App title")
ListModel {
id: modelName
ListElement { name: "alpha" }
ListElement { name: "beeta" }
ListElement { name: "gamma" }
}
Component {
id: delegateName
Text {
text: modelName.name // QML throw error here
font.pixelSize: 32
}
}
ListView {
anchors.fill: parent
model: modelName
delegate: delegateName
clip: true
}
}
I'm building this using QT-Creator. Any help would be appreciated
In a delegate, try this one: text: model.name. Every delegate has its own model property with accordingly model's data
Related
I want to make two input text fields (let's call them X and Y) that are in page A,
X: code of the person, Y: name of the person,
Page A contains two text fields for input, and a combo box for selection (for delete purpose), an adding button, and a delete button,
Once the X and Y are filled in the text fields they are inputed using an 'add' button, I want them to be stored when the button is pressed so I can call them back in the page B, or in the combo box in page A,
Page B contains a combo box and a casual text output next to it, the X goes to the combo box, and the Y goes to the text that both are in page B
When I select in page B one X of the many X that already have been inputed, I want the text to change with what have been selected accordingly,
if I want to delete one of the X, I do so by selecting the combobox that exists in Page A and proceed pressing the delete button,
how to store both the X and Y in variables and insert them in comboboxes and the texts, and make a bond between them, connecting each name with its own code ?
PageA
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.VirtualKeyboard 2.14
Item {
id:pageA
width: 1920
height: 1080
property alias codeIn: code.text
property alias nameIn: name.text
TextField{
id:code
y: 0
width: 200
height: 50
placeholderText: qsTr("Type the code here")
}
TextField{
id:name
x: 0
y: 65
width: 200
height: 50
placeholderText: qsTr("Type the name here")
}
ComboBox{
id:selectionForDelete
x: 320
y: 65
width: 200
height: 50
}
Button{
id:adding
x: 29
y:155
text: "add"
onClicked: {
code.clear()
name.clear()
}
}
Button{
id:deleting
x: 355
y: 155
text: "delete"
}
}
PageB
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.VirtualKeyboard 2.14
Item {
id:pageB
width: 1920
height: 1080
ComboBox{
id:codeList
}
Text {
id: relatedName
y:70
text: qsTr("")
}
}
One very simple solution is to declare a ListModel in your main app and when you instantiate either PageA or PageB they will have read/write access to the ListModel.
//MainApp.qml
Page {
ListModel { id: listModel }
PageA { }
PageB { }
}
//PageA.qml
Page {
}
//PageB.qml
Page {
}
As an example, PageA gives the ability to add new users as well as delete existing users, whereas PageB gives the ability to search for existing users:
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
Page {
RowLayout {
anchors.fill: parent
PageA {
Layout.fillWidth: true
Layout.preferredWidth: 200
Layout.fillHeight: true
}
PageB {
Layout.fillWidth: true
Layout.preferredWidth: 200
Layout.fillHeight: true
}
}
ListModel {
id: listModel
function appendUser(code, name) { append( { code, name } ); }
}
}
//PageA.qml
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
Page {
ColumnLayout {
anchors.fill: parent
Frame {
Layout.fillWidth: true
Text { text: qsTr("PageA") }
}
Label { text: qsTr("Users") }
ListView {
Layout.fillWidth: true
Layout.fillHeight: true
model: listModel
delegate: Frame {
width: ListView.view.width
RowLayout {
width: parent.width
Text {
Layout.fillWidth: true
text: model.code + " " + model.name
}
Button {
text: qsTr("Delete")
onClicked: listModel.remove(model.index)
}
}
}
}
Label { text: qsTr("Create new user") }
TextField {
id: code
Layout.fillWidth: true
placeholderText: qsTr("Code")
}
TextField {
id: name
Layout.fillWidth: true
placeholderText: qsTr("Text")
}
Button {
enabled: code.text && name.text
text: qsTr("Add")
onClicked: listModel.appendUser(code.text, name.text)
}
}
}
//PageB.qml
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
Page {
ColumnLayout {
anchors.fill: parent
Frame {
Layout.fillWidth: true
Text { text: qsTr("PageB") }
}
Label { text: qsTr("Select Code") }
ComboBox {
id: comboBox
model: listModel
textRole: "code"
valueRole: "name"
}
Label { text: qsTr("Selected Name") }
Text {
text: comboBox.currentValue ?? ""
}
Item {
Layout.fillHeight: true
}
}
}
You can Try it Online!
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
Suppose, we have the following code:
import QtQuick 2.4
import QtQuick.Window 2.0
import QtQuick.Controls 1.2
import QtQuick.Layouts 1.1
Window {
id: win
width: 800
height: 600
ListModel {
id: listModel
ListElement { name: "element1" }
ListElement { name: "element2" }
ListElement { name: "element3" }
}
ColumnLayout {
anchors.centerIn: parent
width: 200
height: 200
ComboBox {
model: listModel
currentIndex: 1
Layout.fillWidth: true
}
ListView {
model: listModel
delegate: Text {
text: name
}
Layout.fillHeight: true
Layout.fillWidth: true
}
Button {
text: "Change model"
onClicked: {
listModel.get(1).name = "changed text";
//listModel.setProperty(1,"name","changed text"); this line not works too
}
}
}
}
So clicking the button have to change model's element with index 1. But changing the model affects only ListView. The ComboBox remains unchanged.
Why that happens? Is it bug or feature? Is there a way to update ComboBox after changing its model?
I had a similar problem, I used a workaround. In onClicked function of button, create copy of model, change it as you want and then assign it again to ListViews model:
ListView {
id: listView
...
}
Button {
onClicked: {
var copy = listView.model;
copy.get(1).name = "changed text";
listView.model = copy; }
}
}
I have got QML application and there should be a lot of dialogs. When user press ToolButton appropriate dialog should be visible so that user can modify contols of that dialog. Here is minimum code for that:
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Window 2.0
ApplicationWindow {
visible: true
property variant dialog: Loader{sourceComponent: wind}
toolBar: ToolBar {
Row {
anchors.fill: parent
ToolButton {
iconSource: "1.png"
checkable: true
checked: false
onClicked: dialog.visible=checked
}
}
}
Component {
id: wind
Window{
visible: false
flags: Qt.Dialog
Button{
text: "hello"
}
}
}
}
However when I press ToolButton dialog is not visible. What is wrong?
property variant dialog: Loader{sourceComponent: wind} - that is wrong, don't expect the element to show when declared as a property, it has to be a child of its parent component.
onClicked: dialog.visible=checked - this is wrong, you need to use the item property of the dialog to refer to the object the loader instantiates
Code that works:
ApplicationWindow {
visible: true
toolBar: ToolBar {
Row {
anchors.fill: parent
ToolButton {
checkable: true
checked: false
onClicked: dialog.item.visible = checked
}
}
}
Loader {
id: dialog
sourceComponent: wind
}
Component {
id: wind
Window {
width: 100
height: 100
Button {
text: "hello"
}
}
}
}
I have a ListView nested inside another ListView in my qml. Both the ListViews are being populated by a data models which I get from a C++ class SelectRoomView. This is the code for my qml file:
import bb.cascades 1.0
import Data.SelectRoomView 1.0
import "commons"
Page {
id: pageSelectARoom
property alias currentPage: pageSelectARoom
property string propId
attachedObjects: [
SelectRoomView {
id: selRoom
}
]
onPropIdChanged: {
selRoom.propId = propId;
}
Container {
layout: StackLayout {
orientation: LayoutOrientation.TopToBottom
}
background: Color.create("#FFFFFF")
ScrollView{
horizontalAlignment: HorizontalAlignment.Fill
objectName: "scrollView"
ListView {
id: ruleList
dataModel: selRoom.modelRule
listItemComponents: [
ListItemComponent {
id: outerList
Container{
Label {
id: outerLabel
text: ListItemData.ruleName
textStyle.fontSize: FontSize.XXSmall
textStyle.color: Color.Black
}
Label{
id: outerId
text: ListItemData.ruleId
textStyle.fontSize: FontSize.XXSmall
textStyle.color: Color.Black
visible: true
}
ListView{
id: roomList
dataModel: selRoom.modelRoom //I get the message ReferenceError: Can't find the variable: selRoom
listItemComponents: [
ListItemComponent {
id: innerList
Label{
id: innerLabel
text: ListItemData.name
textStyle.fontSize: FontSize.XXSmall
textStyle.color: Color.Black
}
}
]
}
}
}
]
}
}
}
}
Upon running the app, ruleName of ruleList shows but not the innerLabel of roomList. The console displays the following message:
asset:///SelectARoom.qml:100: ReferenceError: Can't find variable: selRoom
asset:///SelectARoom.qml:100: ReferenceError: Can't find variable: selRoom
i.e., selRoom is not in scope for roomList (the nested list).
What am I doing wrong?
I cannot find a documentation of the ListView.listItemComponents property but it looks like some kind of delegate.
If the sub-list is part of the first list's delegate, it might not have access to global variables.
The QML docs state that delegates can access their position index and the variables of the model only.
Try to access other values outside the delegate's scope like to see whether that works
ListView {
id: roomList
Component.oncompleted: console.log(ruleList.height)
...
}
I found a solution, but it is not very efficient. The following is the new code:
Page {
id: pageSelectARoom
property alias currentPage: pageSelectARoom
property string propId
attachedObjects: [
SelectRoomView {
id: selRoom
}
]
onPropIdChanged: {
selRoom.propId = propId;
}
Container {
layout: StackLayout {
orientation: LayoutOrientation.TopToBottom
}
background: Color.create("#FFFFFF")
ScrollView{
horizontalAlignment: HorizontalAlignment.Fill
objectName: "scrollView"
ListView {
id: ruleList
dataModel: selRoom.modelRule
function getModelMain() { //the object 'selRoom' is accessible from this function.
return selRoom.modelRoom;
}
listItemComponents: [
ListItemComponent {
id: outerList
Container{
id: ruleContainer
function getModel() {
return ruleContainer.parent.getModelMain();
}
Label {
id: outerLabel
text: ListItemData.ruleName
textStyle.fontSize: FontSize.XXSmall
textStyle.color: Color.Black
}
Label{
id: outerId
text: ListItemData.ruleId
textStyle.fontSize: FontSize.XXSmall
textStyle.color: Color.Black
visible: true
}
ListView{
id: roomList
dataModel: ruleContainer.getModel()
listItemComponents: [
ListItemComponent {
id: innerList
Label{
id: innerLabel
text: ListItemData.name
textStyle.fontSize: FontSize.XXSmall
textStyle.color: Color.Black
}
}
]
}
}
}
]
}
}
}
}
Is there a better way to do this or is there an alternate more efficient solution? Waiting for response. Thanks.