hotstring を利用して、今年の指定日の年月日＋曜日を素早く入力する - AutoHotkey v2



# はじめに

この記事では、AutoHotkey v2 で hotstring を利用して、今年の指定日の年月日＋曜日を素早く入力するプログラムを学習します。hotstring を実行するとダイアログが開くので、そこに取得したい月日を入力し、「Enter」キーを押すと、年月日＋曜日の形式で文字が入力されます。
メール文や記事を書くとき、カレンダーを参照せず曜日を含めて素早く文字入力できます。

## AutoHotkey v1.1 利用者が詰まる要所

v2 では後方互換性は犠牲となっており、v1.1 のために書かれたスクリプトは基本的に動作せず、改修の必要があります。

# 必要な前提知識

AutoHotkey v2 は Windows で動作する、自動化に役立つツールです。

## インストール

### AutoHotkey

[AutoHotkey](https://www.autohotkey.com/) をダウンロードします。このプログラムは v2.0 で書かれているので、ダウンロードページで「Download v2.0」の方をダウンロードし、動作させる Windows 機にインストールします。

### AutoHotkey Plus Plus

VSCode をご利用の方は、「[AutoHotkey Plus Plus](https://marketplace.visualstudio.com/items?itemName=mark-wiemer.vscode-autohotkey-plus-plus)」をインストールしましょう。コーディングが快適になります。

#### コマンド

AHK++を使用すると、キーボードショートカットを使用してスクリプトをコンパイル、デバッグ、および実行できます。選択範囲をスタンドアロン スクリプトとして実行することもできます。

- デバッグ：Ctrl + Alt + F9
- ヘルプを開く:Ctrl + F1
- 実行：Ctrl + F9
- 選択範囲の実行:Ctrl + F8

# 手順

## 実装する内容

「t[[」と順番に入力するとテキスト入力できるダイアログが開き、例として「0101」と射ち込むと「2023 年 1 月 1 日(日)」と入力されるようにします。

## ダイアログ、Gui の操作

まず、Gui でダイアログを表示させてみます。

```:testGui-v2.ahk
{
    TestGui := Gui(,"今年日付入力")
    TestGui.Add("Text",, "指定日[4ケタ]")
    TestGui.show() ; Gui を表示する
}
```

これを実行すると、ダイアログが一つ表示されます。
![testgui01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3570094/c3b897e5-aead-36f3-2f7d-31abe0e7cab8.png)

`TestGui := Gui(,"今年日付入力")`

- まず Gui() で Gui を生成します。第二引数の文字列は、ダイアログのタイトルになります。

`TestGui.Add("Text",, "指定日[4ケタ]")`

- TestGui に Add()でテキストを追加します。

`TestGui.show() ; Gui を表示する`

- TestGui を show() で表示させます。

```:testGui-v2.ahk
{
    ; 中略
    TestGui.Add("Edit", "vTargetDay")
    TestGui.show() ; Gui を表示する
}
```

文字入力できるフォームを追加します。
![testgui02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3570094/46295bdb-03da-1c77-96c6-1c01461752c6.png)

`TestGui.Add("Edit", "vTargetDay")`

- Add("Edit") で編集可能なフォームを表示します。オプションの「vTargetDay」は、入力された内容が変数「TargetDay」に代入されます。

```:testGui-v2.ahk
{
    ; 中略
    TestGui.Add("Edit", "vTargetDay Limit4 Number")
    TestGui.show() ; Gui を表示する
}
```

Add("Edit")のオプションに文字入力制限を追加します。

- Limit4：入力可能文字数を 4 文字に制限します。
- Number：入力可能文字を数字だけに制限します。

```:testGui-v2.ahk
{
    ; 中略
    TestGui.Add("Button", "default", "OK")
    TestGui.Add("Button", " x+20" , "Cancel")
    TestGui.show() ; Gui を表示する
}
```

ダイアログに「OK」ボタンと「Cancel」ボタンを追加します。
![testgui03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3570094/4db4ec8e-5ef0-2f31-5314-ca5fa96f016d.png)

- 「OK」ボタンの第二引数のオプションである「default」は、「Enter」キーを押したときと同じ動作として設定されます。
- 「Cancel」ボタンのオプション「x+20」は、「OK」ボタンと横並びになるよう X 軸に対して 20 ずらしています。

```:testGui-v2.ahk
{
    ; 中略
    TestGui.Add("Button", " x+20" , "Cancel").OnEvent("Click", CloseWindow)
    TestGui.show() ; Gui を表示する
}
; MyGui を閉じる関数
CloseWindow(*)
{
    TestGui.Destroy()
}
```

「OK」ボタンの処理は多いので、まず「Cancel」ボタンの機能を実装します。

- Add().OnEvent()とつなげて記述し、ボタンクリック時に CloseWindow 関数を実行します。
- 引数を取らない関数の記述で詰まったのですが、CloseWindow()とカッコに何も書かないと「Invalid callback function」エラーが出ます。そのため、「\*」アスタリスクを記述します。
- ダイアログを閉じるのは、ウィンドウとそのすべてのコントロールを削除する Destroy() 関数を使います。これで、「Cancel」ボタンを押すと、ダイアログが閉じます。

```:testGui-v2.ahk
{
    ; 中略
    TestGui.OnEvent("Escape", CloseWindow) ; [ESC] キーでダイアログを閉じる
    TestGui.show() ; Gui を表示する
}
```

ダイアログを開いた後、「ESC」キーで閉じる機能の実装です。これはマニュアルを読んでもわからなかったのですが、TestGui.OnEvent() のようにつなげて、「Escape」を指定して第二引数に先ほどの CloseWindow 関数を設定すると、正しく動作します。

```:testGui-v2.ahk
{
    ; 中略
    TestGui.Add("Button", "default", "OK").OnEvent("Click", InsDateExec)
    TestGui.show() ; Gui を表示する
}
InsDateExec(*)
{
    Saved := TestGui.Submit()
    TargetDay := Saved.TargetDay
    MsgBox(TargetDay)
}

;ダイアログに「0101」と入力
;ダイアログに「0101」と表示される
```

「OK」ボタンを押されたときの InsDateExec() 関数の中身を記述します。

- Add().OnEvent()とつなげて記述し、「OK」ボタンクリック時に InsDateExec 関数を実行します。
- Add("Edit") で設定した、ユーザー入力による変数を取得します。Submit()関数は、名前付きコントロールから値を収集し、オブジェクトに合成します。
- 変数「TargetDay」に、ユーザーの入力した 4 桁の数字をオブジェクト「Saved.TargetDay」から取得して代入します。
- 表示されたダイアログに「0101」と入力すると、ダイアログが開いて「0101」と表示されます。
![testgui04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3570094/51555b32-fc41-feb0-151c-5fb312085fa6.png)

```:testGui-v2.ahk
InsDateExec(*)
{
    ; 中略
    ;今年の年を取得
    YearString := SubStr((FormatTime(,"ShortDate")), 1, 4)
    ;取得した月日のテキストから曜日を取得
    dayString := FormatTime(YearString . TargetDay . "000000"  ,"WDay")
    ;取得したテキストから年月日を取得
    targetString := FormatTime(YearString . TargetDay . "000000" ,"LongDate")
    ;曜日を(月)の形式に変換
    dowstr := get_dowstr(dayString)
    MsgBox(targetString . dowstr)
}

;ダイアログに「0101」と入力
;ダイアログに「2023年1月1日(日)」と表示される
```

ユーザーが入力した日付から、今日の日付を年月日+曜日の形式で取得します。

- 変数「YearString」に今年の西暦を代入します。
- FormatTime()のオプション「ShortDate」は、例として「2023/10/05」のような形式で日付を返してきます。それを SubStr()関数で頭の西暦だけを取得します。
- 変数「dayString」に指定日の曜日を「WDay」形式で取得します。FormatTime()の第一引数に「今年の年+ユーザー入力の月日＋時間」の数字を入れると、指定日をオプションで指定した形式で取得できます。
- 変数「targetString」も同様にユーザー入力の月日を使い、年月日を取得します。
- 変数「dowstr」で取得する曜日の日本語形式は、「[get_dowstr() 関数を使って引数の「downum」で曜日を指定し、「(曜日)」の形式で曜日を取得](https://qiita.com/DaijiroNozaki-afk/items/4638fe375a446b7e4fb0#%E3%82%B3%E3%83%BC%E3%83%89)」で説明したものと同等の働きです。
- 表示されたダイアログに「0101」と入力すると、ダイアログが開いて「2023年1月1日(日)」と表示されます。
![testgui05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3570094/23f253d5-c6a0-00ea-782b-88eef6d09eab.png)

```:testGui-v2.ahk
InsDateExec(*)
{
    ; 中略
    A_Clipboard := targetString . dowstr
    Send "+{INSERT}"
}
```

クリップボードに「年月日＋曜日」を代入して、「(クリップボードを)貼り付け」を実行します。

## コード

完成したコードは、下記の通りです。

```:testGui-v2.ahk
::t[[::
{
    {
        TestGui := Gui(,"今年日付入力")
        TestGui.Add("Text",, "指定日[4ケタ]")
        TestGui.Add("Edit", "vTargetDay Limit4 Number")
        TestGui.Add("Button", "default", "OK").OnEvent("Click", InsDateExec) ; ダイアログの「OK」ボタンの表示。オプションに"default"とつけることで、テキストを入力後に「Enter」キーを押したときの動作として設定する
        TestGui.Add("Button", " x+20" , "Cancel").OnEvent("Click", CloseWindow) ; キャンセルボタン、ダイアログを閉じる
        TestGui.OnEvent("Escape", CloseWindow) ; [ESC] キーでダイアログを閉じる
        TestGui.show() ; Gui を表示する
    }

    ; MyGui を閉じる関数
    CloseWindow(*)
    {
        TestGui.Destroy()
    }
    ; ダイアログに入力された四桁の数字を年月日＋曜日に変換し、入力する関数
    InsDateExec(*)
    {
        Saved := TestGui.Submit()
        TargetDay := Saved.TargetDay
        ;今年の年を取得
        YearString := SubStr((FormatTime(,"ShortDate")), 1, 4)
        ;取得した月日のテキストから曜日を取得
        dayString := FormatTime(YearString . TargetDay . "000000"  ,"WDay")
        ;取得したテキストから年月日を取得
        targetString := FormatTime(YearString . TargetDay . "000000" ,"LongDate")
        ;曜日を(月)の形式に変換
        dowstr := get_dowstr(dayString)
        A_Clipboard := targetString . dowstr
        Send "+{INSERT}"
    }
}
;曜日をカッコつきで返す
get_dowstr(theNum)
{
    dowtable := "日月火水木金土"
    ; Wday は日曜日が 1、
    dowstr := SubStr(dowtable, theNum, 1)
    return "(" . dowstr . ")"
}
```

```
::t[[::
{コード}
```

- 先ほどのコードを Hotstring の中カッコでくくることで、「t[[」と連続してキーボードを入力するとダイアログが開き、取得したい日付を 4 桁の数字で入力すると、年月日＋曜日の形式でテキストが入力されます。
- get_dowstr() の処理は割愛します。

# 注意点

- Gui 周りの処理がややこしいので、ステップを追って動作を確認していきましょう。

# 結論

- この Hotstring を使えば、今年であれば、入力したい曜日を知らなくても、「t[[」と入力し、ダイアログに取得したい日付を四桁の数字で入力することで、年月日＋曜日の形式で入力できます。
- エラー処理をしていないので、2 月 31 日のように入力したら、「(金)」とだけ入力されます。年月日を取得できない場合は処理を中断するようにしましょう。
- AutoHotkey をインストールした後に付属する「Ahk2Exe」を使えば、AutoHotkey のプログラムを.exe 化できます。それを利用すれば、AutoHotkey をインストールしていない Windows 機でも hotstring が使えるようになります。
- また、作成した.exe ファイルを「Windows 10 の起動時に自動的に実行するアプリ」として登録しておけば、起動時に自動的に立ち上がり、hotstring が利用可能になります。