LoginSignup
7
10

More than 3 years have passed since last update.

Windowsアプリケーションの自動化-Python編②-

Last updated at Posted at 2021-01-25

Pythonでwin32guiと、win32conを使ってWindowsアプリケーションのメニューバーを操作する
今回はフリーソフトの「サクラエディタ」を操作してみる

参考URL
- サクラエディタ
- Pythonで外部プログラムのメニューバーを操作する(WM_COMMAND)
- Section6.3 メニューバー 応用編
- (C++)外部アプリケーションのメニューバー操作

作業環境

  • Windows10
  • Anaconda3 2020.11
  • Python3.8.5
  • サクラエディタ v2.2.0.1 ←ちょっと古い

メニューバーを操作して日付を挿入するイメージ

  • 編集(E)メニューから挿入(I)日付挿入(D)を選択するとPCの現在時刻が出力される sakura01.png sakura02.png

上記のメニューバー操作を、Pythonで自動化する

メニューバー(タブメニュー)とそのサブメニューのハンドルを取得

メニューバーのハンドルは、子ウィンドウハンドルではなくwin32gui.GetMenu()で取得できる
サブメニューや、サブメニューのサブメニューはwin32gui.GetSubMenu()

winapi.py
import win32gui
import win32con

def run():
    p_hWnd = win32gui.FindWindow(None,"(無題)1 - sakura 2.2.0.1  ")  # 親ウィンドウハンドル(識別番号)を取得
    win32gui.SetForegroundWindow(p_hWnd)  # アプリケーションをデスクトップの前面に表示
    p_menu_hWnd = win32gui.GetMenu(p_hWnd)  # メニューバーのハンドルを取得
    p_menu_count = win32gui.GetMenuItemCount(p_menu_hWnd)  # メニューバー内のサブメニューの個数を取得

    # 各サブメニューのハンドルを取得
    s_menu_dict = {}
    for i in range(p_menu_count):
        s_menu_dict["s_menu_pos{}".format(i)] = win32gui.GetSubMenu(p_menu_hWnd, i)
    [print(k,v) for k,v in s_menu_dict.items()]  # サブメニュー辞書確認

if __name__ == '__main__':
    run()
  • サブメニューのハンドルの例
メニューバー 位置 SubMenu hWnd
ファイル(F) 0 15402645
編集(E) 1 459021
変換(C) 2 67635661
検索(S) 3 18022831
ツール(T) 4 31787639
設定(O) 5 5573367
ウィンドウ(W) 6 93652277
ヘルプ(H) 7 62851833

それぞれのメニューのタブにハンドルが割り振られていることが確認できる

サブメニューのサブメニューを取得する

winapi.py
import win32gui
import win32con

def run():
    p_hWnd = win32gui.FindWindow(None,"(無題)1 - sakura 2.2.0.1  ")  # 親ウィンドウハンドル(識別番号)を取得
    win32gui.SetForegroundWindow(p_hWnd)  # アプリケーションをデスクトップの前面に表示
    p_menu_hWnd = win32gui.GetMenu(p_hWnd)  # メニューバーのハンドルを取得
    p_menu_count = win32gui.GetMenuItemCount(p_menu_hWnd)  # メニューバー内のサブメニューの個数を取得

    # 各サブメニューのハンドルを取得
    s_menu_dict = {}
    for i in range(p_menu_count):
        s_menu_dict["s_menu_pos{}".format(i)] = win32gui.GetSubMenu(p_menu_hWnd, i)
    [print(k,v) for k,v in s_menu_dict.items()]  # サブメニュー辞書確認

    # 編集(E)メニューのサブメニューのIDを取得
    edit_menu = s_menu_dict["s_menu_pos1"]  # 編集(E)はpos1
    edit_menu_count = win32gui.GetMenuItemCount(edit_menu)  # 編集メニュー内のサブメニューの個数を取得
    edit_menu_dict = {}
    for i in range(edit_menu_count):
        s_menu_list = [win32gui.GetMenuItemID(edit_menu, i), win32gui.GetSubMenu(edit_menu, i)]
        edit_menu_dict["edit_menu_pos{}".format(i)] = s_menu_list
    [print(k,v) for k,v in edit_menu_dict.items()]

if __name__ == '__main__':
    run()
  • 編集メニューのIDとサブメニューの例(存在しない場合は0)
編集メニュー 位置 ID SubMenu hWnd
元に戻す(U) 0 30210 0
やり直し(R) 1 30211 0
(セパレーター) 2 0 0
切り取り(T) 3 30601 0
コピー(C) 4 30602 0
貼り付け(P) 5 30604 0
削除(D) 6 30221 0
すべて選択(A) 7 30401 0
(セパレーター) 8 0 0
再変換(R) 9 30285 0
(セパレーター) 10 0 0
CRLF改行でコピー(L) 11 30603 0
折り返し位置に改行をつけてコピー(H) 12 30608 0
矩形貼り付け(X) 13 30605 0
カーソル前を削除(B) 14 30222 0
(セパレーター) 15 0 0
挿入(I) 16 -1 1903099
高度な操作(V) 17 -1 57936345
移動(O) 18 -1 120392207
選択(S) 19 -1 56298477
矩形選択(F) 20 -1 290589067
整形(K) 21 -1 162268843

このIDに対してwin32com.SendMessage()が実行できる
ここでIDの取得に失敗して-1が返されているメニューはサブメニューが存在する
今回は日付挿入がしたいので、挿入(I)メニューのサブメニューからIDを取得する

日付挿入メニューを実行する

winapi.py
import win32gui
import win32con

def run():
    p_hWnd = win32gui.FindWindow(None,"(無題)1 - sakura 2.2.0.1  ")  # 親ウィンドウハンドル(識別番号)を取得
    win32gui.SetForegroundWindow(p_hWnd)  # アプリケーションをデスクトップの前面に表示
    p_menu_hWnd = win32gui.GetMenu(p_hWnd)  # メニューバーのハンドルを取得
    p_menu_count = win32gui.GetMenuItemCount(p_menu_hWnd)  # メニューバー内のサブメニューの個数を取得

    # 各サブメニューのハンドルを取得
    s_menu_dict = {}
    for i in range(p_menu_count):
        s_menu_dict["s_menu_pos{}".format(i)] = win32gui.GetSubMenu(p_menu_hWnd, i)
    [print(k,v) for k,v in s_menu_dict.items()]  # サブメニュー辞書確認

    # 編集(E)メニューのサブメニューのIDを取得
    edit_menu = s_menu_dict["s_menu_pos1"]  # 編集(E)はpos1
    edit_menu_count = win32gui.GetMenuItemCount(edit_menu)  # 編集メニュー内のサブメニューの個数を取得
    edit_menu_dict = {}
    for i in range(edit_menu_count):
        s_menu_list = [win32gui.GetMenuItemID(edit_menu, i), win32gui.GetSubMenu(edit_menu, i)]
        edit_menu_dict["edit_menu_pos{}".format(i)] = s_menu_list
    [print(k,v) for k,v in edit_menu_dict.items()]

    # 挿入(I)メニューのサブメニューのIDを取得する
    ins_menu = edit_menu_dict["edit_menu_pos16"][1]  # 挿入(I)はpos16
    ins_menu_count = win32gui.GetMenuItemCount(ins_menu) # 挿入メニュー内のサブメニューの個数を取得
    ins_menu_dict = {}
    for i in range(ins_menu_count):
        ins_menu_dict["ins_menu_pos{}".format(i)] = win32gui.GetMenuItemID(ins_menu,i)
    [print(k,v) for k,v in ins_menu_dict.items()]

    # 日付挿入を実行
    win32gui.SendMessage(p_hWnd, win32con.WM_COMMAND, ins_menu_dict["ins_menu_pos0"], p_menu_hWnd) # 日付挿入(D)はpos0


if __name__ == '__main__':
    run()
  • 挿入メニューのIDの例
挿入メニュー 位置 ID
日付挿入(D) 0 30790
時刻挿入(T) 1 30791
コントロールコード入力(C) 2 30792

ようやく目的のメニューIDを確認できた

  • SendMessageの引数は下記のようにする

win32gui.SendMessage([親ウィンドウハンドル],[win32con.WM_COMMAND],[対象メニューのID],[メニューバーのハンドル])

とまあ、今回はこのくらいで終わり

まとめ

メニューバーのハンドル値やIDをprint文で確認しながらなんとかサブメニューのサブメニューまで操作する方法は確認できましたが、メニューのテキストを直接参照できたらもう少し読み易くなると思いますね

:arrow_backward: 前回の記事 | [次回の記事] :arrow_forward:

7
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
10