QListView with custom QStyledItemDelegate not working
-
Hey,
I have a subclass QSortFilterproxyModel set to QListView. If I now double-click on the selected item, I can edit the whole thing and it must also be written to the model. However, the QListView is not updated. Did I forget to implement something?
I found orientation at the QSpinBox Delegate https://doc.qt.io/qt-6/qtwidgets-itemviews-spinboxdelegate-example.html
Here is a picture and my code
Header-File
class GermanNameRelationalDelegate : public QStyledItemDelegate { Q_OBJECT public: explicit GermanNameRelationalDelegate(QObject *parent = 0); ~GermanNameRelationalDelegate(); QWidget *createEditor(QWidget *aParent, const QStyleOptionViewItem &option, const QModelIndex &index) const override; void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override; void setEditorData(QWidget *editor, const QModelIndex &index) const override; }; #endif // GERMANNAMERELATIONALDELEGATE_H
CPP-File
#include "germannamerelationaldelegate.h" #include <QDebug> #include <QStyledItemDelegate> GermanNameRelationalDelegate::GermanNameRelationalDelegate(QObject *parent): QStyledItemDelegate(parent) {} GermanNameRelationalDelegate::~GermanNameRelationalDelegate() {} QWidget *GermanNameRelationalDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const { QLineEdit *lineEdit = new QLineEdit(parent); lineEdit->installEventFilter( const_cast<GermanNameRelationalDelegate*>(this) ); return lineEdit; } void GermanNameRelationalDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { QLineEdit *line = static_cast<QLineEdit*>(editor); QString value = line->text(); qDebug() << "setModelData text: " << value; qDebug() << "setData: " << model->setData(index, value, Qt::EditRole); } void GermanNameRelationalDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { QString value = index.model()->data(index, Qt::EditRole).toString(); qDebug() << "setEditorData text: " << value; QLineEdit *line = static_cast<QLineEdit*>(editor); line->installEventFilter( const_cast<GermanNameRelationalDelegate*>(this) ); line->setText(value); }
And my qDebug() output:
Database connection successfully established! Delegate: GermanNameRelationalDelegate(0x3022cb0) setEditorData text: "Rötender Saftwirrling7799" setModelData text: "Rötender Saftwirrling7799mm" setEditorData text: "Rötender Saftwirrling7799mm" setData: true Delegate: GermanNameRelationalDelegate(0x3022cb0) setEditorData text: "Rötender Saftwirrling7799mm" setModelData text: "Rötender Saftwirrling" setEditorData text: "Rötender Saftwirrling" setData: true Delegate: GermanNameRelationalDelegate(0x3022cb0) setEditorData text: "Rötender Saftwirrling" setModelData text: "Rötender Saftwirrling" setData: true
Can anybody please help me?
-
@Gabber
If I am not misunderstanding. From your picture, your code and your debug output it looks like all the editing is taking place correctly, right? I see an old value, I see it being changed, and the debug output shows the change has taken (next time editing starts from the new value). So what is the problem? Nothing you have shown here seems to be malfunctioning? -
@JonB
As you can see in the picture, the value does not reappear in the QListView after it has been edited. This is my problem.I have a blue line without the text and I would like to have the text in the blue line.
-
@Gabber
So you have aQListView
showing this model, and the model only has one element in it? And the model shown by the list view is a QSFPM which has an underlying data model? And initially it displays the correct string? And after editing it goes blank? But when you click to edit again then the string shows up in the editor?I think you are not really supposed to edit via a QSFPM, or there can be issue in some circumstances? For one thing there is the setting of QSortFilterProxyModel::dynamicSortFilter
Note that you should not update the source model through the proxy model when dynamicSortFilter is true.
If you temporarily remove the QSFPM does it work? That would tell us whether it is some QSFPM issue.
-
@JonB said in QListView with custom QStyledItemDelegate not working:
So you have a QListView showing this model, and the model only has one element in it?
No the record has about 8000 entries but is filtered by a specific ID, so currently only one item shows up.
@JonB said in QListView with custom QStyledItemDelegate not working:
And the model shown by the list view is a QSFPM which has an underlying data model?
Yes the QSFPM has a QSqlRelationalTableModel as a basis.
@JonB said in QListView with custom QStyledItemDelegate not working:
And initially it displays the correct string?
Yes, if I don't start editing the value is shown as expected.
@JonB said in QListView with custom QStyledItemDelegate not working:
And after editing it goes blank?
Correct!
@JonB said in QListView with custom QStyledItemDelegate not working:
But when you click to edit again then the string shows up in the editor?
Correct! As shown in the picture
@JonB said in QListView with custom QStyledItemDelegate not working:
If you temporarily remove the QSFPM does it work? That would tell us whether it is some QSFPM issue.
No, it didn't work. I have now directly taken the QSqlRelationTableModel with all values and tried to edit it. As soon as I edit a value the text disappears, when I edit it again the correct one is there. Here is another image that shows an example
And this is my database structure
CREATE TABLE "Deutscher_Pilzname" ( "Deutscher_name_id" INTEGER NOT NULL, "Pilz_id" INTEGER NOT NULL, FOREIGN KEY("Deutscher_name_id") REFERENCES "Deutscher_Name"("Id"), PRIMARY KEY("Deutscher_name_id","Pilz_id"), FOREIGN KEY("Pilz_id") REFERENCES "Pilze"("Id") );
-
@Gabber
OK, so at least we have discovered the QSFPM is not relevant! So you are saying I can set up a model (SQL), view it in aQListView
, make an edit, and at the end of the edit it will not longer show anything for the edited item. But the item is there, if you click to re-edit it.It may be an update issue at the end of the edit, for unknown reason. But it would be nice to simplify to remove the relational element from consideration. That complicates, plus you have a two-element primary key, not 100% sure what QSRTM will make of that. Can you temporarily try your editing on a simple QSTM instead, does that behave the same or differently? :)
You have not said what column you are actually showing from your QSRTM? Is it the foreign-key-mapped one? Not sure, but I think QSRTM may have issues if you edit that, you're only supposed to change which one by picking from e.g. a combo box, not editing. At any rate, try eliminating the relational stuff so we know whether that is at issue :)
[Also, while I think of it and am asking questions, for your
QSqlRelationalTableModel
and the underlyingQSqlTableModel
, what is your setting forsetEditStrategy(QSqlTableModel::EditStrategy strategy)
, in case that is relevant? Are your edits ever committed to the database or never (during what you show)?] -
@JonB said in QListView with custom QStyledItemDelegate not working:
OK, so at least we have discovered the QSFPM is not relevant! So you are saying I can set up a model (SQL), view it in a QListView, make an edit, and at the end of the edit it will not longer show anything for the edited item. But the item is there, if you click to re-edit it.
Yes that's right.
@JonB said in QListView with custom QStyledItemDelegate not working:
Can you temporarily try your editing on a simple QSTM instead, does that behave the same or differently? :)
I tried it with a QSTM and see volla my delegate works as I expect!
@JonB said in QListView with custom QStyledItemDelegate not working:
You have not said what column you are actually showing from your QSRTM? Is it the foreign-key-mapped one?
Yes I mapped it to another table. That's to QSRTM Model:
QSqlRelationalTableModel *DatabaseManager::germanFungusNameDataModel() const { QSqlRelationalTableModel *model = new QSqlRelationalTableModel; model->setTable("Deutscher_Pilzname"); model->setRelation(model->fieldIndex("Deutscher_name_id"), QSqlRelation("Deutscher_Name", "Id", "Name")); model->setJoinMode(QSqlRelationalTableModel::LeftJoin); model->setSort(0, Qt::AscendingOrder); model->setEditStrategy(QSqlRelationalTableModel::OnManualSubmit); model->select(); while (model->canFetchMore()) { model->fetchMore(); model->relationModel(0)->fetchMore(); } return model; }
@JonB said in QListView with custom QStyledItemDelegate not working:
Not sure, but I think QSRTM may have issues if you edit that, you're only supposed to change which one by picking from e.g. a combo box, not editing
Okay...but then it is not clear to me how to edit the RelationModel? Which ways are there to display the data in the QListview? The QSRTM is a N:M relation and I don't know how to implement the QT / C++...
@JonB said in QListView with custom QStyledItemDelegate not working:
Are your edits ever committed to the database or never (during what you show)?
Later, I will save all changes via a button and call submitAll()
-
@Gabber said in QListView with custom QStyledItemDelegate not working:
I tried it with a QSTM and see volla my delegate works as I expect!
So the
QSqlRelationalModel
/QSqlRelation
s are (somehow) the issue!You still have not said which column you are editing? The
Deutscher_name_id
, which is a foreign key?? That column in theDeutscher_Pilzname
table? Or theId
/name column inDeutscher_Name
table?? I am not a mind-reader!I don't know what you are trying to do or what you expect. I don't know what you think you are typing into? Which table are you trying to update? If you think you are updating the foreign key table,
Deutscher_Name
, you will be sorely disappointed. With aQSqlRelationalTable
/Delegate
you can only update the referencing table, you can't alter the referenced table, if you think you are doing that.Basically,
QSqlRelationalTable
,QSqlRelation
andQSqlRelationalDelegate
are really pretty simple/limited in Qt. All the delegate gives you is the ability to display the "mapped name" from the foreign key table where there is an id reference in the referencing table. And to pick that from a combobox of the available mapped names instead of the raw id. [Not even sure you are using aQSqlRelationalDelegate
.] That's about it. I suspect whatever you are trying to do might be beyond its abilities. I don't know if the initial non-appearance of the altered string is to do with this or just some refresh bug since it appears later when you click on it. You'll have to play....Finally, if you have a
a N:M relation
, where I presumeN
&M
can both be greater than 1, then I don't know what you think aQListView
, which can only display a list of single (column) items, is going to do for you. -
@JonB said in QListView with custom QStyledItemDelegate not working:
You still have not said which column you are editing? The Deutscher_name_id, which is a foreign key?? That column in the Deutscher_Pilzname table? Or the Id/name column in Deutscher_Name table?? I am not a mind-reader!
I'm sorry, I forgot. I am trying to edit the "Name" column in Deutscher_Name.
@JonB said in QListView with custom QStyledItemDelegate not working:
I don't know what you are trying to do or what you expect.
I don't want to do anything more than the following: Each mushroom has a unique ID and with this ID I want to be able to find the associated German names, display them in a QListView and edit them. And later I will save it via a QPushButton back to database.
@JonB said in QListView with custom QStyledItemDelegate not working:
If you think you are updating the foreign key table, Deutscher_Name, you will be sorely disappointed. With a QSqlRelationalTable/Delegate you can only update the referencing table, you can't alter the referenced table, if you think you are doing that.
Too bad, that's actually what I expected to be able to do with this QSqlRelationTableModel. Do you have a tip how I can implement the whole thing then? What I want to achieve I wrote one sentence above.
-
@Gabber
You and your German mushrooms! ;-) I have seen you asking about this program for a while now, and have been thoroughly lost in your table cross references!I know you think your explanations are clear, because you're familiar with it, but I'm afraid I'm not, and the more I have seen you try to explain previously the more confused I get! So let me take a step back, and tell you what I know you can do.
-
You have a referencing table. I think that is
Deutscher_Pilzname
. -
You have a foreign table. I think that is
Pilze
. Or maybe it isDeutscher_Name
. I think either of those are treated in the same way, so probably does not matter which. I suppose you have twoQSqlRelations
, one for each of these (though you don't show this). -
Only the referencing table is a
QSqlRelationalTableModel
. The other(s) are justQSqlTableModel
. -
The only thing you can do in the referencing table is pick which id(s) you want it to have stored in a row from the foreign key table(s).
-
You can edit the referencing table's own columns. Type whatever into its own, non-foreign-key fields. Select (from a combobox or similar) which item in a foreign key table it points to, but not "type it in".
-
If you are trying to do something like edit the referencing table (
QSqlRelationalTableModel
) and from that make a change to a foreign key table, that is not the way to do it. -
If you are trying to make a change in a foreign key table you can/should only do that when editing that table itself. Which is not a
QSqlRelationalTableModel
, it's some other plainQSqlTableModel
.
So all in all I don't know what your editing delegate named
GermanNameRelationalDelegate
is about. It sounds like aQSqlRelationalDelegate
, but it is not. If you are editing a foreign key table you don't want to go through/have any mention of aQSqlRelationalTableModel
.Like I said, I think you're going to have to try to understand what I am saying and apply it to your case, because I think you're only going to dumbfound me if you try to explain again! Does what I have said help/relate at all?
-
-
@JonB
I would also try to explain it to you a hundred times. I am happy when I get stuck and someone voluntarily tries to help others.In summary, I understand it like this:
If I have a QSqlRelationalTableModel, I can edit the custom table, but I can only view and select the foreign-key table. Editing does not work here. For this I need a QSqlTableModel.Maybe you can explain me one more thing (you do this really good :) ).
//Mushroom - Table +----+--------------+ | ID | MushroomName | +----+--------------+ | 1 | MushroomA | | 2 | MushroomB | | 3 | MushroomC | | 4 | MushroomD | +----+--------------+
//GermanNames - Table +----+----------+ | ID | Name | +----+----------+ | 1 | GerNameA | | 2 | GerNameB | | 3 | GerNameC | | 4 | GerNameD | +----+----------+
//GermanMushroomNames - Table +------------+---------------+ | MushroomID | GermanNamesID | +------------+---------------+ | 1 | 2 | | 1 | 3 | | 2 | 1 | | 3 | 1 | +------------+---------------+
First of all: With the Mushroom - Table ID I can always find the corresponding data (this is how my database is built)
Assume the following example:
I want to find all German names that belong to Mushroom - ID 1, then I get back from the database the names "GerNameB" and "GerNameC".Now if I want to edit my 3 tables (Mushroom, GermanNames and GermanMushroomNames) I need a QSqlTableModel. Let's assume the 3 QSqlTableModels are named exactly the same as the SQL tables names.
And exactly now my problem starts, that I don't know how to filter the data so that I can edit the QSqlModel GermanNames later.
To just show the data you can use the QSqlRelationModel and work with the function setFilter. But how does that work if the data should be edited as well?
Do you have a tip or even better an example? (I hope I could explain what I want ;D
-
@Gabber said in QListView with custom QStyledItemDelegate not working:
Now if I want to edit my 3 tables (Mushroom, GermanNames and GermanMushroomNames) I need a QSqlTableModel.
Indeed, one for each, and that is where the editing needs to be done.
But how does that work if the data should be edited as well?
You never explain what you (think you) want to do for "editing"?! You can
INSERT
,DELETE
andUPDATE
on, say, theGermanNames
table/QSqlTableModel
. What other than that do you want to do?And exactly now my problem starts, that I don't know how to filter the data so that I can edit the QSqlModel GermanNames later.
I just don't know what you mean by this.
To just show the data you can use the QSqlRelationModel and work with the function setFilter. But how does that work if the data should be edited as well?
You can edit the referencing table, which is the
QSqlTableModel
/QSqlRelationalTableModel
. You cannot edit the foreign key table via this table.I just don't know what it is you are trying to do that you cannot figure? What is the simplest example of an "edit" which you would like to do that you cannot? What is the SQL query you think should be executed for it?
I have one thought/guess/possibility that just might be of interest to you. Once you have set up a
QSqlRelation
, and you have filled theQSqlRelationalModel
and the foreign keyQSqlTableModel
. If you look at the foreign key column in the referencing table in arow
,data(QModelIndex(row, fkc), Qt::EditRole)
returns the value actually stored in the table (i.e. an ID) whiledata(QModelIndex(row, fkc), Qt::DisplayRole)
returns the mapped value of the ID in the foreign table (e.g. a name). Are you aware of this, does it help with your desired "edits"?Also, did you see this statement in https://doc.qt.io/qt-6/qsqlrelationaltablemodel.html#details
The reference table name is aliased. The alias is the word "relTblAl" and the relationed column index joined by an underscore (e.g. relTblAl_2). The alias can be used to filter the table (For example,
setFilter("relTblAl_2='Oslo' OR relTblAl_3='USA'")
). -
@JonB said in QListView with custom QStyledItemDelegate not working:
You never explain what you (think you) want to do for "editing"?! You can INSERT, DELETE and UPDATE on, say, the GermanNames table/QSqlTableModel. What other than that do you want to do?
Yes, you are right. For me, editing was always clear, but it can be insert, update or delete. I would insert with insertRows that already works with another QSqlTableModel, delete I have not yet thought about only seen that there is also a function removeRows. In this case it is about updating an existing value e.g. renaming "GerNameA" to "GerNameAAA".
@JonB said in QListView with custom QStyledItemDelegate not working:
I have one thought/guess/possibility that just might be of interest to you. Once you have set up a QSqlRelation, and you have filled the QSqlRelationalModel and the foreign key QSqlTableModel. If you look at the foreign key column in the referencing table in a row, data(QModelIndex(row, fkc), Qt::EditRole) returns the value actually stored in the table (i.e. an ID) while data(QModelIndex(row, fkc), Qt::DisplayRole) returns the mapped value of the ID in the foreign table (e.g. a name). Are you aware of this, does it help with your desired "edits"?
Yes I think that is the info where I had missed.
@JonB said in QListView with custom QStyledItemDelegate not working:
You can edit the referencing table, which is the QSqlTableModel/QSqlRelationalTableModel. You cannot edit the foreign key table via this table.
I understood that. But how this works in combination with a QListView I don't know yet. When I say:
QSqlRelationTableModel *myModel = new QSqlRelationTableModel(this); myModel->setRelation(0, QSqlRelation("Deutscher_Name", "Id", "Name")); listView->setModel(myModel) listView->setModelColumn(0)
And model column 0 is the foreign-key table. How can I edit (update) these values then? I know I can't edit via this table. Can you give me an example pls? Do I need a separate delegate in which I perform a qobjectcast until I have QSqlTableModel to update the values there, for example?
const QSqlRelationalTableModel *sqlModel = qobject_cast<const QSqlRelationalTableModel*>(index.model()); QSqlTableModel *childModel = sqlModel ? sqlModel->relationModel(index.column()) : 0;
Or how does that work then?
-
@Gabber said in QListView with custom QStyledItemDelegate not working:
Do I need a separate delegate in which I perform a qobjectcast until I have QSqlTableModel to update the values there, for example?
No, you won't get anywhere trying that. Just look at everything that is in QSqlRelation Class and think about the only constructor QSqlRelation::QSqlRelation(const QString &tableName, const QString &indexColumn, const QString &displayColumn. Do you see any mention of the foreign key table as a
QSqlTableModel
? No, you do not.QSqlRelationaltableModel
does not hold a reference to or require you to have aQSqlTableModel
for it. It takes only arguments for the foreign table name things:Constructs a
QSqlRelation
object, wheretableName
is the SQL table name to which a foreign key refers,indexColumn
is the foreign key, anddisplayColumn
is the field that should be presented to the user.And uses that in constructing the SQL queries. So if you have a
QSqlTableModel
for the foreign table, which you'd like to update, you have to provide whatever code you like which tells you from foreign table name whichQSqlTableModel
you have created for this.QSqlRelationalTableModel
knows nothing about this. You keep assuming it is cleverer than it is. -
@JonB said in QListView with custom QStyledItemDelegate not working:
If you look at the foreign key column in the referencing table in a row, data(QModelIndex(row, fkc), Qt::EditRole) returns the value actually stored in the table (i.e. an ID) while data(QModelIndex(row, fkc), Qt::DisplayRole) returns the mapped value of the ID in the foreign table (e.g. a name).
Sorry that I ask again here, but somehow I do not manage to get what you wrote. I post times briefly code.
QSqlRelationalTableModel *DatabaseManager::germanFungusNameDataModel() const { QSqlRelationalTableModel *model = new QSqlRelationalTableModel; model->setTable("Deutscher_Pilzname"); model->setRelation(model->fieldIndex("Deutscher_name_id"), QSqlRelation("Deutscher_Name", "Id", "Name")); model->setJoinMode(QSqlRelationalTableModel::LeftJoin); model->setSort(0, Qt::AscendingOrder); model->setEditStrategy(QSqlRelationalTableModel::OnManualSubmit); model->select(); while (model->canFetchMore()) { model->fetchMore(); model->relationModel(0)->fetchMore(); } return model; }
My foreign column is zero. Now I can do that:
//mGermanFungusNameDataModel -> the above QSqlRelationTableModel mGermanFungusNameDataModel->index(row, 0).data(Qt::DisplayRole).toString(); // returns the Name mGermanFungusNameDataModel->index(row, 0).data(Qt::EditRole); //Returns a QVariant but also with the name instead of the ID.
What am I doing wrong here? Can you please go into this in more detail?
-
@Gabber said in QListView with custom QStyledItemDelegate not working:
mGermanFungusNameDataModel->index(row, 0).data(Qt::EditRole); //Returns a QVariant but also with the name instead of the ID.
Hmm, I (admit I) made an assumption about what
EditRole
would hold.You know more than I now. I did say look at the documentation. Please do so. Because I just did for you, and actually I discover:
QSqlTableModel *QSqlRelationalTableModel::relationModel(int column) constReturns a QSqlTableModel object for accessing the table for which column is a foreign key, or nullptr if there is no relation for the given column.
The returned object is owned by the QSqlRelationalTableModel.
However, I am confused, because searching woboq I do not find any implementation for this method anywhere, only its declaration (as
virtual
and with no body) in the.h
file. In 2009(!) one guy claimed in https://www.qtcentre.org/archive/index.php?t-22222.htmlI finally got it to work. I used the table model returned by relationModel to insert into the lookup table. I also had to call submitAll on the QSqlRelationalTableModel itself (after calling it on the relation tableModel) to get that table to notice that lookup table had changed. I don't know if calling submitAll on the relation tableModel is actually rquired, but it had to be talled on the QSqlRelationalTableModel.
Meanwhile I think you should read through https://forum.qt.io/topic/68212/qsqlrelationaltablemodel-get-relation-foreign-key-value
I might have a look at the
EditRole
/DisplayRole
sometime/tomorrow, but really I don't know much you can't discover for yourself.Finally, it still is not clear to me just what you are doing with that list, what you are doing binding it to a
QListView
, why you don't use aQSqlRelationalDelegate
instead, what you want to happen when you "edit" in the list. All I see is you overtyping one German name with another (brand now one?), maybe you want to update that in yourDeutscher_Name
table, I don't know. -
@JonB
I have good news for you, I finally got it right. So what have I done now?@Gabber said in QListView with custom QStyledItemDelegate not working:
//Mushroom - Table
+----+--------------+
| ID | MushroomName |
+----+--------------+
| 1 | MushroomA |
| 2 | MushroomB |
| 3 | MushroomC |
| 4 | MushroomD |
+----+--------------+//GermanNames - Table
+----+----------+
| ID | Name |
+----+----------+
| 1 | GerNameA |
| 2 | GerNameB |
| 3 | GerNameC |
| 4 | GerNameD |
+----+----------+//GermanMushroomNames - Table
+------------+---------------+
| MushroomID | GermanNamesID |
+------------+---------------+
| 1 | 2 |
| 1 | 3 |
| 2 | 1 |
| 3 | 1 |
+------------+---------------+I created a QSqlTableModel from each of these 3 tables. Then using the Mushroom ID in the GermanMushroomNames with the setFilter function return the corresponding GermanName ID. With the help of this ID I have then filtered in GermanNames with setFilter for the corresponding name. In the QListView I then set the GermanName model.
Now I can see and edit the correct data.
I would like to thank you again sincerely for the support. Without you and your tip, that QSqlRelationTable the Foreign-Key Column are not editable, I think I would never have come to the solution.
Top, many thanks!
-
@Gabber said in QListView with custom QStyledItemDelegate not working:
I created a QSqlTableModel from each of these 3 tables. Then using the Mushroom ID in the GermanMushroomNames with the setFilter function return the corresponding GermanName ID. With the help of this ID I have then filtered in GermanNames with setFilter for the corresponding name. In the QListView I then set the GermanName model.
That's just the kind of thing I was trying to say to look for, well done :)