LinuxQuestions.org
Review your favorite Linux distribution.
Home Forums Tutorials Articles Register
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 03-08-2008, 03:02 PM   #1
JMJ_coder
Member
 
Registered: Apr 2006
Distribution: Fedora
Posts: 478

Rep: Reputation: 30
Command-line progress meter in C++


Hello,

In C++, in a program that runs from the terminal, I want to show some sort of progress meter.


I am thinking of a revolving bar: \ | / -- \ | ,etc. - this would be stationary and the character would change to reflect revolving motion.

Or of a percentage completion number: [10%] -> [36%] -> [100%] - again remain stationary and only the numbers would change.

Or of a progress bar - [****> 23% ] -> [**********> 42% ], etc. this would remain stationary, and the numbers would change and the number of stars in the bar would increase.



How do I do this type of dynamic change on a terminal output?
 
Old 03-08-2008, 03:17 PM   #2
jailbait
LQ Guru
 
Registered: Feb 2003
Location: Virginia, USA
Distribution: Debian 12
Posts: 8,337

Rep: Reputation: 548Reputation: 548Reputation: 548Reputation: 548Reputation: 548Reputation: 548
Quote:
Originally Posted by JMJ_coder View Post


How do I do this type of dynamic change on a terminal output?
You do it using escape sequences.

http://www.linux-mag.com/downloads/2...sequences.html

The escape sequence that you need most is backspace. I think that backspace is ^H

http://www.linux.com/articles/53729

In any case the thing to do is look up how to pass escape sequences in C++.

-----------------------
Steve Stites


P.S. This documentation says that in C++ backspace is passed as \b.

http://linux.isoe.ch/Prog/Array/escape.html

Last edited by jailbait; 03-08-2008 at 03:37 PM.
 
Old 03-08-2008, 07:58 PM   #3
osor
HCL Maintainer
 
Registered: Jan 2006
Distribution: (H)LFS, Gentoo
Posts: 2,450

Rep: Reputation: 78
Quote:
Originally Posted by jailbait View Post
P.S. This documentation says that in C++ backspace is passed as \b.
You will most likely not want to use backspace, but carriage-return (‘\r’), so you can keep an internal string representation of your bar, print a carriage-return (which moves the cursor to the beginning of the current line, but does not descend to the next line), followed by the entire next line. Here is a previous post pertaining to what you want (except it is not in C++, and there is no revolving bar). Also, take a look at the source of e2fsprogs.
 
Old 03-08-2008, 08:24 PM   #4
JMJ_coder
Member
 
Registered: Apr 2006
Distribution: Fedora
Posts: 478

Original Poster
Rep: Reputation: 30
Hello,

Quote:
Originally Posted by osor View Post
You will most likely not want to use backspace, but carriage-return (‘\r’), so you can keep an internal string representation of your bar, print a carriage-return (which moves the cursor to the beginning of the current line, but does not descend to the next line), followed by the entire next line. Here is a previous post pertaining to what you want (except it is not in C++, and there is no revolving bar). Also, take a look at the source of e2fsprogs.
Thanks for the links! I'll look over the code (and hopefully will understand it).
 
Old 03-08-2008, 09:43 PM   #5
ta0kira
Senior Member
 
Registered: Sep 2004
Distribution: FreeBSD 9.1, Kubuntu 12.10
Posts: 3,078

Rep: Reputation: Disabled
All you need to know is to use a \r instead of \n (as was suggested) and remember to overwrite the entire used part of the line each time so that you don't have residual display data. You'll really need to use parts of the POSIX C API to make it work reliably because you'll 1) need to write to the terminal or skip the progress bar entirely if there is no terminal, 2) determine the width of the terminal so that you don't get bizarre output for, say, a 20-character terminal. Let us know when you get a basic working version of what you're doing and we'll help with the terminal part if you'd like.
ta0kira

PS Backspace characters don't always perform the function of backspace in the terminal. That only happens if the terminal is set up that way.
 
Old 03-10-2008, 02:29 PM   #6
JMJ_coder
Member
 
Registered: Apr 2006
Distribution: Fedora
Posts: 478

Original Poster
Rep: Reputation: 30
Hello,

Quote:
Originally Posted by ta0kira View Post
Let us know when you get a basic working version of what you're doing and we'll help with the terminal part if you'd like.
Thanks. I think for my current program, the revolving bar will work best (thanks osor for the link). But, I am still going to write a block of code for the progress bar so I'll know how to do it in the future.

I'll post the code in a couple of days after it is written.


Again, thanks for all the help!
 
Old 03-17-2008, 07:17 PM   #7
JMJ_coder
Member
 
Registered: Apr 2006
Distribution: Fedora
Posts: 478

Original Poster
Rep: Reputation: 30
Hello,

Here is the code for the rotating bar:

Code:
#include <iostream>

void rotateBar(int);

int main()
{

  int i;

  for (i = 0; i < 1000; i++){
    
    rotateBar(i);

  }

  std::cout << std::endl;

  return 0;

}


void rotateBar(int numIn)
{

  char barspin[4] = {'\\', '|', '/', '-'};
  int whichOne;

  whichOne = numIn % 4;

  if (whichOne == 3)

    std::cout << '\r'
              << barspin[whichOne] 
              << barspin[whichOne] 
              << " please wait while function is processing";

  else
    
    std::cout << '\r'
              << barspin[whichOne] 
              << " please wait while function is processing";
  

  return;

}
It works, but it goes so fast that all you see is the final one. How do I slow it down so that a human can see it spinning?

I am assuming it has something to do with sleeping or counting a number of clock ticks per cycle, but I have no idea how to do that.
 
Old 03-17-2008, 08:49 PM   #8
osor
HCL Maintainer
 
Registered: Jan 2006
Distribution: (H)LFS, Gentoo
Posts: 2,450

Rep: Reputation: 78
Quote:
Originally Posted by JMJ_coder View Post
It works, but it goes so fast that all you see is the final one. How do I slow it down so that a human can see it spinning?
Actually, it doesn’t. As ta0kira said before, you have to be careful to erase the entire line (overwrite it with spaces) to get a good effect. Now, the second character is always a ‘-’ (which is why it doesn’t appear to change). To correct this behavior, you would need something like:
Code:
else
    
    std::cout << '\r'
              << barspin[whichOne] << ' '
              << " please wait while function is processing";
IMHO, you do not need the second character. It looks out of place, especially if the user is used to a spinner whose width is always 1.
Quote:
Originally Posted by JMJ_coder View Post
I am assuming it has something to do with sleeping or counting a number of clock ticks per cycle, but I have no idea how to do that.
Well, as a demonstration, you could simply do:
Code:
for (i = 0; i < 1000; i++){
    
    rotateBar(i);
    sleep(1);

  }
In real life, you would need divide your task into chunks and call rotateBar() after each chunk is completed. If you want to use C idioms, it would be best not to pass an argument to the function, and instead give it static storage within the function (so its value is preserved between function calls). If you want to use C++ idioms, you would want to make rotateBar() a member function and the internal state integer a member variable.
 
Old 03-17-2008, 09:53 PM   #9
JMJ_coder
Member
 
Registered: Apr 2006
Distribution: Fedora
Posts: 478

Original Poster
Rep: Reputation: 30
Hello,

Quote:
Originally Posted by osor View Post
IMHO, you do not need the second character. It looks out of place, especially if the user is used to a spinner whose width is always 1.
You know, you're right. I guess I was thinking in the way '-' is half as long as '|', '\', or '/' so print it twice.


Quote:
Originally Posted by osor View Post
Well, as a demonstration, you could simply do:
Code:
for (i = 0; i < 1000; i++){
    
    rotateBar(i);
    sleep(1);

  }
I tried that before I posted the code here, but I get nothing (so I took it out). The program compiles, but when I run it, it just sits there with no output.



Quote:
Originally Posted by osor View Post
In real life, you would need divide your task into chunks and call rotateBar() after each chunk is completed. If you want to use C idioms, it would be best not to pass an argument to the function, and instead give it static storage within the function (so its value is preserved between function calls). If you want to use C++ idioms, you would want to make rotateBar() a member function and the internal state integer a member variable.
I'll keep that in mind for when I actually will use this. Right now, it is just experimentation on my part.
 
Old 03-17-2008, 10:18 PM   #10
osor
HCL Maintainer
 
Registered: Jan 2006
Distribution: (H)LFS, Gentoo
Posts: 2,450

Rep: Reputation: 78
Quote:
Originally Posted by JMJ_coder View Post
I tried that before I posted the code here, but I get nothing (so I took it out). The program compiles, but when I run it, it just sits there with no output.
This is because of buffering (I should have tried it out myself). Try putting this statement at the end of rotateBar():
Code:
std::cout.flush();
 
Old 03-18-2008, 08:19 AM   #11
JMJ_coder
Member
 
Registered: Apr 2006
Distribution: Fedora
Posts: 478

Original Poster
Rep: Reputation: 30
Hello,

Quote:
Originally Posted by osor View Post
This is because of buffering (I should have tried it out myself). Try putting this statement at the end of rotateBar():
Code:
std::cout.flush();
Yes, that worked. Why?

Also, sleep() only takes an int for an argument. What if I only want it to sleep for say half a second or a tenth of a second?
 
Old 03-18-2008, 12:16 PM   #12
osor
HCL Maintainer
 
Registered: Jan 2006
Distribution: (H)LFS, Gentoo
Posts: 2,450

Rep: Reputation: 78
Quote:
Originally Posted by JMJ_coder View Post
Yes, that worked. Why?
This is because stdout is normally line-buffered (where “line” is defined as a series of characters delimited by the newline character ‘\n’). Since you don’t have this occuring in your writing to stdout, you have to force a buffer flush.
Quote:
Originally Posted by JMJ_coder View Post
Also, sleep() only takes an int for an argument. What if I only want it to sleep for say half a second or a tenth of a second?
Try usleep() or nanosleep().
 
Old 03-18-2008, 12:31 PM   #13
JMJ_coder
Member
 
Registered: Apr 2006
Distribution: Fedora
Posts: 478

Original Poster
Rep: Reputation: 30
Hello,

Quote:
Originally Posted by osor View Post
This is because stdout is normally line-buffered (where “line” is defined as a series of characters delimited by the newline character ‘\n’). Since you don’t have this occuring in your writing to stdout, you have to force a buffer flush.

Try usleep() or nanosleep().
I understand, thanks for all the help.
 
  


Reply



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
Command line progress bar for copying Maverick1182 Linux - Newbie 6 03-03-2012 12:55 AM
Perl - printing progress on same line. Technoslave Programming 7 09-03-2011 09:20 PM
how to design progress meter with dialog mox Joydeep Bakshi Programming 9 09-26-2007 02:41 AM
How to show a progress bar on the next line? Chowroc Programming 10 10-26-2005 10:27 PM
Connect a scope meter with serial line ? frenchi Linux - Hardware 2 05-06-2004 12:05 PM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 12:32 AM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration