タケモ塾

[VBAユーザーフォームで進捗表示]刻み方が処理速度へ及ぼす影響

オープニング,本日の課題

こんにちは。エクセル開発15年、セルネッツの竹本です。

今回はVBAのユーザーフォームで進捗表示を解説していきます。

セルネッツ:VBAユーザーフォームで進捗表示 |刻み方が処理速度へ及ぼす影響

大量データ処理での状況表示

ユーザーフォームでの進捗の表示というイメージですが、セルネッツではこのような画面キャプチャーのイメージで表示するようにしています。

セルネッツ:VBAユーザーフォームで進捗表示 |刻み方が処理速度へ及ぼす影響

大量データで処理をしていく場合は、全体の何件目を今やっているのかというのがわからないとユーザーが不安になったりします。

動いているのか動いていないのか、やっているかやっていないかがわからないと困りますので、必ず表示をするようにしています。

処理状況が分かることの重要性

表示の方法ですが、処理状況がわかることの重要性として2つあると考えています。

セルネッツ:VBAユーザーフォームで進捗表示 |刻み方が処理速度へ及ぼす影響

まずはユーザー目線です。ユーザーの立場で不安にならないように、動いているかどうか、あと何件ぐらいで終わるのかが確認できることが重要だと思っています。

もうひとつは開発側に立った目線です。たとえばあるステップがやたら遅いというケースでは、このあたりにステップ番号、所持している内容を描画します。

たとえばお客様から「今までは結構早かったんですけど、最近急に遅くなった」とお問い合わせがあった場合、「ステータスバーのユーザーフォームのなにを処理をしているのかを読み上げてください」と伝えられるだけで、だいたいどのステップのどの処理が遅いかがわかってくるので、実際にプログラムソースを分析して原因を解決するという対応が可能です。

これは不具合が発生したときも、「何ステップまでは問題なくいったんだけれども、必ず何ステップでエラーになります」というような情報の手がかりにもなるので、開発者目線でも大事になってきます。

状況把握の方法3つ

状況把握の方法としてはいくつありますが、セルネッツでは1番のユーザーフォームでの表示を推奨しています。

セルネッツ:VBAユーザーフォームで進捗表示 |刻み方が処理速度へ及ぼす影響

その理由はこの表示項目として情報がたくさん表示できるからですが、基本的には処理の内容、全体の件数に対して今何件目をやっているのか、何秒経過したのか、そして強制終了ボタンが重要になってきます。

真ん中の2番目のプログレスバーは100をゴールとして、どのぐらい終わっているのかという経過は把握できるんですけど、情報としてはちょっと足りないので1番を推奨しています。

最後3番目はデモでお見せしますが、ステータスバーに表示されるタイプなので、小さすぎて見にくいとか、1行しか書けない、中断ボタンが実装する仕組みがない。実行時エラーがもし起きた場合、その段階で残像が残ったままになってしまうので、3番は手軽ですが推奨はしていません。

UserForm処理状況の表示

では実際に実演のデモをします。

時間計測が重要になってくるんですが、最近撮った動画で時間の計測の方法を紹介しているので、そちらをご覧ください。

実演デモでは4つについて解説をしていきたいと思います。

セルネッツ:VBAユーザーフォームで進捗表示 |刻み方が処理速度へ及ぼす影響

実演デモ①UserForm描画単位,中断の必要性

これがデモ用のファイルです。3万件のデータが入っています。

セルネッツ:VBAユーザーフォームで進捗表示 |刻み方が処理速度へ及ぼす影響

処理の内容としては単純にA列からF列までの内容をひとつずつ転記していきます。

セルネッツ:VBAユーザーフォームで進捗表示 |刻み方が処理速度へ及ぼす影響

そして何秒かかったのかというようなことで、すでに実測の結果をここに入れています。

この1件ごと10件ごとってなんなのかというと、ユーザーフォームの描画の単位を変えてみた場合の速度です。

セルネッツ:VBAユーザーフォームで進捗表示 |刻み方が処理速度へ及ぼす影響

まず最初にお見せします。

セルネッツ:VBAユーザーフォームで進捗表示 |刻み方が処理速度へ及ぼす影響

3万1行目が最終行です。3万件に対する処理が終わりました。5.031。この5.031というのは今100件ごとに見ているんです。

プログラムソースで解説をします。
処理状況の表示がこのモジュールに入ります。

セルネッツ:VBAユーザーフォームで進捗表示 |刻み方が処理速度へ及ぼす影響

最終行を求めて確認メッセージを出して、OKなら下に入っていきます。

セルネッツ:VBAユーザーフォームで進捗表示 |刻み方が処理速度へ及ぼす影響

時間計測もここからスタートするんですけど、出力エリアをいったんクリアして見出しをセットしたら、ここから下です。

重要な処理に入っていきます。

セルネッツ:VBAユーザーフォームで進捗表示 |刻み方が処理速度へ及ぼす影響

まずはデータを2行目から最終行までどんどん書いていきます。

モジュールの中のこの処理に入ってきましたが、ここですね、読み込み行。

セルネッツ:VBAユーザーフォームで進捗表示 |刻み方が処理速度へ及ぼす影響

MOD1000=0というのは、余りを求める関数がMOD関数、MOD演算子というMODですが、読み込み行を1000で割って0になるときだけやりなさいということで、ここからここまで、1000の単位で入ってくる、10の単位で入ってくる、100の単位で入ってくる、1万の単位で入ってきます。

セルネッツ:VBAユーザーフォームで進捗表示 |刻み方が処理速度へ及ぼす影響

ここに入ってくる回数がそもそも少なくなるので、それだけ処理は早くなるというのがこちらの例になります。

1件ずつ毎回描画をさせるということは、3万回描画をすることになるので、これだけの時間がかかってしまいますが、1000件ごとだと4.984秒ぐらいになるということです。先ほどは1000件でやっていたので、10件にしてみたいと思います。

中断できることも重要なので、これもデモで紹介します。

「はい」を押します。先ほどと描画の単位が違うことにお気づきだと思います。4秒5秒6秒。

セルネッツ:VBAユーザーフォームで進捗表示 |刻み方が処理速度へ及ぼす影響

ここで強制終了ボタンを押すといったん停止をして、処理を続行するかどうかを尋ねてきます。

セルネッツ:VBAユーザーフォームで進捗表示 |刻み方が処理速度へ及ぼす影響

「やっぱりやめた」ができることが重要です。

「やっぱりやめます」であれば、途中までは処理が行われましたが、そこから先はなにもしておらず、2万8949件目を書き終えたところで終わったので、この下は入っていません。

ということで、このデモの1番ですが、何件ごとに描画をするのか、1件の場合は時間はかかるんですけど、1件ごとにわかるということで、このあたりは時間がとても大事になってくるので、1件ごとの描画は必要ない場合のほうがたぶん多かったりするので、その場合は適宜、10件刻みなのか、100件刻みが良いのかということで、刻み方をMOD関数の隣のここを、刻み方を変えることで調整をしてみてください。

実演デモ②Application.StatusBar

そして先ほどちょっとご紹介しましたアプリケーションステータスバーでのやり方を紹介します。

ユーザーフォームを使った場合なので、いったん両方ともコメントアウトして、これも10件ごとでやってみます。

アプリケーション、ピリオド、ステータスバーということで、この条件にマッチしたときだけ入ってきます。ユーザーフォームを使わないパターンなので、終わったら元に戻します。

セルネッツ:VBAユーザーフォームで進捗表示 |刻み方が処理速度へ及ぼす影響

今回必要になるのは、ここでステータスバーに描画をするので、最終行が終わって出てきた場合はfalseで元に戻します。戻さないと残像が残ってしまいます。

ステータスバーと言っているのはこのあたりの領域のことです。

セルネッツ:VBAユーザーフォームで進捗表示 |刻み方が処理速度へ及ぼす影響

ではやってみたいと思います。

セルネッツ:VBAユーザーフォームで進捗表示 |刻み方が処理速度へ及ぼす影響

3万1行目まで。今一瞬で緑色に変わったと思うんですけど、5.374秒で終わりました。

緑色にしておきます。

セルネッツ:VBAユーザーフォームで進捗表示 |刻み方が処理速度へ及ぼす影響

違いはおわかりいただけたかと思います。

ユーザーフォームで表示をした場合、こういうユーザーフォームが出てきますが、今のやり方はステータスバーでやった場合です。表示の仕方が変わりました。これがデモの2番目のアプリケーションステータスバーを使った場合です。

実演デモ③UserForm(RePaintなし)

ユーザーフォームですが、リペイント、Doイベントというものがありますが、そちらを紹介しておきます。

ここをいったん元に戻して、ユーザーフォームが表示されるように、モードレスで表示されるように、そして終わったらユーザーフォームはクローズされるように。ユーザーフォームが表示されました。

解説すべきはここです。印をつけておきます。

セルネッツ:VBAユーザーフォームで進捗表示 |刻み方が処理速度へ及ぼす影響

UserFormの中身紹介

ユーザーフォームの中の要素のお話なんですけど、UF01をダブルクリックして、コントロールオールで見てみると、3つの要素があります。

セルネッツ:VBAユーザーフォームで進捗表示 |刻み方が処理速度へ及ぼす影響

これがテキストステータスという名前で、これがテキスト経過時間という名前で、これがBアンダーバー強制終了という名前になります。

セルネッツ:VBAユーザーフォームで進捗表示 |刻み方が処理速度へ及ぼす影響

なのでこことここに表示したい内容をここにセットすることでどんどん変わってくるようになります。

先ほどステップの処理の内容に関しては、今ここに処理中と見えているんですけど、この処理中と言っているのはここです。

ユーザーフォームを選択して、キャプションです。

セルネッツ:VBAユーザーフォームで進捗表示 |刻み方が処理速度へ及ぼす影響

このキャプションのところに入れたものが表示されますが、このプログラムで書き換えるだけです。このユーザーフォーム01、既定の名前はユーザーフォーム1とかなんですけど、名前が長いので、セルネッツでは略称でUF01としています。UF01の中身はこれだけです。

強制終了ボタンが押されたら確認メッセージが出て、「終了する」と言われたらこの下に入ってきて、エンド。ここで終了になります。

セルネッツ:VBAユーザーフォームで進捗表示 |刻み方が処理速度へ及ぼす影響

この下のユーザーフォーム、クエリクローズはなんのためにやっているのかというと、フォームの右上に閉じるボタンがあったと思いますが、終了するかどうか尋ねてきます。

セルネッツ:VBAユーザーフォームで進捗表示 |刻み方が処理速度へ及ぼす影響

「はい」か「いいえ」ですが、「これなんだろう?」とこれを押すと、ここに入ってくるので、それを無効にするためにしています。

コメントアウトしてやるやらないを評価をしていただければどういうことなのかがわかるかと思います。

実際にやってみたほうが良いですね。いったんコメントした場合、どんなふうになるのか。

ここを押せちゃいます。

セルネッツ:VBAユーザーフォームで進捗表示 |刻み方が処理速度へ及ぼす影響

今度は外して、今動いていましたね、イキにしました。すると今の赤バツを押してもキャンセルトゥルーで無効にしているので、押してるんですけど、ここはアクションが起きないようになります。

セルネッツ:VBAユーザーフォームで進捗表示 |刻み方が処理速度へ及ぼす影響

強制終了しますか?「はい」で終わりました。

今はここの流れでここに入ってきて、そのままエンド。この下にユーザーフォームのhide:unloadと書いてありますが、エンドで終わらせた場合は全部終了されてメモリも開放するので、エンドとやった瞬間にこれは必要はなくなります。

使い方はちょっと注意が必要ですが、エンドでユーザーフォームは全部閉じて、メモリも開放されると覚えてください。

セルネッツ:VBAユーザーフォームで進捗表示 |刻み方が処理速度へ及ぼす影響

ということで、ユーザーフォームのリペイントの解説をしたかったんですが、ユーザーフォームの先ほどの要素はキャプションが左上の頭のところに来るところです。

ここにステップ1とか2とかを書いていただければどこをやっているのかがわかります。

セルネッツ:VBAユーザーフォームで進捗表示 |刻み方が処理速度へ及ぼす影響

そして続いて経過時間。今何秒経過したのかを経過時間のところに入れています。

セルネッツ:VBAユーザーフォームで進捗表示 |刻み方が処理速度へ及ぼす影響

そして分子分母です。ステータスというところでグルグル描画で増えていくのがこれです。

セルネッツ:VBAユーザーフォームで進捗表示 |刻み方が処理速度へ及ぼす影響

増えていくんですが、描画しているところがどんどん変わっていってくれないといけないので、この2つが重要になってきます。

セルネッツ:VBAユーザーフォームで進捗表示 |刻み方が処理速度へ及ぼす影響

このリペイントとDoイベントです。たとえばこれをやらないとどうなるのか、リペイントもしない、Doイベントもコメントで潰しました。すると、リペイント、描画をしに行っていないので追いついていないんです。してるんですけど追いついていないんです。

セルネッツ:VBAユーザーフォームで進捗表示 |刻み方が処理速度へ及ぼす影響

そして5.937、描画しないぶん早いんですけど意味がなくなってしまうので、リペイント、Doイベントは必ずセットでやるようにしてください。

実演デモ④モーダルとモーダレス

最後4番目、モーダル。規定値とモーダルです。

これはユーザーフォームのお話、別の動画で詳しく解説をしますが、ざっくりご紹介だけします。

どのタイミングでユーザーフォームが表示されたのかというと、ユーザーフォーム01、このUF01を表示したタイミングはここですよね。

セルネッツ:VBAユーザーフォームで進捗表示 |刻み方が処理速度へ及ぼす影響

ここでモードレス、そしてもう1個がモーダルですが、VBモーダルはユーザーフォーム01.showとやって、うしろに書かないと規定値がモーダルなので、モードレスでやった場合はマウス操作が可能です。下のほうのマウス操作NGになるのはこのモーダルです。

今ユーザーフォーム01showでモードレスでやったので、普通にボタンを押して、強制終了のボタンを押すことができましたが、モードレスではなくて通常のやつです。

なくても同じなんですけど、ユーザーフォーム01show、VBモーダルで表示した場合はどうなるのかをやってみたいと思います。

よろしいですか?ここは押せるんですが、シートを選択すると他の処理が基本的にできないんです。

セルネッツ:VBAユーザーフォームで進捗表示 |刻み方が処理速度へ及ぼす影響

エクセルのユーザーフォームが前面に表示されていて、ここは触れますが、カーソルでなにか他のことをすることができません。

マウス操作ができないというよりも、ユーザーフォームが起動中は他の処理、アプリケーションを立ち上げたり、なにか操作をすることができなくなります。

ということで、その違いはAを使うかBを使うかによって変わってきてしまうので、これはそういう2つの表示方法になるので、それは学習してください。

描画単位と速度の関係(Mod関数)

そして先ほどちょっと解説、ざっくりしましたけど、このMOD関数というのは割り算の余りを求める関数なので、刻む必要がない、たとえばあっという間に終わる1000件のような場合は1000件出してもかまわないと思うんですが、これは適宜判断してやってください。

UserFormの要素

ユーザーフォームの要素ということでは、①②③ということで、1番目に処理内容、そして2番目に何分の何ですね、分子分母、そして最後が経過時間。

セルネッツ:VBAユーザーフォームで進捗表示 |刻み方が処理速度へ及ぼす影響

必要に応じてその要素を判断してここに反映してください。

まとめ

最後まとめになりますけれども、1番目、中断できることというのは、製品レベルでは必ず必要になってきます。

セルネッツ:VBAユーザーフォームで進捗表示 |刻み方が処理速度へ及ぼす影響

たとえば間違って実行してしまった場合、その処理が通常3、40分かかるようなものであれば、すぐに停止をしたい、中断をしたいということが起きたり、そういうケースがあるので、そこにも対応するように考慮してください。

2番目はユーザー目線、開発者目線ということで、目的としてはユーザーのためだけではなく、開発者の万が一の不具合だったり、情報を得る手がかりにするという意味で、ユーザーフォームに必要な情報を表示することで手がかりになるということを覚えてください。

3番目は描画の単位のお話でしたが、10件単位なのか100件単位なのか、このあたりは実際に試してみて、妥当な適切な刻み方で表示をするようにしてください。

エンディング

今回は以上で終了となります。少しでも参考になったというかたはぜひチャンネル登録、高評価をお願いいたします。

毎週金曜日の夜9時に投稿しています。ご視聴ありがとうございました。

タケモ塾では、今後も皆さんのVBA学習に役立つコンテンツを作成してまいります。
ブログ記事、Youtubeチャンネルのご質問・ご感想・ご要望などお気軽にお問合せください。
お問合せはこちらから
                         タケモ塾運営:株式会社セルネッツ