How to express the delayed operation of QProcess with QProgressBar? [duplicate] - qt

This question already has an answer here:
How to show progress bar while saving file to excel in python?
(1 answer)
Closed 2 years ago.
I use QProcess to call the Ping command, and use waitForFinished to wait for the end of the command execution.
Now I want to use a QProgressBar progress bar connect_gress to indicate that the range is (0, 100) in the initial state, and it becomes (0, 0) when QProcess starts to start, which is the busy state, and the command called by QProcess ends. Time to change back to (0, 100)
void web::gress_begin(){
ui->connect_gress->setRange(0, 0);
}
void web::gress_finish(){
ui->connect_gress->setRange(0, 100);
ui->connect_gress->setValue(100);
}
This is part of QProcess
QProcess excping;
connect(&excping, SIGNAL(started()), this, SLOT(gress_begin()));
connect(&excping, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(gress_finish()));
excping.start(cmdstr);
excping.waitForStarted();
excping.waitForFinished(-1);
But when the program is running, I press the button, the connect_gress on the main interface does not enter the busy state, and until the command ends, the value of connect_gress will become 100, that is, gress_finish is executed and successfully displayed, and gress_start is also It was executed, but it did not show up successfully.

QProcess::waitForFinished blocks the current thread. And I believe you are calling it in main tread aka GUI thread. So main thread never has a change to process events including paintEvent. Also GUI is frozen while ping process running. So user can't do anything at this time, I don't know if it is your intend or not.
Easiest way to solve it not waiting for finish at all. But in this case user can play around with ui while pinging. excping is a stack variable so it might die before emitting finished in this case. We can create it in heap and delete after the finished signal.
auto excping = new QProcess(this);
connect(excping, SIGNAL(started()), this, SLOT(gress_begin()));
connect(excping, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(gress_finish()));
connect(excping, SIGNAL(finished(int, QProcess::ExitStatus)), excping, SLOT(deleteLater()));
excping->start(cmdstr);
excping->waitForStarted();
// no waitForFinished
Another easy way to solve with blocking is forcing event loop to process events. I am not a favor of processEvents. Documentations also discourage to use that. So please don't make it habit to toss around like a some sort of magic sauce.
void web::gress_begin(){
ui->connect_gress->setRange(0, 0);
QCoreApplication::processEvents();
}
Also you can tweak around with the execution delay of functions. Checkout Qt::QueuedConnection and QMetaObject::invokeMethod.

Related

Forking with QT

using QtCreator to make a loftier interface to a sofware.
There is basically a set of buttons to tune and inputs, a start and stop job
buttons, my problem comes from an infinite loop that freezes the display so I came up with using fork() so that the loop have to compete with the main program instead of eating up the whole resources (no multithreading), but the program crashes spiting:
[xcb] Unknown sequence number while processing queue
[xcb] Most likely this is a multi-threaded client and XInitThreads has not
been called
[xcb] Aborting, sorry about that.
a.out: ../../src/xcb_io.c:274: poll_for_event: Assertion
`!xcb_xlib_threads_sequence_lost' failed.
the fonction calling the loop is called 'ON', 'OFF' is supposed to exit the forked process.
//in button 'ON' func
ps = fork();
if(getpid() == ps)
{
while(1)
{
strcpy(word, charset(minlength, maxlength, N));
ui->pass->setText(word);//operation on the display
....SNIP
}
}
//In button 'OFF' func
if(getpid() == ps)
exit(0);
I'm really asking what is the correct way of starting a while(1) and be able to break, exit, or return from it while not freezing the window using QT, thanks.
You crash probably here:
ui->pass->setText(word);//operation on the display
as in Qt, you can not change UI from non UI threads directly. Only from signals and slots mechanism.
The proper way to not freeze UI is obviously to compute lengthy operations in another thread.
You can achieve this in several ways:
One is by sub-classing QObject class to create 'worker object' which would perform all heavy operations. You create new QThread object that should live as long as you need your object. And use QObject::moveToThread method to move created object to new thread. To control your worker object you should send signals from object and call it's slots also via signal-slot mechanism. If you call them directly - they will be executed in caller thread (so do not perform stuff like worker->startHeavyJob(); in UI thread). Instead emit signal in UI (emit sigStartHeavyStuff();) and connect it to slot of your worker object (slotDoHeavyStuff();)
if you do not want to bother with this (if operation is pretty small)
- you can use QApplication::processEvents() to process events in UI event loop while going in your infinite while loop.
Another way is to use QtConcurrentRun framework to run function in separate thread which manages itself. Threads are taken from thread pool and are managed by Qt. This approach looks like something you want to do. Though you still will be able to access UI objects only through signals and slots.
I see one big issue in the presented code that is causing your freeze: You never let Qt process anything while you are in the loop. You need to allow Qt to run it's event loop. The easiest way is to use QApplication::processEvents() inside the loop.
I'm also not a fan of a while(1) loop for a couple of reasons. The first of which is that it can eat machine cycles waiting for things to happen. I doubt you really need to run the code as fast as possible, you could probably get away with some sleeping in your loop.
The other issue is that it is hard to break out. A cleaner approach would be something like this
void MyClass::on_pushButton_ON_clicked()
{
MyClass::done = false; // this is a class attribute
while (!MyClass::done) {
QApplication::processEvents();
//...
}
}
void MyClass::on_pushButton_OFF_clicked()
{
MyClass::done = true;
}

How to know when process gets killed in unix

I am working on an Spark project which has an executable which starts the process in background , now I need to know when the process gets killed through Bash.The only solution I got is to continuously check its PID existence through grep in loop if exist or not , Is there any other possible solution that do it without looping around .
I also tried handling the exit codes ,but the exit code is 0 if process runs and only changes when it gets killed which need to be checked continuously
If I understand right, the situation is this:
Your executable does somewhere fork and run another logic in another process, and you want the main process to get notify whether the forked process is still running.
In order to answer I'll make sure we both are familiar with the same terminology:
The main process (when you run your executable) is called the parent process.
The process that the parent process is starting, is called the child process.
Now when a child process dies a signal SIGCHLD is sent to the parent process, Normally SIGCHLD is mapped to SIG_DFL which is actually SIG_IGN for SIGCHLD. In human words, it means that the parent process does not give a shit if the child process dies.
But, If you want to get notify, you may change the behaviour of what happens when SIGCHLD is sent. In order to do so you must implement signal handler function:
void custom_sigchld_handler(int sig)
{
pid_t p;
int status;
while ((p = waitpid(-1, &status, WNOHANG)) != -1)
{
// If you got here it means p is a dead process. Do your logic here...
}
}
Then you must declare that you want our custom_sigchld_handler to take care of SIGCHLD signals:
struct sigaction signal_action = {0};
signal_action.sa_handler = custom_sigchld_handler;
sigaction(SIGCHLD, &signal_action, NULL);
May need a little more context... if your executable is starting the process, there is surely a way to get or keep a handle on it while executing.
In php, there is proc_open(), in Qt there is the QProcess class, in c/c++, there is popen()
Many languages have this feature, but without more details it's hard to offer a solution.

Reliably showing a "please wait" dialog while doing a lengthy blocking operation in the main Qt event loop

I've got a Qt app that needs to call an expensive non-Qt function (e.g. to unzip a ~200MB zip file), and since I'm calling that function from the main/GUI thread, the Qt GUI freezes up until the operation completes (i.e. sometimes for 5-10 seconds).
I know that one way to avoid that problem would be to call the expensive function from a separate thread, but since there isn't much the user can do until the unzip completes anyway, that seems like overkill. I can't add processEvents() calls into the expensive function itself, since that function is part of a non-Qt-aware codebase and I don't want to add a Qt dependency to it.
The only thing I want to change, then, is to have a little "Please wait" type message appear during the time that the GUI is blocked, so that the user doesn't think that his mouse click was ignored.
I currently do that like this:
BusySplashWidget * splash = new BusySplashWidget("Please wait…", this);
splash->show();
qApp->processEvents(); // make sure that the splash is actually visible at this point?
ReadGiantZipFile(); // this can take a long time to return
delete splash;
This works 95% of the time, but occasionally the splash widget doesn't appear, or it appears only as a grey rectangle and the "Please wait" text is not visible.
My question is, is there some other call besides qApp->processEvents() that I should also do to guarantee that the splash widget becomes fully visible before the lengthy operation commences? (I suppose I could call qApp->processEvents() over and over again for 100mS, or something, to convince Qt that I'm really serious about this, but I'd like to avoid voodoo-based programming if possible ;))
In case it matters, here is how I implemented my BusySplashWidget constructor:
BusySplashWidget :: BusySplashWidget(const QString & t, QWidget * parent) : QSplashScreen(parent)
{
const int margin = 5;
QFontMetrics fm = fontMetrics();
QRect r(0,0,margin+fm.width(t)+margin, margin+fm.ascent()+fm.descent()+1+margin);
QPixmap pm(r.width(), r.height());
pm.fill(white);
// these braces ensure that ~QPainter() executes before setPixmap()
{
QPainter p(&pm);
p.setPen(black);
p.drawText(r, Qt::AlignCenter, t);
p.drawRect(QRect(0,0,r.width()-1,r.height()-1));
}
setPixmap(pm);
}
Moving to another thread is the correct way to go but for simple operations, there's a much less complicated way to accomplish this without the pain of managing threads.
BusySplashWidget splash("Please wait…", this);
QFutureWatcher<void> watcher;
connect(&watcher, SIGNAL(finished()), &splash, SLOT(quit()));
QFuture<void> future = QtConcurrent::run(ReadGiantZipFile);
watcher.setFuture(future);
splash.exec(); // use exec() instead of show() to open the dialog modally
See the documentation about the QtConcurrent framework for more information.

qt qthread in a for loop

i got problem when i trying to run a working thread in a for loop.
my code is something like this:
connect(&myworkingthread,SIGNAL(updataprocess(int)),processbar2,SLOT(setValue(int)));
for (int i=0;i<n;i++){
// each individual data will be loaded in this part...
myworkingthread.start();// this thread will take 5 secs to finish, a signal is
// also emitted to show the process of this thread(processbar2).
// after working thread, the processed data will be saved...
processbar1->setValue(i); // processbar is used to show the processing process
//problem of this code: data is totally wrong because the next thread will start before the last one finish.
}
i also wants to show the process of myworkingthread which is supposed to be implemented by
signal and slot. if i use the above code, the data is totally wrong. because the second thread will starts before the first one finish.
then i change my code like this:
connect(&myworkingthread,SIGNAL(updataprocess(int)),processbar2,SLOT(setValue(int)));
for (int i=0;i<n;i++){
// each individual data will be loaded in this part...
myworkingthread.start();// this thread will take 5 secs to finish
// signal is also emitted to show the process of this thread(processbar2).
myworkingthread.wait();// i will wait the thread until it finish
// after working thread, the processed data will be saved...
processbar1->setValue(i); // processbar is used to show the processing process
}
the problem of this code is that processbar of the thread is not working until the for loop goes through all the files.
is there any way to make the thread process in a for loop?
You need to call
QApplication::processEvents();
see Simple example using QProgressDialog: Any ideas why this doesn't work properly?
As far as I remenber, this should be called after the "emit" line in your thread...

How to run a synchronous chain of QProcess without freezing the gui?

I want to optimize images with the help of some external programs. The programs must be executed one after one, some of them depending on the output of the last program, some of them depending on the characteristics of the image.
I know, I can use signals, but this is not very useful here, because I would have to use dozens of functions and signals for each and every external program, some of them even multiple times depending on the position, where a program is called in the chain. It would be much easier to execute them one by one. But if I do so, the gui freezes immidiately even without displaying the status message until all processes are finished. What else can I do?
ui->status->setText("Do something with program 1");
QProcess *proc1 = new QProcess;
proc1->setReadChannelMode(QProcess::SeparateChannels);
proc1->start("program 1 -args", QIODevice::ReadWrite);
while(!proc1->waitForFinished(10))
;
ui->status->setText("Do something with program 2");
QProcess *proc2 = new QProcess;
proc2->setReadChannelMode(QProcess::SeparateChannels);
proc2->start("program 2 -args", QIODevice::ReadWrite);
while(!proc2->waitForFinished(10))
;
In this case using Signals is the "correct" way. You just need to chain them.
[...]
ui->status->setText("Do something with program 1");
proc1.setReadChannelMode(QProcess::SeparateChannels);
proc1.start("program 1 -args", QIODevice::ReadWrite);
connect(proc1, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(finishedProc1()))
[...]
void finishedProc1()
{
ui->status->setText("Do something with program 2");
proc2.setReadChannelMode(QProcess::SeparateChannels);
proc2.start("program 2 -args", QIODevice::ReadWrite);
connect(proc2, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(finishedProc2()))
}
void finishedProc2()
{
[...]
}
Alternativly you could call processEvents in QApplication while you are waiting or do the whole thing in a different QThread.
Create worker thread (several threads, if you need parallel processing) and do waiting there. GUI will not be blocked then.

Resources