使用 QPropertyAnimation 在布局中为它们设置动画时小部件会晃动

问题描述 投票:0回答:3

当使用 QVBoxLayout 排列小部件时,我想使用 QPropertyAnimation 使小部件的高度增加。

问题是,当我打开多个小部件时,它们在动画期间开始移动/摇晃。

当您按第一个、第二个、然后第三个小部件的“打开”按钮时,就会出现问题,您可以看到它们在“打开”动画期间晃动、轻微上下移动。

如何避免这种情况?

我可以在主布局上设置

setSizeConstraint(QLayout::SetFixedSize)
,它们不会晃动,但例如调整大小就不起作用了。

c++ qt animation qlayout qpropertyanimation
3个回答
1
投票

前段时间,我编写了一个布局,它可以对它包含的小部件位置进行动画处理。

您应该以这样的方式构建布局,即每个应该动画的小部件都应该位于此布局内(每个应该动画的小部件有一个

AnimLayout
):

#include <QLayout>

QT_FORWARD_DECLARE_CLASS(QPropertyAnimation)

class AnimLayout : public QLayout
{
    Q_OBJECT

    Q_PROPERTY(QPoint delta
               READ delta
               WRITE setDelta
               NOTIFY deltaChanged)

    Q_PROPERTY(QRect widgetRect
               READ widgetRect
               WRITE setWidgetRect
               NOTIFY widgetRectChanged)

    Q_PROPERTY(bool active
               READ isDeltaActive
               WRITE setDeltaActive
               NOTIFY deltaActiveChanged)

public:
    explicit AnimLayout(QWidget *parent = 0);
    ~AnimLayout();

    QPoint delta() const;
    void setDelta(const QPoint &value);

    QSize sizeHint() const;
    void setGeometry(const QRect &);
    QSize minimumSize() const;
    int count() const;
    QSize deltaSize() const;

    QRect widgetRect() const;
    void setWidgetRect(const QRect &value);

    bool isDeltaActive() const;
    void setDeltaActive(bool active = true);

    void updateItemPosition();
private:
    void addItem(QLayoutItem *item);
    QLayoutItem *itemAt(int index) const;
    QLayoutItem *takeAt(int index);

signals:
    void deltaChanged(const QPoint &value);
    void widgetRectChanged(const QRect &value);
    void deltaActiveChanged(bool active);

public slots:
    void testIt();

private:
    QLayoutItem *item;
    QPropertyAnimation *animation;
    QPoint mDelta;
    bool mDeltaActive;
};
#include "animlayout.h"
#include <QPropertyAnimation>

AnimLayout::AnimLayout(QWidget *parent) :
    QLayout(parent) ,
    item(0)
{
    animation = new QPropertyAnimation(this);
    animation->setPropertyName("widgetRect");
    animation->setDuration(400);
    animation->setTargetObject(this);
    mDeltaActive = false;
}

AnimLayout::~AnimLayout()
{
    delete item;
}

QPoint AnimLayout::delta() const
{
    return mDelta;
}

void AnimLayout::setDelta(const QPoint &value)
{
    if (mDelta != value) {
        mDelta = value;
        emit deltaChanged(mDelta);
        invalidate();
    }
}

void AnimLayout::addItem(QLayoutItem *newItem)
{
    Q_ASSERT(!item);
    animation->stop();
    item =newItem;
    emit widgetRectChanged(item->geometry());
    invalidate();
}

QSize AnimLayout::sizeHint() const
{
    if (!item)
        return QSize();
    QSize result(item->sizeHint());
    result += deltaSize();

    int m = 2*margin();
    result += QSize(m,m);

    return result;
}

void AnimLayout::updateItemPosition()
{
    QRect dest = contentsRect();

    QPoint d = delta();
    if (isDeltaActive()) {
        d = -d;
    }

    if (d.x()!=0) {
        if (d.x()>0) {
            dest.setLeft(dest.left()+d.x());
        } else {
            dest.setRight(dest.right()+d.x());
        }
    }

    if (d.y()) {
        if (d.y()>0) {
            dest.setTop(dest.top()+d.y());
        } else {
            dest.setBottom(dest.bottom()+d.y());
        }
    }

    animation->setEndValue(dest);
    if (widgetRect()!=dest) {
        animation->start();
    }
}

void AnimLayout::setGeometry(const QRect &rect)
{
    QLayout::setGeometry(rect);

    updateItemPosition();
}

QLayoutItem *AnimLayout::itemAt(int i) const
{
    return i==0?item:0;
}

QLayoutItem *AnimLayout::takeAt(int i)
{
    Q_ASSERT(i==0);
    QLayoutItem *r = item;
    item = 0;
    return r;
}

void AnimLayout::testIt()
{
    setDeltaActive(!isDeltaActive());
}

QRect AnimLayout::widgetRect() const
{
    if (item)
        return item->geometry();
    return QRect();
}

void AnimLayout::setWidgetRect(const QRect &value)
{
    if (item && item->geometry()!=value) {
        item->setGeometry(value);
        emit widgetRectChanged(item->geometry());
    }
}

bool AnimLayout::isDeltaActive() const
{
    return mDeltaActive;
}

void AnimLayout::setDeltaActive(bool active)
{
    if (active!=mDeltaActive) {
        mDeltaActive = active;
        animation->stop();
        updateItemPosition();
        emit deltaActiveChanged(active);
    }
}

QSize AnimLayout::minimumSize() const
{
    QSize result(deltaSize());
    if (item) {
        result += item->minimumSize();
    }
    int m = 2*margin();
    result += QSize(m,m);
    return result;
}

int AnimLayout::count() const
{
    return item?1:0;
}

QSize AnimLayout::deltaSize() const
{
   return QSize(qAbs(mDelta.x()), qAbs(mDelta.y()));
}

它有一些您不需要的额外功能(

mDelta
)。


-1
投票

这个答案效果很好。

但是,我做到了,没有晃动。我所做的更改是将 QWidget 添加到 QScrollArea 中,然后在该小部件上设置 QVBoxLayout。

下面是

animatedLayout
打开/关闭
AnimLayout
的示例。


#include <QApplication>
#include <QtWidgets>

class AnimLayout : public QLayout
{
    Q_OBJECT

    Q_PROPERTY(QRect widgetRect
               READ widgetRect
               WRITE setWidgetRect
               NOTIFY widgetRectChanged)

public:
    explicit AnimLayout(QWidget *parent = 0);
    ~AnimLayout();

    QSize sizeHint() const;
    void setGeometry(const QRect &);
    QSize minimumSize() const;
    int count() const;

    QRect widgetRect() const;
    void setWidgetRect(const QRect &value);

    void updateItemPosition();
private:
    void addItem(QLayoutItem *item);
    QLayoutItem *itemAt(int index) const;
    QLayoutItem *takeAt(int index);

signals:
    void widgetRectChanged(const QRect &value);

public slots:
private:
    QLayoutItem *item;
    QPropertyAnimation *animation;
};

struct FrameDataStruct {
    QFrame      *mainFrame;
    QFrame      *upFrame;
    QFrame      *downFrame;
    QPushButton *button;
    QVBoxLayout *upFrameLayout;
    QLabel      *text;
    QVBoxLayout *downFrameLayout;
    QVBoxLayout *frameLayout;
    QPropertyAnimation  *animation;
    int         frame_id;
    int         basic_height;
    bool        expanded;
    AnimLayout  *animLayout;
};

class Proptest : public QMainWindow
{
    Q_OBJECT

public:
    explicit Proptest();
    ~Proptest();
private slots:
    void setDataStruct();
    void startAnimation(int frame_id);
    void animFinished(int frame_id);
private:
    QMap<int,FrameDataStruct*> frameMap;
    QSignalMapper   *animStartMapper;
    QSignalMapper   *animFinishedMapper;
    bool            initialized;
    QWidget         *scrollWidget;
    QVBoxLayout     *main_layout;
    QWidget         *widget;
    QScrollArea     *scrollArea;
    QVBoxLayout     *central_layout;
    bool            layoutAnimated;
};

Proptest::Proptest()
    : widget(new QWidget)
{
    setCentralWidget(widget);
    this->setGeometry(200,200,300,600);
    central_layout=new QVBoxLayout(widget);
    scrollArea=new QScrollArea(widget);
    central_layout->addWidget(scrollArea);


    animStartMapper=new QSignalMapper(this);
    connect(animStartMapper,SIGNAL(mapped(int)),this,SLOT(startAnimation(int)));

    animFinishedMapper=new QSignalMapper(this);
    connect(animFinishedMapper,SIGNAL(mapped(int)),this,SLOT(animFinished(int)));

    scrollWidget=new QWidget(widget);
    scrollArea->setWidget(scrollWidget);
    main_layout=new QVBoxLayout(scrollWidget);
    main_layout->setSizeConstraint(QLayout::SetMinAndMaxSize);
    scrollArea->setWidgetResizable(true);
    scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);

    layoutAnimated=true;

    this->setDataStruct();
}
void Proptest::setDataStruct() {
    for(int i=0;i<5;i++) {
        FrameDataStruct *r=new FrameDataStruct;
        r->expanded=false;
        r->frame_id=i;
        r->mainFrame=new QFrame(scrollWidget);
        r->upFrame=new QFrame(r->mainFrame);
        r->upFrame->setMinimumHeight(40);
        r->button=new QPushButton(QString("open"),r->upFrame);
        r->upFrameLayout=new QVBoxLayout(r->upFrame);
        r->upFrameLayout->addWidget(r->button);
        r->downFrame=new QFrame(r->mainFrame);
        r->text=new QLabel(QString("some text SOME TEXT some text"),r->downFrame);
        r->downFrameLayout=new QVBoxLayout(r->downFrame);
        r->downFrameLayout->addWidget(r->text);
        r->frameLayout=new QVBoxLayout(r->mainFrame);
        r->frameLayout->addWidget(r->upFrame);
        r->frameLayout->addItem(new QSpacerItem(10,10));
        r->frameLayout->addWidget(r->downFrame);
        r->frameLayout->setStretch(0,0);
        r->frameLayout->setStretch(1,1);
        r->frameLayout->setStretch(2,0);

        r->downFrame->setVisible(false);

        r->animation=new QPropertyAnimation(r->mainFrame,"minimumHeight");
        r->animation->setDuration(500);
        connect(r->button,SIGNAL(clicked(bool)),animStartMapper,SLOT(map()));
        animStartMapper->setMapping(r->button,r->frame_id);
        connect(r->animation,SIGNAL(finished()),animFinishedMapper,SLOT(map()));
        animFinishedMapper->setMapping(r->animation,r->frame_id);


        if(layoutAnimated) {
            r->animLayout=new AnimLayout();
            r->animLayout->addWidget(r->mainFrame);
            main_layout->addItem(r->animLayout);
        }
        else {
            main_layout->addWidget(r->mainFrame);
        }

        frameMap.insert(r->frame_id,r);
    }
    main_layout->addItem(new QSpacerItem(10,10,QSizePolicy::Minimum,QSizePolicy::Expanding));
    main_layout->setStretch(main_layout->count()-1,1);
}
void Proptest::startAnimation(int frame_id) {
    FrameDataStruct *r=frameMap[frame_id];
    if(r->expanded) {
        r->expanded=false;
        if(layoutAnimated) {
            r->downFrame->hide();
        }
        else {
            r->downFrame->setVisible(false);
            r->animation->setStartValue(r->mainFrame->geometry().height());
            r->animation->setEndValue(r->basic_height);
        }

    } else {
        r->expanded=true;
        if(layoutAnimated) {
            r->downFrame->show();
        }
        else {
            r->basic_height=r->mainFrame->geometry().height();
            r->animation->setStartValue(r->basic_height);
            r->animation->setEndValue(r->basic_height*2);
            r->upFrame->setMinimumHeight(r->upFrame->height());
        }
    }
    if(!layoutAnimated)
        r->animation->start();
}
void Proptest::animFinished(int frame_id) {
    FrameDataStruct *r=frameMap[frame_id];
    if(r->expanded)
        r->downFrame->setVisible(true);
}
Proptest::~Proptest() {

}

AnimLayout::AnimLayout(QWidget *parent) :
    QLayout(parent) ,
    item(0)
{
    animation = new QPropertyAnimation(this);
    animation->setPropertyName("widgetRect");
    animation->setDuration(400);
    animation->setTargetObject(this);
}

AnimLayout::~AnimLayout()
{
    delete item;
}
void AnimLayout::addItem(QLayoutItem *newItem)
{
    Q_ASSERT(!item);
    animation->stop();
    item =newItem;
    emit widgetRectChanged(item->geometry());
    invalidate();
}

QSize AnimLayout::sizeHint() const
{
    if (!item)
        return QSize();
    QSize result(item->sizeHint());

    int m = 2*margin();
    result += QSize(m,m);

    return result;
}

void AnimLayout::updateItemPosition()
{
    QRect dest = contentsRect();

    animation->setEndValue(dest);
    if (widgetRect()!=dest) {
        animation->start();
    }
}

void AnimLayout::setGeometry(const QRect &rect)
{
    QLayout::setGeometry(rect);

    updateItemPosition();
}

QLayoutItem *AnimLayout::itemAt(int i) const
{
    return i==0?item:0;
}

QLayoutItem *AnimLayout::takeAt(int i)
{
    Q_ASSERT(i==0);
    QLayoutItem *r = item;
    item = 0;
    return r;
}

QRect AnimLayout::widgetRect() const
{
    if (item)
        return item->geometry();
    return QRect();
}

void AnimLayout::setWidgetRect(const QRect &value)
{
    if (item && item->geometry()!=value) {
        item->setGeometry(value);
        emit widgetRectChanged(item->geometry());
    }
}

QSize AnimLayout::minimumSize() const
{
    QSize result(item->minimumSize());

    int m = 2*margin();
    result += QSize(m,m);
    return result;
}

int AnimLayout::count() const
{
    return item?1:0;
}

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

    QApplication a(argc, argv);

    Proptest w;
    w.show();

    return a.exec();
}

#include "main.moc"

-2
投票

我又回答了,不知道为什么,之前的被删除了。

我使用了你的 AnimLayout,效果很好。 下面是 main.cpp 中的示例,其中包含“layoutAnimated”变量来打开和关闭 AnimLayout。

#include <QApplication>
#include <QtWidgets>

class AnimLayout : public QLayout
{
    Q_OBJECT

    Q_PROPERTY(QRect widgetRect
               READ widgetRect
               WRITE setWidgetRect
               NOTIFY widgetRectChanged)

public:
    explicit AnimLayout(QWidget *parent = 0);
    ~AnimLayout();

    QSize sizeHint() const;
    void setGeometry(const QRect &);
    QSize minimumSize() const;
    int count() const;

    QRect widgetRect() const;
    void setWidgetRect(const QRect &value);

    void updateItemPosition();
private:
    void addItem(QLayoutItem *item);
    QLayoutItem *itemAt(int index) const;
    QLayoutItem *takeAt(int index);

signals:
    void widgetRectChanged(const QRect &value);

public slots:
private:
    QLayoutItem *item;
    QPropertyAnimation *animation;
};

struct FrameDataStruct {
    QFrame      *mainFrame;
    QFrame      *upFrame;
    QFrame      *downFrame;
    QPushButton *button;
    QVBoxLayout *upFrameLayout;
    QLabel      *text;
    QVBoxLayout *downFrameLayout;
    QVBoxLayout *frameLayout;
    QPropertyAnimation  *animation;
    int         frame_id;
    int         basic_height;
    bool        expanded;
    AnimLayout  *animLayout;
};

class Proptest : public QMainWindow
{
    Q_OBJECT

public:
    explicit Proptest();
    ~Proptest();
private slots:
    void setDataStruct();
    void startAnimation(int frame_id);
    void animFinished(int frame_id);
private:
    QMap<int,FrameDataStruct*> frameMap;
    QSignalMapper   *animStartMapper;
    QSignalMapper   *animFinishedMapper;
    bool            initialized;
    QWidget         *scrollWidget;
    QVBoxLayout     *main_layout;
    QWidget         *widget;
    QScrollArea     *scrollArea;
    QVBoxLayout     *central_layout;
    bool            layoutAnimated;
};

Proptest::Proptest()
    : widget(new QWidget)
{
    setCentralWidget(widget);
    this->setGeometry(200,200,300,600);
    central_layout=new QVBoxLayout(widget);
    scrollArea=new QScrollArea(widget);
    central_layout->addWidget(scrollArea);


    animStartMapper=new QSignalMapper(this);
    connect(animStartMapper,SIGNAL(mapped(int)),this,SLOT(startAnimation(int)));

    animFinishedMapper=new QSignalMapper(this);
    connect(animFinishedMapper,SIGNAL(mapped(int)),this,SLOT(animFinished(int)));

    scrollWidget=new QWidget(widget);
    scrollArea->setWidget(scrollWidget);
    main_layout=new QVBoxLayout(scrollWidget);
    main_layout->setSizeConstraint(QLayout::SetMinAndMaxSize);
    scrollArea->setWidgetResizable(true);
    scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);

    layoutAnimated=true;

    this->setDataStruct();
}
void Proptest::setDataStruct() {
    for(int i=0;i<5;i++) {
        FrameDataStruct *r=new FrameDataStruct;
        r->expanded=false;
        r->frame_id=i;
        r->mainFrame=new QFrame(scrollWidget);
        r->upFrame=new QFrame(r->mainFrame);
        r->upFrame->setMinimumHeight(40);
        r->button=new QPushButton(QString("open"),r->upFrame);
        r->upFrameLayout=new QVBoxLayout(r->upFrame);
        r->upFrameLayout->addWidget(r->button);
        r->downFrame=new QFrame(r->mainFrame);
        r->text=new QLabel(QString("some text SOME TEXT some text"),r->downFrame);
        r->downFrameLayout=new QVBoxLayout(r->downFrame);
        r->downFrameLayout->addWidget(r->text);
        r->frameLayout=new QVBoxLayout(r->mainFrame);
        r->frameLayout->addWidget(r->upFrame);
        r->frameLayout->addItem(new QSpacerItem(10,10));
        r->frameLayout->addWidget(r->downFrame);
        r->frameLayout->setStretch(0,0);
        r->frameLayout->setStretch(1,1);
        r->frameLayout->setStretch(2,0);

        r->downFrame->setVisible(false);

        r->animation=new QPropertyAnimation(r->mainFrame,"minimumHeight");
        r->animation->setDuration(500);
        connect(r->button,SIGNAL(clicked(bool)),animStartMapper,SLOT(map()));
        animStartMapper->setMapping(r->button,r->frame_id);
        connect(r->animation,SIGNAL(finished()),animFinishedMapper,SLOT(map()));
        animFinishedMapper->setMapping(r->animation,r->frame_id);


        if(layoutAnimated) {
            r->animLayout=new AnimLayout();
            r->animLayout->addWidget(r->mainFrame);
            main_layout->addItem(r->animLayout);
        }
        else {
            main_layout->addWidget(r->mainFrame);
        }

        frameMap.insert(r->frame_id,r);
    }
    main_layout->addItem(new QSpacerItem(10,10,QSizePolicy::Minimum,QSizePolicy::Expanding));
    main_layout->setStretch(main_layout->count()-1,1);
}
void Proptest::startAnimation(int frame_id) {
    FrameDataStruct *r=frameMap[frame_id];
    if(r->expanded) {
        r->expanded=false;
        if(layoutAnimated) {
            r->downFrame->hide();
        }
        else {
            r->downFrame->setVisible(false);
            r->animation->setStartValue(r->mainFrame->geometry().height());
            r->animation->setEndValue(r->basic_height);
        }

    } else {
        r->expanded=true;
        if(layoutAnimated) {
            r->downFrame->show();
        }
        else {
            r->basic_height=r->mainFrame->geometry().height();
            r->animation->setStartValue(r->basic_height);
            r->animation->setEndValue(r->basic_height*2);
            r->upFrame->setMinimumHeight(r->upFrame->height());
        }
    }
    if(!layoutAnimated)
        r->animation->start();
}
void Proptest::animFinished(int frame_id) {
    FrameDataStruct *r=frameMap[frame_id];
    if(r->expanded)
        r->downFrame->setVisible(true);
}
Proptest::~Proptest() {

}

AnimLayout::AnimLayout(QWidget *parent) :
    QLayout(parent) ,
    item(0)
{
    animation = new QPropertyAnimation(this);
    animation->setPropertyName("widgetRect");
    animation->setDuration(400);
    animation->setTargetObject(this);
}

AnimLayout::~AnimLayout()
{
    delete item;
}
void AnimLayout::addItem(QLayoutItem *newItem)
{
    Q_ASSERT(!item);
    animation->stop();
    item =newItem;
    emit widgetRectChanged(item->geometry());
    invalidate();
}

QSize AnimLayout::sizeHint() const
{
    if (!item)
        return QSize();
    QSize result(item->sizeHint());

    int m = 2*margin();
    result += QSize(m,m);

    return result;
}

void AnimLayout::updateItemPosition()
{
    QRect dest = contentsRect();

    animation->setEndValue(dest);
    if (widgetRect()!=dest) {
        animation->start();
    }
}

void AnimLayout::setGeometry(const QRect &rect)
{
    QLayout::setGeometry(rect);

    updateItemPosition();
}

QLayoutItem *AnimLayout::itemAt(int i) const
{
    return i==0?item:0;
}

QLayoutItem *AnimLayout::takeAt(int i)
{
    Q_ASSERT(i==0);
    QLayoutItem *r = item;
    item = 0;
    return r;
}

QRect AnimLayout::widgetRect() const
{
    if (item)
        return item->geometry();
    return QRect();
}

void AnimLayout::setWidgetRect(const QRect &value)
{
    if (item && item->geometry()!=value) {
        item->setGeometry(value);
        emit widgetRectChanged(item->geometry());
    }
}

QSize AnimLayout::minimumSize() const
{
    QSize result(item->minimumSize());

    int m = 2*margin();
    result += QSize(m,m);
    return result;
}

int AnimLayout::count() const
{
    return item?1:0;
}

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

    QApplication a(argc, argv);

    Proptest w;
    w.show();

    return a.exec();
}

#include "main.moc"
© www.soinside.com 2019 - 2024. All rights reserved.