当更多信息被打印到屏幕时,使boost :: progress_display工作

问题描述:

我有一个控制台程序,可能需要一些时间进行计算。我正在使用boost::progress_display向用户提供一些反馈。当更多信息被打印到屏幕时,使boost :: progress_display工作

我的问题是,我也想打印其他更新到标准输出,如果某些事情发生,并打破进度条:

0% 10 20 30 40 50 60 70 80 90 100% 
|----|----|----|----|----|----|----|----|----|----| 
**Found temporary candidate with score: 40 
Found temporary candidate with score: 46 
*Found temporary candidate with score: 52 
********Found temporary candidate with score: 55 
**Found temporary candidate with score: 67 
************************************** 

有一个简单的办法有两个进度条(理想在代码中为非侵入性代码boost::progress_display)并更新到屏幕?

编辑:在评论中建议说,我还没有提供什么,我找一个例子后,我想代码看起来与此类似:

boost::progress_display progress(10); 
for (size_t i = 0; i< 10; ++i) 
{ 
    std::cout << "Number is: " << i << "\n"; 
    ++progress; 
} 

,但不会导致在这样的输出:

0% 10 20 30 40 50 60 70 80 90 100% 
|----|----|----|----|----|----|----|----|----|----| 
Number is: 0 
*****Number is: 1 
*****Number is: 2 
*****Number is: 3 
*****Number is: 4 
*****Number is: 5 
*****Number is: 6 
*****Number is: 7 
*****Number is: 8 
*****Number is: 9 
****** 

相反,我想在说Number is: z行中的进度条之后或之前它显示出来:

Number is: 0 
Number is: 1 
Number is: 2 
Number is: 3 
Number is: 4 
Number is: 5 
Number is: 6 
Number is: 7 
Number is: 8 
Number is: 9 
0% 10 20 30 40 50 60 70 80 90 100% 
|----|----|----|----|----|----|----|----|----|----| 
*************************************************** 

使用自制类

我找不到使用boost::progress_display一个简单的答案,所以我实现我自己的RAII类,它是在Linux下工作就像一个魅力:

ProgressBar.h

#ifndef PROGRESS_BAR_H 
#define PROGRESS_BAR_H 

#include <string> 

/** 
* RAII implementation of a progress bar. 
*/ 
class ProgressBar 
{ 
public: 
    /** 
    * Constructor. 
    * It takes two values: the expected number of iterations whose progress we 
    * want to monitor and an initial message to be displayed on top of the bar 
    * (which can be updated with updateLastPrintedMessage()). 
    */ 
    ProgressBar(
      uint32_t expectedIterations, const std::string& initialMessage=""); 

    /** 
    * Destructor to guarantee RAII. 
    */ 
    ~ProgressBar(); 

    // Make the object non-copyable 
    ProgressBar(const ProgressBar& o) = delete; 
    ProgressBar& operator=(const ProgressBar& o) = delete; 

    /** 
    * Must be invoked when the progress bar is no longer needed to restore the 
    * position of the cursor to the end of the output. 
    * It is automatically invoked when the object is destroyed. 
    */ 
    void endProgressBar(); 

    /** 
    * Prints a new message under the last printed message, without overwriting 
    * it. This moves the progress bar down to be placed under the newly 
    * written message. 
    */ 
    void printNewMessage(const std::string& message); 

    /** 
    * Prints a message while the progress bar is on the screen on top on the 
    * last printed message. Since the cursor is right at the beginning of the 
    * progress bar, it moves the cursor up by one line before printing, and 
    * then returns it to its original position. 
    */ 
    void updateLastPrintedMessage(const std::string& message); 

    /** 
    * Overloaded prefix operator, used to indicate that the has been a new 
    * iteration. 
    */ 
    void operator++(); 

private: 
    unsigned int mTotalIterations; 
    unsigned int mNumberOfTicks; 
    bool mEnded; 
    size_t mLengthOfLastPrintedMessage; 
}; 

#endif /* PROGRESS_BAR_H */ 

ProgressBar.cpp

#include "ProgressBar.h" 
#include <iostream> 
#include <iomanip> 
#include <sstream> 

#define LENGTH_OF_PROGRESS_BAR 55 
#define PERCENTAGE_BIN_SIZE (100.0/LENGTH_OF_PROGRESS_BAR) 

namespace 
{ 
    std::string generateProgressBar(unsigned int percentage) 
    { 
     const int progress = static_cast<int>(percentage/PERCENTAGE_BIN_SIZE); 
     std::ostringstream ss; 
     ss << " " << std::setw(3) << std::right << percentage << "% "; 
     std::string bar("[" + std::string(LENGTH_OF_PROGRESS_BAR-2, ' ') + "]"); 

     unsigned int numberOfSymbols = std::min(
       std::max(0, progress - 1), 
       LENGTH_OF_PROGRESS_BAR - 2); 

     bar.replace(1, numberOfSymbols, std::string(numberOfSymbols, '|')); 

     ss << bar; 
     return ss.str(); 
    } 
} 

ProgressBar::ProgressBar(
      uint32_t expectedIterations, const std::string& initialMessage) 
    : mTotalIterations(expectedIterations), 
     mNumberOfTicks(0), 
     mEnded(false) 
{ 
    std::cout << initialMessage << "\n"; 
    mLengthOfLastPrintedMessage = initialMessage.size(); 
    std::cout << generateProgressBar(0) << "\r" << std::flush; 
} 

ProgressBar::~ProgressBar() 
{ 
    endProgressBar(); 
} 

void ProgressBar::operator++() 
{ 
    if (mEnded) 
    { 
     throw std::runtime_error(
       "Attempted to use progress bar after having terminated it"); 
    } 

    mNumberOfTicks = std::min(mTotalIterations, mNumberOfTicks+1); 
    const unsigned int percentage = static_cast<unsigned int>(
      mNumberOfTicks*100.0/mTotalIterations); 

    std::cout << generateProgressBar(percentage) << "\r" << std::flush; 
} 

void ProgressBar::printNewMessage(const std::string& message) 
{ 
    if (mEnded) 
    { 
     throw std::runtime_error(
       "Attempted to use progress bar after having terminated it"); 
    } 

    std::cout << "\r" 
     << std::left 
     << std::setw(LENGTH_OF_PROGRESS_BAR + 6) 
     << message << "\n"; 
    mLengthOfLastPrintedMessage = message.size(); 
    const unsigned int percentage = static_cast<unsigned int>(
      mNumberOfTicks*100.0/mTotalIterations); 

    std::cout << generateProgressBar(percentage) << "\r" << std::flush; 

} 

void ProgressBar::updateLastPrintedMessage(const std::string& message) 
{ 
    if (mEnded) 
    { 
     throw std::runtime_error(
       "Attempted to use progress bar after having terminated it"); 
    } 

    std::cout << "\r\033[F" 
     << std::left 
     << std::setw(mLengthOfLastPrintedMessage) 
     << message << "\n"; 
    mLengthOfLastPrintedMessage = message.size(); 
} 

void ProgressBar::endProgressBar() 
{ 
    if (!mEnded) 
    { 
     std::cout << std::string(2, '\n'); 
    } 
    mEnded = true; 
} 

如何使用它

这是两段代码显示它如何被使用和输出可以预期:

updating.cpp

#include "ProgressBar.h" 
#include <unistd.h> 

#define ITERATIONS 10 

int main() 
{ 
    ProgressBar progress(ITERATIONS, "No odd number found"); 
    for (size_t i = 0; i < ITERATIONS; ++i) 
    { 
     // Sleep for 0.5s so that the gif is clear enough 
     usleep(500000); 

     if (i%2 != 0) 
     { 
      progress.updateLastPrintedMessage(
        "New odd number found: " + std::to_string(i)); 
     } 

     ++progress; 
    } 
} 

输出的更新cpp

enter image description here

appending.cpp

#include "ProgressBar.h" 
#include <unistd.h> 

#define ITERATIONS 10 

int main() 
{ 
    ProgressBar progress(ITERATIONS, "Looking for odd numbers..."); 
    for (size_t i = 0; i < ITERATIONS; ++i) 
    { 
     // Sleep for 0.5s so that the gif is clear enough 
     usleep(500000); 

     if (i%2 != 0) 
     { 
      progress.printNewMessage(
        "New odd number found: " + std::to_string(i)); 
     } 

     ++progress; 
    } 
} 

appending.cpp的输出

enter image description here

+0

什么是输出呢?这个问题没有表现出任何期望,你的回答并没有显示任何实际结果。 – sehe

+1

@sehe感谢您的建议。我已经更新了这个问题,以显示我的意思。此答案的输出为用户提供了两个选项:或者打印在条的顶部,尊重先前打印的消息并向下移动条;或“更新”(即覆盖)先前打印的消息。输出将取决于使用情况。关键是不要用'std :: cout'直接打印到屏幕上,而是在前后移动光标。 – user2891462

+0

@sehe我刚刚添加了几段代码,展示了如何使用该类以及应该从中获得什么样的输出,希望现在它更有用:) – user2891462