I have a QFutureWatcher connected to a QProgressBar, the code is running but never show me the progress of the progressBar
QProgressDialog progress;
QFutureWatcher<void> watcher;
connect(&watcher, SIGNAL(finished()),&progress, SLOT(reset()));
connect(&watcher, SIGNAL(progressRangeChanged(int, int)),&progress, SLOT(setRange(int,int)));
connect(&watcher, SIGNAL(progressValueChanged()),&progress, SLOT(setValue(int)));
QFuture<int> file = QtConcurrent::run(aFunction, cmd);
watcher.setFuture(file);
progress.exec();
watcher.waitForFinished();
int aFunction(string cmd){
int status;
status= system(cmd.c_str());
return status;
}
this is the code I have, the result just show me a full progress bar, I want to see from 0 to 100
Related
I am trying to close a QDialog using a timeout from a QTimer.
So far, i have tried to do this :
QDialog dlg;
..
..
myTimer.start(60000); // 60 s
connect(&myTimer, SIGNAL(timeout()),
&dlg, SLOT(close())));
dlg.exec();
qWarning() << "---timer expired or key pressed--";
But when timeout is triggered and the close slot executed the eventloop is not exited. Same behavior with reject slot. I know the done slot should have the expected behavior but as it needs an extra argument (int r), it cannot be directly connected to the timeout() signal.
Of course, i can "relay" the timeout signal to provide the missing argument but is there another more straightforward way to do it ?
Thank you.
dlg.exec(); Is a synchronic, He returns the answer accepted or rejected.
void MainWindow::btnClicked() {
Dialog *dialog = new Dialog();
dialog.exec();
qDebug() << "test";
// while dialog not destroyed (rejected, accepted) Print will not happen never.
}
One way you can use QTimer in your Dialog class:
Dialog::dialog(...) {
//constructor
QTimer::singleShot(60000, this, SLOT(close()));
}
or do not use dialog.exec(); use dialog.show(); if you want dialog let it be modality you can use:
void MainWindow::btnClicked() {
Dialog *dialog = new Dialog();
dialog->setModal(true);
dialog->show();
qDebug() << "test"; //The "test" will be printed, and you can use QTimer :))
}
I suggest to give the dialog its own timer (i.e. instantiate a QTimer locally, before excuting the dialog):
QTimer dlg_timer;
dlg_timer.start(60000); // 60 s
connect(&dlg_timer, SIGNAL(timeout()), &dlg, SLOT(close()));
dlg.exec();
dlg_timer.stop();
As the OP fears in their comment, if the timer timeout signal has been connected to some other slot, before connection with dialog close, and in that slot QTimer::disconnect() is called, the dialog close slot will never be called.
Consider this SLOT, in my main thread, trigged by a button, which takes a list of QTreeWidgetItem from a QTreeWidget. It uses a QtConcurrent::map() call to execute a long task. The watcher connects to a QProgressDialog to show progress.
void Main::on_actionButton_triggered() {
qRegisterMetaType<QVector<int> >("QVector<int>");
//Setting up a progress dialog
QProgressDialog progressDialog;
//Holds the list
QList<QTreeWidgetItem*> list;
//Setup watcher
QFutureWatcher<void> watcher;
//Setting up connections
//Progress dialog
connect(&watcher, SIGNAL(progressValueChanged(int)), &progressDialog, SLOT(setValue(int)));
connect(&watcher, SIGNAL(progressRangeChanged(int, int)), &progressDialog, SLOT(setRange(int,int)));
connect(&watcher, SIGNAL(progressValueChanged(int)), ui->progressBar, SLOT(setValue(int)));
connect(&watcher, SIGNAL(progressRangeChanged(int, int)), ui->progressBar, SLOT(setRange(int,int)));
connect(&watcher, SIGNAL(finished()), &progressDialog, SLOT(reset()));
connect(&progressDialog, SIGNAL(canceled()), &watcher, SLOT(cancel()));
connect(&watcher, SIGNAL(started()), this, SLOT(processStarted()));
connect(&watcher, SIGNAL(finished()), this, SLOT(processFinished()));
//Gets the list filled
for (int i = 0; i < ui->listTreeWidget->topLevelItemCount(); i++) {
list.append(ui->listTreeWidget->topLevelItem(i));
}
//And start
watcher.setFuture(QtConcurrent::map(list, processRoutine));
//Show the dialog
progressDialog.exec();
}
extern void processRoutine(QTreeWidgetItem* item) {
qDebug() << item->text(4);
}
I also added, in the UI (which holds all the previous widgets), a QProgressBar with the same SIGNALS/SLOTS. Keeping the code like the previous works as expected: the progress dialog shows up, and the progress bar updates exactly as the dialog.
Instead, if I comment the
//progressDialog.exec();
or I hide the dialog in some way, the process crashes (not always, sometimes it goes well). Looking at qDebug() << item->text(4); it crashes and the output shows randomly mixed text (they are supposed to be filenames). Also, the progressbar does not update itself when the QProgressDialog is not shown, even if the computation does not crash.
Note: I experienced a similar problem before in another function, and I solved it by setting
QThreadPool::globalInstance()->setMaxThreadCount(1);
on Windows only, OSX was ok.
So, what's the trick behind the QProgressDialog that makes all the things right? Is there a way I can use the QProgressBar instead of the QProgressDialog?
NOTE
This is the output when the process completes without troubles:
"C:/Users/Utente/Pictures/Originals/unsplash_52cee67a5c618_1.jpg"
"C:/Users/Utente/Pictures/Originals/photo-1428278953961-a8bc45e05f72.jpg"
"C:/Users/Utente/Pictures/Originals/photo-1429152937938-07b5f2828cdd.jpg"
"C:/Users/Utente/Pictures/Originals/photo-1429277158984-614d155e0017.jpg"
"C:/Users/Utente/Pictures/Originals/photo-1430598825529-927d770c194f.jpg"
"C:/Users/Utente/Pictures/Originals/photo-1433838552652-f9a46b332c40.jpg"
When you comment progressDialog.exec();
your on_actionButton_triggered() function finishes with destroying progressDialog so your signals are using pointers to nowhere.
Also watcher is being destroyed too before or after all mapping is performed, and afair it does not stop threads so they also working with nowhere.
I was for hours trying to reduce this error to a minimal example, but could not succeed.
I have a GUI with a QPushButton importPCDButton. It should open a QFileDialog and import the selected file. I want to use a thread hoping that the dialog vanishes as soon as I send an update signal to my GUI. So I have an ImportPCD worker class derived from QObject implementing a public slot with the name process(). Inside this, I open the QFileDialog, etc:
void ImportPCD::process() {
std::cout << "foo \n";
QString file_abs = QFileDialog::getOpenFileName(
&*(getControl()->getGuiPtr()),
tr("OpenFile"),
"../data/",
tr("Point Cloud File(*pcd);;ASCII - File(*.asc);;All Files(*)"));
int index = file_abs.lastIndexOf("/");
int size = file_abs.size();
int position = size-index-1;
QString file = file_abs.right(position);
index = file.indexOf(".");
file = file.left(index);
getControl()->setTreeID(file.toStdString());
QString abort;
if(file!=abort)
{
this->path = file_abs.toStdString();
cloudRGB = import ();
emit updateUI();
computeNormals (cloudRGB);
emit updateUI();
principalCurvatures = computeCurvature ();
setRGB ();
getControl()->setCloudPtr(getCloudRGB());
}
emit finished();
}
It also implements the slots updateUI() and finished().
I have a public slot importPCDFile() in my GUI:
void PCLViewer::importPCDFile() {
boost::shared_ptr<QThread> thread_ptr (new QThread);
boost::shared_ptr<ImportPCD> importWorker (new ImportPCD(control));
importWorker->moveToThread(&*thread_ptr);
// connect (ui->importPCDButton , SIGNAL(clicked()), &*thread_ptr, SLOT(start()));
connect (&*thread_ptr, SIGNAL(started()), &*importWorker, SLOT(process()));
// connect (ui->importPCDButton , SIGNAL(clicked()), &*importWorker, SLOT(process()));
connect (&*importWorker, SIGNAL(updateUI()), this, SLOT(updateUI()));
connect (&*importWorker, SIGNAL(finished()), this, SLOT(updateUI()));
connect (&*importWorker, SIGNAL(finished()),&*thread_ptr, SLOT(quit()));
// connect (&*importWorker, SIGNAL(finished()),&*thread_ptr, SLOT(quit()));
connect (&*thread_ptr, SIGNAL(finished()), &*thread_ptr, SLOT(deleteLater()));
// connect (&*thread_ptr, SIGNAL(finished()), &*importWorker, SLOT(deleteLater()));
thread_ptr->start();
}
This method is invoked when I press the button and I get the error:
QThread: Destroyed while thread is still running
bool MainWindow::waitForLoad(QWebView& view)
{
QEventLoop loopLoad;
QTimer timer;
QObject::connect(&view, SIGNAL(loadFinished(bool)), &loopLoad, SLOT(quit()));
QObject::connect(&timer, SIGNAL(timeout()), &loopLoad, SLOT(quit()));
timer.start(timeout);
loopLoad.exec();
if(!timer.isActive())
{
timer.stop();
view.stop();
return false;
}
return true;
}
Say me, is that a correct code? App sometimes freezes after line
loopLoad.exec();
And always returns true even here has happened some problem (timeout, errors when loading, ect -- always true).
start(timeout); starts the timer with a timeout interval of msec milliseconds. So after calling it the timer is running and timer.isActive() always returns true and the if block does not get executed.
You should stop the timer when loadFinished is emitted :
QObject::connect(&view, SIGNAL(loadFinished(bool)), &timer, SLOT(stop()));
If the timer is active then the event loop is stopped by the timer so you should return false because a timeout has been occurred. You should replace if(!timer.isActive()) with if(timer.isActive()).
The correct code is :
bool MainWindow::waitForLoad(QWebView& view)
{
QEventLoop loopLoad;
QTimer timer;
QObject::connect(&view, SIGNAL(loadFinished(bool)), &loopLoad, SLOT(quit()));
QObject::connect(&view, SIGNAL(loadFinished(bool)), &timer, SLOT(stop()));
QObject::connect(&timer, SIGNAL(timeout()), &loopLoad, SLOT(quit()));
timer.start(timeout);
loopLoad.exec();
if(timer.isActive())
{
timer.stop();
view.stop();
return false;
}
return true;
}
I wanted to know what would be the best approach of adding a countdown timer to a QMessageBox ? For instance when a message box is displayed the countdown timer starts for say 5 seconds. If the user doesn't respond to the Message box the message box picks up a default choice.
How about something like this:
#include <QMessageBox>
#include <QPushButton>
#include <QTimer>
class TimedMessageBox : public QMessageBox
{
Q_OBJECT
public:
TimedMessageBox(int timeoutSeconds, const QString & title, const QString & text, Icon icon, int button0, int button1, int button2, QWidget * parent, WindowFlags flags = (WindowFlags)Dialog|MSWindowsFixedSizeDialogHint)
: QMessageBox(title, text, icon, button0, button1, button2, parent, flags)
, _timeoutSeconds(timeoutSeconds+1)
, _text(text)
{
connect(&_timer, SIGNAL(timeout()), this, SLOT(Tick()));
_timer.setInterval(1000);
}
virtual void showEvent(QShowEvent * e)
{
QMessageBox::showEvent(e);
Tick();
_timer.start();
}
private slots:
void Tick()
{
if (--_timeoutSeconds >= 0) setText(_text.arg(_timeoutSeconds));
else
{
_timer.stop();
defaultButton()->animateClick();
}
}
private:
QString _text;
int _timeoutSeconds;
QTimer _timer;
};
[...]
TimedMessageBox * tmb = new TimedMessageBox(10, tr("Timed Message Box"), tr("%1 seconds to go..."), QMessageBox::Warning, QMessageBox::Ok | QMessageBox::Default, QMessageBox::Cancel, QMessageBox::NoButton, this);
int ret = tmb->exec();
delete tmb;
printf("ret=%i\n", ret);
Use QTimer::singleShot with either close(), accept() or reject() slots if you don't need to display the timeout. If you need, then subclass QMessageBox or QDialog and reimplement methods as you want them to be, e.g. reimplement QObject::timerEvent to make text update.
If you want the message box to display the timer value I think you're better off making your own QDialog subclass. Otherwise it sounds simple - display your message with show, start the timer, connect to the timeout slot and manipulate your dialog.