wxWidgets: launching a bunch of code generating output without blocking the window - c++

For my work, I developped an algorithm that computes stuff (which I'm not allowed to talk about just yet). It is a time consumming algorithm. Due to my sincere hatred of command line hard to configure algorithms, I am developping a GUI tool that should help me configure the algorithm and launch it. Here it is for the background.
Where I'm stuggling is here: I have a bunch of parameters to set, which is fine, and then I hit a generate button. What it does (or what I want it to do) is launch a custom popup wxDialog with a wxTextField containing the log info generated by the algorithm. The custom wxDialog only has construtor that sets the widgets in the right position.
Here is how I coded it so far:
void TnF_LoadingFrame::OnGenerate(wxCommandEvent &)
{
std::cout << "Generating!" << std::endl;
LogDialog dialog (m_pController,this,-1,_("Generation"));
{
wxStreamToTextRedirector redirect(dialog.m_textRedirection);
m_pController->Generate();
}
if(dialog.ShowModal() == wxID_OK)
std::cout << "Saving to do" << std::endl;
else
std::cout << "Cancelled" << std::endl;
}
As expected, the dialog shows itself only when the algorithm has finished computing. What I would like is for the window to show itself and for the algorithm to run in the background and its logs to be shown in realtime in the textfield. I was thinking in the line of threads, but I am not sure I understand how it works. I was also thinking in the line of an update function that would automatically be called every frame or so in order to let the dialog shows itself and then launch the algorithm in the update function and finally allows the user to exit the dialog.
I hope I am clear. Does anyone has any suggestions, pointers, ...? Help would be much appreciated ! Thanks in advance.
----EDIT----
So I have tried to implement the wxThread stuff with the LogDialog, and there is a problem. It seems that I have done something wrong with the events that are sent from the thread to the log dialog.
What I wanted was to create a thread when the LogDialog is created. Also, the ok button of the logdialog is disabled while the . This thread launches the algorithm so it runs in the background. When the algorithm is finished, it sends an event to the LogDialog. This event is captured by the log dialog, and the callback function enables the ok button, and deletes the thread.
However, it seems that the LogDialog never captures the event, and while the algorithm is running, finishes and an event is sent somewhere, the callback function is never called... Maybe it something I've done wrong. I included the code below. Thanks again for your help.
Here is what I've wrote: the OnGenerate function is as follows:
void TnF_LoadingFrame::OnGenerate(wxCommandEvent &)
{
std::cout << "Generating!" << std::endl;
LogDialog dialog (m_pController,this,-1,_("Generation"));
if(dialog.ShowModal() == wxID_OK)
std::cout << "Saving to do" << std::endl;
else
std::cout << "Cancelled" << std::endl;
}
It creates a dialog that launches a thread. The thread files are as follows:
MyThread.h
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <wx/wxprec.h>
#ifndef WX_PRECOMP
#include <wx/wx.h>
#endif
#include "controller.h"
#include <tr1/memory>
using std::tr1::shared_ptr;
class Controller;
class MyThread : public wxThread
{
public:
MyThread(shared_ptr<Controller> pController, wxEvtHandler* parent)
: wxThread(wxTHREAD_DETACHED)
{
m_pController = pController;
m_parent = parent;
}
~MyThread(){}
protected:
virtual ExitCode Entry();
shared_ptr<Controller> m_pController;
wxEvtHandler* m_parent;
};
#endif // MYTHREAD_H
MyThread.cpp
#include "MyThread.h"
DEFINE_EVENT_TYPE(GENERATION_FINISHED_EVENT)
wxThread::ExitCode MyThread::Entry()
{
m_pController->Generate();
wxCommandEvent evt(GENERATION_FINISHED_EVENT, wxID_ANY);
m_parent->ProcessThreadEvent( evt );
//m_parent->ProcessPendingEvents();
return 0;
}
The log dialog files are as follows:
LogDialog.h
#ifndef LOGDIALOG_H
#define LOGDIALOG_H
#include <wx/wxprec.h>
#ifndef WX_PRECOMP
#include <wx/wx.h>
#endif
#include "controller.h"
DECLARE_EVENT_TYPE(GENERATION_FINISHED_EVENT, wxID_ANY)
#include "MyThread.h"
class MyThread;
class LogDialog : public wxDialog
{
public:
LogDialog(shared_ptr<Controller> pController, wxWindow *parent, wxWindowID id, const wxString & title,
const wxPoint & pos = wxDefaultPosition,
const wxSize & size = wxDefaultSize,
long style = wxDEFAULT_DIALOG_STYLE );
~LogDialog(){}
wxTextCtrl *m_textRedirection;
private:
void OnGenerationFinished(wxCommandEvent &evt);
shared_ptr<Controller> m_pController;
wxButton *m_buttonOk;
wxStreamToTextRedirector *m_redirect;
MyThread *m_thread;
DECLARE_EVENT_TABLE()
};
#endif
LogDialog.cpp
#include "LogDialog.h"
// how to define the custom event
DEFINE_EVENT_TYPE(GENERATION_FINISHED_EVENT)
LogDialog::LogDialog(shared_ptr<Controller> pController, wxWindow *parent, wxWindowID id, const wxString & title,
const wxPoint & pos,
const wxSize & size,
long style)
: wxDialog(parent, id, title, pos, size, style)
{
m_pController = pController;
//some stuff
m_thread = new MyThread(m_pController,this);
m_thread->Create();
m_thread->Run();
}
void LogDialog::OnGenerationFinished(wxCommandEvent &evt)
{
m_buttonOk->Enable(true);
m_thread->Delete();
}
BEGIN_EVENT_TABLE(LogDialog, wxDialog)
EVT_COMMAND(wxID_ANY, GENERATION_FINISHED_EVENT, LogDialog::OnGenerationFinished)
END_EVENT_TABLE()

there are basically 2 options:
Single threaded Application: Do everything in the main (wx) thread, just implement your algorithm in a loop triggered by an eventhandler, since you want the gui to be responsive and even show output you have to pass processing time back to wx, this can be done by calling yield (or better safeyield), you can do this somewhere in the loop implementing your algorithm
Multithreaded Application: create a separated thread for your algorithm and use events to communicate with the main thread (and the gui) - take a look at the wx-wiki page

So I have managed to make it work... I looked at the sample thread code provided with the library. First of all, I started using wxMACROs instead of MACROs. Don't if that helped... but hey, who am I to judge. Next instead of having a:
DECLARE_EVENT_TYPE(NAME,TYPE)
call in LogDialog.h, and MyThread.h, I use an enum in LogDialog.h:
enum{
wxEVT_COMMAND_MYTHREAD_COMPLETED = wxID_HIGHEST+1
};
This seems to be equivalent to declaring the event, and reduces the macro calls everywhere, so less messy.
Then I declare the event table at the beginning of LogDialog.cpp:
wxBEGIN_EVENT_TABLE(LogDialog, wxDialog)
EVT_THREAD(wxEVT_COMMAND_MYTHREAD_COMPLETED, LogDialog::OnGenerationFinished)
wxEND_EVENT_TABLE()
Again, don't know if that helped. But the using an EVT_THREAD is probablly better. Finally, and I think it is what did the trick, I create and launch the event as follows:
wxThreadEvent evt(wxEVT_THREAD,wxEVT_COMMAND_MYTHREAD_COMPLETED);
wxQueueEvent(m_parent,evt.Clone());
This does the trick.
Also the other problem was to get the standard output back and show it in a text control but it seems that
wxStreamToTextRedirector(m_textRedirection);
is not thread safe... This is a story for another time.
Thanks to #Baxit for his insights.

Related

can't close app from dialog box (Qt app)

Getting right to it I have a MainWindow and a dialog window which is executed if a condition is met but the problem is I can't get the app to quit if the cancel button from the dialog window is clicked. I've tried putting qApp->quit() in the slot function for the cancel button. I've tried connecting the cancel button slot to the predefined close slot for the MainWindow object via a clickme() signal from the dialog class. (as shown below)
qt application does not quit I read the answer to this question which I think got me close because it made me realize that I can't quit the app before showing the MainWindow but making that change didn't solve the problem. I even tried to explicitly emit the clickme() signal from cancel button slot but that actually caused the OS to throw a signal which threw an error at me saying "the inferior stopped because it received a signal from the operating system signal name: SIGSEGV
signal meaning: segmentation fault"
Here's my code:
Notice warning; // this is the object for the dialog window also all of this code is in main.cpp
warning.setModal(true);
QObject::connect(&warning, SIGNAL(clickme()), &warning, SLOT(on_Cancel_button_clicked()));
QObject::connect(&warning, SIGNAL(clickme()), &w, SLOT(close()));
warning.exec();
Also before that code is
MainWindow w;
w.show();
Also while writing this question I tried this
QObject::connect(&warning, SIGNAL(clickme()), qApp, SLOT(quit()));
But that still didn't work. If you need more info just let me know.
Update: I'm starting to think that the reason I'm having so much trouble with this connect signal/slot function is because it's not designed to connect two windows of two different classes and I should rework my app to do everything from the MainWindow class which is a shame because when I picture a GUI program I picture multiple windows connected to each other regardless of whether or not the object representing each window is from the same class as the others yet I have such a hard time trying do that with the QT framework when it comes to trying to connect objects of different classes.
Update: please forgive me. I assume that the code that I originally thought was the answer would work and took a break from working on the program before actually testing out that code. Going back to it now I discovered that it doesn't work. The code I'm referring to is the following
QMessageBox msg;
msg.setText("Continue?");
msg.addButton(QMessageBox::Yes);
msg.addButton(QMessageBox::No);
QObject::connect(&msg, &QMessageBox::rejected,
&app, &QApplication::quit); // this line doesn't work for me and I don't know why
QObject::connect(&msg, &QMessageBox::accepted, [&dlg]{
(new QLabel("I'm running")).show();
});
QApp->quit(); should work. Remove warning.setModal(true); The dialog becomes modal when you call exec(). SetModal(true) should be used with show() according to Qt docs. So this may be causing your problem.
I think I've found the problem.
Probably, you're calling exec() twice:
To enter the QApplicationevent loop
To execute the dialog.
Use show() instead of exec() for the dialog. You have an example below where you can check the signal/slot works well. In your application, you need the slot to close the window, but:
With the line of code dialog.exec();, the app keeps running. This is your issue.
With the line of code dialog.show();, the app stops.
By the way, I saw your last question update, but it is not correct. In fact, of course you can connect two different classes.
window.h
#ifndef WINDOW_H
#define WINDOW_H
#include <QApplication>
#include <QMainWindow>
#include <QAbstractButton>
#include <QDebug>
#include "dialog.h"
class Window : public QMainWindow
{
Q_OBJECT
public:
Window()
{
dialog = new Dialog();
dialog->setText("Continue?");
dialog->addButton(QMessageBox::Yes);
dialog->addButton(QMessageBox::No);
auto onClick = [this]() {
auto role = dialog->buttonRole(dialog->clickedButton());
if (role == QMessageBox::NoRole) {
qDebug() << "QMessageBox::NoRole";
QApplication::quit();
}
if (role == QMessageBox::YesRole) {
qDebug() << "QMessageBox::YesRole";
}
};
QObject::connect(dialog, &QMessageBox::buttonClicked, onClick);
dialog->show(); // this must be show(), not exec()
}
virtual ~Window() { delete dialog; }
private:
Dialog *dialog;
public slots:
void windowSlot() { qDebug() << Q_FUNC_INFO;
close();
}
};
#endif // WINDOW_H
dialog.h
#ifndef DIALOG_H
#define DIALOG_H
#include <QMessageBox>
class Dialog : public QMessageBox
{
Q_OBJECT
public:
Dialog() {}
virtual ~Dialog() {}
};
#endif // DIALOG_H
main.cpp
#include <QApplication>
#include <QtGui>
#include "window.h"
int main(int argc, char **argv)
{
QApplication app(argc, argv);
Window window;
window.setWindowTitle("window");
window.show();
return app.exec();
}
Update #1: a very interesting post.

Substitute for sleep function in Qt/C++

So I am writing a program that displays each letter of a word for 1 second with a 1 second interval between the letters. (It's for a spelling exercise for grade 1). I am currently using the sleep function to "pause" the program for 1 second before it "updates" again. After that it displays the word for a second and then removes it. I repaint before the sleep function, else it does not seem to update in time.
Here is the basic function:
QString word = "apple";
QThread thread;
for(int i = 0; i < word.size(); i++)
{
ui->label1->setText(word[i]);
ui->label1->repaint();
thread.sleep(1);
ui->label1->setText("");
thread.sleep(1);
}
ui->label1->setText(word);
ui->label1->repaint();
thread.sleep(1);
ui->label1->setText("");
This works fine, except the program stops responding (even though I can see the correct output is still displaying) until the whole function is done executing then it works fine again. Is there another way I can accomplish this goal without using sleep? I am quite new to Qt.
Update I made. I made a new class that will handle the timer, but it does not seem to actually connect the signal and slot. Here is the .h file:
#ifndef TIMERDISPLAY_H
#define TIMERDISPLAY_H
#include <QTimer>
#include <QObject>
class TimerDisplay:public QObject
{
Q_OBJECT
public:
TimerDisplay();
public slots:
void expired();
private:
QTimer timer;
};
#endif // TIMERDISPLAY_H
and the .cpp file:
#include "timerdisplay.h"
#include <QDebug>
TimerDisplay::TimerDisplay()
{
connect(&timer, SIGNAL(timeout()), this, SLOT(expired()));
timer.setSingleShot(false);
timer.setInterval(1000);
timer.start();
}
void TimerDisplay::expired()
{
qDebug()<<"timer expired";
}
Use QTimer or QElapsedTimer if you need more precision.
main.cpp
#include <QTimer>
#include <QCoreApplication>
#include <QString>
#include <QTextStream>
#include <QDebug>
int main(int argc, char **argv)
{
QCoreApplication application(argc, argv);
QTimer timer;
QTextStream textStream(stdout);
QString word = "apple";
int i = 0;
QObject::connect(&timer, &QTimer::timeout, [&textStream, word, &i] () {
if (i < word.size()) {
textStream << word.at(i) << flush;
++i;
}
});
timer.start(1000);
return application.exec();
}
main.pro
TEMPLATE = app
TARGET = main
QT = core
CONFIG += c++11
SOURCES += main.cpp
Build and Run
qmake && make && ./main
Output
apple
This is happening, because you're blocking the thread. In most cases you're interested in using event loop.
You can use timer and increment a counter in a slot function. Every QObject has support for timer. You can start it using int QObject::startTimer(int interval) and it will call virtual void timerEvent(QTimerEvent *event) every interval miliseconds (it's a virtual method - reimplement it).
You can also use QTimer and connect QTimer::timeout() signal to accomplish the same thing.
Inside timer handler increment a counter and print the character.
It's a good idea to put your hands on finite state machine concept. FSMs are a great tool for solving similar problems problems. In fact, using a timer callback you create a state machine, but very simple one.
I still don't know what the problem is, but I found a make-shift solution. In my program I include one class (eg class1) into my mainwindow.cpp and from that class I include the timer class. I solved the problem by removing the timer class and adding all those functions to class1. Doesn't make sense to me, but it works.

Simple multithreading with Qt: am I doing this right?

I'm new to StackOverflow and wondering if I'm doing this right:
I'm writing a simple Qt application to test multi-threading (something I am also completely new to). I made a MainWindow that contains widgets, and a class MyThread that subclasses QThread and overrides the run() method.
The application simply displays two buttons, "Start Counter" and "Stop Counter", and a text field. When "start counter" is pressed, a worker thread is created and runs in the background, continuously incrementing a counter in a while loop and signaling the main thread (where the GUI is) with the updated value. When "Stop Counter" is pressed, a signal is sent to the main thread that stops the while loop, and the counter is stopped until "Start Counter" is pressed again.
This works perfectly fine ... but is it the best way? I'm new at this, and read a lot of people saying "don't subclass QThread" and other people saying "subclass QThread", and it's a little bit confusing. If this isn't the best way to implement this sort of thing (run a computationally-intensive loop in a background thread with "start" and "stop" buttons), what is? If I'm doing it wrong, how do I do it right? I don't want to learn the wrong way.
Thank you! And here's the code:
MyThread.h
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QThread>
#include <QMutex>
class MyThread : public QThread
{
Q_OBJECT
public slots:
void stopRunning();
protected:
virtual void run();
signals:
void signalValueUpdated(QString);
private:
bool isRunning;
};
MyThread.cpp
#include "MyThread.h"
#include <QString>
void MyThread::run()
{
qDebug("Thread id inside run %d",(int)QThread::currentThreadId());
static int value=0; //If this is not static, then it is reset to 0 every time this function is called.
isRunning = 1;
while(isRunning == 1)
{
QString string = QString("value: %1").arg(value++);
sleep(1/1000); //If this isn't here, the counter increments way too fast and dies, or something; the app freezes, anyway.
emit signalValueUpdated(string);
}
}
void MyThread::stopRunning()
{
isRunning = 0;
}
MainWindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QApplication>
#include <QPushButton>
#include <QHBoxLayout>
#include <QLineEdit>
#include "MyThread.h"
class MainWindow : public QWidget
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
private:
//Widgets
QHBoxLayout * boxLayout;
QPushButton * startButton;
QPushButton * stopButton;
QLineEdit * lineEdit;
MyThread thread;
};
#endif
MainWindow.cpp
#include "MainWindow.h"
MainWindow::MainWindow(QWidget *parent) : QWidget(parent)
{
boxLayout = new QHBoxLayout(this);
startButton = new QPushButton("Start Counter", this);
stopButton = new QPushButton("Stop Counter", this);
lineEdit = new QLineEdit(this);
boxLayout->addWidget(startButton);
boxLayout->addWidget(stopButton);
boxLayout->addWidget(lineEdit);
qDebug("Thread id %d",(int)QThread::currentThreadId());
//When the start button is pressed, invoke the start() method in the counter thread
QObject::connect(startButton,SIGNAL(clicked()),&thread,SLOT(start()), Qt::QueuedConnection);
//When the stop button is pressed, invoke the stop() method in the counter thread
QObject::connect(stopButton,SIGNAL(clicked()),&thread,SLOT(stopRunning()), Qt::QueuedConnection);
//When the counter thread emits a signal saying its value has been updated, reflect that change in the lineEdit field.
QObject::connect(&thread,SIGNAL(signalValueUpdated(const QString&)),lineEdit,SLOT(setText(const QString&)), Qt::QueuedConnection);
}
Most of the time QThread sub-classing is a wrong way to do threading in Qt. I suggest you to read an article about threads, event loops and other which could give you an idea how to use threads in Qt in a better way. But do not listen to anyone who arguing that there is the only one right way to use QThread. There are 2 ways and while subclassing is not needed in general it could be useful sometimes. You just need to use non-subclassing way until you really need to subclass. In your particular case you don't need subclassing.
Replace sleep(1/1000); with msleep(100); Things will be just fine :)

Reading large Files

I want to read a 50MB file and send it over tcp. The file contains only floats. First I created only a Mainwindow, witch reads one line and sends it to the Server, but the gui got frozen. So I created a class that depends on QThread called QSendThread. Here is the Code for the class QThread:
#ifndef QSENDTHREAD_H
#define QSENDTHREAD_H
#include <QThread>
#include <QLabel>
#include <QFile>
#include <QMessageBox>
#include <QtNetwork/QTcpSocket>
#include <QtNetwork/QHostAddress>
class QSendThread : public QThread
{
Q_OBJECT
public:
QSendThread(QTcpSocket* qtcpso, QLabel* qlbl, QFile* qfiel, QObject *parent = NULL);
~QSendThread();
protected:
void run(void);
private:
QTcpSocket* qtcpsoDest;
QLabel* qlblRef;
QFile* qfileRef;
signals:
void error(QString qstrError);
};
#endif // QSENDTHREAD_H
#include "qsendthread.h"
QSendThread::QSendThread(QTcpSocket* qtcpso, QLabel* qlbl, QFile* qfile, QObject *parent)
: QThread(parent)
{
qtcpsoDest = qtcpso;
qlblRef = qlbl;
qfileRef = qfile;
}
QSendThread::~QSendThread()
{
}
void QSendThread::run(void)
{
int iLine = 0;
do
{
QByteArray qbarrBlock;
QDataStream qdstrmOut(&qbarrBlock, QIODevice::WriteOnly);
// show witch line is read
qlblRef->setText(tr("Reading Line: %1").arg(++iLine));
qdstrmOut.setVersion(QDataStream::Qt_4_6);
qdstrmOut << (quint16)0;
qdstrmOut << qfileRef->readLine().data();
qdstrmOut.device()->seek(0);
qdstrmOut << (quint16)(qbarrBlock.size() - sizeof(quint16));
qtcpsoDest->write(qbarrBlock);
qtcpsoDest->flush();
qbarrBlock.clear();
} while(!qfileRef->atEnd());
}
But the program crashing in the method qregion::qt_region_strictContains(const QRegion &region, const QRect &rect)
Is the method to read the file like I am doing wrong?
Thanks for Help.
First, you shouldn't really need to subclass QThread. The Qt documentation is misleading on this point. See this accepted answer for a similar question for lots of good info.
Second, you can only correctly access the gui from the main thread so your call qlblRef->setText() would be a problem. Accessing the gui from a thread other than the main one can be done using signals and slots or postEvent(). You can read up on events here.
Finally, this documentation is really required reading for working with threads in Qt. Pay particular attention to the section on threads and QObjects.
Addition:
To follow the advice above, you could certainly wrap your file reading code in a QObject subclass. An alternative (which I have little experience with myself) may be to try putting your code in QtConcurrent::run() and getting the result with a QFuture.

processEvents And Memory Leak?

NOTE: THE CODE PROVIDED JUST GIVES THE IDEA OF THE STRUCTURE OF THE APPLICATION
I have a Qt application, interfacing with external hardware. The structure is such that the class for interfacing with the hardware inherits from QObject and is composed with the main GUI thread class. Let's say the class is test.h, here is its sample code:
#ifndef TEST_H
#define TEST_H
#include <QLineEdit>
#include <QString>
#include <QTimer>
#include "ui_test.h"
#define TIMEOUT 100
class TestObj;
class TestApp:public QWidget, public Ui::mTestForm
{
Q_OBJECT
public:
TestApp(QWidget* parent=0);
QTimer* mTimer;
bool mWindowClosed;
TestObj* mObj;
public slots:
void UpdateText();
void Exit();
};
class TestObj:public QObject
{
Q_OBJECT
public:
TestObj();
void RandomTest();
};
#endif
Code for test.cpp is
#include "test.h"
TestApp::TestApp(QWidget* parent):QWidget(parent)
{
setupUi(this);
mObj = new TestObj();
mWindowClosed=false;
mLineEdit->setText("Hello");
mTimer=new QTimer();
mTimer->singleShot(1000,this,SLOT(UpdateText()));
connect(mExitButton,SIGNAL(clicked()),this, SLOT(Exit()));
}
void TestApp::UpdateText()
{
if(mWindowClosed)
{
//QApplication::processEvents();
return;
}
else
{
//QApplication::processEvents();
mObj->RandomTest();
mLineEdit->setText("Hi");
mTimer->singleShot(100,this,SLOT(UpdateText()));
}
}
void TestApp::Exit()
{
mWindowClosed=true;
}
Now consider that TestObj class is the one used to interface with the hardware. This class sends three possible commands (in actual code, the above is just a sample structure) with different timeouts to the hardware, thus we have a timer which is used when sending commands (implemented as functions) to the hardware. Each of these has a processEvents entry to identify any changes to variables in the meanwhile.
The problem is this module is responsible for a steady rise in memory during program execution.
When I comment out the UpdateText() function in the TestApp constructor, the app works fine.
My guess is that most likely there is queuing of signal slots due to which the memory increase, because there are lots of GUI events taking place. And since the class isn't implemented as a separate thread and clubbed with the main GUI thread. There is continuous update of the main thread.
Can someone suggest a way out? I don't have the authority to change the design otherwise I would have implemented the interface class as a thread. So if a solution can be suggested with the current design as is, it would be beneficial.
Creating a regular timer, setting its interval to 100 and connecting its timout signal to the
UpdateText function will avoid events piling up.
P.S: You don't need a QTimer object for singleShot, you can call QTimer::singleShot directly.
Wild guess:
Try changing this:
mTimer->singleShot(100,this,SLOT(UpdateText()));
to this:
if (!slotSet)
{
mTimer->singleShot(100,this,SLOT(UpdateText()));
slotSet = true;
}