QPropertyAnimation объекта QGraphicsTextItem с рамкой делает текст шатким

Я оживляю QGraphicsTextItem что я добавил рамку вокруг. Во время анимации текст кажется слегка дрожащим внутри кадра, что очень раздражает.

Пример кода:

class MovingFrameText : public QGraphicsTextItem
{
Q_OBJECT;

public:
MovingFrameText( ) : QGraphicsTextItem(0)
{
setPlainText ( "human ");
QFont f =  font();
f.setPixelSize(40);
setFont(f);
setFlags(QGraphicsItem::ItemIsMovable);
}

QRectF boundingRect() const
{
return QGraphicsTextItem::boundingRect().adjusted(-2,-2,+2,+2);
}

void paint(QPainter *painter,const QStyleOptionGraphicsItem *option,QWidget *widget)
{
QGraphicsTextItem::paint(painter,option,widget);
painter->setPen(Qt::black);
painter->drawRect(boundingRect());
}

};

int main(int argc, char *argv[])
{
QApplication app(argc,argv);MovingFrameText t;
t.setPos(640,680);

QGraphicsScene scene;
scene.addItem(&t);

QGraphicsView view(&scene);
view.resize(640, 680);
view.show();

auto moveAnimation = new QPropertyAnimation( &t,  "pos" );
moveAnimation->setDuration( 10000 );
moveAnimation->setStartValue( QPointF(640, 680) );
moveAnimation->setEndValue(  QPointF(0, 0) );
moveAnimation->setEasingCurve( QEasingCurve::Linear );
moveAnimation->start(QAbstractAnimation::DeleteWhenStopped);

return app.exec();
}

Есть ли способ сгладить анимацию?

1

Решение

Вы можете существенно улучшить анимацию:

  1. С помощью QVariantAnimation вместо QPropertyAnimation и звонит QGraphicsItem::update на каждой итерации
  2. Буферизация картины с использованием дополнительного QPainter с QPixmap в качестве холста для всех операций покраски, а затем покрасить холст с помощью художника, переданного paint метод

Замечания: QGraphicsTextItem будет все еще немного дрожать, но по крайней мере он будет вести себя как один объект вместо нескольких независимых.

Вот пример, который я подготовил для вас, как ваш код может быть изменен для реализации предложенного решения:

class MovingFrameText : public QGraphicsTextItem
{
public:
MovingFrameText(const QString &text, QGraphicsItem *parent = nullptr)
: QGraphicsTextItem(parent)
{
QFont f(font());

f.setPixelSize(40);

setFont(f);
setPlainText(text);
}

void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
painter->setClipping(true);
painter->setClipRect(option->rect);
painter->setRenderHint(QPainter::SmoothPixmapTransform);

QPixmap canvas(option->rect.size());
QPainter canvasPainter;

canvas.fill(Qt::transparent);

canvasPainter.begin(&canvas);

canvasPainter.setFont(font());
canvasPainter.drawRect(option->rect.adjusted(0, 0, -1, -1));
canvasPainter.drawText(option->rect, toPlainText());

painter->drawPixmap(0, 0, canvas);
}
};

int main(int argc, char *argv[])
{
QApplication app(argc,argv);

QGraphicsView view;
auto *t = new MovingFrameText("human");

view.setScene(new QGraphicsScene(&view));
view.setAlignment(Qt::AlignLeft | Qt::AlignTop);
view.setSceneRect(0, 0, 640, 680);
view.scene()->addItem(t);
view.show();

auto *moveAnimation = new QVariantAnimation();

moveAnimation->setDuration(10000);
moveAnimation->setStartValue(QPointF(640, 680));
moveAnimation->setEndValue(QPointF(0, 0));
moveAnimation->start(QAbstractAnimation::DeleteWhenStopped);

QObject::connect(moveAnimation, &QVariantAnimation::valueChanged, [t](const QVariant &value){
t->setPos(value.toPointF());
t->update();
});

return app.exec();
}
2

Другие решения

Ответ @scopchanov очень хороший. Я добавил здесь решение, которое я также протестировал, и, кажется, работает. Мне понадобится дополнительное время, чтобы проверить, какой из них, на мой взгляд, лучше, но я оставлю это здесь для других, чтобы попытаться, если вы закончите с аналогичной проблемой.

class MovingFrameText : public QGraphicsTextItem
{

Q_PROPERTY( QPointF pos READ pos WRITE setPosition )
public:

void setPosition(QPointF pos)
{
setPos(floor(pos.x()+0.5),floor(pos.y()+.5) );
}

};
1

По вопросам рекламы [email protected]