jueves, 21 de febrero de 2008

Crear una barra de progreso para la consola de Linux en C++.

Un ejemplo de cómo programar una barra de progreso para utilizarla en la consola de Linux.


Primero creamos el fichero Stflib.h
#include <iostream>
#include <string>
#include <sstream>

using namespace std;

/**
 * Función para convertir cualquier tipo de datos a string.
 */
template <class T>
inline string ToString(const T& t) {
  stringstream ss;
  ss << t;
  return ss.str();
}

/**
 * Función para emular al gotoxy en la consola de Linux.
 */
inline string Gotoxy(const int& x, const int& y) {
  return "\33[" + ToString(x) + ";" + ToString(y) + "H";
}
Segundo creamos el fichero Colors.h
/** \file   Colors.h
*  \brief  Declaraciones de la clase \c Colors.
*  \author Airam Rodríguez Rivero.
*  \date   11-11-07
*  El propósito de esta clase es la de permitir la utilización de colores
*  utilizando la consola de linux que será donde se envíe el flujo de datos
*  generado por nuestro programa.
*/

#ifndef _COLORS_H_
#define _COLORS_H_


#include <string>
#include <iostream>

using namespace std;

typedef string     TColor;

const TColor Reset     = "\033[0m";    // Reset
const TColor Bold      = "\033[1m";    // Brillo o negrita.
const TColor Underline = "\033[4m";    // Subrayado

// ******************* Foreground Non-lighted Colors *******************

const TColor Black     = "\033[30m";   // Negro
const TColor Red       = "\033[31m";   // Rojo
const TColor Green     = "\033[32m";   // Verde
const TColor Brown     = "\033[33m";   // Marron
const TColor Blue      = "\033[34m";   // Azul
const TColor Magenta   = "\033[35m";   // Magenta
const TColor Cyan      = "\033[36m";   // Cyan
const TColor White     = "\033[37m";   // Blanco

// ******************* Foreground Lighted Colors *******************

const TColor HBlack    = "\033[1m\033[30m"; /// bright/bold black
const TColor HRed      = "\033[1m\033[31m"; /// b red
const TColor HGreen    = "\033[1m\033[32m"; /// b green
const TColor HYellow   = "\033[1m\033[33m"; /// b yellow
const TColor HBlue     = "\033[1m\033[34m"; /// b blue
const TColor HMagenta  = "\033[1m\033[35m"; /// b pink
const TColor HCyan     = "\033[1m\033[36m"; /// b cyan
const TColor HWhite    = "\033[1m\033[37m"; /// b white

// ******************* Background Non-lighted Colors *******************

const TColor BBlack    = "\033[40m";   // Negro
const TColor BRed      = "\033[41m";   // Rojo
const TColor BGreen    = "\033[42m";   // Verde
const TColor BBrown    = "\033[43m";   // Marron
const TColor BBlue     = "\033[44m";   // Azul
const TColor BMagenta  = "\033[45m";   // Magenta
const TColor BCyan     = "\033[46m";   // Cyan
const TColor BWhite    = "\033[47m";   // Blanco

// ******************* Background Lighted Colors *******************

const TColor HBBlack   =  "\033[40;1m";   // Negro
const TColor HBRed     =  "\033[41;1m";   // Rojo
const TColor HBGreen   =  "\033[42;1m";   // Verde
const TColor HBBrown   =  "\033[43;1m";   // Marron
const TColor HBBlue    =  "\033[44;1m";   // Azul
const TColor HBMagenta =  "\033[45;1m";   // Magenta
const TColor HBCyan    =  "\033[46;1m";   // Cyan
const TColor HBWhite   =  "\033[47;1m";   // Blanco

#endif
Tercero creamos el fichero ProgressBar.h
/** \file   ProgressBar.h
*  \brief  Declaraciones de la clase \c ProgressBar.
*  \author Airam Rodríguez Rivero.
*  \date   21-02-08
*  La clase ProgressBar permite generar barras de progreso para la
*  consola de Linux. Se puede determinar el color de fondo de la barra
*  de progreso ( el parámetro _background ), el color de progreso cargado
*  ( el parámetro _loadedColor ), el caracter de relleno de la barra de 
*  progreso que por defecto es un espacio en blanco. Se especifica la
*  posición en la pantalla donde se va a colocar la barra de progreso mediante
*  el atributo _coordinate.
*/

#ifndef _PROGRESSBAR_H_
#define _PROGRESSBAR_H_

#include <iostream>
#include <string>
#include "Stflib.h"

using namespace std;

typedef short int            TProgress;
typedef short int                TSize;
typedef string                  TColor;
typedef pair TCoordinate;
typedef char                 TFillChar;

/// Clase para manejar barras de progreso en la consola de linux.
class ProgressBar {
private:
TProgress         _loaded;    // Representa el porcentaje cargado en la barra de progreso.
TSize               _size;    // Tamaño de la barra de progreso.
TColor        _background;    // Color de fondo de la barra de progreso.
TColor       _loadedColor;    // Color del porcentaje cargado.
TCoordinate   _coordinate;    // Posición en la que presentar en pantalla la barra de progreso.
TFillChar       _fillChar;    // Carácter de relleno de la barra de progreso.
bool   _percentageVisible;
public:
ProgressBar( const TSize& s, const TColor& bg, const TColor& lc, const TFillChar& fc = ' ' ) : _size( s ), _background( bg ), _loadedColor( lc ), _fillChar( fc ), _percentageVisible( true ) { }
~ProgressBar() { }

inline void SetBackground( const TColor& color ) { _background  = color; }
inline void SetLoadedColor( const TColor& color ) { _loadedColor = color; }
inline void SetSize( const TSize& size ) { _size        = size; }
inline void SetCoordinate ( const TCoordinate& coordinate ) { _coordinate  = coordinate; }
inline void SetLoaded( const TProgress& progress )  { _loaded      = progress; }
inline void SetPercentageVisible( const bool& v ) { _percentageVisible = v; }

inline TSize GetX() const { return _coordinate.first;  }
inline TSize GetY() const { return _coordinate.second; }
inline TSize GetSize() const { return _size; }

inline bool IsPercentageVisible() const { return _percentageVisible; }

inline TProgress GetLoaded() const { return _loaded; }

inline TColor GetBackground() const  { return _background;  }
inline TColor GetLoadedColor() const { return _loadedColor; }
inline TFillChar GetFillChar() const { return _fillChar; }

void ShowPercentage( ostream& sout ) const;
void Paint( ostream& sout ) const;

friend ostream& operator<<( ostream& sout, const ProgressBar& pb ); };  #endif  
Cuarto: Creamos el fichero ProgressBar.C
#include "ProgressBar.h"
#include "Colors.h"

using namespace std;

void ProgressBar::ShowPercentage( ostream& sout ) const {
  if ( IsPercentageVisible() ) {
    sout << "[" << GetLoaded() << "%] ";
  }
}

void ProgressBar::Paint( ostream& sout ) const {
  sout << Gotoxy( GetX(), GetY() );
  // Calculamos el número de caracteres con los que rellenamos la barra de progreso.
  float charged = GetLoaded() * GetSize() / 100;
  TSize partialSize = static_cast(charged);
  for ( TProgress i = 0; i <= partialSize; i++ )
    sout << GetLoadedColor() << GetFillChar();
  for ( TProgress i = partialSize; i < GetSize(); i++ )
    sout << GetBackground() << ' ';
  sout << Reset;
  ShowPercentage( sout );
}

ostream& operator<<( ostream& sout, const ProgressBar& pb ) {
  pb.Paint( sout );
  return sout;
}
Quinto: Probamos nuestra barra de progreso con el fichero ProgressBarTest.C
#include <iostream>
#include <time.h>
#include <cstdlib>
#include "ProgressBar.h"
#include "Colors.h"

using namespace std;

int main() {
  //system("clear"); // Importante para no sobreescribir la pantalla.
  // Emular goto(x, y);
  TProgress completed = 10;
  ProgressBar pb( completed, HBWhite, BBlue, ' ' );
  TCoordinate c;
  c.first = 6;
  c.second = 10;
  pb.SetCoordinate(c);
  TProgress i = 0;
  time_t lastTime = time( NULL );
  while ( i <= 10 ) {
    if ((difftime(time(NULL), lastTime)) >= 0.55) {
      cout << Reset; // Importante poner este reset separado, sino tira error de segmentación. 
      system("clear");
      lastTime = time (NULL);
      pb.SetLoaded(i * 10);
      cout << pb;
      cout << flush;
      i++; 
    } else 
      lastTime = time(NULL);
  }
  cout << Reset << endl; // Importante poner este reset separado, sino tira error de segmentación. 
}  
La compilación de todo el código sería:
g++ Stflib.h
g++ Colors.h
g++ ProgressBar.h
g++ -c ProgressBar.C
g++ -o bar ProgressBar.o ProgressBarTest.C
Y ahora para ejecutar:
./bar
Espero que les sirva y que les guste. Pueden dejar comentarios por si tienen dudas o si quieren que cambie alguna cosa! Muchas gracias!!

No hay comentarios:

Publicar un comentario

Escriba su comentario (no necesita registrarse).