QSqlRelationalTableModel with multiple Joins?
-
Did you try re-running
qmake
before building ?Thank you, sir! That solved it :)
I'm going back to your very original post and thinking about what you're actually trying to achieve anyway. The code we're talking about is all well & good, if you want to proceed, but I'm wondering about your expectations of the interface.
Back to the original post... at the moment I can successfully double-click items and edit them.
How they are edited and how the edit is going to affect the table and the database is something I believe I can do by myself now.
I guess I've caught the mechanism behind it!
BUT
One thing that has remained unresolved is the fact that anytime I change a field (setData() is called) the edited cell must change its background color.
I still don't get this one.
P.S: @JonB I wanted to thank you too. It's amazing the way you're helping me. I just can't thank you enough :)
-
Do you mean you want to mark the cell as "edited" ?
-
@SGaist said in QSqlRelationalTableModel with multiple Joins?:
Do you mean you want to mark the cell as "edited" ?
Yes but... my way!
I want to change it's background color to, say, Cyan.
This way the user, before committing the changes, can take a look and see what is going to really change in the database.
-
One way could be to keep a vector of edited cells that you update when
setData
is called with the EditRole and that you will use whendata
is called for the BackgroundRole and you return the colour you want. -
One way could be to keep a vector of edited cells that you update when
setData
is called with the EditRole and that you will use whendata
is called for the BackgroundRole and you return the colour you want.@SGaist said in QSqlRelationalTableModel with multiple Joins?:
One way could be to keep a vector of edited cells that you update when
setData
is called with the EditRole and that you will use whendata
is called for the BackgroundRole and you return the colour you want.Thank you. The problem is I can't find the method which allows me to change te color to a cell. I guess something like setBackgroundColor(row,column)
-
Because there's none. Do you have a custom setData method ? If so you should emit the dataChanged signal properly and it should trigger an update of the view which should request all the roles including the one for background colour.
-
Because there's none. Do you have a custom setData method ? If so you should emit the dataChanged signal properly and it should trigger an update of the view which should request all the roles including the one for background colour.
-
That's what dataChanged is for.
-
That's what dataChanged is for.
@SGaist said in QSqlRelationalTableModel with multiple Joins?:
That's what dataChanged is for.
In fact I emit the signal including the role
emit dataChanged(index, index, roleArray);
roleArray includes Qt::BackgroundColor
Now what? Where do I select the color the cell has to become?
-
Re-implement the
data
method and handle theBackgroundRole
special case there. -
@SGaist said in QSqlRelationalTableModel with multiple Joins?:
Re-implement the
data
method and handle theBackgroundRole
special case there.I now return the red color when the role is Qt::BackgroundColor
if (!item.isValid()) return QVariant(); if(role == Qt::BackgroundRole) return QColor(255, 0, 0); return QVariant();
My Table View now is all red without changing anything!
Also, is it good to return a default-constructed QVariant when no particular criteria are met?
-
@SGaist said in QSqlRelationalTableModel with multiple Joins?:
Re-implement the
data
method and handle theBackgroundRole
special case there.I now return the red color when the role is Qt::BackgroundColor
if (!item.isValid()) return QVariant(); if(role == Qt::BackgroundRole) return QColor(255, 0, 0); return QVariant();
My Table View now is all red without changing anything!
Also, is it good to return a default-constructed QVariant when no particular criteria are met?
My Table View now is all red without changing anything!
Have you understood you now need to look up the cell coordinates (
item
) in the vector of changed cells, as per:@SGaist said in QSqlRelationalTableModel with multiple Joins?:
One way could be to keep a vector of edited cells that you update when
setData
is called with the EditRole and that you will use whendata
is called for the BackgroundRole and you return the colour you want. -
My Table View now is all red without changing anything!
Have you understood you now need to look up the cell coordinates (
item
) in the vector of changed cells, as per:@SGaist said in QSqlRelationalTableModel with multiple Joins?:
One way could be to keep a vector of edited cells that you update when
setData
is called with the EditRole and that you will use whendata
is called for the BackgroundRole and you return the colour you want.@JonB @SGaist said in QSqlRelationalTableModel with multiple Joins?:
My Table View now is all red without changing anything!
Have you understood you now need to look up the cell coordinates (
item
) in the vector of changed cells, as per:@SGaist said in QSqlRelationalTableModel with multiple Joins?:
One way could be to keep a vector of edited cells that you update when
setData
is called with the EditRole and that you will use whendata
is called for the BackgroundRole and you return the colour you want.I did it.
But now all the text is gone!
The color is applied to the modified cells.
But there's no text! Even if I don't edit any cell, the text is gone!
-
@JonB @SGaist said in QSqlRelationalTableModel with multiple Joins?:
My Table View now is all red without changing anything!
Have you understood you now need to look up the cell coordinates (
item
) in the vector of changed cells, as per:@SGaist said in QSqlRelationalTableModel with multiple Joins?:
One way could be to keep a vector of edited cells that you update when
setData
is called with the EditRole and that you will use whendata
is called for the BackgroundRole and you return the colour you want.I did it.
But now all the text is gone!
The color is applied to the modified cells.
But there's no text! Even if I don't edit any cell, the text is gone!
-
@devhobby
What? You want text as well as color? ;-)I think you'll need to show us your
data()
function now?@JonB said in QSqlRelationalTableModel with multiple Joins?:
@devhobby
What? You want text as well as color? ;-)I think you'll need to show us your
data()
function now?Oh God, don't tell me it's another pain in the neck!
QVariant MyModel::data(const QModelIndex& item, int role) const { if(role == Qt::BackgroundRole) { if(MainWindow::cellsEdited.contains(item)) return QColor(66, 197, 244, 150); } return QVariant(); }
Yes... I want the background color to stay behind the text, of course...
-
@JonB said in QSqlRelationalTableModel with multiple Joins?:
@devhobby
What? You want text as well as color? ;-)I think you'll need to show us your
data()
function now?Oh God, don't tell me it's another pain in the neck!
QVariant MyModel::data(const QModelIndex& item, int role) const { if(role == Qt::BackgroundRole) { if(MainWindow::cellsEdited.contains(item)) return QColor(66, 197, 244, 150); } return QVariant(); }
Yes... I want the background color to stay behind the text, of course...
@devhobby
You're supposed to be only handlingQt::BackgroundRole
special case. The rest of the time presumably you want to return the inherited class's implementation ofdata()
. So: instead of yourreturn QVariant();
catch-all, you want whatever it is (remember I'm not C++) forreturn base::data(item, role);
.The idea is: your overload is not called only for the color (which is when
role == Qt::BackgroundRole
), it's called loads of other times for quite different information (including the text) with other values ofrole
. You were returning an emptyQVariant
for any other "property" of the cell, including its text! That's how it works.BTW, if it's any consolation, I'm as new to this as you are. So I didn't know it worked this way till earlier too.
-
@devhobby
You're supposed to be only handlingQt::BackgroundRole
special case. The rest of the time presumably you want to return the inherited class's implementation ofdata()
. So: instead of yourreturn QVariant();
catch-all, you want whatever it is (remember I'm not C++) forreturn base::data(item, role);
.The idea is: your overload is not called only for the color (which is when
role == Qt::BackgroundRole
), it's called loads of other times for quite different information (including the text) with other values ofrole
. You were returning an emptyQVariant
for any other "property" of the cell, including its text! That's how it works.BTW, if it's any consolation, I'm as new to this as you are. So I didn't know it worked this way till earlier too.
@JonB said in QSqlRelationalTableModel with multiple Joins?:
@devhobby
You're supposed to be only handling Qt::BackgroundRole special case. The rest of the time presumably you want to return the inherited class's implementation of data(). So: instead of your return QVariant(); catch-all, you want whatever it is (remember I'm not C++) for return base::data(item, role);.
The idea is: your overload is not called only for the color (which is when role == Qt::BackgroundRole), it's called loads of other times for quite different information (including the text) with other values of role. You were returning an empty QVariant for any other "property" of the cell, including its text! That's how it works.
BTW, if it's any consolation, I'm as new to this as you are. So I didn't know it worked this way till earlier too.Oh ok thanks, it worked!
Do you know, by chance, in which order are setData() and data() called? When is data() specifically called? When a dataChanged signal is emitted?
-
@JonB said in QSqlRelationalTableModel with multiple Joins?:
@devhobby
You're supposed to be only handling Qt::BackgroundRole special case. The rest of the time presumably you want to return the inherited class's implementation of data(). So: instead of your return QVariant(); catch-all, you want whatever it is (remember I'm not C++) for return base::data(item, role);.
The idea is: your overload is not called only for the color (which is when role == Qt::BackgroundRole), it's called loads of other times for quite different information (including the text) with other values of role. You were returning an empty QVariant for any other "property" of the cell, including its text! That's how it works.
BTW, if it's any consolation, I'm as new to this as you are. So I didn't know it worked this way till earlier too.Oh ok thanks, it worked!
Do you know, by chance, in which order are setData() and data() called? When is data() specifically called? When a dataChanged signal is emitted?
@devhobby
Well, I presume:setData()
is called whenever the data is changed/setdataChanged
signal should be emitted bysetData()
whenever new data is different from current datadata()
is called many times, with whatever role aspect is wanted, not only by your code but also by Qt code whenever it wants a piece of information
http://doc.qt.io/qt-5/qt.html#ItemDataRole-enum:
enum Qt::ItemDataRole
Each item in the model has a set of data elements associated with it, each with its own role. The roles are used by the view to indicate to the model which type of data it needs. Custom models should return data in these types.
-
@devhobby
Well, I presume:setData()
is called whenever the data is changed/setdataChanged
signal should be emitted bysetData()
whenever new data is different from current datadata()
is called many times, with whatever role aspect is wanted, not only by your code but also by Qt code whenever it wants a piece of information
http://doc.qt.io/qt-5/qt.html#ItemDataRole-enum:
enum Qt::ItemDataRole
Each item in the model has a set of data elements associated with it, each with its own role. The roles are used by the view to indicate to the model which type of data it needs. Custom models should return data in these types.
@JonB said in QSqlRelationalTableModel with multiple Joins?:
@devhobby
Well, I presume:setData()
is called whenever the data is changed/setdataChanged
signal should be emitted bysetData()
whenever new data is different from current datadata()
is called many times, with whatever role aspect is wanted, not only by your code but also by Qt code whenever it wants a piece of information
http://doc.qt.io/qt-5/qt.html#ItemDataRole-enum:
enum Qt::ItemDataRole
Each item in the model has a set of data elements associated with it, each with its own role. The roles are used by the view to indicate to the model which type of data it needs. Custom models should return data in these types.
What I have noticed:
- Double-Click signal on the Table View calls slot function onTableChanged()
- onTableChanged() calls data() when it has finished
<> - Flags are checked
- setData() is called if flag is editable
- Signal dataChanged is emitted by setData()
Where's the problem?
Say I want to allow modification of some cells (name, surname...) and forbid modification of others (primary key, foreign key...)
I have a vector of immutable columns (0, 3, 5...)
I have to check if the column being edited belongs to the vector of immutable columns... TWICE!
One inside the flag() function and one inside onTableChanged()
- Doing the check in flags() keeps me from modifying the content of the cell (hence, disabling the double-click)
- Doing the check in onTableChanged() keeps the cells from being added to the vector of cells that need to be coloured by data()
Both checks are identical, but serve for 2 different purposes: one for disabling the double-click, the other for disabling the coloration.
All of these events are not sequential. I put a symbol <> in the list above to evidence two apparently unrelated events.
If these events had been sequential, I would've written only ONE check at the beginning of the event.
So, the question is: should I keep the situation this way (2 checks)?
-
If you overwrote the flags method correctly, you shouldn't be able to edit the corresponding cell so you shouldn't have to make several checks.