From 2005ede7d3ef69a666955fd01a21b8560c512fc5 Mon Sep 17 00:00:00 2001 From: Mpdreamz Date: Mon, 11 Apr 2016 14:46:29 +0200 Subject: [PATCH] support sub progressbars to any depth --- build.bat | 2 +- build/Build.proj | 2 +- .../DeeplyNestedProgressBarTreeExample.cs | 61 ++++ .../Examples/DrawsOnlyOnTickExample.cs | 23 ++ .../Examples/LongRunningExample.cs | 23 ++ .../Examples/NegativeMaxTicksExample.cs | 23 ++ .../NestedProgressBarPerStepProgress.cs | 64 ++++ .../Examples/NeverCompletesExample.cs | 21 ++ .../Examples/NeverTicksExample.cs | 17 + .../Examples/ThreadedTicksOverflowExample.cs | 22 ++ .../Examples/TicksOverflowExample.cs | 23 ++ .../Examples/UpdatesMaxTicksExample.cs | 30 ++ .../Examples/ZeroMaxTicksExample.cs | 23 ++ .../IProgressBarExample.cs | 10 + src/ShellProgressBar.Example/Program.cs | 95 +++-- .../ShellProgressBar.Example.csproj | 13 + src/ShellProgressBar.sln | 4 +- src/ShellProgressBar/ChildProgressBar.cs | 51 +++ src/ShellProgressBar/IProgressBar.cs | 17 + src/ShellProgressBar/ProgressBar.cs | 344 +++++++++++++----- src/ShellProgressBar/ProgressBarBase.cs | 91 +++++ src/ShellProgressBar/ProgressBarHeight.cs | 7 + src/ShellProgressBar/ProgressBarOptions.cs | 17 + .../Properties/AssemblyInfo.cs | 4 +- src/ShellProgressBar/ShellProgressBar.csproj | 109 +++--- src/ShellProgressBar/StringExtensions.cs | 2 +- 26 files changed, 897 insertions(+), 201 deletions(-) create mode 100644 src/ShellProgressBar.Example/Examples/DeeplyNestedProgressBarTreeExample.cs create mode 100644 src/ShellProgressBar.Example/Examples/DrawsOnlyOnTickExample.cs create mode 100644 src/ShellProgressBar.Example/Examples/LongRunningExample.cs create mode 100644 src/ShellProgressBar.Example/Examples/NegativeMaxTicksExample.cs create mode 100644 src/ShellProgressBar.Example/Examples/NestedProgressBarPerStepProgress.cs create mode 100644 src/ShellProgressBar.Example/Examples/NeverCompletesExample.cs create mode 100644 src/ShellProgressBar.Example/Examples/NeverTicksExample.cs create mode 100644 src/ShellProgressBar.Example/Examples/ThreadedTicksOverflowExample.cs create mode 100644 src/ShellProgressBar.Example/Examples/TicksOverflowExample.cs create mode 100644 src/ShellProgressBar.Example/Examples/UpdatesMaxTicksExample.cs create mode 100644 src/ShellProgressBar.Example/Examples/ZeroMaxTicksExample.cs create mode 100644 src/ShellProgressBar.Example/IProgressBarExample.cs create mode 100644 src/ShellProgressBar/ChildProgressBar.cs create mode 100644 src/ShellProgressBar/IProgressBar.cs create mode 100644 src/ShellProgressBar/ProgressBarBase.cs create mode 100644 src/ShellProgressBar/ProgressBarHeight.cs create mode 100644 src/ShellProgressBar/ProgressBarOptions.cs diff --git a/build.bat b/build.bat index 094a0ff..4c984ad 100644 --- a/build.bat +++ b/build.bat @@ -1,2 +1,2 @@ -"C:\Windows\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe" ^ +"C:\Program Files (x86)\MSBuild\14.0\Bin\MsBuild.exe" ^ build\Build.proj /p:NuspecFile=build\ShellProgressBar.nuspec;BUILD_NUMBER=%1 /t:NugetPackage \ No newline at end of file diff --git a/build/Build.proj b/build/Build.proj index 277fa3f..6aa2816 100644 --- a/build/Build.proj +++ b/build/Build.proj @@ -33,7 +33,7 @@ AssemblyVersion="$(Version)" AssemblyFileVersion="$(Version)"/> - + diff --git a/src/ShellProgressBar.Example/Examples/DeeplyNestedProgressBarTreeExample.cs b/src/ShellProgressBar.Example/Examples/DeeplyNestedProgressBarTreeExample.cs new file mode 100644 index 0000000..398a17c --- /dev/null +++ b/src/ShellProgressBar.Example/Examples/DeeplyNestedProgressBarTreeExample.cs @@ -0,0 +1,61 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace ShellProgressBar.Example.Examples +{ + public class DeeplyNestedProgressBarTreeExample : IProgressBarExample + { + public Task Start(CancellationToken token) + { + var random = new Random(); + + var numberOfSteps = 7; + + var overProgressOptions = new ProgressBarOptions + { + BackgroundColor = ConsoleColor.DarkGray, + }; + + using (var pbar = new ProgressBar(numberOfSteps, "overal progress", overProgressOptions)) + { + var stepBarOptions = new ProgressBarOptions + { + ForeGroundColor = ConsoleColor.Cyan, + ForeGroundColorDone = ConsoleColor.DarkGreen, + ProgressCharacter = '─', + BackgroundColor = ConsoleColor.DarkGray, + CollapseWhenFinished = false, + + } ; + Parallel.For(0, numberOfSteps, (i) => + { + var workBarOptions = new ProgressBarOptions + { + ForeGroundColor = ConsoleColor.Yellow, + ProgressCharacter = '─', + BackgroundColor = ConsoleColor.DarkGray, + }; + var childSteps = random.Next(1, 5); + using (var childProgress = pbar.Spawn(childSteps, $"step {i} progress", stepBarOptions)) + Parallel.For(0, childSteps, (ci) => + { + var childTicks = random.Next(50, 250); + using (var innerChildProgress = childProgress.Spawn(childTicks, $"step {i}::{ci} progress", workBarOptions)) + { + for (var r = 0; r < childTicks; r++) + { + innerChildProgress.Tick(); + Program.BusyWait(50); + } + } + childProgress.Tick(); + }); + + pbar.Tick(); + }); + } + return Task.FromResult(1); + } + } +} \ No newline at end of file diff --git a/src/ShellProgressBar.Example/Examples/DrawsOnlyOnTickExample.cs b/src/ShellProgressBar.Example/Examples/DrawsOnlyOnTickExample.cs new file mode 100644 index 0000000..734f19b --- /dev/null +++ b/src/ShellProgressBar.Example/Examples/DrawsOnlyOnTickExample.cs @@ -0,0 +1,23 @@ +using System.Threading; +using System.Threading.Tasks; + +namespace ShellProgressBar.Example.Examples +{ + public class DrawsOnlyOnTickExample : IProgressBarExample + { + public Task Start(CancellationToken token) + { + var ticks = 5; + var updateOnTicksOnlyOptions = new ProgressBarOptions {DisplayTimeInRealTime = false}; + using (var pbar = new ProgressBar(ticks, "only update time on ticks", updateOnTicksOnlyOptions)) + { + for (var i = 0; i < ticks; i++) + { + pbar.Tick("only update time on ticks, current: " + i); + Thread.Sleep(1750); + } + } + return Task.FromResult(1); + } + } +} \ No newline at end of file diff --git a/src/ShellProgressBar.Example/Examples/LongRunningExample.cs b/src/ShellProgressBar.Example/Examples/LongRunningExample.cs new file mode 100644 index 0000000..18f0efa --- /dev/null +++ b/src/ShellProgressBar.Example/Examples/LongRunningExample.cs @@ -0,0 +1,23 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace ShellProgressBar.Example.Examples +{ + public class LongRunningExample : IProgressBarExample + { + public Task Start(CancellationToken token) + { + var ticks = 100; + using (var pbar = new ProgressBar(ticks, "my long running operation", ConsoleColor.Green)) + { + for (var i = 0; i < ticks; i++) + { + pbar.Tick("step " + i); + Thread.Sleep(50); + } + } + return Task.FromResult(1); + } + } +} \ No newline at end of file diff --git a/src/ShellProgressBar.Example/Examples/NegativeMaxTicksExample.cs b/src/ShellProgressBar.Example/Examples/NegativeMaxTicksExample.cs new file mode 100644 index 0000000..0f8df1d --- /dev/null +++ b/src/ShellProgressBar.Example/Examples/NegativeMaxTicksExample.cs @@ -0,0 +1,23 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace ShellProgressBar.Example.Examples +{ + public class NegativeMaxTicksExample : IProgressBarExample + { + public Task Start(CancellationToken token) + { + var ticks = -100; + using (var pbar = new ProgressBar(ticks, "my operation with negative ticks", ConsoleColor.Cyan)) + { + for (var i = 0; i < ticks; i++) + { + pbar.Tick("step " + i); + Thread.Sleep(50); + } + } + return Task.FromResult(1); + } + } +} \ No newline at end of file diff --git a/src/ShellProgressBar.Example/Examples/NestedProgressBarPerStepProgress.cs b/src/ShellProgressBar.Example/Examples/NestedProgressBarPerStepProgress.cs new file mode 100644 index 0000000..f5b0e0a --- /dev/null +++ b/src/ShellProgressBar.Example/Examples/NestedProgressBarPerStepProgress.cs @@ -0,0 +1,64 @@ +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace ShellProgressBar.Example.Examples +{ + public class NestedProgressBarPerStepProgress : IProgressBarExample + { + public Task Start(CancellationToken token) + { + var outerTicks = 10; + using (var pbar = new ProgressBar(outerTicks, "outer progress", ConsoleColor.Cyan)) + { + for (var i = 0; i < outerTicks; i++) + { + InnerProgressBars(pbar); + pbar.Tick(); + } + } + return Task.FromResult(1); + } + + private static void InnerProgressBars(ProgressBar pbar) + { + var innerProgressBars = Enumerable.Range(0, new Random().Next(2, 6)) + .Select(s => pbar.Spawn(new Random().Next(2, 5), $"inner bar {s}")) + .ToList(); + + var maxTicks = innerProgressBars.Max(p => p.MaxTicks); + + for (var ii = 0; ii < maxTicks; ii++) + { + foreach (var p in innerProgressBars) + { + InnerInnerProgressBars(p); + p.Tick(); + } + + + Thread.Sleep(4); + } + foreach (var p in innerProgressBars) p.Dispose(); + } + + private static void InnerInnerProgressBars(ChildProgressBar pbar) + { + var progressBarOption = new ProgressBarOptions { ForeGroundColor = ConsoleColor.Yellow }; + var innerProgressBars = Enumerable.Range(0, new Random().Next(1, 3)) + .Select(s => pbar.Spawn(new Random().Next(5, 10), $"inner bar {s}", progressBarOption)) + .ToList(); + if (!innerProgressBars.Any()) return; + + var maxTicks = innerProgressBars.Max(p => p.MaxTicks); + + for (var ii = 0; ii < maxTicks; ii++) + { + foreach (var p in innerProgressBars) + p.Tick(); + } + foreach (var p in innerProgressBars) p.Dispose(); + } + } +} \ No newline at end of file diff --git a/src/ShellProgressBar.Example/Examples/NeverCompletesExample.cs b/src/ShellProgressBar.Example/Examples/NeverCompletesExample.cs new file mode 100644 index 0000000..6c21404 --- /dev/null +++ b/src/ShellProgressBar.Example/Examples/NeverCompletesExample.cs @@ -0,0 +1,21 @@ +using System.Threading; +using System.Threading.Tasks; + +namespace ShellProgressBar.Example.Examples +{ + public class NeverCompletesExample : IProgressBarExample + { + public Task Start(CancellationToken token) + { + var ticks = 5; + using (var pbar = new ProgressBar(ticks, "A console progress bar does not complete")) + { + pbar.Tick(); + pbar.Tick(); + pbar.Tick(); + pbar.Tick(); + } + return Task.FromResult(1); + } + } +} \ No newline at end of file diff --git a/src/ShellProgressBar.Example/Examples/NeverTicksExample.cs b/src/ShellProgressBar.Example/Examples/NeverTicksExample.cs new file mode 100644 index 0000000..a45acfa --- /dev/null +++ b/src/ShellProgressBar.Example/Examples/NeverTicksExample.cs @@ -0,0 +1,17 @@ +using System.Threading; +using System.Threading.Tasks; + +namespace ShellProgressBar.Example.Examples +{ + public class NeverTicksExample : IProgressBarExample + { + public Task Start(CancellationToken token) + { + var ticks = 10; + using (var pbar = new ProgressBar(ticks, "A console progress bar that never ticks")) + { + } + return Task.FromResult(1); + } + } +} \ No newline at end of file diff --git a/src/ShellProgressBar.Example/Examples/ThreadedTicksOverflowExample.cs b/src/ShellProgressBar.Example/Examples/ThreadedTicksOverflowExample.cs new file mode 100644 index 0000000..1a602e2 --- /dev/null +++ b/src/ShellProgressBar.Example/Examples/ThreadedTicksOverflowExample.cs @@ -0,0 +1,22 @@ +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace ShellProgressBar.Example.Examples +{ + public class ThreadedTicksOverflowExample : IProgressBarExample + { + public Task Start(CancellationToken token) + { + var ticks = 200; + using (var pbar = new ProgressBar(ticks/10, "My operation that ticks to often using threads", ConsoleColor.Cyan)) + { + var threads = Enumerable.Range(0, ticks).Select(i => new Thread(() => pbar.Tick("threaded tick " + i))).ToList(); + foreach (var thread in threads) thread.Start(); + foreach (var thread in threads) thread.Join(); + } + return Task.FromResult(1); + } + } +} \ No newline at end of file diff --git a/src/ShellProgressBar.Example/Examples/TicksOverflowExample.cs b/src/ShellProgressBar.Example/Examples/TicksOverflowExample.cs new file mode 100644 index 0000000..a3d6c2c --- /dev/null +++ b/src/ShellProgressBar.Example/Examples/TicksOverflowExample.cs @@ -0,0 +1,23 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace ShellProgressBar.Example.Examples +{ + public class TicksOverflowExample : IProgressBarExample + { + public Task Start(CancellationToken token) + { + var ticks = 10; + using (var pbar = new ProgressBar(ticks, "My operation that ticks to often", ConsoleColor.Cyan)) + { + for (var i = 0; i < ticks*10; i++) + { + pbar.Tick("too many steps " + i); + Thread.Sleep(50); + } + } + return Task.FromResult(1); + } + } +} \ No newline at end of file diff --git a/src/ShellProgressBar.Example/Examples/UpdatesMaxTicksExample.cs b/src/ShellProgressBar.Example/Examples/UpdatesMaxTicksExample.cs new file mode 100644 index 0000000..8d022c3 --- /dev/null +++ b/src/ShellProgressBar.Example/Examples/UpdatesMaxTicksExample.cs @@ -0,0 +1,30 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace ShellProgressBar.Example.Examples +{ + public class UpdatesMaxTicksExample : IProgressBarExample + { + public Task Start(CancellationToken token) + { + var ticks = 10; + using (var pbar = new ProgressBar(ticks, "My operation that updates maxTicks", ConsoleColor.Cyan)) + { + var sleep = 1000; + for (var i = 0; i < ticks; i++) + { + pbar.Tick("Updating maximum ticks " + i); + if (i == 5) + { + ticks = 120; + pbar.UpdateMaxTicks(ticks); + sleep = 50; + } + Thread.Sleep(sleep); + } + } + return Task.FromResult(1); + } + } +} \ No newline at end of file diff --git a/src/ShellProgressBar.Example/Examples/ZeroMaxTicksExample.cs b/src/ShellProgressBar.Example/Examples/ZeroMaxTicksExample.cs new file mode 100644 index 0000000..184c7ef --- /dev/null +++ b/src/ShellProgressBar.Example/Examples/ZeroMaxTicksExample.cs @@ -0,0 +1,23 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace ShellProgressBar.Example.Examples +{ + public class ZeroMaxTicksExample : IProgressBarExample + { + public Task Start(CancellationToken token) + { + var ticks = 0; + using (var pbar = new ProgressBar(ticks, "my operation with zero ticks", ConsoleColor.Cyan)) + { + for (var i = 0; i < ticks; i++) + { + pbar.Tick("step " + i); + Thread.Sleep(50); + } + } + return Task.FromResult(1); + } + } +} \ No newline at end of file diff --git a/src/ShellProgressBar.Example/IProgressBarExample.cs b/src/ShellProgressBar.Example/IProgressBarExample.cs new file mode 100644 index 0000000..297b7ea --- /dev/null +++ b/src/ShellProgressBar.Example/IProgressBarExample.cs @@ -0,0 +1,10 @@ +using System.Threading; +using System.Threading.Tasks; + +namespace ShellProgressBar.Example +{ + public interface IProgressBarExample + { + Task Start(CancellationToken token); + } +} \ No newline at end of file diff --git a/src/ShellProgressBar.Example/Program.cs b/src/ShellProgressBar.Example/Program.cs index 1274b9a..cfaceb5 100644 --- a/src/ShellProgressBar.Example/Program.cs +++ b/src/ShellProgressBar.Example/Program.cs @@ -1,71 +1,64 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; +using ShellProgressBar.Example.Examples; namespace ShellProgressBar.Example { class Program { + private static readonly IList ExampleProgressBars = new List + { + new DeeplyNestedProgressBarTreeExample(), + new NestedProgressBarPerStepProgress(), + new DrawsOnlyOnTickExample(), + new ThreadedTicksOverflowExample(), + new TicksOverflowExample(), + new NegativeMaxTicksExample(), + new ZeroMaxTicksExample(), + new LongRunningExample(), + new NeverCompletesExample(), + new UpdatesMaxTicksExample(), + new NeverTicksExample(), + }; + static void Main(string[] args) { - var ticks = 10; - using (var pbar = new ProgressBar(ticks, "A console progress bar that never ticks")) - { - } - using (var pbar = new ProgressBar(ticks, "A console progress bar does not complete")) - { - pbar.Tick(); - pbar.Tick(); - pbar.Tick(); - pbar.Tick(); - } - ticks = 100; - using (var pbar = new ProgressBar(ticks, "my long running operation", ConsoleColor.Green)) - { - for (var i = 0; i < ticks; i++) - { - pbar.Tick("step " + i); - Thread.Sleep(50); - } - } - ticks = 0; - using (var pbar = new ProgressBar(ticks, "my operation with zero ticks", ConsoleColor.Cyan)) - { - for (var i = 0; i < ticks; i++) - { - pbar.Tick("step " + i); - Thread.Sleep(50); - } - } - ticks = -100; - using (var pbar = new ProgressBar(ticks, "my operation with negative ticks", ConsoleColor.Cyan)) - { - for (var i = 0; i < ticks; i++) - { - pbar.Tick("step " + i); - Thread.Sleep(50); - } - } - ticks = 10; - using (var pbar = new ProgressBar(ticks, "My operation that ticks to often", ConsoleColor.Cyan)) + Console.WindowWidth = Console.LargestWindowWidth / 2; + Console.WindowHeight = Console.LargestWindowHeight / 3; + + var cts = new CancellationTokenSource(); + + Console.CancelKeyPress += (s, e) => { - for (var i = 0; i < ticks * 10; i++) - { - pbar.Tick("too many steps " + i); - Thread.Sleep(50); - } - } - ticks = 200; - using (var pbar = new ProgressBar(ticks / 10, "My operation that ticks to often using threads", ConsoleColor.Cyan)) + e.Cancel = true; + cts.Cancel(); + }; + + MainAsync(args, cts.Token).GetAwaiter().GetResult(); + } + + static async Task MainAsync(string[] args, CancellationToken token) + { + foreach (var example in ExampleProgressBars) { - var threads = Enumerable.Range(0, ticks).Select(i => new Thread(() => pbar.Tick("threaded tick " + i))).ToList(); - foreach (var thread in threads) thread.Start(); - foreach (var thread in threads) thread.Join(); + await example.Start(token); + Console.Clear(); } + Console.ReadLine(); } + public static void BusyWait(int milliseconds) + { + var sw = Stopwatch.StartNew(); + + while (sw.ElapsedMilliseconds < milliseconds) + Thread.SpinWait(1000); + } + } } diff --git a/src/ShellProgressBar.Example/ShellProgressBar.Example.csproj b/src/ShellProgressBar.Example/ShellProgressBar.Example.csproj index a428070..79fda04 100644 --- a/src/ShellProgressBar.Example/ShellProgressBar.Example.csproj +++ b/src/ShellProgressBar.Example/ShellProgressBar.Example.csproj @@ -42,8 +42,20 @@ + + + + + + + + + + + + @@ -54,6 +66,7 @@ ShellProgressBar +