I have time consuming image loading (image is big), also some operations on it are done when loading. I do not want to block application GUI.
My idea is to load image in another thread, emit signal that image is loaded and then redraw view with this image.
My approach:
void Window::loadImage()
{
ImageLoader* loaderThread = new ImageLoader();
connect(loaderThread,SIGNAL(imageLoaded()),this,SLOT(imageLoadingFinished());
loaderThread->loadImage(m_image, m_imagesContainer, m_path);
}
void Window::imageLoadingFinished()
{
m_imagesContainer->addImage(m_image);
redrawView();
}
class ImageLoader : public QThread
{
Q_OBJECT
public:
ImageLoader(QObject *parent = 0) : m_image(NULL), m_container(NULL)
void loadImage(Image* img, Container* cont, std::string path)
{
m_image = img;
m_container = cont;
...
start();
}
signals:
void imageLoaded();
protected:
void run()
{
//loading image and operations on it
emit imageLoaded();
}
protected:
Image* m_image;
Container* m_container;
}
I was basing on quedcustomtype example from Qt writing this code. When googling and searching in stackoverflow I've also find out that subclassing QThread is not a good idea.
So the question is what is the correct way to do it? As I said I want non blocking GUI, loading and operations done in another thread and signal which says loading is finished. After signal is emited view should be redrawn.
I don't know much about multithreading however think to understand or have sufficient knowledge to understand basic ideas.
Use QtConcurent framework.
#include <QtConcurentRun>
#include <QFutureWatcher>
//....
class Window: public QWidget /*or something*/
{
//....
private:
QFutureWatcher<QImage> _wacther; //this object will signal when loading finished
};
//...
void Window::loadImage()
{
connect(&_watcher, SIGNAL(finished(), SLOT(finishLoading());
_wacther.setFuture(QtConcurent::run<QImage>(this, &Window::doLoadImage));
}
QImage Window::doLoadImage() //this function will be executed in the new thread. SHOULD BE Thread Safe
{
return someImage;
}
void window::finishLoading()
{
QImage result = _watcher.result();
}
I suppose this is the best way to go:
#include <QApplication>
#include <QLabel>
#include <QThread>
class ImageLoader : public QObject
{
Q_OBJECT
public:
ImageLoader() : QObject() {
moveToThread(&t);
t.start();
}
~ImageLoader() {
qDebug("Bye bye!");
t.quit();
t.wait();
}
void requestImage(QString absPath) {
QMetaObject::invokeMethod(this, "loadImage", Q_ARG(QString, absPath));
}
public slots:
void loadImage(QString absPath) {
// Simulate large image.
QImage image(absPath);
sleep(10);
qDebug("Image loaded!");
emit imageReady(image);
}
signals:
void imageReady(QImage image);
private:
QThread t;
};
class MyLabel : public QLabel
{
Q_OBJECT
public:
MyLabel() : QLabel() {}
void mousePressEvent(QMouseEvent* ev) {
Q_UNUSED(ev);
qDebug("I got the event!");
}
public slots:
void setImage(QImage image) {
setPixmap(QPixmap::fromImage(image));
resize(image.width(), image.height());
qDebug("Image shown!");
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MyLabel label;
label.show();
ImageLoader imageLoader;
QObject::connect(&imageLoader, SIGNAL(imageReady(QImage)), &label, SLOT(setImage(QImage)));
imageLoader.requestImage(some_abs_path);
return a.exec();
}
#include "main.moc"
I also like QtConcurrent, but consider that its use is somehow discouraged: http://www.mail-archive.com/development#qt-project.org/msg07794.html.
Related
I have problem with sharing pointer to Pixmap via signal-slot mechanism.
Inside slot function I have correct QPixmap with fulled data.
In reciever I have unaccessible QPixmap with empty data.
Any ideas?
class A
{
public:
A:A():pixmap(0){};
void fillPixmap()
{
// correct Pixmap and data isn't null
}
public signals:
void sendQPixmapToB(QPixmap*);
private:
QPixmap *pixmap;
}
class B
{
public:
B:B(){};
public slots:
void recievePixmap(QPixmap* pixmap)
{
// here in debugger pixmap is unaccesible and data is 0
}
}
void onButtonClicked()
{
a.fillPixmap();
}
int main()
{
A a;
B b;
.....
connect(a, SIGNAL(sendQPixmapToB(QPixmap*)),b,SLOT(recievePixmap(QPixmap*)));
return 0;
}
I can not tell you what is wrong with your code because as you indicate you are showing a pseudo-code, so I will show you the correct way to do it.
#include <QGuiApplication>
#include <QPixmap>
#include <QTimer>
#include <QDebug>
class A: public QObject{
Q_OBJECT
public:
using QObject::QObject;
void sendPixmap(){
fillPixmap();
emit sendQPixmapToB(pixmap);
}
signals:
void sendQPixmapToB(const QPixmap & pixmap);
private:
void fillPixmap(){
pixmap = QPixmap(64, 64);
pixmap.fill(Qt::red);
}
QPixmap pixmap;
};
class B: public QObject{
Q_OBJECT
public:
using QObject::QObject;
public slots:
void recievePixmap(const QPixmap & pixmap){
qDebug()<<pixmap;
}
};
int main(int argc, char *argv[])
{
QGuiApplication a(argc, argv);
A obja;
B objb;
QObject::connect(&obja, &A::sendQPixmapToB, &objb, &B::recievePixmap);
QTimer::singleShot(1000, &obja, &A::sendPixmap);
return a.exec();
}
#include "main.moc"ยท
I've got a c++ code including GUI, in which I need to run a time consuming loop for optimization.
class OptimizationAlgorith(data *data);
{
private:
var var1;
var var2;
public:
method1();
method2();
..
timeConsumingMethod(data);
}
this need to be called in a GUI class like following:
class QRegistration: public QWidget
{
Q_OBJECT
private:
data *m_data;
QPushButton *m_button_run;
OptimizationAlgorithm *m_optimizationalgorithm;
WorkerThread *m_workerThread;
QThread *m_thread;
..
private slots:
void on_pushButton_run_clicked();
void registrationDone();
I need to move the timeConsumingMethod into a seperate thread than main thread, so that the GUI does not freez while timeConsumingMethodis running.
I have made a new class "WorkerThread" using the official documentation of Qt, which looks like:
class WorkerThread : public QObject
{
Q_OBJECT
public:
WorkerThread(ApplicationData* data, QOptimizationAlgorithm * OptimizationAlgorithm);
~WorkerThread();
public slots:
void run(data* data);
signals:
void finished();
private slots:
private:
OptimizationAlgorithm *m_OptimizationAlgorithm;
ApplicationData *m_data;
}
How shoud I now implement my run()in WorkerThread? Can I simply write:
void WorkerThread::run(data *m_data)
{
m_optimization.timeConsumingMethod(m_data);
emit finished();
}
or do I have to copy the whole definition of timeConsumingMethod in run()? Why/Why not?
You don't need to do any explicit thread management, Qt already does it for you. Use QtConcurrent::run to do the work in a worker thread from the thread pool.
You should also decouple the controller that manages the work, and the UI. The knowledge of how to couple these objects should be separate from the objects themselves. This allows more flexibility in the design of the UI and the controller, and helps avoid several classes of errors that stem from accessing non-thread-safe methods from incorrect threads.
Complete example:
// https://github.com/KubaO/stackoverflown/tree/master/questions/threadwork-simple-40865259
#include <QtWidgets>
#include <QtConcurrent>
struct ApplicationData {};
struct OptimizationAlgorithm {
void timeConsumingMethod(QSharedPointer<ApplicationData>) {
QThread::sleep(3);
}
};
class Controller : public QObject {
Q_OBJECT
QSharedPointer<ApplicationData> m_data{new ApplicationData};
OptimizationAlgorithm m_algorithm;
public:
Q_SLOT void run() {
QtConcurrent::run([this]{
emit busy();
m_algorithm.timeConsumingMethod(m_data);
emit finished();
});
}
Q_SIGNAL void busy();
Q_SIGNAL void finished();
};
class Registration : public QWidget {
Q_OBJECT
QVBoxLayout m_layout{this};
QLabel m_status{"Idle"};
QPushButton m_run{"Run"};
public:
Registration() {
m_layout.addWidget(&m_status);
m_layout.addWidget(&m_run);
connect(&m_run, &QPushButton::clicked, this, &Registration::reqRun);
}
Q_SIGNAL void reqRun();
Q_SLOT void onBusy() { m_status.setText("Running"); }
Q_SLOT void onFinished() { m_status.setText("Idle"); }
};
void setup(Registration *reg, Controller *ctl) {
using Q = QObject;
Q::connect(reg, &Registration::reqRun, ctl, &Controller::run);
Q::connect(ctl, &Controller::busy, reg, &Registration::onBusy);
Q::connect(ctl, &Controller::finished, reg, &Registration::onFinished);
}
int main(int argc, char ** argv) {
QApplication app{argc, argv};
Controller ctl;
Registration reg;
setup(®, &ctl);
reg.show();
return app.exec();
}
#include "main.moc"
The core of my project is independent of GUI framework that's why I prefer std::thread. But Qt gives me an error when thread is using.
The inferior stopped because it received a signal from the operating system.
Signal name: SIGSEGV
Signal meaning: Segmentation fault
//MainWindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <thread>
#include <mutex>
#include <QMainWindow>
namespace Ui { class MainWindow; }
struct Observer
{
virtual void notify() = 0;
};
class Core
{
public:
std::thread *run()
{
std::thread thread(&Core::runP, this);
thread.detach();
return &thread;
}
void setObserver(Observer *observer) { _observer = observer; }
int ii() const { return _ii; }
void nextIi() { _ii++; }
void lock() { _mutex.lock(); }
bool tryLock() { return _mutex.try_lock(); }
void unlock() { _mutex.unlock(); }
private:
void runP()
{
for (int i = 1; i <= 1000; i++) {
if (i % 10 == 0) {
lock();
nextIi();
unlock();
notify();
}
}
}
void notify() { _observer->notify(); } //!!!
Observer *_observer;
int _ii;
std::mutex _mutex;
};
struct MwObserver : public Observer
{
explicit MwObserver(struct MainWindow *mainWindow) { _mainWindow = mainWindow; }
virtual void notify();
MainWindow *_mainWindow;
};
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow() { delete _ui; }
void upd();
public slots:
void run() { _core.run(); }
private:
Ui::MainWindow *_ui;
MwObserver _observer;
Core _core;
};
inline void MwObserver::notify() { _mainWindow->upd(); }
#endif
-
//MainWindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
_ui(new Ui::MainWindow),
_observer(this)
{
_ui->setupUi(this);
connect(_ui->pushButtonRun, SIGNAL(clicked(bool)), this, SLOT(run()));
}
void MainWindow::upd()
{
_core.lock();
setWindowTitle(QString::number(_core.ii()));
_core.unlock();
}
There are multiple problems here, first and most obvious was already noted by perencia. You are returning a pointer to stack variable. In c++ terms it's unacceptable.
Secondly. The crash comes from not using std::thread, but from race condition. The Qt event loop does not know about you mutex, so your setWindowTitle call is introducing a race, that leads to crash.
You need to use QMetaObject::invokeMethod to post function to the Qts event loop.
Example:
change
inline void MwObserver::notify() { _mainWindow->upd(); }
to
inline void MwObserver::notify() {
if(!QMetaObject::invokeMethod(_mainWindow, "upd", Qt::QueuedConnection))
std::cerr << " Failed to invoke method" << std::endl;
}
additional includes may apply
This updates the GUI from a thread different then the GUI thread! Which is not allowed.
Why not to use QThread and a signal/slot mechanism to update your window title. The Qt framework does the thread switching automatically.
class Core : public QObject
{
Q_OBJECT
public:
explicit Core(QObject * parent = 0) : QObject(parent) {}
signals:
void notify();
public slots:
void nextIi() { _ii++; }
void runP()
{
for (int i = 1; i <= 1000; i++) {
if (i % 10 == 0) {
nextIi();
notify();
}
}
}
private:
Q_DISABLE_COPY(Core);
int _ii;
};
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
public slots:
void run() {_th.start();}
void upd(int ii) {setWindowTitle(QString::number(ii));}
private:
Ui::MainWindow *_ui;
Core _core;
QThread _th;
};
//MainWindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
_ui(new Ui::MainWindow),
_observer(this)
{
_ui->setupUi(this);
connect(_ui->pushButtonRun, SIGNAL(clicked(bool)), this, SLOT(run()));
connect(&_core, SIGNAL(notify(int)), this, SLOT(upd(int)));
_core.moveToThread(&_th);
}
MainWindow::~MainWindow()
{
delete _ui;
_th.quit();
_th.wait(1000);
}
You are creating thread on the stack and returning a pointer to that. After run() that pointer is no longer valid.
Aside from returning pointer to stack variable and updating GUI from thread object that is not known for QT. I don't see from your code, where you set up _observer member of Core class. There is no setObserver call for _core member of MainWindow class.
So consructor of MainWindow class calls consructor of _core member, but after that _core._observer contains garbage. I think this is the cause of your Segmentaion Fault in call of notify method of Core class.
The answers to all the problems have already been given, let me summarize.
The program crash has nothing to do with the threading, The problem is that the _observer in the _core member of MainWindowis not set. A call to setObserver must be added.
explicit MainWindow( QWidget *parent = nullptr ) :
QMainWindow( parent ),
_observer( this )
{
_core.setObserver( &_observer );
}
This will lead to the next problem, that the observer actually calls the udp message from another thread, causing a UI update in a different thread context. To solve this, it is easiest to use Qt's Qt::QueuedConnection. To enable this we must make upt() a slot.
public slots:
void run();
void upd();
Then we can either call it using QMetaObject::invokeMethod in
inline void MwObserver::notify()
{
QMetaObject::invokeMethod( _mainWindow, "upd", Qt::QueuedConnection );
}
or use a signal / slot connection by deriving MwObserver from QObject, giving it a signal, and connect that signal to the upd slot and raising the signal in notify.
struct MwObserver
: public QObject
, public Observer
{
Q_OBJECT;
signals:
void sigUpd();
public:
explicit MwObserver( MainWindow *mainWindow );
virtual void notify()
MainWindow *_mainWindow;
};
void MwObserver::notify()
{
sigUpd();
}
MwObserver::MwObserver( MainWindow *mainWindow )
{
_mainWindow = mainWindow;
connect( this, SIGNAL(sigUpd()), _mainWindow, SLOT(upd()) )
}
Disclaimer: I haven't used Qt in some time but with X/XMotif on Linux/UNIX the GUI MUST run in the 'main-thread', not spawned threads. Maybe this applies to your situation. Just a thought, have your GUI code run in the main-thread.
The best approach is to wrap pure C++ code with QObejct instance and fire signals when this objects receive some notification from pure C++ code.
SO in your case:
class MwObserver : public QObject, public Observer
{
Q_OBJECT
public:
explicit MwObserver(QObject *parent)
: QObject(parent)
{}
signals:
void SomeEvent();
protected:
// Observer
void notify() {
emit SomeEvent();
}
};
Now MainWindow should connect some slot to signal provided this way and everything should work out of the box (Qt will do thread jumping behind the scenes).
In your code form comment the crash is caused by invalid use of temporary object. This is INVALID C++ code no mater what kind of object is returned:
std::thread *run()
{
std::thread thread(&Core::runP, this);
thread.detach();
return &thread;
}
You cant return a pointer to local object of the function method, since this object becomes invalid immediately when you return a function. This is basic C++ knowledge.
I want my application to start maximized because the entire computer will be dedicated to that task, so I found the showMaximized() function that fills that requirement, instead of the standard show(). The problem is that I want my widgets to be ratiometric to the resulting useful size of the app window so that we can change hardware or window managers at some point in the future without touching the app at all.
The best way that I've found on my own to do it is as follows, but it relies on a race condition that usually works but sometimes causes the entire UI to be crammed into the top-left corner. Restarting the app fixes it, but I'd rather not have to tell the non-technical users to do that.
main.h:
#ifndef MAIN_H
#define MAIN_H
#include <QtWidgets>
class ResizeFilter : public QObject
{
Q_OBJECT
public:
ResizeFilter();
protected:
bool eventFilter(QObject* obj, QEvent* event);
signals:
void ResizeEvent(QSize size);
};
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
static MainWindow* GetInstance();
private:
static MainWindow* singleton;
MainWindow();
~MainWindow();
ResizeFilter* filter;
private slots:
void FinishInit(QSize size);
};
#endif // MAIN_H
main.cpp:
#include "main.h"
#include <QApplication>
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
MainWindow::GetInstance();
return app.exec();
}
ResizeFilter::ResizeFilter()
: QObject()
{
}
bool ResizeFilter::eventFilter(QObject* obj, QEvent* event)
{
if(event->type() == QEvent::Resize)
{
QResizeEvent* resizeEv = static_cast<QResizeEvent*>(event);
emit ResizeEvent(resizeEv->size());
}
return QObject::eventFilter(obj, event);
}
MainWindow* MainWindow::singleton = 0;
MainWindow* MainWindow::GetInstance()
{
if(singleton == 0)
{
singleton = new MainWindow();
}
return singleton;
}
MainWindow::MainWindow()
: QMainWindow(0)
{
filter = new ResizeFilter();
installEventFilter(filter);
showMaximized(); //do this before connecting so we miss the first resize event (wrong size) and catch the second (right size)
connect(filter, SIGNAL(ResizeEvent(QSize)), this, SLOT(FinishInit(QSize)));
}
void MainWindow::FinishInit(QSize size)
{
disconnect(filter, SIGNAL(ResizeEvent(QSize)), this, SLOT(FinishInit(QSize)));
delete filter;
filter = 0;
//build widgets based on size argument
}
MainWindow::~MainWindow()
{
}
I'm also open to a less rube-goldberg way of doing it. This looks a bit messy to me, but it's as compact as I think I can get it with my present knowledge.
(The singleton architecture is for another part of the project.)
You can get the geometry and size of the screen using the DesktopWidget.
I am trying to implement Signal and Slot system between the main gui and another object moved to another thread...the following is how the class design looks like...unfortunately cannot implement it...
MainWindow.h
signals:
void StopDisplayWidget();
void StartDisplayWidget();
void signalFromGUI();
private slots:
void on_pushButton_start_display_clicked();
void on_pushButton_stop_display_clicked();
void on_pushButton_check_clicked();
private:
Ui::MainWindow *ui;
displaythread *threadforDisplay;
display *displayWidget;
QThread *WorkerDisplay;
MainWindow.cpp
{
threadforDisplay = new displaythread;
threadforDisplay->setptr2display(displayWidget);
WorkerDisplay = new QThread;
QObject::connect(WorkerDisplay,SIGNAL(started()),threadforDisplay,SLOT(Process()));
QObject::connect(this,SIGNAL(StartDisplayWidget()),threadforDisplay,SLOT(StartDisplay()));
QObject::connect(this,SIGNAL(StopDisplayWidget()),threadforDisplay,SLOT(StopDisplay()));
QObject::connect(this,SIGNAL(signalFromGUI()),threadforDisplay,SLOT(Check()));
threadforDisplay->moveToThread(WorkerDisplay);
}
void MainWindow::on_pushButton_start_display_clicked()
{
if(!threadforDisplay->IsDisplayActive())
emit this->StartDisplayWidget();
if(!WorkerDisplay->isRunning())
WorkerDisplay->start();
}
void MainWindow::on_pushButton_stop_display_clicked()
{
if(threadforDisplay->IsDisplayActive())
{
emit this->StopDisplayWidget();
}
}
void MainWindow::on_pushButton_check_clicked()
{
std::cout<<"CHECKING SIGNAL SLOT"<<std::endl;
emit this->signalFromGUI();
}
threadforDisplay is a pointer to displaythread class which looks like
displaythread.h
#include <QObject>
#include <QWaitCondition>
#include <QMutex>
#include "display.h"
class displaythread : public QObject
{
Q_OBJECT
public:
explicit displaythread(QObject *parent = 0);
bool IsDisplayActive() const;
void setptr2display(display *);
signals:
public slots:
void Process();
void StartDisplay();
void StopDisplay();
void Check();
private:
void SleepThread();
volatile bool stopped,running;
QMutex mutex;
QWaitCondition waitcondition;
display *displayinGUI;
displaythread.cpp
void displaythread::setptr2display(display *ptr)
{
displayinGUI = ptr;
}
void displaythread::Process()
{
std::cout<<"RECEIVED START PROCESS SIGNAL"<<std::endl;
running = true;
while(true)
{
if(!stopped)
{
displayinGUI->update();
this->SleepThread();
}
}
}
void displaythread::SleepThread()
{
mutex.lock();
waitcondition.wait(&mutex,20);
mutex.unlock();
}
void displaythread::StartDisplay()
{
std::cout<<"RECEIVED START SIGNAL"<<std::endl;
stopped = false;
running = true;
}
void displaythread::StopDisplay()
{
std::cout<<"RECEIVED STOP SIGNAL"<<std::endl;
stopped = true;
running = false;
}
bool displaythread::IsDisplayActive() const
{
return running;
}
void displaythread::Check()
{
std::cout<<"SIGNAL FROM GUI RECEIVED"<<std::endl;
}
display.h
class display : public QWidget
{
Q_OBJECT
public:
explicit display(QWidget *parent = 0);
~display();
signals:
public slots:
private:
void paintEvent(QPaintEvent *);
IplImage *image_opencvBGR,*image_opencvRGB;
QImage image;
CvCapture *webcam;
display.cpp
display::display(QWidget *parent) :
QWidget(parent)
{
image_opencvRGB = cvCreateImage(cvSize(640,480),8,3);
webcam = cvCaptureFromCAM(-1);
}
display::~display()
{
cvReleaseCapture(&webcam);
}
void display::paintEvent(QPaintEvent *)
{
//std::cout<<"IN PAINT LOOP"<<std::endl;
image_opencvBGR = cvQueryFrame(webcam);
cvCvtColor(image_opencvBGR,image_opencvRGB,CV_BGR2RGB);
image = QImage((const unsigned char*)image_opencvRGB->imageData,image_opencvRGB->width,image_opencvRGB->height,QImage::Format_RGB888);
QRectF target(0.0,0.0,image.width(),image.height());
QRectF source(0.0,0.0,image.width(),image.height());
QPainter painter(this);
painter.drawImage(target,image,source);
}
OUTPUT :
RECEIVED START PROCESS SIGNAL
However except the Process slot no other slot is working when signals are emitted from the main gui i.e. MainWindow..is it due to movetoThread command? Donno where i am going wrong..
The answer is simple : Qwidgets don't work outside the main thread. So you cannot execute GUI code using displaythread.
Furthermore your while loop may cause issues (I know the variable is volatile but I dont have the time to analyze properly if it is correct)
See the documentation for more information.
ps: It seems you are overdoing things. Rework your whole design. GUI operations are in the main thread. Use threads for computations only. If the communication between thread and access to their variables use signal and slots only, you dont need locking mechanisms.