I am running a Qt app that launches a process. (The Assistant, launched from the main app).
When I close the app, I get the warning
QProcess: Destroyed while process is still running.
How can I get rid of it ?
I saw this similar question and tried to kill... Nothing happened.
This question seems to say maybe I should add waitForFinished()... Help won't close when app does.
Help::Help():m_helpProcess(0) {}
Help::~Help()
{
if (m_helpProcess) {
m_helpProcess.waitForFinished(); // help stays open after app closes
m_helpProcess->kill(); // added with no effect
delete m_helpProcess;
}
}
bool Help::start()
{
if (!m_helpProcess)
process = new QProcess();
QStringList args;
args << QLatin1String("-collectionFile")
<< QLatin1String("mycollection.qhc");
process->start(QLatin1String("Assistant.app"), args);
if (!process->waitForStarted())
return;
}
It should be sufficient to rewrite the destructor using close():
Closes all communication with the process and kills it. After calling this function, QProcess will no longer emit readyRead(), and data can no longer be read or written.
Help::~Help()
{
if (m_helpProcess) {
// m_helpProcess->waitForFinished(); // help stays open after app closes
m_helpProcess->close(); // close channels
delete m_helpProcess; // destructor actually kills the process
}
}
Related
Introduction
I am new to multithreading. I have an created a GUI application in c++ on windows 10 using wxWidgets. A worker thread communicates in the background via sockets and appends events to some event handlers from the main thread (GUI); I have two available protocols for the communication, and I allow the user to choose among those at the beginning (before instantiating the thread). To append events, I have a few shared pointers on the event handlers; a bit like in this tutorial, where it keeps a pointer of the m_parent. To communicate with the working thread, I created a queue using a mutex and semaphore, also like in the tutorial. For now, everything works.
Now, I want to let the user change the protocol while the application runs. I have thus a menu with a related button. This button calls the following function (create_app()) that deletes the top window and the communication thread and recreates everything. That is where I have problems.
bool cApp::create_app() {
wxWindow* main_window = GetTopWindow();
// If there is already a window, delete everything before creation
if(main_window) {
//thread_communication->Wait(); // Should I wait for the thread to finish? I shouldn't
if (thread_communication->Delete() != wxTHREAD_NO_ERROR) { // Should I delete it since it should be destroyed when Entry is finished
std::cerr << "DELETE IMPOSSIBLE!" << std::endl;
return false;
}
if (main_window->Destroy() != wxTHREAD_NO_ERROR) { // Here I can't delete the window
std::cerr << "DELETE WINDOW IMPOSSIBLE!" << std::endl; // prints
return false;
}
SetTopWindow(NULL);
}
// Create everything
m_frame1 = new MainFrame(*this);
SetTopWindow(m_frame1);
m_frame1->Show();
std::vector<std::shared_ptr<wxEvtHandler>> evt_handlers;
m_frame1->get_handlers(evt_handlers);
// Construct the working thread with the evt handlers and the queue from the main window
thread_communication = std::unique_ptr<ThreadCom>(new ThreadCom(evt_handlers, m_frame1->get_queue());
wxThreadError err = thread_communication->Create();
...
err = thread_communication->Run();
...
return true;
}
bool cApp::OnInit() {
return create_app();
}
When the user click the related button, it calls this create_app function but before sends a task via the queue to the working thread with the highest priority (meaning it will be the next task achieved by it); afterwards, in the working thread, it exits after poping the task the while loop in Entry. Thus, the working thread should terminate there.
Problem
I have problem when deleting the thread and the application. I have memory leaks since, I guess, they share common material (pointers).
When I try to use Wait, I have first a assert !IsDetached. That is because I cannot wait for a detached thread. If I continue, the programm crashes (Access violation reading location) during the thread->Delete() call.
If I don't use Wait, the thread->Delete() call works fine. Afterwards, the program crashes at main_window->Destroy(); the return code is not ok thus it prints "DELETE WINDOW IMPOSSIBLE" with an Unhandled exception thrown: read access violation message.
So, how should I proceed? Should I implement destructors in the classes? How can I delete the thread and the app safely?
Thanks in advance for your help.
I'm trying to use QProcess inside a thread to do some operations (read I2C connections).The update method is calling every 100 msec:
void TempsReader::update()
{
if (_currProcess == nullptr) {
_currProcess = new QProcess();
connect(_currProcess, &QProcess::errorOccurred, this, &TempsReader::onProcessError);
connect(_currProcess, SIGNAL(finished(int,QProcess::ExitStatus)),
this, SLOT(onProcessFinished()));
}
_currProcess->start("sh");
if (_currProcess->waitForStarted()) {
_currProcess->write("i2cdetect -y 1");
_currProcess->closeWriteChannel();
_currProcess->waitForFinished();
}
}
After some time, the process gives "FailedToStart" error and never starts again.
void TempsReader::onProcessError(QProcess::ProcessError error)
{
qDebug() << error;
_currProcess->close();
}
void TempsReader::onProcessFinished()
{
QString devs = _currProcess->readAll();
_currProcess->waitForFinished();
// doing some stuff with devs
_currProcess->close();
}
How can I fix this issue? Am I using QProcess in a wrong way? and how can I start the process again when it drops in error slot. Thanks in advance.
Update: QProcess::errorString() gives this: "Resource error (fork failure): Too many open files"
UPDATE: Finally I've found the issue and it was not related to QProcess itself. It was relating to I2C connection.
My guess is that you get the failure because all your update() calls share the same QProcess object.
What happens here is that when you call update(), you start the process. And 100ms later, you call it again without ensuring that the previous update() has finished to wait the end of the process.
The consequence is that you try to start an already started process and thus it fails.
For me, the easiest solution is to create one QProcess object for each update() call.
Something like:
void TempsReader::update()
{
QProcess * current_process = new QProcess;
connect(current_process, &QProcess::errorOccured, this, &TempReader::onProcessError);
connect(current_process, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this, &TempReader::onProcessFinished());
current_process->start("sh"); // Your command
current_process->waitForStarted();
current_process->write("i2cdetect -y 1");
current_process->waitForFinished();
current_process->deleteLater();
}
Or without pointers:
void TempsReader::update()
{
QProcess current_process;
connect(¤t_process, &QProcess::errorOccured, this, &TempReader::onProcessError);
connect(¤t_process, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this, &TempReader::onProcessFinished());
current_process.start("sh"); // Your command
current_process.waitForStarted();
current_process.write("i2cdetect -y 1");
current_process.waitForFinished();
}
As you did not show the calling part of the code (the thread creation, the 100ms loop, ...), this may not be the solution you need.
In this case, please let me know if it does not solve your issue so that I'll remove this answer.
Finally I've found the issue and it was not related to QProcess itself. It was relating to I2C connection. I was using this command in update: wiringPiI2CSetup(addr); and it opens a new device each time.
I'm a newbie in using QT
Code :
void MainWindow::test()
{
ui->label->setText("it worked !");
proc->start("c:/windows/system32/calc.exe");
}
void MainWindow::on_pushButton_clicked()
{
proc = new QProcess();
connect(proc,SIGNAL(finished(int,QProcess::ExitStatus)),this,SLOT(test()));
proc->start("c:/windows/system32/notepad.exe");
}
So it starts notepad when I close it, calc is started but it keeps opening infinitely.
What's wrong in my code?
Since you are not disconnecting the slot, the moment "calc.exe" exits it will be launched again.
I.e. when notepad.exe finishes, the signal triggers the test() slot which runs "calc.exe".
When "calc.exec" finished, the very same series of events happens again.
I'm using the QT Creator on Ubuntu.
I have GUI with a mainwindow and another window called "progress".
Upon clicking a button the QProcess starts and executes an rsync command which copies a folder into a specific directory. I created a textbrowser which reads the output from the rsync command. Also clicking the button causes the "progress" window to pop up.
So far so so good, now my problem.
Instead of showing the rsync output in my mainwindow i want it to be in progress.
I've tried several methods to get the QProcess into the progress via connect but that doesn't seem to work.
mainwindow.cpp
void MainWindow::on_pushButton_clicked()
{
if (ui->checkBox->isChecked()
)
m_time ="-t";
QObject parent;
m_myProcess = new QProcess();
connect(m_myProcess, SIGNAL(readyReadStandardOutput()),this, SLOT(printOutput()));
QString program = "/usr/bin/rsync";
arguments << "-r" << m_time << "-v" <<"--progress" <<"-s"
<< m_dir
<< m_dir2;
m_myProcess->start(program, arguments);
}
progress.cpp
void Progress::printOutput()
{
ui->textBrowser->setPlainText(m_myProcess->readAllStandardOutput());
}
I know it's pretty messy iv'e tried alot of things and haven't cleaned the code yet also I'm pretty new to c++.
My goal was to send the QProcess (m_myProcess) to progress via connect but that didn't seem to work.
Can you send commands like readyReadAllStandardOutput via connect to other windows (I don't know the right term )?
Am I doing a mistake or is there just another way to get the output to my progress window ?
m_myProcess is a member of the class MainWindow and you haven't made it visible to the class Progress. That's why you have the compilation error
m_myProcess was not declared in this scope
What you could do:
Redirect standard error of m_myProcess to standard output, such that you also print what is sent to standard error (unless you want to do something else with it). Using
m_myProcess.setProcessChannelMode(QProcess::MergedChannels);
Make the process object available outside MainWindow
QProcess* MainWindow::getProcess()
{
return m_myProcess;
}
Read the process output line by line in Progress. It needs to be saved in a buffer because readAllStandardOutput() only return the data which has been written since the last read.
... // somewhere
connect(window->getProcess(), SIGNAL(readyReadStandardOutput()), this, SLOT(printOutput())
...
void Progress::printOutput(){
//bigbuffer is member
bigbuffer.append(myProcess->readAllStandardOutput();)
ui->textBrowser->setPlainText(bigbuffer);
}
I've a dialog displaying progress bar + some other data, and I also have a cancel button on this dialog. While this dialog is displayed there is potentially heavy computation going on, which is show on progress bar. This computation is started from withing this dialog code so I have:
Counting_Progress_Dialog::Counting_Progress_Dialog(QWidget *parent) :
QDialog(parent)
{
setupUi(this);
thread_ = new Threaded;//THIS IS THE THREAD IN WHICH COMPUTATION IS BEING PERFORMED
connect(thread_,SIGNAL(counter_value(int)),this,SLOT(update_progress_bar(int)));
connect(this,SIGNAL(rejected()),thread_,SLOT(terminate()),Qt::QueuedConnection);//
HERE I'M CONNECTING REJECTED ON DIALOG TO TERMINATE ON THREAD
}
void Counting_Progress_Dialog::start()
{
thread_->start(QThread::LowestPriority);
}
and I do invoke this in part of the program:
void My_Class::dummy_()
{
auto old_priority = this->thread()->priority();
this->thread()->setPriority(QThread::HighestPriority);
Counting_Progress_Dialog progress;
progress.start();//this will start thread
progress.exec();//this will enter it's event loop
progress.wait();//this will wait until thread is finished
this->thread()->setPriority(QThread::NormalPriority);
}
But despite all this, when I press cancel on my dialog, the whole application freezes. What am I doing wrong? How to make it behave correctly?
UPDATED:
void Counting_Progress_Dialog::wait()
{
thread_->wait();
}
I see that you are connecting using 2 different strategies. But if thread_ and this(counting dialog) are really within two separated threads then the connection will always be Qt::QueuedConnection. Well that's not the issue.
progress.exec();//this will enter it's event loop
Calling exec() suspend the execution of dummy_() until the dialog have to return. And when the dialog return your thread is terminated. So I don't see the purpose of
progress.wait();//this will wait until thread is finished
By the way which function is that? the only one I know is Qthread::wait(). I am pretty confident the issue is here...
edit:
progress.wait() is not the issue... But it is possible that the events sent by the thread are causing trouble in some way. Use the debugger or some qDebug() to see if update_progress_bar is called after you push cancel.