I want to animate the color (in time) of a QTableview cell once it's value is updated via the connected data model to attract the end-users attention that something has changed.
The idea is that the color changes in gradients of f.i. blue, starts off blue just after the value change and fades to white in about 1 ~ 2 seconds.
I guess one has to use the QStyledItemDelegate here since I use the model-view concept (http://doc.qt.io/qt-5/model-view-programming.html).
One needs a trigger for the cell's value-change for the animation to start, this can be achieved via the paint() method since it is called on a value change. One can figure out the row and column, from the index parameter that is passed to paint(), to get a hold of which cell to animate.
So far so good, you can set the color of that cell to let's say blue. Here comes the problem; the color shall fade towards white in time steps. So I was thinking about a QTimer in the QStyledItemDelegate class and maintain a kinda bookkeeping for the cell's that are being animated (could be a simple list of countdown values that are used to calculate the blue-gradient color. The lower the value the more the gradient goes towards white, once 0 the result is white which is the default color of the cell. On each QTimer timeout() event all values not equal to 0 are lowered by 1. The QStyledItemDelegate is only connected to the row of the QTableview that I want to color-animate i.e. where the value items are displayed.
Problem I face is that:
- paint() is a const method so you cannot change any class parameters.
- how to re-paint the cell color on a QTimer event (re-paint the whole QTableview is not god-style)
I managed to get it to work but I consider it a dirty solution. What I did is maintain the bookkeeping of the color animation for each cell in the data-model. I consider it a dirty solution because the color-animation is only a visual aspect so imho it should not reside in the data-model. In this way it is also not a portable solution i.e. in another project you have to rework a lot to get it to work.
I stripped down my application to the core problem. Full code can be found here (a working application): https://github.com/fruitCoder123/animated_tableview_cell
void TableViewDelegateValueWritable::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
// Paint background
uint8_t red_gradient = calculate_color_gradient(RGB_RED_MAX, RGB_RED_MIN, red_gradient_step_size, m_step_value);
uint8_t green_gradient = calculate_color_gradient(RGB_GREEN_MAX, RGB_GREEN_MIN, green_gradient_step_size, m_step_value);
painter->fillRect(option.rect, QColor(red_gradient, green_gradient, 255));
// Paint text
QStyledItemDelegate::paint(painter, option, index);
}
uint8_t TableViewDelegateValueWritable::calculate_color_gradient(const uint8_t MAX_COLOR, const uint8_t MIN_COLOR, const uint8_t step_size, uint8_t step) const
{
uint16_t color = (step_size * (1 + MAX_COLOR_GRADIENT_STEP - step)) + MIN_COLOR;
// Handle overflow and rounding errors
if(color > MAX_COLOR || color > (MAX_COLOR-(step_size/2)))
color = MAX_COLOR;
return static_cast<uint8_t>(color);
}
void TableViewDelegateValueWritable::gradient_timer_elapsed()
{
if(m_step_value)
{
m_step_value--;
m_timer->start(GRADIENT_TIMEOUT_VALUE);
//this->paint(m_painter, m_option, m_model_index);
}
}
I spent a horrific amount of hours to find a nice solution. I started with Qt a month ago so maybe I lack knowledge. Hopefully someone can give a hint how to solve it in a nice way - encapsulated in the view and not entangled with the data-model.