//################################################################################################## // // Custom Visualization Core library // Copyright (C) 2011-2012 Ceetron AS // // 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. // //################################################################################################## #include "cafProgressInfo.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. //================================================================================================== //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- ProgressInfo::ProgressInfo(size_t maxProgressValue, const QString& title) { ProgressInfoStatic::start(maxProgressValue, title); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- ProgressInfo::~ProgressInfo() { ProgressInfoStatic::finished(); } //-------------------------------------------------------------------------------------------------- /// Sets a description of the step currently being executed. /// Used to create a nice text in the progressDialog //-------------------------------------------------------------------------------------------------- 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 progDialog; if (progDialog.isNull()) { progDialog = new QProgressDialog(); progDialog->hide(); } return progDialog; } //-------------------------------------------------------------------------------------------------- /// A static vector containing the maximum values for the progress on each sublevel (call stack level) //-------------------------------------------------------------------------------------------------- static std::vector& maxProgressStack() { static std::vector m_maxProgressStack; return m_maxProgressStack; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- static std::vector& titleStack() { static std::vector m_titleStack; return m_titleStack; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- static std::vector& descriptionStack() { static std::vector m_descriptionStack; return m_descriptionStack; } //-------------------------------------------------------------------------------------------------- /// The actual progress value on each level (call stack level) 0 corresponds to the outermost function //-------------------------------------------------------------------------------------------------- static std::vector& progressStack() { static std::vector 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& progressSpanStack() { static std::vector m_progressSpanStack; return m_progressSpanStack; } //-------------------------------------------------------------------------------------------------- /// Calculate the total maximum value for the progress bar composed /// of the complete stack //-------------------------------------------------------------------------------------------------- static size_t currentTotalMaxProgressValue() { size_t levCount = 1; for (size_t levelDepth = 0; levelDepth < maxProgressStack().size(); ++levelDepth) { levCount *= maxProgressStack()[levelDepth]; } return levCount; } //-------------------------------------------------------------------------------------------------- /// Calculate the total progress value based on the current level subdivision and progress //-------------------------------------------------------------------------------------------------- static size_t currentTotalProgress() { double progress = 0; for (int i = static_cast(progressStack().size()) - 1; i >= 0; --i) { size_t span = (i < 1) ? 1 : progressSpanStack()[i-1]; progress = span*(progress + progressStack()[i])/maxProgressStack()[i]; } size_t totalIntProgress = static_cast(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; if (!maxProgressStack().size()) { progressDialog()->setWindowModality(Qt::WindowModal); progressDialog()->setMinimum(0); progressDialog()->setWindowTitle(title); progressDialog()->setCancelButton(NULL); progressDialog()->show(); } maxProgressStack().push_back(maxProgressValue); progressStack().push_back(0); progressSpanStack().push_back(1); titleStack().push_back(title); descriptionStack().push_back(""); progressDialog()->setMaximum(static_cast(currentTotalMaxProgressValue())); progressDialog()->setValue(static_cast(currentTotalProgress())); progressDialog()->setLabelText(currentComposedLabel()); QCoreApplication::processEvents(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void ProgressInfoStatic::setProgressDescription(const QString& description) { if (!isUpdatePossible()) return; descriptionStack().back() = description; progressDialog()->setLabelText(currentComposedLabel()); QCoreApplication::processEvents(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void ProgressInfoStatic::setProgress(size_t progressValue) { if (!isUpdatePossible()) return; if (progressValue == progressStack().back()) return; // Do nothing if no progress. // Guard against the max value set for this level if (progressValue > maxProgressStack().back() ) progressValue = maxProgressStack().back(); progressStack().back() = progressValue; progressSpanStack().back() = 1; int totalProgress = static_cast(currentTotalProgress()); int totalMaxProgress = static_cast(currentTotalMaxProgressValue()); assert(static_cast(totalProgress) <= totalMaxProgress); progressDialog()->setMaximum(totalMaxProgress); progressDialog()->setValue(totalProgress); QCoreApplication::processEvents(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void ProgressInfoStatic::incrementProgress() { if (!isUpdatePossible()) return; assert(progressStack().size()); ProgressInfoStatic::setProgress(progressStack().back() + progressSpanStack().back()); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void ProgressInfoStatic::setNextProgressIncrement(size_t nextStepSize) { if (!isUpdatePossible()) return; assert(progressSpanStack().size()); progressSpanStack().back() = nextStepSize; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void ProgressInfoStatic::finished() { if (!isUpdatePossible()) return; assert(maxProgressStack().size() && progressStack().size() && progressSpanStack().size() && titleStack().size() && descriptionStack().size()); // Set progress to max value, and leave it there until somebody touches the progress again ProgressInfoStatic::setProgress(maxProgressStack().back()); // Pop all the stacks maxProgressStack().pop_back(); progressStack().pop_back(); progressSpanStack().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().size()) { if (progressDialog() != NULL) { progressDialog()->hide(); delete progressDialog(); } } // Make sure the Gui is repainted QCoreApplication::processEvents(); } } // namespace caf