与python的“ elevate”函数一起使用时,为什么kivy失败?

问题描述

使用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',但收到相同的错误,除非没有“未找到协议”。请注意,这是一个必须在比我自己的机器更多的机器上运行的应用程序,因此我需要可以在代码中放入一个修复程序,而不是使用bash一次执行命令。

我还应该提到,当我运行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在您有机会阅读之前都会被删除。我还没有测试过。