首发于 技术笔记

使用Python脚本更改显示器亮度

前言

不管在电脑屏幕前办公还是娱乐,环境光线在变化,看的东西也在变化。显示器的亮度也应该及时调整,白天亮一些,晚上暗一些,打游戏亮一些,看文档暗一些。

如果是笔记本,可以在Win10的任务栏最右边的通知中心的亮度条上调整,还可以通过键盘快捷键调整。如果是台式机可能就没这么方便了,大多数人应该是对着显示器上的按钮按来按去,十分不方便。实际上,台式机也可以通过键盘鼠标调整显示器亮度。这里分享一个用Python写的更改主显示亮度的脚本。

文档

本人使用的是一款Dell显示器,使用DP接口连接Nvidia显卡。找来找去发现Dell官网有个Dell Display Manager,可以直接在任务栏上弹出滑块调亮度和对比度(网易云右边)。这说明,实际上显示器和操作系统之间是有某个通道的,使得除了视频数据,还可以传输更改亮度这类控制数据。

应该是叫DDC/CI,顺着找到了微软的文档 docs.microsoft.com/en-u

直接搜"win32 monitor configuration"应该也可以找到(微软的链接没事就变)。还有一个相关的VESA标准,VESA Monitor Control Command Set (MCCS) standard。

Internally, the monitor configuration functions use the Display Data Channel Command Interface (DDC/CI) to send commands to the monitor.

Python脚本

虽然有点不科学,不过先写了一个C的程序,实现了亮度的更改。然后,对着ctypes文档翻译成Python即可。如果使用了奇怪的转接头,或者VGA这种接口,大概率是不能用的。

"""
Get and set the brightness of the monitor.
import ctypes
from ctypes import wintypes
PHYSICAL_MONITOR_DESCRIPTION_SIZE = 128
class PHYSICAL_MONITOR(ctypes.Structure):
    _fields_ = [('hPhysicalMonitor', wintypes.HANDLE),
                ('szPhysicalMonitorDescription', ctypes.c_wchar * PHYSICAL_MONITOR_DESCRIPTION_SIZE)]
if __name__ == '__main__':
    user32 = ctypes.windll.user32
    h_wnd = user32.GetDesktopWindow()
    MONITOR_DEFAULTTOPRIMARY = 1
    h_monitor = user32.MonitorFromWindow(h_wnd, MONITOR_DEFAULTTOPRIMARY)
    print('Monitor Handle', h_monitor)
    dxva2 = ctypes.windll.Dxva2
    nummons = wintypes.DWORD()
    bres = dxva2.GetNumberOfPhysicalMonitorsFromHMONITOR(
        h_monitor, ctypes.byref(nummons))
    assert bres
    print('Number of Monitors', nummons)
    physical_monitors = (PHYSICAL_MONITOR * nummons.value)()
    bres = dxva2.GetPhysicalMonitorsFromHMONITOR(
        h_monitor, nummons, physical_monitors)
    assert bres
    print('Phyical Monitors', physical_monitors)
    physical_monitor = physical_monitors[0]
    print('    first', physical_monitor.hPhysicalMonitor,
          physical_monitor.szPhysicalMonitorDescription)
    min_brightness = wintypes.DWORD()
    max_brightness = wintypes.DWORD()
    cur_brightness = wintypes.DWORD()
    bres = dxva2.GetMonitorBrightness(physical_monitor.hPhysicalMonitor, ctypes.byref(
        min_brightness), ctypes.byref(cur_brightness), ctypes.byref(max_brightness))
    assert bres
    print('Brightness', min_brightness, 'min',
          cur_brightness, 'max', max_brightness)
    bres = dxva2.SetMonitorBrightness(physical_monitor.hPhysicalMonitor, 10)
    assert bres
    kernel32 = ctypes.windll.kernel32
    err = kernel32.GetLastError()
    print(err)

什么额外库都不需要安装,使用Python3执行一下,亮度变化了么?更改下面的10,

    bres = dxva2.SetMonitorBrightness(physical_monitor.hPhysicalMonitor, 10)

例如改成80,再执行一下,亮度应该变成80%。下面是我的台式机上执行时的输出,留作对照参考(执行前亮度为5,执行后更亮了,变成了10)

"C:/Program Files/Python38/python.exe" e:/Worky/Python/monitor.py
Monitor Handle 65537
Number of Monitors c_ulong(1)
Phyical Monitors <__main__.PHYSICAL_MONITOR_Array_1 object at 0x0000021E0B6D9740>
    first 1 U2518D (DP)
Brightness c_ulong(0) min c_ulong(5) max c_ulong(100)
0

这里就不逐行解释代码了,有疑问欢迎留言。有几个东西需要看下才能完全理解。

  • Win32编程,每个函数直接在Bing上搜就可以跳到微软的文档上。
  • Python ctypes,可能需要跑一遍文档里的例子。
  • Windows和ctypes结合。例如怎么知道某个函数在哪个dll上呢?在微软函数文档的最下面的Requirements里。再如找Enum的值,Structure的组成,在Visual Studio的头文件里。

色温

试了一下改色温的接口,也可以用。

    # typedef enum _MC_COLOR_TEMPERATURE
    #     MC_COLOR_TEMPERATURE_UNKNOWN,
    #     MC_COLOR_TEMPERATURE_4000K, 
    #     MC_COLOR_TEMPERATURE_5000K, 
    #     MC_COLOR_TEMPERATURE_6500K, 
    #     MC_COLOR_TEMPERATURE_7500K, 
    #     MC_COLOR_TEMPERATURE_8200K, 
    #     MC_COLOR_TEMPERATURE_9300K, 
    #     MC_COLOR_TEMPERATURE_10000K,
    #     MC_COLOR_TEMPERATURE_11500K
    # } MC_COLOR_TEMPERATURE, *LPMC_COLOR_TEMPERATURE;
    color_temperature = ctypes.c_int32()
    bres = dxva2.GetMonitorColorTemperature(physical_monitor.hPhysicalMonitor,
        ctypes.byref(color_temperature))
    assert bres
    print('ColorTemperature', color_temperature)