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...
Related
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.
I have a Qt application, where, among other things, there is a function render which goes through a list of objects and creates for each an according (subclassed) QGraphicsPathItem which it then puts as a child in a (subclassed) QGraphicsScene. (It is done in the code below through a visitor GGObjConstructor which gets initialized with the variable scene which is the scene where the items are to be added to).
XTimer timer;
timer.start();
gobjlist gobjlis = ogc._gobjectlis;
GGObjConstructor ggoc(scene, z_value, bkground_color);
for(auto obj: gobjlis) {
obj->exec( &ggoc );
}
timer.stop();
My class XTimer is used in an obvious way to measure the time for this proceeding.
Now the problem is: Only the time spent in the loop where all the items are prepared and inserted into the scene is measured by timer. For a typical example with ~165000 items this gives about 7.5 sec as timer-value at reaching timer.stop(). But the application is after these 7.5 sec still frozen, with the screen-window where the scene is to by displayed yet invisible and only after about 25 sec (hand-stopped) suddenly the display window appears with all the items to be displayed.
Now of course I would like to measure these "freeze time" (or time till the application becomes responsive again, or maybe time till display window appears). But I found no way to do this although I looked some time through stackoverflow or the net in general. The best hint I found was
stackoverflow question
The answer there seemed to imply, that it would be not really simple to achieve (overriding paintEvent method and such things).
Question: Is this true? Or is there a simple way to measure the time till the application becomes responsive again/the image is really displayed?
I had a similar problem with an application once, where I wanted to measure the time the app freezes to figure out with logging what was causing these freezes. What I came up was to measure how long the Eventloop of the mainthread was not responding, because this directly corresponds to a frozen app.
The basic idea is to not run a QApplication but inherit from QApplication and override the notify() function. Some apps do this anyway to catch exceptions which would otherwise break the eventloop. Here is some pseudo-code which should bring the idea across:
bool MyApplication::notify( QObject * receiver, QEvent * event )
{
// something like storing current time like:
// auto start = std::chrono::system_clock::now();
// auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(start - end );
// if( elapsed.count() > 1000 ){
// log something like: Mainthread responds again after <elapsed> seconds
// }
// Note that end must be a member variable
// end = start;
return QApplication::notify(receiver, event);
}
Note 1: If your app does not continuesly run through notify() you can for testing purposes introduce a dummy QTimer which triggers faster than the logging time threshold.
Note 2: if you use multiple threads, esp. QThreads it could be necessary to filter the receiver object and perform that code only if the reciever is in the mainthread.
With this code you can log every freeze of the main-thread (frozen GUI) and determine the length of the freeze. With proper logging you can figure out whats causing the freezes.
Keep in mind that this will only log after the freeze has resolved!
Addition:
It is more complicated and slow, but for debugging/investigation purposes you can store the last event and Object-Tree of the reciever and log that as well. Than you even know which was the last event which triggered the freeze and the recieving Object.
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;
}
I get continuous answers from a server with a delay from 1 sec. I append() this answers to an QTextEdit field. But the changes first displayed when the method call has finished.
How can I displayed the changes immediately?
I have tryed update() but it doesent work..
void ClientWidget::setAnswer(ValueStream *resultStream){
std::vector<std::string> answer;
for(int i = 0; i < 15; i++){
value tmpResultValue;
if(resultStream->get(tmpResultValue)){
this->client.parseResult(tmpResultValue, answer);
std::vector<QString> qAnswer = vectorStringToVectorQstring(answer);
for (unsigned int n = 0; n < qAnswer.size(); n++){
this->answerTextEdit->append(qAnswer[n]);
}
this->answerTextEdit->update();
}
answer.clear();
}
resultStream->close();
delete resultStream;
}
after this->answerTextEdit->append(qAnswer[n]); the changes should displayed but they dont displayed immediately
When you set the TextEdit widget's text, a signal is emitted that it has changed, in order for the widget to update what you see. That signal is placed in a queue of messages that are processed as events in the Qt event loop.
While you're processing the incoming data, Qt's event loop cannot continue until you finish. An easy, but not the best way of handling this is to call QApplication::processEvents to allow the event loop to run; this can be very inefficient as all events in the queue are processed.
A better way to handle time-consuming processing is to move it onto a new thread, which is reasonably easy to do with QThread. That way, you can process the incoming requests from the server and emit a signal from your thread to the main thread, which can then update the TextEdit widget.
To understand how to use QThread, I suggest reading this article. It really isn't difficult to do and I recommend you try it, rather than adding a call to QApplication::processEvents.
You can call
QApplication::processEvents();
after calling your method.
if you can change ValueStream to emit a signal each time a new value is available with the value as a parameter, then it will become much easier. just connect a slot to it where you append the answer to the answerTextEdit
I have a class derived from QThread: class MyClass : public QThread. In the run method I have "my own" message loop:
run() {
// exec(); // while not reached
while (_runMessageLoop && ...) {
hr = CallDispatch(.....);
if (hr== 0) QThread::msleep(100);
// QCoreApplication::processEvents(); // Does not work
}
}
Since exec() is not executed, I have no Qt event loop. This obviously causes signal / slots not to work correctly. Is there any chance to combine the Qt and my own message loop? Or do I need a frequently firing timer in order to do what I have accomplished in my infinite loop?
The right way "Qt-wise" is to use a timer and let Qt manage the event loop.
If you need to depend on external things, you can use things like QAbstractSocket to send events when data comes in over an external socket, eg.
This is not really the answer for implementing the event loop correctly, I'm fairly sure there is a way, but more of a workaround:
Start the thread normally, exec() and all, and connect the start signal to a slot (make sure it gets called in the right thread), then put your loop there, and call Qt's processEvents() in that loop. That makes sure Qt event loop gets properly set up.