dataChanged signal not reaching TableView unless I add 1 line of unrelated javascript
-
wrote on 22 Jan 2022, 16:00 last edited by ocgltd
I am creating a simple TableView in QML, 5 col wide by 4 rows tall. This table connects to a QSortFilterProxyModel, which connects to a QAbstractTableModel. When I call the sort method of the QSFPM my table refreshes as expected and works great.
Now, I am trying to highlight a row of my table; the row delegate just uses BLUE as the background color instead of WHITE if that row is selected.The ItemSelectionModel appears to work properly (printing the selected row).
However, my table does not refresh (confirmed with console.log statements in the table). It's as if the dataChanged signal is not reaching my table (though calling the resort method in this same class DOES cause the table to refresh).
As you will see in the last comment below, adding one unrelated line of Javascript causes the dataChanged signal to have the desired effect. Why?
Source file companysortfilterproxymodel.cpp (descends from QSortFilterProxyModel):
import QtQuick 2.15 import QtQuick.Window 2.15 import QtQuick.Controls 2.15 import Qt.labs.qmlmodels 1.0 import QtQml.Models 2.15 Item { id: companyTable readonly property int minWidth: 300 readonly property int maxWidth: 700 property int parentWidth: parent.width anchors.top: parent.top anchors.bottom: parent.bottom anchors.left: parent.left // Create the company sort filter proxy model only when the company table is showing CompanySortFilterProxyModel { id: companySFPM } // Show the company table view TableView { id: companyTableView anchors.left: parent.left anchors.right: parent.right anchors.top: companyTableHeader.bottom anchors.bottom: parent.bottom clip: true // columnWidthProvider Allow for dynamic col width when screen size changes columnWidthProvider: function (column) { return colWidth(column) } // sets the model to be displayed in this view model: companySFPM delegate: DelegateChooser { role: "displayStyle" DelegateChoice { roleValue: "Number" delegate: Rectangle { // I chose 50 as the height for each row of the table because it looks good implicitHeight: 50 // Color every other row light gray color: getColor(row) Text { anchors.verticalCenter: parent.verticalCenter anchors.right: parent.right anchors.rightMargin: 10 // Left padding of 10 width: colWidth(0) - 20 wrapMode: Text.Wrap text: model.data } MouseArea { anchors.fill: parent onClicked: { console.log("Clicked on row - number") selectRow(row); } } } } // This delegate choice is used for creating all table cells that are in the company name column DelegateChoice { roleValue: "CompanyName" delegate: Rectangle { // I chose 50 as the height for each row of the table because it looks good implicitHeight: 50 // Color every other row light gray color: getColor(row) Text { anchors.verticalCenter: parent.verticalCenter anchors.right: parent.right anchors.rightMargin: 10 // Left padding of 10 width: colWidth(1) - 20 wrapMode: Text.Wrap text: model.data } MouseArea { anchors.fill: parent onClicked: { console.log("Clicked on row - companyName") selectRow(row); } } } } // This delegate choice is used for creating all table cells that contain checkmarks of boolean type DelegateChoice { roleValue: "CheckMark" delegate: Rectangle { implicitHeight: 50 // Color every other row light gray color: getColor(row) Image { anchors.centerIn: parent visible: model.data source: MediaManager.assetPath("img/black_icons/checkmark.svg", Qt.resolvedUrl(".")) } MouseArea { anchors.fill: parent onClicked: { console.log("Clicked on row - checkmark"); selectRow(row); } } } } // DeletgateChoice } // Delegate /* ScrollBar.vertical: ScrollBar { anchors.top: companyTableView.top anchors.bottom: companyTableView.bottom } */ } // Tableview // Create header containing column name and up/down arrow for sort state HorizontalHeaderView { anchors.top: parent.top anchors.left: parent.left anchors.right: parent.right id: companyTableHeader syncView: companyTableView reuseItems: false // The column that is currently being used for sorting property int sortingColumn : 0 // stores a state for each column representing whether the triangle is pointing down property var state_sortArrowDown : [] // This variable holds whether a new column has been selected on the most recent click // This variable starts as true so that the triangle does not animate at the start of the program property bool newColumnSelected : true // Pass the label and column number as parameters into the header delegate delegate: ColumnHeader { label: model.display columnNum: index } // This block runs only once when headerview is created Component.onCompleted: { // Set all columns to start as sort ascending var index; for(index = 0; index < companySFPM.columnCount(); index++) { state_sortArrowDown[index] = false; } } } ItemSelectionModel { id: companyTableISM model: companySFPM } function selectRow(row) { console.log("In selectRow row "+row); companyTableISM.select(companySFPM.index(row, 0), ItemSelectionModel.Current | ItemSelectionModel.Toggle); console.log(companyTableISM.selectedIndexes); console.log(companyTableISM.hasSelection); }
(I have removed all C++ from this question, so now it's purely QML related - perhaps someone could move to QML forum)
-
Why do you need to call dataChanged() when you select a row? Don't see a relation between a selection color and the model's data.
-
Why do you need to call dataChanged() when you select a row? Don't see a relation between a selection color and the model's data.
wrote on 22 Jan 2022, 17:33 last edited by ocgltd@Christian-Ehrlicher When I click a row in my table I want it to be 'selected' (and shown by changing color). I do so by tracking the row selected, and in my TableView row delegate I check if THIS row is the selected one, and if so I use BLUE as the background color; otherwise I use white.
I need some way to tell the table to redraw the row in question, to use the new color. So I want to use the datachanged signal to notify the table to redraw that row. (I assume that's the right way to achieve what I want)....
-
When the selection changes, the old selected row and the new one is redrawn automatically - otherwise you won't see a difference between a selected and unselected row. No need to store this information anywhere since the view already knows about it. The information is available in https://doc.qt.io/qt-5/qstyleoption.html#state-var in https://doc.qt.io/qt-5/qstyleditemdelegate.html#paint
-
When the selection changes, the old selected row and the new one is redrawn automatically - otherwise you won't see a difference between a selected and unselected row. No need to store this information anywhere since the view already knows about it. The information is available in https://doc.qt.io/qt-5/qstyleoption.html#state-var in https://doc.qt.io/qt-5/qstyleditemdelegate.html#paint
wrote on 22 Jan 2022, 19:22 last edited by@Christian-Ehrlicher I must be doing something else wrong...let me expand (and update my question). In my table delegate I have a mouse area, which passes the clicked row number to my "selectRow" method - which is now provided in the question above.
My selectRow method toggles selection for cell 0 of the clicked row, and the onSelectionChanged slot calls my emitDataChanged method. So if the table should recolor the selection on it's own, shouldn't column 0 highlight itself?
-
Why do you want to toggle your current selection? Should column 0 not be selectable?
Selection has nothing to do with dataChanged() so calling dataChanged() after changing a selection is not needed. -
Why do you want to toggle your current selection? Should column 0 not be selectable?
Selection has nothing to do with dataChanged() so calling dataChanged() after changing a selection is not needed.wrote on 22 Jan 2022, 19:43 last edited by@Christian-Ehrlicher My goals is to be able to select one (and only row) of the table by clicking on it. If I click the same row again I want to deselect it. Once selected, I want the background color of the row to change.
Sounds like I'm on the wrong path. What is the right way to achieve this? As it stands now nothing changes color (even though my ItemSelectionModel says I have selected the (row,0) column clicked on.
-
A normal view will draw the selected row in another color. Why it's not in your case - don't know. First remove your custom delegate.
-
A normal view will draw the selected row in another color. Why it's not in your case - don't know. First remove your custom delegate.
wrote on 22 Jan 2022, 20:32 last edited by ocgltdThis post is deleted! -
A normal view will draw the selected row in another color. Why it's not in your case - don't know. First remove your custom delegate.
wrote on 23 Jan 2022, 15:45 last edited byThis post is deleted! -
wrote on 23 Jan 2022, 21:47 last edited by
I got it working - but can't explain why. I confirmed the dataChanged signal is being emitted and received in my class above. However, the table just does not redraw (getColor function is never called).
However, if I add the ONE line of code below (in getColor), then the table redraws as expected!! (Putting this line into 'getColor' results in the signal causing the table to refresh, which causes 'getColor' to execute). Why?
function getColor(row) { console.log("In getColor"); var i = companyTableISM.selectedIndexes; // <--- WHY DO I NEED THIS LINE if (companyTableISM.isSelected(companySFPM.index(row, 0)))
1/11