如何在 QListView 中为列表项的边框设置动画?
How do I animate the border of a list item in a QListView?
我正在使用带有从 QStyledItemDelegate 扩展的自定义委托的 QListView。我重新实现了 paint 方法来自定义绘制列表中的每个项目。在 paint 方法中,我在列表视图中围绕 selected 项目绘制边框。
我希望能够在 select 项目时为项目边框设置动画。例如,如果预期的项目边框为 5 像素,我希望在项目 selected 时将其 "animate in" 从 0 像素到 5 像素。
我最初的想法是连接一个计时器,使其每 50 毫秒关闭一次,并在每次计时器关闭时让代理绘制,直到绘制完整个边框宽度。但是,代理重新实现的paint方法是const的,所以我不能在paint方法的每次遍历过程中保存或者更新一个border width成员变量。
完成此任务的最佳方法是什么?
一个可能的解决方案是创建一个管理项目边框大小的角色,并使用 QVariantAnimation
:
更新它
#include <QApplication>
#include <QListView>
#include <QPainter>
#include <QStandardItemModel>
#include <QStyledItemDelegate>
#include <QVariantAnimation>
int BorderSizeRole = Qt::UserRole+1;
class AnimationDelegate: public QStyledItemDelegate{
public:
using QStyledItemDelegate::QStyledItemDelegate;
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const{
QStyledItemDelegate::paint(painter, option, index);
bool ok;
int borderSize = index.data(BorderSizeRole).toInt(&ok);
if(borderSize >0 && ok){
painter->save();
QPen pen(QBrush(Qt::red), borderSize);
painter->setPen(pen);
painter->drawRect(option.rect);
painter->restore();
}
}
};
class CustomAnimation: public QVariantAnimation{
QPersistentModelIndex m_index;
QAbstractItemModel *m_model;
public:
CustomAnimation(QAbstractItemModel *m_model, QPersistentModelIndex index, QObject *parent=nullptr)
: QVariantAnimation(parent),
m_index(index),
m_model(m_model)
{
setStartValue(0);
setEndValue(5);
setDuration(50*5);
connect(this, &CustomAnimation::valueChanged, this, &CustomAnimation::on_valueChanged);
// delete animation
start(QAbstractAnimation::DeleteWhenStopped);
}
private:
Q_SLOT void on_valueChanged(const QVariant & value){
if(m_model)
m_model->setData(m_index, value, BorderSizeRole);
else
stop();
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QListView view;
view.setItemDelegate(new AnimationDelegate(&view));
QStandardItemModel model;
for(int i=0; i<10; i++){
QStandardItem *item = new QStandardItem(QString("item %1").arg(i));
item->setData(-1, BorderSizeRole);
model.appendRow(item);
}
view.setModel(&model);
QObject::connect(view.selectionModel(), &QItemSelectionModel::selectionChanged,
[&model](const QItemSelection &selected, const QItemSelection & deselected){
for(const QModelIndex & index: selected.indexes()){
new CustomAnimation(&model, QPersistentModelIndex(index));
}
// remove border
for(const QModelIndex & index: deselected.indexes()){
model.setData(index, -1, BorderSizeRole);
}
});
view.show();
return a.exec();
}
我正在使用带有从 QStyledItemDelegate 扩展的自定义委托的 QListView。我重新实现了 paint 方法来自定义绘制列表中的每个项目。在 paint 方法中,我在列表视图中围绕 selected 项目绘制边框。
我希望能够在 select 项目时为项目边框设置动画。例如,如果预期的项目边框为 5 像素,我希望在项目 selected 时将其 "animate in" 从 0 像素到 5 像素。
我最初的想法是连接一个计时器,使其每 50 毫秒关闭一次,并在每次计时器关闭时让代理绘制,直到绘制完整个边框宽度。但是,代理重新实现的paint方法是const的,所以我不能在paint方法的每次遍历过程中保存或者更新一个border width成员变量。
完成此任务的最佳方法是什么?
一个可能的解决方案是创建一个管理项目边框大小的角色,并使用 QVariantAnimation
:
#include <QApplication>
#include <QListView>
#include <QPainter>
#include <QStandardItemModel>
#include <QStyledItemDelegate>
#include <QVariantAnimation>
int BorderSizeRole = Qt::UserRole+1;
class AnimationDelegate: public QStyledItemDelegate{
public:
using QStyledItemDelegate::QStyledItemDelegate;
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const{
QStyledItemDelegate::paint(painter, option, index);
bool ok;
int borderSize = index.data(BorderSizeRole).toInt(&ok);
if(borderSize >0 && ok){
painter->save();
QPen pen(QBrush(Qt::red), borderSize);
painter->setPen(pen);
painter->drawRect(option.rect);
painter->restore();
}
}
};
class CustomAnimation: public QVariantAnimation{
QPersistentModelIndex m_index;
QAbstractItemModel *m_model;
public:
CustomAnimation(QAbstractItemModel *m_model, QPersistentModelIndex index, QObject *parent=nullptr)
: QVariantAnimation(parent),
m_index(index),
m_model(m_model)
{
setStartValue(0);
setEndValue(5);
setDuration(50*5);
connect(this, &CustomAnimation::valueChanged, this, &CustomAnimation::on_valueChanged);
// delete animation
start(QAbstractAnimation::DeleteWhenStopped);
}
private:
Q_SLOT void on_valueChanged(const QVariant & value){
if(m_model)
m_model->setData(m_index, value, BorderSizeRole);
else
stop();
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QListView view;
view.setItemDelegate(new AnimationDelegate(&view));
QStandardItemModel model;
for(int i=0; i<10; i++){
QStandardItem *item = new QStandardItem(QString("item %1").arg(i));
item->setData(-1, BorderSizeRole);
model.appendRow(item);
}
view.setModel(&model);
QObject::connect(view.selectionModel(), &QItemSelectionModel::selectionChanged,
[&model](const QItemSelection &selected, const QItemSelection & deselected){
for(const QModelIndex & index: selected.indexes()){
new CustomAnimation(&model, QPersistentModelIndex(index));
}
// remove border
for(const QModelIndex & index: deselected.indexes()){
model.setData(index, -1, BorderSizeRole);
}
});
view.show();
return a.exec();
}