ProgressBar.cpp

Go to the documentation of this file.
00001 /***************************************************************************
00002  *   Copyright (c) 2004 Werner Mayer <wmayer[at]users.sourceforge.net>     *
00003  *                                                                         *
00004  *   This file is part of the FreeCAD CAx development system.              *
00005  *                                                                         *
00006  *   This library is free software; you can redistribute it and/or         *
00007  *   modify it under the terms of the GNU Library General Public           *
00008  *   License as published by the Free Software Foundation; either          *
00009  *   version 2 of the License, or (at your option) any later version.      *
00010  *                                                                         *
00011  *   This library  is distributed in the hope that it will be useful,      *
00012  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00013  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
00014  *   GNU Library General Public License for more details.                  *
00015  *                                                                         *
00016  *   You should have received a copy of the GNU Library General Public     *
00017  *   License along with this library; see the file COPYING.LIB. If not,    *
00018  *   write to the Free Software Foundation, Inc., 59 Temple Place,         *
00019  *   Suite 330, Boston, MA  02111-1307, USA                                *
00020  *                                                                         *
00021  ***************************************************************************/
00022 
00023 
00024 #include "PreCompiled.h"
00025 #ifndef _PreComp_
00026 # include <QApplication>
00027 # include <QGenericReturnArgument>
00028 # include <QKeyEvent>
00029 # include <QMessageBox>
00030 # include <QMetaObject>
00031 # include <QStatusBar>
00032 # include <QThread>
00033 # include <QTime>
00034 # include <QTimer>
00035 #endif
00036 
00037 #include "ProgressBar.h"
00038 #include "MainWindow.h"
00039 #include "WaitCursor.h"
00040 
00041 using namespace Gui;
00042 
00043 
00044 namespace Gui {
00045 struct SequencerPrivate
00046 {
00047     ProgressBar* bar;
00048     WaitCursor* waitCursor;
00049     QTime measureTime;
00050     QTime progressTime;
00051     QString text;
00052     bool guiThread;
00053 };
00054 
00055 struct ProgressBarPrivate
00056 {
00057     QTimer* delayShowTimer;
00058     int minimumDuration;
00059     int observeEventFilter;
00060 
00061     bool isModalDialog(QObject* o) const
00062     {
00063         QWidget* parent = qobject_cast<QWidget*>(o);
00064         while (parent) {
00065             QMessageBox* dlg = qobject_cast<QMessageBox*>(parent);
00066             if (dlg && dlg->isModal())
00067                 return true;
00068             parent = parent->parentWidget();
00069         }
00070 
00071         return false;
00072     }
00073 };
00074 }
00075 
00076 Sequencer* Sequencer::_pclSingleton = 0;
00077 
00078 Sequencer* Sequencer::instance()
00079 {
00080     // not initialized?
00081     if (!_pclSingleton)
00082     {
00083         _pclSingleton = new Sequencer();
00084     }
00085 
00086     return _pclSingleton;
00087 }
00088 
00089 Sequencer::Sequencer ()
00090 {
00091     d = new SequencerPrivate;
00092     d->bar = 0;
00093     d->waitCursor = 0;
00094     d->guiThread = true;
00095 }
00096 
00097 Sequencer::~Sequencer ()
00098 {
00099     delete d;
00100 }
00101 
00102 void Sequencer::pause()
00103 {
00104     QThread *currentThread = QThread::currentThread();
00105     QThread *thr = d->bar->thread(); // this is the main thread
00106     if (thr != currentThread)
00107         return;
00108 
00109     // allow key handling of dialog and restore cursor
00110     d->bar->leaveControlEvents();
00111     d->waitCursor->restoreCursor();
00112     QApplication::setOverrideCursor(Qt::ArrowCursor);
00113 }
00114 
00115 void Sequencer::resume()
00116 {
00117     QThread *currentThread = QThread::currentThread();
00118     QThread *thr = d->bar->thread(); // this is the main thread
00119     if (thr != currentThread)
00120         return;
00121 
00122     QApplication::restoreOverrideCursor();
00123     d->waitCursor->setWaitCursor();
00124     // must be called as last to get control before WaitCursor
00125     d->bar->enterControlEvents(); // grab again
00126 }
00127 
00128 void Sequencer::startStep()
00129 {
00130     QThread *currentThread = QThread::currentThread();
00131     QThread *thr = d->bar->thread(); // this is the main thread
00132     if (thr != currentThread) {
00133         d->guiThread = false;
00134         d->bar->setRange(0, (int)nTotalSteps);
00135         if (nTotalSteps == 0) {
00136             d->progressTime.start();
00137         }
00138         d->measureTime.start();
00139         QMetaObject::invokeMethod(d->bar, "aboutToShow", Qt::QueuedConnection);
00140     }
00141     else {
00142         d->guiThread = true;
00143         d->bar->setRange(0, (int)nTotalSteps);
00144         if (nTotalSteps == 0) {
00145             d->progressTime.start();
00146         }
00147 
00148         d->measureTime.start();
00149         d->waitCursor = new Gui::WaitCursor;
00150         d->bar->enterControlEvents();
00151         d->bar->aboutToShow();
00152     }
00153 }
00154 
00155 void Sequencer::nextStep(bool canAbort)
00156 {
00157     QThread *currentThread = QThread::currentThread();
00158     QThread *thr = d->bar->thread(); // this is the main thread
00159     if (thr != currentThread) {
00160         setValue((int)nProgress+1);
00161     }
00162     else {
00163         if (wasCanceled() && canAbort) {
00164             // restore cursor
00165             pause();
00166             bool ok = d->bar->canAbort();
00167             // continue and show up wait cursor if needed
00168             resume();
00169 
00170             // force to abort the operation
00171             if ( ok ) {
00172                 abort();
00173             } else {
00174                 rejectCancel();
00175                 setValue((int)nProgress+1);
00176             }
00177         }
00178         else {
00179             setValue((int)nProgress+1);
00180         }
00181     }
00182 }
00183 
00184 void Sequencer::setProgress(size_t step)
00185 {
00186     d->bar->show();
00187     setValue((int)step);
00188 }
00189 
00190 void Sequencer::setValue(int step)
00191 {
00192     QThread *currentThread = QThread::currentThread();
00193     QThread *thr = d->bar->thread(); // this is the main thread
00194     // if number of total steps is unknown then increment only by one
00195     if (nTotalSteps == 0) {
00196         int elapsed = d->progressTime.elapsed();
00197         // allow an update every 500 milliseconds only
00198         if (elapsed > 500) {
00199             d->progressTime.restart();
00200             if (thr != currentThread) {
00201                 QMetaObject::invokeMethod(d->bar, "setValue", Qt::/*Blocking*/QueuedConnection,
00202                     QGenericReturnArgument(), Q_ARG(int,d->bar->value()+1));
00203             }
00204             else {
00205                 d->bar->setValue(d->bar->value()+1);
00206                 qApp->processEvents();
00207             }
00208         }
00209     }
00210     else {
00211         if (thr != currentThread) {
00212             QMetaObject::invokeMethod(d->bar, "setValue", Qt::/*Blocking*/QueuedConnection,
00213                 QGenericReturnArgument(), Q_ARG(int,step));
00214             if (d->bar->isVisible())
00215                 showRemainingTime();
00216         }
00217         else {
00218             d->bar->setValue(step);
00219             if (d->bar->isVisible())
00220                 showRemainingTime();
00221             d->bar->resetObserveEventFilter();
00222             qApp->processEvents();
00223         }
00224     }
00225 }
00226 
00227 void Sequencer::showRemainingTime()
00228 {
00229     QThread *currentThread = QThread::currentThread();
00230     QThread *thr = d->bar->thread(); // this is the main thread
00231 
00232     int elapsed = d->measureTime.elapsed();
00233     int progress = d->bar->value();
00234     int totalSteps = d->bar->maximum() - d->bar->minimum();
00235 
00236     QString txt = d->text;
00237     // More than 5 percent complete or more than 5 secs have elapsed.
00238     if (progress * 20 > totalSteps || elapsed > 5000) {
00239         int rest = (int) ( (double) totalSteps/progress * elapsed ) - elapsed;
00240 
00241         // more than 1 secs have elapsed and at least 100 ms are remaining
00242         if (elapsed > 1000 && rest > 100) {
00243             QTime time( 0,0, 0);
00244             time = time.addSecs( rest/1000 );
00245             QString remain = Gui::ProgressBar::tr("Remaining: %1").arg(time.toString());
00246             QString status = QString::fromAscii("%1\t[%2]").arg(txt).arg(remain);
00247 
00248             if (thr != currentThread) {
00249                 QMetaObject::invokeMethod(getMainWindow()->statusBar(), "showMessage",
00250                     Qt::/*Blocking*/QueuedConnection,
00251                     QGenericReturnArgument(),
00252                     Q_ARG(QString,status));
00253             }
00254             else {
00255                 getMainWindow()->statusBar()->showMessage(status);
00256             }
00257         }
00258     }
00259 }
00260 
00261 void Sequencer::resetData()
00262 {
00263     QThread *currentThread = QThread::currentThread();
00264     QThread *thr = d->bar->thread(); // this is the main thread
00265     if (thr != currentThread) {
00266         QMetaObject::invokeMethod(d->bar, "reset", Qt::QueuedConnection);
00267         QMetaObject::invokeMethod(d->bar, "hide", Qt::QueuedConnection);
00268         QMetaObject::invokeMethod(getMainWindow()->statusBar(), "showMessage",
00269             Qt::/*Blocking*/QueuedConnection,
00270             QGenericReturnArgument(),
00271             Q_ARG(QString,QString()));
00272         QMetaObject::invokeMethod(getMainWindow(), "setPaneText",
00273             Qt::/*Blocking*/QueuedConnection,
00274             QGenericReturnArgument(),
00275             Q_ARG(int,1),
00276             Q_ARG(QString,QString()));
00277     }
00278     else {
00279         d->bar->reset();
00280         // Note: Under Qt 4.1.4 this forces to run QWindowsStyle::eventFilter() twice 
00281         // handling the same event thus a warning is printed. Possibly, this is a bug
00282         // in Qt. The message is QEventDispatcherUNIX::unregisterTimer: invalid argument.
00283         d->bar->hide();
00284         delete d->waitCursor;
00285         d->waitCursor = 0;
00286         d->bar->leaveControlEvents();
00287         getMainWindow()->setPaneText(1, QString());
00288         getMainWindow()->statusBar()->showMessage(QString());
00289     }
00290 
00291     SequencerBase::resetData();
00292 }
00293 
00294 void Sequencer::abort()
00295 {
00296     //resets
00297     resetData();
00298     Base::AbortException exc("Aborting...");
00299     throw exc;
00300 }
00301 
00302 void Sequencer::setText (const char* pszTxt)
00303 {
00304     QThread *currentThread = QThread::currentThread();
00305     QThread *thr = d->bar->thread(); // this is the main thread
00306 
00307     // print message to the statusbar
00308     d->text = pszTxt ? QString::fromUtf8(pszTxt) : QLatin1String("");
00309     if (thr != currentThread) {
00310         QMetaObject::invokeMethod(getMainWindow()->statusBar(), "showMessage",
00311             Qt::/*Blocking*/QueuedConnection,
00312             QGenericReturnArgument(),
00313             Q_ARG(QString,d->text));
00314     }
00315     else {
00316         getMainWindow()->statusBar()->showMessage(d->text);
00317     }
00318 }
00319 
00320 bool Sequencer::isBlocking() const
00321 {
00322     return d->guiThread;
00323 }
00324 
00325 QProgressBar* Sequencer::getProgressBar(QWidget* parent)
00326 {
00327     if (!d->bar)
00328         d->bar = new ProgressBar(this, parent);
00329     return d->bar;
00330 }
00331 
00332 // -------------------------------------------------------
00333 
00334 /* TRANSLATOR Gui::ProgressBar */
00335 
00336 ProgressBar::ProgressBar (Sequencer* s, QWidget * parent)
00337     : QProgressBar(parent), sequencer(s)
00338 {
00339     d = new Gui::ProgressBarPrivate;
00340     d->minimumDuration = 2000; // 2 seconds
00341     d->delayShowTimer = new QTimer(this);
00342     d->delayShowTimer->setSingleShot(true);
00343     connect(d->delayShowTimer, SIGNAL(timeout()), this, SLOT(delayedShow()));
00344     d->observeEventFilter = 0;
00345 
00346     setFixedWidth(120);
00347 
00348     // write percentage to the center
00349     setAlignment(Qt::AlignHCenter);
00350     hide();
00351 }
00352 
00353 ProgressBar::~ProgressBar ()
00354 {
00355     disconnect(d->delayShowTimer, SIGNAL(timeout()), this, SLOT(delayedShow()));
00356     delete d->delayShowTimer;
00357     delete d;
00358 }
00359 
00360 int ProgressBar::minimumDuration() const
00361 {
00362     return d->minimumDuration;
00363 }
00364 
00365 void ProgressBar::setMinimumDuration (int ms)
00366 {
00367     if (value() == 0)
00368     {
00369         d->delayShowTimer->stop();
00370         d->delayShowTimer->start(ms);
00371     }
00372 
00373     d->minimumDuration = ms;
00374 }
00375 
00376 void ProgressBar::aboutToShow()
00377 {
00378     // delay showing the bar
00379     d->delayShowTimer->start(d->minimumDuration);
00380 }
00381 
00382 void ProgressBar::delayedShow()
00383 {
00384     if (!isVisible() && !sequencer->wasCanceled() && sequencer->isRunning())
00385         show();
00386 }
00387 
00388 bool ProgressBar::canAbort() const
00389 {
00390     int ret = QMessageBox::question(getMainWindow(),tr("Aborting"),
00391     tr("Do you really want to abort the operation?"),  QMessageBox::Yes, 
00392     QMessageBox::No|QMessageBox::Default);
00393 
00394     return (ret == QMessageBox::Yes) ? true : false;
00395 }
00396 
00397 void ProgressBar::showEvent(QShowEvent* e)
00398 {
00399     QProgressBar::showEvent(e);
00400     d->delayShowTimer->stop();
00401 }
00402 
00403 void ProgressBar::hideEvent(QHideEvent* e)
00404 {
00405     QProgressBar::hideEvent(e);
00406     d->delayShowTimer->stop();
00407 }
00408 
00409 void ProgressBar::resetObserveEventFilter()
00410 {
00411     d->observeEventFilter = 0;
00412 }
00413 
00414 void ProgressBar::enterControlEvents()
00415 {
00416     qApp->installEventFilter(this);
00417 
00418     // Make sure that we get the key events, otherwise the Inventor viewer usurps the key events
00419     // This also disables accelerators.
00420     grabKeyboard();
00421 }
00422 
00423 void ProgressBar::leaveControlEvents()
00424 {
00425     qApp->removeEventFilter(this);
00426 
00427     // relase the keyboard again
00428     releaseKeyboard();
00429 }
00430 
00431 bool ProgressBar::eventFilter(QObject* o, QEvent* e)
00432 {
00433     if (sequencer->isRunning() && e != 0) {
00434         switch ( e->type() )
00435         {
00436         // check for ESC
00437         case QEvent::KeyPress:
00438             {
00439                 QKeyEvent* ke = (QKeyEvent*)e;
00440                 if (ke->key() == Qt::Key_Escape) {
00441                     // eventFilter() was called from the application 50 times without performing a new step (app could hang)
00442                     if (d->observeEventFilter > 50) {
00443                         // tries to unlock the application if it hangs (propably due to incorrect usage of Base::Sequencer)
00444                         if (ke->modifiers() & (Qt::ControlModifier | Qt::AltModifier)) {
00445                             sequencer->resetData();
00446                             return true;
00447                         }
00448                     }
00449 
00450                     // cancel the operation
00451                     sequencer->tryToCancel();
00452                 }
00453 
00454                 return true;
00455             }   break;
00456 
00457         // ignore alle these events
00458         case QEvent::KeyRelease:
00459         case QEvent::Enter:
00460         case QEvent::Leave:
00461         case QEvent::MouseButtonDblClick:
00462         case QEvent::ContextMenu:
00463             {
00464                 return true;
00465             }   break;
00466       
00467         // special case if the main window's close button was pressed 
00468         case QEvent::Close:
00469             {
00470                 // avoid to exit while app is working
00471                 // note: all other widget types are allowed to be closed anyway
00472                 if (o == getMainWindow()) {
00473                     e->ignore();
00474                     return true; 
00475                 }
00476             }   break;
00477 
00478         // do a system beep and ignore the event
00479         case QEvent::MouseButtonPress:
00480             {
00481                 if (d->isModalDialog(o))
00482                     return false;
00483                 QApplication::beep();
00484                 return true;
00485             }   break;
00486 
00487         default:
00488             {
00489             }   break;
00490         }
00491 
00492         d->observeEventFilter++;
00493     }
00494 
00495     return QProgressBar::eventFilter(o, e);
00496 }
00497 
00498 
00499 #include "moc_ProgressBar.cpp"

Generated on Wed Nov 23 19:00:30 2011 for FreeCAD by  doxygen 1.6.1