如何在 Azure Devops 上的 ubuntu 图像中为 matplotlib 使用 TkAgg 后端?

How to use TkAgg backend for matplotlib in ubuntu image on Azure Devops?

我的 matplotlib 代码在 Azure DevOps 中的 ubuntu 个虚拟机中失败;而相同的代码适用于 Azure Windows VM 和我自己的 ubuntu VM。这个简化的 Azure 管道 YAML 重现了这个问题:

trigger:
- none

pool:
  vmImage: ubuntu-18.04

variables:
  python.version: '3.7'

steps:
- task: UsePythonVersion@0
  inputs:
    versionSpec: '$(python.version)'
    addToPath: true
  displayName: 'Use Python $(python.version)'

- bash: |
    pip install --upgrade pip &&
    pip install matplotlib &&
    pip list
  displayName: 'Install required packages'

- bash: |
    sudo apt-get install python3-tk
  displayName: 'Install Tcl/Tk GUI toolkit'

- task: PythonScript@0
  inputs:
    scriptSource: inline
    script: |
      import tkinter
      print('Tcl/Tk version: {}'.format(tkinter.Tcl().eval('info patchlevel')))
  displayName: 'Report Tcl/Tk version'

- task: PythonScript@0
  inputs:
    scriptSource: inline
    script: |
      import matplotlib
      import matplotlib.pyplot as plt
      
      print("Using: " + matplotlib.get_backend())
      
      plt.rcParams['toolbar'] = 'toolmanager'
      
      fig = plt.figure()
      ax = fig.add_subplot(111)
      
      tm = fig.canvas.manager.toolmanager
      tm.remove_tool('help')
  displayName: 'Remove help button from toolbar'

当管道运行时,它报告虚拟机上安装了 matplotlib 3.5.2 和 Tcl/Tk 版本 8.6.8。但是最后的任务失败了:

/home/vsts/work/_temp/5e3dce70-dff2-405e-b5ef-8dac88d22243.py:6: UserWarning: Treat the new Tool classes introduced in v1.5 as experimental for now; the API and rcParam may change in future versions.
  plt.rcParams['toolbar'] = 'toolmanager'
/home/vsts/work/_temp/5e3dce70-dff2-405e-b5ef-8dac88d22243.py:13: UserWarning: ToolManager does not control tool help
  tm.remove_tool('help')
Using: agg
Traceback (most recent call last):
  File "/home/vsts/work/_temp/5e3dce70-dff2-405e-b5ef-8dac88d22243.py", line 13, in <module>
    tm.remove_tool('help')
  File "/opt/hostedtoolcache/Python/3.7.12/x64/lib/python3.7/site-packages/matplotlib/backend_managers.py", line 210, in remove_tool
    tool.destroy()
AttributeError: 'NoneType' object has no attribute 'destroy'

请注意,matplotlib 的打印后端是 agg 而不是 TkAgg。在 运行 sudo apt-get install python3-tk 之后,相同的 Python 代码在我自己的 ubuntu 18.04 VM 中工作,然后代码在那里报告 TkAgg

那么,为什么这在 Azure DevOps 的 ubuntu 中不起作用? Tcl/Tk 似乎未在 ubuntu VM 中正确安装或配置。

更新 如果我在 import matplotlib 之后插入 matplotlib.use('TkAgg'),那么输出会显示 Using: TkAgg。但是我在 fig = plt.figure() 行收到此错误:

ImportError: Cannot load backend 'TkAgg' which requires the 'tk' interactive framework, as 'headless' is currently running

我也试过设置环境变量 DISPLAY 和 MPLBACKEND,但无济于事。

事实证明,我需要为 Tcl/Tk 创建虚拟显示才能在远程 Azure DevOps VM 上正常工作。我使用了 Xvfb, as that's already installed on the ubuntu 18.04 agent (as per here)。您还需要设置 DISPLAY 环境变量以指向虚拟显示器。以下是最终对我有用的方法:

trigger:
- none

pool:
  vmImage: ubuntu-18.04

variables:
  python.version: '3.7'

steps:
- task: UsePythonVersion@0
  inputs:
    versionSpec: '$(python.version)'
    addToPath: true
  displayName: 'Use Python $(python.version)'

- bash: |
    pip install matplotlib &&
    pip list
  displayName: 'Install required packages'

- task: PythonScript@0
  inputs:
    scriptSource: inline
    script: |
      import tkinter
      print('Tcl/Tk version: {}'.format(tkinter.Tcl().eval('info patchlevel')))
  displayName: 'Report Tcl/Tk version'

- bash: |
    Xvfb :1 -screen 0 640x480x16 &
  displayName: 'Create virtual display'

- bash: |
    echo "##vso[task.setvariable variable=DISPLAY]:1.0"
  displayName: 'Set DISPLAY'

- task: PythonScript@0
  inputs:
    scriptSource: inline
    script: |
      import matplotlib
      import matplotlib.pyplot as plt
      
      print("Using: " + matplotlib.get_backend())
      
      plt.rcParams['toolbar'] = 'toolmanager'
      
      fig = plt.figure()
      ax = fig.add_subplot(111)
      
      tm = fig.canvas.manager.toolmanager
      tm.remove_tool('help')
  displayName: 'Remove help button from toolbar'

最终任务的输出是:

/home/vsts/work/_temp/b7d18caa-56d0-4438-9d8c-b6bbaa04935b.py:10: UserWarning: Treat the new Tool classes introduced in v1.5 as experimental for now; the API and rcParam may change in future versions.
  plt.rcParams['toolbar'] = 'toolmanager'
Using: TkAgg

注意 matplotlib 自动检测 TkAgg 后端;你不必告诉它使用它。有两个问题让我很困惑:Xvfb 以大写字母 X 开头;并确保你 运行 它在后台!