i have a problem with QPropertyAnimation in Qt
my code:
QString my_text = "Hello Animation";
ui->textBrowser->setText((quote_text));
ui->textBrowser->show();
QPropertyAnimation animation2(ui->textBrowser,"geometry");
animation2.setDuration(1000);
animation2.setStartValue(QRect(10,220/4,1,1));
animation2.setEndValue(QRect(10,220,201,71));
animation2.setEasingCurve(QEasingCurve::OutBounce);
animation2.start();
till now it seems very good , but the problem is that i can see this animation only when i show a message box after it .
QMessageBox m;
m.setGeometry(QRect(100,180,100,50));
m.setText("close quote");
m.show();
m.exec();
when i remove the code of this message box , i can't see the animation anymore.
the functionality of the program doesn't require showing this MessageBox at all.
Can anybody help?
Maybe it is an update problem. Could you try to connect the valueChanged() signal of QPropertyAnimation to an update() call in the GUI?
My guess is that the code for the animation that you present is inside a larger chunk of code where the control doesn't get back to the event loop (or the event loop hasn't started yet). This means that when the MessageBox's exec function is called, an event loop starts operating again, and the animation starts. If you were to dismiss the message box in the middle of the animation, it would probably freeze at that point, as well.
animation2 is declared as a local variable. When the enclosing function
exits, it is no longer in scope and is deleted. The animation never runs as
it does not exist when Qt returns to the event loop and, as noted in the QAbstractAnimation
documentation
(QPropertyAnimation inherits QAbstractAnimation), for QPropertyAnmiation to execute, it must exist when Qt returns to the event loop.
When control reaches the event loop, the animation will run by itself,
periodically calling updateCurrentTime() as the animation progresses.
The solution is to dynamically allocate animation2 rather than declare it as
a local variable.
QPropertyAnimation *animation2 = new QPropertyAnimation(ui->textBrowser,"geometry");
animation2->setDuration(1000);
animation2->setStartValue(QRect(10,220/4,1,1));
animation2->setEndValue(QRect(10,220,201,71));
animation2->setEasingCurve(QEasingCurve::OutBounce);
animation2->start();
Note that this it the same technique is the same as that used in the C++
example provided in the QPropertyAnmiation
documentation:
QPropertyAnimation *animation = new QPropertyAnimation(myWidget, "geometry");
animation->setDuration(10000);
animation->setStartValue(QRect(0, 0, 100, 30));
animation->setEndValue(QRect(250, 250, 100, 30));
animation->start();
The original question notes:
i can see this animation only when i show a message box after it
This is an interesting side affect of how QMessageBox works. The exec()
method executes an event loop. Since the event loop executes within the scope
of the function enclosing animation2, animation2 still exists and the
desired animation executes.
By default, animation2 will be deleted when the parent, ui->textBrowser in
the original question, is deleted. If you wish for the animation to be
deleted when it completes executing, QAbstractAnimation provides a property
that controls when the animation is deleted. To automatically delete
animation2 when it finishes executing, change the start() method to:
animation2->start(QAbstractAnimation::DeleteWhenStopped);
Related
I am trying to execute a SequentialTransition, but between the animations, I need to execute some commands.
My problem is that it is always executing the last commands passed on the node. Is there any way to fix this?
Where is "is ignored" is the code that I need to be executed in the first animation, then where this "is executed" is the code that I need to be executed in the second animation.
Thanks
private void startAnimation(){
vb_adv.setPrefWidth(197);
ap_services.toBack();// Is ignored
vb_adv.toFront();// Is ignored
ScaleTransition expandAdvertising = new ScaleTransition(Duration.millis(2000), vb_adv);
expandAdvertising.setToX(2);
expandAdvertising.setCycleCount(2);
expandAdvertising.setAutoReverse(true);
ap_services.setPrefWidth(124);
ap_services.toFront();//is executed
ScaleTransition expandService = new ScaleTransition(Duration.millis(2000), ap_services);
expandService.setDelay(Duration.seconds(3));
expandService.setToX(3.7);
expandService.setCycleCount(2);
expandService.setAutoReverse(true);
SequentialTransition sequence = new SequentialTransition(expandAdvertising, expandService);
sequence.play();
}
In the code as you currently have it, you move ap_services to the back of the z-order, and vb_adv to the front:
ap_services.toBack();
vb_adv.toFront();
Then you create and set up you ScaleTransition. Note that doing this part takes essentially no time; all you are doing is configuring the animation which will run later.
The next thing you do is to move ap_services to the front:
ap_services.toFront();
Note that this will happen essentially immediately after the previous calls to toFront() and toBack(), and of course this negates the effect of those calls. So your initial calls are actually executed (not "ignored"), but you immediately do something which undoes their effect.
What you really want is to execute ap_services.toFront() after the ScaleTransition finishes. You can do this by putting that call in an onFinished() handler:
// ap_services.toFront();
expandAdvertising.setOnFinished(e -> ap_services.toFront());
I try much method i have find to do this, but no one work for me.
I have tried with qApp.processEvents() and with update() but no one work.
void GUI::startLoading(int currentFile) {
ui->progressBar->setValue(currentFile);
ui->progressBar->update();
}
currentFile is an int of the current loaded file from another function.
Here's a screenshot of de dubug that told the current value of the progress bar, but the progress bar don't increment.
Are you doing busy work in the GUI thread? Qt needs to get a chance to process events for the redraw to happen.
update() only enqueues the redraw. Note that setValue() calls update() itself. To avoid this problem, you can call QEventLoop::processEvents() from time to time (e.g. at the same place where you update the progress bar), or use a worker thread.
MyWindow which inherits from QMainWindow. MyWindow contains a QGLWidget that displays an animation.
The problem is that the animation pauses whenever I open a menu or resize the window.
The animation is implemented by calling QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateRequest)) periodically, then calling redrawing each time the window receives the QEvent::UpdateRequest, like this:
bool MyWindow::event(QEvent *event)
{
qDebug() << event;
switch (event->type())
{
case QEvent::UpdateRequest:
render();
return true;
default:
return QMainWindow::event(event);
}
}
As seen from qDebug(), while a menu is open or the window is being resized, the window stops receiving update request events.
Is there a setting on QMainWindow/QWidget to make it continue to receive update request events? Or is there some better way to implement the animation?
Edit: I'm on Mac OS X.
This may be a Qt bug. I'll investigate.
Alas, you're way overcomplicating your code.
The postEvent should be simply replaced by this->update(). Behind the scenes it posts the event for you.
One can simply connect a QTimer instance's signal to widget, SLOT(update()). If you want to save on a QObject instance, use QBasicTimer and reimplement timerEvent as follows: void MyWidget::timerEvent(QTimerEvent* ev) { if (ev.timerId() == m_timer.timerId()) update(); }
There's no need to deal with event() reimplementation. Simply reimplement paintEvent() - that's what it's for.
Qt GUI updates are performing on MainThread. So slow gui response is reasonable, if you have many gui functionality does at same time. So generally, do not overload MaiThread with so many heavey function calls.
Probable solution to speed up your GUI response.
If PostEvent is called by your MainThread( if you are using timer from main gui thread ), instead move those to backend functionality in
a worker thread and postEvent once it has been done.
you call QCoreApplication::processEvents(), after your render(); function in MainThread.
This will help system to process all the other events that are in the event-loop before to continue
Please check, following link How to improve GUI response
Note: When creating and triggering the timer it will run within your thread by default, it wont start another thread.
Since I haven't heard any more from Kuba Ober about the possibility of this being a Qt bug, I went ahead and filed a bug report: https://bugreports.qt-project.org/browse/QTBUG-33382
I was able to partially work around the problem by calling the render() function more directly — that is, instead of sending an event, receiving the event, and having the event handler call the function. I accomplished this with a dispatch queue (but not the main dispatch queue, since that's tied to the default run loop so it has the same problem). However, working with the QGLWidget on multiple threads was difficult. After trying for a while to use the moveToThread() function to make this work, and considering other factors involved in the project, I decided to use something other than Qt to display this window.
So I'm trying to use QPropertyAnimation on a QWidget that has been added to a QGraphicsScene and it's not working. I can't really copy and paste the code as it's intertwined with a somewhat complicated system but the summary is basically this:
A custom widget is created
CustomWidget widget;
Widget is added to a graphics scene
graphicsScene,addWidget(widget);
at some later point, one of the widget's member functions tries to create and start a QPropertyAnimation
QPropertyAnimation *anim = new QPropertyAnimation(this, "_opacity");
anim->setDuration(5000);
anim->setKeyValueAt(0, 0);
anim->setKeyValueAt(1, 1);
anim->start();
instead of a smooth animation, the property changes to the second value and stays there.
I've looked online at some related problems and their solutions, but none seem to match my situation. Does anyone know how to accomplish this?
EDIT: I discovered I just needed to make the WRITE function for _opacity call update()
I guess you are allocating QPropertyAnimation and friends on the stack instead of the heap.
Note that the QPropertyAnimation object will be destructed at the end of the scope, hence no properties are changed later on.
You most likely wanted to create that object on the heap. Also see the example at: http://doc.qt.digia.com/4.7/qpropertyanimation.html
I have a class derived from QThread: class MyClass : public QThread. In the run method I have "my own" message loop:
run() {
// exec(); // while not reached
while (_runMessageLoop && ...) {
hr = CallDispatch(.....);
if (hr== 0) QThread::msleep(100);
// QCoreApplication::processEvents(); // Does not work
}
}
Since exec() is not executed, I have no Qt event loop. This obviously causes signal / slots not to work correctly. Is there any chance to combine the Qt and my own message loop? Or do I need a frequently firing timer in order to do what I have accomplished in my infinite loop?
The right way "Qt-wise" is to use a timer and let Qt manage the event loop.
If you need to depend on external things, you can use things like QAbstractSocket to send events when data comes in over an external socket, eg.
This is not really the answer for implementing the event loop correctly, I'm fairly sure there is a way, but more of a workaround:
Start the thread normally, exec() and all, and connect the start signal to a slot (make sure it gets called in the right thread), then put your loop there, and call Qt's processEvents() in that loop. That makes sure Qt event loop gets properly set up.