I'm trying to get large data from a database but when running the main window freezes.
I am working under windows and according to this link Windows automatically set the program to a hanging state state after 5 seconds.
Is there a way to prevent freezing?
Here is the code:
void MainWindow::on_getDataButtonClicked()
{
ui->centralWidget->setEnabled(false);
QApplication::setOverrideCursor(Qt::WaitCursor);
try
{
Client client(user, password);
std::future<map<string, map<string, string> > > fut =
std::async(std::launch::async, &Client::get_data, &client);
// While not all data has been retrieved, set message to the status bar.
while (fut.wait_for(std::chrono::seconds(0)) != std::future_status::ready)
{
ui->statusBar->showMessage("Getting data.");
std::this_thread::sleep_for(std::chrono::milliseconds(500));
ui->statusBar->showMessage("Getting data..");
std::this_thread::sleep_for(std::chrono::milliseconds(500));
ui->statusBar->showMessage("Getting data...");
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
map<string, map<string, string> > exported_strings = std::move(fut.get());
ui->statusBar->showMessage("%All data has been retrieved!");
}
catch (std::string& s)
{
QMessageBox::critical(this, "Error", QString::fromStdString(s));
}
catch (std::exception& e)
{
QMessageBox::critical(this, "Error", QString(e.what()));
}
catch (...)
{
QMessageBox::critical(this, "Error", "An unknown error has occurred.");
}
ui->centralWidget->setEnabled(true);
QApplication::restoreOverrideCursor();
}
On a side note, the main window does not freezes when debugging.
There's no point to doing asynchronous work if you're waiting for it and blocking the GUI thread anyway in the while loop. You need to get rid of the while loop.
You could use QtConcurrent::run instead of std::async, and use QFutureWatcher to get notified asynchronously, without blocking, when the async task has finished.
// https://github.com/KubaO/stackoverflown/tree/master/questions/async-sane-39396761
#include <QtWidgets>
#include <QtConcurrent>
#include <map>
#include <string>
struct Client {
using result_type = std::map<std::string, std::map<std::string, std::string>>;
result_type get_data() {
QThread::sleep(5); // pretend to do some work
return result_type();
}
};
class MainWindow : public QMainWindow {
Q_OBJECT
Client::result_type exported_strings;
QWidget centralWidget;
QVBoxLayout layout{¢ralWidget};
QPushButton getDataButton{"Get Data"};
QStatusBar statusBar;
QTimer statusTimer;
QString statusMessage;
void setBusyStatus(const QString & status) {
centralWidget.setEnabled(false);
QApplication::setOverrideCursor(Qt::WaitCursor);
statusMessage = status;
statusTimer.start(0);
}
void setNormalStatus(const QString & status) {
centralWidget.setEnabled(true);
QApplication::restoreOverrideCursor();
statusBar.showMessage(status);
statusTimer.stop();
}
Q_SLOT void on_getDataButtonClicked();
public:
MainWindow() {
setStatusBar(&statusBar);
setCentralWidget(¢ralWidget);
layout.addWidget(&getDataButton);
int n = 0;
connect(&statusTimer, &QTimer::timeout, [=]() mutable {
statusBar.showMessage(QStringLiteral("%1%2").arg(statusMessage).arg(QString{n+1, QChar{'.'}}));
n = (n+1)%3;
statusTimer.start(500);
});
connect(&getDataButton, &QPushButton::clicked, this, &MainWindow::on_getDataButtonClicked);
}
};
void MainWindow::on_getDataButtonClicked()
{
auto future = QtConcurrent::run([=]{
Client client;
return client.get_data();
});
auto watcher = new QFutureWatcher<Client::result_type>{this};
connect(watcher, &QFutureWatcher<Client::result_type>::finished, this, [=]{
exported_strings = std::move(watcher->result());
watcher->deleteLater();
setNormalStatus("All data has been retrieved!");
});
watcher->setFuture(future);
setBusyStatus("Getting data");
}
int main(int argc, char ** argv) {
QApplication app{argc, argv};
MainWindow w;
w.show();
return app.exec();
}
#include "main.moc"
Alternatively, you could emit a signal from the async code, letting you retain the use of std::async if you prefer that:
#include <QtWidgets>
#include <future>
#include <map>
#include <string>
struct Client {
using result_type = std::map<std::string, std::map<std::string, std::string>>;
result_type get_data() {
QThread::sleep(5); // pretend to do some work
return result_type();
}
};
class MainWindow : public QMainWindow {
Q_OBJECT
Client::result_type exported_strings;
QWidget centralWidget;
QVBoxLayout layout{¢ralWidget};
QPushButton getDataButton{"Get Data"};
QStatusBar statusBar;
QTimer statusTimer;
QString statusMessage;
std::future<Client::result_type> resultFuture;
void setBusyStatus(const QString & status) {
centralWidget.setEnabled(false);
QApplication::setOverrideCursor(Qt::WaitCursor);
statusMessage = status;
statusTimer.start(0);
}
void setNormalStatus(const QString & status) {
centralWidget.setEnabled(true);
QApplication::restoreOverrideCursor();
statusBar.showMessage(status);
statusTimer.stop();
}
Q_SLOT void on_getDataButtonClicked();
Q_SIGNAL void hasResult();
public:
MainWindow() {
setStatusBar(&statusBar);
setCentralWidget(¢ralWidget);
layout.addWidget(&getDataButton);
int n = 0;
connect(&statusTimer, &QTimer::timeout, [=]() mutable {
statusBar.showMessage(QStringLiteral("%1%2").arg(statusMessage).arg(QString{n+1, QChar{'.'}}));
n = (n+1)%3;
statusTimer.start(500);
});
connect(&getDataButton, &QPushButton::clicked, this, &MainWindow::on_getDataButtonClicked);
}
};
void MainWindow::on_getDataButtonClicked()
{
connect(this, &MainWindow::hasResult, this, [this](){
exported_strings = std::move(resultFuture.get());
setNormalStatus("All data has been retrieved!");
}, Qt::UniqueConnection);
resultFuture = std::async(std::launch::async, [this]{
Client client;
auto result = client.get_data();
emit hasResult();
return result;
});
setBusyStatus("Getting data");
}
int main(int argc, char ** argv) {
QApplication app{argc, argv};
MainWindow w;
w.show();
return app.exec();
}
#include "main.moc"
If everything is happening on the same thread and some operation is slow, then you won't get back to the event-loop in time for the OS to not declare you as stuck.
The solution(s) are to either;
1) use multiple processes.
2) use multiple threads.
3) divide the work on your one thread into chunks that are all guaranteed to be small enough that you can get back to servicing the eventloop in a timely fashion.
To avoid multithreading, here's what you should do:
Cut your code (virtually, just in thought) into chunks
Run your program and study these chunks, and see which of them causes the freezing (takes longest to execute)
Once you know what part causes the freezing, use QApplication::processEvents(); to force the MainWindow to become responsive.
Example: Let me illustrate with an example. Say you have this function
void MainWindow::on_getDataButtonClicked()
{
//do some easy stuff
for(long i = 0; i < 100000; i++)
{
//do some stuff
//do some other stuff
}
//do some different stuff
}
Now while executing this nasty function, your window will definitely freeze until the whole function is finished. The "easy stuff" is not the problem, because it's fast. But the big loop is the problem. So all you have to do is tell Qt to reprocess the events that get the main window to become responsive again; Like this:
void MainWindow::on_getDataButtonClicked()
{
//do some easy stuff
for(long i = 0; i < 100000; i++)
{
QApplication::processEvents();
//do some stuff
//do some other stuff
}
QApplication::processEvents();
//do some different stuff
}
How many times should you call processEvents();? Call it whenever you want the window to respond again. If in doubt, just put processEvents() like everywhere! Infest every line with that. It's not the best practice, but then you can remove them one by one and see where your program starts freezing again.
One additional piece of advice: Don't throw std::string exceptions. This is a very bad idea. Learn how to throw classes inherited from std::exception. You can still hold your string inside the std::exception object. For example, you can do: throw std::exception("This is very very bad");. There are many other classes you can throw. Find them here.
Related
I'm junior programmer
recently, I have implemented grabbing of Image using Halcon library.
when I press live button, Timer start to grab image. it works but main screen freezes to the timer cycle.
so, I am improving performance grabbing of Image using Thread
first I implemented thread like this
[ImageUpdateWorker.h]
class ImageUpdateWorker : public QObject
{
Q_OBJECT
public:
explicit ImageUpdateWorker(QObject* parent = 0, QString strThreadName = "ImageUpdateWorker");
~ImageUpdateWorker();
signals:
void finished();
void grab();
public slots:
void run();
private:
bool m_bStop{ false };
};
[ImageUpdateWorker.cpp]
ImageUpdateWorker::ImageUpdateWorker(QObject* parent, QString strThreadName)
: QObject(parent)
{
setObjectName(strThreadName);
}
ImageUpdateWorker::~ImageUpdateWorker()
{
}
void ImageUpdateWorker::run()
{
while (m_bStop == false)
{
emit grab();
}
emit finished();
}
second I implemented inherited QWidget UI Widget with output Screen like this
m_pThread = new QThread();
m_pUpdateWorker = new ImageUpdateWorker(nullptr, strName);
m_pUpdateWorker->moveToThread(m_pThread); // UpdateWorker move to Thread
connect(m_pThread, SIGNAL(started()), m_pUpdateWorker, SLOT(run()));
connect(m_pThread, SIGNAL(finished()), m_pThread, SLOT(deleteLater()));
connect(m_pUpdateWorker, SIGNAL(finished()), m_pThread, SLOT(quit()));
connect(m_pUpdateWorker, SIGNAL(finished()), m_pUpdateWorker, SLOT(deleteLater()));
connect(m_pUpdateWorker, SIGNAL(grab()), this, SLOT(onGrab()));
when I call "m_pThread->start();" screen starts to blokcing :(
If you have any advice or information, I would appreciate it. thank you for reading.
Use m_pImageUpdateThread->moveToThread(m_pThread);
I don't know in QT.
I sent you the code I used in C#.
Mainly you must use the delegates if you don't want to freeze the GUI.
hdisplay is the object HalconDotNet:HWindowControlWPF.
camera is a class where I define the camera parameters.
inside camera.Grab there is the code:
HOperatorSet.GrabImage(out ho_Image, _AcqHandle);
h_Image = new HImage(ho_Image);
At the initialization there is the code:
// Initialise the delegate
updateLiveDelegate = new UpdateLiveDelegate(this.UpdateLive);
HImage ho_Image = new HImage();
Here the code I use:
// ==================
// LIVE
// ==================
bool stopLive = true;
// Declare the thread
private Thread liveThread = null;
// Declare a delegate used to communicate with the UI thread
private delegate void UpdateLiveDelegate();
private UpdateLiveDelegate updateLiveDelegate = null;
private void btnLive_Click(object sender, RoutedEventArgs e)
{
try
{
stopLive = !stopLive;
// if stopLive = false, live camera is activated
if (!stopLive)
{
// Launch the thread
liveThread = new Thread(new ThreadStart(Live));
liveThread.Start();
}
}
catch (Exception ex)
{
// Error
}
}
private void Live()
{
try
{
while (stopLive == false)
{
if (camera.Grab(out ho_Image))
{
// Show progress
Dispatcher.Invoke(this.updateLiveDelegate);
}
else
{
// No grab
stopLive = true;
}
}
// here stopLive is true
}
catch (Exception ex)
{
// Error
}
}
private void UpdateLive()
{
try
{
int imageHeight;
int imageWidth;
string imageType;
ho_Image.GetImagePointer1(out imageType, out imageWidth, out imageHeight);
hDisplay.HalconWindow.SetPart(0, 0, imageHeight - 1, imageWidth - 1);
// display
hDisplay.HalconWindow.DispImage(ho_Image);
}
catch (Exception ex)
{
// Error
}
}
I have the following method in one of my C++ classes (using QtWebEngine):
QString get()
{
QString result;
view->page()->runJavaScript("test();", [this](const QVariant &v)
{
result = v.toString();
});
return result;
}
It is to execute test() JS function and return the result of this invocation.
Unfortunately, the callback is asynchronous and the program crashes. How can I make it work?
The callback is asynchronous because the JavaScript execution occurs not only in another thread but in another process. So there is no way to make it fully synchronous.
The best possible solution would be to migrate your C++ code to work asynchronously. If you can't do that, the only feasible solution is to use QEventLoop, somewhat like this:
void ranJavaScript()
{
emit notifyRanJavaScript();
}
QString get()
{
QString result;
QEventLoop loop;
QObject::connect(this, SIGNAL(notifyRanJavaScript()), &loop, SLOT(quit()));
view->page()->runJavaScript("test();", [this](const QVariant &v)
{
result = v.toString();
this.ranJavaScript();
});
loop.exec();
return result;
}
However, note that this example is oversimplified for a real-world usage: you need to ensure the JavaScript was not ran before the event loop is started. The most proper way to do that would involve implementing a proper slot instead of a lambda + factoring out the call to view->page()->runJavaScript() into another slot which would be called asynchronously after starting the event loop. It is a lot of glue code for such a seemingly simple task but that's what it takes. Here's an example:
MainWindow.h
#ifndef TMP_MAIN_WINDOW_H
#define TMP_MAIN_WINDOW_H
#include <QMainWindow>
#include <QVariant>
class QWebEngineView;
class QPushButton;
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget * parent = 0);
QString get();
void onScriptEnded(const QVariant & data);
Q_SIGNALS:
void notifyRanJavaScript();
private Q_SLOTS:
void onButtonPressed();
void startScript();
private:
QWebEngineView * m_view;
QPushButton * m_button;
QString m_scriptResult;
};
#endif // TMP_MAIN_WINDOW_H
MainWindow.cpp
#include "MainWindow.h"
#include <QWebEngineView>
#include <QPushButton>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QMessageBox>
#include <QEventLoop>
#include <QDebug>
#include <QTimer>
MainWindow::MainWindow(QWidget * parent) :
QMainWindow(parent)
{
m_view = new QWebEngineView;
QWebEnginePage * page = new QWebEnginePage(m_view);
m_view->setPage(page);
QString html = QStringLiteral("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\""
"\"http://www.w3.org/TR/html4/strict.dtd\"><html>"
"<head><h3>head</h3>\n</head>"
"<script type=\"text/javascript\">function test() { return \"A!\"; }</script>"
"<body>text\n</body></html>");
m_view->page()->setHtml(html);
m_button = new QPushButton;
m_button->setMinimumWidth(35);
m_button->setText(QStringLiteral("Test"));
QObject::connect(m_button, SIGNAL(pressed()), this, SLOT(onButtonPressed()));
QHBoxLayout * buttonLayout = new QHBoxLayout;
buttonLayout->addWidget(m_button);
buttonLayout->addStretch();
QVBoxLayout * viewLayout = new QVBoxLayout;
viewLayout->addLayout(buttonLayout);
viewLayout->addWidget(m_view);
QWidget * widget = new QWidget(this);
widget->setLayout(viewLayout);
setCentralWidget(widget);
}
QString MainWindow::get()
{
QEventLoop loop;
QObject::connect(this, SIGNAL(notifyRanJavaScript()), &loop, SLOT(quit()));
// Schedule the slot to run in 0 seconds but not right now
QTimer::singleShot(0, this, SLOT(startScript()));
// The event loop would block until onScriptEnded slot is executed
loop.exec();
// If we got here, the script has been executed and the result was saved in m_scriptResult
return m_scriptResult;
}
void MainWindow::onScriptEnded(const QVariant & data)
{
qDebug() << QStringLiteral("Script ended: ") << data;
m_scriptResult = data.toString();
emit notifyRanJavaScript();
}
void MainWindow::onButtonPressed()
{
QString str = get();
QMessageBox::information(this, QStringLiteral("Script result"), str,
QMessageBox::StandardButton::Ok);
}
struct Functor
{
Functor(MainWindow & window) : m_window(window) {}
void operator()(const QVariant & data)
{
m_window.onScriptEnded(data);
}
MainWindow & m_window;
};
void MainWindow::startScript()
{
qDebug() << QStringLiteral("Start script");
m_view->page()->runJavaScript(QStringLiteral("test();"), Functor(*this));
}
Dmitry's solution only works partially, unfortunately. In his example the code is invoked upon pressing a button. However, if the same code is moved to execute while the window is loading, the slot for the finished script (onScriptEnded) is never called. To fix this, I had to wrap the call to the evaluation of JS itself (called evaluateJavaScript in my project) in another QTimer::singleShot:
QTimer::singleShot(0, this, [&] { evaluateJavaScript(); });
Unfortunately, in my real project I have many calls, often one evaluation waiting for another before it to finish. It's practically impossible to use this every single time so I still look for a solution.
In the kchmviewer project, I found a very easy way to wait for the result of the runJavaScript function. The following code is part of the ViewWindow class which inherits from QWebEngineView.
int ViewWindow::getScrollbarPosition()
{
QAtomicInt value = -1;
page()->runJavaScript("document.body.scrollTop", [&value](const QVariant &v)
{
qDebug( "value retrieved: %d\n", v.toInt());
value = v.toInt();
});
while (value == -1)
{
QApplication::processEvents();
}
qDebug( "scroll value %d", value.load() );
return value;
}
I tried to create QProgressBar according to Manual. Yet it works really bad (for instance, if I create QProgressDialog in the constructor, it will appear as soon as app is running, so I decided to use QProgressBar). But there is a problem:
Although I used advices from the internet. My code:
UPD![2]
// StudentAbsenceTableApp.h
using Job = std::function<void ()>;
Q_DECLARE_METATYPE(Job)
class StudentAbsenceTableApp{
public:
StudentAbsenceTableApp(QWidget *parent = 0);
private:
Q_SIGNAL void reqLoadFile(const QString& fileName);
Q_SIGNAL void reqSaveFile(const QString& fileName);
Q_SIGNAL void reqGui(const Job&);
bool documentModified;
QProgressBar *progressBar;
};
// StudentAbsenceTableApp.cpp
StudentAbsenceTableApp::StudentAbsenceTableApp(QWidget *parent)
: QMainWindow(parent)
{
// ...
setStatusBar(new QStatusBar(this));
qRegisterMetaType<Job>();
progressBar = new QProgressBar(statusBar());
progressBar->setMinimum(0);
progressBar->setMaximum(0);
progressBar->setMaximumWidth(150);
progressBar->hide();
statusBar()->addPermanentWidget(progressBar);
connect(this, &StudentAbsenceTableApp::reqLoadFile, this, [this] (const QString& fileName){
QtConcurrent::run(this, &StudentAbsenceTableApp::loadFile, fileName);
});
connect(this, &StudentAbsenceTableApp::reqGui, [this](const Job & job){
job();
});
}
// funtion that emit reqLoadFile(fileName)
bool StudentAbsenceTableApp::loadFile(const QString& fileName)
{
reqGui([=] () { progressBar->show(); });
auto xmlParser = XMLParser(model);
try
{
reqGui([&] () {
xmlParser.read(fileName);
setCurrentFileName(fileName);
statusBar()->showMessage(tr("Файл загружен"), 2000);
documentModified = false;
});
}
catch(FileOpenException)
{
reqGui([=] () {
QMessageBox::warning(this, "Ошибка!", "Ошибка открытия файла!", QMessageBox::Ok);
statusBar()->showMessage(tr("Загрузка отменена"), 2000);
});
return false;
}
catch(FileReadException)
{
reqGui([=] () {
QMessageBox::warning(this, "Ошибка!", "Ошибка чтения файла!", QMessageBox::Ok);
statusBar()->showMessage(tr("Загрузка отменена"), 2000);
});
return false;
}
reqGui([=] () { progressBar->hide(); });
return true;
}
I don't know how to write code, that is possible to compile, because there is a lot of code.
No QWidget (and derived classes) methods provided by Qt are thread-safe. Thus you can't access QProgressBar nor any other widgets from any thread other then the GUI thread.
The experimentFunction runs in a non-GUI thread and thus must not access widgets. You must figure out some other means of communication, e.g. using signals and slots. Recall that you're free to emit signals in experimentFunction, since the signal implementations are by contract thread-safe.
It's all really simple, and you don't need the future watcher. In your attempts to "fix" the issue, you've hopelessly combobulated your code.
For other ways of invoking methods safely across threads, see this question and that question.
// https://github.com/KubaO/stackoverflown/tree/master/questions/thread-progress-future-44445248
#include <QtConcurrent>
#include <QtWidgets>
#include <exception>
#include <functional>
struct FileOpenException : std::exception {};
struct FileReadException : std::exception {};
struct Model {};
struct XMLParser {
XMLParser(Model &) {}
void read(const QString &) {
static int outcome;
QThread::sleep(3);
switch (outcome++ % 3) {
case 0: return;
case 1: throw FileOpenException();
case 2: throw FileReadException();
}
}
};
using Job = std::function<void()>;
Q_DECLARE_METATYPE(Job)
class StudentAbsenceTable : public QMainWindow {
Q_OBJECT
QStatusBar m_statusBar;
QProgressBar m_progress;
QPushButton m_start{"Start Concurrent Task"};
Model m_model;
bool m_documentModified = {};
public:
StudentAbsenceTable() {
qRegisterMetaType<Job>();
m_statusBar.addPermanentWidget(&m_progress);
m_progress.setMinimum(0);
m_progress.setMaximum(0);
m_progress.setMaximumWidth(150);
m_progress.hide();
setStatusBar(&m_statusBar);
setCentralWidget(&m_start);
connect(&m_start, &QPushButton::clicked, this, [this]{
m_start.setEnabled(false);
QtConcurrent::run(this, &StudentAbsenceTable::loadFile);
});
connect(this, &StudentAbsenceTable::reqGui, this, [this](const Job & job){
job();
});
}
private:
bool loadFile() {
reqGui([=]{ m_progress.show(); });
auto fileName = QStringLiteral("/media/bsuir/data.xml");
auto xmlParser = XMLParser(m_model);
try {
xmlParser.read(fileName);
reqGui([=]{
setCurrentFileName(fileName);
statusBar()->showMessage(tr("Файл загружен"), 2000);
m_documentModified = false;
});
}
catch(FileOpenException&) {
reqGui([=]{
QMessageBox::warning(this, "Ошибка!", "Ошибка открытия файла!", QMessageBox::Ok);
statusBar()->showMessage(tr("Загрузка отменена"), 2000);
});
}
catch(FileReadException&) {
reqGui([=]{
QMessageBox::warning(this, "Ошибка!", "Ошибка чтения файла!", QMessageBox::Ok);
statusBar()->showMessage(tr("Загрузка отменена"), 2000);
});
}
reqGui([=]{ m_progress.hide(); m_start.setEnabled(true); });
return false;
}
Q_SIGNAL void reqGui(const Job &);
void setCurrentFileName(const QString &) {}
};
int main(int argc, char ** argv) {
QApplication app(argc, argv);
StudentAbsenceTable ui;
ui.setMinimumSize(350, 350);
ui.show();
return app.exec();
}
#include "main.moc"
I am trying to develop a simple Qt application.
After I press a "START" button, the application should continuosly retrieves data from a device (using third party libraries) and forward them as soon as possible on a serial connection.
The (ugly) software I used till now was a console application that ran in a sequential way and got data frame as soon as they are made available by the host, using the following cycle:
while(1)
{
[...]
while( MyClient.GetFrame().Result != Result::Success )
{
Sleep( 200 );
std::cout << ".";
}
[... pack and send on serial]
}
I was wondering which is the more convinient way to implement this in Qt, in order to keep the GUI responsive but also with the minimum possible latency between getFrame and the serial write function.
Should I use a timer triggered SLOT? QtConcurrent namespace? QRunnable?
Which are the main advantages and disadvantages of each of these approaches?
Thanks
for your help!
Since the existing library forces you to poll for data, the only thing to do is to run it on a timer. It's your choice as to if the object that does this job will run in the main thread, or in a worker thread. There's no need to use Qt Concurrent nor QRunnable - using a QObject makes life somewhat simpler since you can easily provide feedback to the GUI.
For example, making some assumptions about your client's API:
class Worker : public QObject {
Client m_client;
QSerialPort m_port;
QBasicTimer m_timer;
void processFrame() {
if (m_client.GetFrame().Result != Result::Success) return;
QByteArray frame = QByteArray::fromRawData(
m_client.GetFrame().Data, m_client.GetFrame().Size);
... process the frame
if (m_port.write(frame) != frame.size()) {
... process the error
}
}
void timerEvent(QTimerEvent * ev) {
if (ev->timerId() == m_timer.timerId()) processFrame();
}
public:
Worker(QObject * parent = 0) : QObject(parent) {}
Q_SLOT bool open(const QString & name) {
m_port.close();
m_port.setPortName(name);
if (!m_port.open(name)) return false;
if (!m_port.setBaudRate(9600)) return false;
if (!m_port.setDataBits(QSerialPort::Data8)) return false;
... other settings go here
return true;
}
Q_SLOT void start() { m_timer.start(200, this); }
Q_SLOT void stop() { m_timer.stop(); }
...
}
/// A thread that's always safe to destruct
class Thread : public QThread {
using QThread::run; // lock the default implementation
public:
Thread(QObject * parent = 0) : QThread(parent) {}
~Thread() { quit(); wait(); }
};
int main(int argc, char ** argv) {
QApplication app(argc, argv);
Worker worker;
Thread thread; // worker must be declared before thread!
if (true) {
// this code is optional, disabling it should not change things
// too much unless the client implementation blocks too much
thread.start();
worker.moveToThread(&thread);
}
QPushButton button("Start");
QObject::connect(&button, &QPushButton::clicked, [&worker]{
// Those are cross-thread calls, they can't be done directly
QMetaObject::invoke(&worker, "open", Q_ARG(QString, "COM1");
QMetaObject::invoke(&worker, "start");
});
button.show();
return app.exec(argc, argv);
}
QT 5.1.0rc2 , msvc 2010 , Microsoft Visual Studio 2010
It is working code on Qt 4.8.4 , msvc 2008
I have compile error at
#if defined( HANDLE_PROCESSING_IN_MAIN_THREAD )
if(QThread::currentThread() != this)
emit started();
#endif
inherited::run();
and
#if defined( HANDLE_PROCESSING_IN_MAIN_THREAD )
if(QThread::currentThread() != this)
emit finished();
#endif
error C2660: 'QThread::started' : function does not take 0 arguments
error C2660: 'QThread::finished' : function does not take 0 arguments
In QThread i have seen
Q_SIGNALS:
void started(
#if !defined(Q_QDOC)
QPrivateSignal
#endif
);
void finished(
#if !defined(Q_QDOC)
QPrivateSignal
#endif
);
when I defined Q_QDOC I got many errors in QT sources.
QPrivateSignal is empty structure that defined in macro Q_OBJECT
Need a solution that does not affect the architecture of the application, as to be backward compatible with Qt4.8.4
Some ideas?
The thread's signals are emitted automatically. You should never emit them manually.
You're trying to use preprocessor to handle two variants of the code: processing in the gui thread or a dedicated thread. Qt provides a very easy way of dealing with it.
Implement your processing functionality in a slot in a class deriving from QObject. You could also do it in the reimplemented event() method if it's easier for you to start processing by posting an event rather than invoking a slot.
If you want your object's slots to run in a different thread, use the moveToThread() method.
You don't need to derive from QThread. Its default implementation of run() spins a local event loop.
If you want your object to be compatible with living in the GUI thread, it must do its processing in small chunks and relinquish control so that the GUI doesn't stall.
Below is a complete example that demonstrates how you can start the worker object in either GUI or a separate thread, and how you can safely move it between threads.
// https://github.com/KubaO/stackoverflown/tree/master/questions/qobject-thread-18653347
#include <QtGui>
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
#include <QtWidgets>
#endif
/// See http://stackoverflow.com/a/40382821/1329652
bool isSafe(QObject * obj) {
Q_ASSERT(obj->thread() || qApp && qApp->thread() == QThread::currentThread());
auto thread = obj->thread() ? obj->thread() : qApp->thread();
return thread == QThread::currentThread();
}
class Helper : private QThread {
public:
using QThread::usleep;
};
class Worker : public QObject {
Q_OBJECT
int m_counter;
QBasicTimer m_timer;
void timerEvent(QTimerEvent * ev) override;
public:
Worker(QObject *parent = nullptr) : QObject(parent) {}
/// This method is thread-safe.
Q_SLOT void start() {
if (!isSafe(this)) return (void)QMetaObject::invokeMethod(this, "start");
if (m_timer.isActive()) return;
m_counter = 0;
m_timer.start(0, this);
}
/// This method is thread-safe.
Q_INVOKABLE void startInThread(QObject *targetThread) {
if (!isSafe(this)) return (void)QMetaObject::invokeMethod(this, "startInThread", Q_ARG(QObject*, targetThread));
QObject::moveToThread(qobject_cast<QThread*>(targetThread));
start();
}
Q_SIGNAL void done();
Q_SIGNAL void progress(int percent, bool inGuiThread);
};
void Worker::timerEvent(QTimerEvent * ev)
{
const int busyTime = 50; // [ms] - longest amount of time to stay busy
const int testFactor = 128; // number of iterations between time tests
const int maxCounter = 30000;
if (ev->timerId() != m_timer.timerId()) return;
const auto inGuiThread = []{ return QThread::currentThread() == qApp->thread(); };
QElapsedTimer t;
t.start();
while (1) {
// do some "work"
Helper::usleep(100);
m_counter ++;
// exit when the work is done
if (m_counter > maxCounter) {
emit progress(100, inGuiThread());
emit done();
m_timer.stop();
break;
}
// exit when we're done with a timed "chunk" of work
// Note: QElapsedTimer::elapsed() may be expensive, so we call it once every testFactor iterations
if ((m_counter % testFactor) == 0 && t.elapsed() > busyTime) {
emit progress(m_counter*100/maxCounter, inGuiThread());
break;
}
}
}
class Window : public QWidget {
Q_OBJECT
QVBoxLayout m_layout{this};
QPushButton m_startGUI{"Start in GUI Thread"};
QPushButton m_startWorker{"Start in Worker Thread"};
QLabel m_label;
QThread m_thread{this};
Worker m_worker;
Q_SLOT void showProgress(int p, bool inGuiThread) {
m_label.setText(QString("%1 % in %2 thread")
.arg(p).arg(inGuiThread ? "gui" : "worker"));
}
Q_SLOT void on_startGUI_clicked() {
m_worker.startInThread(qApp->thread());
}
Q_SLOT void on_startWorker_clicked() {
m_worker.startInThread(&m_thread);
}
public:
Window(QWidget *parent = {}, Qt::WindowFlags f = {}) : QWidget(parent, f) {
m_layout.addWidget(&m_startGUI);
m_layout.addWidget(&m_startWorker);
m_layout.addWidget(&m_label);
m_thread.start();
connect(&m_worker, SIGNAL(progress(int,bool)), SLOT(showProgress(int,bool)));
connect(&m_startGUI, SIGNAL(clicked(bool)), SLOT(on_startGUI_clicked()));
connect(&m_startWorker, SIGNAL(clicked(bool)), SLOT(on_startWorker_clicked()));
}
~Window() {
m_thread.quit();
m_thread.wait();
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Window w;
w.show();
return a.exec();
}
#include "main.moc"