2016-03-03

PowerShellでユーザーフォームを作る - プログレスバー編 -

PowerShellでは、ユーザーの好みに合わせて、独自にフォームを作成することができます。

今回は「PowerShellでユーザーフォームを作る - プログレスバー編 -」です。

プログレスバーとは、容量の大きいデータを移動した時などに表示される、進捗状況をユーザーに明示するものです。

以前、Write-Progressコマンドレットを使用したプログレスバーについての記事を投稿しましたが、今回は.NETを使用したプログレスバーとなります。

<今回の完成品>


※画像は、処理が終了したところ。(バーが全て埋まったところ)

それではスクリプトを記述します。

# プログレスバー

Add-Type -AssemblyName System.Windows.Forms

$Form = New-Object System.Windows.Forms.Form
$Form.Size = "300,200"
$Form.Startposition = "CenterScreen"
$Form.Text = "プログレスバーのテスト"

$Button = New-Object System.Windows.Forms.Button
$Button.Location = "110,20"
$Button.Size = "80,30"
$Button.Text = "開始"

# プログレスバー
$Bar = New-Object System.Windows.Forms.ProgressBar
$Bar.Location = "10,100"
$Bar.Size = "260,30"
$Bar.Maximum = "10"
$Bar.Minimum = "0"
$Bar.Style = "Blocks"

# ボタンのクリックイベント
$Start = {
    For ( $i = 0 ; $i -lt 10 ; $i++ )
    {
        $Bar.Value = $i+1
        start-sleep -s 1
    }
    [System.Windows.Forms.MessageBox]::Show("完了しました", "info")
}
$Button.Add_Click($Start)

$Form.Controls.AddRange(@($Bar,$Button))

$Form.ShowDialog()

上記内容をコピーし、PowerShell ISEに貼り付けて、実行してみてください。
完成品と同じものが表示されるはずです。

それでは解説をしていきます。
また過去の記事で解説した部分については割愛致します。

内容をご確認いただきたい場合は【ユーザーフォーム - 基礎編 -】をご覧ください。

*****解説*****************************************************************************
# プログレスバー
$Bar = New-Object System.Windows.Forms.ProgressBar
$Bar.Location = "10,100"
$Bar.Size = "260,30"
$Bar.Maximum = "10"
$Bar.Minimum = "0"
$Bar.Style = "Blocks"
ここでは、ProgressBarクラスをインスタンス化し、各種プロパティを設定しています。
Locationプロパティで位置を、Sizeプロパティで長さと幅を指定しています。
MaximumプロパティとMinimumプロパティは名前の通り、最大値と最小値を指定します。
Styleプロパティは描画方法を指定するもので、「Blocks」「Continuous」「Marquee」から選択します。
その違いについては、画像を見ていただければおわかりのことと思います。

Blocks

Continuous

Marquee

さらに、StyleプロパティにMarqueeを指定した場合はMarqueeAnimationSpeedプロパティを指定することで、動く早さを調節することができます。

ここで、(私の環境だけかもしれませんが)謎の現象が起こっていることをお伝えしておきます。

謎1 バーの色が実行方法によって違う。
  PowerShell ISEから実行すると緑色、スクリプトファイルを右クリック→実行すると青色になる。

謎2 PowerShell ISE上では、Blocksを指定してもContinuousと同じ表示になる
  
謎3 Marqueeを指定しても、スクリプトファイルを右クリック→実行するとBlocksになる。

この3つの謎について、詳しくご存知の方がいらっしゃいましたら、コメントいただけると幸いです。

# ボタンのクリックイベント
$Start = {
    For ( $i = 0 ; $i -lt 10 ; $i++ )
    {
        $Bar.Value = $i+1
        start-sleep -s 1
    }
    [System.Windows.Forms.MessageBox]::Show("完了しました", "info")
}
$Button.Add_Click($Start)
ここでは、ボタンのクリックイベントを設定しています。
イベントの内容は、Valueプロパティでブログレスバーの値を1つ増やす+1秒待つ という処理を10回繰り返し、その後、完了メッセージを表示します。

*************************************************************************************************

解説は以上となります。

容量の大きいファイルや大量のファイルを取り扱う場合、処理に時間がかかっていると、フリーズしたと勘違いする場合があり、✕ボタンを連打し、強制的に終了するなんてこともあります。

そういったことを防ぐためにもプログレスバーを表示するようにし、ちゃんと動いていることを明確にしてあげましょう!

=============================================================
本投稿に関する疑問や質問には可能な限りお答えさせていただきます。
お気軽にコメントやメールをお送りください。
(リクエストも歓迎します)
メール:tkk-powershell@gmail.com
また、間違いのご指摘・アドバイス等も歓迎いたします。
=============================================================
Google+、Twitterで更新情報をお届けしています!
ぜひフォローをお願い致します!           
=============================================================
スポンサーリンク


5 件のコメント:

  1. PSに元からあるWrite-Progressと、この記事のプログレスバーと入れ替えたいのですが、
    うまく機能しません。
    お知恵を拝借できますでしょうか。

    ロジックとしては、掲載いただいたFormを表示して、
    その後に、foreachでメイン処理を行い、その中で$Bar.Valueを
    発行しています。

    症状としては、Formは表示されますが、
    1.プログレス表示は更新されない。
    2.Formが表示されている間、メインのforeach処理は
    全く動かない(Formを閉じると動き出す)

    Form On Load的なイベントを組み込んでメインのforeachを
    使うべきでしょうか。。。。

    お助けいただけますと幸いです。

    返信削除
  2. コメントありがとうございます。

    大変恐縮ですが、いただきましたコメント内容だけでは原因を掌握しかねます。
    そこで、差し支えない部分だけで結構ですので、作成されたスクリプトファイルの内容をみせていただけないでしょうか。

    コメント欄への記載でも直接メールでも構いません。

    できるだけご助力させていただきたいと思いますので、ご検討の程、宜しくお願い致します。

    返信削除
    返信
    1. ご返信ありがとうございます。


      ソースコードですが、記事を拝見して、「こんな感じ?」とサラッと記述しただけなので、
      正直、まじめに書いた内容ではありませんが。。。。
      ごく単純に、(最初は)記事にあるfor文を、ボタンイベントではなく、外に単独で
      切り出したカタチが、私が期待する最終形にほぼ等しい構成となります。

      Write-Progressの時同様に、ループ分の処理中に、プログレス値を更新すれば
      動くと思っていました。

      以下、その内容です。よろしくお願い申し上げます。


      # プログレスバー

      Add-Type -AssemblyName System.Windows.Forms

      $Form = New-Object System.Windows.Forms.Form
      $Form.Size = "300,200"
      $Form.Startposition = "CenterScreen"
      $Form.Text = "プログレスバーのテスト"

      $Button = New-Object System.Windows.Forms.Button
      $Button.Location = "110,20"
      $Button.Size = "80,30"
      $Button.Text = "開始"

      # プログレスバー
      $Bar = New-Object System.Windows.Forms.ProgressBar
      $Bar.Location = "10,100"
      $Bar.Size = "260,30"
      $Bar.Maximum = "10"
      $Bar.Minimum = "0"
      $Bar.Style = "Blocks"

      # ボタンのクリックイベント
      #$Start = {
      # For ( $i = 0 ; $i -lt 10 ; $i++ )
      # {
      # $Bar.Value = $i+1
      # start-sleep -s 1
      # }
      # [System.Windows.Forms.MessageBox]::Show("完了しました", "info")
      #}
      #$Button.Add_Click($Start)
      #
      $Form.Controls.AddRange(@($Bar,$Button))

      $Form.ShowDialog()

      For ( $i = 0 ; $i -lt 10 ; $i++ )
      {
      $Bar.Value = $i+1
      start-sleep -s 1
      }
      [System.Windows.Forms.MessageBox]::Show("完了しました", "info")


      削除
    2. ソース拝見いたしました。

      $Form.ShowDialog()以降のFor文が、フォームを閉じないと処理されないということですが、その原因が判明致しました。

      当ブログではまだ触れていませんでしたが、フォームを表示する方法として「モーダル」と「モードレス」の2種類があります。

      モーダルは.ShowDialog()と記述し、モードレスは.Show()と記述します。
      この違いをざっくりまとめると次の通りとなります。
      ①モーダル … フォームが閉じられるまで、ShowDialog()以降の処理が行われない。
      ②モードレス … フォームが閉じられなくても、Show()以降の処理が行われる。

      基本的にプログラムは上から順に処理を行っていきます。
      上記スクリプトはモーダルで表示させていますので、ShowDialog()以降のFor文はフォームが閉じられるまで処理されなかったのです。

      したがって、ShowDialog()をShow()とすれば、プログレスバーは動き出し、メッセージボックスも表示されます。
      ただし、その後フリーズしてしまいますのでご注意ください。
      詳細は今後のブログ内で取り上げますが、フリーズするのは「メインとなるフォームはモーダルで表示させなければならない」というルールがあるからです。

      以上、ご参考のほど、宜しくお願い致します。

      削除
    3. ご回答ありがとうございます。
      モードの違い、承知しました。ひとまず・・・メイン画面上に設けることにしました。

      ところで、PSコンソール上から当該スクリプトを実行した場合も、マーキーはブロックになりますね。
      なぜなんでしょう。。。

      削除

疑問・質問・リクエスト お気軽にどうぞ (^O^)/