I am trying to load multiple QImage objects from files using a threadpool. I have created my own QRunnable subclass to load the image from a file and copy it into a buffer:
class ImageLoader : public QRunnable
{
public:
ImageLoader(const QString &filename, char **buffer, int *size) :
QRunnable(),
filename(filename),
buffer(buffer),
size(size)
{}
// QRunnable interface
void run() {
QImage image(filename);
(*size) = image.byteCount();
(*buffer) = new char[(*size)];
memcpy_s(*buffer), (*size), image.constBits(), image.byteCount());
}
private:
const QString filename;
char **buffer;
int *size;
};
The code works fine if executed on the main thread, but as soon as I run the runnable on a QThreadPool, I get a huge bunch of errors, that basically all say the same:
QObject::moveToThread: Current thread (0x2a023ae6550) is not the object's thread (0x2a023ae65c0).
Cannot move to target thread (0x2a023aca0f0)
The first 2 addresses change each message, I assume they represent the different threads of the pool. Whats interesting:
The first and the second are never the same, however, they are all of the same "group", i.e. the first address of the first error can become the second address of the second error etc...
The third address always stays the same, it's the address of the main (gui) thread.
Any Ideas why that happens or how to fix it? I read the documentation of QImage but wasn't able to find anything about threads in there, except:
Because QImage is a QPaintDevice subclass, QPainter can be used to draw directly onto images. When using QPainter on a QImage, the painting can be performed in another thread than the current GUI thread.
Is solved the problem myself:
The path I passed to the QImage was invalid. I don't know how this was able to produce such an error, but after I fixed the path, it works just fine!
Related
I want to write an tool of objective annotation using qt5 MinGw32, which could annotate object in video file and diplay them during playing. So QGraphicsScene is inherited to implementation the function.
Something wrong happens when I change the QGraphicsScene's background frequently(e.g. 30 fps): most of time it works as expected while sometimes the background could not move.
Here is my code:
void MyGraphicsScene::UpdateFrame(QImage image)
{
QPixmap pixmap = QPixmap::fromImage(image);
//fix the view's size and scene's size
views().first()->setFixedSize(pixmap.size());
setSceneRect(0,0, pixmap.width(), pixmap.height());
setBackgroundBrush(QBrush(pixmap));
}
...
//In another thread
void Thread::run()
{
...
myScene.UpdateFrame(newImage);
...
}
I have search through the qt's document and found no answer.
However, there is something strange:
when wrong thing happens, I find the background continues to change, but it didn't show change on the screen unless I move the app to another screen (I have two screen). However, with the app moved, the QGraphicsScene's background just change once and becomes static afterwards.
I guess the background has been changed but doesn't repainted, so I used update(), but it didn't help.
BTW, I couldn't reproduce the occasion, sometiems it happens, somtimes not.
do I need to represented any methods? Or I called the methods in a wrong way? Or is there an alternative approach that would work?
Many thanks for your help in advance.
You should not change QtGui elements from a different thread by calling a method directly.
Use the Qt signal-slot concept.
Calling update() is not necessary.
class MyGraphicsScene{...
....
signals:
void singalFromThread(QImage image);
public:
//CTor
MyGraphicsScene()
{
connect(this, SIGNAL(singalFromThread(QImage)),this,SLOT(UpdateFrame(QImage)));
}
//Call this method from your thread
void updateForwarder(QImage image)
{
//Jump into gui thread
emit singalFromThread(image);
}
public slots:
void UpdateFrame(QImage image)
{
setBackgroundBrush(....);
}
};
I wrote a C++/QT Application with "Installer" features. Everything workes fine, but when I click outside the window while my programm is in a "copy process", it somehow loses focus and freezes until the copy process is over, then everything is displayed normaly with a QProgressBar value of 100%.
Im copying like this:
void Installer_C::copy(QString aSrcPath, QString aDstPath)
{
//handles
hSrc = CreateFileW(..); //source file
hDst = CreateFileW(..); //destination
//copy
ReadFile(...);
LockFile(...);
WriteFile(...); //->returnes bytesWritten
UnlockFile(...);
updateQProgressBar(bytesWritten); //updates progressbar in my application
CloseHandle(...);
}
This function is called in a foreach loop iterating through a QStringList with files (located in my launchInstall() function).
Due to my problems, I thought about creating Threads for this copy process. Is it more efficient to create a new thread for each Installer_C::copy() call, or to just create one Thread to call the launchInstall() function (I think it wouldn't help much).Or a better Question: Would it even solve my problem that the application freezes? And how shall I do it so that the ProgressBar still will be updated from this thread?
I think, the best way to solve your problem is to create one addition thread to copy process. You can use QThread (Qt documentation: QThread) class to create a thread, which will copy files. The main thread will execute your GUI and it will be available during files copying.
Small example for copying thread:
class CopyThread : public QThread
{
Q_OBJECT
private:
QStringList oldfiles_;
QStringList newfiles_;
public:
CopyThread(const QStringList& oldfiles,
const QStringList& newfiles,
QObject * parent = 0)
: QThread(parent)
, oldfiles_(oldfiles)
, newfiles_(newfiles)
{}
void run() Q_DECL_OVERRIDE
{
int min = qMin(oldfiles_.count(), newFiles.count());
for(int i=0; i<min; ++i)
{
copyFile(oldfiles_.at(i), newFiles_.at(i));
emit signalCopyFile(oldfiles_.at(i), newFiles_.at(i));
}
}
signals:
void signalCopyFile(const QString&, const QString&);
private:
void copyFile(QString aSrcPath, QString aDstPath)
{
QFile::copy(aSrcPath, aDstPath);
}
};
Of course, you must implement slot on your widget for signalCopyFile(const QString&, const QString&) and make connection. Small piece of code (for example) to start copy thread and make connection:
QStringList oldFiles;
oldfiles.append("C:/1.txt");
QStringList newFiles;
newFiles.append("C:/2.txt");
yourProgressBar.setMaximum(oldFiles.count());
yourProgressBar.setMinimum(0);
yourProgressBar.setValue(0);
CopyThread *copyThread = new CopyThread(oldFiles, newFiles, this);
connect(copyThread, &CopyThread::finished, copyThread, &QObject::deleteLater);
connect(copyThread, &CopyThread::signalCopyFile, youWidget, &YouWidget::yourSlot);
copyThread->start();
In yourSlot you can update value of your QProgressBar:
void yourSlot(const QString& oldFile, const QString& newFile)
{
// your actions when one file was copied
yourProgressBar->setValue(yourProgressBar.value() + 1);
}
Everything will be all right without freezes!
As I know there are two possible ways to solve this problem in Qt:
Using QCoreApplication::processEvents(). As docs said it's not a bad solution and suitable for handling long operations. I think it could be usefull for you. For example, you could call it after copying each file (or couple of files).
Using multithreading. It's a great approach to devide GUI threads and logical threads. You could adapt Kirill's solution for your goals.
So my suggestion is: if you need easy and quick working solution, the first way is for you.
If you want to make well-designed and more complex application and you are ready to code little bit more, use the second approach.
//Case 1:
QImage* tImg = new QImage("Some Image Here");
painter->drawImage(x, y, *tImg );
...
delete tImg;
//Case 2:
QImage* tImg = new QImage("Some Image Here");
{
QImage aImg(*tImg);
painter->drawImage(x, y, aImg );
}
...
delete tImg;
I am trying to load several images in a worker thread and draw them out int the main thread. But I am not sure is it ok to delete the image in the worker thread after drawing them out.
//Case 3:
...
//In worker thread
QImage* tImg = new QImage("Some Image Here");
mutex.lock();
matrix.insert(tImg); // matrix is a QList
mutex.unlock();
...
//In main thread
mutex.lock();
foreach(QImage* tImg, matrix)
{
painter->drawImage(x, y, *tImg);
}
mutex.unlock();
...
//In worker thread
mutex.lock();
matrix.remove(tImg);
delete tImg;
mutex.unlock();
Will the above code causing issues? Since the drawImage function is a "pass by const reference". Will this causing any memory issue?
What if delete tImg is on another thread? Will it be safe if I use mutex to make sure the delete tImg only called after painter->drawImage(x, y, *tImg );
The manual memory management is unnecessary. You should leverage Qt to do it for you. You can pass the image through a signal-slot connection, and use the fact that the value will be automatically copied, and any access to it will be automatically synchronized by Qt.
Here's how you could do it, very simply, letting the compiler do all the hard work of resource management for you:
// https://github.com/KubaO/stackoverflown/tree/master/questions/imageloader-36265788
#include <QtWidgets>
#include <QtConcurrent>
First, let's have a class with a signal that is the image source. It has a signal that provides the image, of a const reference type, since any copying will be done automatically by Qt if necessary to cross thread boundaries.
class ImageSource : public QObject {
Q_OBJECT
public:
Q_SIGNAL void hasImage(const QImage & image);
A method generates the image, and emits the signal. As long as automatic connections are used with the hasImage signal, this method can be run in any thread - safely. In our case, we always run this method from the worker thread, but we could run it from the main thread, too - the only difference would be in performance.
/// This method is thread-safe (ignoring the UB of incrementing a shared int)
void generate() {
static auto counter = 0;
QImage img(128, 128, QImage::Format_ARGB32);
img.fill(Qt::white);
QPainter p(&img);
p.drawText(img.rect(), Qt::AlignCenter, QString::number(counter++));
p.end();
emit hasImage(img);
}
};
We'll need an instance of that class, and something to show the image on - say, a QLabel:
int main(int argc, char ** argv) {
QApplication app{argc, argv};
ImageSource source;
QLabel label;
label.show();
We can now connect the hasImage to a functor that sets the label's size and sets the image on it. It then immediately runs the image generator again in a worker thread from the global pool. That's handled automatically by QtConcurrent::run.
The functor runs in the main thread: this is assured by providing the context parameter to the connect: connect(--, --, context, --). The functor runs in label.thread(), just as we wish.
QObject::connect(&source, &ImageSource::hasImage, &label, [&](const QImage & image){
label.setFixedSize(image.size());
label.setPixmap(QPixmap::fromImage(image));
QtConcurrent::run(&source, &ImageSource::generate);
});
Since the connection is automatic, the effect of calling the hasImage signal results in posting of the slot call to the receiving object (label) thread's event queue - here, the queue of the main thread. The event loop picks up the slot call, and executes it. So, even through hasImage has been called in a worker thread, the image is automatically copied and delivered to our functor in the main thread.
Finally, we generate the first image to start the process.
QtConcurrent::run(&source, &ImageSource::generate); // generate the first image
return app.exec();
}
The #include at the end is needed to provide the implementation of the signal hasImage signal, and the metadata describing the ImageSource class. It is generated by moc.
#include "main.moc"
This is complete code, you can paste it into a new project, compile and run; or download the complete project from the github link.
It shows a label that gets its pixmap updated at a rate of approximately 1000/s, on my machine. The application is fully responsive: you can freely move the window, and quit it at any time.
See this answer for another example of a threaded image loader.
I am new to Qt, and today I've been trying to adapt my application to work with QFuture to concurrently run some tasks off the main thread.
I have a method which changes the saturation value of a QImage which returns a QPixmap object to be drawn to my QGraphicsView. This all worked fine not using threads, but obviously it is extremely intensive on the main thread so I wanted to move it off.
I read a few articles regarding Threading in Qt, and found that v5 supports a concurrent run functionality, this sounded perfect for my use case as I thought I would just be able to dispatch my functions onto a new thread as in the snippet below:
void MainWindow::on_slideSaturation_valueChanged(int value)
{
QFuture<QPixmap> future = QtConcurrent::run(slider->modifySaturation, value, image);
future.waitForFinished();
image = future.result();
renderImageToCanvas();
modified = true;
}
However I get the error reference to non-static member function must be called.
This error is being called from my Sliders class, I know I haven't declared the method as static, but when I do it causes a whole heap of errors - is there any way I can pass this method to my concurrent call without it being declared as static?
Sliders.h
class Sliders
{
public:
Sliders();
enum Member { RED, GREEN, BLUE, BRIGHTNESS, CONTRAST, HUE, SATURATION, LIGHTNESS };
QPixmap modifySaturation(int, QPixmap);
void setMember(enum Member, int);
int getMember(enum Member);
private:
int m_redValue;
int m_greenValue;
int m_blueValue;
int m_brightnessValue;
int m_contrastValue;
int m_hueValue;
int m_saturationValue;
int m_lightnessValue;
};
You should call QtConcurrent::run like this:
QFuture<QPixmap> future = QtConcurrent::run(slider,
&Sliders::modifySaturation,
value, image);
But I think you are using it wrong anyway. Your on_slideSaturation_valueChanged slot, which I assume to be executed in the main thread, will be blocked until future.waitForFinished() returns.
I am struggling with memory issues, I think I missed something and would greatly appreciate if someone can point me to what I understand/do wrong.
What I want to do
My gui runs in the main thread. I am launching a computation on a separate thread T. The result of this computation is a bunch of opencv images. I want to display them in my gui during the computation.
How I understand I should do it
Launch computation thread.
When a new image is computed, convert it to a QImage, wrap it in a custom QEvent, and post it to my gui.
Only use heap memory.
How I implemented it
In my computation thread, when a new image is ready :
std::shared_ptr<cv::Mat> cvimRGB = std::shared_ptr<cv::Mat>(new cv::Mat);
cv::Mat cvimBGR;
cv::Mat cvim = MyNewComputedImage;
cvim.convertTo(cvimBGR,CV_8UC3);
cv::cvtColor(cvimBGR,*cvimRGB,cv::COLOR_BGR2RGB);
std::shared_ptr<QImage> qim = std::shared_ptr<QImage>(
new QImage((uint8_t*) cvimRGB->data,cvimRGB->cols,cvimRGB->rows,cvimRGB->step,QImage::Format_RGB888));
ImageAddedEvent* iae = new ImageAddedEvent(qim,i);
QCoreApplication::postEvent(gui, iae);
In my event handler :
bool mosaicage::event(QEvent * e){
if (e->type() == ImageAdded) {
ImageAddedEvent* ie = dynamic_cast<ImageAddedEvent*>(e);
QImage qim(*(ie->newImage));
QPixmap pm(QPixmap::fromImage(qim));
auto p = scene.addPixmap(pm);
images_on_display.push_back(p);
return true;
} else {
return QWidget::event(e);
}
}
My custom event is defined as follow :
class ImageAddedEvent: public QEvent {
public:
ImageAddedEvent();
~ImageAddedEvent();
ImageAddedEvent(std::shared_ptr<QImage> im, int i);
std::shared_ptr<QImage> newImage;
int index;
};
What happens
In debug mode, I get crap on display.
In release mode, I get an access violation error.
I am pretty confident about the part where I convert cv::Mat to qimage because I did not change it, I used to update the display from the computation thread but I learned better. It worked though (when it did not crash).
How I fixed it
The problem was in the memory pointed by the QImage, which was taken charge of by the cv::Mat I constructed it from. If I want to keep this way of constructing the QImage, using data managed by someone else, I must keep the data valid. Hence I moved the cv::Mat to the custom event :
class ImageAddedEvent: public QEvent {
public:
ImageAddedEvent();
~ImageAddedEvent();
ImageAddedEvent(cv::Mat im, int i);
QImage newImage;
cv::Mat cvim;
int index;
};
I changed the constructor of the event to initialize the QImage with the cv::Mat data :
ImageAddedEvent::ImageAddedEvent(cv::Mat cvimRGB, int i) : QEvent(ImageAdded),
index(i),
cvim(cvimRGB)
{
newImage = QImage((uint8_t*) cvim.data,cvim.cols,cvim.rows,cvim.step,QImage::Format_RGB888);
}
And now I only have to pass a cv::Mat to my event constructor :
cv::Mat cvimBGR,cvimRGB;
cv::Mat cvim = MyNewImage;
cvim.convertTo(cvimBGR,CV_8UC3);
cv::cvtColor(cvimBGR,cvimRGB,cv::COLOR_BGR2RGB);
ImageAddedEvent* iae = new ImageAddedEvent(cvimRGB,i);
QCoreApplication::postEvent(gui, iae);
et voilĂ , again, thanks for the help!
you are using the wrong constructor
from the doc(emph mine):
The buffer must remain valid throughout the life of the QImage and all copies that have not been modified or otherwise detached from the original buffer. The image does not delete the buffer at destruction. You can provide a function pointer cleanupFunction along with an extra pointer cleanupInfo that will be called when the last copy is destroyed.
and you are using the stack allocated cvimRGB for the data pointer which (I believe) will clean up the buffer in it's destructor before the event is handled, leading to accessing "crap" data
so you should create a fresh Qimage and then copy the data
std::shared_ptr<cv::Mat> cvimRGB = std::shared_ptr<cv::Mat>(new cv::Mat);
cv::Mat cvimBGR;
cv::Mat cvim = MyNewComputedImage;
cvim.convertTo(cvimBGR,CV_8UC3);
cv::cvtColor(cvimBGR,*cvimRGB,cv::COLOR_BGR2RGB);
QImage qim = QImage(cvimRGB->cols,cvimRGB->rows,QImage::Format_RGB888));
//copy from cvimRGB->data to qim.bits()
ImageAddedEvent* iae = new ImageAddedEvent(qim,i);
QCoreApplication::postEvent(gui, iae);
or detach cvimRGB->data and let the cleanupFunction delete the buffer
on another note there is no need to use std::shared_ptr<QImage> as QImage will not copy the underlying data unless needed, This is known in Qt as implicit data sharing
to call the gui you can provide a Q_INVOKABLE method (or just a slot) in gui and use QMetaObject::invokeMethod(gui, "imageUpdated", Q_ARG(QImage, qim));