自作キーボードのキーマップをショートカットキーで表示する方法

前置き

自作キーボードを組み立てたり買ったりして、今使っているNaked48が5台目になるが、自作キーボードはキー数が少ないのが一般的なので、どうしてもレイヤー機能を駆使して少ないキーでやりくりしていく必要がある。

しかし、アルファベットやControlなどの装飾キーはともかく、レイヤー機能を使って割り当てたキーについては、キーボードを変えると覚えられなくなってしまう。

Keyboard Layout Editorで書き上げたキーマップを印刷するという方法でも対処できるのだが、せっかくパソコンを使っているのだから、ディスプレイ上にキーマップを表示する方が効率的なはずである。

幸い、WindowsでもMacでも、アプリを使えば任意のショートカットキーを作成できるので、どちらのOSでも同じショートカットキーでキーマップを表示できるようにしてみた。本記事では、その方法を紹介したい。

なお、「一度に接続するキーボードは一つだけで、表示させるキーマップ画面も一種類だけ」という前提なので、その前提で読んで欲しい。

TL, DR

Windowsの場合

キーマップ表示に使ったAutoHotKeyのコードをコンパイルして実行ファイル化しているので、必要な人は以下のリンクからZipファイルをダウンロードして、適当なフォルダに回答してexeファイルを実行すればOKである。なお、設定ファイルのdisplay-keyboardLayout.iniファイルは、実行ファイルと同じフォルダに保存すること。また、設定を変更したいときは、AutoHotKeyをインストールして、display-keyboardLayout.ahkを編集すること。

キーマップ表示画面(Windows)
キーマップ表示画面(Windows)

Macの場合

キーマップ表示に使ったKarabiner-Elementsの設定をインポートできるようにしているので、必要な人は以下のリンクから設定(Display Custom Keymap)をインポートすればOKである。なお、デフォルトでは~/cheetSheet_customKeyboard.pngという画像ファイルを読み込むようにしている。

キーマップ表示画面(Mac)
キーマップ表示画面(Mac)

Windowsでの方法

使うアプリ

  • AutoHotKey

処理の概要

AutoHotKeyには、画像を読み込んで表示する機能がデフォルトで備わっており、さらに、設定をiniファイルで読み書きする機能も備わっている。そのため、表示させるキーマップ画像をユーザーが指定し、その指定を.iniファイルに書き込んで、2回目以降はその設定を.iniファイルから読み込んで表示させる方法とした。

具体的な処理の流れは以下のコードのとおり。

~+^Space::
  Input, inputText, I L11 T0.5, +^Space
  If (A_PriorHotKey == A_ThisHotKey and A_TimeSincePriorHotkey < 200)
  {
    Gui, Destroy  ;一度画面を破棄しないと、もう一度ホットキーを押したときにエラーになる。
    IniRead, filePath, %A_WorkingDir%/display-keyboardLayout.ini, ImageSource, filePath,
    Gui, New,, Keyboard Layout 
    Gui, Color, FFFFFF
    Gui, Add, Picture ,vcheetSheet, %filePath%
    Gui, Font, S14 Norm Q5
    Gui, Add, Text ,Border BackgroundWhite vimgFilePath, ImageSource: %filePath%
    Gui, Add, Button, default gSelectImage, Select ImageFile(Enter)
    Gui, Add, Button, gClose, Close(ESC)
    Gui, Show
  }
  Return
GuiEscape:
  Gui, Destroy
  Return
SelectImage:
  FileSelectFile, newFilePath, 1, %A_WorkingDir%, , Image File(*.jpg;*.jpeg;*.png;*.bmp;*.gif)
  If (ErrorLevel == 0)
  {
    IniWrite, %newFilepath%, %A_WorkingDir%/display-keyboardLayout.ini, ImageSource, filePath
    Gui, Destroy  ;一度画面を破棄しないと思い通りの再表示にならない。
    Gui, New,, Keyboard Layout 
    Gui, Add, Picture ,vcheetSheet, %newFilePath%
    Gui, Font, S14 Norm Q5
    Gui, Add, Text ,Border BackgroundWhite vimgFilePath, ImageSource: %newFilePath%
    Gui, Add, Button, default gSelectImage, Change_ImageFile
    Gui, Add, Button, gClose, Close(ESC)
    Gui, Show
  }
  Return
Close:
  Gui, Destroy
  Return

まず、~+^Space::で、Shift-Ctrl-Spaceというショートカットキー(AutoHotKeyではホットキーと呼ぶ)を指定する。

次のInput, inputText, I L11 T0.5, +^SpaceIf (A_PriorHotKey == A_ThisHotKey and A_TimeSincePriorHotkey < 200)については、AutoHotKeyの解説と自分の設定について | 閑古鳥ブログで解説しているキー2連打処理を、キーの組み合わせを変えて適用したものである。

5行目のGui,は画面を閉じる処理である。なぜ画面表示前に画面を閉じる処理を書いているかというと、画面を開いている時に再度Shift-Ctrl-Spaceを2連打すると、同じ変数を繰り返し使うなというAutoHotKeyのエラーが表示されるためである。このエラーの回避方法が分からなかったので、強引な方法ではあるが、画面の表示に関係なく画面を閉じる処理を追加したのである。

6行目のIniRead, filePath, %A_WorkingDir%/display-keyboardLayout.ini, ImageSource, filePath,は、このスクリプトファイル(またはコンパイル済みの実行ファイル)が保存されているフォルダと同じフォルダにあるdisplay-keyboardLayout.iniファイルのImageSourceセクションのfilePathキーの値を読み込んで、filePath変数に代入するという処理である。

7行目〜13行目は、キーマップ画像の読み込み、キーマップ画像のフルパスを表示するテキストボックスの作成、キーマップ画像の選択画面を表示するボタンの作成、フォントの設定などを行なっている。

そして、14行目のGui, Showで作成したGUI画面を表示し、16行目のReturnで処理を一旦終了させている。

18行目のGuiEscapeは、画面表示中にEscapeキーを押した時に実行されるサブルーチンである。Escapeキーを押したら画面を閉じてメモリやシステムリソースも開放したいので、19行目のGui, Destroyで画面を閉じて20行目のReturnで処理を終了させている。

22行目のSelectImageは、キーマップファイルの選択画面を表示して、選択したキーマップを表示するサブルーチンで、「Image Select」ボタンに割り当てている。

23行目のFileSelectFile, newFilePath, 1, %A_WorkingDir%, , Image File(*.jpg;*.jpeg;*.png;*.bmp;*.gif)でファイル選択画面を表示し、選択したファイルのフルパスをnewFilePath変数に格納している。

このファイル選択が成功すれば、AutoHotKeyの組み込み変数のErrorLevel0が格納されるので、If (ErrorLevel == 0)でファイル選択が成功したか否かを判定している。

その選択したファイルのパスは、26行目のIniWrite, %newFilepath%, %A_WorkingDir%/display-keyboardLayout.ini, ImageSource, filePathiniファイルのImageSourceセクションのfilePathキーに書き込んでいる。これにより、次にキーマップを開いた時は、ここで選択したキーマップが表示されるようになる。

27行目以降の処理は、5〜14行目の処理と同じなので、説明は省略する。

38行目のCloseは、画面を閉じようとした時に実行されるサブルーチンである。処理の内容は18行目のGuiEscapeの処理と同じである。

Macでの方法

使うアプリ

  • Karabiner-Elements
  • QuickLook

処理の概要

Karabiner-Elementsには、上で紹介したAutoHotKeyとは違い、画像表示の機能が備わっていないことから、画像表示の処理はQuickLookを使用した。QuickLookを使用した理由は、Macにはデフォルトで備わっていることと、Escapeキーを押せばすぐに閉じられるためである。

そのため、ショートカットキーを押したらコマンドラインからQuickLookを立ち上げてキーマップを表示する、という処理にしている。画像を表示した後の処理はQuickLookに任せているので、Karabiner-Elementsで設定する部分は、画像を表示することだけである。

また、AutoHotKeyとの違いは画像表示だけではなく、ファイル選択機能や設定をiniファイルで読み書きする機能も無いことから、表示するキーマップの画像ファイルのフルパスは、設定ファイルにハードコーディングすることになる。

一方、Karabiner-Elementsには、特定のキーボードを接続している時だけ自作のショートカットキーを有効にするという機能が備わっている。そのため、同じショートカットキーで、複数のキーボードのキーマップファイルを表示させることができる。

実際のコード

{
  "description": "Double type 'Shift-Ctrl-Space', Open 'CheetSheet for Naked48'.",
  "manipulators": [
    {
      "type": "basic",
      "from": {
        "key_code": "spacebar",
        "modifiers": {
          "mandatory": [
            "control",
            "shift"
          ],
          "optional": [
            "any"
          ]
        }
      },
      "to": [
        {
          "shell_command": "qlmanage -p ~/cheetSheet_customKeyboard.png > /dev/null 2<&1"
        }
      ],
      "conditions": [
        {
          "type": "variable_if",
          "name": "ctrl_shift_space_tapCount",
          "value": 1
        }
      ]
    },
    {
      "type": "basic",
      "from": {
        "key_code": "spacebar",
        "modifiers": {
          "mandatory": [
            "control",
            "shift"
          ],
          "optional": [
            "any"
          ]
        }
      },
      "to": [
        {
          "set_variable": {
            "name": "ctrl_shift_space_tapCount",
            "value": 1
          }
        },
        {
          "key_code": "spacebar",
          "modifiers": [
            "shift",
            "control"
          ]
        }
      ],
      "to_delayed_action": {
        "to_if_invoked": [
          {
            "set_variable": {
              "name": "ctrl_shift_space_tapCount",
              "value": 0
            }
          }
        ],
        "to_if_canceled": [
          {
            "set_variable": {
              "name": "ctrl_shift_space_tapCount",
              "value": 0
            }
          }
        ]
      },
      "conditions": [
        {
          "type": "variable_if",
          "name": "ctrl_shift_space_tapCount",
          "value": 0
        },
        {
          "type": "device_if",
          "identifiers": [
            {
              "vendor_id": 65261,
              "product_id": 12384
            }
          ]
        }
      ]
    }
  ]
}

キー2連打の処理については、Karabiner-Elementの設定例について - 左control2連打でSafariを起動 - Qiitaで紹介した方法を、ショートカットキーを入れ替えて適用したものである。

また、キーマップ画像表示の処理は"shell_command": "qlmanage -p ~/cheetSheet_customKeyboard.png > /dev/null 2<&1"の部分であるが、表示する画像ファイルを設定ファイル中にハードコーディングする必要があるので、キーボード毎に違うキーマップ画像を表示させるには、キーボード毎に設定コードを書く必要がある。

ただし、Karabiner-Elementsの設定項目をまとめました - conditions/identifiers/vendor_id, product_id - Qiitaで紹介している方法を使うことで、特定のキーボードを繋いだ時だけ設定を有効にすることが可能なので、その方法を使えば、上のコードをコピーして次の2カ所を変えるだけでキーボード毎の設定が可能になる。

  • "shell_command": "qlmanage -p ~/cheetSheet_customKeyboard.png > /dev/null 2<&1"
  • { "type": "device_if", "identifiers": [ { "vendor_id": 65261, "product_id": 12384 } ] }

"vendor_id": 65261, "product_id": 12384に指定するIDは、Karabiner-Elementsの以下の画面で確認することができる。

キーボードのIDの確認画面

あとがき

自作キーボードのキーマップをWindowsとMacでそれぞれ表示するショートカットの作成方法は、以上のとおりである。

これ以外にも方法はあると思うが、今のところこれで問題なく運用できているので、しばらくはこの方法で対処していくことになると思う。

キーマップを画面で確認できるのは、思ったより便利なので、よろしければ導入してみて欲しい。


本記事の執筆で参考にした記事

  • Windows

  • Mac

記事中でリンクを貼ったのに「参考にした記事」に上がっていない記事は、本ブログの記事か私がQiitaに書いた記事である。