Qt zabezpiecz widget podczas działania animacji

0

W jaki sposób mogę "zabezpieczyć" / ochronić przed sygnałami widget np QToolBar podczas działania na nim QPropertyAnimation? Musze to zrobić bo w przypadku gdy animacja jest w trakcie działania a widget dostanie sygnał np uruchom_animacje_ponownie to dostaję

QPropertyAnimation: you can't change the target of a running animation

w tej chwili mam to zrobione tak że przy uruchamianiu animacji robię setEnabled( false ) i łącze QPropertyAnimation::finished() z wywołaniem setEnabled( true ) ale to rozwiązanie mimo że działa nie bardzo mi się podoba. Jest lepszy sposób aby czekać z sygnałami do zakończenia danej animacji?

0
  1. Pokaż kod
  2. zawsze możesz anulować poprzednią animację by ustawić nową (jeśli nie ustawisz punktu wyjściowego to animacje ładnie się połączą).
  3. Poza tym dobrym rozwiązaniem jest QStateMachine, które wtedy zarządza animacjami pomiędzy stanami.
0
void MainWindow::menu_option_enabled( QAbstractButton * button )
{

    animation_enabled->setTargetObject( button );
    animation_enabled->setPropertyName( "pos" );
    animation_enabled->setDuration( 100 );
    animation_enabled->setStartValue( button->pos() );
    animation_enabled->setEndValue( QPoint( button->pos().x(), button->pos().y() - 20 ) );
    //animation_enabled->setEasingCurve( QEasingCurve::OutBack );

    menu_option_disabled();
    animation_enabled->start();

    option_enabled = button_group->id( button );
    button->setEnabled( false );

}

void MainWindow::menu_option_disabled()
{

    animation_disabled->setTargetObject( button_group->button( option_enabled ));
    animation_disabled->setPropertyName( "pos" );
    animation_disabled->setDuration( 100 );
    animation_disabled->setStartValue( button_group->button( option_enabled )->pos() );
    animation_disabled->setEndValue( 
    QPoint( button_group->button( option_enabled )->pos().x(),   
                 button_group->button( option_enabled )->pos().y() + 20 ) );

    animation_disabled->start();

}

Po aktywowaniu jednego buttona ten zmienia swoją pozycje a jednocześnie poprzedni button wraca do swojej pozycji bazowej.

Ad 2 nie wchodzi w grę bo chce uniemożliwić przerwanie animacji.
Ad 3 wydaje się być rozwiązaniem lepszym niż setEnabled( true / false ) bo mogę ustawic stany on i off i wtedy zrobić coś takiego

void MainWindow::menu_option_enabled( QAbstractButton * button )
{

    if( animation_state == on ) {
           return;
    }
    else {
         // wykonaj animacje...
    }

}

dobrze myślę?

0

nie wiem co powoduje aktywację animacji, ale jeśli jest to jakiś przycisk lub coś podobnego to można go wyłączyć na czas animacji (setEnabled(false)), a po zakończeniu animacji (animacja ma taki sygnał) znowu go aktywować (setEnabled(true)).

Co mi się w twoim kodzie nie podoba:

  • ustalanie początku i końca animacji na podstawie wartości animowanego parametru. setStartValue można sobie zupełnie darować, bo jeśli tego nie ustawisz to samo się ustawia pobierając wartość z properties'a. Wartość końcowa powinna być ustalona sztywno lub w relacji do innego elementu ui.
  • wygląda na to, że powinieneś użyć QParallelAnimationGroup
0

Masz racje

setStartValue
jest zbędne ;)
Animacja jest aktywowana poprzez strzałki ( left / right ) albo poprzez klikniecie danego buttona myszka. W tej chwili radzę sobie poprzez właśnie ustawianie setEnabled( true / false ).
Tylko że dalej jest problem z animacja gdy ta jest wykonywana a ja rozciągam sobie okno MainWindow - wywołuje to

void MainWindow::paintEvent( QPaintEvent * event )

i animacja zaczyna wariować po czym kompletnie się "rozjeżdża" - tutaj chyba najlepszym sposobem jest zablokowanie mozliwości powiększania głównego okna podczas trwania animacji?
I jeszcze jedno ważne pytanie - co masz na myśli poprzez

Wartość końcowa powinna być ustalona sztywno lub w relacji do innego elementu ui

wydawało mi się że kodem

animation_enabled->setEndValue( QPoint( button->pos().x(), button->pos().y() - 20 ) );

sztywno podnoszę buttona? Może mi się tylko wydawało ;)

Dodaje gifa żebyś mógł zobaczyć o czym rozmawiamy ;)

0

Emacs jeśli wiesz od którego z obiektów dostajesz przerywający animację sygnał to zapodaj na nim funkcję QObject::blockSignals(),jak się animacja skończy odblokuj go.
Inną opcją może być połączenie sygnałów do obiektu z animacją poprzez typ połączenia QueuedConnection

0

Pojawiły się nowe fakty więc pisze. Te buttony są wrzucone w zwykłego QToolBar który to z lewej i prawej strony ma widgety które mają ustawione

spacer_left->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding );
spacer_right->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding );

po to aby buttony przy zmianie wielkości głównego okna zawsze były na środku. I teraz załączam gifa co się dzieje z buttonami gdy w trakcie animacji zmieniam wielkość MainWindow.
Już naprawdę nie wiem czemu tak się dzieje... Animacja jest wrażliwa na każdą zmianę wielkości okna i "skacze" a najczęściej po prostu totalnie się "rozsypuje".

1

problem polega na tym, że te buttony siedzą w layoucie, więc za ich pozycję i rozmiar odpowiada layout, a ty wtryniasz się layoutowi animacją.
Efekt jest taki, że przy zmianie rozmiaru layout ustawia swoją pozycję, a animacja swoją.

0

Masz rację, tylko pozostaje pytanie co z tym fantem mogę zrobić? Czy muszę ręcznie zarządzać pozycją buttonów w resizeEvent() głównego okna? jeśli tak to musiałbym dynamicznie zmieniać wartość endValue() animacji a tutaj po pierwsze nie wiem czy to w ogóle możliwe a po drugie boje się że straciłbym jej płynność.
Jeśli jest to totalnie złe podejście do tego co chce zrobić to może mógłbyś mi podpowiedzieć jak coś takiego powinno się robić?

1

Wyjść jest kilka:

  1. własny layout, który obsługuję animację (troszkę roboty jest i trzeba widzieć jak działa system layoutów)
  2. wpakować buttona do pustego widgeta (albo layouta), który będzie siedział layoucie i będzie obsługiwał twoją animację (mało roboty, tylko sizeHint trzeba zaimplementować, dając mu troszkę miejsca).
  3. zarządzać pozycją ręcznie (wychodzi prawie na to samo co własny layout)
  4. ...
0

Dziękuje @MarekR22 twój sposób zadziałał ;) problemy z animacja znikły po wrzuceniu tych buttonów do layoutu a z tym layoutem do widgetu i dopiero wtedy ten widget umieściłem w QToolBar ;) To tak w skrócie

QWidget * buttons_widget = new QWidget( ui->mainToolBar );
buttons_widget->setMinimumSize( QSize( 350, 90 ) );

QHBoxLayout * box = new QHBoxLayout( ui->mainToolBar );

// create and add buttons in layout
QToolButton * prompt = new QToolButton( buttons_widget );

// .....
box->addWidget( prompt );
buttons_widget->setLayout( box );

// add layout in mainToolBar
ui->mainToolBar->addWidget( buttons_widget );

Musiałem tylko zadbać o minimalna szerokość / wysokość widgetu żeby jego layout nie musiał podejmować żadnych działań ;)

0

Nie o to dokładnie mi chodziło, ale może być. Powinno być raczej tak, że tworzysz nowy widget (layout) implementujesz w nim sizeHint uwzględniając swoje wymagania co do miejsca.
Zresztą napisałem wersję z layout'em obsługującym animację, ale zostawiłem go na innym komputerze. Jeśli znowu nie zapomnę to wrzucę go tu wieczorem.

1
#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 detlaActive
               READ isDeltaActive
               WRITE setDeltaActive
               NOTIFY deltaActiveChanged)

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

    QPoint delta() const;

    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;

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

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

public slots:
    void setDelta(const QPoint &value);
    void setDeltaActive(bool active = true);

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;
}

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()));
}

1 użytkowników online, w tym zalogowanych: 0, gości: 1