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
}
}
Related
My problem is the following one: I have 2 classes (mainwindow and mythread), I run the thread from the mainwindow and I would like to display some QLabel of my mainwindow from mythread :
mythread.cpp :
void mythread::run()
{
while(1)
{
this->read();
}
}
void mythread::read()
{
RF_Power_Control(&MonLecteur, TRUE, 0);
status = ISO14443_3_A_PollCard(&MonLecteur, atq, sak, uid, &uid_len);
if (status != 0){
//display Qlabel in mainwindow
}
}
mainwindow.cpp :
_thread = new mythread();
_thread->start();
You should use Qt's signal/slot mechanism. The thread will emit a signal, that new data has been read. Any interested object can connect to that signal, and perform actions depending on it.
This also works across thread-boundaries, as in your example. In Qt, it is required that only the main-thread interacts with UI elements.
Here is an outline:
// Your mainwindow:
class MyWindow : public QMainWindow {
Q_OBJECT
// as needed
private slots:
void setLabel(const QString &t) { m_label->setText(t); }
};
// Your thread
class MyThread: public QThread {
Q_OBJECT
// as needed
signals:
void statusUpdated(const QString &t);
};
// in your loop
if (status != 0) {
emit statusUpdated("New Status!");
}
// in your mainwindow
_thread = new MyThread;
connect(_thread, &MyThread::statusUpdated, this, &MyWindow::setLabel);
_thread->start();
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"
How to properly connect to QObject's destroyed signal from Qt Script?
When I connect to it like to your average signal, it does not work. I did test that I removed the object and that other QObjects did get the signal, but the script function I connected to it is not invoked.
Below is the sample I'm using for testing. The most important part is the line:
_engine.evaluate("obj.destroyed.connect(function() { debug(\"obj destroyed\") })");
I'd expect it to invoke that function when obj is destroyed. The code below ensures that object is deleted when even loop already started and it also tests that the object did send destroyed signal to another QObject. What I want to fix is for the script to invoke the script function with debug("obj destroyed") after destroyed signal is emitted.
ScriptTester.h:
#ifndef SCRIPTTESTER_H
#define SCRIPTTESTER_H
#include <QtScript>
#include <QtCore>
class ScriptTester: public QObject {
Q_OBJECT
public:
explicit ScriptTester(QObject *parent = 0);
private slots:
void signalTest();
void boo();
private:
QScriptEngine _engine;
};
#endif // SCRIPTTESTER_H
ScriptTester.cpp:
#include "ScriptTester.h"
static QScriptValue scriptDebug(QScriptContext *context, QScriptEngine *engine) {
Q_UNUSED(engine);
if (context->argumentCount() >= 1) {
QString msg = context->argument(0).toString();
for (int i = 1; i < context->argumentCount(); ++i) {
msg = msg.arg(context->argument(i).toString());
}
qDebug() << msg;
}
return QScriptValue();
}
ScriptTester::ScriptTester(QObject *parent) :
QObject(parent)
{
QTimer::singleShot(0, Qt::CoarseTimer, this, SLOT(signalTest()));
_engine.globalObject().setProperty("debug", _engine.newFunction(scriptDebug, 1));
}
void ScriptTester::signalTest() {
QObject *obj = new QObject(this);
_engine.globalObject().setProperty("obj", _engine.newQObject(obj));
_engine.evaluate("obj.destroyed.connect(function() { debug(\"obj destroyed\") })");
if (_engine.hasUncaughtException()) {
qDebug() << "Exception:" << _engine.uncaughtException().toString();
}
connect(obj, SIGNAL(destroyed()), this, SLOT(boo()));
QTimer *timer = new QTimer;
_engine.globalObject().setProperty("timer", _engine.newQObject(timer));
_engine.evaluate("timer.timeout.connect(function() { debug(\"timer timeout\"); obj.deleteLater(); })");
if (_engine.hasUncaughtException()) {
qDebug() << "Exception:" << _engine.uncaughtException().toString();
}
timer->setSingleShot(true);
timer->start(100);
}
void ScriptTester::boo() {
qDebug() << "was destroyed!";
}
Note that I don't want a hack like passing destroyed first to C++ code and then manually or by a signal informing script of that. I'm searching for an implementation that's done fully in the script.
Why every tick of QTimer creates new thread? My application needs to run as long as possible, but after xx of ticks it freeze, it still running (it's responding), but next ticks are not executed. I looked into debug info and i saw:
QThread::start: Failed to create thread () QThread::start: Failed to
create thread () QThread::start: Failed to create thread ()
QThread::start: Failed to create thread () QThread::start: Failed to
create thread () QThread::start: Failed to create thread ()
QThread::start: Failed to create thread () QThread::start: Failed to
create thread ()
waat?
Tick are executed every xx seconds, signal is located into QWidged (which is a one of tab of TabWidget)
namespace Ui {
class accountTab;
}
class accountTab : public QMainWindow
{
Q_OBJECT
public:
explicit accountTab(QWidget *parent = 0);
class player *_player;
~accountTab();
private slots:
void on_clean_timer_clicked();
public:
Ui::accountTab *ui;
};
void accountTab::on_clean_timer_clicked()
{
if(user->timers.value("clean")->isActive()) {
_player->timers.value("clean")->stop();
}
else if(!user->timers.value("clean")->isActive()) {
_player->timers.value("clean")->start(1800000); //900000
}
}
_player is a simple class.
_player->clean() execute a few static classes, which are try/catched.
player.h
class player : public QObject
{
Q_OBJECT
public:
player();
~player();
player(Ui::accountTab *tab, std::string login, std::string password);
player(Ui::accountTab *tab, User user);
public:
bool logIn();
Ui::accountTab *tab = new Ui::accountTab();
public slots:
void clean();
private:
User user;
QMap<std::string, QTimer*> timers;
void initializeTimers();
};
player.cpp
player::player(Ui::accountTab *tab, std::string login, std::string password)
{
this->tab = tab;
this->user.login = login;
this->user.password = password;
}
player::~player()
{
delete this->manager;
delete this->tab;
}
bool player::logIn()
{
...
Log::writeLog("Login completed!", *this);
return true;
}
bool player::setup(bool saved, bool save)
{
if(!this->logIn())
return false;
Packets::sendPacket("getSimulation", *this);
this->initializeTimers();
return true;
}
void player::initializeTimers()
{
this->timers.insert("clean", new QTimer(this));
connect(this->timers.value("clean"), SIGNAL(timeout()), this, SLOT(cleanZoo()));
}
void player::clean()
{
Packets::sendPacket()
}
user class in player class keeps login and password.
timers is a QMap: QMap timers;
Packets::sendPacket() is static void
and sendPacket()
QString httpManager::sendPacket()
{
QNetworkRequest request("https://www.google.pl/");
if(headers.size() > 0) {
for (QMap<const char*, const char*>::iterator i = headers.begin(); i != headers.end(); ++i)
request.setRawHeader(i.key(), i.value());
}
QNetworkAccessManager *manager = new QNetworkAccessManager();
manager->setCookieJar(this->cookies);
QNetworkReply *reply = manager->get(request);
QEventLoop loop;
QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
loop.exec();
QList<QNetworkCookie> cookies = reply->manager()->cookieJar()->cookiesForUrl(QUrl(reply->url()));
foreach(QNetworkCookie cookie, cookies)
{
this->cookies->insertCookie(cookie);
}
return reply->readAll().data();
}
QEventLoop is executed to get response in the same void. Is this creating new threads?
There are a few things wrong here:
Your application should have a single QNetworkAccessManager that all code uses, don't create one for every call, create one in main and pass that to where it is needed.
You need to delete QNetworkReply using deleteLater as explained in the manual.
Creating another event loop in a function is generally not a good idea. Create a slot on the class httpManager connected to the QNetworkAccessManager::finished(QNetworkReply * reply) signal, read the reply and call deleteLater from here.
im using Qt QProgressBar and place it in the statusBar on my main window
Like this in the constructor :
pb = new QProgressBar(statusBar());
pb->setTextVisible(false);
pb->hide();
statusBar()->addPermanentWidget(pb);
then im running procsses (web page loadding in this case )
and trying to show the progress with :
connect(ui.webView, SIGNAL(loadProgress(int)), SLOT(setProgress (int)));
void myMainWindow:: setProgress(int progress)
{
pb->show();
pb->setRange(0,100);
pb->setValue(progress);
}
But im getting Unhandled exception when it comes to pb->show()
I guess it has to do something with loading the parent main windows and then the progress bar
I was reading about the QAbstractEventDispatcher and processEvents but not understood how to implement it .
i did small test and put the pb->show() function call in button click signal/slut
that means im triggering the pb->show() after the web page and the mainwindows fully loaded and its working fine without the exception. that make me belive there is problem
with the event processing.
here is the class :
class MainWindowMainWindowContainer : public QMainWindow
{
Q_OBJECT
public:
MainWindowContainer(QWidget *parent = 0);
public slots:
void adjustLocation();
void changeLocation();
void adjustTitle();
void setProgress(int p);
void finishLoading(bool);
void finishedSlot(QNetworkReply* reply);
private:
Ui::OnLineBack_mainWindow ui;
int progress;
void createWebViewActions();
QProgressBar *pb;
void setprogressBar(int progress,bool show);
};
MainWindowContainer::MainWindowContainer(QWidget* parent) :
QMainWindow(parent),
{
ui.setupUi(this);
progress = 0;
createWebViewActions();
ui.webView->load(QUrl("www.cnnnn.com"));
ui.webView->show();
pb = new QProgressBar(statusBar());
pb->setTextVisible(false);
pb->hide();
statusBar()->addPermanentWidget(pb);
}
void MainWindowContainer::createWebViewActions()
{
connect(ui.webView, SIGNAL(loadFinished(bool)), SLOT(adjustLocation()));
connect(ui.webView, SIGNAL(titleChanged(QString)), SLOT(adjustTitle()));
connect(ui.webView, SIGNAL(loadProgress(int)), SLOT(setProgress(int)));
connect(ui.webView, SIGNAL(loadFinished(bool)), SLOT(finishLoading(bool)));
connect(ui.webView, SIGNAL(linkClicked(const QUrl&)),this, SLOT(linkClicked(const QUrl&)));
}
void MainWindowContainer::setProgress(int p)
{
progress = p;
adjustTitle();
}
void MainWindowContainer::adjustTitle()
{
qApp->processEvents();
pb->show();
if (progress <= 0 || progress >= 100)
{
QString titl = ui.webView->title();
statusBar()->showMessage(titl);
setprogressBar(-1,false);
}
else
{
statusBar()->showMessage(QString("%1 (%2%)").arg(ui.webView->title()).arg(progress));
setprogressBar(progress,true);
}
}
void MainWindowContainer::finishLoading(bool)
{
progress = 100;
adjustTitle();
}
void MainWindowContainer::setprogressBar(int progress,bool show)
{
if(show)
{
pb->show();
pb->setRange(0,100);
pb->setValue(progress);
}
else
{
pb->hide();
}
}
In your createWebViewActions() function you connect the signals to their respective slots. (One small remark, the connect for the titleChanged(QString) signal and adjustTitle() slot fails because they have different signatures)
Among others you are connecting the signal loadProgress(int) to slot setProgress(int). In this slot you call adjustTitle() where the instruction pb->show() is being executed.
Notice that you are calling the createWebViewActions() function before the call to QProgressBar constructor
(...)
createWebViewActions(); <------ here you establish the signal->slot connections
ui.webView->load(QUrl("www.cnnnn.com"));
ui.webView->show();
pb = new QProgressBar(statusBar()); <------ here you call the QProgressBar constructor
pb->setTextVisible(false);
pb->hide();
statusBar()->addPermanentWidget(pb);
(...)
I think that maybe this slot (setProgress()) is being called before the QProgressBar is constructed which triggers the Unhandled exception you are getting.
You could try and move the call to the QProgressBar constructor so that it is created before the slots connection.