やりたいこと
Windows10でPythonを使って、2つのウィンドウを一度アクティブ化し、
Alt+Tab
で2つのウィンドウを行き来して、コピペ等の作業を自動化したい。
環境
- Windows10
- Python 3.9.7
- pywin32やpyautoguiはインストール済とする
案1(win32gui.SetWindowPos)
以下を参考に
import win32gui
import win32con
hwnd = win32gui.FindWindow(None, '(無題)1 - sakura 2.3.2.0 ')
win32gui.SetWindowPos(hwnd, win32con.HWND_TOPMOST, 0, 0, 0, 0, win32con.SWP_NOMOVE | win32con.SWP_NOSIZE | win32con.SWP_SHOWWINDOW)
win32gui.SetWindowPos(hwnd, win32con.HWND_NOTOPMOST, 0, 0, 0, 0, win32con.SWP_NOMOVE | win32con.SWP_NOSIZE | win32con.SWP_SHOWWINDOW)
hwnd = win32gui.FindWindow(None, 'Book1 - Excel')
win32gui.SetWindowPos(hwnd, win32con.HWND_TOPMOST, 0, 0, 0, 0, win32con.SWP_NOMOVE | win32con.SWP_NOSIZE | win32con.SWP_SHOWWINDOW)
win32gui.SetWindowPos(hwnd, win32con.HWND_NOTOPMOST, 0, 0, 0, 0, win32con.SWP_NOMOVE | win32con.SWP_NOSIZE | win32con.SWP_SHOWWINDOW)
前面に出てきた!
HWND_TOPMOST
の後にHWND_NOTOPMOST
をすぐ呼ぶ事で固定を解除したうえで前面に持ってこれた。
問題発生
アクティブ化せずに本当にただ前面に表示させただけなので、肝心のAlt+Tab
をしたときに1つ目と2つ目に使いたいウィンドウがいない。
これでは意味がない。
SetWindowPos
の後に強制的にウィンドウ内でクリックイベントを発生させてアクティブ化させよう。
みたいなのが結構あったけど、なんかいまいちしっくりこない…。
ということでこの案は不採用。
案2(win32gui.SetForegroundWindow)
実は調べてて先に出てきたのがこちらの案
以下を参考に
import win32gui
hwnd = win32gui.FindWindow(None, '(無題)1 - sakura 2.3.2.0 ')
win32gui.SetForegroundWindow(hwnd)
hwnd = win32gui.FindWindow(None, 'Book1 - Excel')
win32gui.SetForegroundWindow(hwnd)
こんな感じにしてみる。
問題発生
残念ながらこちらでも問題発生。
前面に表示され、アクティブ化されたのは1つ目のウィンドウだけで、
なぜか2つ目のウィンドウは前面に表示されず、タスクバーで点滅しているのみという謎の現象になってしまった。
さらに2回目のSetForegroundWindow
呼び出し時に以下のようなエラーも出ている。
pywintypes.error: (0, 'SetForegroundWindow', 'No error message is available')
解決策
いろいろ試行錯誤したり調べたりしていると、Stack overflowで同様の現象を発見
なんと、SetForegroundWindow
の前にAltキー
の押下を呼べば大丈夫とのこと。
んなアホな。と思いつつ修正し実行してみる。
import win32gui
import win32com.client
hwnd = win32gui.FindWindow(None, '(無題)1 - sakura 2.3.2.0 ')
win32gui.SetForegroundWindow(hwnd)
hwnd = win32gui.FindWindow(None, 'Book1 - Excel')
shell = win32com.client.Dispatch("WScript.Shell")
shell.SendKeys('%')
win32gui.SetForegroundWindow(hwnd)
できてるー!
エラーもでなくなってるし、Alt+Tab
で2つのウィンドウの行き来ができる!
目的は達成されました。
なぜなのか。そういう仕様?そういう仕組み?おまじないと思っておけばいい?
もしわかる人は教え…と思ったけど聞いても理解できないかもな…。
もう少し改造
画面名完全一致じゃなくて、部分一致とかにしたいなー。とか、
どうせ自動化処理でpyautogui
使うしAltキー
押下もpyautogui
でいいや。とか考えて、
以下も参考にし
最終的にできたものがこちら
import pyautogui as pgui
import win32gui
targetWindows = ['(無題)1', 'Book1']
pgui.press('alt')
def forground(hwnd, title):
name = win32gui.GetWindowText(hwnd)
if name.find(title) >= 0:
win32gui.SetForegroundWindow(hwnd)
# return False # 列挙終了しちゃうと2つ目が取得できないので終了させない
return True
for window in targetWindows:
win32gui.EnumWindows(forground, window)
# 以下ループ処理等でpgui.hotkey('alt','tab')をして
# 画面を交互に切り替えてコピペしたりする。
すっきり収まった感がありますね。
満足!
最後に
Altキー
押下の部分、試しにShiftキー
とかにもしてみましたが、それでも問題なく動く事を確認しました。
何かのキーが押下されれば正常に動かせるようになる、ということなのでしょうか。
あとは、SetForegroundWindow
自体非推奨という記事もちらほらありました。
確証を得られるソースは見つけられませんでしたが、
自分で使うお遊び程度のスクリプトなのでヨシ!とします。
以上です。