Dynamically add more combobox without resetting exiting data - qt

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
}
}
}
}

Related

Receive signal in newly added listview entry

I am trying to execute a function in a newly added entry to my ListView.
After adding an entry via button I want to execute the just added delegate's onNewEntry function. But only the old delegates execute it.
Minimal example:
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.0
Window {
id:root
width: 640
height: 480
visible: true
signal newEntry(int new_row)
property var someProperty
ListModel {
id:listmodel
ListElement {
name: "Bill Smith"
}
ListElement {
name: "John Brown"
}
ListElement {
name: "Sam Wise"
}
}
ListView{
id: listView
width:100
height:200
model: listmodel
delegate: ItemDelegate{
id:delegateId
Text{
text:name
}
Connections { //ISSUE!!!: the new delegate doesnt execute this, just the old ones
target: root
function onNewEntry(new_row){
console.debug(index)
console.debug(new_row)
if(new_row==index){ //doesnt get true
listView.currentIndex = index
setProductData()
}
function setProductData(){
root.someProperty=name
}
}
}
}
}
Button {
anchors.top: listView.bottom
id: btnAdd
text:"+"
onClicked:{
listmodel.append({"name":"Joe Black"})
newEntry(listView.count-1) //emit signal newEntry
}
}
}
output:
qml: 0
qml: 3
qml: 1
qml: 3
qml: 2
qml: 3
My already mentioned workaround is using ListView.onAdd in the delegate:
delegate: ItemDelegate{
ListView.onAdd: {
setProductData()
}
My question is: Why does the newly added entry doesnt listen to the newEntry signal. Thank you
It would be easier to just set the currentIndex after inserting the new row:
Button {
id: btnAdd
onClicked: {
sqlTableModel.insertNewEmptyRow()
listView.currentIndex = listView.count - 1
}
}
I found a better, probably more efficient solution:
delegate: ItemDelegate{
ListView.onAdd: {
listView.currentIndex=index
setProductData()
}
only the newly added delegate receives onAdd not all, like in the approach before.

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 dynamically update a child combobox from parent combobox

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.

Adding TabButton dynamically to TabBar in QML

I am trying to add a tabButton to TabBar dynamically on pressing a button but i have spent a lot of time searching but i am not getting how to add, below is the code which i am working on :
MyTabButton.qml
import QtQuick 2.4
import QtQuick.Controls 2.2
Item
{
property int BtnWidth:0
property int BtnHeight:0
property string BtnText: ""
property bool isChecked : false
TabButton
{
id:tabBtn
text:BtnText
width:BtnWidth
height:BtnHeight
}
}
MainForm.qml
import QtQuick 2.4
import QtQuick.Controls 2.2
Rectangle
{
Button
{
id:button
width:100
height:100
anchors.top:parent.top
text:qStr("Add")
onClicked{
//How to add logic here to add tab in below tabBar.
}
}
TabBar
{
id:tabBar
anchors.top:button.bottom
width:500
height:500
}
}
Example:
import QtQuick 2.7
import QtQuick.Controls 2.0
ApplicationWindow {
id: window
width: 360
height: 630
visible: true
header: TabBar {
id: tabBar
}
Component {
id: tabButton
TabButton { }
}
Button {
text: "Add"
anchors.centerIn: parent
onClicked: {
var tab = tabButton.createObject(tabBar, {text: "Tab " + tabBar.count})
tabBar.addItem(tab)
}
}
}
You need to have something like a Component that is a TabButton. Your file MyTabButton.qml won't result in a TabButton, but instead an Item containing a TabButton, but with this, your TabBar does not know what to do.
So your file will need to have TabButton as root element
//MyTabButton.qml
import QtQuick 2.4
import QtQuick.Controls 2.2
TabButton
{
id: tabBtn
// customize as you like
}
Then you create a Component of this in your file where you want to use it. (e.g. main.qml)
import QtQuick 2.4
import QtQuick.Controls 2.0
ApplicationWindow {
width: 800
height: 600
visible: true
TabBar {
id: tabBar
width: 800
height: 50
}
// The component is like a factory for MyTabButtons now.
// Use myTabButton.createObject(parent, jsobject-with-property-assignments) to create instances.
Component {
id: myTabButton
MyTabButton {
/// EDIT ACCORDING TO YOUR COMMENTS ***
Connections {
target: tabBar
onCurrentIndexChanged: doSomething()
}
/// EDIT OVER
}
}
Button {
anchors.centerIn: parent
// Create a object out of the component, and add it to the container
onClicked: tabBar.addItem(myTabButton.createObject(tabBar /*, { object to set properties }*/))
}
}
TabBar inherits Container, which has addItem().
Try it in Window
Row {
anchors.fill: parent
TabBar {
id: tabBar
currentIndex: 0
width: parent.width - addButton.width
TabButton { text: "TabButton" }
}
Component {
id: tabButton
TabButton { text: "TabButton" }
}
Button {
id: addButton
text: "+"
flat: true
onClicked: {
tabBar.addItem(tabButton.createObject(tabBar))
console.log("added:", tabBar.itemAt(tabBar.count - 1))
}
}
}

QML reset dialog with tabview

I was trying to implement a tabbed Dialog in QML with the means to reset it to the intial values.
Since tabs are dynamically instantiated, none of the straight forward methods seem to work. The parent Dialog can not reference the inner Combobox and the Combobox can not reference the outer Dialog. How can this be achieved?
import QtQuick 2.3
import QtQuick.Controls 1.4
import QtQuick.Dialogs 1.2
import QtQuick.Layouts 1.1
Dialog {
id: dlg
title: "Settings"
visible: true
standardButtons: StandardButton.Apply | StandardButton.Reset
property string val: ""
onApply: console.log(val)
onReset: {
// RESET COMBOBOX TO DEFAULT
}
TabView {
id: tabView
anchors.fill: parent
Tab {
title: "ValueTab"
id: tabVal
GridLayout {
id: gridVal
anchors.fill: parent
GroupBox {
title: qsTr("Choose value")
id: gb
Layout.fillWidth: true
ColumnLayout {
anchors.fill: parent
id: cl
ComboBox {
id: valueChooser
editable: false
model: ListModel {
id: listModel
ListElement { text: "One" }
ListElement { text: "Two" }
ListElement { text: "Three" }
}
Layout.fillWidth: true
onCurrentTextChanged : val = currentText
}
}
}
}
}
}
}
I am quite unsure, if I got your question right as you say, you can not reference the Dialog from within the Combobox. I can not see the reason why.
Assuming the example of yours contains indeed your problem and all you want to do is to reset the values (and you know the original values) once the reset button is pressed, this is how I would solve it.
Using the Connections-type to connect to the Dialog's reset() from within the Combobox
import QtQuick 2.3
import QtQuick.Controls 1.4
import QtQuick.Dialogs 1.2
import QtQuick.Layouts 1.1
Dialog {
id: dlg
title: "Settings"
visible: true
standardButtons: StandardButton.Apply | StandardButton.Reset
property string val: ""
onApply: console.log(val)
onReset: {
// **DONT** RESET COMBOBOX TO DEFAULT **HERE**
}
TabView {
id: tabView
anchors.fill: parent
Tab {
title: "ValueTab"
id: tabVal
GridLayout {
id: gridVal
anchors.fill: parent
GroupBox {
title: qsTr("Choose value")
id: gb
Layout.fillWidth: true
ColumnLayout {
anchors.fill: parent
id: cl
ComboBox {
id: valueChooser
editable: false
model: ListModel {
id: listModel
ListElement { text: "One" }
ListElement { text: "Two" }
ListElement { text: "Three" }
}
Layout.fillWidth: true
onCurrentTextChanged : val = currentText
/// *** INTERESTING PART HERE! ***
Connections {
target: dlg
onReset: {
// RESET COMBOBOX TO DEFAULT **HERE** INSTEAD
valueChooser.currentIndex = 0
}
}
}
}
}
}
}
}
}

Resources