14

我想让我的监视器从 Windows 控制(简单的东西,比如更改输入源),但找不到从 Python 发送 DDC/CI 命令的方法......

关于可以在这里提供帮助的库或方法的任何线索?

4

2 回答 2

13

这很容易使用windows 监视器 API实现。我认为那里没有任何 Python 绑定,并且 pywin32 不包含这些函数。但是,使用ctypes它们来调用它们并不难。

这是一个将显示器切换到软关闭然后重新打开的示例;它应该很容易适应更改输入源等。唯一复杂的部分毕竟是获取物理监视器的句柄:

from ctypes import windll, byref, Structure, WinError, POINTER, WINFUNCTYPE
from ctypes.wintypes import BOOL, HMONITOR, HDC, RECT, LPARAM, DWORD, BYTE, WCHAR, HANDLE


_MONITORENUMPROC = WINFUNCTYPE(BOOL, HMONITOR, HDC, POINTER(RECT), LPARAM)


class _PHYSICAL_MONITOR(Structure):
    _fields_ = [('handle', HANDLE),
                ('description', WCHAR * 128)]


def _iter_physical_monitors(close_handles=True):
    """Iterates physical monitors.

    The handles are closed automatically whenever the iterator is advanced.
    This means that the iterator should always be fully exhausted!

    If you want to keep handles e.g. because you need to store all of them and
    use them later, set `close_handles` to False and close them manually."""

    def callback(hmonitor, hdc, lprect, lparam):
        monitors.append(HMONITOR(hmonitor))
        return True

    monitors = []
    if not windll.user32.EnumDisplayMonitors(None, None, _MONITORENUMPROC(callback), None):
        raise WinError('EnumDisplayMonitors failed')

    for monitor in monitors:
        # Get physical monitor count
        count = DWORD()
        if not windll.dxva2.GetNumberOfPhysicalMonitorsFromHMONITOR(monitor, byref(count)):
            raise WinError()
        # Get physical monitor handles
        physical_array = (_PHYSICAL_MONITOR * count.value)()
        if not windll.dxva2.GetPhysicalMonitorsFromHMONITOR(monitor, count.value, physical_array):
            raise WinError()
        for physical in physical_array:
            yield physical.handle
            if close_handles:
                if not windll.dxva2.DestroyPhysicalMonitor(physical.handle):
                    raise WinError()


def set_vcp_feature(monitor, code, value):
    """Sends a DDC command to the specified monitor.

    See this link for a list of commands:
    ftp://ftp.cis.nctu.edu.tw/pub/csie/Software/X11/private/VeSaSpEcS/VESA_Document_Center_Monitor_Interface/mccsV3.pdf
    """
    if not windll.dxva2.SetVCPFeature(HANDLE(monitor), BYTE(code), DWORD(value)):
        raise WinError()


# Switch to SOFT-OFF, wait for the user to press return and then back to ON
for handle in _iter_physical_monitors():
    set_vcp_feature(handle, 0xd6, 0x04)
    raw_input()
    set_vcp_feature(handle, 0xd6, 0x01)
于 2013-08-05T18:55:21.297 回答
3

这是你的幸运日,你不必再处理那些 ctypes 了。有人实际上制作了一个 python 包来为您执行此操作并公开一个简单的 api。它被称为monitorcontrol

这是在所有显示器上设置亮度的快速示例:

from monitorcontrol import get_monitors

for monitor in get_monitors():
    with monitor:
        monitor.set_luminance(50)
于 2021-10-07T19:50:04.197 回答