2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Pythonで2つのウィンドウを前面表示しアクティブにしたい

Last updated at Posted at 2022-01-26

やりたいこと

Windows10でPythonを使って、2つのウィンドウを一度アクティブ化し、
Alt+Tabで2つのウィンドウを行き来して、コピペ等の作業を自動化したい。

環境

  • Windows10
  • Python 3.9.7
  • pywin32やpyautoguiはインストール済とする

案1(win32gui.SetWindowPos)

以下を参考に

forground.py
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)

実は調べてて先に出てきたのがこちらの案
以下を参考に

forground.py
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キーの押下を呼べば大丈夫とのこと。
んなアホな。と思いつつ修正し実行してみる。

forground.py
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でいいや。とか考えて、
以下も参考にし

最終的にできたものがこちら

forground.py
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自体非推奨という記事もちらほらありました。
確証を得られるソースは見つけられませんでしたが、
自分で使うお遊び程度のスクリプトなのでヨシ!とします。
以上です。

2
3
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
2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?