Using Custom Events in QT4 - c++

I have an app that has a progress bar & spawns a worker thread to do some work & report back progress. The dialog class overrides the customEvent method so that I can process events that are being passed to the gui thread via the worker thread. Before I was using a QThread derived class as the worker thread and I changed it to use ACE_Thread_Manager->spawn() with a static function for the worker.
The problem shows up when I run the app and press the button so the worker is spawned & starts doing work. When it sends the signal to increment the progress bar I get the following errors logged to std out.
QPixmap: It is not safe to use pixmaps outside the GUI thread
This seems to happen when the progressBar->setValue() is called. So it seems like the setting of the progress bar is happening in a different thread than the main gui thread. I'm unclear as to how that's possible. I'm under the impression that I have a main gui thread which has my gui & the customEvent method is on that same thread and the worker is on it's own thread. Is this assumption wrong? And is there any difference when using the QThread derived class versus the static run_svc method?
Any help would be appreciated. The code snippets for the customEvent handler, run_svc, and button handler code are below and the code is attached.
void MyDlgEx::customEvent(QEvent * e)
{
if (e->type() == IdNumOperations)
{
NumOperations* pEvt = static_cast<NumOperations*>(e);
_steps = 0;
cout << "Num Operations = " << pEvt->operations() << endl;
}
else if (e->type() == IdStep)
{
if (_steps % 10 == 0)
{
cout << "Step++ = " << _steps << endl;
}
_steps++;
_progressBar->setValue(_steps);
}
}
void* MyDlgEx::run_svc(void* args)
{
auto_ptr<ThreadArgs> thread_args(static_cast<ThreadArgs*>(args));
QApplication::sendEvent((QObject*)thread_args->m_pDlg, new NumOperations(300));
// does some work that takes time -- ommitted for clarity
// called in a loop
QApplication::sendEvent((QObject*)thread_args->m_pDlg, new Step());
QApplication::sendEvent((QObject*)thread_args->m_pDlg, new Completed());
return 0;
}
Button Handler
Commented out lines where where I used a QT class derived from QThread. Using ACE has to spawn the thread has uncovered this issue.
void MyDlgEx::btnShowProgress_clicked()
{
//_pProc = new ProcessThread(this);
//_pProc->run();
auto_ptr<ThreadArgs> thread_args(new ThreadArgs(this));
if (ACE_Thread_Manager::instance()->spawn(
MyDlgEx::run_svc,
static_cast<void*>(thread_args.get()),
THR_DETACHED | THR_SCOPE_SYSTEM) == -1)
cout << "Failed to spawn thread." << endl;
thread_args.release();
}

Try calling QApplication::postEvent(...) instead of QApplication::sendEvent(). The docs say that sendEvent sends the event directly, meaning that it calls the customEvent() function directly from the other thread. postEvent() adds the event to the event queue where it can later be dispatched to customEvent() by the main GUI event loop.
Just because the customEvent() function is a member of an object created in the main GUI thread doesn't mean another thread cannot call the function. I believe that is what is happening when you call QApplication::sendEvent() from another thread.

Related

Starting/stopping function using QT GUI buttons

I am writing a simple Qt C++ GUI window for the user to input certain values for a USRP device to record (i.e. input start frequency, stop frequency, etc..). Once the user inputted the values, the "EXECUTE" button is clicked and the execute_run_usrp() function is called in its own thread (so not to block the GUI). Then the STOP button should be able to be clicked at any time to terminate the thread running the function that runs the USRP, execute_run_usrp(), thus terminating the USRP recording process.
The function run_usrp(x,y,z,etc) is defined in another *.cpp file in the Project.
The problem that I am having that the STOP button when clicked only seems to "pause" the function...doesn't actually kill it (like with CTRL-C, which works great here)
Here is my code from MainWindow.cpp for the EXECUTE button click:
// run the following when EXECUTE button is clicked
void MainWindow::on_button_EXECUTE_clicked()
{
if ( ui->calculated_StartTime->text() == "" )
{
QMessageBox messageBox;
messageBox.critical(0,"Error","Hit the \"CALCULATE SCHEDULE\" button first above!!");
messageBox.setFixedSize(500,200);
return;
}
ui->button_STOP->setVisible(true);
ui->button_EXECUTE->setVisible(false);
auto function = std::bind([this]{execute_run_usrp();});
QThread* temp = QThread::create(function);
temp->start();
connect( ui->button_STOP, SIGNAL(clicked()), temp, SLOT( terminate() ));
}
Here is the execute_run_usrp() function:
void MainWindow::execute_run_usrp()
{
float startFreq = ui->input_startFreq->text().toFloat();
float stopFreq = ui->input_stopFreq->text().toFloat();
float stepFreq = ui->input_stepFreq->text().toFloat();
int nRepeats = ui->input_numRepeats->text().toInt();
float ipp = ui->input_IPP->text().toFloat();
int sweepCadence = ui->calculated_sweepCadence->text().toInt();
int numSweeps = ui->input_numSweeps->text().toInt();
std::string schedule_run = ui->calculated_StartTime->text().toStdString();
std::cout << startFreq << std::endl;
std::cout << stopFreq << std::endl;
std::cout << stepFreq<< std::endl;
std::cout << nRepeats << std::endl;
std::cout << ipp << std::endl;
std::cout << sweepCadence << std::endl;
std::cout << numSweeps << std::endl;
run_usrp(startFreq, stopFreq, stepFreq, nRepeats, ipp, sweepCadence, numSweeps, schedule_run);
}
And here is the STOP button code:
void MainWindow::on_button_STOP_clicked()
{
ui->button_STOP->setVisible(false);
ui->button_EXECUTE->setVisible(true);
}
Clicking the STOP button only seems to pause the function, doesn't actually kill it like doing CTRL-C with the keyboard. I think the UHD library (that runs the USRPs) spwans its own thread for running.
Question: How do I 100% terminate the entire function (and including any spwaned children from UHD) when I hit the STOP button ?
Thank you very much!
How do I 100% terminate the entire function (and including any spwaned children from UHD) when I hit the STOP button ?
From the code you've shown there's no easy answer imho.
The shortest one is: by incorporating some inter-thread communication inside the USRP function, provided you want some sort of graceful exit.
As for terminate, QT's docs state:
This function is dangerous and its use is discouraged. The thread can be terminated at any point in its code path. Threads can be terminated while modifying data. There is no chance for the thread to clean up after itself, unlock any held mutexes, etc. In short, use this function only if absolutely necessary.
Alternatively, the whole USRP function can be run inside a separate process, that is then killed. Again, plain killing of the process is far from graceful, still it seems easier and safer than doing it to a thread. You may want to check QProcess for reference.

Why does QDialog::exec() blocks QTcpSocket work

I make a Bomberman (or DynaBlast) game clone with multiplayer. Server game and client game communicate through messages using QTcpSocket. Typical workflow for playing network game is following:
Server player execs NetworkGame dialog, among other things this dialog creates NetworkGame object
Client player execs ClientGame dialog, among other things this dialog creates ClientGame object
Client chooses IP of server and clicks "Connect"
Server accepts connections, server and clients are now able to send messages each other
Client sends "ready"-message, server start game
When game over, the game object emits GameStatusChanged(GameStatus newStatus) signal. This signal connected to MainWindow, which execs GameOverDialog. If player chooses "Play again" at GameOverDialog, MainWindow execs NetworkGame or ClientGame dialogs again and we are at the first points.
So, after first game is over, second exec of ClientGameDialog blocks QTcpSocket work in the way it cann't read data or emit QTcpSocket::readyRead signal (I don't know which one point exactly). ClientGameDialog's GUI is responsive, it can send messages to server, but it cann't read messages. At the same time NetworkGame and NetworkGameDialog work properly - they are able to send and receive messages. I checked all my classes several times and don't see any significant difference.
I think full code is to huge to post it here, so I gave UML a try. This is a chart for most important classes. Green arrow designates Qt's child-parent relations, starting at a child QObject it points to a parent.
When Socket class receives new message through QTcpSocket interface, it emits messageReceived(const Message& message) signal; other classes can connect to this signal via slots and handle messages. I don't see what Client, ServerWorker, Server classes can do with event loop, the just help to process raw data from QTcpSocket and deliver messages to other classes, particulary to Game and Dialog classes.
Here is some code (I have some code duplications, I leave them until better times). Creating game:
// Server game
void MainWindow::startNetworkGame() // User clicked "Start network game" button
{
const auto& player = mainMenuWidget_->selectedPlayer();
gameDialogs_ = createGameDialogs(this, GameType::Server, player);
auto answer = gameDialogs_.creationDialog->exec();
if (answer == QDialog::Accepted) {
auto initializationData = gameDialogs_.creationDialog->initializationData();
initializeGame(initializationData);
startGame(initializationData);
}
}
// Client game
void MainWindow::connectToServer() // User clicked "Connect to server" button
{
const auto& player = mainMenuWidget_->selectedPlayer();
gameDialogs_ = createGameDialogs(this, GameType::Client, player);
auto answer = gameDialogs_.creationDialog->exec(); // At first time it works fine
if (answer == QDialog::Accepted) {
auto initializationData = gameDialogs_.creationDialog->initializationData();
initializeGame(initializationData);
startGame(initializationData);
}
}
Next snippet is code for processing GameStatusChanged signal when game was over:
void MainWindow::gameStatusChanged(GameStatus newStatus)
{
if (newStatus == GameStatus::GameOver) {
auto* gameOverDialog = gameDialogs_.gameOverDialog;
gameOverDialog->setGameResult(gameData_.game->gameResult());
auto gameOverDialogAnswer = gameOverDialog->exec();
if (gameOverDialogAnswer == QDialog::Accepted) {
gameDialogs_.creationDialog->reset();
auto answer = gameDialogs_.creationDialog->exec(); // At this point client cann't receive messages, but server can.
if (answer == QDialog::Accepted) {
auto initializationData = gameDialogs_.creationDialog->initializationData();
initializeGame(initializationData);
startGame(initializationData);
} else {
showMainMenu();
}
} else {
showMainMenu();
}
}
}
I suspect that ClientGameDialog's event loop (is it indeed has it's own event loop) doesn't processes QTcpSocket's events. I tried to replace exec() with open methods for client dialog:
void MainWindow::gameStatusChanged(GameStatus newStatus)
{
if (newStatus == GameStatus::GameOver) {
auto* gameOverDialog = gameDialogs_.gameOverDialog;
gameOverDialog->setGameResult(gameData_.game->gameResult());
auto gameOverDialogAnswer = gameOverDialog->exec();
if (gameOverDialogAnswer == QDialog::Accepted) {
gameDialogs_.creationDialog->reset();
auto d = qobject_cast<ClientGameDialog*>(gameDialogs_.creationDialog);
if (d) {
gameDialogs_.creationDialog->open();
} else {
auto answer = gameDialogs_.creationDialog->exec();
if (answer == QDialog::Accepted) {
auto initializationData = gameDialogs_.creationDialog->initializationData();
initializeGame(initializationData);
startGame(initializationData);
} else {
showMainMenu();
}
}
} else {
showMainMenu();
}
}
}
It works, but I want to find where was the problem. Maybe someone can prompt, where to search a solution. Main questions are: where the difference between Server and Client code flows and why Client code works fine at the first time and breaks at the second.
This is because QDialog::exec is a synchronous blocking operation that will stop the event loop of the main thread and start a new event loop for this dialog. This most of the time is not a problem unless you are doing some continuous work on the main thread such as processing QTCPSocket
Instead of using QDialog::exec use QDialog::open which is asynchronous and does not start a new event loop, you can simply connect signals from the dialog to read the results once the user will accept/close the dialog.
If you require blocking dialogs then you also can simply offload QTcpSocket to another thread and do whole processing asynchronously and only emit required updates to the main GUI thread.

Sleep inside QTConcurrent run method

I'm using QtConcurrent::run to execute some functions in background and not hang the GUI thread. In one function, I read logs from local SQlite database and send them to server by TCP socket.
Now I want to delay the execution after each log so the server has time to save it (TCP response is read in different thread). I'm stuck with Qt4.8 due to implementation limitations (many embeded devices - no chance to upgrade QT on them) and I can't use QThread::sleep(2) because it is protected in 4.8.
Is it possible to somehow pause the execution of thread inside QtConcurrent::run method or should I redesign it to implement my own class inheriting QThread?
void MainWindow::ReportFinishedHUs(bool asyncWork)
{
if(asyncWork == false)
{
QMutexLocker locker(&localDBmutex);
QList<QSqlRecord> HUsToReport = localDB->getHUsForBook();
qDebug() << "HUs to report" << HUsToReport.count();
if(!HUsToReport.isEmpty())
{
Cls_log::WriteDebugLog("HUs to report: " + QString::number(HUsToReport.count()));
foreach (QSqlRecord record, HUsToReport)
{
int _hu = record.indexOf("hu");
int _logTime = record.indexOf("logTime");
QString logTimeString = record.value(_logTime).toString();
QString hu = record.value(_hu).toString();
qDebug() << hu << logTimeString;
// creating message here ...
qDebug() << message;
emit sig_SendTCPMessage(message);
// this is where I need to wait for 2 seconds
QThread::sleep(2);
}
}
}
else
{
QtConcurrent::run(this, &MainWindow::ReportFinishedHUs, false);
}
}
EDIT:
Solved by usleep(2000000) which I somehow discarded for being platform specific... but hey, half of my aplication is platform specific and I only use it in embeded device with constant OS.
Keeping the question open if anyone can suggest more elegand solution using Qt methods. I like to get inspired.

Threading issue where 1 thread works but causes the calling method to not return

I am currently learning C++ and I am having an odd issue with threads. I've done lots of threaded stuff in Java and C# and not had an issue. I am currently trying to replicate a C# library that I have in C++ with a library and a test app.
In main.cpp I create an instance of a InitialiseLibrary class and call the method initialise. This does config loading and is then supposed to start a thread which stays running through the duration the application is running. I am expecting this thread to start, and then my initialise function returns true where I then continue and I create an instance of a test class which writes to a log file every 1 second.
Below is main.cpp
InitialiseLibrary initLibrary("config.ini");
if (!initLibrary.initialise(1))
{
cout << "Failed to initialise library" << endl;
return EXIT_FAILURE;
}
TestClass testClass;
testClass.writeSomeLogsInThread();
cout << "The library config log file is: " << GlobalConfig::GeneralConfig::logFile << endl;
In my Initialise method (which is in the library) I have:
bool InitialiseLibrary::initialise(int applicationAlarmID, int applicationTerminationTimeout)
{
//statusManager.setApplicationStatus(StatusManager::ApplicationStatus::Starting);
if (!this->loadInconfiguration(applicationAlarmID))
{
cout << "*****Failed to configuration. Cannot continue******" << endl;
return false;
}
GlobalConfig::libraryInitialised = true;
LogRotation logRotation;
logRotation.startLogRotation();
BitsLibrary bitsLibrary;
//Set up the signal handler if its needed, 0 means it terminates instantly, doesn't wait -1 is don't use signal handler
if (applicationTerminationTimeout >= 0)
{
bitsLibrary.setupSignalHandler(applicationTerminationTimeout);
}
return true;
}
As you can see, I read in the configuration and I then call `logRotation.startLogRotation().
Where I have the following code:
void LogRotation::startLogRotation()
{
//Is the configuration successfully loaded
if (!LogRotateConfiguration::configurationLoaded)
{
throw exception("Log Rotation not initialised");
}
BitsLibrary bitsLibrary;
stringstream logStream;
logStream << "Log rotation thread starting, monitor cycle time is " << LogRotateConfiguration::archiveSleepTimeInSeconds << " second(s)";
bitsLibrary.writeToLog(logStream.str(), "LogRotation", "startLogRotation");
thread logRotationThread(&LogRotation::logRotationThread, this);
logRotationThread.join();
}
void LogRotation::logRotationThread()
{
BitsLibrary bitsLibrary;
while (bitsLibrary.getApplicationStatus() == StatusManager::ApplicationStatus::Starting || bitsLibrary.getApplicationStatus() == StatusManager::ApplicationStatus::Running)
{
bitsLibrary.writeToLog("Running log monitoring");
this_thread::sleep_for(chrono::seconds(LogRotateConfiguration::archiveSleepTimeInSeconds));
}
stringstream logStream;
logStream << "Log rotation archive monitoring stopped. Current application status: " << bitsLibrary.getApplicationStatus();
bitsLibrary.writeToLog(logStream.str(), "LogRotation", "logRotationThread");
}
Here I am expecting, startLogRotation() to start run the method logRotationThread within a thread, the thread starts, and the startLogrotation() method finishes and backs through the stack to the initialise() method where that returns true, back up to main where I can then call my TestClass method within a thread.
For some reason though, the thread starts and keeps logging every few seconds Running log monitoring so I know the thread has started, yet it doesn't seem to return back to the initialise function to return true, so the app gets stuck on that function call and doesn't go any further.
I've read that you need to run join on the thread to keep it in sync with the main thread, otherwise the main thread exits, while the new threads are running and cause a SIGABRT which in deed it does, but having the join there seems to stop the method returning.
join waits for a thread to finish executing, so when you join, startLogRotation won't return until that happens.
Also, the normal rules of scope and lifetime apply to thread objects - logRotationThread will be destroyed when startLogRotation returns.
If the thread is "joinable" at the time of destruction, it's an error.
The simplest solution is probably to have a LogRotation member in InitialiseLibrary and a thread member in LogRotation.
You can then join the thread in LogRotation's destructor.
Your main thread blocks at this line, waiting for the logRotationThread to end.
logRotationThread.join();
Your main thread should go and do whatever work it needs to do after spawning the other thread, then only when it has nothing left to do should it join() the log rotate thread.
"having an odd issue with threads" - welcome to the world of threading!
I think you mis-understand the stack of the thread you create, and how it will interact with the main thread. The created thread's stack starts at the function you tell it to start with - when that function completes, the thread dies (which will then allow your join() to stop blocking)
With regards to your communication between threads, I think you should have a read about mutex's, conditions and semaphores.
From what I can tell,
logRotationThread is a long running thread (it runs for the entire duration of the application). There is no need for the main thread to ever wait (join) on the logRotationThread.
For starters remove logRotationThread.join().

How to use a process (QProcess) in a new thread (QThread)?

I have the following code:
void Processmethod()
{
QDialog *ProcessMessage = new QDialog;
Ui::DialogProcessMessage Dialog;
Dialog.setupUi(ProcessMessage);
ProcessMessage->setModal(true);
ProcessMessage->setAttribute(Qt::WA_DeleteOnClose);
ProcessMessage->show();
qApp->processEvents();
processmethodONE();
processmethodTWO();
processmethodTHREE();
}
void processmethodONE()
{
QString ProcessCommand = "w8 " + blablubli";
Prozess.setWorkingDirectory(Path); //QProcess "Prozess" is globaly defined
Prozess.setStandardOutputFile(Path); //in my class
QThread* thread = new QThread;
Prozess.moveToThread(thread);
Prozess.start(ProcessCommand);
while(!Prozess.waitForFinished(2000))
{
std::cerr << "Process running " << std::endl;
}
QProcess::ExitStatus Status = Prozess.exitStatus();
if (Status == 0)
{
std::cout << "File created!" << std::endl;
}
}
In this source code I try to open a popup dialog before some processes are starting. problem is that the dialog is not clickable, but on the dialog I want to create a button to abort the running method. As you can see I tried using QThread to run the process(es) in another thread, but still I can't click the dialog. Furthermore if I open my application (GUI) with the "application/x-executable"-file the dialogs content is missing when activating the above shown method. How can I fix these problems? Where am I wrong? greetings
void processmethodONE()
{
QThread* thread = new QThread;
Prozess.moveToThread(thread);
Prozess.start(ProcessComand);
Here you moved the QProcess to another thread. But then you call start() on it. That's already not thread-safe.
while(!Prozess.waitForFinished(2000))
{
std::cerr << "Process running " << std::endl;
}
This blocks and makes using a thread useless. Also, it's not thread-safe.
You should instead not use threads but:
remove the waitForFinished() call
Connect the finished() and error() signals of the QProcess to slots which then start the next step, i.e. processMethodTWO.
I would also advise against reusing QProcess objects and just create a new one for each step.
While I still don't fully understand your recently updated code example, I feel this might be your issue:
while(!Prozess.waitForFinished(2000))
{
std::cerr << "Process running " << std::endl;
}
Wherever you are really calling this in your original code is blocking while waiting for Prozess to finish.
Use a brand new QProcess instance for each one, and connect their finished() signals to a SLOT that will get called when they have finished. Don't manually poll them and block. This will allow you to completely get rid of QThreads altogether.