torazaemon2016’s blog

手書き文字認識メモ開発

AutohotkeyでGUIメニューを表示し、ペン1本で PCを操作する

Autohotkeyを使ってGUIメニューを出すことで物理キーボードがなくてもPCを操作する環境を作る

Autohotkeyなのにキーボードなしって、AutohotkeyのKeyという意味からはちょっと意味がわからない話?と思われるかもしれませんが、 タブレットPCで物理キーボードを付けない状態の総重量の軽いPC環境(500g~1kg以内)を目指しているものです。

軽いSurface Go3に重いキーボードを付けることなし利用できており、 また、後述のペンでの手書き入力アプリやKobo電子書籍を読むも快適になっています。

なお、Autohotkeyそのものがどういうものとかインストール方法などについては、ここでは説明していませんので、 インターネットなどで検索してください。


この記事を書いてから1年ほど経つので、Updateして、カラー化したバージョン(2024/11/23 )は以下のページです。

torazaemon2016.hatenablog.jp

最近では、タッチスクリーンキーボードも作ってみました。(2025-09-23)

torazaemon2016.hatenablog.jp


Autohotkey Ver.2 での文法変更

AHKがVer2になってからGUIの書き方も少し変わったのですが、インターネットでは Ver1でのサンプルのページばかりがヒットするため、なかなかうまく書けてなく Classを作ってうまく書けるより、まずは動作するものとして作ってています。

zenn.dev では、Ver.2でのclass とnewを使った例が紹介されています。

GUI_menu()

では、Autohotkey Ver.2を使ったGUIメニューの例です。

GUIでボタンで、やりたいことを登録しています。 (2024/06/23 Update)

2024


Autohotkey Ver1形式に近い形の記載ですが、 やりたいことができることを確認する意味もあったので、 洗練されていませんので、どんどん拡張していただければと思います。

途中 func_ での関数は また別途定義されているものですが、また別途紹介できればと思います。

基本的にラウンチャーなので、起動時にGUIを消して、コマンドを実行している形ですが、 カーソルキーの動きの際には、一度隠して、コマンドを実行し、再度GUIを出す形です。

また、タイマーで5秒間メニューを出し、何もしなければ消える形にしています。(2024-06-23 タイマー止める)

また、最初でGUIメニューの多重起動を避ける形にしています。 (2024/06/23 Update)

GUI_menu()
{
    If WinExist("AHK Menu")
    {
        WinActivate
        return
    }
    
    MyGui := Gui(,"AHK Menu")
 
    MyGui.BackColor := "00EE99"  ; RGB color
    MyGui.SetFont("s12")  ; フォントサイズ(ポイント)を設定

    MyGui.Add("Button", " default", "Obsidian").OnEvent("Click", Obsidian_click) ; 
    MyGui.Add("Button", " x+8", "Date").OnEvent("Click", Date_click) ; 
    MyGui.Add("Button", " x+8", "Time").OnEvent("Click", Time_click) ; 
    MyGui.Add("Button", " X+8 ", "-").OnEvent("Click", Add_item_click) ; 
    MyGui.Add("Button", " X+8 ", "- [ ] ").OnEvent("Click", Add_checkitem_click) ; 

    MyGui.Add("Button", " x16 y+20", "Google").OnEvent("Click", Chrome_click) ; 
    MyGui.Add("Button", " x+20", "Chrome URL get").OnEvent("Click", Chrome_get_click) ; 

    MyGui.Add("Button", " x16 y+20", "Gmail").OnEvent("Click", Gmail_click) ; 
    MyGui.Add("Button", " x+20", "Calendar").OnEvent("Click", Gcalendar_click) ; 

    MyGui.Add("Button", " X16 y+20", "Edit Autohotkey ").OnEvent("Click", Edit_ahk_click) ; 
    MyGui.Add("Button", " x+8", "Edit ahk GUI").OnEvent("Click", Edit_ahk_gui_click) ; 
    MyGui.Add("Button", " x+8", "Reload AHK").OnEvent("Click", Reload_ahk_click) ; 

    ;--------
    MyGui.Add("Button", " X16 y+40", "←").OnEvent("Click", Arrow_Left) ; 
    MyGui.Add("Button", " X+8 ", "↓").OnEvent("Click", Arrow_Down) ; 
    MyGui.Add("Button", " X+8 ", "↑").OnEvent("Click", Arrow_Up) ; 
    MyGui.Add("Button", " X+8 ", "→").OnEvent("Click", Arrow_Right) ; 
    MyGui.Add("Button", " X+20 ", "^{Home}").OnEvent("Click", Goto_Head) ; 
    MyGui.Add("Button", " X+8 ", "^{End}").OnEvent("Click", Goto_Tail) ; 
    MyGui.Add("Button", " X+8 ", "BS").OnEvent("Click", Back_space) ;Z

    MyGui.Add("Button", " X16 y+40 ", "SPC").OnEvent("Click", Hankaku_space) ; 
    MyGui.Add("Button", " X+8 ", "全角空白 ").OnEvent("Click", Zenkaku_space) ; 
    MyGui.Add("Button", " X+8 ", "Tab").OnEvent("Click", Tab_space) ; 
    MyGui.Add("Button", " X+8 ", "Enter").OnEvent("Click", Enter) ; 

    MyGui.Add("Button", " x16 y+40", "Win+Shift+s").OnEvent("Click", Capture_click) ; 
    MyGui.Add("Button", " x+8", "Win+PrtScn(全画面cap)").OnEvent("Click", FullCapture_click) ; 
    
    MyGui.Add("Button", " X16 y+40", "1").OnEvent("Click", Win_1) ; 
    MyGui.Add("Button", " X+8 ", "2").OnEvent("Click", Win_2) ; 
    MyGui.Add("Button", " X+8 ", "3").OnEvent("Click", Win_3) ; 
    MyGui.Add("Button", " X+8 ", "4").OnEvent("Click", Win_4) ; 
    MyGui.Add("Button", " X+20 ", "Win+Tab").OnEvent("Click", Win_Tab) ;

    MyGui.Add("Button", "      y+40 cRed", "Task Mgr").OnEvent("Click", TaskManager_click) ; 
    MyGui.Add("Button", " x+16 cRed", "Shutdown").OnEvent("Click", Shutdown_click) ; 

    Aaa := WinActive("A")
    if(Aaa != 0){
        Title := WinGetTitle("A")   ;アクティブ タイトル名を取得し Title へ格納
    } else {
        Title := "no active"
    }
    MyGui.Add("Text", " x16 y+20", Title)

    MyGui.OnEvent("Escape", CloseWindow) ; [ESC] キーで閉じる
    MyGui.OnEvent("Close", CloseWindow) ; X で閉じる

    ;SetTimer(UpdateMenu, 5000) ;5秒後にメニューを閉じる (2024-06-23 add ;) 
    MyGui.Show("x1800 y100")

;--------------------
    UpdateMenu(*)
    {
        CloseWindow()
    }
;---MyGui を閉じる関数-----
    CloseWindow(*)
    {
        SetTimer(UpdateMenu, 0)
        Sleep(100)
        MyGui.Destroy()
    }
;--------------------
    Date_click(*)
    {
        MyGui.Destroy()
        ;IME_set(0)
        now := FormatTime(, "yyyy-MM-dd")
        Sleep(100)
        Send(now)
        Send(A_Space)
    }

    Time_click(*)
    {
        MyGui.Destroy()
        ;IME_set(0)
        now := FormatTime(, "HH:mm")
        Sleep(100)
        Send(now)
        Send(A_Space)
    }

    Edit_ahk_click(*)
    {
        MyGui.Destroy()
        Run("notepad++.exe `"" A_ScriptDir "\AutoHotkey_V2.ahk`"") ;このスクリプトを編集 
    }

    Edit_ahk_gui_click(*)
    {
        MyGui.Destroy()
        Run("notepad++.exe `"" A_ScriptDir "\GUI.ahk`"") ;このスクリプトを編集 
    }

    Reload_ahk_click(*)
    {
        msg := "`"Reload Autohotkey `"" 
        ToolTip(msg)
        Sleep(500)
        ToolTip()
        Reload()    ;このスクリプトをリロードして適用
        return
    }
    
    Chrome_click(*)
    {
        MyGui.Destroy()
        func_chrome_open()
    }

    Chrome_get_click(*)
    {
        MyGui.Destroy()
        func_chrome_capture()
    }

    Capture_click(*)
    {
        MyGui.Destroy()
        Send "#+s"  ; Win+Shift+s
    }

    FullCapture_click(*)
    {
        MyGui.Destroy()
        Sleep(1000)
        Send("#{PrintScreen}")
        Sleep(1000)
        Run("C:\Users\xxxxxxxx\Pictures\Screenshots")   ; xxxxxxx -> User名  キャプチャした画像のFolderを開く(最新ファイル順に並べてあるとすぐアクセスできる)
        return
    }
    
    Gmail_click(*)
    {
        MyGui.Destroy()
        browser := "C:\Program Files\Google\Chrome\Application\chrome.exe --disk-cache-dir=R:\Temp"
        Run(browser " `"https://mail.google.com/mail/u/0/#inbox`"")
    }
    
    Gcalendar_click(*)
    {
        MyGui.Destroy()
        browser := "C:\Program Files\Google\Chrome\Application\chrome.exe --disk-cache-dir=R:\Temp"
        Run(browser " `"https://calendar.google.com/calendar/u/0/r?pli=1`"")
    }
    
    
    Obsidian_Click(*)
    {
        MyGui.Destroy()
        func_obsidian()
    }

    Add_item_click(*)
    {
        MyGui.Destroy()
        Sleep(50)
        Send "- "           ; 行末へ移動
    }
    Add_checkitem_click(*)
    {
        MyGui.Destroy()
        Sleep(50)
        Send "- [ ] "           ; 行末へ移動
    }

;---画面リストアする(またメニューを表示する)-----
    Arrow_Left(*)
    {
        MyGui.Hide()    ; メニュー消す (アクティブがエディタ画面に)
        Sleep(50)       ; ちょっと待つ(速すぎるとアクティブが切り替わらない)
        Send "{Left}"   ; ←移動
        Sleep(50)       ; ちょっと待つ
        Title := WinGetTitle("A")   ;アクティブ タイトル名を取得し Title へ格納
        MyGui.Restore() ; Guiメニューを表示する
        ;SetTimer(UpdateMenu, 5000)
    }
    Arrow_Right(*)
    {   
        MyGui.Hide()
        Sleep(50)
        Send "{Right}"          ; →移動
        Sleep(50)
        Title := WinGetTitle("A")   ;アクティブ タイトル名を取得し Title へ格納
        MyGui.Restore() ; Gui を表示する
        ;SetTimer(UpdateMenu, 5000)
    }
    Arrow_Down(*)
    {
        MyGui.Hide()
        Sleep(50)
        Send "{Down}"           ; ↓移動
        Sleep(50)
        Title := WinGetTitle("A")   ;アクティブ タイトル名を取得し Title へ格納
        MyGui.Restore() ; Gui を表示する
        ;SetTimer(UpdateMenu, 5000)
    }
    Arrow_Up(*)
    {
        MyGui.Hide()
        Sleep(50)
        Send "{Up}"         ; ↑移動
        Sleep(50)
        Title := WinGetTitle("A")   ;アクティブ タイトル名を取得し Title へ格納
        MyGui.Restore() ; Gui を表示する
        ;SetTimer(UpdateMenu, 5000)
    }
    Goto_Head(*)
    {
        MyGui.Hide()
        Sleep(50)
        Send "^{Home}"  ; 先頭へ移動
        Sleep(100)
        Title := WinGetTitle("A")   ;アクティブ タイトル名を取得し Title へ格納
        MyGui.show() ; Gui を表示する
        ;SetTimer(UpdateMenu, 5000)
    }
    Goto_Tail(*)
    {
        MyGui.Hide()
        Sleep(50)
        Send "^{End}"           ; 末尾へ移動
        Sleep(100)
        Title := WinGetTitle("A")   ;アクティブ タイトル名を取得し Title へ格納
        MyGui.show() ; Gui を表示する
        ;SetTimer(UpdateMenu, 5000)
    }
    
    Zenkaku_space(*)
    {
        MyGui.Hide()
        Sleep(50)
        Send " "
        Sleep(100)
        Title := WinGetTitle("A")   ;アクティブ タイトル名を取得し Title へ格納
        MyGui.show() ; Gui を表示する
        ;SetTimer(UpdateMenu, 5000)
    }

    Hankaku_space(*)
    {
        MyGui.Hide()
        Sleep(50)
        Send " "
        Sleep(100)
        Title := WinGetTitle("A")   ;アクティブ タイトル名を取得し Title へ格納
        MyGui.show() ; Gui を表示する
        ;SetTimer(UpdateMenu, 5000)
    }

    Enter(*)
    {
        MyGui.Hide()
        Sleep(50)
        Send("{Enter}")
        Sleep(100)
        Title := WinGetTitle("A")   ;アクティブ タイトル名を取得し Title へ格納
        MyGui.show() ; Gui を表示する
        ;SetTimer(UpdateMenu, 5000)
    }

    Tab_space(*)
    {
        MyGui.Hide()
        Sleep(50)
        Send("{Tab}")
        Sleep(100)
        Title := WinGetTitle("A")   ;アクティブ タイトル名を取得し Title へ格納
        MyGui.show() ; Gui を表示する
        ;SetTimer(UpdateMenu, 5000)
    }

    Back_space(*)
    {
        MyGui.Hide()
        Sleep(50)
        Send("{BS}")
        Sleep(100)
        Title := WinGetTitle("A")   ;アクティブ タイトル名を取得し Title へ格納
        MyGui.show() ; Gui を表示する
        ;SetTimer(UpdateMenu, 5000)
    }

;---画面リストアする(またメニューを表示する)-----
    Win_1(*)
    {
        MyGui.Hide()    ; メニュー消す (アクティブがエディタ画面に)
;       Sleep(50)       ; ちょっと待つ(速すぎるとアクティブが切り替わらない)
        Send("#^{Left}#^{Left}#^{Left}")
;       Sleep(50)       ; ちょっと待つ
;       Title := WinGetTitle("A")   ;アクティブ タイトル名を取得し Title へ格納
;       MyGui.Restore() ; Guiメニューを表示する
;       SetTimer(UpdateMenu, 5000)
    }
    Win_2(*)
    {
;       MyGui.Hide()    ; メニュー消す (アクティブがエディタ画面に)
        Sleep(50)       ; ちょっと待つ(速すぎるとアクティブが切り替わらない)
        Send("#^{Left}#^{Left}#^{Left}#^{Right}")
;       Sleep(50)       ; ちょっと待つ
;       Title := WinGetTitle("A")   ;アクティブ タイトル名を取得し Title へ格納
;       MyGui.Restore() ; Guiメニューを表示する
;       SetTimer(UpdateMenu, 5000)
    }
    Win_3(*)
    {
;       MyGui.Hide()    ; メニュー消す (アクティブがエディタ画面に)
        Sleep(50)       ; ちょっと待つ(速すぎるとアクティブが切り替わらない)
        Send("#^{Right}#^{Right}#^{Right}#^{Left}")
;       Sleep(50)       ; ちょっと待つ
;       Title := WinGetTitle("A")   ;アクティブ タイトル名を取得し Title へ格納
;       MyGui.Restore() ; Guiメニューを表示する
;       SetTimer(UpdateMenu, 5000)
    }
    Win_4(*)
    {
;       MyGui.Hide()    ; メニュー消す (アクティブがエディタ画面に)
        Sleep(50)       ; ちょっと待つ(速すぎるとアクティブが切り替わらない)
        Send("#^{Right}#^{Right}#^{Right}")
;       Sleep(50)       ; ちょっと待つ
;       Title := WinGetTitle("A")   ;アクティブ タイトル名を取得し Title へ格納
;       MyGui.Restore() ; Guiメニューを表示する
;       SetTimer(UpdateMenu, 5000)
    }
    Win_Tab(*)
    {
        MyGui.Hide()    ; メニュー消す (アクティブがエディタ画面に)
        Sleep(50)       ; ちょっと待つ(速すぎるとアクティブが切り替わらない)
        Send("#{Tab}")
;       Sleep(50)       ; ちょっと待つ
;       Title := WinGetTitle("A")   ;アクティブ タイトル名を取得し Title へ格納
;       MyGui.Restore() ; Guiメニューを表示する
;       SetTimer(UpdateMenu, 5000)
    }

    TaskManager_Click(*)
    {
        MyGui.Destroy()
        Run("C:\ProgramData\Microsoft\Windows\Start Menu\Programs\System Tools\Task Manager.lnk")
    }
    
    Shutdown_click(*)
    {
        MyGui.Destroy()
        func_shutdown()
    }

}

func_shutdown()
{
    MyGui := Gui(,"Shutdown")
    MyGui.BackColor := "FF0000"  ; 赤
    MyGui.SetFont("s32")  ; 大きなフォント
    MyGui.Add("Text",, "Shutdown")
    MyGui.Add("Button", " default", "OK").OnEvent("Click", OK_click) ; ;
    MyGui.OnEvent("Escape", CloseWindow) ; [ESC]でダイアログを閉じる
    MyGui.show() ; Gui を表示する

    OK_Click(*)
    {
        Run("C:\Windows\system32\shutdown.exe /s /f /t 0")
        return
    }
    CloseWindow(*)
    {
        MyGui.Destroy()
    }
}

他にも、スタートメニューでは、起動しにくい、よく使うアプリを登録したり、 キー操作に該当するものを、ボタンで実行できるようにしたりなど、 利用方法は無限です。

GUIメニューを呼び出す方法

Autohotkeyのキー登録で、たとえば「Shift + F1」でGUIメニューを呼び出すように登録しておく。

+F1::
{
        GUI_menu()
        return
}

これでキーボードがあれば、Shift+F1で起動しますが、 キーボードなしの環境には、以下の

ジェスチャーで操作 torazaemon2016.hatenablog.jp

のTouchPanelGesturesを使って、

3本指のシングルタップでGUIメニューを呼び出す

の、「③SIngleTap Shift+F1」と3本指でシングルタップとか、 一番下の「②↑→↓」のように2本指で上右下と、登録しておけば簡単に呼び出せます。

ぜひ使いやすいように、カスタマイズして、 「キーボードのないPC」でPCを使いこなしてください。

AutohotkeyとTouchPabelGestures、また以下の手書き文字認識アプリなどを導入して、SurfaceProやGOなどでPCをペンのみで操作する環境を構築してみてください。

手書き入力&音声入力でキーボードなしでも文章を入力することが快適に(&漢字を書いてボケ防止) torazaemon2016.hatenablog.jp

Koboを快適に使う torazaemon2016.hatenablog.jp