Skip to content

Commit

Permalink
Add a combined example with Live, Status and Progress
Browse files Browse the repository at this point in the history
  • Loading branch information
ChrisPulman committed Aug 26, 2023
1 parent fe057fb commit f1c65ce
Show file tree
Hide file tree
Showing 20 changed files with 557 additions and 88 deletions.
14 changes: 14 additions & 0 deletions src/CombinedExample/CombinedExample.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\Spectre.Console.Rx\Spectre.Console.Rx.csproj" />
</ItemGroup>

</Project>
52 changes: 52 additions & 0 deletions src/CombinedExample/DescriptionGenerator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Copyright (c) Chris Pulman. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace ProgressDemo;

/// <summary>
/// DescriptionGenerator.
/// </summary>
public static class DescriptionGenerator
{
private static readonly string[] _verbs = new[] { "Downloading", "Rerouting", "Retriculating", "Collapsing", "Folding", "Solving", "Colliding", "Measuring" };
private static readonly string[] _nouns = new[] { "internet", "splines", "space", "capacitators", "quarks", "algorithms", "data structures", "spacetime" };

private static readonly Random _random;
private static readonly HashSet<string> _used;

static DescriptionGenerator()
{
_random = new Random(DateTime.Now.Millisecond);
_used = new HashSet<string>();
}

/// <summary>
/// Tries the generate.
/// </summary>
/// <param name="name">The name.</param>
/// <returns>A bool.</returns>
public static bool TryGenerate(out string name)
{
for (var iterations = 0; iterations < 25; iterations++)
{
name = Generate();
if (!_used.Contains(name))
{
_used.Add(name);
return true;
}
}

name = Generate();
return false;
}

/// <summary>
/// Generates this instance.
/// </summary>
/// <returns>A string.</returns>
public static string Generate() =>
_verbs[_random.Next(0, _verbs.Length)]
+ " "
+ _nouns[_random.Next(0, _nouns.Length)];
}
209 changes: 209 additions & 0 deletions src/CombinedExample/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
// Copyright (c) Chris Pulman. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.Reactive.Concurrency;
using System.Reactive.Linq;
using ProgressDemo;
using Spectre.Console.Rx;

// NOTE: This code executes synchronously, only one instance of the console context can be executing at a time.
// This is because the console context is not thread safe.
// Remember to call IsFinished() on the console context when you're done with it.
var table = new Table().LeftAligned();

AnsiConsoleRx
.Status(
"[yellow]Initializing warp drive[/]",
p => p.AutoRefresh(true).Spinner(Spinner.Known.Default))
.ObserveOn(AnsiConsoleRx.Scheduler)
.Subscribe(
ctx => ctx.Schedule(async scheduler =>
{
// Initialize
await scheduler.Sleep(TimeSpan.FromMilliseconds(3000));
WriteLogMessage("Starting gravimetric field displacement manifold");
await scheduler.Sleep(TimeSpan.FromMilliseconds(1000));
WriteLogMessage("Warming up deuterium chamber");
await scheduler.Sleep(TimeSpan.FromMilliseconds(2000));
WriteLogMessage("Generating antideuterium");
// Warp nacelles
await scheduler.Sleep(TimeSpan.FromMilliseconds(3000));
ctx.Spinner(Spinner.Known.BouncingBar);
ctx.Status("[bold blue]Unfolding warp nacelles[/]");
WriteLogMessage("Unfolding left warp nacelle");
await scheduler.Sleep(TimeSpan.FromMilliseconds(2000));
WriteLogMessage("Left warp nacelle [green]online[/]");
WriteLogMessage("Unfolding right warp nacelle");
await scheduler.Sleep(TimeSpan.FromMilliseconds(1000));
WriteLogMessage("Right warp nacelle [green]online[/]");
// Warp bubble
await scheduler.Sleep(TimeSpan.FromMilliseconds(3000));
ctx.Spinner(Spinner.Known.Star2);
ctx.Status("[bold blue]Generating warp bubble[/]");
await scheduler.Sleep(TimeSpan.FromMilliseconds(3000));
ctx.Spinner(Spinner.Known.Star);
ctx.Status("[bold blue]Stabilizing warp bubble[/]");
// Safety
ctx.Spinner(Spinner.Known.Monkey);
ctx.Status("[bold blue]Performing safety checks[/]");
WriteLogMessage("Enabling interior dampening");
await scheduler.Sleep(TimeSpan.FromMilliseconds(2000));
WriteLogMessage("Interior dampening [green]enabled[/]");
// Warp!
await scheduler.Sleep(TimeSpan.FromMilliseconds(3000));
ctx.Spinner(Spinner.Known.Moon);
WriteLogMessage("Preparing for warp");
await scheduler.Sleep(TimeSpan.FromMilliseconds(1000));
for (var warp = 1; warp < 10; warp++)
{
ctx.Status($"[bold blue]Warp {warp}[/]");
await scheduler.Sleep(TimeSpan.FromMilliseconds(500));
}
// Done
WriteLogMessage("[bold green]Crusing at Warp 9[/]");
// Finish animation and allow next animation to start
ctx.IsFinished();
}));

// Animate
AnsiConsoleRx.Live(table, ld => ld.AutoClear(false).Overflow(VerticalOverflow.Ellipsis).Cropping(VerticalOverflowCropping.Top))
.ObserveOn(AnsiConsoleRx.Scheduler)
.Subscribe(
ctx =>
// Columns
ctx.Update(230, () => table.AddColumn("Release date"))
.Update(230, () => table.AddColumn("Title"))
.Update(230, () => table.AddColumn("Budget"))
.Update(230, () => table.AddColumn("Opening Weekend"))
.Update(230, () => table.AddColumn("Box office"))
// Rows
.Update(70, () => table.AddRow("May 25, 1977", "[yellow]Star Wars[/] [grey]Ep.[/] [u]IV[/]", "$11,000,000", "$1,554,475", "$775,398,007"))
.Update(70, () => table.AddRow("May 21, 1980", "[yellow]Star Wars[/] [grey]Ep.[/] [u]V[/]", "$18,000,000", "$4,910,483", "$547,969,004"))
.Update(70, () => table.AddRow("May 25, 1983", "[yellow]Star Wars[/] [grey]Ep.[/] [u]VI[/]", "$32,500,000", "$23,019,618", "$475,106,177"))
.Update(70, () => table.AddRow("May 19, 1999", "[yellow]Star Wars[/] [grey]Ep.[/] [u]I[/]", "$115,000,000", "$64,810,870", "$1,027,044,677"))
.Update(70, () => table.AddRow("May 16, 2002", "[yellow]Star Wars[/] [grey]Ep.[/] [u]II[/]", "$115,000,000", "$80,027,814", "$649,436,358"))
.Update(70, () => table.AddRow("May 19, 2005", "[yellow]Star Wars[/] [grey]Ep.[/] [u]III[/]", "$113,000,000", "$108,435,841", "$850,035,635"))
.Update(70, () => table.AddRow("Dec 18, 2015", "[yellow]Star Wars[/] [grey]Ep.[/] [u]VII[/]", "$245,000,000", "$247,966,675", "$2,068,223,624"))
.Update(70, () => table.AddRow("Dec 15, 2017", "[yellow]Star Wars[/] [grey]Ep.[/] [u]VIII[/]", "$317,000,000", "$220,009,584", "$1,333,539,889"))
.Update(70, () => table.AddRow("Dec 20, 2019", "[yellow]Star Wars[/] [grey]Ep.[/] [u]IX[/]", "$245,000,000", "$177,383,864", "$1,074,114,248"))
// Column footer
.Update(230, () => table.Columns[2].Footer("$1,633,000,000"))
.Update(230, () => table.Columns[3].Footer("$928,119,224"))
.Update(400, () => table.Columns[4].Footer("$10,318,030,576"))
// Column alignment
.Update(230, () => table.Columns[2].RightAligned())
.Update(230, () => table.Columns[3].RightAligned())
.Update(400, () => table.Columns[4].RightAligned())
// Column titles
.Update(70, () => table.Columns[0].Header("[bold]Release date[/]"))
.Update(70, () => table.Columns[1].Header("[bold]Title[/]"))
.Update(70, () => table.Columns[2].Header("[red bold]Budget[/]"))
.Update(70, () => table.Columns[3].Header("[green bold]Opening Weekend[/]"))
.Update(400, () => table.Columns[4].Header("[blue bold]Box office[/]"))
// Footers
.Update(70, () => table.Columns[2].Footer("[red bold]$1,633,000,000[/]"))
.Update(70, () => table.Columns[3].Footer("[green bold]$928,119,224[/]"))
.Update(400, () => table.Columns[4].Footer("[blue bold]$10,318,030,576[/]"))
// Title
.Update(500, () => table.Title("Star Wars Movies"))
.Update(400, () => table.Title("[[ [yellow]Star Wars Movies[/] ]]"))
// Borders
.Update(230, () => table.BorderColor(Color.Yellow))
.Update(230, () => table.MinimalBorder())
.Update(230, () => table.SimpleBorder())
.Update(230, () => table.SimpleHeavyBorder())
// Caption
.Update(400, () => table.Caption("[[ [blue]THE END[/] ]]"))
// Finish animation and allow next animation to start
.IsFinished());

AnsiConsole.MarkupLine("[yellow]Initializing warp drive[/]...");

// Show progress
AnsiConsoleRx.Progress(p =>
p.AutoClear(false)
.Columns(new ProgressColumn[]
{
new TaskDescriptionColumn(), // Task description
new ProgressBarColumn(), // Progress bar
new PercentageColumn(), // Percentage
new RemainingTimeColumn(), // Remaining time
new SpinnerColumn(), // Spinner
}))
.ObserveOn(AnsiConsoleRx.Scheduler)
.Subscribe(async ctx =>
{
var random = new Random(DateTime.Now.Millisecond);
// Create some tasks
var tasks = CreateTasks(ctx, random);
var warpTask = ctx.AddTask("Going to warp", autoStart: false).IsIndeterminate();
await ctx.Schedule(
TimeSpan.FromMilliseconds(100),
() => ctx.IsFinished,
() =>
{
// Increment progress
foreach (var (task, increment) in tasks)
{
task.Increment(random.NextDouble() * increment);
}
// Write some random things to the terminal
if (random.NextDouble() < 0.1)
{
GenerateLogMessage();
}
});
// Now start the "warp" task
warpTask.StartTask().IsIndeterminate(false);
await ctx.Schedule(
TimeSpan.FromMilliseconds(100),
() => warpTask.Increment(12 * random.NextDouble()));
// Done
await ctx.Schedule(_ => WriteLogMessage("[green]Done![/]"));
// Progress is finished and will automatically exit
});

WriteLogMessage("[red]Press ctrl+c to exit[/]");

static List<(ProgressTask Task, int Delay)> CreateTasks(ProgressContext context, Random random)
{
var tasks = new List<(ProgressTask, int)>();
while (tasks.Count < 5)
{
if (DescriptionGenerator.TryGenerate(out var name))
{
tasks.Add((context.AddTask(name), random.Next(2, 10)));
}
}

return tasks;
}

static void GenerateLogMessage() => AnsiConsole.MarkupLine(
"[grey]LOG:[/] " +
DescriptionGenerator.Generate() +
"[grey]...[/]");

static void WriteLogMessage(string message) => AnsiConsole.MarkupLine($"[grey]LOG:[/] {message}[grey]...[/]");
2 changes: 1 addition & 1 deletion src/LiveExample/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,6 @@ public static void Main()
.Update(230, () => table.SimpleHeavyBorder())
// Caption
.Update(400, () => table.Caption("[[ [blue]THE END[/] ]]")));
.Update(400, () => table.Caption("[[ [blue]THE END[/] ]]")).IsFinished());
}
}
7 changes: 7 additions & 0 deletions src/Spectre.Console.Rx.sln
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ProgressThreadExample", "Pr
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "_build", "..\build\_build.csproj", "{15A30A6D-9A94-4A7C-A74B-D7A9CB36C216}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CombinedExample", "CombinedExample\CombinedExample.csproj", "{00E6E161-7DD9-49BB-AEDC-FFFD0447368E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -51,6 +53,10 @@ Global
{CB209C31-2CD2-4188-A088-18E68DF23788}.Release|Any CPU.Build.0 = Release|Any CPU
{15A30A6D-9A94-4A7C-A74B-D7A9CB36C216}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{15A30A6D-9A94-4A7C-A74B-D7A9CB36C216}.Release|Any CPU.ActiveCfg = Release|Any CPU
{00E6E161-7DD9-49BB-AEDC-FFFD0447368E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{00E6E161-7DD9-49BB-AEDC-FFFD0447368E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{00E6E161-7DD9-49BB-AEDC-FFFD0447368E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{00E6E161-7DD9-49BB-AEDC-FFFD0447368E}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -61,6 +67,7 @@ Global
{9B8D02EB-C0CB-42B1-B44C-74471D83A8C2} = {E17E245C-D501-46B4-A804-A68241982088}
{51ACCE14-F44D-455B-A5E9-AEC71F7C6F61} = {E17E245C-D501-46B4-A804-A68241982088}
{CB209C31-2CD2-4188-A088-18E68DF23788} = {E17E245C-D501-46B4-A804-A68241982088}
{00E6E161-7DD9-49BB-AEDC-FFFD0447368E} = {E17E245C-D501-46B4-A804-A68241982088}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {2FADF055-54C6-4DE5-98D0-D6F352AB1EFA}
Expand Down
Loading

0 comments on commit f1c65ce

Please sign in to comment.