How to dynamically update a child combobox from parent combobox - qt

I have a QML app that has two combo boxes, one is parent (country) combo box, other one is child (city) combo box. When selecting a country, the child combo box should have cities of the country at the same time.
I have a code that first combo box selects country but it doesn't set the list model of city's combo box. It sets after reopening the app.
import QtQuick 2.11
import QtQuick.Window 2.11
import QtQuick.Controls 2.4
import QtQuick.Layouts 1.3
import Qt.labs.settings 1.0
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
property alias comboBox: comboBox2
Settings{
property alias country : comboBox2.currentIndex
}
Dialog {
id: dialog
title: "Select Country"
implicitWidth: parent.width
implicitHeight: parent.height/2
Column{
ComboBox {
id: comboBox2
x: 199
y: 176
width: 277
height: 48
currentIndex: 0
flat: false
model: ["USA","Russia","Iran"]
}
}
}
ColumnLayout{
anchors.horizontalCenter: parent.horizontalCenter
spacing:20
width: parent.width
Button{
anchors.horizontalCenter: parent.horizontalCenter
id:select_country
text:"select country"
onClicked: dialog.open()
}
RowLayout{
anchors.horizontalCenter: parent.horizontalCenter
spacing:5
width: parent.width
Text {
text:"Select City: "
}
ComboBox {
id: comboBox1
x: 199
y: 176
width: 277
height: 48
model: ListModel {
id: model1
dynamicRoles: true
Component.onCompleted: {
coord_combo()
}
function coord_combo(){
var i = comboBox1.currentIndex
if (comboBox2.currentIndex===0){
comboBox1.model = ["New York","Washington","Houston"]
return comboBox1
}
else if (comboBox2.currentIndex === 1){
comboBox1.model = ["Moscow","Saint Petersburg","Novosibirsk"]
return comboBox1
}
else if (comboBox2.currentIndex === 2){
comboBox1.model = ["Tehran","Tabriz","Shiraz"]
return comboBox1
}
}
}
}
}
}
}
I used javascript functions to change Material QML Theme using combo box. But the same logic doesn't work for updating other combo box's list model. Is there any way to update the child combobox dynamically using qml and javascript?

You should use onCurrentIndexChanged signal in parent ComboBox and update the child whenever it is triggered. Something like this;
onCurrentIndexChanged:
{
if(currentIndex === 0)
comboBox1.model = ["New York", "Washington", "Houston"]
...
}
You can go further and write a function to get the cities of current country. Then you can update the model of child ComboBox whenever the currentIndex of parent ComboBox changes.

Related

Qt QML specify ListView delegate property from outside

I have a ChoicePage.qml like this:
import QtQuick 2.9
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3
Item {
property alias searchBar: searchBar
property alias model: listView.model
property alias itemDelegate: listView.delegate
ColumnLayout {
height: parent.height
width: parent.width * 0.9
anchors.horizontalCenter: parent.horizontalCenter
TextField {
id: searchBar
Layout.preferredHeight: parent.height * 0.1
Layout.preferredWidth: parent.width
placeholderText: qsTr("Search..")
}
ListView {
id: listView
Layout.fillHeight: true
Layout.preferredWidth: parent.width
ScrollBar.vertical: ScrollBar {}
clip: true
spacing: height * 0.01
}
}
}
And a TestChoicePage.qml like this:
import QtQuick 2.0
ChoicePage {
model: proxy.list // c++ proxy that expose a list of QObject*
itemDelegate: RadioButton {
height: ListView.view.height * 0.1
width: ListView.view.width
text: modelData.description
}
}
I would like to have the RadioButton delegate item inside ChoicePage and define the modelData.description or whatelse the page needs from TestChoicePage, Test2ChoicePage and so on.
Is it possible?
Model's data cannot be accessed outside the delegate. Any model related bindings have to defined within the delegate.
Non model related properties of the delegate can be controlled externally.
New properties can be defined outside the delegate. These new properties can be binded to the delegate's properties.
See example below:
List.qml:
ListView {
width: 200
height: 200
spacing: 5
property string rectColor: ""
delegate: Rectangle{
width: parent.width
height: parent.height / 5
color: rectColor
required property string modelProperty
RadioButton{
text: parent.modelProperty
}
}
}
main.qml:
MyList{
model: myCppList
rectColor: "green"
}

Dynamically add more combobox without resetting exiting data

I am looking to dynamically add more Combobox to my view. The code works fine and adds a new Combobox on the click of the button.
However, when I add a new Combobox, the data in existing Comboboxes are reset to default. How do I just append another Combobox without resetting the earlier selected values. I will prefer not to save the existing values in some variable
Here is my code
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.3
Page {
id : somepageid
property int modelList: 0
property int counter: 0
// Combobox Listmodel
ListModel{
id: listmodel
ListElement{
elem: "A"
}
ListElement{
elem: "B"
}
ListElement{
elem: "C"
}
}
// Add more item to Listview
function addMore(){
if(counter < listmodel.count){
counter++
modelList++
listid.model = modelList
}
}
// Button
Button{
id: testButton
text: "Click to add Combobox"
onClicked: {
addMore()
}
}
// Listview
ListView{
id: listid
model: modelList
anchors.top: testButton.bottom
height: listid.model * 40
delegate: Row{
ComboBox{
id: combo
textRole: "elem"
model:listmodel
}
}
}
}
The problem is you are resetting the ListView each time you do a listid.model = modelList.
You need to have a fixed model for your listview and do the changes there.
An example (applied to your code) could look like this:
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.3
Page {
id : somepageid
property int counter: 0
// ListView model
ListModel{
id: listmodel
}
// Add more item to Listview
function addMore(){
if(counter < 3){
counter++
listmodel.append({elements: [{elem: "A"}, {elem: "B"}, {elem: "C"}]})
}
}
// Button
Button{
id: testButton
text: "Click to add Combobox"
onClicked: {
addMore()
}
}
// Listview
ListView{
id: listid
model: listmodel
anchors.top: testButton.bottom
height: listid.model.count * 40
delegate: Row{
ComboBox{
id: combo
textRole: "elem"
model: elements
}
}
}
}

How to have a numerical model start at 1 in a combobox from QML

In the combobox from qml we can give them a model with a numerical value.
The options of the combobox then start from 0 to the model value minus 1.
What i want is to show the values on the combobox plus 1.
But still, if i try to access, with qml javascript, the value that is selected in that combobox is the original value, what i mean is, if it's the first option it's the value 0. The numbers shall only start from 1 in the display part of combobox.
I'm going to give an example (with a model=32):
display of the combobox
the options:
what i want is them to start at one, like this:
The code used was the following:
import QtQuick 2.9
import QtQuick.Controls 2.0
ApplicationWindow {
id: window
title: "Stack"
visible: true
height: 200
width: 400
Item {
id: page
anchors.fill: parent
width:parent.width
height: parent.height
Column{
width:parent.width
spacing:10
ComboBox {
id:comboBox
model: 32
objectName: "test"
implicitHeight: 30
displayText: currentText
// delegate:
// Button {
// id:buttonCombo
// width: parent.width
// text: index+1
// height:40
// contentItem: Text {
// text: buttonCombo.text
// font: comboBoxCustom.font
// leftPadding: 5
// horizontalAlignment: Text.AlignLeft
// verticalAlignment: Text.AlignVCenter
// elide: Text.ElideRight
// }
// background: Rectangle {
// color: buttonCombo.hovered ? (buttonCombo.pressed ? "#d8d8d8" : "#e8e8e8" ) : "#fff"
// }
// }
anchors.topMargin: 10
}
}
}
}
Maybe this could be done:
displayText: Number(currentText)+1
but i don't know if its the best solution...

How to dynamically update a child ListModel within a singleton?

I try to understand how shared qml objects can be used globally by other qml and javascript files. I have a QML app that has two comboboxes in one window. One is parent (country) combo box in ApplicationWindow, other one is child (city) combo box in Page1.qml. When selecting a country, the child combo box should have cities of the country at the same time. This is possible when using the comboboxes and the function are in ApplicationWindow, I had asked a question about it here. With the help of the answer of my previous question I tried to connect child combobox outside of ApplicationWindow to parent combobox in ApplicationWindow using qmldir, singleton and javascript.
I tried child combobox's list model to be a singleton object, it doesn't work and it gives error qrc:/script.js:11: Error: Cannot assign QJSValue to QQmlListModel*
main.qml:
import QtQuick 2.11
import QtQuick.Window 2.11
import QtQuick.Controls 2.4
import QtQuick.Layouts 1.3
import Qt.labs.settings 1.0
import "script.js" as JS
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
property int currentindex: comboBox2.currentIndex
Settings{
property alias country : comboBox2.currentIndex
}
Dialog {
id: dialog
title: "Select Country"
implicitWidth: parent.width
implicitHeight: parent.height/2
Column{
ComboBox {
id: comboBox2
x: 199
y: 176
width: 277
height: 48
currentIndex: 0
model:
ListModel {
ListElement { text: qsTr("USA") }
ListElement { text: qsTr("Russia") }
ListElement { text: qsTr("Iran") }
}
onCurrentIndexChanged:{
currentindex = currentIndex
JS.coord_combo_changed()
}
}
}
}
ColumnLayout{
anchors.horizontalCenter: parent.horizontalCenter
spacing:20
width: parent.width
Button{
anchors.horizontalCenter: parent.horizontalCenter
id:select_country
text:"select country"
onClicked: dialog.open()
}
Page1{
anchors.horizontalCenter: parent.horizontalCenter
}
}
}
Page1.qml:
import QtQuick 2.11
import QtQuick.Controls 2.4
import QtQuick.Layouts 1.3
Page{
RowLayout{
anchors.horizontalCenter: parent.horizontalCenter
spacing:5
width: parent.width
Text{
text:"Select City: "
}
ChildCombo{
id:comboBox1
model: Shared.childmodel
}
}
}
qmldir:
singleton Shared 1.0 Shared.qml
Shared.qml:
pragma Singleton
import QtQuick 2.9
import QtQuick.Controls 2.2
QtObject {
property ListModel childmodel: ChildModel{}
}
ChildModel.qml:
import QtQuick 2.0
ListModel {}
script.js:
function coord_combo_changed(){
if(currentindex === 0){
Shared.childmodel = ["New York", "Washington", "Houston"]
return Shared.childmodel
}
else if (currentindex === 1){
Shared.childmodel = ["Moscow","Saint Petersburg","Novosibirsk"]
return Shared.childmodel
}
else if (currentindex === 2){
Shared.childmodel = ["Tehran","Tabriz","Shiraz"]
return Shared.childmodel
}
}
I also tried to make combobox as a shared object, it also didn't work and it didn't give any error either. Is it possible to update the child combo box?
Thank you.
In QML a model can be a list, a ListModel, a QAbstractItemModel, etc., but those objects are not equivalent. And that is your mistake, you are trying to point out that a ListModel is a list, in this case the solution is to first clean the model and then add the elements with the append method:
function coord_combo_changed(){
var values = [];
if(currentindex === 0){
values = ["New York", "Washington", "Houston"]
}
else if (currentindex === 1){
values = ["Moscow","Saint Petersburg","Novosibirsk"]
}
else if (currentindex === 2){
values = ["Tehran","Tabriz","Shiraz"]
}
Shared.childmodel.clear()
for(var i in values){
Shared.childmodel.append({"text": values[i]});
}
}
On the other hand I see that your code throws warning indicating that you should not use the anchors if the items are within a layout, in your case you should change to:
ColumnLayout{
anchors.horizontalCenter: parent.horizontalCenter
spacing:20
width: parent.width
Button{
Layout.alignment: Qt.AlignHCenter // <---
id:select_country
text:"select country"
onClicked: dialog.open()
}
Page1{
Layout.alignment: Qt.AlignHCenter // <---
}
}

How to change the page to the next or previous item of a SwipeView by clicking on the right and left arrow respectively?

I have a SwipeView that loads its internal elements through a Repeater and a Loader.
I would like to swipe between the items forward and backward by just clicking the arrows on the right and left of the SwipeView.
How can I implement this behavior in QML?
SwipeView {
id: __swipeView
Layout.fillHeight: true
Layout.fillWidth: true
Repeater {
model: 3
Loader {
source: "qrc:/../SwipeDelegate.qml"
}
}
}
Within your delegate, you can access the SwipeView via the SwipeView attached property, and then increment or decrement the current index as necessary:
import QtQuick 2.6
import QtQuick.Controls 2.0
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
SwipeView {
anchors.fill: parent
Repeater {
model: 3
Item {
id: delegate
Button {
text: "<"
enabled: index > 0
onClicked: delegate.SwipeView.view.decrementCurrentIndex()
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
}
Label {
text: "Page " + (index + 1)
anchors.centerIn: parent
}
Button {
text: ">"
enabled: index < delegate.SwipeView.view.count - 1
onClicked: delegate.SwipeView.view.incrementCurrentIndex()
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
}
}
}
}
}
It's important to use the functions as opposed to setting currentIndex directly, for the reasons described here.

Resources