为什么这可以解决 matplotlib 的 'no $DISPLAY environment' 问题?

Why does this solve the 'no $DISPLAY environment' issue with matplotlib?

当 运行在我的桌面 PC 中使用 matplotlib 库的代码时,我使用以下行没有问题:

import matplotlib.pyplot as plt

在代码的下方,这是我实际使用绘图函数的地方。

如果我 运行 服务器中的代码虽然只有在我导入 matplotlib 之前 并强制它使用 Agg 后端。即,我必须将以下行添加到代码的开头:

import matplotlib
# Force matplotlib to not use any Xwindows backend.
matplotlib.use('Agg')

(参见 this answer where this is explained). Otherwise the code will crash with TclError: no display name and no $DISPLAY environment variable (see this question 示例)。

问题是:为什么我需要这样做?该解决方案非常有效,但我不知道为什么我不必在我的台式 PC 上执行此操作,但我绝对必须在服务器中的代码 运行s 时执行此操作。

X11 遵循 client/server 模型,其中 X 服务器接受来自客户端应用程序(例如交互式 matplotlib 会话)的图形输出请求,并从键盘、鼠标等发回用户输入。为了这个为了工作,客户端应用程序需要知道将它们的请求发送到哪个 X 服务器。这是由 $DISPLAY 环境变量控制的。在连接到远程 X 会话(例如通过 SSH 连接)的情况下,远程会话中的 $DISPLAY 变量需要指向本地 X 服务器。

$DISPLAY 变量的结构如下:

hostname:displaynumber.screennumber

可能并非所有部分都存在 - 本地会话通常会省略主机名,如果只有一个屏幕,屏幕编号也会被省略。在笔记本电脑上的本地终端会话中,我的 $DISPLAY 看起来像这样:

alistair@laptop:~$ echo $DISPLAY
:0

只要远程服务器也支持 X11,就可以在远程机器上打开图形 windows 并使用 X11 forwarding 让它们出现在您的本地机器上。对于 SSH 连接,您可以通过传递 -X(或 -Y)标志来执行此操作。

例如:

alistair@laptop:~$ ssh -X alistair@workstation.address.co.uk
alistair@workstation:~$ echo $DISPLAY
localhost:10.0

远程 SSH 服务器应该在您打开连接时适当地设置 $DISPLAY 变量。在这种特殊情况下,localhost:10.0 实际上是远程计算机上的 'proxy' X11 服务器 运行,它在显示器 10 上侦听并通过 SSH 连接将命令中继到本地 X 服务器(take a look at this 如果您对详细信息感兴趣)。

现在您应该能够启动远程 IPython 会话,使用交互式后端导入 matplotlib,并创建绘图 windows,然后该绘图将出现在您的本地计算机上。由于您的 keyboard/mouse 输入和显示输出现在通过加密网络连接传递,因此绘图 windows 的响应速度将低于您在本地会话中的响应速度。

另一个警告:如果你有一个 IPython 会话打开一个交互式 matplotlib 会话 运行 就不可能在不终止 IPython 进程的情况下关闭 SSH 连接。我有时也会在启动导入 matplotlib 的长 运行 进程之前调用 matplotlib.use("Agg") - 这样我就可以在不终止进程的情况下与远程服务器断开连接。