在 Python 中获取显示器数量和分辨率,无需 xrandr 命令行工具

问题描述 投票:0回答:5

我正在运行 Ubuntu,我想获取连接的显示器的数量、它们当前的分辨率以及它们相对于彼此的位置(如果可能)。 因为我不喜欢解析 xrandr 命令行工具的控制台输出(至少在不需要的情况下不会),所以我想使用 Python-XLib 或类似的 Pythonic 方法来做到这一点。

这是我的显示配置的 xrandr 输出:

$ xrandr
Screen 0: minimum 320 x 200, current 2960 x 1050, maximum 8192 x 8192
DVI-0 connected 1680x1050+0+0 (normal left inverted right x axis y axis) 473mm x 296mm
   1680x1050      60.0*+
   [some lines cut]
VGA-0 connected 1280x1024+1680+26 (normal left inverted right x axis y axis) 376mm x 301mm
   1280x1024      60.0 +   75.0*
   [some more lines cut]

我想用Python获取这些值,就像这样:

monitors = get_monitors()
print monitors[0].width # out: 1680
print monitors[1].width # out: 1280
print monitors[0].x_position # out: 0
print monitors[1].x_position # out: 1680

当尝试通过 Python-XLib(或其他库,如 pyGTK 和 pygame)获取信息时,似乎所有监视器都始终作为一个显示器进行处理。例如,这是我迄今为止使用 XLib 得到的:

import Xlib
import Xlib.display

display = Xlib.display.Display(':0')

print display.screen_count()        # output: 1
root = display.screen().root
print root.get_geometry().width     # output: 2960 -> no way to get width of single monitor?
print root.get_geometry().height    # output: 1050

但正如我所说,我更喜欢一种更干净的方法,而无需解析控制台输出。 难道真的没有办法在不解析 xrandr 输出的情况下使用 Python 获取(详细)显示信息吗?

python xlib xrandr
5个回答
13
投票

xrandr
只是一个从命令行访问“RandR”X11扩展的客户端。您可以直接从 Python-Xlib 访问该功能。 这里是一个示例(来自Python-Xlib自己的代码!)。

以防万一 URL 再次更改,这里有一小段代码可以帮助我们获取显示模式。我们需要创建窗口(大小等无关紧要):

from __future__ import print_function
from Xlib import X, display
from Xlib.ext import randr

d = display.Display()
s = d.screen()
window = s.root.create_window(0, 0, 1, 1, 1, s.root_depth)

然后我们就可以使用它来查询屏幕资源了。例如,按照OP的例子:

res = randr.get_screen_resources(window)
for mode in res.modes:
    w, h = mode.width, mode.height
    print("Width: {}, height: {}".format(w, h))

在我的电脑中我得到:

$ python minimal.py 
Xlib.protocol.request.QueryExtension
Width: 1600, height: 900
Width: 1440, height: 900
Width: 1360, height: 768
Width: 1360, height: 768
Width: 1152, height: 864
Width: 1024, height: 768
Width: 800, height: 600
Width: 800, height: 600
Width: 640, height: 480

5
投票

最新片段。它从所有连接的显示器中提取具有当前分辨率的所有模式。

from Xlib import display
from Xlib.ext import randr

def find_mode(id, modes):
   for mode in modes:
       if id == mode.id:
           return "{}x{}".format(mode.width, mode.height)

def get_display_info():
    d = display.Display(':0')
    screen_count = d.screen_count()
    default_screen = d.get_default_screen()
    result = []
    screen = 0
    info = d.screen(screen)
    window = info.root

    res = randr.get_screen_resources(window)
    for output in res.outputs:
        params = d.xrandr_get_output_info(output, res.config_timestamp)
        if not params.crtc:
           continue
        crtc = d.xrandr_get_crtc_info(params.crtc, res.config_timestamp)
        modes = set()
        for mode in params.modes:
            modes.add(find_mode(mode, res.modes))
        result.append({
            'name': params.name,
            'resolution': "{}x{}".format(crtc.width, crtc.height),
            'available_resolutions': list(modes)
        })

    return result

print(get_display_info())

2
投票

您的代码正在获取有关 X11 屏幕的信息。您正在寻找真实+虚拟 (X11) 显示器。所以试试这个:

import Xlib.display

# grab default X11 display (from $DISPLAY environment var)    
display = Xlib.display.Display()

# get first and almost always the only X11 screen of this X11 display
screen = display.screen()

# the root window of this screen provides the info we need
root = screen.root

# iterate over the X11 monitors; often equal real monitors
# will include virtual X11 monitors
for m in root.xrandr_get_monitors().monitors:
  connector = display.get_atom_name(m.name)
  print(f'{connector}, {m.width_in_pixels}x{m.height_in_pixels}, '\
        f'x={m.x}, y={m.y}')

给我退货:

HDMI-0, 1920x1080, x=0, y=1050
DVI-0, 1680x1050, x=1920, y=1050
DVI-1, 1680x1050, x=0, y=0

请注意,“显示”和“屏幕”是 X11 技术术语,与现实世界的使用无关。 “监视器”通常是指真实的东西 - 不过,在 X11 中,您可以使用 Xrandr 将多个监视器连接到虚拟监视器中......

  • X11显示是指整个X-server
    • 同一主机上的不同 X11 显示 → 可能是不同的用户
    • 用户拥有的所有键盘和鼠标(通常各一个)都属于一台 X11 显示器
  • 如今每个 X11 显示器一个 X11 屏幕
    • 每个 X11 显示器多个 X11 屏幕实际上指的是一个已经消失的概念
      • 想象一下,其他人完全共享鼠标和键盘 不同的输出设备
      • 无法在 X11 屏幕之间共享或移动窗口
      • 可以在每个 X11 屏幕上运行不同的窗口管理器
        • 或者至少必须运行完全独立的窗口管理器实例
    • 与 X11 显示屏分开的 X11 屏幕概念,并没有被证明非常有用
  • 因此今天的 X11 屏幕必须涵盖所有显示器
    • 通常是一个大矩形将它们全部包围
  • X11 显示器通常是真正的显示器
    • 应用程序应该认为这些监视器是真实的,而不是其他东西
    • 有时您确实想影响应用程序对监视器的看法
      • 这就是您可以创建虚拟 X11 显示器的原因
      • ...并且可以从此处列出的列表中删除真实的
        • 通常适用于那些构成虚拟显示器一部分的真实显示器
      • 示例:
        • 由无边框监视器图块组成的巨大硬件监视器
        • 桌面上有两个无边框显示器,用户真的只想要一个跨越两个显示器的“桌面”
  • (X11) EWMH 规范中有一个桌面术语
    • 窗口管理器是否将 X11 屏幕视为一个桌面取决于窗口管理器
    • ...或者如果它将每个显示器视为桌面
    • 想要在 (X11 EWMH) 桌面之间移动其窗口的 X11 应用程序(“客户端”)可能需要针对不同的窗口管理器采取不同的做法
    • 但应用程序大多将其留给窗口管理器和用户全屏显示或通过(X11 EWMH)桌面或显示器组织它们

0
投票

您只需要pip3安装Xlib并使用此代码来获取信息:

from Xlib import display
d = display.Display()
screen_count = d.screen_count()
default_screen = d.get_default_screen()
for screen in range(0,screen_count):
    info = d.screen(screen)
    print("Screen: %s. Default: %s" % (screen, screen==default_screen))
    print("Width: %s, height: %s" % (info.width_in_pixels,info.height_in_pixels))

您可以在此处找到从那里获得的属性列表: http://python-xlib.sourceforge.net/doc/html/python-xlib_16.html#SEC15

使用python Xlib库而不是调用命令行。他们都做同样的事情。


-1
投票

要获取窗口中的显示计数,您可以使用

import win32api
print(len(win32api.EnumDisplayMonitors()))
© www.soinside.com 2019 - 2024. All rights reserved.