Solved Programmatically modify QSqlTableModel field
-
@Oak77
IIRC,QSqlTableModel::record(row)
just copies out of the model'sdata()
into a structure, it's not a "live" connection. So you can modify the content of that structure, like you did, but that does not in itself copy it back into the underlyingdata()
. Hence nor committed back to the database. You have to callQSqlTableModel::setRecord(row)
to copy back from aQSqlRecord
to thedata()
, or usesetData()
directly. -
@JonB said in Programmatically modify QSqlTableModel field:
@Oak77
IIRC,QSqlTableModel::record(row)
just copies out of the model'sdata()
into a structure, it's not a "live" connection. So you can modify the content of that structure, like you did, but that does not in itself copy it back into the underlyingdata()
. Hence nor committed back to the database. You have to callQSqlTableModel::setRecord(row)
to copy back from aQSqlRecord
to thedata()
, or usesetData()
directly.Oh that was a super important point! Thank you very much!
Now I understand, why it wasn't committed to database and it was easy to do a fix:
rec = mdl2.record(self.rowIdx.row() + 1) rec.setValue("CName", self.txtName.text()) mdl2.setRecord(self.rowIdx.row() + 1, rec)
...and it works as a charm! That would do the job decently. However, of course I'll commence with implementing
QDataWidgetMapper
to learn and understand it. -
So far all my attempts to employ
QDataWidgetMapper
failed. The documentation seems to be pretty straight-forward and resulted in a code like this:mapper = QDataWidgetMapper(self) mapper.setModel(self.model) mapper.addMapping(self.propsbws.txtName,1)
...to bind 1
QLineEdit
calledtxtName
. It doesn't throw any errors, but it doesn't work either. After making sure my references are correct I'm thinking now (apart from that I have no further ideas how to debug and test it), that the reason it is not working is, that theself.model
is not directly bound to theQTreeView
and thus selecting a row does not let the model know a row is selected and thus the mapper is not activated. How actually selecting works with the model is not covered (AFAIK, pardon me if I missed anything) in the documentation of theQSqlTableModel
. There's no direct method of setting selected row.
My wild guess is that I should setup my ownQItemSelectionModel
as per (docs)?A QItemSelectionModel keeps track of the selected items in a view, or in several views onto the same model. It also keeps track of the currently selected item in a view.
The reason why the
QTreeView
is unbound lies in combination of table data behind (whereas only one field is displayed), setting an icon and tree position from that table data, all the drag&drop functions to allow manipulation with the tree and perhaps my limited knowledge how to make all this directly bound. This is a different story, so I'm not elaborating on that (unless requested).EDIT:
I tried to set:self.selectionmodel = QItemSelectionModel() self.selectionmodel.setModel = self.model
self.model
is my classTreeModel
, which in turn is subclassedQStandardItemModel
. And then on change inQTreeView
I set:self.selectionmodel.setCurrentIndex(Idx, QItemSelectionModel.SelectCurrent)
...but when
setCurrentIndex
is invoked, I get a warning printed out into console:QItemSelectionModel: Setting the current index when no model has been set will result in a no-op.
-
@Oak77 said in Programmatically modify QSqlTableModel field:
self.selectionmodel.setModel = self.model
Wrong syntax:
self.selectionmodel.setModel(self.model)
Now there are no errors but it
QDataWidgetMapper
is not working either. -
@Oak77
Don't know what your problem is. I have usedQDataWidgetMapper
and found it obvious/easy, though I didn't use it in combination with aQTreeView
. But the principles should be pretty clear. Start from a cut-down example, get it working, move up to your full situation. FWIW, there is an old example at https://doc.qt.io/archives/qt-5.7/qtwidgets-itemviews-simplewidgetmapper-example.html, don't know why they seemed to have removed it (or at least I can't find it now), you could check you are doing the right things. -
@JonB Thank you for advises, I started to work on a dedicated simplified (minimal) code yesterday. I can also try a combination with
QTableView
. I also followed the example you referenced; in all I found 3 for 3 different cases (I'm listing them for possible followers of the thread):I'll do the MUC and study widget mapping and post a solution, if I will be able to find it.
-
Heureka!
Got it working with this single line put inside a selection change function (connected to
selectionChanged
signal of theQTreeView
:self.mapper.setCurrentIndex(self.DetailTree.selectionModel().currentIndex().row())
It connects sets the current index to the mapper, taking the index from the tree view.
-
@JonB (almost a year old, but still ...). I have used QDataWidgetMapper effectively for binding widget data to the model. Any edited changes are recorded back in the model. But I never got it to work programmatically, say, using
qLineEdit->setText()
Can't find this limitation in the document either. Any thoughts, ideas?
-
@NameRakes
You're not supposed to do it that way. The text is bound to the model data, you should rather set the desired value in the model and let that update the edit text. -
@JonB thanks for the quick response. Just to confirm, according to the manual, editing by the user is allowed (but not programmatically, I assume). From the documentation:
"If the user edits the contents of a widget, the changes are read using the same property and written back to the model"
So, I will use it this way!
-
@NameRakes
Absolutely. The updates work in two directions:- When the bound value in the model changes, the widget is updated accordingly.
- When the user (interactively) changes the widget's value, the bound value in the model is updated accordingly.
I would not have necessarily known that calling
QLineEdit::setText()
does not alter the model value. I am taking your word for that. That would emitQLineEdit::textChanged()
signal. But there are alsotextEdited()
&editingFinished()
signals, and those are only emitted when the user interactively changes the text, not viasetText()
. So at a guess the model update happens on one of those instead. One would have to look at the code. If you wanted to test, you might force those to be emitted (by calling them directly) and see if that did cause the model to change.Other widgets do not have this distinction between programmatic and user interaction signals. For example,
QComboBox::currentIndexChanged()
is emitted whenever the user or code causessetCurrentIndex()
to be called. I imagine that one would cause the model value to be updated if called from code.As a general rule if you want to change a value on a bound widget I would suggest/expect you to do so by altering the model, and letting that reflect back to the widget.