PDA

View Full Version : Color a row - QSqlRelationalTableModel or QSqlQueryModel



AKG
3rd September 2016, 12:35
I am trying both QSqlRelationalTableModel and QSqlQueryModel to view records from a database in a QtableView, but when I want to do something as simple as setting a background color on a row, i bang my head on the wall,... two days, and still nothing is working... i cant believe how impossible it is to find a simple solution to this. I have read so many messages on forums and various websites, and there is nothing that works, it all suggests the same things that does not work. You can for example not use:

model->setData(index, QVariant(QColor(Qt::yellow)), Qt::BackgroundRole);

... because both QSqlRelationalTableModel and QSqlQueryModel is ignoring Qt::BackgroundRole. QSqlQueryModel is read only, so only model->data() works for getting data. QSqlRelationalTableModel can model->setData() but it only cares about Qt::EditRole, and ignores all I try to do with background color.

I have found some extremely long and complicated solutions, that might work, but I cant believe that i have to write several pages of code to set a freaking background color on a row.

Please, if someone knows how to do this, I would really appreciate some help on this.
Thank you,

anda_skoa
3rd September 2016, 16:34
You can derive from the model class and reimplement data() such that it returns something for the Qt::BackgroundRole.
Whether you set that value from outside or whether the value is determined in the model itself is up to you.

You can use a QIdentityProxyModel between the model and the view to provide data for additional roles.
More or less very similar to the first approach, but works independent of what the source model is.

You can set your own item delegate that draws the background in a way you want.

First option is obviously the easiest one, only a handful of lines of code, the last one is the most demanding one.

Cheers,
_

AKG
4th September 2016, 05:13
Well, if I create a new class, and a new data(), then i can use the index it receives and read what row im on, and what column im on, and set the color on the row if I know the row number i want.

But lets say i want to put blue background on the row if column 2 is blank. I cant get to my model to create a new index to check on that column from within the data().

anda_skoa
4th September 2016, 09:53
But lets say i want to put blue background on the row if column 2 is blank.

Then you retrieve the value for column 2 of the same row and check it?


I cant get to my model to create a new index to check on that column from within the data().
You don't need to "get to my model", inside data() you are the model.

But even if you weren't QModelIndex::model() always gives you access to the model an index came from.

Cheers,
_

AKG
4th September 2016, 15:18
Im not explaining well, that is why you don't see what I am actually trying to ask. Sorry about that, english is not my primary language.

I have a view with lots of images and hundres of columns, and a ton of rows, so when data() is called by the view on every cell every time the user clicks on something, it takes a lot of time to refresh if I do to much in the data() function.

I understand how to do all that is suggested, and i can get it to do all i want, but the code i end up with is just not fast enough. There is to much code that does things like getting my model from the index, then creating a new index, then getting the row and the column of the cell i am checking the content of from that index, for then finally setting some changes to a cell.... remember, this code is run for every single cell, every singe time something in the view is clicked.

Someone on a different forum made me aware of the sibling function, that lets me stay on the same row, ans i assume all columns are sibling of the same row ?? I have not tired it at this time, but if that is true, i guess that will shorten the code a bit.

I just wish i could do a row at a time, or some other logic ... maybe saving the criteria for the cells in the database, and then displaying every second column, hiding the "setting column" ... i don't know.

If the columns in my database alternate between one settings row, then one content column. The data() function will read the setting row to find out what and how to display it, and display what ever is in the content column.

any thoughts ?? I feel like Im stubling around in the dark right now. Just cant get this to go faster.

anda_skoa
4th September 2016, 17:49
Im not explaining well, that is why you don't see what I am actually trying to ask. Sorry about that, english is not my primary language.

Then maybe post the code for data()?



Someone on a different forum made me aware of the sibling function, that lets me stay on the same row, ans i assume all columns are sibling of the same row ?? I have not tired it at this time, but if that is true, i guess that will shorten the code a bit.

sibling() will also just call index().
If you have the same background color for each cell in a row, you could cache the value.

Cheers,
_

AKG
5th September 2016, 22:54
I don't understand what you mean by cash the value ?

A simple data() function to deal with background color wold be for example like this:


QVariant CustomSqlModel::data(const QModelIndex &index, int role) const
{
const QAbstractItemModel * model = index.model();
QModelIndex index2 = model->index(index.row(), 1, QModelIndex()); // position 1 on current row
QVariant value2 = QSqlQueryModel::data(index2, Qt::DisplayRole);
if(value2.toString() == "" && role == Qt::BackgroundRole) { // if the column 2 on the same row is blank we set the color
return QVariant(QColor(Qt::yellow));
}
QVariant value = QSqlQueryModel::data(index, role);
return value;
}

The code is called on every cell, so every cell on the row that has a blank column 2 will be painted - because this if is true on all cell on that row

The faster way would be to have a list of what needs to be updated and the indexes saved - and then call a function that goes in to only the selected rows and repaints only those cell that needs extra features / colors etc. This function must be automatically triggered after the view calls the data function on all cells every time it does that.

Any ideas ?

Or if I try to ask a more precise question : where can i intercept the code that fires the data() function ?? so that I can add a function call to a function that only selct spesific cells to pain over again... hmmm dont know if im makins sence or not sorry.

anda_skoa
6th September 2016, 10:30
I don't understand what you mean by cash the value ?

You determine the value the first time you need it and store it in a data member for retrieval when it is needed again.



A simple data() function to deal with background color wold be for example like this:

That is like an example from a C++ recruiter, when potential hires are asked "what is wrong with this".

Obviously the worst thing is doing unnecessary things all the time which are only needed when retrieving the background color, but in order of execution:




const QAbstractItemModel * model = index.model();
Creating an alias for "this". Through a function call so that the compiler can't easily figure out that "model" and "this" are the same.
Would be worse of course if QModelIndex::model() weren't inline and a const expression.




QModelIndex index2 = model->index(index.row(), 1, QModelIndex()); // position 1 on current row
QVariant value2 = QSqlQueryModel::data(index2, Qt::DisplayRole);


As already pointed out, doing things not necessary for all calls to data()




if(value2.toString() == "" && role == Qt::BackgroundRole) { // if the column 2 on the same row is blank we set the color

This is one is a classic :-)
Doing the two condition parts in the wrong order, expensive string comparison before the cheap int comparison.
Also needlessly doing a string comparison, which would be even worse if QString::operator==() didn't have a const char* overload.
Very likely needless conversion from QVariant to QString, depending on the column's data.




QVariant value = QSqlQueryModel::data(index, role);
return value;
}
Creating of a local, non-const, variable to force the compiler to track it for optimization.



The code is called on every cell

Which usually means that code in data() should not be artifically pessimised a lot.



so every cell on the row that has a blank column 2 will be painted - because this if is true on all cell on that row

Which would lend itself to caching.



The faster way would be to have a list of what needs to be updated and the indexes saved - and then call a function that goes in to only the selected rows and repaints only those cell that needs extra features / colors etc. This function must be automatically triggered after the view calls the data function on all cells every time it does that.

The view calls data() when it thinks it needs it, e.g. when a new cell comes into view, when the model signals change.



Or if I try to ask a more precise question : where can i intercept the code that fires the data() function ?? so that I can add a function call to a function that only selct spesific cells to pain over again... hmmm dont know if im makins sence or not sorry.

That is in the view code, in your case likely somewhere here https://code.woboq.org/qt5/qtbase/src/widgets/itemviews/qtableview.cpp.html

Cheers,
_

AKG
8th September 2016, 00:22
Thank you,

I am afraid i am having som problems undestanding what you are saying.
I would really appreciate if you can write the code for this data() function, so i can see this is code. It is easier for me to understand if you just write it in code how this should be done.

Thank you again

jefftee
8th September 2016, 05:09
Pardon me for jumping into this thread late, but are you looking for something as simple as the following (uncompiled and untested but should be close):


QVariant CustomSqlModel::data(const QModelIndex &index, int role) const
{
QVariant value = QSqlQueryModel::data(index, role);

if (role == Qt::BackgroundRole && index.column() == 2 && QSqlQueryModel::data(index, Qt::DisplayRole).toString() == "")
value = QColor(Qt::yellow);

return value;
}

anda_skoa
8th September 2016, 10:35
Close, but the background color applies for all cells of the row. this would be something more like



QVariant CustomSqlModel::data(const QModelIndex &index, int role) const
{
if (role == Qt::BackgroundRole) {
const QModelIndex column1Index = this->index(index.row(), 1);
const QVariant value = QSqlQueryModel::data(column1Index, Qt::DisplayRole);

if (!value.isValid() || value.toString().isEmpty()) return QColor(Qt::yellow);
}

return QSqlQueryModel::data(index, role);
}


Cheers,
_

AKG
8th September 2016, 15:52
Can I not just do this :



QVariant CustomSqlModel::data(const QModelIndex &index, int role) const
{
if (role == Qt::BackgroundRole) {
const QVariant value = QSqlQueryModel::data(this->index(index.row(), 1), Qt::DisplayRole);
if (!value.isValid() || value.toString().isEmpty()) return QColor(Qt::yellow);
}
return QSqlQueryModel::data(index, role);
}

What do you think ?? Is it to cryptic to understand what cell I'm testing ?? this->index(index.row() does mean this row... so to me this is easier to read - but what do you think ??

Maybe it is over kill or bad to do this ??? :


QVariant CustomSqlModel::data(const QModelIndex &index, int role) const
{
if (role == Qt::BackgroundRole) {
if (QSqlQueryModel::data(this->index(index.row(), 1), Qt::DisplayRole).toString().isEmpty()) return QColor(Qt::yellow);
}
return QSqlQueryModel::data(index, role);
}

Crazy to do a one liner ?? :


QVariant CustomSqlModel::data(const QModelIndex &index, int role) const
{
if (role == Qt::BackgroundRole && QSqlQueryModel::data(this->index(index.row(), 1), Qt::DisplayRole).toString().isEmpty()) return QColor(Qt::yellow);
return QSqlQueryModel::data(index, role);
}

anda_skoa
8th September 2016, 16:16
That's basically identical to my code, no?
Just passing the result of index() directly to data(), right?

Cheers,
_

AKG
8th September 2016, 16:25
Sorry, I ment to ask you if any of these 3 alternatives would make a slightly faster code, or if it makes no difference ?? :confused:

Or maybe this is the fastest wiith more readability ?? What do you think, is it faster code ?? :


QVariant CustomSqlModel::data(const QModelIndex &index, int role) const
{
if (role == Qt::BackgroundRole && QSqlQueryModel::data(this->index(index.row(), 1), Qt::DisplayRole).toString().isEmpty()){
return QColor(Qt::yellow);
}
return QSqlQueryModel::data(index, role);
}

anda_skoa
8th September 2016, 17:58
I would go for the first of the three options, it is the most readable IMHO.

Cheers,
_

jefftee
9th September 2016, 00:42
Close, but the background color applies for all cells of the row. this would be something more like
Hard to tell what the actual requirements were... :) So, if the requirement is for a yellow background for each column in a row where column 2 is blank, I'd change my code to be the following:


QVariant CustomSqlModel::data(const QModelIndex &index, int role) const
{
QVariant value = QSqlQueryModel::data(index, role);

if (role == Qt::BackgroundRole && QSqlQueryModel::data(this->index(index.row(), 1), Qt::DisplayRole).toString() == "")
value = QColor(Qt::yellow);

return value;
}


Edit: Fixed *major* bug that resulted in the wrong column being used for the index! :)

AKG
9th September 2016, 02:15
That is interesting, although column 2 is actually the third column as 0 is column 1
but why not const ??



const QVariant value = QSqlQueryModel::data(this->index(index.row(), 1), Qt::DisplayRole);


And what is the fastes code ?? I ask because i will be adding a lot of things to this data function, so it will mater later on what is the fastes.

jefftee
9th September 2016, 02:26
And what is the fastes code ?? I ask because i will be adding a lot of things to this data function, so it will mater later on what is the fastes.
It doesn't matter which of those is fastest if you're going to be "adding a lot of things to this data function", so I suggest you test all of the code that people have written for you and use whichever you wish as the building blocks for adding your own code.

So, my recommendation is stop asking for which version of the code is fastest and start writing the code that *you* need in the data function. :)

Added after 6 minutes:


That is interesting, although column 2 is actually the third column as 0 is column 1
but why not const ??
I can't believe that I actually have to say this, but change the column number to match your needs! See line 6 and see if you can answer yourself why the variable is not const.

AKG
9th September 2016, 04:26
And i can't belive that you are insinutating and assuming that im stupid.... but if you need to do that, we would both be better off if you don't answer me at all.
I was trying to say - why not the other example where const was used - not why you did not use const on a value that is receiving a value.

I was clear in the start that english is not my language and that im spendign lots of time just translating trying to understand the english of what is explained.

Even in the first message i say i have code that is woriking, but it is to big many lines of code.

And this discussion is ONLY about speed, as you can see all the code is painting the row yellow so that is clearly not what this is about but to make if faster, because my view is very slow because so many is loaded and going on.
When is said dont work, i mean it is to slow

jefftee
9th September 2016, 04:45
And i can't belive that you are insinutating and assuming that im stupid.... but if you need to do that, we would both be better off if you don't answer me at all.
I have reread my post and I don't believe that I insinuated or assumed that you are stupid. I just find it astonishing that you ask for people to write code for you and then point out a simple mistake in a code example which I clearly stated was not tested or compiled. How about you test the various code samples people gave you and determine which is fastest or write yourself from scratch rather than demand that someone tell you which is fastest? Learn how to help yourself and you will be much better off in the long run, I promise.

Regarding why the QVariant was not const, try to make it const and compile the sample I gave you and the answer will become obvious.

You insist that performance is the only thing that matters and you fret over which trivial versions of code multiple people have voluntarily given to you is most efficient. You admitted that you will be "adding a lot of things to this data function". Don't you think that the "lot of things" you will be adding may perhaps have a more relevant impact to performance than the trivial examples you were given?

Clearly I don't think I can help you, but my advice to you remains the same. Stop the analysis paralysis and move on to writing the "lot of things" that you need to add to the data method.

Good luck!

AKG
9th September 2016, 06:49
I'm quoting you now as you keep changing your messages... after i reply, making me confused.


I have reread my post and I don't believe that I insinuated or assumed that you are stupid. I just find it astonishing that you ask for people to write code for you and then point out a simple mistake in a code example which I clearly stated was not tested or compiled. How about you test the various code samples people gave you and determine which is fastest or write yourself from scratch rather than demand that someone tell you which is fastest? Learn how to help yourself and you will be much better off in the long run, I promise.

Regarding why the QVariant was not const, try to make it const and compile the sample I gave you and the answer will become obvious.

You insist that performance is the only thing that matters and you fret over which trivial versions of code multiple people have voluntarily given to you is most efficient. You admitted that you will be "adding a lot of things to this data function". Don't you think that the "lot of things" you will be adding may perhaps have a more relevant impact to performance than the trivial examples you were given?

Clearly I don't think I can help you, but my advice to you remains the same. Stop the analysis paralysis and move on to writing the "lot of things" that you need to add to the data method.

Good luck!

It is ok that you assume that another person has a typo, or that you assume he pasted the wrong text and pointing that out, but you are insinuating stupidity or some type of lack of capacity.... and you are on it again now... insinuating that I am demanding, and that i am somehow bothering people refusing to make my own effort.

anda_skoa pointed out many things that is wrong about the code, what the things are and things that should not be int he code etc. and it is clearly just fine to point out "stupid code" but you are not addressing the code but the coder... when you talk about what is wrong.

I am done with this topic, you are welcome to not replying any more of my messages, because you make me feel bad, and ruin my fun.

jefftee
9th September 2016, 07:40
It is ok that you assume that another person has a typo, or that you assume he pasted the wrong text and pointing that out, but you are insinuating stupidity or some type of lack of capacity.... and you are on it again now... insinuating that I am demanding, and that i am somehow bothering people refusing to make my own effort.
Sorry that you feel I have attacked you (again). I only post here to try to help others. I am certainly not the brightest or best coder here, but I *have* been able to help many people.

I hope you are familiar with the proverb "Give a man a fish and he'll eat for a day, teach a man to fish and he will eat for a lifetime". That is how I prefer to try to help people, by pointing them in the right direction so that they can learn how to problem solve on their own. One of the best skills you will ever learn as a programmer is how to problem solve. When you ask for code for the data method or ask which version of the code is the fastest, you are asking for a fish. If someone here gives you that fish, you will have learned nothing.

I gave you an example of a bare bones data method that met your requirement for changing the background color to yellow if column 2 was an empty string, as did several others. When you asked which is fastest of the several examples you were given, I recommended that you test each one and make that determination on your own.

To get you started how to do that, one possible method would be to use QTime::start and QTime::elapsed to calculate the elapsed time between the entry/exit into the data method and output those timings using qDebug so that you can evaluate the elapsed time of each. As you add the "lots of things" functionality into the data method, you can do the same to determine where your data method is spending its time, etc.

A more advanced approach would be to profile your application using an tool like valgrind or similar.

No need to respond to my post, I have done what I can to help until you have demonstrated that you've done what I have suggested and have additional questions. You may also completely ignore my advice and maybe someone will give you the fish, but it won't be me.

Whether you believe me or not, I do hope you accomplish your objective.

Good luck!

AKG
9th September 2016, 10:12
When the compiler sees == on a string, at the same time that we are evaluating the other values, what is all that happens ? and how expensive is that compared to for example the other suggestion where it is using the .isEmpty() ?? What is happening inside isEmpty() ?? is it more expensive or is there something to gain ?

I know you are not interested in answering my questions and that is totally fine, I'm not interested in hearing abut fish anyway ;-)

Now after your insinuations you are clearly starting to parent me on how i should go about learning programming .... just give it a rest already mr America :-) no nead to save the entire world all by yourself.

anda_skoa
9th September 2016, 11:00
When the compiler sees == on a string, at the same time that we are evaluating the other values, what is all that happens ? and how expensive is that compared to for example the other suggestion where it is using the .isEmpty() ?? What is happening inside isEmpty() ?? is it more expensive or is there something to gain ?

isEmpty() is almost certainly faster since the expected value is either a null string or a non-empty string.
But that was one of my original points, wasn't it?

In any case it really doesn't matter, only actually measured timing data does.

The only thing that is always true is that things that are not executed are "faster" than things that are executed, so doing things that aren't necessary is always going to be more time consuming than not doing it in the first place.
Which of course is also one of the initially pointed out issues.

Cheers,
_

AKG
9th September 2016, 12:28
The reason I was repeating this question, was to explain to the other poster jefftee what my question were actually about.

I'm sorry that it sounded like i did not already get it, because you did point it out very well already, and I really appreciate your comment i previous posts. You already explained it very well, and I really appreciate your previous answers. Sorry for the repeated question. That was intended for the other poster to make him understand what my question was before. But it is already answered, you are absolutely correct about that.

Thank you /