developer's diary

最近はc#のエントリが多いです

C# (dotnetcore)で標準出力を利用して進行中のスピナーを作る (6:完)

Console用のスピナー用の部品を作ろうという連載を初めて6記事目。

Console用のスピナー用の部品の開発は、今回を持って一旦終了とします。

前回の記事

mitsugeek.net

Stepの実装を修正

ProgressBar.Step プロパティ をみても、特に何もしていなさそう。このドキュメントからわかることは、 既定値は 10ということだけか。

        private int step = 10;

        /// <summary>PerformStep() メソッドを呼び出したときに、プログレス バーの現在の位置を進める量を取得または設定します。</summary>
        public int Step {
            get
            {
                return this.step;
            }
            set
            {
                this.step = value;
            }
        } 

Stepのテストを実装

デフォルト値の試験

        [Fact]
        public void StepDefaultTest()
        {
            ConsoleProgressSpiner c = new ConsoleProgressSpiner();
            Assert.Equal(10, c.Step);
        }
        

PerformStep を実装する

PerformStepでは、プロパティから値を設定せず、直接内部の値にインクリメント。

そして、minimumとmaximum の間に治るよう調整してから、updateを実行という流れ。

今回は、Reference Sourceを参考にしました。

        /// <summary>プログレス バーの現在位置を Step プロパティの値の分だけ進めます。 </summary>
        public void PerformStep()
        {
            this.value += this.Step;

            if (this.value < minimum)
            {
                this.value = minimum;
            }
            if (this.value > maximum)
            {
                this.value = maximum;
            }

            update();
        }

PerformStepのテストを実装

デフォルト値で0~100まで、PerformStepを実行した場合のテストケースを追加しておく。

        [Fact]
        public void PerformStepTest()
        {
            var output = new System.IO.StringWriter();
            Console.SetOut(output);
            ConsoleProgressSpiner c = new ConsoleProgressSpiner();
            Assert.Equal("", output.ToString());
            c.Value = 0;
            Assert.Equal("⠋  0%", output.ToString());
            c.PerformStep();
            Assert.Equal("⠋  0%⠋ 10%", output.ToString());
            c.PerformStep();
            Assert.Equal("⠋  0%⠋ 10%⠋ 20%", output.ToString());
            c.PerformStep();
            Assert.Equal("⠋  0%⠋ 10%⠋ 20%⠋ 30%", output.ToString());
            c.PerformStep();
            Assert.Equal("⠋  0%⠋ 10%⠋ 20%⠋ 30%⠋ 40%", output.ToString());
            c.PerformStep();
            Assert.Equal("⠋  0%⠋ 10%⠋ 20%⠋ 30%⠋ 40%⠋ 50%", output.ToString());
            c.PerformStep();
            Assert.Equal("⠋  0%⠋ 10%⠋ 20%⠋ 30%⠋ 40%⠋ 50%⠋ 60%", output.ToString());
            c.PerformStep();
            Assert.Equal("⠋  0%⠋ 10%⠋ 20%⠋ 30%⠋ 40%⠋ 50%⠋ 60%⠋ 70%", output.ToString());
            c.PerformStep();
            Assert.Equal("⠋  0%⠋ 10%⠋ 20%⠋ 30%⠋ 40%⠋ 50%⠋ 60%⠋ 70%⠋ 80%", output.ToString());
            c.PerformStep();
            Assert.Equal("⠋  0%⠋ 10%⠋ 20%⠋ 30%⠋ 40%⠋ 50%⠋ 60%⠋ 70%⠋ 80%⠋ 90%", output.ToString());
            c.PerformStep();
            Assert.Equal("⠋  0%⠋ 10%⠋ 20%⠋ 30%⠋ 40%⠋ 50%⠋ 60%⠋ 70%⠋ 80%⠋ 90%Done! \n", output.ToString());
        }

ConsoleProgressSpinerクラスの実装全体

    /// <summary>コンソール用のスピナー</summary>
    public class ConsoleProgressSpiner
    {

        //アニメーションのコマ
        string[] bars = { "⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏" };


        private int minimum = 0;

        /// <summary>コントロールの範囲の最小値を取得または設定します。</summary>
        public int Minimum
        {
            get
            {
                return minimum;
            }
            set
            {
                if (value < 0)
                    throw new ArgumentException("プロパティに対して指定された値が 0 未満です。");

                minimum = value;

                if (this.minimum > this.value)
                {
                    this.value = this.minimum;
                }
            }
        }

        private int maximum = 100;

        /// <summary>コントロールの範囲の最大値を取得または設定します。</summary>
        public int Maximum
        {
            get
            {
                return maximum;
            }
            set
            {
                if (value < 0)
                    throw new ArgumentException("プロパティに対して指定された値が 0 未満です。");

                maximum = value;

                if (this.maximum < this.value)
                {
                    this.value = this.maximum;
                }
            }
        }

        private int value = 0;

        /// <summary>プログレス バーの現在位置を取得または設定します。</summary>
        public int Value
        {
            get
            {
                return this.value;
            }
            set
            {
                if (value < this.Minimum)
                    throw new ArgumentException("指定された値が Minimum プロパティの値未満です。");

                if (value > this.Maximum)
                    throw new ArgumentException("指定された値が Maximum プロパティの値を超えます。");

                this.value = value;

                update();
            }
        }

        private int step = 10;

        /// <summary>PerformStep() メソッドを呼び出したときに、プログレス バーの現在の位置を進める量を取得または設定します。</summary>
        public int Step {
            get
            {
                return this.step;
            }
            set
            {
                this.step = value;
            }
        } 

        /// <summary>プログレス バーの現在位置を Step プロパティの値の分だけ進めます。 </summary>
        public void PerformStep()
        {
            this.value += this.Step;

            if (this.value < minimum)
            {
                this.value = minimum;
            }
            if (this.value > maximum)
            {
                this.value = maximum;
            }

            update();
        }

        /// <summary>Minimum〜Maximumに対するValueの進捗率を計算して返却</summary>
        /// <returns>進捗率(%)</returns>
        private int CalcProportion()
        {
            decimal oneMeter = new decimal(100) / (this.Maximum - this.Minimum);
            return Decimal.ToInt32(oneMeter * (this.Value - this.Minimum));
        }

        /// <summary>進捗終了の印字</summary>
        private void DoneProgressConsoleWrite()
        {
            //色を戻す
            Console.ResetColor();

            //カーソル位置を変更
            Console.SetCursorPosition(0, Console.CursorTop);

            //終了の表示
            Console.WriteLine("Done! ");
        }

        /// <summary>進捗進行中の印字</summary>
        private void InProgressConsoleWrite()
        {
            //進捗率計算
            int proportion = CalcProportion();

            //カーソル位置を変更
            Console.SetCursorPosition(0, Console.CursorTop);

            //色変更
            Console.ForegroundColor = ConsoleColor.Green;
            Console.BackgroundColor = ConsoleColor.Black;

            //進捗率の印字
            Console.Write("{0}{1, 3:d0}%", bars[proportion % bars.Length], proportion);
        }

        /// <summary>描画</summary>
        private void update()
        {
            if (this.Maximum == this.Value)
            {
                DoneProgressConsoleWrite();
            }
            else
            {
                InProgressConsoleWrite();
            }
        }
    }

操作方法

    class Program
    {
        static void Main(string[] args)
        {
            ConsoleProgressSpiner progressSpiner = new ConsoleProgressSpiner();
            progressSpiner.Step = 1;
            for (int i = progressSpiner.Minimum; i <= progressSpiner.Maximum-1; i++)
            {
                progressSpiner.PerformStep();
                //一時停止
                System.Threading.Thread.Sleep(10);
            }
        }
    }

以上!

最初の記事はこちら。

mitsugeek.net