Excel と VBA による周期イベントタイマーの使い方を記載してみます。
この手法を使って、デジタル時計とアナログ時計を作成してみます。
こんな感じのものを作成します。
まず最初に、もっとも標準的な Application.OnTime を使って1秒周期で関数を起動する VBA プログラムを作成してみます。
ここでは1秒周期で "A1" セルに現在の日時情報を表示してみます。
Microsoft Excel | 2021 MSO (バージョン 2307 ビルド 16.0.16626.20170) | 64 ビット |
VBAコードを以下に示します。下記例では "Microsoft ExcelObjects" > "Sheet1" に作成しています。
' ' Application.OnTime を使った周期動作により、デジタル時計 を作成してみます ' Option Explicit Private mOnTime As Date ' ' デジタル時計を表示する ' Private Sub TimerProc() Range("A1") = Format(Now, "yyyy/mm/dd hh:mm:ss") End Sub ' ' 周期タイマー起動 ' Sub OnTimeStart() ' デジタル時計 表示更新 Call TimerProc ' 次回のタイマー起動を設定 - "Now + TimeSerial(0, 0, 1)" は 現在時刻から1秒後 を意味します。 mOnTime = Now + TimeSerial(0, 0, 1) Call Application.OnTime(mOnTime, "Sheet1.OnTimeStart") End Sub ' ' 周期タイマー停止 ' Sub OnTimeStop() ' 同一の Procedure と EarliestTime がないとエラーになる On Error Resume Next Call Application.OnTime(mOnTime, "Sheet1.OnTimeStart", , False) End Sub
Sheet1 上にボタンを作成して、スタート/ストップをそれぞれ Sheet1.OnTimeStart/Sheet1.OnTimeStop を実行するようにすると、下記動画のように動作します。
1秒周期で画面更新することができました。これを応用することで簡単な動画やゲームなどを実現することもできそうです。
ところでマウスカーソルが時々 Wait 表示になるところが若干気になります。次章で紹介する SetTimer (Windows API) を使うとこの問題を解決できます。
SetTimer, KillTimer API (Windows API) を使って1秒周期で関数を起動する VBA プログラムを作成してみます。
前章同様に1秒周期で "A1" セルに現在の日時情報を表示してみます。
Microsoft Excel | 2021 MSO (バージョン 2307 ビルド 16.0.16626.20170) | 64 ビット |
VBAコードを以下に示します。下記例では "Microsoft ExcelObjects" > "Sheet2" に作成しています。
' ' Win32 API の SetTimer, KillTimer を使った周期動作により、デジタル時計、アナログ時計を作成してみます。 ' Option Explicit ' ' デジタル時計を表示する ' Sub TimerProc() Range("A1") = Format(Now, "yyyy/mm/dd hh:mm:ss") End Sub ' ' 周期タイマー起動 ' Sub TimerStart() If mTimerID <> 0 Then MsgBox "起動済です。" Exit Sub End If Call StartTimer End Sub ' ' 周期タイマー停止 ' Sub TimerStop() Call StopTimer End Sub
下記コードを "標準モジュール" > "Module1" に記載します。
' ' Win32 API の SetTimer, KillTimer を使って周期動作を実現してみます。 ' 下記内容は Sheet2 のためのVBAプログラムです。 ' Sheet2 内で全てを実現したかったのですが、標準モジュール(Module1)内で記載しないと意図通りに動作してくれませんでした。残念。 ' Option Explicit Private Declare PtrSafe Function SetTimer Lib "user32" (ByVal hwnd As LongPtr, ByVal nIDEvent As LongPtr, ByVal uElapse As LongPtr, ByVal lpTimerFunc As LongPtr) As Long Private Declare PtrSafe Function KillTimer Lib "user32" (ByVal hwnd As LongPtr, ByVal nIDEvent As Long) As Long Public mTimerID As Long Private Sub TimerProc() If mTimerID = 0 Then End '終了できない時の対策 On Error Resume Next 'デバッグ出すとExcelが固まるので Call Sheet2.TimerProc End Sub ' ' SetTimer API を使って周期タイマー動作を開始する ' Public Sub StartTimer() mTimerID = SetTimer(0&, 1&, 1000&, AddressOf TimerProc) End Sub ' ' KillTimer API を使って周期タイマー動作を停止する ' Public Sub StopTimer() Call KillTimer(0&, mTimerID) mTimerID = 0 End Sub
Sheet2 上にボタンを作成して、スタート/ストップをそれぞれ Sheet2.TimerStart/Sheet2.TimerStop を実行するようにすると、下記動画のように動作します。
OnTime による実現と異なってマウスカーソルが Wait 状態に頻繁になることが無くなりました。良い感じです。
一方、SetTimer, KillTimer を標準モジュール内で使用する必要がありました。Sheet2 内で使用したかったのですが私はうまく実現することができませんでした。
ポイント
SetTimer, KillTimer を標準モジュール内で実行する必要がありました。
Sheet2 内で完結したプログラムにしたかったのですが、私がトライした範疇ではこれらAPIを Sheet2 内で実行すると、Excel が異常終了するなどしてうまく実現することができませんでした。
何か良い方法がわかったらこちら記事を更新したいと思います。
上記タイマーイベントを使用してアナログ時計を作成します。完成イメージは下図動画の通りです。
Microsoft Excel | 2021 MSO (バージョン 2307 ビルド 16.0.16626.20170) | 64 ビット |
3-1. アナログ時計を描く
最初にアナログ時計の絵を描きます。
ポイント
3-2. 各針に名前を付ける
各針に名称を付けます。本ページで紹介するプログラムで付与した名称を下記 table に記載します。
針 | 名称 |
---|---|
秒針 | "second_line" |
分針(長針) | "minute_line" |
時針(短針) | "hour_line" |
3-3. 秒針を動かす
指定した秒数に従って秒針を動かす関数を作成します。
ポイント
' ' 針(shape)の高さを rate 倍する ' Private Sub ScaleHeight_Update(shape As shape, rate As Double) shape.ScaleHeight rate, msoFalse End Sub ' ' 秒針を更新 ' Private Sub UpdateSecond(second As Double) Dim shp As shape Dim rate As Double Set shp = Sheet1.Shapes("second_line") rate = 0.5 ' 元の長さにする Call ScaleHeight_Update(shp, 1# / rate) ' 回転する shp.Rotation = second * 360 / 60 ' 長さを半分ぐらいにする Call ScaleHeight_Update(shp, rate) End Sub ' ' アナログ時計の表示テスト用 ' Sub test_AnalogTimer() UpdateSecond (10) End Sub
上記 "test_AnalogTimer()" を実行した結果は下図の通りです。値を適当に変更しつつ、テスト&デバッグを行います。
3-4. 分針(長針)を動かす
指定した分数に従って分針を動かす関数を作成します。
秒針とほぼ同じ処理なので、詳細説明を割愛します。
' ' 分針を更新 ' Private Sub UpdateMinute(minite As Double) Dim shp As shape Dim rate As Double Set shp = Sheet1.Shapes("minute_line") rate = 0.55 ' 元の長さにする Call ScaleHeight_Update(shp, 1# / rate) ' 回転する shp.Rotation = minite * 360 / 60 ' 長さを半分ぐらいにする Call ScaleHeight_Update(shp, rate) End Sub
3-5. 時針(短針)を動かす
指定した時数に従って時針を動かす関数を作成します。
秒針とほぼ同じ処理なので、詳細説明を割愛します。
' ' 時針を更新 ' Private Sub UpdateHour(hour As Double) Dim shp As shape Dim rate As Double Set shp = Sheet1.Shapes("hour_line") rate = 0.55 ' 元の長さにする Call ScaleHeight_Update(shp, 1# / rate) ' 回転する shp.Rotation = hour * 360 / 12 ' 長さを半分ぐらいにする Call ScaleHeight_Update(shp, rate) End Sub
3-6. 現在時刻に従ってアナログ時計を更新する
現在時刻に従ってアナログ時計を更新する関数を作成します。
' ' アナログ時計を現在時間に従って表示する ' Private Sub AnalogTimerProc() Dim dateTime As Date Dim h As Double Dim m As Double Dim s As Double dateTime = Now() h = hour(dateTime) m = minute(dateTime) s = second(dateTime) UpdateHour (h + (m / 60) + (s / 3600)) UpdateMinute (m + (s / 60)) UpdateSecond (s) End Sub
3-7. 1秒周期で現在時刻を更新する
最後に1秒周期で現在時刻を更新するようにします。
1章、2章、で作成した周期イベントの中から上記関数 AnalogTimerProc() をコールするだけです。
1章の内容(Sheet1)にアナログ時計表示を加えた全ソースコードを以下に記載します。
' ' Excel VBA の Application.OnTime を使った周期動作により、デジタル時計、アナログ時計を作成してみます。 ' Option Explicit Private mOnTime As Date ' ' デジタル時計を表示する ' Private Sub TimerProc() Range("A1") = Format(Now, "yyyy/mm/dd hh:mm:ss") End Sub ' ' 周期タイマー起動 ' Sub OnTimeStart() ' デジタル時計 表示更新 Call TimerProc ' アナログ時計 表示更新 Call AnalogTimerProc ' 次回のタイマー起動を設定 mOnTime = Now + TimeSerial(0, 0, 1) 'Tm = 200 'mOnTime = [Now()] + Tm / 86400000 Call Application.OnTime(mOnTime, "Sheet1.OnTimeStart") End Sub ' ' 周期タイマー停止 ' Sub OnTimeStop() ' 同一の Procedure と EarliestTime がないとエラーになる On Error Resume Next Call Application.OnTime(mOnTime, "Sheet1.OnTimeStart", , False) End Sub ' ' === 以下、アナログ時計用の処理 === ' ' ' 針(shape)の高さを rate 倍する ' Private Sub ScaleHeight_Update(shape As shape, rate As Double) shape.ScaleHeight rate, msoFalse End Sub ' ' 時針を更新 ' Private Sub UpdateHour(hour As Double) Dim shp As shape Dim rate As Double Set shp = Sheet1.Shapes("hour_line") rate = 0.55 ' 元の長さにする Call ScaleHeight_Update(shp, 1# / rate) ' 回転する shp.Rotation = hour * 360 / 12 ' 長さを半分ぐらいにする Call ScaleHeight_Update(shp, rate) End Sub ' ' 分針を更新 ' Private Sub UpdateMinute(minite As Double) Dim shp As shape Dim rate As Double Set shp = Sheet1.Shapes("minute_line") rate = 0.55 ' 元の長さにする Call ScaleHeight_Update(shp, 1# / rate) ' 回転する shp.Rotation = minite * 360 / 60 ' 長さを半分ぐらいにする Call ScaleHeight_Update(shp, rate) End Sub ' ' 秒針を更新 ' Private Sub UpdateSecond(second As Double) Dim shp As shape Dim rate As Double Set shp = Sheet1.Shapes("second_line") rate = 0.5 ' 元の長さにする Call ScaleHeight_Update(shp, 1# / rate) ' 回転する shp.Rotation = second * 360 / 60 ' 長さを半分ぐらいにする Call ScaleHeight_Update(shp, rate) End Sub ' ' アナログ時計を現在時間に従って表示する ' Private Sub AnalogTimerProc() Dim dateTime As Date Dim h As Double Dim m As Double Dim s As Double dateTime = Now() h = hour(dateTime) m = minute(dateTime) s = second(dateTime) UpdateHour (h + (m / 60) + (s / 3600)) UpdateMinute (m + (s / 60)) UpdateSecond (s) End Sub ' ' アナログ時計の表示テスト用 ' Sub test_AnalogTimer() UpdateHour (10) UpdateMinute (10) UpdateSecond (30) End Sub
上記で作成した Excel ファイルを こちら からダウンロードできます。
本ページの情報は、特記無い限り下記 MIT ライセンスで提供されます。
2023-08-19 | - | 新規作成 |