如何使用Python获取任务栏的大小和位置

我正在用Python开发一个模块,想找出一种在没有用户交互的情况下获取监视器分辨率减去任务栏的方法,并且最好不会干扰到视觉界面。如果我能够获取任务栏的大小和位置,就可以简单地将其从监视器分辨率中减去。然而,我似乎找不到方法。
在Windows中,我可以使用win32api模块并使用GetMonitorInfo来获取监视器分辨率。然而,我还没有找到在Ubuntu或Linux中实现这一点的方法。如果您也知道如何在macOS中实现这一点,那也很好,但对于这个问题来说并不是必需的。
我希望尽可能支持多种桌面环境,但我意识到这可能很困难,因为每个桌面环境可能有自己的解决方案。考虑到这一点,我更倾向于从以下桌面环境开始:
- KDE - XFCE - Gnome - Cinnamon
如果需要使用外部程序来实现,我更愿意使用与桌面环境或操作系统捆绑的工具。

2监视器分辨率很容易获得,可以通过Shell工具或API实现。我们还可以使用一些手动方法。但是正如之前提到的,任务栏大小可能是特定于桌面环境的,如果您打算在没有用户互动的情况下计算它。这真的取决于您对应用程序的要求。 - Sergiy Kolodyazhnyy
2这实际上是一件棘手的事情。最可靠的方法似乎是从xprop获取所声称的空间(支撑)值,看起来像这样:_NET_WM_STRUT(CARDINAL) = 0, 0, 0, 52_NET_WM_STRUT_PARTIAL(CARDINAL) = 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 1919 ,但特别是在多显示器设置中,并非所有面板都能返回正确的跨屏布局值:https://bugs.launchpad.net/plank/+bug/1755731 - Jacob Vlijm
@SergiyKolodyazhnyy 我已经编辑了我的问题以进行澄清。 - Christian Sirolli
@ChristianSirolli 我昨天看到了。我会进一步调查,看看能做些什么。 - Sergiy Kolodyazhnyy
@ChristianSirolli 请在几天后提醒我一下,可能是周三。帖子经常会被忽略,用户也会忘记回访。谢谢。 - Sergiy Kolodyazhnyy
@SergiyKolodyazhnyy 今天是星期三。 :) - Christian Sirolli
@ChristianSirolli 谢谢你的提醒。我已经找到了一些东西,可能会在今天或明天发布(所以请在接下来的48小时内再次查看),并附上一些代码示例。敬请关注! - Sergiy Kolodyazhnyy
2嗨,@ChristianSirolli,我已经发布了一个回答,其中包含了一些例子和对潜在问题的讨论。我希望稍后能够更新它,并找出其他实现所需结果的方法。如果你有任何其他问题和建议来改进这个回答,请告诉我。 - Sergiy Kolodyazhnyy
1个回答

我想找到一种方法,可以在没有任何用户交互的情况下获取监视器分辨率减去任务栏的数值,并且最好不会干扰可视界面。
这就是所要求的内容,我将专注于根据我所找到的信息来回答。获取任务栏的大小和位置是一个最初的想法,但正如在评论中所讨论的那样,这将很难实现,因为需要了解大量的信息——运行哪个面板或停靠栏,它们是否设置了_NET_WM_STRUT属性,它们是否通过GSettings数据库或其他方法公开这些信息。我相信这不是不可能的,但确实很困难。
NET_WORKAREA属性
根据规范(specifications),对于根窗口,_NET_WORKAREA定义如下:
_NET_WORKAREA,x,y,width,height CARDINAL/32
此属性必须由窗口管理器在计算每个桌面的工作区时设置。包含每个桌面的几何图形。这些几何图形是相对于每个桌面上的视口指定的,并指定一个完全包含在视口内的区域。桌面应用程序应该使用工作区来适当地放置桌面图标。
窗口管理器应通过获取当前页面减去由客户端窗口上设置的_NET_WM_STRUT或_NET_WM_STRUT_PARTIAL属性指示的停靠窗口和面板窗口占用的空间来计算此空间。
有几点需要注意:
  • 符合这些规范的窗口管理器必须设置此属性,因此在像XFCE、Budgie、Unity、GNOME、KDE等桌面环境中,即使是不使用任何实验性窗口管理器的任何官方Ubuntu版本也会设置此属性;换句话说,这在受支持的桌面上可以正常工作。
  • 这个计算可用的宽度和高度,但也计算偏移量相对于整个显示器的左上角开始(在双屏幕的情况下,您最左边的屏幕的左上角将是起始点)。
  • _NET_WM_STRUT_NET_WM_STRUT_PARTIAL是由坞站和面板设置的属性。这意味着,如果面板或坞站没有设置它,它们基本上会像一个普通的窗口一样,只是位于所有其他窗口之上(可能会让用户感到恼火)。也就是说,对我们来说已经足够好了。我在Ubuntu 18.04上的Cinnamon、LXQt和XFCE上测试过这个属性。在所有情况下,坞站和面板都正确地通知了窗口管理器它们的支架区域。这意味着,在99%的情况下,_NET_WORKAREA对您有效。

可以通过在根窗口上使用xprop实用程序来查询此属性。

$ xprop -root _NET_WORKAREA
_NET_WORKAREA(CARDINAL) = 0, 32, 3120, 974, 0, 32, 3120, 974

这是我双显示器设置的输出,两个显示器顶部对齐,有一个底栏和顶部面板。如果你注意到只有前四个值是重要的,其他四个是重复的。特别感兴趣的是3120974。那就是我们感兴趣的值。前两个告诉我们桌面最左上角的位置在哪里。我的顶部面板占用了32个点,因此桌面最左上角从最左边开始,向下偏移32个点。对于窗口来说,从底部偏移通常不重要-它们会填充可用的工作区域。
如果我把底栏放在主显示器的右侧,你会注意到宽度减小了,但高度增加了。
$ xprop -root _NET_WORKAREA
_NET_WORKAREA(CARDINAL) = 0, 32, 3059, 1018, 0, 32, 3059, 1018

高度变化为44个点。再加上32个点(顶部面板窗口的大小),你将得到1050,正好是我的桌面的几何形状(请注意,在没有码头时的3120大小,因此使用了完整的桌面宽度):
$ xprop -root _NET_DESKTOP_GEOMETRY
_NET_DESKTOP_GEOMETRY(CARDINAL) = 3120, 1050

Python和xprop

上面的xprop命令附带在大多数桌面环境中,属于x11-utils软件包,所以如果你的目标是主要基于Debian的系统,你应该始终可以使用它。我会使用Python的subprocess,并利用subprocess.check_output()subprocess.run()命令,并通过re模块解析输出。

>>> import re,subprocess
>>> out = subprocess.check_output(['xprop','-root','_NET_WORKAREA'])
>>> workarea_tokens = re.split('=|,',out.decode())
>>> workarea_tokens[1:5]
[' 0', ' 32', ' 3059', ' 1018']

Python和Gdk

另一种(通常是我首选的方式)是利用与其他GNOME项目库密切相关的Gdk库。如果您正在使用Gtk开发桌面应用程序,那么您已经拥有必要的工具,为什么不使用它们呢?棘手的部分是您必须计算每个单独监视器的工作区域。

#!/usr/bin/env python3
import gi
gi.require_version('Gtk','3.0')
gi.require_version('Gdk','3.0')
from gi.repository import Gdk,Gtk,GdkX11

display = Gdk.Display().get_default()
for i in range(display.get_n_monitors()):
    monitor = display.get_monitor(i)
    w_area  = monitor.get_workarea()
    print(w_area.x, w_area.y,
          w_area.width, w_area.height)

这个程序的结果是:
$ python3  display_area.py
1440 32 1619 1018
0 0 1440 900
# width of two monitors with dock on right adds up to _NET_WORKAREA
$ echo $((1619+1440))
3059
$ xprop -root _NET_WORKAREA
_NET_WORKAREA(CARDINAL) = 0, 32, 3059, 1018, 0, 32, 3059, 1018

问题
现在,记住我提到过我有双显示器吗?这样的设置打开了许多可能性,但也揭示了某些奇怪的行为。
# added xfce4-panel at the bottom of second screen, no change in height - 900
$ python3 display_area.py ; xprop -root _NET_WORKAREA
1440 32 1619 1018
0 0 1440 900
_NET_WORKAREA(CARDINAL) = 526, 32, 2533, 1018, 526, 32, 2533, 1018
# stopped xfce4-panel process, same height - 900
$ python3 display_area.py ; xprop -root _NET_WORKAREA
1440 32 1619 1018
0 0 1440 900
_NET_WORKAREA(CARDINAL) = 0, 32, 3059, 1018, 0, 32, 3059, 1018
# added xfce4-panel to the top of second monitor
$ python3 display_area.py ; xprop -root _NET_WORKAREA
1440 49 1619 1001
0 0 1440 900
_NET_WORKAREA(CARDINAL) = 0, 49, 3059, 1001, 0, 49, 3059, 1001

记住,我在一个屏幕上有一个面板和一个停靠栏,并且为了测试,在另一个屏幕上添加了xfce4-panel。Gdk的错误结果是第二个监视器的工作区没有改变。而且xprop也报告了错误的数字 - 在第一个输出中,从左边偏移的526个点根本不正确。然而,在最后的输出中,垂直偏移增加了,这是好事 - xfce4-panel添加的额外宽度应该被添加到总数中,可能有帮助的是我的两个监视器顶部对齐。
换句话说,如果您的应用程序计划确定没有停靠栏和面板的区域,这些方法在单个监视器设置下效果最好。在双显示器设置下 - 结果可能不一致。请注意,我正在使用Metacity窗口管理器,可能归因于此导致了错误。然而,目前我没有办法测试其他具有双显示器设置的WM,所以我将把这一部分留待以后再说。

待办事项(某天)

  • 用C或Python编写libx11示例

  • 使用替代窗口管理器进行双显示器测试

  • 查找停靠栏和面板的位置


脚注

1 功能未记录,取决于用户的耐心程度。