ちまたで話題のRPA(Robotic Process Automation)をpythonで自作することで、業務効率化はもちろん、必要に応じて内製でカスタマイズできるシステム作成を目指します。
今回は使えそうなライブラリ PyWin32 の検証をしていきます。
-- 実行環境 --
Windows 10 64ビット版(1903)、python 3.7.4
PyWin32
前回はウィンドウのハンドル取得→ウィンドウ内のコントロールのハンドル取得→クリックの流れを検証しました。引き続きPyWin32で使えそうな関数を試していきます。
subprocess
PyWin32開始前に、pythonからアプリやコマンドの実行には subprocess を使用しています。詳細はドキュメントを参照して頂きたいと思いますが、様々な引数を設定することが可能です。
まずはサンプルとして、subprocess でメモ帳を起動して、起動したメモ帳のハンドル取得→5秒後メモ帳終了のソースを試してみます。
import subprocess
import time
import win32gui
from datetime import datetime, timedelta
def main():
# 対象定義
target_start_command = "notepad.exe"
target_window_title = "無題 - メモ帳"
target_window_class = "Notepad"
timeout_sec = 5
# 実行
prc = exec_command( target_start_command)
print( "Popen : ", prc)
# ハンドル取得
hWnd = get_window_handle( target_window_class, target_window_title, timeout_sec)
print( "hWnd : ", hex( hWnd))
# 5秒後強制終了
time.sleep( 5)
prc.terminate()
#================================================
# コマンド実行(待機なし)
#================================================
def exec_command(target_start_command):
try:
# 実行
prc = subprocess.Popen(target_start_command)
return prc
except Exception as err:
return 0
#================================================
# ウィンドウハンドル取得
#================================================
def get_window_handle(target_window_class, target_window_title, timeout_sec):
try:
# タイムアウト時間取得
end_time = datetime.now() + timedelta( seconds=int(timeout_sec))
# ウィンドウ取得まで待機
while True:
hWnd = win32gui.FindWindow( target_window_class, target_window_title)
if ( hWnd != 0 ): return hWnd
# タイムアウト時間超えたらエラー
if datetime.now() > end_time:
raise ValueError("[ERROR] Function : get_window_handle(Timeout)")
time.sleep(1)
except Exception as err:
return 0
if __name__ == '__main__':
main()
上記の流れ通りの動作をすると思います。注意点は実行→ハンドル取得の間に多少のウェイトが必要だということでしょうか。今回は上記ソースに機能を追加していく形でご紹介していきます。
ウィンドウの位置・サイズ調整、 強制最前列表示
ウィンドウの位置調整やサイズ調整、最前列表示(Zオーダー制御)を試してみたいと思います。このあたりRPAで画像認識で制御する場合は必須になる制御かと思います。
前のソースを利用して、下記処理を行いたいと思います。ちなみに制御の確認時間を取るために、サンプルの強制終了は削りました。手動でメモ帳を閉じて下さい。
- ウィンドウ位置を画面左上に移動
- ウィンドウサイズを700×300に変更
- 最前列表示化(強制)
import subprocess
import time
import win32gui
import win32con
from datetime import datetime, timedelta
def main():
# 対象定義
target_start_command = "notepad.exe"
target_window_title = "無題 - メモ帳"
target_window_class = "Notepad"
timeout_sec = 10
# ウィンドウ制御用定義
pos_x = 0
pos_y = 0
size_cx = 700
size_cy = 300
op_insertafter = win32con.HWND_TOPMOST
op_flags = win32con.SWP_SHOWWINDOW
# 実行
prc = exec_command( target_start_command)
print( "Popen : ", prc)
# ハンドル取得
hWnd = get_window_handle( target_window_class, target_window_title, timeout_sec)
print( "hWnd : ", hex( hWnd))
# ウィンドウ制御
setup_window( hWnd, pos_x, pos_y, size_cx, size_cy, op_insertafter, op_flags)
#================================================
# コマンド実行(待機なし)
#================================================
def exec_command(target_start_command):
try:
# 実行
prc = subprocess.Popen(target_start_command)
return prc
except Exception as err:
return 0
#================================================
# ウィンドウハンドル取得
#================================================
def get_window_handle(target_window_class, target_window_title, timeout_sec):
try:
# タイムアウト時間取得
end_time = datetime.now() + timedelta( seconds=int(timeout_sec))
# ウィンドウ取得まで待機
while True:
hWnd = win32gui.FindWindow( target_window_class, target_window_title)
if ( hWnd != 0 ): return hWnd
# タイムアウト時間超えたらエラー
if datetime.now() > end_time:
raise ValueError("[ERROR] Function : get_window_handle(Timeout)")
time.sleep(1)
except Exception as err:
return 0
#================================================
# ウィンドウ制御
#================================================
def setup_window( hWnd, pos_x, pos_y, size_cx, size_cy, op_insertafter, op_flags):
try:
# 設定
win32gui.SetWindowPos( hWnd, op_insertafter, pos_x, pos_y, size_cx, size_cy, op_flags)
except Exception as err:
return 0
if __name__ == '__main__':
main()
ウィンドウのサイズ・位置調整やZオーダー制御はwin32gui.SetWindowPosで行います。パラメーターのpos_x・pos_y は座標、size_cx・size_cy はウィンドウサイズをセットします。op_insertafter と op_flags については下表を参照してください。ちなみにflags と他のパラメーターが競合する場合どうやらflagが優先されるようです。
INSERTAFTER 設定値 | 数値 |
win32con.HWND_BOTTOM | 1 |
win32con.HWND_TOP | 0 |
win32con.HWND_TOPMOST | -1 |
win32con.HWND_NOTOPMOST | -2 |
FLAGS 設定値 | 数値 |
win32con.SWP_NOREPOSITION | 0x200 (512) |
win32con.SWP_NOCOPYBITS | 0x100 (256) |
win32con.SWP_HIDEWINDOW | 0x80 (128) |
win32con.SWP_SHOWWINDOW | 0x40 (64) |
win32con.SWP_DRAWFRAME | 0x20 (32) |
win32con.SWP_FRAMECHANGED | 0x20 (32) |
win32con.SWP_NOACTIVATE | 0x10 (16) |
win32con.SWP_NOREDRAW | 0x8 (8) |
win32con.SWP_NOZORDER | 0x4 (4) |
win32con.SWP_NOMOVE | 0x2 (2) |
win32con.SWP_NOSIZE | 0x1 (1) |
まとめ
今回はアプリ・コマンド実行とウィンドウの位置など制御についてご紹介しました。引き続き次回もPyWin32で使えそうな関数を探していきたいと思います。
ご覧頂きありがとうございました。
コメント