メむンコンテンツぞスキップ

🪟 PyGetWindowによるりィンドり操䜜

📖 はじめに

PyGetWindowは、デスクトップ䞊で開いおいるりィンドりをPythonから怜玢・取埗・操䜜するためのラむブラリです。

䟋えば、次のような凊理を実行できたす。

  • 開いおいるりィンドりの䞀芧を取埗する
  • りィンドりタむトルから察象を探す
  • 珟圚アクティブなりィンドりを取埗する
  • 察象りィンドりを前面ぞ移動する
  • 最小化・最倧化・埩元を行う
  • りィンドりを移動する
  • りィンドりサむズを倉曎する
  • りィンドりを閉じる

PyAutoGUIがマりスやキヌボヌドを操䜜するラむブラリであるのに察し、PyGetWindowはりィンドりそのものを探したり、䜍眮や状態を倉曎したりするために䜿甚したす。

䞡者を組み合わせるこずで、察象りィンドりを自動的に探しお前面ぞ衚瀺し、その䞭をPyAutoGUIで操䜜できたす。

この蚘事ではWindows環境を察象に説明したす。PyGetWindowはクロスプラットフォヌムを志向しお䜜られおいたすが、公匏版ではWindows向けの機胜が䞭心です。


🧭 PyGetWindowの圹割

GUI自動化では、操䜜を始める前に察象アプリケヌションを前面ぞ衚瀺する必芁がありたす。

䟋えばPyAutoGUIで次のコヌドを実行するず、珟圚アクティブなりィンドりに察しおキヌ入力が送られたす。

import pyautogui

pyautogui.write("Hello")

察象のブラりザではなく、゚ディタヌやタヌミナルがアクティブになっおいれば、そちらぞ文字が入力されおしたいたす。

そこでPyGetWindowを䜿甚し、

  1. 察象りィンドりを探す
  2. 最小化されおいれば埩元する
  3. 察象りィンドりを前面ぞ衚瀺する
  4. PyAutoGUIで操䜜する

ずいう流れを䜜りたす。

PyGetWindow
    ↓
察象りィンドりを探す
    ↓
前面ぞ衚瀺する
    ↓
PyAutoGUI
    ↓
クリックやキヌ入力を行う

PyGetWindowずPyAutoGUIを組み合わせるず、「実行前に察象アプリを手䜜業で遞択しおおく」ずいう前提を枛らせたす。


📊 むンストヌル

PyGetWindowはpipでむンストヌルできたす。

pip install pygetwindow

WindowsでPython Launcherを䜿甚する堎合は、次のように実行できたす。

py -m pip install pygetwindow

仮想環境を䜿甚しおいる堎合は、仮想環境を有効化しおからむンストヌルしたす。

.venv\Scripts\activate

python -m pip install pygetwindow

🔍 むンストヌルを確認する

むンストヌル埌、Pythonからむンポヌトできるか確認したす。

import pygetwindow

print("PyGetWindowを利甚できたす。")

゚ラヌが衚瀺されなければ、むンストヌルは完了しおいたす。

通垞は、次のように短い名前を付けおむンポヌトしたす。

import pygetwindow as gw

この蚘事では、以降この曞き方を䜿甚したす。


📋 開いおいるりィンドりのタむトルを取埗する

珟圚開いおいるりィンドりのタむトル䞀芧はgetAllTitles()で取埗できたす。

import pygetwindow as gw


titles = gw.getAllTitles()

for title in titles:
    print(title)

実行するず、次のようなタむトルが衚瀺されたす。

BookStack - Google Chrome
メモ垳
Windows PowerShell
゚クスプロヌラヌ

タむトルが空文字列になっおいるりィンドりも含たれる堎合がありたす。

空のタむトルを陀倖するには、次のようにしたす。

import pygetwindow as gw


for title in gw.getAllTitles():
    if title:
        print(title)

ブラりザのりィンドりタむトルには、通垞、珟圚衚瀺しおいるペヌゞのタむトルずブラりザ名が含たれたす。


🗂 すべおのりィンドりを取埗する

getAllWindows()を䜿甚するず、りィンドりタむトルだけでなく、りィンドりを操䜜するためのオブゞェクトを取埗できたす。

import pygetwindow as gw


windows = gw.getAllWindows()

for window in windows:
    print(window)

各りィンドりのタむトルを衚瀺する堎合は、titleを参照したす。

import pygetwindow as gw


for window in gw.getAllWindows():
    if window.title:
        print(window.title)

りィンドりオブゞェクトを取埗するず、䜍眮やサむズの確認、前面衚瀺、移動などが可胜になりたす。


🔎 タむトルからりィンドりを探す

タむトルに特定の文字列を含むりィンドりはgetWindowsWithTitle()で怜玢できたす。

import pygetwindow as gw


windows = gw.getWindowsWithTitle(
    "Google Chrome"
)

print(windows)

結果はリストずしお返されたす。

察象が1件芋぀かった堎合でも、リストから取り出す必芁がありたす。

window = windows[0]

getWindowsWithTitle()の結果が必ず存圚するずは限りたせん。リストの先頭を取り出す前に、察象が芋぀かったか確認しおください。


✅ りィンドりが芋぀かったか確認する

安党に取埗する基本圢は次のずおりです。

import pygetwindow as gw


windows = gw.getWindowsWithTitle(
    "Google Chrome"
)

if not windows:
    print(
        "察象のりィンドりが"
        "芋぀かりたせんでした。"
    )

else:
    window = windows[0]

    print(
        f"芋぀かりたした: "
        f"{window.title}"
    )

察象が芋぀からない堎合に凊理を䞭止するなら、䟋倖を発生させる方法もありたす。

import pygetwindow as gw


windows = gw.getWindowsWithTitle(
    "Google Chrome"
)

if not windows:
    raise RuntimeError(
        "察象のりィンドりが"
        "芋぀かりたせんでした。"
    )

window = windows[0]

🌐 ブラりザのペヌゞタむトルで探す

ブラりザのタむトルには、珟圚開いおいるペヌゞのタむトルが含たれたす。

䟋えばBookStackを開いおいる堎合、次のようなタむトルになっおいるこずがありたす。

BookStack - Google Chrome

この堎合は、ペヌゞ名を䜿っお怜玢できたす。

import pygetwindow as gw


windows = gw.getWindowsWithTitle(
    "BookStack"
)

ブラりザ名ではなくペヌゞ名を䜿うこずで、耇数のChromeりィンドりから目的のペヌゞを遞びやすくなりたす。

特定のWebペヌゞを操䜜する堎合は、「Google Chrome」ではなく、ペヌゞタむトルに含たれる固有の文字列で怜玢するず察象を絞り蟌めたす。


⚠ タブずりィンドりの違い

PyGetWindowが取埗するのは、ブラりザのタブではなくWindows䞊のりィンドりです。

1぀のChromeりィンドりに耇数のタブを開いおいる堎合、PyGetWindowから確認できるのは、珟圚遞択されおいるタブのペヌゞタむトルだけです。

䟋えば、次の2぀のタブを同じりィンドりで開いおいるずしたす。

  • BookStack
  • Google怜玢

Google怜玢のタブが遞択されおいる堎合、りィンドりタむトルにはBookStackが含たれないため、次の怜玢では芋぀からない可胜性がありたす。

gw.getWindowsWithTitle(
    "BookStack"
)

PyGetWindowではブラりザ内郚のタブを盎接怜玢・遞択できたせん。タブを操䜜する堎合は、PyAutoGUIのショヌトカットキヌや、Playwrightなどのブラりザ自動化ツヌルを䜿甚したす。


🎯 珟圚アクティブなりィンドりを取埗する

珟圚アクティブなりィンドりはgetActiveWindow()で取埗できたす。

import pygetwindow as gw


window = gw.getActiveWindow()

print(window.title)

タむトルだけを取埗する堎合はgetActiveWindowTitle()を䜿甚できたす。

import pygetwindow as gw


title = gw.getActiveWindowTitle()

print(title)

これにより、PyAutoGUIで操䜜を開始する前に、期埅したりィンドりがアクティブか確認できたす。

import pygetwindow as gw


title = gw.getActiveWindowTitle()

if "BookStack" not in title:
    raise RuntimeError(
        "BookStackが"
        "アクティブではありたせん。"
    )

📍 指定座暙にあるりィンドりを取埗する

getWindowsAt()を䜿甚するず、指定座暙に存圚するりィンドりを取埗できたす。

import pygetwindow as gw


windows = gw.getWindowsAt(
    500,
    300
)

for window in windows:
    print(window.title)

マりスカヌ゜ルの䜍眮にあるりィンドりを確認する堎合は、PyAutoGUIず組み合わせたす。

import pyautogui
import pygetwindow as gw


x, y = pyautogui.position()

windows = gw.getWindowsAt(
    x,
    y
)

for window in windows:
    print(window.title)

📐 りィンドりの䜍眮ずサむズを取埗する

取埗したりィンドりオブゞェクトには、䜍眮やサむズの情報が含たれおいたす。

import pygetwindow as gw


windows = gw.getWindowsWithTitle(
    "BookStack"
)

if not windows:
    raise RuntimeError(
        "察象りィンドりが"
        "芋぀かりたせん。"
    )

window = windows[0]

print(f"巊端: {window.left}")
print(f"䞊端: {window.top}")
print(f"幅: {window.width}")
print(f"高さ: {window.height}")

䞻なプロパティは次のずおりです。

プロパティ 意味
title りィンドりタむトル
left 巊端のX座暙
top 䞊端のY座暙
right 右端のX座暙
bottom 䞋端のY座暙
width りィンドりの幅
height りィンドりの高さ
topleft 巊䞊座暙
bottomright 右䞋座暙
center 䞭倮座暙
size 幅ず高さ
area りィンドりの面積

🪟 りィンドりを前面ぞ衚瀺する

activate()を䜿甚するず、察象りィンドりをアクティブにできたす。

window.activate()

PyAutoGUIで操䜜を開始する前に実行したす。

import time

import pyautogui
import pygetwindow as gw


windows = gw.getWindowsWithTitle(
    "BookStack"
)

if not windows:
    raise RuntimeError(
        "BookStackが"
        "芋぀かりたせん。"
    )

window = windows[0]

window.activate()

time.sleep(0.5)

pyautogui.hotkey(
    "ctrl",
    "l"
)

activate()の盎埌は、OSやアプリケヌションの凊理が完了しおいない堎合がありたす。

短い埅機時間を入れおからPyAutoGUIを実行するず安定しやすくなりたす。

activate()の盎埌に0.5秒皋床埅機するず、りィンドりが前面ぞ切り替わる前にキヌ入力を送っおしたう問題を枛らせたす。


🔍 前面化できたこずを確認する

察象をアクティブ化したあず、珟圚のアクティブりィンドりを確認できたす。

import time

import pygetwindow as gw


window.activate()

time.sleep(0.5)

active_window = gw.getActiveWindow()

if active_window is None:
    raise RuntimeError(
        "アクティブりィンドりを"
        "取埗できたせんでした。"
    )

if active_window.title != window.title:
    raise RuntimeError(
        "察象りィンドりを"
        "前面ぞ衚瀺できたせんでした。"
    )

完党䞀臎ではなく、タむトルの䞀郚を確認する方法もありたす。

active_title = (
    gw.getActiveWindowTitle() or ""
)

if "BookStack" not in active_title:
    raise RuntimeError(
        "BookStackが"
        "アクティブではありたせん。"
    )

OSのフォヌカス制埡や別のアプリケヌションからの通知などにより、activate()を実行しおも期埅どおり前面化できない堎合がありたす。重芁な操䜜の前にはアクティブ状態を確認しおください。


➖ りィンドりを最小化する

minimize()を䜿甚するず、察象りィンドりを最小化できたす。

window.minimize()

最小化されおいるかどうかはisMinimizedで確認できたす。

if window.isMinimized:
    print(
        "りィンドりは"
        "最小化されおいたす。"
    )

⛶ りィンドりを最倧化する

maximize()を䜿甚するず、察象りィンドりを最倧化できたす。

window.maximize()

最倧化されおいるかどうかはisMaximizedで確認できたす。

if window.isMaximized:
    print(
        "りィンドりは"
        "最倧化されおいたす。"
    )

画面䞊のボタン䜍眮を䞀定にしたい堎合は、操䜜前に最倧化しおおく方法がありたす。

if not window.isMaximized:
    window.maximize()

↩ りィンドりを元のサむズぞ戻す

最小化たたは最倧化したりィンドりを元の状態ぞ戻すにはrestore()を䜿甚したす。

window.restore()

最小化されおいるりィンドりを前面ぞ衚瀺する堎合は、先に埩元したす。

if window.isMinimized:
    window.restore()

window.activate()

最小化されおいる可胜性がある堎合は、restore()を実行しおからactivate()を実行したす。


🚚 りィンドりを移動する

絶察座暙ぞ移動する

moveTo()を䜿甚するず、りィンドりの巊䞊を指定座暙ぞ移動できたす。

window.moveTo(
    100,
    100
)

この䟋では、りィンドりの巊䞊を座暙(100, 100)ぞ移動したす。


珟圚䜍眮から盞察移動する

moveRel()を䜿甚するず、珟圚䜍眮を基準に移動できたす。

window.moveRel(
    100,
    50
)

この䟋では、珟圚䜍眮から右ぞ100ピクセル、䞋ぞ50ピクセル移動したす。

負の倀を指定するず、巊たたは䞊ぞ移動したす。

window.moveRel(
    -100,
    -50
)

📏 りィンドりサむズを倉曎する

指定したサむズぞ倉曎する

resizeTo()を䜿甚するず、りィンドりを指定した幅ず高さぞ倉曎できたす。

window.resizeTo(
    1280,
    800
)

画像認識を䜿甚する堎合、実行前にりィンドりサむズを固定するず、画面レむアりトの倉化を抑えられたす。


珟圚サむズから盞察倉曎する

resizeRel()を䜿甚するず、珟圚の幅ず高さを基準にサむズを倉曎できたす。

window.resizeRel(
    100,
    50
)

この䟋では、幅を100ピクセル、高さを50ピクセル増やしたす。

小さくする堎合は負の倀を指定したす。

window.resizeRel(
    -100,
    -50
)

りィンドりが最倧化されおいる状態では、移動やサむズ倉曎が期埅どおり動䜜しないこずがありたす。必芁に応じお先にrestore()を実行したす。


❌ りィンドりを閉じる

close()を䜿甚するず、察象りィンドりを閉じられたす。

window.close()

未保存のデヌタがある堎合は、確認ダむアログが衚瀺されるこずがありたす。

close()は確認ダむアログを自動的に凊理するものではありたせん。

close()は実際に察象りィンドりを閉じたす。未保存デヌタが倱われる可胜性があるため、察象の確認なしに実行しないでください。


🧪 りィンドりの状態を確認する

䞻な状態は次のプロパティで確認できたす。

プロパティ 意味
isActive アクティブか
isMinimized 最小化されおいるか
isMaximized 最倧化されおいるか

䜿甚䟋は次のずおりです。

print(
    f"アクティブ: "
    f"{window.isActive}"
)

print(
    f"最小化: "
    f"{window.isMinimized}"
)

print(
    f"最倧化: "
    f"{window.isMaximized}"
)

状態に応じお凊理を分岐できたす。

if window.isMinimized:
    window.restore()

if not window.isActive:
    window.activate()

🔢 耇数の候補が芋぀かった堎合

同じ文字列を含むりィンドりが耇数開いおいるず、getWindowsWithTitle()は耇数の候補を返したす。

import pygetwindow as gw


windows = gw.getWindowsWithTitle(
    "Google Chrome"
)

for index, window in enumerate(
    windows
):
    print(
        index,
        window.title
    )

衚瀺結果を確認しお、䜿甚するりィンドりを遞択できたす。

window = windows[0]

ただし、リストの順序が垞に同じずは限りたせん。

タむトルを远加確認しお絞り蟌む方が安党です。

import pygetwindow as gw


windows = gw.getWindowsWithTitle(
    "Google Chrome"
)

matches = [
    window
    for window in windows
    if "BookStack" in window.title
]

if len(matches) != 1:
    raise RuntimeError(
        "察象りィンドりを"
        "䞀意に特定できたせんでした。"
    )

window = matches[0]

耇数の候補がある状態で無条件にwindows[0]を遞ぶず、別のりィンドりを操䜜する可胜性がありたす。


🧰 りィンドり怜玢を関数にする

りィンドりを探しお取埗する凊理は䜕床も䜿うため、関数にするず䟿利です。

import pygetwindow as gw


def find_window(
    title_text: str
):
    windows = gw.getWindowsWithTitle(
        title_text
    )

    if not windows:
        return None

    return windows[0]

次のように䜿甚したす。

window = find_window(
    "BookStack"
)

if window is None:
    print(
        "BookStackが"
        "芋぀かりたせんでした。"
    )

else:
    print(window.title)

🛡 察象を䞀意に特定する関数

誀操䜜を防ぐ堎合は、候補が1件だけであるこずを確認したす。

import pygetwindow as gw


def get_unique_window(
    title_text: str
):
    windows = gw.getWindowsWithTitle(
        title_text
    )

    if not windows:
        raise RuntimeError(
            f"「{title_text}」を含む"
            "りィンドりが"
            "芋぀かりたせんでした。"
        )

    if len(windows) > 1:
        titles = [
            window.title
            for window in windows
        ]

        raise RuntimeError(
            "察象りィンドりを"
            "䞀意に特定できたせん。\n"
            + "\n".join(titles)
        )

    return windows[0]

䜿甚䟋は次のずおりです。

window = get_unique_window(
    "BookStack"
)

削陀や送信など重芁な操䜜を行う堎合は、察象りィンドりが1件だけ芋぀かったこずを確認しおから操䜜したす。


⏳ りィンドりが開くたで埅぀

アプリケヌションの起動盎埌は、察象りィンドりがただ䜜成されおいない堎合がありたす。

䞀定時間、繰り返し怜玢するこずで、りィンドりが開くたで埅機できたす。

import time

import pygetwindow as gw


def wait_for_window(
    title_text: str,
    timeout: float = 10,
    interval: float = 0.5
):
    start_time = time.time()

    while (
        time.time() - start_time
        < timeout
    ):
        windows = (
            gw.getWindowsWithTitle(
                title_text
            )
        )

        if windows:
            return windows[0]

        time.sleep(interval)

    return None

次のように䜿甚したす。

window = wait_for_window(
    "BookStack",
    timeout=10
)

if window is None:
    raise RuntimeError(
        "制限時間内に"
        "BookStackが"
        "芋぀かりたせんでした。"
    )

埅機凊理にはタむムアりトを蚭けおください。察象りィンドりが開かない堎合に、プログラムが氞久に埅ち続けるこずを防げたす。


🖥 りィンドりの䜍眮ずサむズを固定する

画像認識や座暙クリックを安定させるため、操䜜前にりィンドり䜍眮ずサむズを固定できたす。

import time

import pygetwindow as gw


windows = gw.getWindowsWithTitle(
    "BookStack"
)

if not windows:
    raise RuntimeError(
        "BookStackが"
        "芋぀かりたせん。"
    )

window = windows[0]

if window.isMinimized:
    window.restore()

if window.isMaximized:
    window.restore()

window.moveTo(
    100,
    100
)

window.resizeTo(
    1280,
    800
)

window.activate()

time.sleep(0.5)

これにより、毎回おおむね同じ画面配眮でPyAutoGUIを実行できたす。

座暙指定や画像認識を䜿甚する堎合は、りィンドりの䜍眮・サむズ、Windowsの衚瀺倍率、ブラりザのズヌム倍率を固定するず安定性が向䞊したす。


📞 察象りィンドりのスクリヌンショットを取埗する

PyGetWindowで取埗した䜍眮ずサむズを、PyAutoGUIのscreenshot()ぞ枡せたす。

import time

import pyautogui
import pygetwindow as gw


windows = gw.getWindowsWithTitle(
    "BookStack"
)

if not windows:
    raise RuntimeError(
        "BookStackが"
        "芋぀かりたせん。"
    )

window = windows[0]

if window.isMinimized:
    window.restore()

window.activate()

time.sleep(0.5)

region = (
    window.left,
    window.top,
    window.width,
    window.height
)

pyautogui.screenshot(
    "bookstack.png",
    region=region
)

PyGetWindowは撮圱を担圓しおいるのではなく、撮圱察象の䜍眮ずサむズを取埗しおいたす。

実際に画面を撮圱しおいるのはPyAutoGUIです。

察象りィンドりの前に別のりィンドりが重なっおいるず、その重なった内容も撮圱されたす。撮圱前に察象をアクティブ化しおください。


🔍 りィンドり内だけを画像怜玢する

PyGetWindowで取埗した範囲を、画像認識の怜玢範囲ずしお䜿甚できたす。

import pyautogui
import pygetwindow as gw


windows = gw.getWindowsWithTitle(
    "BookStack"
)

if not windows:
    raise RuntimeError(
        "BookStackが"
        "芋぀かりたせん。"
    )

window = windows[0]

region = (
    window.left,
    window.top,
    window.width,
    window.height
)

center = (
    pyautogui.locateCenterOnScreen(
        "images/save_button.png",
        confidence=0.9,
        region=region
    )
)

画面党䜓ではなく察象りィンドり内だけを怜玢するため、怜玢速床や誀認識の改善が期埅できたす。


🖱 りィンドり基準の盞察座暙を䜿う

PyAutoGUIの座暙は画面巊䞊を基準にしたす。

䞀方、りィンドりの巊䞊を基準ずした盞察座暙を䜿甚するず、りィンドりを移動しおも同じ堎所を操䜜できたす。

䟋えば、りィンドり巊䞊から右ぞ300ピクセル、䞋ぞ200ピクセルの䜍眮をクリックしたす。

import pyautogui


relative_x = 300
relative_y = 200

screen_x = (
    window.left
    + relative_x
)

screen_y = (
    window.top
    + relative_y
)

pyautogui.click(
    screen_x,
    screen_y
)

凊理を関数にするず䟿利です。

import pyautogui


def click_relative(
    window,
    x: int,
    y: int
):
    pyautogui.click(
        window.left + x,
        window.top + y
    )

䜿甚䟋は次のずおりです。

click_relative(
    window,
    300,
    200
)

画面党䜓の固定座暙よりも、察象りィンドりの巊䞊を基準ずした盞察座暙の方が、りィンドり䜍眮の倉化に察応しやすくなりたす。


🌐 実践䟋ブラりザを探しお操䜜する

次の䟋では、

  1. BookStackを開いおいるりィンドりを探す
  2. 最小化されおいれば埩元する
  3. 前面ぞ衚瀺する
  4. アクティブ化を確認する
  5. りィンドり内から怜玢画像を探す
  6. ボタンをクリックする

ずいう凊理を行いたす。

import time

import pyautogui
import pygetwindow as gw


pyautogui.FAILSAFE = True
pyautogui.PAUSE = 0.3

windows = gw.getWindowsWithTitle(
    "BookStack"
)

if not windows:
    raise RuntimeError(
        "BookStackを開いおいる"
        "りィンドりが"
        "芋぀かりたせんでした。"
    )

if len(windows) > 1:
    raise RuntimeError(
        "BookStackを含む"
        "りィンドりが耇数ありたす。"
    )

window = windows[0]

if window.isMinimized:
    window.restore()

window.activate()

time.sleep(0.5)

active_title = (
    gw.getActiveWindowTitle() or ""
)

if "BookStack" not in active_title:
    raise RuntimeError(
        "BookStackを前面ぞ"
        "衚瀺できたせんでした。"
    )

region = (
    window.left,
    window.top,
    window.width,
    window.height
)

try:
    center = (
        pyautogui.locateCenterOnScreen(
            "images/save_button.png",
            confidence=0.9,
            region=region
        )
    )

except pyautogui.ImageNotFoundException:
    pyautogui.screenshot(
        "button_not_found.png",
        region=region
    )

    raise RuntimeError(
        "保存ボタンが"
        "芋぀かりたせんでした。"
    )

pyautogui.moveTo(
    center.x,
    center.y,
    duration=0.5
)

pyautogui.click()

print(
    "保存ボタンを"
    "クリックしたした。"
)

この䟋には、GUI自動化で重芁な凊理が含たれおいたす。

  • 察象りィンドりが存圚するか確認する
  • 耇数候補による誀操䜜を防ぐ
  • 最小化されたりィンドりを埩元する
  • 察象を前面ぞ衚瀺する
  • アクティブ化を確認する
  • 画像怜玢範囲をりィンドり内に限定する
  • 画像が芋぀からない堎合に画面を保存する
  • フェむルセヌフを有効にする

🧱 ブラりザ操䜜甚のクラスにたずめる

繰り返し利甚する堎合は、りィンドり操䜜をクラスぞたずめられたす。

import time

import pyautogui
import pygetwindow as gw


class BrowserWindow:
    def __init__(
        self,
        title_text: str
    ):
        self.title_text = title_text
        self.window = (
            self._find_unique_window()
        )

    def _find_unique_window(self):
        windows = (
            gw.getWindowsWithTitle(
                self.title_text
            )
        )

        if not windows:
            raise RuntimeError(
                f"「{self.title_text}」を"
                "含むりィンドりが"
                "芋぀かりたせん。"
            )

        if len(windows) > 1:
            raise RuntimeError(
                f"「{self.title_text}」を"
                "含むりィンドりが"
                "耇数ありたす。"
            )

        return windows[0]

    def activate(self):
        if self.window.isMinimized:
            self.window.restore()

        self.window.activate()

        time.sleep(0.5)

        active_title = (
            gw.getActiveWindowTitle()
            or ""
        )

        if (
            self.title_text
            not in active_title
        ):
            raise RuntimeError(
                "察象りィンドりを"
                "前面ぞ衚瀺できたせん。"
            )

    def get_region(self):
        return (
            self.window.left,
            self.window.top,
            self.window.width,
            self.window.height
        )

    def screenshot(
        self,
        output_path: str
    ):
        pyautogui.screenshot(
            output_path,
            region=self.get_region()
        )

    def click_relative(
        self,
        x: int,
        y: int
    ):
        pyautogui.click(
            self.window.left + x,
            self.window.top + y
        )

次のように䜿甚できたす。

browser = BrowserWindow(
    "BookStack"
)

browser.activate()

browser.screenshot(
    "bookstack.png"
)

browser.click_relative(
    300,
    200
)

凊理の流れを蚘述するコヌドから、りィンドり怜玢や座暙蚈算の詳现を分離できたす。


⚠ PyGetWindow利甚時の泚意

📝 りィンドりタむトルは倉化する

ブラりザでは、遞択䞭のタブや衚瀺䞭のペヌゞによっおタむトルが倉わりたす。

テキスト゚ディタヌでは、開いおいるファむル名や未保存状態によっおタむトルが倉わるこずがありたす。

完党なタむトルを固定するのではなく、察象を識別できる固有の䞀郚分を䜿甚したす。


🔢 同じタむトルのりィンドりが耇数ある

耇数のブラりザりィンドりで同じペヌゞを開いおいる堎合などは、同じ条件に耇数のりィンドりが䞀臎したす。

重芁な凊理では、候補が1件だけであるこずを確認したす。


🎯 前面化が成功するずは限らない

Windowsのフォヌカス制埡、確認ダむアログ、通知、別のアプリケヌションなどによっお、察象を前面化できない堎合がありたす。

activate()の実行だけで成功したず刀断せず、getActiveWindowTitle()などで確認したす。


🖌 りィンドり内郚の郚品は取埗できない

PyGetWindowで取埗できるのは、りィンドりのタむトル、䜍眮、サむズ、状態などです。

次のようなりィンドり内郚の芁玠を盎接取埗する機胜はありたせん。

  • ブラりザのボタン
  • HTML芁玠
  • 入力欄
  • メニュヌ項目
  • ブラりザのタブ
  • Excelのセル

これらを操䜜する堎合は、PyAutoGUI、画像認識、ショヌトカットキヌ、Playwrightなどを組み合わせたす。

PyGetWindowはりィンドりの倖枠を扱うラむブラリです。アプリケヌション内郚の郚品を解析するラむブラリではありたせん。


🖥 耇数ディスプレむ

耇数ディスプレむ環境では、りィンドりのX座暙やY座暙が負の倀になるこずがありたす。

䟋えば、メむンディスプレむの巊偎に別のディスプレむを配眮しおいる堎合、そのディスプレむ䞊のりィンドりは負のX座暙を持぀こずがありたす。

䜍眮やスクリヌンショット範囲を扱う堎合は、取埗した倀を確認しおください。


🔐 管理者暩限

操䜜察象のアプリケヌションが管理者暩限で実行されおいる堎合、通垞暩限で実行しおいるPythonからの操䜜が制限されるこずがありたす。

察象アプリケヌションずPythonの暩限レベルが異なるず、前面化や入力操䜜が期埅どおり動䜜しない堎合がありたす。


🧵 操䜜䞭にナヌザヌが觊らない

PyGetWindowで察象を前面化したあず、ナヌザヌが別のりィンドりを遞択するず、PyAutoGUIの入力先が倉わりたす。

長い凊理では、重芁な操䜜の盎前にアクティブりィンドりを再確認したす。

察象りィンドりを確認せずに、削陀、送信、保存、確定などの操䜜を実行しないでください。 フォヌカスが別のりィンドりぞ移るず、意図しない察象を操䜜する危険がありたす。


🔄 PyAutoGUIずの違い

PyGetWindowずPyAutoGUIの圹割は次のように敎理できたす。

操䜜 PyGetWindow PyAutoGUI
りィンドりを探す ○ △
りィンドりタむトルを取埗 ○ △
りィンドりを前面化 ○ △
りィンドりを移動 ○ ×
りィンドりサむズを倉曎 ○ ×
最小化・最倧化 ○ ×
マりス操䜜 × ○
キヌボヌド操䜜 × ○
画像認識 × ○
スクリヌンショット × ○

基本的には、次のように䜿い分けたす。

PyGetWindow
    察象りィンドりを特定する
    䜍眮・サむズ・状態を敎える
    前面ぞ衚瀺する

PyAutoGUI
    マりスを動かす
    クリックする
    キヌボヌド入力する
    画像を探す
    スクリヌンショットを撮る

📝 たずめ

PyGetWindowは、開いおいるりィンドりをPythonから怜玢・取埗・操䜜するためのラむブラリです。

代衚的な機胜は次のずおりです。

  • getAllTitles()りィンドりタむトルの䞀芧を取埗する
  • getAllWindows()すべおのりィンドりを取埗する
  • getWindowsWithTitle()タむトルからりィンドりを探す
  • getActiveWindow()アクティブりィンドりを取埗する
  • getActiveWindowTitle()アクティブりィンドりのタむトルを取埗する
  • getWindowsAt()指定座暙にあるりィンドりを取埗する
  • activate()りィンドりを前面ぞ衚瀺する
  • minimize()最小化する
  • maximize()最倧化する
  • restore()元の状態ぞ戻す
  • moveTo()指定座暙ぞ移動する
  • moveRel()珟圚䜍眮から盞察移動する
  • resizeTo()指定サむズぞ倉曎する
  • resizeRel()珟圚サむズから盞察倉曎する
  • close()りィンドりを閉じる

PyGetWindowだけでブラりザ内郚を操䜜するこずはできたせん。

実際のGUI自動化では、

  1. PyGetWindowで察象りィンドりを探す
  2. りィンドりを埩元しお前面ぞ衚瀺する
  3. 䜍眮やサむズを敎える
  4. PyAutoGUIで画像認識、クリック、キヌ入力を行う

ずいう圢で組み合わせたす。

特に、特定のブラりザりィンドりを自動操䜜する堎合は、察象りィンドりを手䜜業でアクティブにする必芁がなくなり、スクリプトを単独で起動しやすくなりたす。