screen_brightness_control.windows
1import ctypes 2import platform 3import threading 4import time 5from ctypes import POINTER, WINFUNCTYPE, Structure, WinError, byref, windll 6from ctypes.wintypes import (BOOL, BYTE, DWORD, HANDLE, HDC, HMONITOR, LPARAM, 7 RECT, WCHAR) 8from typing import List, Optional, Union 9 10import pythoncom 11import win32api 12import win32con 13import wmi 14 15from . import filter_monitors, get_methods 16from ._debug import log 17from .helpers import EDID, __cache__, _monitor_brand_lookup 18 19# a bunch of typing classes were deprecated in Python 3.9 20# in favour of collections.abc (https://www.python.org/dev/peps/pep-0585/) 21if int(platform.python_version_tuple()[1]) < 9: 22 from typing import Generator 23else: 24 from collections.abc import Generator 25 26 27def _wmi_init(): 28 '''internal function to create and return a wmi instance''' 29 # WMI calls don't work in new threads so we have to run this check 30 if threading.current_thread() != threading.main_thread(): 31 pythoncom.CoInitialize() 32 return wmi.WMI(namespace='wmi') 33 34 35def enum_display_devices() -> Generator[win32api.PyDISPLAY_DEVICEType, None, None]: 36 ''' 37 Yields all display devices connected to the computer 38 39 Yields: 40 win32api.PyDISPLAY_DEVICEType 41 ''' 42 for monitor_enum in win32api.EnumDisplayMonitors(): 43 pyhandle = monitor_enum[0] 44 monitor_info = win32api.GetMonitorInfo(pyhandle) 45 for adaptor_index in range(5): 46 try: 47 device = win32api.EnumDisplayDevices(monitor_info['Device'], adaptor_index, 1) 48 except Exception: 49 log.debug(f'failed to get display device {monitor_info["Device"]} on adaptor index {adaptor_index}') 50 pass 51 else: 52 yield device 53 break 54 55 56def get_display_info() -> List[dict]: 57 ''' 58 Gets information about all connected displays using WMI and win32api 59 60 Returns: 61 list: list of dictionaries 62 63 Example: 64 ```python 65 import screen_brightness_control as s 66 67 info = s.windows.get_display_info() 68 for display in info: 69 print(display['name']) 70 ``` 71 ''' 72 info = __cache__.get('windows_monitors_info_raw') 73 if info is None: 74 info = [] 75 try: 76 # collect all monitor UIDs (derived from DeviceID) 77 monitor_uids = {} 78 for device in enum_display_devices(): 79 monitor_uids[device.DeviceID.split('#')[2]] = device 80 81 # gather list of laptop displays to check against later 82 wmi = _wmi_init() 83 try: 84 laptop_displays = [ 85 i.InstanceName 86 for i in wmi.WmiMonitorBrightness() 87 ] 88 except Exception as e: 89 log.debug(f'get_display_info: failed to gather list of laptop displays - {e}') 90 laptop_displays = [] 91 92 extras, desktop, laptop = [], 0, 0 93 uid_keys = list(monitor_uids.keys()) 94 for monitor in wmi.WmiMonitorDescriptorMethods(): 95 try: 96 model, serial, manufacturer, man_id, edid = None, None, None, None, None 97 instance_name = monitor.InstanceName.replace('_0', '', 1).split('\\')[2] 98 pydevice = monitor_uids[instance_name] 99 100 # get the EDID 101 try: 102 edid = ''.join(f'{char:02x}' for char in monitor.WmiGetMonitorRawEEdidV1Block(0)[0]) 103 except Exception as e: 104 log.debug(f'failed to get EDID string for {monitor.InstanceName} - {type(e).__name__}: {e}') 105 edid = None 106 107 # get serial, model, manufacturer and manufacturer ID 108 try: 109 if edid is None: 110 # we already log the EDID exception earlier so don't log it again here 111 raise Exception('invalid EDID. Cannot parse') 112 # we do the EDID parsing ourselves because calling wmi.WmiMonitorID 113 # takes too long 114 parsed = EDID.parse(edid) 115 man_id, manufacturer, model, name, serial = parsed 116 if name is None: 117 raise Exception('parsed EDID returned invalid monitor name') 118 except Exception as e: 119 log.debug(f'exception parsing edid str for {monitor.InstanceName} - {type(e).__name__}: {e}') 120 devid = pydevice.DeviceID.split('#') 121 serial = devid[2] 122 man_id = devid[1][:3] 123 model = devid[3] 124 del devid 125 try: 126 man_id, manufacturer = _monitor_brand_lookup(man_id) 127 except TypeError: 128 manufacturer = None 129 130 if (serial, model) != (None, None): 131 data = { 132 'name': f'{manufacturer} {model}', 133 'model': model, 134 'serial': serial, 135 'manufacturer': manufacturer, 136 'manufacturer_id': man_id, 137 'edid': edid 138 } 139 if monitor.InstanceName in laptop_displays: 140 data['index'] = laptop 141 data['method'] = WMI 142 laptop += 1 143 else: 144 data['method'] = VCP 145 desktop += 1 146 147 if instance_name in uid_keys: 148 # insert the data into the uid_keys list because 149 # uid_keys has the monitors sorted correctly. This 150 # means we don't have to re-sort the list later 151 uid_keys[uid_keys.index(instance_name)] = data 152 else: 153 extras.append(data) 154 except Exception as e: 155 log.debug(f'exception getting device info for {monitor.InstanceName} - {type(e).__name__}: {e}') 156 pass 157 158 info = uid_keys + extras 159 if desktop: 160 # now make sure desktop monitors have the correct index 161 count = 0 162 for item in info: 163 if item['method'] == VCP: 164 item['index'] = count 165 count += 1 166 except Exception as e: 167 log.debug(f'error gathering display information - {type(e).__name__}: {e}', exc_info=True) 168 pass 169 170 # return info only which has correct data 171 info = [i for i in info if isinstance(i, dict)] 172 173 __cache__.store('windows_monitors_info_raw', info) 174 175 return info 176 177 178class WMI: 179 ''' 180 A collection of screen brightness related methods using the WMI API. 181 This class primarily works with laptop displays. 182 ''' 183 @classmethod 184 def get_display_info(cls, display: Optional[Union[int, str]] = None) -> List[dict]: 185 ''' 186 Returns a list of dictionaries of info about all detected monitors 187 188 Args: 189 display (str or int): [*Optional*] the monitor to return info about. 190 Pass in the serial number, name, model, edid or index 191 192 Returns: 193 list: list of dicts 194 195 Example: 196 ```python 197 import screen_brightness_control as sbc 198 199 info = sbc.windows.WMI.get_display_info() 200 for i in info: 201 print('================') 202 for key, value in i.items(): 203 print(key, ':', value) 204 205 # get information about the first WMI addressable monitor 206 primary_info = sbc.windows.WMI.get_display_info(0) 207 208 # get information about a monitor with a specific name 209 benq_info = sbc.windows.WMI.get_display_info('BenQ GL2450H') 210 ``` 211 ''' 212 info = [i for i in get_display_info() if i['method'] == cls] 213 if display is not None: 214 info = filter_monitors(display=display, haystack=info) 215 return info 216 217 @classmethod 218 def set_brightness(cls, value: int, display: Optional[int] = None): 219 ''' 220 Sets the display brightness for Windows using WMI 221 222 Args: 223 value (int): The percentage to set the brightness to 224 display (int): The specific display you wish to query. 225 226 Raises: 227 LookupError: if the given display cannot be found 228 229 Example: 230 ```python 231 import screen_brightness_control as sbc 232 233 # set brightness of WMI addressable monitors to 50% 234 sbc.windows.WMI.set_brightness(50) 235 236 # set the primary display brightness to 75% 237 sbc.windows.WMI.set_brightness(75, display = 0) 238 239 # set the brightness of the secondary display to 25% 240 sbc.windows.WMI.set_brightness(25, display = 1) 241 ``` 242 ''' 243 brightness_method = _wmi_init().WmiMonitorBrightnessMethods() 244 if display is not None: 245 brightness_method = [brightness_method[display]] 246 247 for method in brightness_method: 248 method.WmiSetBrightness(value, 0) 249 250 @classmethod 251 def get_brightness(cls, display: Optional[int] = None) -> List[int]: 252 ''' 253 Returns the current display brightness using WMI 254 255 Args: 256 display (int): The specific display you wish to query. 257 258 Returns: 259 list: list of integers (0 to 100) 260 261 Raises: 262 LookupError: if the given display cannot be found 263 264 Example: 265 ```python 266 import screen_brightness_control as sbc 267 268 # get brightness of all WMI addressable monitors 269 current_brightness = sbc.windows.WMI.get_brightness() 270 if type(current_brightness) is int: 271 print('There is only one detected display') 272 else: 273 print('There are', len(current_brightness), 'detected displays') 274 275 # get the primary display brightness 276 primary_brightness = sbc.windows.WMI.get_brightness(display = 0) 277 278 # get the brightness of the secondary monitor 279 benq_brightness = sbc.windows.WMI.get_brightness(display = 1) 280 ``` 281 ''' 282 brightness_method = _wmi_init().WmiMonitorBrightness() 283 if display is not None: 284 brightness_method = [brightness_method[display]] 285 286 values = [i.CurrentBrightness for i in brightness_method] 287 return values 288 289 290class VCP: 291 '''Collection of screen brightness related methods using the DDC/CI commands''' 292 _MONITORENUMPROC = WINFUNCTYPE(BOOL, HMONITOR, HDC, POINTER(RECT), LPARAM) 293 294 class _PHYSICAL_MONITOR(Structure): 295 '''internal class, do not call''' 296 _fields_ = [('handle', HANDLE), 297 ('description', WCHAR * 128)] 298 299 @classmethod 300 def iter_physical_monitors(cls, start: int = 0) -> Generator[ctypes.wintypes.HANDLE, None, None]: 301 ''' 302 A generator to iterate through all physical monitors 303 and then close them again afterwards, yielding their handles. 304 It is not recommended to use this function unless you are familiar with `ctypes` and `windll` 305 306 Args: 307 start (int): skip the first X handles 308 309 Yields: 310 ctypes.wintypes.HANDLE 311 312 Raises: 313 ctypes.WinError: upon failure to enumerate through the monitors 314 ''' 315 def callback(hmonitor, *_): 316 monitors.append(HMONITOR(hmonitor)) 317 return True 318 319 monitors = [] 320 if not windll.user32.EnumDisplayMonitors(None, None, cls._MONITORENUMPROC(callback), None): 321 raise WinError('EnumDisplayMonitors failed') 322 323 # user index keeps track of valid monitors 324 user_index = 0 325 # monitor index keeps track of valid and pseudo monitors 326 monitor_index = 0 327 display_devices = list(enum_display_devices()) 328 329 wmi = _wmi_init() 330 try: 331 laptop_displays = [ 332 i.InstanceName.replace('_0', '').split('\\')[2] 333 for i in wmi.WmiMonitorBrightness() 334 ] 335 except Exception as e: 336 log.debug(f'iter_physical_monitors: failed to gather list of laptop displays - {e}') 337 laptop_displays = [] 338 339 for monitor in monitors: 340 # Get physical monitor count 341 count = DWORD() 342 if not windll.dxva2.GetNumberOfPhysicalMonitorsFromHMONITOR(monitor, byref(count)): 343 raise WinError('call to GetNumberOfPhysicalMonitorsFromHMONITOR returned invalid result') 344 if count.value > 0: 345 # Get physical monitor handles 346 physical_array = (cls._PHYSICAL_MONITOR * count.value)() 347 if not windll.dxva2.GetPhysicalMonitorsFromHMONITOR(monitor, count.value, physical_array): 348 raise WinError('call to GetPhysicalMonitorsFromHMONITOR returned invalid result') 349 for item in physical_array: 350 # check that the monitor is not a pseudo monitor by 351 # checking it's StateFlags for the 352 # win32con DISPLAY_DEVICE_ATTACHED_TO_DESKTOP flag 353 if display_devices[monitor_index].StateFlags & win32con.DISPLAY_DEVICE_ATTACHED_TO_DESKTOP: 354 # check if monitor is actually a laptop display 355 if display_devices[monitor_index].DeviceID.split('#')[2] not in laptop_displays: 356 if start is None or user_index >= start: 357 yield item.handle 358 # increment user index as a valid monitor was found 359 user_index += 1 360 # increment monitor index 361 monitor_index += 1 362 windll.dxva2.DestroyPhysicalMonitor(item.handle) 363 364 @classmethod 365 def get_display_info(cls, display: Optional[Union[int, str]] = None) -> List[dict]: 366 ''' 367 Returns a dictionary of info about all detected monitors or a selection of monitors 368 369 Args: 370 display (int or str): [*Optional*] the monitor to return info about. 371 Pass in the serial number, name, model, edid or index 372 373 Returns: 374 list: list of dicts 375 376 Example: 377 ```python 378 import screen_brightness_control as sbc 379 380 # get the information about all monitors 381 vcp_info = sbc.windows.VCP.get_display_info() 382 print(vcp_info) 383 # EG output: [{'name': 'BenQ GL2450H', ... }, {'name': 'Dell U2211H', ... }] 384 385 # get information about a monitor with this specific model 386 bnq_info = sbc.windows.VCP.get_display_info('GL2450H') 387 # EG output: {'name': 'BenQ GL2450H', 'model': 'GL2450H', ... } 388 ``` 389 ''' 390 info = [i for i in get_display_info() if i['method'] == cls] 391 if display is not None: 392 info = filter_monitors(display=display, haystack=info) 393 return info 394 395 @classmethod 396 def get_brightness(cls, display: Optional[int] = None, max_tries: int = 50) -> List[int]: 397 ''' 398 Retrieve the brightness of all connected displays using the `ctypes.windll` API 399 400 Args: 401 display (int): The specific display you wish to query. 402 max_tries (int): the maximum allowed number of attempts to 403 read the VCP output from the monitor 404 405 Returns: 406 list: list of ints (0 to 100) 407 408 Examples: 409 ```python 410 import screen_brightness_control as sbc 411 412 # Get the brightness for all detected displays 413 current_brightness = sbc.windows.VCP.get_brightness() 414 print('There are', len(current_brightness), 'detected displays') 415 416 # Get the brightness for the primary display 417 primary_brightness = sbc.windows.VCP.get_brightness(display = 0)[0] 418 419 # Get the brightness for a secondary display 420 secondary_brightness = sbc.windows.VCP.get_brightness(display = 1)[0] 421 ``` 422 ''' 423 code = BYTE(0x10) 424 values = [] 425 start = display if display is not None else 0 426 for index, monitor in enumerate(cls.iter_physical_monitors(start=start), start=start): 427 current = __cache__.get(f'vcp_brightness_{index}') 428 if current is None: 429 cur_out = DWORD() 430 handle = HANDLE(monitor) 431 for attempt in range(max_tries): 432 if windll.dxva2.GetVCPFeatureAndVCPFeatureReply(handle, code, None, byref(cur_out), None): 433 current = cur_out.value 434 break 435 current = None 436 time.sleep(0.02 if attempt < 20 else 0.1) 437 438 if current is not None: 439 __cache__.store(f'vcp_brightness_{index}', current, expires=0.1) 440 values.append(current) 441 442 if display == index: 443 # if we have just got the display we wanted then exit here 444 # no point iterating through all the other ones 445 break 446 447 return values 448 449 @classmethod 450 def set_brightness(cls, value: int, display: Optional[int] = None, max_tries: int = 50): 451 ''' 452 Sets the brightness for all connected displays using the `ctypes.windll` API 453 454 Args: 455 display (int): The specific display you wish to query. 456 max_tries (int): the maximum allowed number of attempts to 457 send the VCP input to the monitor 458 459 Examples: 460 ```python 461 import screen_brightness_control as sbc 462 463 # Set the brightness for all detected displays to 50% 464 sbc.windows.VCP.set_brightness(50) 465 466 # Set the brightness for the primary display to 75% 467 sbc.windows.VCP.set_brightness(75, display = 0) 468 469 # Set the brightness for a secondary display to 25% 470 sbc.windows.VCP.set_brightness(25, display = 1) 471 ``` 472 ''' 473 __cache__.expire(startswith='vcp_brightness_') 474 code = BYTE(0x10) 475 value = DWORD(value) 476 start = display if display is not None else 0 477 for index, monitor in enumerate(cls.iter_physical_monitors(start=start), start=start): 478 if display is None or display == index: 479 handle = HANDLE(monitor) 480 for attempt in range(max_tries): 481 if windll.dxva2.SetVCPFeature(handle, code, value): 482 break 483 time.sleep(0.02 if attempt < 20 else 0.1) 484 485 486def list_monitors_info(method: Optional[str] = None, allow_duplicates: bool = False) -> List[dict]: 487 ''' 488 Lists detailed information about all detected monitors 489 490 Args: 491 method (str): the method the monitor can be addressed by. See `screen_brightness_control.get_methods` 492 for more info on available methods 493 allow_duplicates (bool): whether to filter out duplicate displays (displays with the same EDID) or not 494 495 Returns: 496 list: list of dicts upon success, empty list upon failure 497 498 Raises: 499 ValueError: if the method kwarg is invalid 500 501 Example: 502 ```python 503 import screen_brightness_control as sbc 504 505 monitors = sbc.windows.list_monitors_info() 506 for info in monitors: 507 print('=======================') 508 # the manufacturer name plus the model 509 print('Name:', info['name']) 510 # the general model of the display 511 print('Model:', info['model']) 512 # a unique string assigned by Windows to this display 513 print('Serial:', info['serial']) 514 # the name of the brand of the monitor 515 print('Manufacturer:', info['manufacturer']) 516 # the 3 letter code corresponding to the brand name, EG: BNQ -> BenQ 517 print('Manufacturer ID:', info['manufacturer_id']) 518 # the index of that display FOR THE SPECIFIC METHOD THE DISPLAY USES 519 print('Index:', info['index']) 520 # the method this monitor can be addressed by 521 print('Method:', info['method']) 522 # the EDID string of the monitor 523 print('EDID:', info['edid']) 524 ``` 525 ''' 526 # no caching here because get_display_info caches its results 527 info = get_display_info() 528 529 all_methods = get_methods() 530 531 if method is not None: 532 method = method.lower() 533 if method not in all_methods: 534 log.debug(f'requested method {repr(method)} invalid') 535 raise ValueError(f'method must be one of: {list(all_methods)}') 536 info = [i for i in info if i['method'].__name__.lower() == method] 537 538 if allow_duplicates: 539 return info 540 541 try: 542 # use filter_monitors to remove duplicates 543 return filter_monitors(haystack=info) 544 except LookupError: 545 return []
36def enum_display_devices() -> Generator[win32api.PyDISPLAY_DEVICEType, None, None]: 37 ''' 38 Yields all display devices connected to the computer 39 40 Yields: 41 win32api.PyDISPLAY_DEVICEType 42 ''' 43 for monitor_enum in win32api.EnumDisplayMonitors(): 44 pyhandle = monitor_enum[0] 45 monitor_info = win32api.GetMonitorInfo(pyhandle) 46 for adaptor_index in range(5): 47 try: 48 device = win32api.EnumDisplayDevices(monitor_info['Device'], adaptor_index, 1) 49 except Exception: 50 log.debug(f'failed to get display device {monitor_info["Device"]} on adaptor index {adaptor_index}') 51 pass 52 else: 53 yield device 54 break
Yields all display devices connected to the computer
Yields
- win32api.PyDISPLAY_DEVICEType
57def get_display_info() -> List[dict]: 58 ''' 59 Gets information about all connected displays using WMI and win32api 60 61 Returns: 62 list: list of dictionaries 63 64 Example: 65 ```python 66 import screen_brightness_control as s 67 68 info = s.windows.get_display_info() 69 for display in info: 70 print(display['name']) 71 ``` 72 ''' 73 info = __cache__.get('windows_monitors_info_raw') 74 if info is None: 75 info = [] 76 try: 77 # collect all monitor UIDs (derived from DeviceID) 78 monitor_uids = {} 79 for device in enum_display_devices(): 80 monitor_uids[device.DeviceID.split('#')[2]] = device 81 82 # gather list of laptop displays to check against later 83 wmi = _wmi_init() 84 try: 85 laptop_displays = [ 86 i.InstanceName 87 for i in wmi.WmiMonitorBrightness() 88 ] 89 except Exception as e: 90 log.debug(f'get_display_info: failed to gather list of laptop displays - {e}') 91 laptop_displays = [] 92 93 extras, desktop, laptop = [], 0, 0 94 uid_keys = list(monitor_uids.keys()) 95 for monitor in wmi.WmiMonitorDescriptorMethods(): 96 try: 97 model, serial, manufacturer, man_id, edid = None, None, None, None, None 98 instance_name = monitor.InstanceName.replace('_0', '', 1).split('\\')[2] 99 pydevice = monitor_uids[instance_name] 100 101 # get the EDID 102 try: 103 edid = ''.join(f'{char:02x}' for char in monitor.WmiGetMonitorRawEEdidV1Block(0)[0]) 104 except Exception as e: 105 log.debug(f'failed to get EDID string for {monitor.InstanceName} - {type(e).__name__}: {e}') 106 edid = None 107 108 # get serial, model, manufacturer and manufacturer ID 109 try: 110 if edid is None: 111 # we already log the EDID exception earlier so don't log it again here 112 raise Exception('invalid EDID. Cannot parse') 113 # we do the EDID parsing ourselves because calling wmi.WmiMonitorID 114 # takes too long 115 parsed = EDID.parse(edid) 116 man_id, manufacturer, model, name, serial = parsed 117 if name is None: 118 raise Exception('parsed EDID returned invalid monitor name') 119 except Exception as e: 120 log.debug(f'exception parsing edid str for {monitor.InstanceName} - {type(e).__name__}: {e}') 121 devid = pydevice.DeviceID.split('#') 122 serial = devid[2] 123 man_id = devid[1][:3] 124 model = devid[3] 125 del devid 126 try: 127 man_id, manufacturer = _monitor_brand_lookup(man_id) 128 except TypeError: 129 manufacturer = None 130 131 if (serial, model) != (None, None): 132 data = { 133 'name': f'{manufacturer} {model}', 134 'model': model, 135 'serial': serial, 136 'manufacturer': manufacturer, 137 'manufacturer_id': man_id, 138 'edid': edid 139 } 140 if monitor.InstanceName in laptop_displays: 141 data['index'] = laptop 142 data['method'] = WMI 143 laptop += 1 144 else: 145 data['method'] = VCP 146 desktop += 1 147 148 if instance_name in uid_keys: 149 # insert the data into the uid_keys list because 150 # uid_keys has the monitors sorted correctly. This 151 # means we don't have to re-sort the list later 152 uid_keys[uid_keys.index(instance_name)] = data 153 else: 154 extras.append(data) 155 except Exception as e: 156 log.debug(f'exception getting device info for {monitor.InstanceName} - {type(e).__name__}: {e}') 157 pass 158 159 info = uid_keys + extras 160 if desktop: 161 # now make sure desktop monitors have the correct index 162 count = 0 163 for item in info: 164 if item['method'] == VCP: 165 item['index'] = count 166 count += 1 167 except Exception as e: 168 log.debug(f'error gathering display information - {type(e).__name__}: {e}', exc_info=True) 169 pass 170 171 # return info only which has correct data 172 info = [i for i in info if isinstance(i, dict)] 173 174 __cache__.store('windows_monitors_info_raw', info) 175 176 return info
Gets information about all connected displays using WMI and win32api
Returns
- list: list of dictionaries
Example
import screen_brightness_control as s info = s.windows.get_display_info() for display in info: print(display['name'])
179class WMI: 180 ''' 181 A collection of screen brightness related methods using the WMI API. 182 This class primarily works with laptop displays. 183 ''' 184 @classmethod 185 def get_display_info(cls, display: Optional[Union[int, str]] = None) -> List[dict]: 186 ''' 187 Returns a list of dictionaries of info about all detected monitors 188 189 Args: 190 display (str or int): [*Optional*] the monitor to return info about. 191 Pass in the serial number, name, model, edid or index 192 193 Returns: 194 list: list of dicts 195 196 Example: 197 ```python 198 import screen_brightness_control as sbc 199 200 info = sbc.windows.WMI.get_display_info() 201 for i in info: 202 print('================') 203 for key, value in i.items(): 204 print(key, ':', value) 205 206 # get information about the first WMI addressable monitor 207 primary_info = sbc.windows.WMI.get_display_info(0) 208 209 # get information about a monitor with a specific name 210 benq_info = sbc.windows.WMI.get_display_info('BenQ GL2450H') 211 ``` 212 ''' 213 info = [i for i in get_display_info() if i['method'] == cls] 214 if display is not None: 215 info = filter_monitors(display=display, haystack=info) 216 return info 217 218 @classmethod 219 def set_brightness(cls, value: int, display: Optional[int] = None): 220 ''' 221 Sets the display brightness for Windows using WMI 222 223 Args: 224 value (int): The percentage to set the brightness to 225 display (int): The specific display you wish to query. 226 227 Raises: 228 LookupError: if the given display cannot be found 229 230 Example: 231 ```python 232 import screen_brightness_control as sbc 233 234 # set brightness of WMI addressable monitors to 50% 235 sbc.windows.WMI.set_brightness(50) 236 237 # set the primary display brightness to 75% 238 sbc.windows.WMI.set_brightness(75, display = 0) 239 240 # set the brightness of the secondary display to 25% 241 sbc.windows.WMI.set_brightness(25, display = 1) 242 ``` 243 ''' 244 brightness_method = _wmi_init().WmiMonitorBrightnessMethods() 245 if display is not None: 246 brightness_method = [brightness_method[display]] 247 248 for method in brightness_method: 249 method.WmiSetBrightness(value, 0) 250 251 @classmethod 252 def get_brightness(cls, display: Optional[int] = None) -> List[int]: 253 ''' 254 Returns the current display brightness using WMI 255 256 Args: 257 display (int): The specific display you wish to query. 258 259 Returns: 260 list: list of integers (0 to 100) 261 262 Raises: 263 LookupError: if the given display cannot be found 264 265 Example: 266 ```python 267 import screen_brightness_control as sbc 268 269 # get brightness of all WMI addressable monitors 270 current_brightness = sbc.windows.WMI.get_brightness() 271 if type(current_brightness) is int: 272 print('There is only one detected display') 273 else: 274 print('There are', len(current_brightness), 'detected displays') 275 276 # get the primary display brightness 277 primary_brightness = sbc.windows.WMI.get_brightness(display = 0) 278 279 # get the brightness of the secondary monitor 280 benq_brightness = sbc.windows.WMI.get_brightness(display = 1) 281 ``` 282 ''' 283 brightness_method = _wmi_init().WmiMonitorBrightness() 284 if display is not None: 285 brightness_method = [brightness_method[display]] 286 287 values = [i.CurrentBrightness for i in brightness_method] 288 return values
A collection of screen brightness related methods using the WMI API. This class primarily works with laptop displays.
184 @classmethod 185 def get_display_info(cls, display: Optional[Union[int, str]] = None) -> List[dict]: 186 ''' 187 Returns a list of dictionaries of info about all detected monitors 188 189 Args: 190 display (str or int): [*Optional*] the monitor to return info about. 191 Pass in the serial number, name, model, edid or index 192 193 Returns: 194 list: list of dicts 195 196 Example: 197 ```python 198 import screen_brightness_control as sbc 199 200 info = sbc.windows.WMI.get_display_info() 201 for i in info: 202 print('================') 203 for key, value in i.items(): 204 print(key, ':', value) 205 206 # get information about the first WMI addressable monitor 207 primary_info = sbc.windows.WMI.get_display_info(0) 208 209 # get information about a monitor with a specific name 210 benq_info = sbc.windows.WMI.get_display_info('BenQ GL2450H') 211 ``` 212 ''' 213 info = [i for i in get_display_info() if i['method'] == cls] 214 if display is not None: 215 info = filter_monitors(display=display, haystack=info) 216 return info
Returns a list of dictionaries of info about all detected monitors
Args
- display (str or int): [Optional] the monitor to return info about. Pass in the serial number, name, model, edid or index
Returns
- list: list of dicts
Example
import screen_brightness_control as sbc info = sbc.windows.WMI.get_display_info() for i in info: print('================') for key, value in i.items(): print(key, ':', value) # get information about the first WMI addressable monitor primary_info = sbc.windows.WMI.get_display_info(0) # get information about a monitor with a specific name benq_info = sbc.windows.WMI.get_display_info('BenQ GL2450H')
218 @classmethod 219 def set_brightness(cls, value: int, display: Optional[int] = None): 220 ''' 221 Sets the display brightness for Windows using WMI 222 223 Args: 224 value (int): The percentage to set the brightness to 225 display (int): The specific display you wish to query. 226 227 Raises: 228 LookupError: if the given display cannot be found 229 230 Example: 231 ```python 232 import screen_brightness_control as sbc 233 234 # set brightness of WMI addressable monitors to 50% 235 sbc.windows.WMI.set_brightness(50) 236 237 # set the primary display brightness to 75% 238 sbc.windows.WMI.set_brightness(75, display = 0) 239 240 # set the brightness of the secondary display to 25% 241 sbc.windows.WMI.set_brightness(25, display = 1) 242 ``` 243 ''' 244 brightness_method = _wmi_init().WmiMonitorBrightnessMethods() 245 if display is not None: 246 brightness_method = [brightness_method[display]] 247 248 for method in brightness_method: 249 method.WmiSetBrightness(value, 0)
Sets the display brightness for Windows using WMI
Args
- value (int): The percentage to set the brightness to
- display (int): The specific display you wish to query.
Raises
- LookupError: if the given display cannot be found
Example
import screen_brightness_control as sbc # set brightness of WMI addressable monitors to 50% sbc.windows.WMI.set_brightness(50) # set the primary display brightness to 75% sbc.windows.WMI.set_brightness(75, display = 0) # set the brightness of the secondary display to 25% sbc.windows.WMI.set_brightness(25, display = 1)
251 @classmethod 252 def get_brightness(cls, display: Optional[int] = None) -> List[int]: 253 ''' 254 Returns the current display brightness using WMI 255 256 Args: 257 display (int): The specific display you wish to query. 258 259 Returns: 260 list: list of integers (0 to 100) 261 262 Raises: 263 LookupError: if the given display cannot be found 264 265 Example: 266 ```python 267 import screen_brightness_control as sbc 268 269 # get brightness of all WMI addressable monitors 270 current_brightness = sbc.windows.WMI.get_brightness() 271 if type(current_brightness) is int: 272 print('There is only one detected display') 273 else: 274 print('There are', len(current_brightness), 'detected displays') 275 276 # get the primary display brightness 277 primary_brightness = sbc.windows.WMI.get_brightness(display = 0) 278 279 # get the brightness of the secondary monitor 280 benq_brightness = sbc.windows.WMI.get_brightness(display = 1) 281 ``` 282 ''' 283 brightness_method = _wmi_init().WmiMonitorBrightness() 284 if display is not None: 285 brightness_method = [brightness_method[display]] 286 287 values = [i.CurrentBrightness for i in brightness_method] 288 return values
Returns the current display brightness using WMI
Args
- display (int): The specific display you wish to query.
Returns
- list: list of integers (0 to 100)
Raises
- LookupError: if the given display cannot be found
Example
import screen_brightness_control as sbc # get brightness of all WMI addressable monitors current_brightness = sbc.windows.WMI.get_brightness() if type(current_brightness) is int: print('There is only one detected display') else: print('There are', len(current_brightness), 'detected displays') # get the primary display brightness primary_brightness = sbc.windows.WMI.get_brightness(display = 0) # get the brightness of the secondary monitor benq_brightness = sbc.windows.WMI.get_brightness(display = 1)
291class VCP: 292 '''Collection of screen brightness related methods using the DDC/CI commands''' 293 _MONITORENUMPROC = WINFUNCTYPE(BOOL, HMONITOR, HDC, POINTER(RECT), LPARAM) 294 295 class _PHYSICAL_MONITOR(Structure): 296 '''internal class, do not call''' 297 _fields_ = [('handle', HANDLE), 298 ('description', WCHAR * 128)] 299 300 @classmethod 301 def iter_physical_monitors(cls, start: int = 0) -> Generator[ctypes.wintypes.HANDLE, None, None]: 302 ''' 303 A generator to iterate through all physical monitors 304 and then close them again afterwards, yielding their handles. 305 It is not recommended to use this function unless you are familiar with `ctypes` and `windll` 306 307 Args: 308 start (int): skip the first X handles 309 310 Yields: 311 ctypes.wintypes.HANDLE 312 313 Raises: 314 ctypes.WinError: upon failure to enumerate through the monitors 315 ''' 316 def callback(hmonitor, *_): 317 monitors.append(HMONITOR(hmonitor)) 318 return True 319 320 monitors = [] 321 if not windll.user32.EnumDisplayMonitors(None, None, cls._MONITORENUMPROC(callback), None): 322 raise WinError('EnumDisplayMonitors failed') 323 324 # user index keeps track of valid monitors 325 user_index = 0 326 # monitor index keeps track of valid and pseudo monitors 327 monitor_index = 0 328 display_devices = list(enum_display_devices()) 329 330 wmi = _wmi_init() 331 try: 332 laptop_displays = [ 333 i.InstanceName.replace('_0', '').split('\\')[2] 334 for i in wmi.WmiMonitorBrightness() 335 ] 336 except Exception as e: 337 log.debug(f'iter_physical_monitors: failed to gather list of laptop displays - {e}') 338 laptop_displays = [] 339 340 for monitor in monitors: 341 # Get physical monitor count 342 count = DWORD() 343 if not windll.dxva2.GetNumberOfPhysicalMonitorsFromHMONITOR(monitor, byref(count)): 344 raise WinError('call to GetNumberOfPhysicalMonitorsFromHMONITOR returned invalid result') 345 if count.value > 0: 346 # Get physical monitor handles 347 physical_array = (cls._PHYSICAL_MONITOR * count.value)() 348 if not windll.dxva2.GetPhysicalMonitorsFromHMONITOR(monitor, count.value, physical_array): 349 raise WinError('call to GetPhysicalMonitorsFromHMONITOR returned invalid result') 350 for item in physical_array: 351 # check that the monitor is not a pseudo monitor by 352 # checking it's StateFlags for the 353 # win32con DISPLAY_DEVICE_ATTACHED_TO_DESKTOP flag 354 if display_devices[monitor_index].StateFlags & win32con.DISPLAY_DEVICE_ATTACHED_TO_DESKTOP: 355 # check if monitor is actually a laptop display 356 if display_devices[monitor_index].DeviceID.split('#')[2] not in laptop_displays: 357 if start is None or user_index >= start: 358 yield item.handle 359 # increment user index as a valid monitor was found 360 user_index += 1 361 # increment monitor index 362 monitor_index += 1 363 windll.dxva2.DestroyPhysicalMonitor(item.handle) 364 365 @classmethod 366 def get_display_info(cls, display: Optional[Union[int, str]] = None) -> List[dict]: 367 ''' 368 Returns a dictionary of info about all detected monitors or a selection of monitors 369 370 Args: 371 display (int or str): [*Optional*] the monitor to return info about. 372 Pass in the serial number, name, model, edid or index 373 374 Returns: 375 list: list of dicts 376 377 Example: 378 ```python 379 import screen_brightness_control as sbc 380 381 # get the information about all monitors 382 vcp_info = sbc.windows.VCP.get_display_info() 383 print(vcp_info) 384 # EG output: [{'name': 'BenQ GL2450H', ... }, {'name': 'Dell U2211H', ... }] 385 386 # get information about a monitor with this specific model 387 bnq_info = sbc.windows.VCP.get_display_info('GL2450H') 388 # EG output: {'name': 'BenQ GL2450H', 'model': 'GL2450H', ... } 389 ``` 390 ''' 391 info = [i for i in get_display_info() if i['method'] == cls] 392 if display is not None: 393 info = filter_monitors(display=display, haystack=info) 394 return info 395 396 @classmethod 397 def get_brightness(cls, display: Optional[int] = None, max_tries: int = 50) -> List[int]: 398 ''' 399 Retrieve the brightness of all connected displays using the `ctypes.windll` API 400 401 Args: 402 display (int): The specific display you wish to query. 403 max_tries (int): the maximum allowed number of attempts to 404 read the VCP output from the monitor 405 406 Returns: 407 list: list of ints (0 to 100) 408 409 Examples: 410 ```python 411 import screen_brightness_control as sbc 412 413 # Get the brightness for all detected displays 414 current_brightness = sbc.windows.VCP.get_brightness() 415 print('There are', len(current_brightness), 'detected displays') 416 417 # Get the brightness for the primary display 418 primary_brightness = sbc.windows.VCP.get_brightness(display = 0)[0] 419 420 # Get the brightness for a secondary display 421 secondary_brightness = sbc.windows.VCP.get_brightness(display = 1)[0] 422 ``` 423 ''' 424 code = BYTE(0x10) 425 values = [] 426 start = display if display is not None else 0 427 for index, monitor in enumerate(cls.iter_physical_monitors(start=start), start=start): 428 current = __cache__.get(f'vcp_brightness_{index}') 429 if current is None: 430 cur_out = DWORD() 431 handle = HANDLE(monitor) 432 for attempt in range(max_tries): 433 if windll.dxva2.GetVCPFeatureAndVCPFeatureReply(handle, code, None, byref(cur_out), None): 434 current = cur_out.value 435 break 436 current = None 437 time.sleep(0.02 if attempt < 20 else 0.1) 438 439 if current is not None: 440 __cache__.store(f'vcp_brightness_{index}', current, expires=0.1) 441 values.append(current) 442 443 if display == index: 444 # if we have just got the display we wanted then exit here 445 # no point iterating through all the other ones 446 break 447 448 return values 449 450 @classmethod 451 def set_brightness(cls, value: int, display: Optional[int] = None, max_tries: int = 50): 452 ''' 453 Sets the brightness for all connected displays using the `ctypes.windll` API 454 455 Args: 456 display (int): The specific display you wish to query. 457 max_tries (int): the maximum allowed number of attempts to 458 send the VCP input to the monitor 459 460 Examples: 461 ```python 462 import screen_brightness_control as sbc 463 464 # Set the brightness for all detected displays to 50% 465 sbc.windows.VCP.set_brightness(50) 466 467 # Set the brightness for the primary display to 75% 468 sbc.windows.VCP.set_brightness(75, display = 0) 469 470 # Set the brightness for a secondary display to 25% 471 sbc.windows.VCP.set_brightness(25, display = 1) 472 ``` 473 ''' 474 __cache__.expire(startswith='vcp_brightness_') 475 code = BYTE(0x10) 476 value = DWORD(value) 477 start = display if display is not None else 0 478 for index, monitor in enumerate(cls.iter_physical_monitors(start=start), start=start): 479 if display is None or display == index: 480 handle = HANDLE(monitor) 481 for attempt in range(max_tries): 482 if windll.dxva2.SetVCPFeature(handle, code, value): 483 break 484 time.sleep(0.02 if attempt < 20 else 0.1)
Collection of screen brightness related methods using the DDC/CI commands
300 @classmethod 301 def iter_physical_monitors(cls, start: int = 0) -> Generator[ctypes.wintypes.HANDLE, None, None]: 302 ''' 303 A generator to iterate through all physical monitors 304 and then close them again afterwards, yielding their handles. 305 It is not recommended to use this function unless you are familiar with `ctypes` and `windll` 306 307 Args: 308 start (int): skip the first X handles 309 310 Yields: 311 ctypes.wintypes.HANDLE 312 313 Raises: 314 ctypes.WinError: upon failure to enumerate through the monitors 315 ''' 316 def callback(hmonitor, *_): 317 monitors.append(HMONITOR(hmonitor)) 318 return True 319 320 monitors = [] 321 if not windll.user32.EnumDisplayMonitors(None, None, cls._MONITORENUMPROC(callback), None): 322 raise WinError('EnumDisplayMonitors failed') 323 324 # user index keeps track of valid monitors 325 user_index = 0 326 # monitor index keeps track of valid and pseudo monitors 327 monitor_index = 0 328 display_devices = list(enum_display_devices()) 329 330 wmi = _wmi_init() 331 try: 332 laptop_displays = [ 333 i.InstanceName.replace('_0', '').split('\\')[2] 334 for i in wmi.WmiMonitorBrightness() 335 ] 336 except Exception as e: 337 log.debug(f'iter_physical_monitors: failed to gather list of laptop displays - {e}') 338 laptop_displays = [] 339 340 for monitor in monitors: 341 # Get physical monitor count 342 count = DWORD() 343 if not windll.dxva2.GetNumberOfPhysicalMonitorsFromHMONITOR(monitor, byref(count)): 344 raise WinError('call to GetNumberOfPhysicalMonitorsFromHMONITOR returned invalid result') 345 if count.value > 0: 346 # Get physical monitor handles 347 physical_array = (cls._PHYSICAL_MONITOR * count.value)() 348 if not windll.dxva2.GetPhysicalMonitorsFromHMONITOR(monitor, count.value, physical_array): 349 raise WinError('call to GetPhysicalMonitorsFromHMONITOR returned invalid result') 350 for item in physical_array: 351 # check that the monitor is not a pseudo monitor by 352 # checking it's StateFlags for the 353 # win32con DISPLAY_DEVICE_ATTACHED_TO_DESKTOP flag 354 if display_devices[monitor_index].StateFlags & win32con.DISPLAY_DEVICE_ATTACHED_TO_DESKTOP: 355 # check if monitor is actually a laptop display 356 if display_devices[monitor_index].DeviceID.split('#')[2] not in laptop_displays: 357 if start is None or user_index >= start: 358 yield item.handle 359 # increment user index as a valid monitor was found 360 user_index += 1 361 # increment monitor index 362 monitor_index += 1 363 windll.dxva2.DestroyPhysicalMonitor(item.handle)
A generator to iterate through all physical monitors
and then close them again afterwards, yielding their handles.
It is not recommended to use this function unless you are familiar with ctypes
and windll
Args
- start (int): skip the first X handles
Yields
- ctypes.wintypes.HANDLE
Raises
- ctypes.WinError: upon failure to enumerate through the monitors
365 @classmethod 366 def get_display_info(cls, display: Optional[Union[int, str]] = None) -> List[dict]: 367 ''' 368 Returns a dictionary of info about all detected monitors or a selection of monitors 369 370 Args: 371 display (int or str): [*Optional*] the monitor to return info about. 372 Pass in the serial number, name, model, edid or index 373 374 Returns: 375 list: list of dicts 376 377 Example: 378 ```python 379 import screen_brightness_control as sbc 380 381 # get the information about all monitors 382 vcp_info = sbc.windows.VCP.get_display_info() 383 print(vcp_info) 384 # EG output: [{'name': 'BenQ GL2450H', ... }, {'name': 'Dell U2211H', ... }] 385 386 # get information about a monitor with this specific model 387 bnq_info = sbc.windows.VCP.get_display_info('GL2450H') 388 # EG output: {'name': 'BenQ GL2450H', 'model': 'GL2450H', ... } 389 ``` 390 ''' 391 info = [i for i in get_display_info() if i['method'] == cls] 392 if display is not None: 393 info = filter_monitors(display=display, haystack=info) 394 return info
Returns a dictionary of info about all detected monitors or a selection of monitors
Args
- display (int or str): [Optional] the monitor to return info about. Pass in the serial number, name, model, edid or index
Returns
- list: list of dicts
Example
import screen_brightness_control as sbc # get the information about all monitors vcp_info = sbc.windows.VCP.get_display_info() print(vcp_info) # EG output: [{'name': 'BenQ GL2450H', ... }, {'name': 'Dell U2211H', ... }] # get information about a monitor with this specific model bnq_info = sbc.windows.VCP.get_display_info('GL2450H') # EG output: {'name': 'BenQ GL2450H', 'model': 'GL2450H', ... }
396 @classmethod 397 def get_brightness(cls, display: Optional[int] = None, max_tries: int = 50) -> List[int]: 398 ''' 399 Retrieve the brightness of all connected displays using the `ctypes.windll` API 400 401 Args: 402 display (int): The specific display you wish to query. 403 max_tries (int): the maximum allowed number of attempts to 404 read the VCP output from the monitor 405 406 Returns: 407 list: list of ints (0 to 100) 408 409 Examples: 410 ```python 411 import screen_brightness_control as sbc 412 413 # Get the brightness for all detected displays 414 current_brightness = sbc.windows.VCP.get_brightness() 415 print('There are', len(current_brightness), 'detected displays') 416 417 # Get the brightness for the primary display 418 primary_brightness = sbc.windows.VCP.get_brightness(display = 0)[0] 419 420 # Get the brightness for a secondary display 421 secondary_brightness = sbc.windows.VCP.get_brightness(display = 1)[0] 422 ``` 423 ''' 424 code = BYTE(0x10) 425 values = [] 426 start = display if display is not None else 0 427 for index, monitor in enumerate(cls.iter_physical_monitors(start=start), start=start): 428 current = __cache__.get(f'vcp_brightness_{index}') 429 if current is None: 430 cur_out = DWORD() 431 handle = HANDLE(monitor) 432 for attempt in range(max_tries): 433 if windll.dxva2.GetVCPFeatureAndVCPFeatureReply(handle, code, None, byref(cur_out), None): 434 current = cur_out.value 435 break 436 current = None 437 time.sleep(0.02 if attempt < 20 else 0.1) 438 439 if current is not None: 440 __cache__.store(f'vcp_brightness_{index}', current, expires=0.1) 441 values.append(current) 442 443 if display == index: 444 # if we have just got the display we wanted then exit here 445 # no point iterating through all the other ones 446 break 447 448 return values
Retrieve the brightness of all connected displays using the ctypes.windll
API
Args
- display (int): The specific display you wish to query.
- max_tries (int): the maximum allowed number of attempts to read the VCP output from the monitor
Returns
- list: list of ints (0 to 100)
Examples
import screen_brightness_control as sbc # Get the brightness for all detected displays current_brightness = sbc.windows.VCP.get_brightness() print('There are', len(current_brightness), 'detected displays') # Get the brightness for the primary display primary_brightness = sbc.windows.VCP.get_brightness(display = 0)[0] # Get the brightness for a secondary display secondary_brightness = sbc.windows.VCP.get_brightness(display = 1)[0]
450 @classmethod 451 def set_brightness(cls, value: int, display: Optional[int] = None, max_tries: int = 50): 452 ''' 453 Sets the brightness for all connected displays using the `ctypes.windll` API 454 455 Args: 456 display (int): The specific display you wish to query. 457 max_tries (int): the maximum allowed number of attempts to 458 send the VCP input to the monitor 459 460 Examples: 461 ```python 462 import screen_brightness_control as sbc 463 464 # Set the brightness for all detected displays to 50% 465 sbc.windows.VCP.set_brightness(50) 466 467 # Set the brightness for the primary display to 75% 468 sbc.windows.VCP.set_brightness(75, display = 0) 469 470 # Set the brightness for a secondary display to 25% 471 sbc.windows.VCP.set_brightness(25, display = 1) 472 ``` 473 ''' 474 __cache__.expire(startswith='vcp_brightness_') 475 code = BYTE(0x10) 476 value = DWORD(value) 477 start = display if display is not None else 0 478 for index, monitor in enumerate(cls.iter_physical_monitors(start=start), start=start): 479 if display is None or display == index: 480 handle = HANDLE(monitor) 481 for attempt in range(max_tries): 482 if windll.dxva2.SetVCPFeature(handle, code, value): 483 break 484 time.sleep(0.02 if attempt < 20 else 0.1)
Sets the brightness for all connected displays using the ctypes.windll
API
Args
- display (int): The specific display you wish to query.
- max_tries (int): the maximum allowed number of attempts to send the VCP input to the monitor
Examples
import screen_brightness_control as sbc # Set the brightness for all detected displays to 50% sbc.windows.VCP.set_brightness(50) # Set the brightness for the primary display to 75% sbc.windows.VCP.set_brightness(75, display = 0) # Set the brightness for a secondary display to 25% sbc.windows.VCP.set_brightness(25, display = 1)
487def list_monitors_info(method: Optional[str] = None, allow_duplicates: bool = False) -> List[dict]: 488 ''' 489 Lists detailed information about all detected monitors 490 491 Args: 492 method (str): the method the monitor can be addressed by. See `screen_brightness_control.get_methods` 493 for more info on available methods 494 allow_duplicates (bool): whether to filter out duplicate displays (displays with the same EDID) or not 495 496 Returns: 497 list: list of dicts upon success, empty list upon failure 498 499 Raises: 500 ValueError: if the method kwarg is invalid 501 502 Example: 503 ```python 504 import screen_brightness_control as sbc 505 506 monitors = sbc.windows.list_monitors_info() 507 for info in monitors: 508 print('=======================') 509 # the manufacturer name plus the model 510 print('Name:', info['name']) 511 # the general model of the display 512 print('Model:', info['model']) 513 # a unique string assigned by Windows to this display 514 print('Serial:', info['serial']) 515 # the name of the brand of the monitor 516 print('Manufacturer:', info['manufacturer']) 517 # the 3 letter code corresponding to the brand name, EG: BNQ -> BenQ 518 print('Manufacturer ID:', info['manufacturer_id']) 519 # the index of that display FOR THE SPECIFIC METHOD THE DISPLAY USES 520 print('Index:', info['index']) 521 # the method this monitor can be addressed by 522 print('Method:', info['method']) 523 # the EDID string of the monitor 524 print('EDID:', info['edid']) 525 ``` 526 ''' 527 # no caching here because get_display_info caches its results 528 info = get_display_info() 529 530 all_methods = get_methods() 531 532 if method is not None: 533 method = method.lower() 534 if method not in all_methods: 535 log.debug(f'requested method {repr(method)} invalid') 536 raise ValueError(f'method must be one of: {list(all_methods)}') 537 info = [i for i in info if i['method'].__name__.lower() == method] 538 539 if allow_duplicates: 540 return info 541 542 try: 543 # use filter_monitors to remove duplicates 544 return filter_monitors(haystack=info) 545 except LookupError: 546 return []
Lists detailed information about all detected monitors
Args
- method (str): the method the monitor can be addressed by. See
screen_brightness_control.get_methods
for more info on available methods - allow_duplicates (bool): whether to filter out duplicate displays (displays with the same EDID) or not
Returns
- list: list of dicts upon success, empty list upon failure
Raises
- ValueError: if the method kwarg is invalid
Example
import screen_brightness_control as sbc monitors = sbc.windows.list_monitors_info() for info in monitors: print('=======================') # the manufacturer name plus the model print('Name:', info['name']) # the general model of the display print('Model:', info['model']) # a unique string assigned by Windows to this display print('Serial:', info['serial']) # the name of the brand of the monitor print('Manufacturer:', info['manufacturer']) # the 3 letter code corresponding to the brand name, EG: BNQ -> BenQ print('Manufacturer ID:', info['manufacturer_id']) # the index of that display FOR THE SPECIFIC METHOD THE DISPLAY USES print('Index:', info['index']) # the method this monitor can be addressed by print('Method:', info['method']) # the EDID string of the monitor print('EDID:', info['edid'])