//################################################################################################## // // Custom Visualization Core library // Copyright (C) 2011-2013 Ceetron AS // // This library may be used under the terms of either the GNU General Public License or // the GNU Lesser General Public License as follows: // // GNU General Public License Usage // This library is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This library is distributed in the hope that it will be useful, but WITHOUT ANY // WARRANTY; without even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. // // See the GNU General Public License at <> // for more details. // // GNU Lesser General Public License Usage // This library is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation; either version 2.1 of the License, or // (at your option) any later version. // // This library is distributed in the hope that it will be useful, but WITHOUT ANY // WARRANTY; without even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. // // See the GNU Lesser General Public License at <> // for more details. // //################################################################################################## #include "cafProgressInfo.h" #include "cafAssert.h" #include #include #include #include #include namespace caf { //================================================================================================== /// /// \class caf::ProgressInfo /// This class provides a simple frontend to the Qt progressbar, allowing distributed /// progress calculation. /// /// Create an instance of this object in the method/function that needs progress information /// Then call incrementProgress() or setProgress() at proper times in your method. /// When the method returns, the ProgressInfo destructor will clean up and finish. /// The real beauty is that this class will magically interact with possible ProgressInfo instances /// in functions that your method calls, providing a complete, consistent and detailed progress bar /// /// caf::ProgressInfo progInfo(3, "Open File"); /// progInfo.setProgressDescription("Reading"); /// ...readFile() /// progInfo.incrementProgress(); /// progInfo.setProgressDescription("Validating"); /// ... validateData(); /// progInfo.incrementProgress(); /// progInfo.setProgressDescription("Building geometry"); /// ... buildGeometry(); /// progInfo.incrementProgress(); // not needed really, because the destructor will send the progress to top. /// /// There are one particular limitation: The progress will not work correctly if the higher level /// ProgressInfo object does not increment progress between the creation and operation of two (or more) /// independent lower level ProgressInfo objects. If not, the progress will restart (within its limits) /// for each progress object that is operating. /// /// caf::ProgressInfo progInfoHighLevel(3, "Open File"); /// /// { /// caf::ProgressInfo progInfoLowLevel(10, ""); /// } /// // NEEDS progInfoHighLevel.incrementProgress() here !! /// { /// caf::ProgressInfo progInfoLowLevel(10, ""); /// } /// /// It is not allowed to have several ProgressInfo objects in the same scope level /// /// caf::ProgressInfo progInfo1(10, ""); /// caf::ProgressInfo progInfo2(10, ""); //<-- Will not work well //================================================================================================== //-------------------------------------------------------------------------------------------------- /// The title will be written centered in the dialog, under other progress titles in action. /// If you do not need a title for a particular level, simply pass "" and it will be ignored. /// \sa setProgressDescription //-------------------------------------------------------------------------------------------------- ProgressInfo::ProgressInfo(size_t maxProgressValue, const QString& title) { ProgressInfoStatic::start(maxProgressValue, title); if (qApp) { QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- ProgressInfo::~ProgressInfo() { ProgressInfoStatic::finished(); if (qApp) { QApplication::restoreOverrideCursor(); } } //-------------------------------------------------------------------------------------------------- /// Sets a description of the step currently being executed. /// Used to create a nice text in the progressDialog, by appending " : " /// to the current levels title. If no title is requested for this level, the ":" is omitted. /// So: One line per level that has title and/or description. /// in the format: /// : <Description> /// The ":" is only there when needed //-------------------------------------------------------------------------------------------------- void ProgressInfo::setProgressDescription(const QString& description) { ProgressInfoStatic::setProgressDescription(description); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void ProgressInfo::setProgress(size_t progressValue) { ProgressInfoStatic::setProgress(progressValue); } //-------------------------------------------------------------------------------------------------- /// Increments progress by 1, or by the amount set by setNextProgressStepSize //-------------------------------------------------------------------------------------------------- void ProgressInfo::incrementProgress() { ProgressInfoStatic::incrementProgress(); } //-------------------------------------------------------------------------------------------------- /// To make a certain operation span more of the progress bar than one tick, /// set the number of progress ticks that you want it to use before calling it. /// Eg. /// caf::ProgressInfo progInfo(5, "Doing things"); /// // ... Do one small thing /// progInfo.incrementProgress(); /// progInfo.setNextProgressStepSize(3); /// // ... Some heavy function call with its own progress handling /// progInfo.incrementProgress(); /// //-------------------------------------------------------------------------------------------------- void ProgressInfo::setNextProgressIncrement(size_t nextStepSize) { ProgressInfoStatic::setNextProgressIncrement(nextStepSize); } //================================================================================================== /// /// Internal file-scope private functions to implement the progress dialog /// /// /// //================================================================================================== //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- static QProgressDialog* progressDialog() { static QPointer<QProgressDialog> progDialog; if (progDialog.isNull()) { progDialog = new QProgressDialog(); progDialog->hide(); progDialog->setAutoClose(false); progDialog->setAutoReset(false); } return progDialog; } //-------------------------------------------------------------------------------------------------- /// A static vector containing the maximum values for the progress on each sublevel (call stack level) //-------------------------------------------------------------------------------------------------- static std::vector<size_t>& maxProgressStack() { static std::vector<size_t> m_maxProgressStack; return m_maxProgressStack; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- static std::vector<QString>& titleStack() { static std::vector<QString> m_titleStack; return m_titleStack; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- static std::vector<QString>& descriptionStack() { static std::vector<QString> m_descriptionStack; return m_descriptionStack; } //-------------------------------------------------------------------------------------------------- /// The actual progress value on each level (call stack level) 0 corresponds to the outermost function //-------------------------------------------------------------------------------------------------- static std::vector<size_t>& progressStack() { static std::vector<size_t> m_progressStack; return m_progressStack; } //-------------------------------------------------------------------------------------------------- /// The number of progress ticks (span) on each callstack level that the level deeper (in callstack) shall fill /// used to balance the progress, making some (heavy) operations span more of the progress bar //-------------------------------------------------------------------------------------------------- static std::vector<size_t>& progressSpanStack() { static std::vector<size_t> m_progressSpanStack; return m_progressSpanStack; } //-------------------------------------------------------------------------------------------------- /// Calculate the total maximum value for the progress bar composed /// of the complete stack //-------------------------------------------------------------------------------------------------- static size_t currentTotalMaxProgressValue() { std::vector<size_t>& maxProgressStack_v = maxProgressStack(); size_t levCount = 1; for (size_t levelDepth = 0; levelDepth < maxProgressStack_v.size(); ++levelDepth) { levCount *= maxProgressStack_v[levelDepth]; } return levCount; } //-------------------------------------------------------------------------------------------------- /// Calculate the total progress value based on the current level subdivision and progress //-------------------------------------------------------------------------------------------------- static size_t currentTotalProgress() { double progress = 0; std::vector<size_t>& progressStack_v = progressStack(); std::vector<size_t>& progressSpanStack_v = progressSpanStack(); std::vector<size_t>& maxProgressStack_v = maxProgressStack(); for (int i = static_cast<int>(progressStack_v.size()) - 1; i >= 0; --i) { size_t span = (i < 1) ? 1 : progressSpanStack_v[i - 1]; progress = span*(progress + progressStack_v[i]) / maxProgressStack_v[i]; } size_t totalIntProgress = static_cast<size_t>(currentTotalMaxProgressValue()*progress); return totalIntProgress; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- static QString currentComposedLabel() { QString labelText; for (size_t i = 0; i < titleStack().size(); ++i) { if (!titleStack()[i].isEmpty()) labelText += titleStack()[i]; if (!titleStack()[i].isEmpty() && !descriptionStack()[i].isEmpty()) labelText += ": "; if (!descriptionStack()[i].isEmpty()) labelText += descriptionStack()[i]; if (!(titleStack()[i].isEmpty() && descriptionStack()[i].isEmpty())) labelText += "\n"; } labelText += "\n "; return labelText; } static bool isUpdatePossible() { if (!qApp) return false; if (!progressDialog()) return false; return progressDialog()->thread() == QThread::currentThread(); } //================================================================================================== /// /// \class caf::ProgressInfoStatic /// /// /// //================================================================================================== //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void ProgressInfoStatic::start(size_t maxProgressValue, const QString& title) { if (!isUpdatePossible()) return; std::vector<size_t>& progressStack_v = progressStack(); std::vector<size_t>& progressSpanStack_v = progressSpanStack(); std::vector<size_t>& maxProgressStack_v = maxProgressStack(); if (!maxProgressStack_v.size()) { //progressDialog()->setWindowModality(Qt::ApplicationModal); progressDialog()->setMinimum(0); progressDialog()->setWindowTitle(title); progressDialog()->setCancelButton(NULL); progressDialog()->show(); } maxProgressStack_v.push_back(maxProgressValue); progressStack_v.push_back(0); progressSpanStack_v.push_back(1); titleStack().push_back(title); descriptionStack().push_back(""); progressDialog()->setMaximum(static_cast<int>(currentTotalMaxProgressValue())); progressDialog()->setValue(static_cast<int>(currentTotalProgress())); progressDialog()->setLabelText(currentComposedLabel()); //QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents); if (progressDialog()) progressDialog()->repaint(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void ProgressInfoStatic::setProgressDescription(const QString& description) { if (!isUpdatePossible()) return; descriptionStack().back() = description; progressDialog()->setLabelText(currentComposedLabel()); //QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents); if (progressDialog()) progressDialog()->repaint(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void ProgressInfoStatic::setProgress(size_t progressValue) { if (!isUpdatePossible()) return; std::vector<size_t>& progressStack_v = progressStack(); std::vector<size_t>& progressSpanStack_v = progressSpanStack(); std::vector<size_t>& maxProgressStack_v = maxProgressStack(); if (progressValue == progressStack_v.back()) return; // Do nothing if no progress. // Guard against the max value set for this level if (progressValue > maxProgressStack_v.back()) progressValue = maxProgressStack_v.back(); progressStack_v.back() = progressValue; progressSpanStack_v.back() = 1; int totalProgress = static_cast<int>(currentTotalProgress()); int totalMaxProgress = static_cast<int>(currentTotalMaxProgressValue()); CAF_ASSERT(static_cast<int>(totalProgress) <= totalMaxProgress); progressDialog()->setMaximum(totalMaxProgress); progressDialog()->setValue(totalProgress); //QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents); if (progressDialog()) progressDialog()->repaint(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void ProgressInfoStatic::incrementProgress() { if (!isUpdatePossible()) return; std::vector<size_t>& progressStack_v = progressStack(); std::vector<size_t>& progressSpanStack_v = progressSpanStack(); CAF_ASSERT(progressStack_v.size()); ProgressInfoStatic::setProgress(progressStack_v.back() + progressSpanStack_v.back()); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void ProgressInfoStatic::setNextProgressIncrement(size_t nextStepSize) { if (!isUpdatePossible()) return; CAF_ASSERT(progressSpanStack().size()); progressSpanStack().back() = nextStepSize; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void ProgressInfoStatic::finished() { if (!isUpdatePossible()) return; std::vector<size_t>& progressStack_v = progressStack(); std::vector<size_t>& progressSpanStack_v = progressSpanStack(); std::vector<size_t>& maxProgressStack_v = maxProgressStack(); CAF_ASSERT(maxProgressStack_v.size() && progressStack_v.size() && progressSpanStack_v.size() && titleStack().size() && descriptionStack().size()); // Set progress to max value, and leave it there until somebody touches the progress again ProgressInfoStatic::setProgress(maxProgressStack_v.back()); // Pop all the stacks maxProgressStack_v.pop_back(); progressStack_v.pop_back(); progressSpanStack_v.pop_back(); titleStack().pop_back(); descriptionStack().pop_back(); // Update the text to reflect the "previous level" progressDialog()->setLabelText(currentComposedLabel()); // If we are finishing the last level, clean up if (!maxProgressStack_v.size()) { if (progressDialog() != NULL) { progressDialog()->reset(); progressDialog()->close(); } } // Make sure the Gui is repainted //QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents); if (progressDialog()) progressDialog()->repaint(); } } // namespace caf