0
4

More than 5 years have passed since last update.

コンソールアプリケーションにアクティビティインジケータを表示

Last updated at Posted at 2019-02-11

概要

コンソールアプリケーションにおいて、重たい処理を実行中であることをユーザーに伝えるためのアクティビティインジケータ(ビジーマーク)を表示するプログラムをC#とF#で作成しました。

aaa.gif

一定時間毎に...を表示するためにRx(Observable.Timer)を利用しました。

  • 処理中
  • 処理中.
  • 処理中..
  • 処理中...
  • 処理が終わるまで上記の表示の繰り返し、処理が完了したら下記の表示で上書き
  • 処理完了

C#版

nugetでSystem.Reactiveをインストールしておきます。

using System;
using System.Reactive.Linq;
using System.Threading;
using System.Threading.Tasks;

class Program {

  // アクティビティインジケータを出力する関数
  static void IndicationPrinter(string msg1, string msg2, int i) {
    Console.SetCursorPosition(0, Console.CursorTop);
    if (i < 0) {
      Console.WriteLine(msg2);
    } else {
      var n = i % 4;
      Console.Write($"{msg1}{new string('.', n)}{new string(' ', 3 - n)}");
    }
  }

  // インジケータを出力しながら関数funcをする非同期で実行する関数
  static Task<T> RunWithActivityIndicator<S, T>(string msg1, string msg2, Func<S, T> func, S arg) {

    var sTimer = Observable.Timer(TimeSpan.FromSeconds(0), TimeSpan.FromSeconds(0.2));
    var timerSubscription = sTimer.Subscribe(i =>
      IndicationPrinter(msg1, msg2, (int)i));

    return Task.Run(() => {
      return func(arg);
    }).ContinueWith((Task<T> t) => {
      timerSubscription.Dispose();
      IndicationPrinter(msg1, msg2, -1);
      return t.Result;
    });
  }

  static void Main(string[] args) {

    Console.WriteLine("C# Ver.");
    //Console.CursorVisible = false; //カーソルを消したい場合

    Func<string, string> heavyWeightFunc1 = (string arg) => {
      Thread.Sleep(2500);
      return $"=={arg}==";
    };

    // 非常に時間がかかる関数1 (Func<int>) 
    Func<int> heavyWeightFunc2 = () => {
      Thread.Sleep(1500);
      return 0;
    };

    var msg1 = "処理中";
    var msg2 = "処理完了";

    var task1 = RunWithActivityIndicator(msg1, msg2, heavyWeightFunc1, "hoge");
    Console.WriteLine($"処理結果:{task1.Result}");

    Console.ReadKey();
  }
}

F#版

nugetでFSharp.Control.Reactiveをインストールしておきます。

open System
open System.Reactive.Linq
open FSharp.Control.Reactive
open System.Threading
open System.Threading.Tasks

// アクティビティインジケータを出力する関数
let indicationPrinter msg1 msg2 i = 
  Console.SetCursorPosition(0, Console.CursorTop) |> ignore
  if i < 0 then
    printfn "%s" msg2
  else
    match i%4 with
    | 0 -> ignore( printf "%s" (msg1+"   "))
    | 1 -> ignore( printf "%s" (msg1+".  "))
    | 2 -> ignore( printf "%s" (msg1+".. "))
    | _ -> ignore( printf "%s" (msg1+"..."))

// インジケータを出力しながら関数funcをする非同期で実行する関数
let runWithActivityIndicator (msg1,msg2) (func:'S->'T) arg =

  let indicationPrinter = indicationPrinter msg1 msg2

  let sTimer = Observable.Timer(TimeSpan.FromSeconds(0.),TimeSpan.FromSeconds(0.2))
  let timerSubscription = sTimer.Subscribe( fun i -> int(i) |> indicationPrinter  )

  let task = async { return func arg } |> Async.StartAsTask
  task.ContinueWith( fun (t:Task<'T>) -> 
    timerSubscription.Dispose()
    indicationPrinter -1
    t.Result ) 

[<EntryPoint>]
let main argv =

  printfn "F# Ver."
  //Console.CursorVisible <- false; // カーソルを消したい場合

  // 非常に時間がかかる関数1 (string->string) 
  let heavyWeightFunc1 arg =
    Thread.Sleep(1500)
    "==" + arg + "=="

  // 非常に時間がかかる関数2 (unit->string) 
  let heavyWeightFunc2 () =
    Thread.Sleep(2500)
    0

  // 非常に時間がかかる関数3 (unit->unit) 
  let heavyWeightFunc3 () =
    Thread.Sleep(1000)

  let msg = ("処理中","処理完了")  
  let task1 = runWithActivityIndicator msg heavyWeightFunc1 "hoge"
  printfn "%s" ("処理結果 : " + task1.Result )

  let task2 = runWithActivityIndicator msg heavyWeightFunc2 ()
  printfn "%s" ("処理結果 : " + (task2.Result.ToString()) )

  let task3 = runWithActivityIndicator msg heavyWeightFunc3 ()
  task3.Wait()
  printfn "." 

  Console.ReadKey() |> ignore
  0

参考資料

0
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
4