ChatGPTで「ウィンドウをサブモニタに移動するツール」を作ってみた!

はじめに

マルチモニター環境でPCゲームをプレイする際、サブモニターなど特定のモニターに表示したいが、ゲームのオプション設定にモニター選択機能がなかったり、ウィンドウ化しても移動できなかったりすることがあります。
それでも やっぱり
「マルチモニター環境でウィンドウを自由に動かしたい!」

そんな思いから、ChatGPTを活用して、Windows上のウィンドウを操作できるツールの制作を試みました。

この記事では、古いPCゲームやインディーゲームをマルチモニター環境で快適にプレイしたい人や、効率的な作業環境を求める方に向けて、ChatGPTによるツール開発の流れと環境構築方法を紹介していきます。

完成したツールの概要

このツールでは以下のようなことができます:

  • 任意のウィンドウのタイトルを入力して移動対象を指定
  • 接続されているモニターの中から移動先を選択
  • 選択したモニターの中央にウィンドウを移動
  • ダイアログで直感的に操作可能

マルチモニター環境で古いゲームのウィンドウが勝手にメインモニターに固定される、といった問題に悩まされている方には特に便利です。

制作したツールの実行イメージ

ウィンドウ名入力画面

移動先モニター選択画面

Python環境の構築方法(Windows向け)

ChatGPTで何も指定せずに「スクリプトを作って」と言うと、自分の環境ではPythonのスクリプトを作成します。
そこで、ここでは Windows上でPythonの実行環境を構築する手順を簡単に説明します。詳しい手順などは解説しているWebサイトが色々あるので、そちらを参考にして下さい。
※開発環境でVisual Studio等を使用している場合、インストーラで追加する方法もあります。

1. Pythonのインストール

  • 公式サイト から最新版(3.11以上推奨)をダウンロード
  • インストーラ実行時に「Add Python to PATH」にチェックを入れてインストール

2. パッケージ管理(pip)

Pythonには便利なライブラリが多数あります。 次のようにインストール可能です:

pip install pygetwindow pywin32 screeninfo

3. 開発環境のおすすめ

コードの編集だけであれば、メモ帳でも問題ありませんが、
Pythonに対応したエディタを使用することで、コードの不備などを発見しやすくなります。

  • Visual Studio Code(無料・軽量)
  • PyCharm(高機能)
  • Jupyter Notebook(インタラクティブ開発)

4.使用したPythonライブラリ

  • pygetwindow:ウィンドウタイトルから対象を取得
  • win32gui, pywin32:Windows API を使ってウィンドウ操作
  • screeninfo:複数モニターの解像度や位置を取得
  • tkinter:GUIダイアログ表示
  • ctypes:OSレベルのAPIへアクセス(モニター名取得に使用)

ChatGPTがコードを生成したタイミングで必要なライブラリを教えてくれるので、その都度追加していきました。
ただ、教えてくれない(忘れている?)こともあり、スクリプトが実行できなかったり、エラーとなったりすることで、ライブラリが未導入であることが発覚することもあります。

ほとんどのライブラリは、コマンドプロンプトで「pip install <ライブラリ名>」と入力することで導入できますが、バージョンによっては上手く導入できない場合は、ChatGPTに質問しても良いですが、情報が古いことがあるので、Webでの検索をお勧めします。

ChatGPTを活用した具体的な指示例

今回のツール制作ではChatGPTを以下のように活用しました:

指示例1: 「Windowsでアクティブなウィンドウを別モニターに移動させるPythonスクリプトを書いてください」

何も指示しないと、コマンドプロンプトでPythonを実行するコードを生成するので、「GUIにしてください」と指示をするウィンドウ画面が表示されるようにしてくれます。

指示例2: 「複数のモニターがある場合、モニターを選択するダイアログを追加してください」

本ツールではモニター名を取得するようにしていますが、取得できていないため、モニターを識別できる情報をできるだけ入れるように指示しています。

指示例3: 「指定したウィンドウを移動先モニターの中央に配置するようにしてください」

何も指定しないと、適当な位置に表示されてしまうので、本ツールでは中央を指定しています。用途によっては、オフセットで位置をシフトして表示させるような機能を追加しても良いと思います。

このように、やりたいことを日本語で丁寧に説明すれば、ChatGPTは実用的なコードを提案してくれます。 さらに「このコードの意味を解説して」といった逆引き的な活用も可能です。

スクリプトが上手く動かない場合

スクリプトが上手く動かない場合、コマンドプロンプトにエラーが表示されているのであれば、エラーを丸ごとコピーして、ChatGPTに「以下のメッセージが表示されました」と書いて、貼り付けるとエラーの原因を解析して対応方法も教えてくれます。
何も表示されずに起動できない場合は、スクリプトで利用しているライブラリが未導入の可能性があるので、ライブラリが導入済みかを確認する方法などをChatGPTに尋ねてみると良いかもしれません。

ファイルの拡張子を「.pyw」にして「pyw.exe」と関連付けすると、コマンドプロンプトなしでダブルクリックで実行できるようになりますが、エラーなどは表示されなくなります。スクリプトが起動して正常に動いているように見える場合でもエラーやワーニングなどのメッセージを出している場合もあるので、希望する機能追加がすべて完了してエラー等が発生しないことが確認できるまでは、コマンドプロンプトから「python <スクリプト名>」で実行することをお勧めします。

まとめ

今回作成したツールは、古いゲームや作業用アプリケーションを自在に移動できる便利ツールとして活用できます。 AIをうまく活用することで、プログラミング初心者でも実用的なツールが作れると実感しました。

コード全文と使い方

以下に今回のツールの完全なコードを掲載します。Pythonファイルとして保存し、python ファイル名.py で実行可能です。

import pygetwindow as gw
import win32gui
import win32con
import screeninfo
import tkinter as tk
from tkinter import simpledialog
import ctypes

def get_window_by_title(title):
    """ 指定されたタイトルを持つウィンドウを取得 """
    windows = gw.getWindowsWithTitle(title)
    return windows[0] if windows else None

def move_window(hwnd, x, y, width=None, height=None):
    """ 指定したウィンドウを(x, y)の座標に移動 """
    rect = win32gui.GetWindowRect(hwnd)
    if width is None:
        width = rect[2] - rect[0]
    if height is None:
        height = rect[3] - rect[1]
    win32gui.MoveWindow(hwnd, x, y, width, height, True)

def get_monitor_name(monitor_index):
    """ 指定したモニターの名前を取得 """
    monitor_info = ctypes.create_string_buffer(32)
    hdc = win32gui.GetDC(0)
    success = ctypes.windll.user32.GetMonitorInfoA(monitor_index, monitor_info)
    win32gui.ReleaseDC(0, hdc)
    return monitor_info.value.decode('utf-8') if success else f"モニター {monitor_index + 1}"

def move_window_to_monitor(title, monitor_index):
    """ ウィンドウを指定したモニターの中央に移動 """
    window = get_window_by_title(title)
    if not window:
        print("ウィンドウが見つかりませんでした。")
        return
    
    hwnd = window._hWnd
    monitors = screeninfo.get_monitors()
    if monitor_index >= len(monitors):
        print("指定されたモニターが見つかりません。")
        return
    
    target_monitor = monitors[monitor_index]
    rect = win32gui.GetWindowRect(hwnd)
    window_width = rect[2] - rect[0]
    window_height = rect[3] - rect[1]
    
    center_x = target_monitor.x + (target_monitor.width - window_width) // 2
    center_y = target_monitor.y + (target_monitor.height - window_height) // 2
    
    move_window(hwnd, center_x, center_y)
    print(f"ウィンドウ '{title}' をモニター {monitor_index + 1} の中央に移動しました。")

def get_user_input(prompt):
    """ ユーザーに入力を求めるダイアログを表示 """
    root = tk.Tk()
    root.withdraw()
    return simpledialog.askstring("ウィンドウ移動", prompt)

if __name__ == "__main__":
    window_title = get_user_input("移動するウィンドウのタイトルを入力してください:")
    if window_title:
        monitors = screeninfo.get_monitors()
        monitor_options = "\n".join([
            f"{i + 1}: {get_monitor_name(i)} ({m.x}, {m.y}, {m.width}x{m.height})" for i, m in enumerate(monitors)
        ])
        monitor_choice = get_user_input(f"移動先のモニターを選択してください:\n{monitor_options}")
        
        if monitor_choice and monitor_choice.isdigit():
            monitor_index = int(monitor_choice) - 1
            move_window_to_monitor(window_title, monitor_index)

タイトルとURLをコピーしました