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

17 投票
5 回答
18107 浏览
提问于 2025-04-17 09:24

我在使用Ubuntu系统,想要获取连接的显示器数量、它们当前的分辨率,以及如果可能的话,它们之间的位置关系。因为我不太喜欢解析xrandr这个命令行工具的输出,至少在我不需要的时候,我希望能用Python-XLib或者其他类似的Python方法来实现。

这是我显示配置的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

但是正如我所说的,我更希望有一种更简洁的方法,而不需要解析命令行的输出。真的没有办法用Python获取(详细的)显示信息,而不需要解析xrandr的输出吗?

5 个回答

1
import Xlib.display

display = Xlib.display.Display()
root = display.screen().root
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

注意,“display”和“screen”是X11的专业术语,它们和我们日常用语不太一样。“Monitor”通常指的是实际的显示器——不过在X11中,你可以用Xrandr把多个显示器合成一个虚拟显示器……

  • X11的显示指的是整个X服务器
    • 同一台主机上的不同X11显示 → 可能是不同的用户
    • 一个用户所用的所有键盘和鼠标(通常各一个)都属于一个X11显示
  • 现在每个X11显示通常只有一个X11屏幕
    • 多个X11屏幕的概念其实已经不再流行了
    • 想象一下,一个鼠标和键盘被完全不同的输出设备共享
    • 不能在X11屏幕之间共享或移动窗口
    • 可以在每个X11屏幕上运行不同的窗口管理器
      • 或者至少必须运行完全独立的窗口管理器实例
    • 这并没有证明非常有用
  • 现在的X11屏幕必须包含所有显示器
    • 通常是一个大矩形把它们都包起来
  • 有一个来自(X11) EWMH规范的桌面术语
    • 是否把X11屏幕视为一个桌面取决于窗口管理器
    • ……或者它是否认为每个显示器都是一个桌面
    • 想要在(X11 EWMH)桌面之间移动窗口的X11应用(“客户端”)可能需要根据不同的窗口管理器采取不同的方法
    • 但大多数应用都把全屏或在(X11 EWMH)桌面或显示器上组织窗口的事情留给窗口管理器和用户来处理
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())
13

xrandr 是一个可以通过命令行访问 "RandR" X11 扩展的工具。你也可以直接通过 Python-Xlib 来使用这些功能。这里有一个示例(来自 Python-Xlib 自己的代码!)。

为了防止网址再次变化,这里有一段简单的代码,可以帮助我们获取显示模式。我们需要创建一个窗口(窗口的大小等都无所谓):

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)

然后我们可以使用这个窗口来查询屏幕资源。例如,按照提问者的例子:

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

撰写回答