-
-
Save rusith/80b9c5dfaf4aced9f0c4006f9e4d1f4e to your computer and use it in GitHub Desktop.
Console progress bar. Code is under the MIT License: http://opensource.org/licenses/MIT
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System; | |
| using System.Threading; | |
| static class Program { | |
| static void Main() { | |
| Console.Write("Performing some task... "); | |
| using (var progress = new ProgressBar()) { | |
| for (int i = 0; i <= 100; i++) { | |
| progress.Report((double) i / 100); | |
| Thread.Sleep(20); | |
| } | |
| } | |
| Console.WriteLine("Done."); | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System; | |
| using System.Text; | |
| using System.Threading; | |
| /// <summary> | |
| /// An ASCII progress bar | |
| /// </summary> | |
| public class ProgressBar : IDisposable, IProgress<double> | |
| { | |
| private const int BlockCount = 10; | |
| private readonly TimeSpan _animationInterval = TimeSpan.FromSeconds(1.0 / 8); | |
| private const string Animation = @"|/-\"; | |
| private readonly Timer _timer; | |
| private double _currentProgress; | |
| private string _currentText = string.Empty; | |
| private bool _disposed; | |
| private int _animationIndex; | |
| public ProgressBar() | |
| { | |
| _timer = new Timer(TimerHandler); | |
| // A progress bar is only for temporary display in a console window. | |
| // If the console output is redirected to a file, draw nothing. | |
| // Otherwise, we'll end up with a lot of garbage in the target file. | |
| if (!Console.IsOutputRedirected) | |
| { | |
| ResetTimer(); | |
| } | |
| } | |
| /// <summary> | |
| /// Progress / 100 (0..1) | |
| /// </summary> | |
| /// <param name="value"></param> | |
| public void Report(double value) | |
| { | |
| // Make sure value is in [0..1] range | |
| value = Math.Max(0, Math.Min(1, value)); | |
| Interlocked.Exchange(ref _currentProgress, value); | |
| } | |
| /// <summary> | |
| /// Progress (0 - 100) | |
| /// </summary> | |
| /// <param name="value"></param> | |
| public void Report(int value) | |
| { | |
| Report((double)value / 100); | |
| } | |
| private void TimerHandler(object state) | |
| { | |
| lock (_timer) | |
| { | |
| if (_disposed) return; | |
| var progressBlockCount = (int)(_currentProgress * BlockCount); | |
| var percent = (int)(_currentProgress * 100); | |
| var text = string.Format("[{0}{1}] {2,3}% {3}", | |
| new string('#', progressBlockCount), new string('-', BlockCount - progressBlockCount), | |
| percent, | |
| Animation[_animationIndex++ % Animation.Length]); | |
| UpdateText(text); | |
| ResetTimer(); | |
| } | |
| } | |
| private void UpdateText(string text) | |
| { | |
| // Get length of common portion | |
| var commonPrefixLength = 0; | |
| var commonLength = Math.Min(_currentText.Length, text.Length); | |
| while (commonPrefixLength < commonLength && text[commonPrefixLength] == _currentText[commonPrefixLength]) | |
| { | |
| commonPrefixLength++; | |
| } | |
| // Backtrack to the first differing character | |
| var outputBuilder = new StringBuilder(); | |
| outputBuilder.Append('\b', _currentText.Length - commonPrefixLength); | |
| // Output new suffix | |
| outputBuilder.Append(text.Substring(commonPrefixLength)); | |
| // If the new text is shorter than the old one: delete overlapping characters | |
| var overlapCount = _currentText.Length - text.Length; | |
| if (overlapCount > 0) | |
| { | |
| outputBuilder.Append(' ', overlapCount); | |
| outputBuilder.Append('\b', overlapCount); | |
| } | |
| Console.Write(outputBuilder); | |
| _currentText = text; | |
| } | |
| private void ResetTimer() | |
| { | |
| _timer.Change(_animationInterval, TimeSpan.FromMilliseconds(-1)); | |
| } | |
| public void Dispose() | |
| { | |
| lock (_timer) | |
| { | |
| _disposed = true; | |
| UpdateText(string.Empty); | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment