Pythonでホットキーを取得する方法:pywin32とwindowprocを使った画面外からのショートカットキー取得方法

Python
この記事は約11分で読めます。
スポンサーリンク

プログラム解説

ライブラリのインポート

今回のプログラムに必要なライブラリは以下の3つです。
ここは以下をインポートするだけですので、特に説明はありません。

from ctypes import *          # windll 使用のため
from ctypes.wintypes import * # handle取得のため
import win32api,win32gui,win32con # win32のAPI使用のため

ホットキーの登録

では続いて今回の主題となりホットキーの登録処理について解説します。

#ホットキーIDは このプログラムで使用するための物なので適当な値を設定する
#0x0000-0xBFFFの間で設定
HOTKEY_ID_1 = 0x0001

def Regist_selfhotkey():
    global exit_flg
    try:
        hInstance  = win32api.GetModuleHandle(None)
        CLASS_NAME   = "myAppl"
        lpWindowName = CLASS_NAME
        
        wc = win32gui.WNDCLASS()
        wc.lpfnWndProc = WindowProc
        wc.hInstance = hInstance
        wc.lpszClassName = CLASS_NAME
        win32gui.RegisterClass(wc)
        
        hwnd = win32gui.CreateWindowEx(0,CLASS_NAME,lpWindowName,win32con.WS_OVERLAPPEDWINDOW,
                  win32con.CW_USEDEFAULT,win32con.CW_USEDEFAULT,win32con.CW_USEDEFAULT,
                  win32con.CW_USEDEFAULT,None,None,hInstance,None)
        
        if hwnd != None:
            windll.user32.RegisterHotKey(hwnd,HOTKEY_ID_1,
                                        win32con.MOD_CONTROL | win32con.MOD_SHIFT,0x41)

        msg = MSG()
        while (windll.user32.GetMessageW(pointer(msg),0,0,0)):
            windll.user32.TranslateMessage(pointer(msg))
            windll.user32.DispatchMessageW(pointer(msg))
            if exit_flg == True:
                break
    finally:
        windll.user32.UnregisterHotKey(None,HOTKEY_ID_1) 

まずは、ホットキーが発生した際に受信するメッセージで、登録したものかどうか判断するために必要なIDを決めます。
以下がそれに該当します。この値は、0x0000~0xBFFFの間であれば任意の値を設定すればいいようです。

HOTKEY_ID_1 = 0x0001

続いて、自分のアプリの名前の登録が必要となりますのでその名前を決めます。
以下が該当し、好きに決めてください。

CLASS_NAME   = "myAppl"

ここまで決めればあとは、以下のような流れでキーを登録していきます。

  1. アプリのインスタンスを取得
  2. ウィンドウクラスを生成して必要な設定を行う
  3. ウィンドウハンドルを生成
  4. ウィンドウハンドルにホットキーを登録

まず 1 アプリのインスタンスを取得 です。

以下のようにwin32apiのGetModuleHandleを呼び出しし、インスタンスを取得します。

hInstance  = win32api.GetModuleHandle(None)

続いて、2 ウィンドウクラスを生成して必要な設定を実施です。

まず、win32gui.WNDCLASS()でインスタンスを生成し、続いて必要な設定を行います。
lpfnWndProc には、ホットキーが発生したときのコールバックを設定します。
hInstance やlpszClassName には、事前に取得したアプリのインスタンスと事前に決めたアプリ名を登録します。
そして最後にwin32guiのRegisterClassを呼び出しクラスを登録します。

        wc = win32gui.WNDCLASS()
        wc.lpfnWndProc = WindowProc
        wc.hInstance = hInstance
        wc.lpszClassName = CLASS_NAME
        win32gui.RegisterClass(wc)

続いて、ウィンドウハンドルを生成します。

以下がそれに該当します。
ここで指定している引数については、別途win32apiを調べてみてください。

        hwnd = win32gui.CreateWindowEx(0,CLASS_NAME,lpWindowName,win32con.WS_OVERLAPPEDWINDOW,
                  win32con.CW_USEDEFAULT,win32con.CW_USEDEFAULT,win32con.CW_USEDEFAULT,
                  win32con.CW_USEDEFAULT,None,None,hInstance,None)

最後に、以下でホットキーを登録します。

windll.user32のRegisterHotKeyを呼び出しキーを登録します。
第1引数には、先ほど生成したウィンドウハンドルを指定、第2引数にはどのキーなのかを識別するためのIDを指定、第3・4引数には、キーを指定します。

MOD_CONTROL -> Ctrlキー
MOD_SHIFT -> Shiftキー
第4引数の0x41というので、Aキーを指定しています。
この第4引数はアスキーコードで0-9もしくは、A-Zを設定すればOKです。

 windll.user32.RegisterHotKey(hwnd,HOTKEY_ID_1,
                                        win32con.MOD_CONTROL | win32con.MOD_SHIFT,0x41)

メッセージの取得処理

そして、メッセージを取得するために以下を継続してメッセージ受信と解析・分配処理を呼び続ける必要があります。

以下がそれに該当します。
はじめのmsg = MSG()とい部分は、処理するためのポインタを取得しています。

 msg = MSG()
        while (windll.user32.GetMessageW(pointer(msg),0,0,0)):
            windll.user32.TranslateMessage(pointer(msg))
            windll.user32.DispatchMessageW(pointer(msg))
            if exit_flg == True:
                break

GetMessageWで、メッセージの取得をします。これは呼び出すとキー入力があるまではここで待機する同期関数になります。
TranslateMessageで解釈・DispatchMessageWで分配といった流れになります。
最後にexit_flgがTrueになるとLoopから抜ける処理を入れてあります。
※exit_flgは、登録したホットキーが入力されると、Trueになるようにしてあります。

コールバック処理

最後にキーの入力イベントを披露コールバック関数です。
以下が該当の関数です。
WindowProcの引数については、Win32の関数と同じものになりますので、そちらを参照ください。
この処理では、単純にイベントがWM_HOTKEYの時に、HOTKEY_ID_1の入力の場合に、プリント出力をして、フラグを立てるだけの処理を行っています。
最後に、DefWindowProcWを呼び出しして標準の処理も行うようにしている形となります。

def WindowProc(hwnd,uMsg,wParam,lPram):
    global exit_flg
    if uMsg == win32con.WM_HOTKEY:
        if wParam == HOTKEY_ID_1:
            print("receive Ctrl Shift A")
            exit_flg = True
    return windll.user32.DefWindowProcW(hwnd,uMsg,wParam,lPram)
スポンサーリンク

まとめ

いかがだったでしょうか?
.netでアプリを作成する際と同じような処理でWin32 の処理が使用できることが理解いただけたと思います。
たったこれだけの処理が、アプリの外部からキーイベント取得できますので、とても便利なのではないかと思います。(あまり使用することもないのかもしれませんが・・・)

ただ、.netで作成するときも同じだったと思いますが、Winメッセージを取得するのにループ処理で取得処理を呼び出し続けないといけないので、このメッセージ取得は別でスレッド等を立ち上げて呼び出しする必要があるかと思いますので、ご使用の際はそのあたりを考慮に入れておいてください。

ちなみに、スレッドの使い方が分からないという方は以下でマルチスレッドの使用方法を記載していますので、参考にしてみてください。

最後に、Pythonの基礎を学びたい方は以下がおすすめです。私も持っていてたまに眺めて勉強していますものですのでぜひ購入して学習してみてください。

コメント

タイトルとURLをコピーしました