Keyhac
Keyhac とは
Keyhac とは、Python を使ってキーカスタマイズが行 えるアプリケーションです.
https://sites.google.com/site/craftware/keyhac-ja
定型文入力をスニペットのように入力できるツールとして、 aText を使っています.無料でも単純な定型文は入力することができますが、フル機能を使うためにはProバージョンにしなければならず、有料です.そんなに頻繁に利用するわけでもないですし、とりあえず他の方法でできないかと探していたところ、Keyhacにたどり着きました.
Keyhac のいいところは Python でカスタマイズできるところにあります.以前は Charu3 を使っていました.マクロは用意されていたりプラグインもあるので、ある程度カスタマイズができるのですが、やはり限界があります.Python が使えるなら、テキスト処理以外のこともいろいろできるので汎用性は高いです.
Keyhacの設定
Keyhacの設定方法を解説します.環境は Windows です.
まず、Keyhacを起動するとシステムトレイに常駐します.システムトレイからKeyhacアイコンを右クリックして、設定の編集
を選択すると、設定ファイル(config.py
)がエディタで開かれます.この設定ファイルを編集して、システムトレイから右クリック、設定のリロード
で反映されます.
コンソール画面
設定ファイルに問題があったり、実行したときにエラーが発生した場合はコンソール画面が表示されます.設定ファイルから print
すると、このコンソール画面が表示されます.画面が表示されていない場合、システムトレイのKeyhacアイコンをダブルクリックすると表示されます.コンソール画面をクリアしたい場合、システムトレイからKeyhacアイコンを右クリックして端末のクリア
を選択します.また、内部ログをON
を選択すると、ログ情報がコンソール画面に出力されます.無効にしたいときは内部ログをOFF
を選択します.
configure関数
Keyhacを起動すると、設定ファイル(config.py
)にあるconfigure
関数が呼ばれます.ここに処理を記述してきます.
エディタの設定
設定ファイルの編集に使うエディタを設定します.Visual Studio Codeは開く場合は次のようにします.
keymap.editor = "code"
フォントの設定
コンソールなどのウィンドウに表示されるテキストのフォントを指定します.好きなフォントを指定してください.フォントを指定すると、起動に少し時間がかかるようになります.
keymap.setFont("Iosevka", 16)
テーマの設定
標準では white
と black
が用意されています.
keymap.setTheme("black")
キーの再登録
Keyhacではキーを別のキーに割り当てることができます.それにはkeymap.replaceKey
メソッドを使います.私の場合、無変換
キーと変換キー
を適当な場所に再割り当てし、ユーザー修飾キーとしてそれらを登録します.ユーザー修飾キーの登録は keymap.defineModifier
メソッドを使います.
# Key replacement
keymap.replaceKey("(29)", 235) # 無変換
keymap.replaceKey("(28)", 236) # 変換
# User modifier key definition
keymap.defineModifier(235, "User0")
keymap.defineModifier(236, "User1")
グローバルキーマップ
Keyhacではウィンドウごとにキーカスタマイズを行うことができますし、すべてのウィンドウに対してキーカスタマイズできます.後者の場合、keymap.defineWindowKeymap
メソッドで取得したオブジェクトで設定します.
# Global keymap which affects any windows
keymap_global = keymap.defineWindowKeymap()
例えば、Shift
キーとZ
キーを同時に押したときの処理を設定したい場合、次のようになります.
keymap_global["S-Z"] = closure
キーに対応したクロージャを指定します.Keyhacはキーが押されたときに対応したクロージャを呼び出します.
指定できるキーやモディファイアについてはドキュメントを参照してください.
初期の設定
Keyhacでは標準でいくつか設定されています.まずは全部消すか、コメントアウトしておきましょう.
Keyhacモジュール
pyauto
とkeyhac
はKeyhacに用意されたモジュールです.pyauto
は低レベルOS機能で、keyhac
はそれ以外のものです.例えば、クリップボードを操作する場合、keyhac
の setClipboardText
, getClipboardText
を使います.
from keyhac import setClipboardText, getClipboardText
面倒なら、一括でインポートしても構いません.
from keyhac import *
これらのモジュールについて、詳しくはドキュメントを参照してください.
Keyhacを使って emacs ライクなキー操作を実現している方がいます.設定ファイルも参考になると思います.
https://github.com/smzht/fakeymacs
実装した機能の紹介
それでは、次から具体的な設定をしていきます.といっても、あまり使いこなしている訳ではありませんので悪しからず. ヘルパー関数は、それぞれ初出した場所に明記しています.
カーソルをス クリーン中央に移動する
Shift
キーと右Ctrl
キーを押したときに実行します.
def set_cursor_pos(x, y):
keymap.beginInput()
keymap.input_seq.append(pyauto.MouseMove(x, y))
keymap.endInput()
def cursor_to_center():
wnd = keymap.getTopLevelWindow()
wnd_left, wnd_top, wnd_right, wnd_bottom = wnd.getRect()
to_x = int((wnd_left + wnd_right) / 2)
to_y = int((wnd_bottom + wnd_top) / 2)
set_cursor_pos(to_x, to_y)
keymap_global["S-RCtrl"] = cursor_to_center
ウィンドウをスクリーン 中央に移動する
左右のCtrl
キーを押したときに実行します.
def delay(sec=0.05):
time.sleep(sec)
def get_monitor_areas():
monitors = pyauto.Window.getMonitorInfo()
main_monitor_first = sorted(monitors, key=lambda x: x[2], reverse=True)
non_taskbar_areas = list(map(lambda x: x[1], main_monitor_first))
return non_taskbar_areas
def set_window_rect(rect):
wnd = keymap.getTopLevelWindow()
if list(wnd.getRect()) == rect:
wnd.maximize()
else:
if wnd.isMaximized():
wnd.restore()
delay()
wnd.setRect(rect)
def window_to_center():
wnd = keymap.getTopLevelWindow()
if wnd.isMaximized():
return None
wnd_left, wnd_top, wnd_right, wnd_bottom = wnd.getRect()
width = wnd_right - wnd_left
height = wnd_bottom - wnd_top
mntr_left, mntr_top, mntr_right, mntr_bottom = get_monitor_areas()[0]
center_h = (mntr_right - mntr_left) / 2
center_v = (mntr_bottom - mntr_top) / 2
lx = int(center_h - width / 2)
ly = int(center_v - height / 2)
to_rect = (lx, ly, lx + width, ly + height)
set_window_rect(to_rect)
keymap_global["C-RCtrl"] = window_to_center
もし、ウィンドウを最大化していた場合、先に通常のウィンドウに戻す必要があります.その場合、次のようにします.
def window_to_center_force():
wnd = keymap.getTopLevelWindow()
if wnd.isMaximized():
wnd.restore()
delay()
window_to_center()
ウィンドウをカーソル位置に移動する
無変換
キーと右Ctrl
キーを押したときに実行します.
def window_to_cursor():
wnd = keymap.getTopLevelWindow()
if wnd.isMaximized():
return None
wnd_left, wnd_top, wnd_right, wnd_bottom = wnd.getRect()
width = wnd_right - wnd_left
height = wnd_bottom - wnd_top
x, y = pyauto.Input.getCursorPos()
to_rect = (x, y, x + width, y + height)
set_window_rect(to_rect)
keymap_global["U0-RCtrl"] = window_to_cursor
ウィンドウを切り替える
Alt+Tab
やWin+Tab
のような機能です.Keyhacではリストを表示するウィンドウ機能がありますので、そちらを使います.今回は、無変換
キーとスペース
キーを押したときに実行します.
# import re
# from keyhac import cblister_FixedPhrase
debug_mode = False # デバッグ出力を有効にする場合は True にする
def dbg(text):
if debug_mode:
print("dbg: " + text)
def truncate(string, length, ellipsis="..."):
return string[:length] + (ellipsis if string[length:] else "")
def truncate_cjk(string, length, ellipsis="..."):
# http://www.unicode.org/reports/tr11/
count = 0
text = ""
for c in string:
if unicodedata.east_asian_width(c) in "FWA":
count += 2
else:
count += 1
if count > length:
text += ellipsis
break
text += c
return text
def switch_windows():
dbg(">>>>> switch_windows <<<<<")
def popWindowList():
# If the list window is already opened, just close it
if keymap.isListWindowOpened():
keymap.cancelListWindow()
return
def getWindowList(wnd, arg):
if not wnd.isVisible():
return True
# if not wnd.getOwner():
# return True
if wnd.getText() == "":
return True
dbg(wnd.getProcessName())
dbg(" " + wnd.getClassName())
dbg(" " + wnd.getText())
if re.match(
r"(keyhac|SystemSettings|ApplicationFrameHost|TextInputHost|explorer|onenoteim)\.exe",
wnd.getProcessName(),
):
dbg("(pass)")
return True
# if re.match(r"chrome", wnd.getClassName()):
# window_list.append(wnd)
window_list.append(wnd)
return True
window_list = []
Window.enum(getWindowList, None)
popup_list = [
("{:>20s} :: {}".format(truncate(i.getProcessName()[:-4], 17), truncate_cjk(i.getText(), 45)), i)
for i in sorted(window_list, key=lambda x: x.getProcessName())
]
if mysetting.debug:
for i in popup_list:
dbg(i[0])
listers = [("Windows", cblister_FixedPhrase(popup_list))]
item, mod = keymap.popListWindow(listers)
if item:
item[1].setForeground()
# Because the blocking procedure cannot be executed in the key-hook,
# delayed-execute the procedure by delayedCall().
keymap.delayedCall(popWindowList, 0)
keymap_global["U0-Space"] = switch_windows
実行結果は次のようになります.
正規表現(Line:47)を使って表示されるウィンドウを制限することもできます.
ちなみに、以前はTascherというのを使っていたのですが、Keyhacに置き換えました.
Keyhacのウィンドウ機能は、実は複数のリストを内部で持つことができます.以下のコードにあるように、listers
に配列を設定しています.
listers = [("Windows", cblister_FixedPhrase(popup_list))]
複数指定した場合、キーボードの左右キーで切り替えることができます.