PDA

View Full Version : QTableView + BackgroundRole + Gradient



alcotor
3rd May 2011, 13:20
I could use a little help to verify if I'm missing something, or if this is a Qt-bug.

Please see the attached screenshot and the code.




#include <QTableView>
#include <QStandardItemModel>
#include <QLinearGradient>
#include <QList>
#include <QStandardItem>

class ChartModel : public QStandardItemModel
{
public:
ChartModel():QStandardItemModel()
{
for (int i = 0; i < 15; ++i)
{
QList<QStandardItem*> row;
for (int j = 0; j < 3; ++j)
{
QStandardItem* item = new QStandardItem;
item->setData(i%10, Qt::EditRole);
row.push_back(item);
}
appendRow(row);
}
}

QVariant data(const QModelIndex& index, int role) const
{
if (role == Qt::BackgroundRole)
{
QLinearGradient grad(QPointF(0, 0), QPointF(1, 0));
grad.setColorAt(0, Qt::blue);
grad.setCoordinateMode(QGradient::ObjectBoundingMo de);
grad.setColorAt(static_cast<double>(index.row()%10)/10.0, Qt::blue);
grad.setColorAt(static_cast<double>(index.row()%10)/10.0+0.00001, Qt::white);
grad.setColorAt(1, Qt::white);
return QBrush(grad);
}
return QStandardItemModel::data(index, role);
}
};

extern void showExample()
{
QTableView* view = new QTableView;
view->setWindowTitle("charttest");
view->setWindowFlags(Qt::Tool);
view->setModel(new ChartModel);
view->show();
}



6347

It's obvious what effect I want to achive. I want to use the gradient to "draw" something.
I like it because it is very powerful yet easy to maintain. However, this example only works for the first column. After that, the Brush does not get the correct offset (or something), and the Gradient does not get the color right.
I expect column 2 and 3 to show the exact same "histogram", but they do not.

Any idea why this is not working?

I would really prefer this approach over any custom QPainter stuff. I'm on Qt 4.6.2, and I use QStyledItemDelegate, not QItemDelegate, so overwriting QItemDelegate::drawBackground is not where I want to go.


Thanks for any help

MasterBLB
3rd May 2011, 14:31
Well QItemDelegate::drawBackground() is pointless as there is a bug in Qt-this function has been forgotten to be declared as virtual :/
About your code,I'd write it like:

if(role == Qt::BackgroundRole && index.isValid())
but even without this 'purism' it seems to be OK.For the test,screw up the gradient and return a simple QColor to see if it works.

alcotor
3rd May 2011, 15:10
...For the test,screw up the gradient and return a simple QColor to see if it works.

The simple QColor case works fine. I can light up the table like a christmas-tree.

With the gradients I can actually "draw" something meaningful. You could create pie-charts, horizontal and vertical bar charts, and more, and since all is relative, those "charts" resize with the column-witdh. All this works in the first column, but not in the second.
ObjectBoundingMode does not seem to work correctly, or I'm doing something wrong.

:confused:

MasterBLB
3rd May 2011, 16:06
I've found something: change the second color from white to other,yellow for example.It seems that everything what's wrong lies inside the gradient itself,not the view/model
Also,try to comment out lines:

grad.setColorAt(static_cast<double>(index.row()%10)/10.0, Qt::blue);
grad.setColorAt(static_cast<double>(index.row()%10)/10.0+0.00001, Qt::white);

alcotor
3rd May 2011, 16:53
Yes, the problem is within the Gradient. Exactly!

Updated example (smaller):


#include <QTableView>
#include <QStandardItemModel>
#include <QLinearGradient>
#include <QList>
#include <QStandardItem>

class ChartModel : public QStandardItemModel
{
public:
ChartModel():QStandardItemModel()
{
for (int i = 0; i < 10; ++i)
{
QList<QStandardItem*> row;
for (int j = 0; j < 2; ++j)
{
QStandardItem* item = new QStandardItem;
item->setData("50 %", Qt::EditRole);
row.push_back(item);
}
appendRow(row);
}
}

QVariant data(const QModelIndex& index, int role) const
{
if (role == Qt::BackgroundRole && index.isValid())
{
QLinearGradient grad(QPointF(0, 0), QPointF(1, 0));
grad.setCoordinateMode(QGradient::ObjectBoundingMo de);
grad.setColorAt(0, Qt::blue);
grad.setColorAt(0.5, Qt::blue);
grad.setColorAt(0.50001, Qt::white);
grad.setColorAt(1, Qt::white);
return QBrush(grad);
}
return QStandardItemModel::data(index, role);
}
};

extern void showExample()
{
QTableView* view = new QTableView;
view->setWindowTitle("charttest");
view->setWindowFlags(Qt::Tool);
view->setModel(new ChartModel);
view->show();
}


In this example I want the blue to end at 50% of the width of the column, and use white for the other 50%. And to do that, I use a LinearGradient
0 (blue) ---- 0.5 (blue) - 0.500001 (white) ---- 1.0 (white).
The actual gradient effect is only between 0.5 and 0.500001.

6353

Anyway, here is another screenshot. Maybe someone has an idea?

Thanks
:confused:

MasterBLB
3rd May 2011, 19:09
Alcotor how the griadient in the first colum behaves when you're resizing it?Has it still the proper size?

alcotor
3rd May 2011, 21:05
Alcotor how the griadient in the first colum behaves when you're resizing it?Has it still the proper size?

Yes.

6358

You can see how the Gradient aligns at 50% under the header "1", which is center aligned. However, only for the first column. When you resize the header, you notice some interaction between the gradient-calculation of column two, and the width of column one, which I think is the bug.

Well, I'm not ready to give up though!

:p

MasterBLB
3rd May 2011, 21:55
I've discovered that

grad.setSpread(QGradient::RepeatSpread);
helps a bit until the columns are not resized.