在宅勤務ー5 Windows10 Pythonでディスプレイの解像度を変更する

 見に来てくださって、ありがとうございます。石川さんです。

 ここのところ、暑さが少し和らいで、少しだけですが秋の気配を感じ始めましたね。
さて、今日は仕事に便利な裏技を紹介したいと思います。「ディスプレイの解像度」と「テキスト、アプリ、その他の項目のサイズを変更する」の値をワンアクションで変更できるようにする方法です。

ディスプレイの設定

 ちなみに、この設定は、画面上の何もないところを右クリックして表示されるコンテキストメニューの、「ディスプレイの設定」を選択することで表示できます。

ディスプレイ設定

やり方

 ぼくは、ここのところPythonにハマっていますので、Pythonを使ってできないかなぁ、ということで調べてみました。以下のPythonスクリプトで変更可能です。ぼくはデスクトップにこのスクリプト置いて、ダブルクリックして実行しています。あ、拡張子は「.py」ファイルにして、この拡張子のファイルをPython.exeで実行するように定義しました。

''' ディスプレイの解像度と倍率を変更します '''
import win32con
import win32api
import subprocess
import time
import pyautogui

proc = subprocess.Popen('DpiScaling') # ディスプレイの設定を開いておきます。
time.sleep(3)
proc.wait()

DEVICE = win32api.EnumDisplayDevices(None, 0)
DEVMODE = win32api.EnumDisplaySettings(DEVICE.DeviceName, win32con.ENUM_CURRENT_SETTINGS)

if DEVMODE.PelsWidth == 2160:
    DEVMODE.PelsWidth = 1920
    DEVMODE.PelsHeight = 1080
else:
    DEVMODE.PelsWidth = 2160
    DEVMODE.PelsHeight = 1440

DEVMODE.Fields = win32con.DM_PELSWIDTH | win32con.DM_PELSHEIGHT
win32api.ChangeDisplaySettings(DEVMODE, 0)


window = pyautogui.getWindowsWithTitle('設定')[0]
if window:
    window.activate()
    if DEVMODE.PelsWidth == 1920:
        pyautogui.typewrite(['tab','tab','up','up'])
    else:
        pyautogui.typewrite(['tab','tab','down','down'])
    pyautogui.hotkey('alt', 'f4')

ソースコードの説明

 今回の設定変更ですが、ノートパソコンからリモートデスクトップ接続で他のパソコンにログインしたときに、解像度が一致していないと挙動がおかしくなることがあって、その対応としてはリモート接続先のパソコンの解像度にあわせてからログインするとうまくいく、ということだったのだけど、何度も設定を切り替えるのが面倒になってきたので、スクリプトを用意して、ダブルクリック一回だけで切り替わらないかなぁ、というのが事の発端です。ちなみにぼくはWindows10のノートパソコン(Let’s Note)を使っています。

テキスト、アプリ、その他の項目サイズを変更するディスプレイの解像度
通常150%(推奨)2160 × 1440(推奨)
リモート時100%1920 × 1080
ディスプレイ設定の切り替え

 このほかにもプロジェクターで発表するときや、Zoomなどのウェブ会議なんかで利用できるかも、と、思っています。

 まずは、「ディスプレイの解像度」の方ですが、こちらはGoogle先生に聞いて、すぐに解決できました。win32apiChangeDisplaySettingsを利用します。

 12行目の「DEVICE = win32api.EnumDisplayDevices(None, 0)」で現在のディスプレイ設定の名称などを取得しています。この0のところを1、2と増やしていって他のデバイスも取得できるらしいので、ちょっと試してみたところ、ぼくの環境では「\\.\DISPLAY1」「\\.\DISPLAY2」「\\.\DISPLAY3」と三つのDEVICE.DeviceNameが取得できました。モニターを複数つないだ時に増えるのかと思っていたのですが、違いました。DeviceIDは同じだったので、同じモニターの異なる設定を持っているのかも知れませんが、とりあえず深追いはやめました。どなたか知っているひとがいたら、教えてください♪

 13行目の「DEVMODE = win32api.EnumDisplaySettings(DEVICE.DeviceName, win32con.ENUM_CURRENT_SETTINGS)」で現在のディスプレイ設定の詳細な設定値を取得しています。ちなみに「\\.\DISPLAY2」「\\.\DISPLAY3」を指定しても以下のようなエラーが発生するだけで設定値は取得できませんでした。

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
pywintypes.error: (123, 'EnumDisplaySettings', 'ファイル名、ディレクトリ名、またはボリューム ラベルの構文が間違っています。')
>

 次に15行目のPelsWidthのチェックですが、現在値が2160のとき切り替えして、そうじゃないときに元の値に戻すようにするために記載してあります。ここは皆さんの事情と環境に応じて書き換える必要がある個所です。22行目の「DEVMODE.Fields = win32con.DM_PELSWIDTH | win32con.DM_PELSHEIGHT」を指定することでその次の23行目の「win32api.ChangeDisplaySettings(DEVMODE, 0)」呼び出しで、指定してPelsWidthPelsHightの値を反映してくれるようになります。

 26行目からは、「テキスト、アプリ、その他の項目のサイズを変更する」の変更です。最初のうちは、上記のChangeDisplaySettingsでこちらも設定できるのでしょうね、と思い込んでしまって、ずいぶんと遠回りしてしまいました。Scaleという項目があるので、これでできそうだと思ったのですよねぇ。いろいろ調べて回った結果、最終的に、pyautoguiで実現することにしました。pyautoguiはGUIで操作することをプログラムから再現するためのモジュールですので、実行中はマウスを触ったりキー入力したりしないようにお願いしますね。途中でクリックしたりすると、正しく動作しなくなりますので。まずは「設定」というタイトルのウィンドウを取得していますが、これは、8行目の「subprocess.Popen('DpiScaling')」で事前に実行しておいた、ディプレイ設定の画面を取得しています。

 設定ウィンドウが取得できた場合に、そのウィンドウをアクティブにして、タブキーを二回押して項目移動して、↑または↓の矢印キー2回で設定を変更しています。終わったら「ALT+F4」で設定画面を終了するようにキー入力しています。もしかしたらこのキー入力は環境によって異なるかも知れませんので、それぞれ変更して利用するようにお願いします。ちなみに、このpyautoguiを使って「ディスプレイの解像度」まで変更できないのか、というツッコミもありそうですけど、できますね。ただ、現在値から設定する値を判断するためにwin32api.EnumDisplaySettingsを使う必要があったので、どうせ使うなら設定変更まで、ということでこのようになっています。

 ちなみに、最初はこちらの「テキスト、アプリ、その他の項目のサイズを変更する」の設定を、ウィンドウズのレジストリを変更して対応する、winregというモジュールを使うやり方を調べて実現できたので、そちらで説明を書こうとしていました。しかし、説明を書きながら確認していくと、思った動きと反対の動きになっていたり、レジストリの値が思ったように変更されなかったり、ということがあったので、pyautoguiを使うように変更しました。ホントは完全にプログラムから実行したかったのですけどねぇ。さすがにレジストリの変更を無責任におすすめできませんよね。でもせっかく調べたので、参考までに、winregを使ったやり方を掲載しておきます。後半部分が異なります。SCALEは、0が推奨値で、正の値は拡大した値、負の値は縮小した値です。今回、-2が推奨値から二つ縮小した値の100%を表しているのですが、実際には4294967294を設定しています。これは、16進数の0xFFFFFFFEを符号なし整数値で表現した値になります。

''' ディスプレイの解像度と倍率を変更します '''
import winreg
import win32con
import win32api

DEVICE = win32api.EnumDisplayDevices(None, 0)
DEVMODE = win32api.EnumDisplaySettings(DEVICE.DeviceName, win32con.ENUM_CURRENT_SETTINGS)

if DEVMODE.PelsWidth == 2160:
    DEVMODE.PelsWidth = 1920
    DEVMODE.PelsHeight = 1080
else:
    DEVMODE.PelsWidth = 2160
    DEVMODE.PelsHeight = 1440

DEVMODE.Fields = win32con.DM_PELSWIDTH | win32con.DM_PELSHEIGHT
win32api.ChangeDisplaySettings(DEVMODE, 0)


REG_PATH = r"Control Panel\Desktop\PerMonitorSettings"
REG_PATH += r"\※ここはそれぞれの値をレジストリで調べて変更してください※"
KEY = winreg.OpenKey(winreg.HKEY_CURRENT_USER, REG_PATH, 0, winreg.KEY_WRITE)
if DEVMODE.PelsWidth == 1920:
    SCALE = 0
else:
    SCALE = 4294967294  # -2
winreg.SetValueEx(KEY, "DpiValue", 0, winreg.REG_DWORD, SCALE)
winreg.CloseKey(KEY)

まとめ

 最終的にはpyautoguiがあれば、何でも自動化できそうですね。

追記 2021年1月7日

 上記のスクリプト、ときどきうまくいかないことがあって、しばらく理由がわからなかったのですけど、やっとわかりました!

解像度変更時の通知

 そう!解像度を変更したときにこの通知が画面右下に登場して、どうやらフォーカスを奪っていたのが原因だったようです。スクリプトでは、解像度を変更(→通知が表示)→倍率を変更、というふうに実行していたので、通知が表示するタイミングによって、倍率が変更できないことがあった、ということでした。それに気づいたので、先に倍率を変更してから解像度を変更することにしました。スクリプトで言うと、解像度を変更する部分(最初のスクリプトの23行目)「win32api.ChangeDisplaySettings(DEVMODE, 0)」をスクリプトの一番最後に移動するだけです。

 これでスッキリしました!!!

“在宅勤務ー5 Windows10 Pythonでディスプレイの解像度を変更する” への1件の返信

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です


The reCAPTCHA verification period has expired. Please reload the page.