为什么 kivy 在与 python 的 "elevate()" 函数一起使用时会失败?

Why does kivy fail when used with python's "elevate()" function?

使用elevate模块中的elevate()函数时,kivy无法启动。相关部分代码如下:

from elevate import elevate
from os import environ

environ['DISPLAY'] = ':0'
environ['KIVY_WINDOW'] = 'sdl2'

elevate()

稍后,我给出了一个 kivy 应用程序的代码并尝试 运行 它。这样做时我收到以下错误:

[INFO   ] [Logger      ] Record log in /root/.kivy/logs/kivy_20-10-04_91.txt
[INFO   ] [Kivy        ] v2.0.0rc3, git-20c14b2, 20200615
[INFO   ] [Kivy        ] Installed at "/usr/local/lib/python3.8/dist-packages/Kivy-2.0.0rc3-py3.8-linux-x86_64.egg/kivy/__init__.py"
[INFO   ] [Python      ] v3.8.5 (default, Aug  2 2020, 15:09:07) 
[GCC 10.2.0]
[INFO   ] [Python      ] Interpreter at "/usr/bin/python3"
[INFO   ] [Factory     ] 185 symbols loaded
[INFO   ] [Image       ] Providers: img_tex, img_dds, img_sdl2, img_pil (img_ffpyplayer, img_gif ignored)
[INFO   ] [KivyMD      ] 0.104.2.dev0, git-f0a8217, 2020-09-27 (installed at "/usr/local/lib/python3.8/dist-packages/kivymd-0.104.2.dev0-py3.8.egg/kivymd/__init__.py")
[INFO   ] [Text        ] Provider: sdl2
[INFO   ] [Window      ] Provider: sdl2
No protocol specified
[CRITICAL] [Window      ] Unable to find any valuable Window provider. Please enable debug logging (e.g. add -d if running from the command line, or change the log level in the config) and re-run your app to identify potential causes
sdl2 - RuntimeError: b'No available video device'
  File "/usr/local/lib/python3.8/dist-packages/Kivy-2.0.0rc3-py3.8-linux-x86_64.egg/kivy/core/__init__.py", line 70, in core_select_lib
    cls = cls()
  File "/usr/local/lib/python3.8/dist-packages/Kivy-2.0.0rc3-py3.8-linux-x86_64.egg/kivy/core/window/window_sdl2.py", line 152, in __init__
    super(WindowSDL, self).__init__()
  File "/usr/local/lib/python3.8/dist-packages/Kivy-2.0.0rc3-py3.8-linux-x86_64.egg/kivy/core/window/__init__.py", line 982, in __init__
    self.create_window()
  File "/usr/local/lib/python3.8/dist-packages/Kivy-2.0.0rc3-py3.8-linux-x86_64.egg/kivy/core/window/window_sdl2.py", line 287, in create_window
    self.system_size = _size = self._win.setup_window(
  File "kivy/core/window/_window_sdl2.pyx", line 112, in kivy.core.window._window_sdl2._WindowSDL2Storage.setup_window
  File "kivy/core/window/_window_sdl2.pyx", line 74, in kivy.core.window._window_sdl2._WindowSDL2Storage.die

[CRITICAL] [App         ] Unable to get a Window, abort.

我该如何解决这个问题?我已尝试设置 environ['DISPLAY'] = '*:0',但收到相同的错误,只是没有“找不到协议”。请注意,这是一个应用程序,它必须 运行 在更多的机器上,而不仅仅是我自己的,所以我需要一个可以放入代码中的修复程序,而不是使用 [=27] 运行 的一次性命令=].

我还应该提一下,当我 运行 sudo python3 main.py 时,该应用程序运行良好。只有当我使用 python3 main.py(这会导致 elevate() 函数提示我登录)时,我才会收到此错误。

失败的原因是切换到root后,没有设置DISPLAYXAUTHORITY环境变量。您可以设置这些来解决该问题。这是一个可以做到这一点的示例:

def is_root():
    return os.getuid() == 0

file_name = os.path.join(gettempdir(), 'f.txt')
if not is_root():
    print('saving DISPLAY:', os.environ['DISPLAY'])
    print('saving XAUTHORITY:', os.environ['XAUTHORITY'])
    with open(file_name, mode='w') as f:
        f.write(os.environ['DISPLAY'] + ' ' + os.environ['XAUTHORITY'])
else:
    with open(file_name) as f:
        txt = f.readline()
    tokens = txt.split()
    os.environ['DISPLAY'] = tokens[0]
    os.environ['XAUTHORITY'] = tokens[1]
    print('restored DISPLAY:', os.environ['DISPLAY'])
    print('rstored XAUTHORITY:', os.environ['XAUTHORITY'])
    os.remove(file_name)

这只是概念验证。您应该考虑 file_name 的安全性和选择。显然,这仅适用于 posix 系统,不适用于 Windows。我考虑过尝试使用 python tempfile 模块,但我相信任何 tempfile 写的内容都会在您有机会阅读之前被删除。我还没有测试过。