QQuickWindow grabbing, animation issue
-
I am trying to save an animated QtQuick scene as a sequence of images.
To grab the scene image I use the QQuickWindow::grabWindow method.@
#include <QApplication>
#include <QQuickView>
#include <QTimer>// save window as a sequence of images
void grabWindow(QQuickWindow* window)
{
for(int i = 0; i < 100; i++)
{
QImage image = window->grabWindow();
image.save(QString("%1.png").arg(i));
// TODO: I need update the animation here
}
}int main(int argc, char* argv[])
{
QApplication app(argc, argv);// create invisible QQuickView QQuickView window; window.setResizeMode(QQuickView::SizeViewToRootObject); window.setSource(QUrl::fromLocalFile("qml/main.qml")); window.create(); // start the window grabbing after app.exec() QTimer timer; timer.setSingleShot(true); QObject::connect(&timer, &QTimer::timeout, [&window]() { grabWindow(&window); QApplication::exit(); }); timer.start(0); app.exec();
}
@But how can I update the QQuickWindow animation so that it draw the next frame?
Something like
@
int fps = 25;
float dt = 1000.f / fps;
window->update(dt);
@I understand that it is odd usage of QtQuick, which is developed for interactive applications.
But I hope there is a way to do it, may be with use of private Qt classes.Guru of Qt internals, help please! :)
-
Hello and welcome to qt-project.org,
i'm not sure if that what you want to do will work as expected. Since i know you can not render a QML in a custom amount of frames/second, but you can use QML Timers to animate multiple properties in a sequential way and send a signal to capture a screen after a sucessfull animation/timer-shot.
If you are interested in Timers i can provide you an example.
For further information please read: "Timers":http://qt-project.org/doc/qt-4.8/qml-timer.html -
[quote author="onek24" date="1391769567"]
Since i know you can not render a QML in a custom amount of frames/second, but you can use QML Timers to animate multiple properties in a sequential way and send a signal to capture a screen after a sucessfull animation/timer-shot.
[/quote]
The problem is that I can't modify qml file to add timer to it.
The task is:
There is arbitrary qml file with animation elements: NumberAnimation, Transition, Behaviour, etc. I need convert it to a video (an image sequence, for simplicity) with given frame rate. -
[quote author="o.kh" date="1391774854"]
I need convert it to a video (an image sequence, for simplicity) with given frame rate.
[/quote]QML is not made for that so i don't think that there will be a good way to do this.
-
[quote author="onek24" date="1391777446"]
QML is not made for that
[/quote]
how can you be sure? :) -
[quote author="o.kh" date="1391805404"][quote author="onek24" date="1391777446"]
QML is not made for that
[/quote]
how can you be sure? :)[/quote]bq. QML (Qt Meta Language or Qt Modeling Language[2]) is a JavaScript-based, declarative language for designing user interface–centric applications.
And so it is. If you want to create and record animations i would recommend you to use Adobe AfterEffects for that.
-
It is true that QML is not meant for screenscraping itself in realtime, but it might be possible if the scene is not too complex and the machine is somewhat powerful.
What you can do is to connect to QQuickWindow::afterRendering() using a Qt::DirectConnection and perform a glReadPixels() there. Then you take the pixels you got there and deliver them to a background thread which saves that into a QImage. Saving the image is costly, so you want this done outside both the GUI and Render threads to not interfer with the animations.
glReadPixels() is also a very slow operation, so this might end up being your bottleneck.
QQuickWindow::grabWindow is not suitable for grabbing animations running animations as it interfers halts the GUI thread while rendering.
An alternative method would be to install a tool that can do uncompressed screenscraping at 60FPS and simply grab the window from that tool. That way, the QML application is non the wiser and you get your videostream.
-
Hi, sletta. Thanks for your answer.
Unfortunately, I don't think that your solution is suitable for me.
I do not need screen capturing. I need generation of frames by request, and the animation frame rate should not be equals to the monitor frame rate.
Something like:
@
void saveFrames(QQuickWindow* window)
{
const int frameCount = 100;
const int fps = 25;
float dt = 1000.f / fps;
for(int i = 0; i < frameCount; i++)
{
QImage image = window->grabWindow();
image.save(QString("%1.png").arg(i));
updateAnimation(window, dt); // Can I implement this?
}
}
@ -
There is no way you can access your QML file? I would really recommend to do that with timers and signals. On each timeremit it updates the window, that means the values by using transisions or animations and emits a signal to capture a screenshot. Then you can combine these images to a video.
To sletta's recording method: It would work if you would record it with 60fps+ which means that there is a 100% chance that you capture each necessary frame for your 30fps rate. The monitor frequency should not be a problem because that is only the display ratio of your monitor which doesn't affect the true frames per second. So your graphics card and/or your cpu could render/record with more than your monitors frame rate of by default 60fps(60hz).