Qt QImageReader won't loop, uncontrollable - qt

really quick problem here
I'm trying to use QImageReader to read the frames of a Gif, but when the animation finishes, it won't loop. I can't even control which frame is loaded, as using either the ImageReader's read() function to return a QImage OR using QPixmap::fromImageReader() both cause the QImageReader to automatically jump to the next frame. The problem is that they both use the same logic where if it is at the end of the animation, return only null instead of resetting.
Here is the way I'm attempting to work with Gifs: My class has a QTimer and a QImageReader. Upon TimeOut of the timer, I call my "nextFrame()" slot.
void GifPlayer::nextFrame()
{
if (img->currentImageNumber() == img->imageCount()-1)
{
img->jumpToImage(0);
}
else
img->jumpToNextImage();
this->lbl->setPixmap(QPixmap::fromImageReader(img));
}
I must be making some fundamental problem here - can someone please help me? I've even updated to the latest version of Qt, and it hasn't helped

QMovie sounds like a better fit for your use case, provided you can use that instead. The following gives the basic idea and will work with a GIF, looping automatically:
...
void MyWindow::init() {
movie = new QMovie("mymovie.gif");
if(!movie->isValid()) {
qFatal("Movie not valid");
}
movie->stop(); // Ensure stopped.
}
void::MyWindow nextFrameSlot() {
this->lbl->setPixmap(movie->currentPixmap());
this->movie->jumpToNextFrame();
}
...

This isn't something I've tried, but the QImageReader class has the following three methods:
int currentImageNumber () const
For image formats that support animation, this function returns the sequence number of the current frame.
int imageCount () const
For image formats that support animation, this function returns the total number of images in the animation.
bool jumpToImage ( int imageNumber )
For image formats that support animation, this function skips to the image whose sequence number is imageNumber.
It seems to me like it would be possible with the first two methods to determine when you've read the last frame of the animation, and then use jumpToImage to jump back to the first frame before the next read().

Related

Qt - start process with click event and stop it again with the second click event

I found time to investigate a bit into QT, and it is very interesting for me. However, right now I am encountering a problem that I am not aware about how to solve it. My aim is actually simple. I have a QCheckBox that I want to activate. If it is activated, I am starting a process (I am opening a file, reading it, taking some values out and change different labels accordingly). This process is repeated until the user is deactivating the QCheckBox. Some small code example to get a better idea of what I am going to do.
void Analyzer::on_actualTemperature_stateChanged(int arg1)
{
// Read data and change labels
if (arg1 != 0)
{
qDebug() << "Start data analysis";
// Infinity loop to get the data and display it
while true
{
// Open file and extract data
const actualTemperature = getData();
// Change any label or do something with the data
ui->anyLabel->setText(actualTemperature);
// Some break
QThread::sleep(1);
// Leave the loop if user deactivate the QCheckBox
// Something like on_actualTemperature_stateChange == 0
}
}
// Stop reading the data
else
{
qDebug() << "Stop data analysis";
}
}
It is obvious that after activating the QCheckBox, the loop will not finish at all and the GUI will not recognize anything anymore. Hence, I guess I have to start some new thread and have to kill it. However, I have no idea how to proceed here. An idea would be:
void Analyzer::on_actualTemperature_stateChanged(int arg1)
{
// Read data and change labels
if (arg1 != 0)
{
// Start reading the file and updating the label using some other thread
startThread(XY);
}
// Stop reading the data
else
{
// Kill thread 1234
killThread(XY);
}
}
Any hint is warmly welcomed and I hope this question is not too basic for you. Thank you for reading, Tobi.
I think killing a running thread is not a decent behavior. Let's be gentle to our threads with a loop control variable. In this example it named keepLoop. Set keepLoop when checkbox checked. Then start thread if it is not running. We are using QtConcurrent::run, and monitoring it by a QFuture in this case.
connect(ui->checkBox, &QCheckBox::toggled,
[&](const bool checked) {
analyzer->keepLoop = checked;
if (checked && !future.isRunning())
future = QtConcurrent::run(analyzer, &Analyzer::on_actualTemperature_stateChanged);
}
);
Don't call user interface slots directly, instead connect them to signals. Connections will be queued connection when signals emitted from another thread. It means slots will be called in event loop of main thread and changes will be shown when the next frame painted.
connect(analyzer, &Analyzer::temperatureCalculated, ui->anyLabel, &QLabel::setText);
Our asynchronous function does not forced to die immediately when user toggle checkbox. Instead we letting it to finish the iteration it already on halfway through.
Analyzer::on_actualTemperature_stateChanged() {
while (keepLoop) {
// Open file and extract data
const QString& actualTemperature = getData();
// send data
emit temperatureCalculated(actualTemperature);
}
}
You can use atomic bool if you want a more precise loop control.
Bonus:
If you don't want to mess with threads, you can avoid GUI freezing by using QTimer to run your loop periodically in main thread.

CefSharp ScreenshotAsync does not always return the correct bitmap

I'm having trouble capturing every frame of an animation using a CefSharp bounded object. It appears the bitmap returned by Browser.ScreenshotAsync is not always correctly synchronized. This is my javascript render function which moves a WebGL object horizontally at a constant rate:
function animate() {
object.position.x -= 100;
renderer.render(scene, camera);
cefBountObject('animate()');
}
This is my CEF bounded object:
public void cefBountObject(string callback) {
Browser.ScreenshotAsync().ContinueWith((task) => {
task.Result.Save(string.Format("{0:D4}.png", frameNumber++));
Browser.EvaluateScriptAsync(callback);
});
}
This does save every file in the sequence without skips: 0000.png, 0001.png, 0002.png, etc. However, some image files are repeats and some image files jump a frame. I think I am encountering an unsynchronized threading issue but I don't see where that might be. In fact, if I put a breakpoint on the last line Browser.EvaluateScriptAsync(callback); and step through the animation, all the files are generated in sequence correctly.

Does the main window only update after a slot subroutine returns?

I don't know if I can explain this problem without posting reams of code, but here goes.
I have a Qt app which is supposed to display a sequence of images. Each image is displayed in the main Qt window by using a QLabel. To display each image, I wrote a subrotuine called DisplayImg(int i) where the integer i is the frame number. It works for displaying single images.
I then created a 'Play' button (in Qt) which is supposed to play a sequence of images. The Play button is connected to a slot which is named on_Play_clicked(). That is a subroutine which does this:
for (i = 0 ; i < N ; i++)
DisplayImg(i);
The problem is that the display only changes for the final image in the sequence. None of the intermediate images are displayed, even though I know (from printf()) that DisplayImg() is called for all intermediate images.
Is that enough information to explain the problem? Am I misunderstanding how the slot works? Does the main Qt window not update until the subroutine on_Play_clicked() returns?
Yes. Qt performs painting only when the control flow is back in the event loop. You should create a QTimer, connect its timeout signal to a slot and start the timer with desired interval in milliseconds. In the slot you should display the next image, e.g. DisplayImg(i); i++;. Current image number (i) should be stored in a class member variable. When the last image is displayed, stop the timer.
The reason that your images don't appear to be changing on the Qt main window is that you don't have a delay between changing each of your images. If you change your code to something like this, you should get the desired effect:
for (i = 0 ; i < N ; i++)
QTimer::singleShot(5000 * i, this, SLOT(DisplayImg(i))); // sleep 5 seconds and then call display image
You will need to declare your DisplayImage method as a slot in your class like so:
yourclass.h
#include <QtCore>
class YourClass : public QObject
{
Q_OBJECT
private slots:
void DisplayImage(int i);
// private members
// public members
};
Just for completeness. Often for a problem like this QCoreApplication::processEvents() is a solution.
So your code would look like:
for (i = 0 ; i < N ; i++) {
QCoreApplication::processEvents();
DisplayImg(i);
}
BUT... The timer solution is clearly superior to the processEvent solution in this particular case. You just have more control over your display.

OpenCV and (not) returning IplImages, when is it okay, when not?

is it okay to do something like this, the code snippet is of course not complete, just to show what I mean:
void draw(IplImage* image){
cvLine(image,cvPoint(20,20),cvPoint(100,100),cvScalar(0,0,255),1);}
int main(){
cvNamedWindow("preview",CV_WINDOW_AUTOSIZE);
IplImage* image;
image=cvCreateImage(cvSize(480,360),8,3);
while(true){
draw(image);
cvShowImage("preview",image);
int ops=cvWaitKey(10)
if ops!=-1 break;}
cvReleaseImage(&image);cvDestroyWindow("preview");return 0;
}
or will it cause problems if I don't return the IplImage like this:
IplImage* draw(IplImage* image){
cvLine(image,cvPoint(20,20),cvPoint(100,100),cvScalar(0,0,255),1);return image;}
well, the reason why I'm asking is that sometimes it works if I don't return the IplImage. However it may also happen that I'll receive some sort of NULL pointer error message in other cases. If for example I release the image in the function and then create it anew right after that, still being in that function, a crash may happen.
You don't need to return anything, but you definitely need to check for failures!
The problem is that you are not coding safely. You are never checking for failures when calling OpenCV functions, which may result in draw() receiving a NULL pointer as parameter, and that might cause a crash or some other weird behavior along the way.
The first thing you should do is start coding defensively:
IplImage* image = cvCreateImage(cvSize(480,360),8,3);
if (!image)
{
// print error and quit
}
and it wouldn't hurt to add a safety check inside your function:
void draw(IplImage* image)
{
if (!image)
{
// print error
return;
}
cvLine(image,cvPoint(20,20),cvPoint(100,100),cvScalar(0,0,255),1);
}

Property binding not updating

I am continuously getting data for my application as it runs, but I am having a bit of trouble displaying the data once I have read it in and stored it in a map.
When I try to display the data in the QML, it simply displays zero, despite the fact that I can see it updating in the application output.
I access the value in QML using property bindings (I was under the impression that these led headingSensor to be updated whenever carData.headingSensor changed?):
property int headingSensor: carData.headingSensor
Text { text: "Heading: " + headingSensor }
In my data class I have:
Q_PROPERTY(int headingSensor READ getHeadingSensor NOTIFY headingSensorChanged)
int headingSensor;
In the c++ implementation I originally had:
int data::getHeadingSensor(){
return data.value(heading)[headingSensorReading];
}
Where it returns the value in the map which is being updated with the incoming information.
This I realized, probably doesn’t work, because the property is dependent upon the headingSensor variable, which is itself not being updated despite the correct value being returned. So, I thought if I changed it to update the headingSensor value and return that it might work.
So in my data aquisition logic I wrote a method to update the variables as well.
data.insert(key, value);
updateVariables();
}
}
}
void data::updateVariables(){
headingSensor = data.value(heading)[headingSensorReading];
}
int data::getHeadingSensor(){
return headingSensor;
}
While this led to the headingSensor variable being updated in addition to the value in the map, the correct value is still not displayed in the QML display. It simply displays 0 (its default value when it is initially displayed since it has not gotten a value from incoming data yet).
So, I am wondering, how can I get the value of sensorHeading displayed in the QML to update as the value of it and/or the value in the map changes in C++? Do I need to do something like:
Connections {
target: carData
onSensorHeadingChanged: updateValues
}
EDIT:
Trying something like this, the onSensorHeadingChanged never fires. I am not sure why, since the value of sensorHeading clearly changes as I watch it in the application output
Connections{
target: carData
onHeadingSensorChanged: console.log("It's noting the change!")
}
It is the responsibility of the C++ element writer to emit headingSensorChanged() in order to cause the binding to be updated.
This tutorial is a good place to start when implementing a C++ element.
In your case you need to do something like this:
void data::updateVariables(){
int sensorReading = data.value(heading)[headingSensorReading];
if (headingSensor != sensorReading) {
headingSensor = sensorReading;
emit headingSensorChanged();
}
}
Note that we don't emit the change notifier unless there really is a change. This prevents needless JS evaluations, and also removes the possibility of binding loops.

Resources