QPropertyAnimation for widgets in layout - problem



  • Hi all,

    I wanted to make widgets increase in height with QPropertyAnimation, when widgets are arranged with QVboxLayout.
    I fought that I solved this problem, but when I animate more than one widget, they start to move/shake during animation.

    I have prepared minimum working example, here example
    The problem appears when you press "open" button for first, second, then third widget, you can see then that they are shaking, moving slightly up and down during "open" animation.

    Has someone idea what to do to avoid this ?
    I can set setSizeConstraint(QLayout::SetFixedSize) on main layout and they dont shake, but then resizing and other doesn't work.

    Best Regards
    Marek



  • Hi

    I have made it to work - with a little help ;)
    so in case anyone had this problem below is single file example (main.cpp)

    #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"
    
    

    Best Regards
    Marek


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.