Receive signal in newly added listview entry - qt

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.

Related

QML SwipeView or Tumbler gets reloaded upon content change

I'm trying to set up a crawler that holds items with signals that may change over time. Unfortunately, I realized, that upon any signal update, the whole SwipeView is reset, which causes the index to jump back to its initial state.
The following example you can run with e.g. qmlscene to see the behavior. Whenever the trigger is fired, then the currentIndex is reset and the view jumps back to 1/first.
Is there any way how I can remain at the index I was at? I tried a little with setting the index back manually, however, then I ended up in a flickering behavior.
The same happens if I put content into a Tumbler.
import QtQuick 2.12
import QtQuick.Controls 2.12
SwipeView {
id: id_swipeView
width: 100
height: 100
property int value: 0
property var pages: [{"a": "1", "b": "first"}, {"a": "2", "b": "second"}, {"a": id_swipeView.value, "b": "continuous"}]
Repeater {
model: id_swipeView.pages
Item{
Column{
Text{
text: modelData["a"]
}
Text{
text: modelData["b"]
}
}
}
}
Timer{
repeat: true; running: true; interval: 1000;
onTriggered: {id_swipeView.value += 1}
}
}
Instead of using a list of objects as a model, it is better to use a ListModel that allows you to handle the values in a simple way, unlike the list that is created if an element is modified.
import QtQuick 2.12
import QtQuick.Controls 2.12
SwipeView {
id: id_swipeView
width: 100
height: 100
ListModel {
id: list_model
ListElement {
a: 1
b: "first"
}
ListElement {
a: 2
b: "second"
}
ListElement {
a: 0
b: "continuous"
}
}
Repeater {
model: list_model
Item {
Column {
Text {
text: model.a
}
Text {
text: model.b
}
}
}
}
Timer {
repeat: true
running: true
interval: 1000
onTriggered: {
var value = list_model.get(2).a;
list_model.setProperty(2, "a", value + 1);
}
}
}

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

QML How to call method of a model from the delegate

I have a simple QML program which has one ListView. ListView's model and delegate are defined in a separate QML files.
//Main.qml
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.12
//import TheModel.qml
//import TheDelegate.qml
Window {
id: window
visible: true
width: 640
height: 480
title: qsTr("Hello World")
ListView {
anchors.fill: parent
model: theModel
delegate: theDelegate
focus: true
}
Button{
x: 394
y: 257
text: "press me"
onPressed: theModel.append({"color":"black", "cost": 5.95, "name":"Pizza"})
}
TheDelegate{
id: theDelegate
}
TheModel{
id:theModel
}
}
then the model file
//TheModel.qml
import QtQuick 2.0
ListModel{
ListElement {
color:"red";
name: "Bill Smith"
number: "555 3264"
}
ListElement {
color:"blue";
name: "John Brown"
number: "555 8426"
}
ListElement {
color:"green";
name: "Sam Wise"
number: "555 0473"
}
}
and finally the delegate
//TheDelegate.qml
import QtQuick 2.0
Component {
Rectangle{
color: model.color
width: 100
height: 100
MouseArea{
anchors.fill: parent
onPressed: model.append({"color":"black", "cost": 5.95, "name":"Pizza"})
}
}
}
if I click on delegate's MouseArea the onPressed method will need to create one ListItem, but the issue is that I cannot access the model's function from delegate. what is confusing though, that properties are being accessed in delegate through model.
can anyone point out on the right way of doing this, say if I know that the model is a ListModel and it has append method, but delegate doesn't know that, is there a way to cast model to known type then call a method of it?
This can be done by adding a signal to TheDelegate, which you can connect in the scope where theModel is available. I'd like to call this 'contained components', there is probably some fancy term for it ;-)
Please change TheDelegate.qml to (btw, I also changed it to not be a Component, for reusability):
import QtQuick 2.0
Rectangle{
color: model.color
width: 100
height: 100
signal appendRequested(var newItem)
MouseArea{
anchors.fill: parent
onPressed: appendRequested({"color":"black", "cost": 5.95, "name":"Pizza"})
}
}
Then in main.qml you can use it as following:
ListView {
anchors.fill: parent
model: theModel
delegate: theDelegate
focus: true
}
Component {
id: theDelegate
TheDelegate {
onAppendRequested: theModel.append(newItem)
}
}
The model object is exposed to each delegate in a view, and it provides the model data for that particular delegate. It is not the same as the ListView's model property.
#Amfasis' approach is nice, because it will work with any type of model and view.
If you don't mind tying the delegate to ListView, you can use the attached ListView API:
Component {
Rectangle{
id: root
color: model.color
width: 100
height: 100
MouseArea{
anchors.fill: parent
onPressed: root.ListView.view.model.append({"color":"black", "cost": 5.95, "name":"Pizza"})
}
}
}

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

QML TableView rowDelegate styleData.row not defined

I am trying to create a custom row delegate for a TableView. according to the Documentation, the rowDelegate should have access to a property called styleData.row. However, when I'm try to access this property, it is undefined. I used the debugger to check whats inside the styleData, and the row is missing:
My code is very simple:
TableView {
width: 500//DEBUG
height: 300//DEBUG
model: ListModel {
ListElement {
lectureName: "Baum1"
}
ListElement {
lectureName: "Baum2"
}
ListElement {
lectureName: "Baum3"
}
ListElement {
lectureName: "Baum4"
}
}
rowDelegate: HeaderRowDelegate {//simply a Rectangle with an in property called "modelRow"
id: rowDelegate
modelRow: {
var data = styleData;
return data.row;
}
}
TableViewColumn {
id: c1
role: "lectureName"
title: "TEST"
}
}
You don't have to assign it yourself: as the HeaderRowDelegate component is assigned to the rowDelegate property of the TableView, your component has already access to the styleData.row property.
Here's an example:
main.qml
import QtQuick 2.4
import QtQuick.Layouts 1.1
import QtQuick.Controls.Styles 1.2
import QtQuick.Controls 1.3
ApplicationWindow {
id: app
title: qsTr("Test")
width: 800
height: 600
TableView {
width: 500//DEBUG
height: 300//DEBUG
model: ListModel {
ListElement {
lectureName: "Baum1"
}
ListElement {
lectureName: "Baum2"
}
ListElement {
lectureName: "Baum3"
}
ListElement {
lectureName: "Baum4"
}
}
rowDelegate: RowDel {}
TableViewColumn {
id: c1
role: "lectureName"
title: "TEST"
}
}
}
Now the row delegate, RowDel.qml
import QtQuick 2.4
Rectangle {
id: rowDel
color: "blue"
height: 60
readonly property int modelRow: styleData.row ? styleData.row : 0
MouseArea {
anchors.fill: parent
onClicked: {
console.log("[!] log: " + modelRow);
}
}
}
The important thing here is that you can reference styleData.row directly from your component (obliviously as long as you use this precise component as a row delegate).
As an example, if you click on each tableview row, you should see the correct row number displayed in the console log:
qml: [!] log: 0
qml: [!] log: 1
qml: [!] log: 2
qml: [!] log: 3

Resources