[kivy] "XDG_RUNTIME_DIR not set" Wo muss ich das machen?

Hier werden alle anderen GUI-Toolkits sowie Spezial-Toolkits wie Spiele-Engines behandelt.
Benutzeravatar
Dennis89
User
Beiträge: 219
Registriert: Freitag 11. Dezember 2020, 15:13

Samstag 1. Mai 2021, 22:39

Guten Abend,

ich habe mal wieder ein Problem. Ich habe ein Programm mit Kivy geschrieben und das lief die letzte Zeit auf einem Raspberry Pi 4 mit Raspberry OS with Desktop. Neulich bin ich über einen Artikel von "c't" gestolpert in dem stand, das Kivy einiges mit bringt um die GUI auch auf einem Betriebssystem ohne Desktop anzuzeigen. Ich hoffe ich habe das richtig verstanden. Hier der Artikel:
https://www.heise.de/select/ct/2017/7/1490629104678785

Habe mir also das Raspberry OS-lite installiert und lasse das Kivy-Programm bei einem Neustart mit einem Crontabeintrag starten. Der Eintrag sieht so aus:

Code: Alles auswählen

@reboot /home/pi/Python/MusicBox_GUI.py -c kivy:log_level:debug > /home/pi/musicbox.log 2>&1
Nach einem Neustart, startet meine Kivy GUI aber nicht. Der Log-Eintrag sieht so aus:

Code: Alles auswählen

[INFO   ] [Logger      ] Record log in /home/pi/.kivy/logs/kivy_21-05-01_20.txt
[INFO   ] [Kivy        ] v2.1.0.dev0
[INFO   ] [Kivy        ] Installed at "/home/pi/.local/lib/python3.7/site-packages/kivy/__init__.py"
[INFO   ] [Python      ] v3.7.3 (default, Jan 22 2021, 20:04:44) 
[GCC 8.3.0]
[INFO   ] [Python      ] Interpreter at "/usr/bin/python3"
[INFO   ] [Logger      ] Purge log fired. Processing...
[INFO   ] [Logger      ] Purge finished!
[INFO   ] [Factory     ] 189 symbols loaded
[DEBUG  ] [Cache       ] register <kv.resourcefind> with limit=None, timeout=60
[DEBUG  ] [Cache       ] register <kv.lang> with limit=None, timeout=None
[DEBUG  ] [Cache       ] register <kv.image> with limit=None, timeout=60
[DEBUG  ] [Cache       ] register <kv.atlas> with limit=None, timeout=None
[INFO   ] [Image       ] Providers: img_tex, img_dds, img_sdl2, img_pil (img_ffpyplayer ignored)
[DEBUG  ] [Cache       ] register <kv.texture> with limit=1000, timeout=60
[DEBUG  ] [Cache       ] register <kv.shader> with limit=1000, timeout=3600
[INFO   ] [Window      ] Provider: sdl2
error: XDG_RUNTIME_DIR not set in the environment.
* failed to add service - already in use?
Mit

Code: Alles auswählen

printenv | grep RUNTIME
erhalte ich

Code: Alles auswählen

XDG_RUNTIME_DIR=/run/user/1000
Könnt ihr mit bitte sagen wo ich Kivy das fehlende Verzeichnis mitteilen muss?

In dier 'config.ini' habe ich nichts gefunden. In der Kivy Dokumentation steht zwar was davon, wie man das 'envoronment' kontrollieren kann, aber ich weis nicht wie ich das auf meinen Fall anwenden muss. Das wäre hier beschrieben:
https://kivy.org/doc/stable/guide/environment.html

Ansonsten findet man schon einige Beiträge im Internet, mit denen bin ich auch so weit gekommen und habe einige andere Fehler beseitigen können. Nur die Lösung dazu habe ich nicht gefunden oder übersehen.

Zum Schluss noch der Code meines Kivy-Programms:

Code: Alles auswählen

#!/usr/bin/env python3

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.dropdown import DropDown
from kivy.uix.slider import Slider
from kivy.uix.button import Button
from kivy.uix.behaviors import ButtonBehavior
from kivy.uix.image import Image
from kivy.uix.widget import Widget
from InternetRadio import Radio
from MusicPlayer import PlaylistPlayer
from pathlib import Path
from time import sleep
from subprocess import run
from loguru import logger

RADIOSENDER_PATH = Path('/media/MusicBox/Senderliste.csv')
PLAYLIST_PATH = Path('/media/MusicBox/Playlist/')


class MainScreen(Screen):
    pass


class RadioScreen(Screen):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.radiostation = Radio()

    def create_radioplayer(self):
        try:
            for sender in self.radiostation.read_station(RADIOSENDER_PATH):
                sender_button = Button(text=sender, size_hint_y=None, height=44)
                sender_button.bind(on_release=lambda sender_button: self.ids.DropDownMenu.select(sender_button.text))
                #
                # Check to add only once the radiostation to DropDownMenu.
                #
                if self.ids.sender_button.text not in self.radiostation.read_station(RADIOSENDER_PATH):
                    self.ids.DropDownMenu.add_widget(sender_button)
                else:
                    break
        except Exception as e:
            logger.exception(e)


    def set_sender(self, sendername):
        self.radiostation.switch_station(sendername)

    def radio_play(self):
        self.radiostation.play_music()

    def radio_stop(self):
        self.radiostation.radio_stop()

    def set_volume(self, volume):
        self.radiostation.set_volume(volume)


class PlaylistScreen(Screen):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.player = PlaylistPlayer()

    def load_playlist(self):
        #
        #       Check to add only once the playlistobjects to DropDownMenu.
        #
        try:
            check_new_folder = Path(f"{PLAYLIST_PATH}/{self.ids.playlist_button.text}")
            if not check_new_folder.is_dir():
                for playlist in PLAYLIST_PATH.iterdir():
                    playlist_button = Button(text=playlist.name, size_hint_y=None, height=44)
                    playlist_button.bind(
                        on_release=lambda playlist_button:
                        self.ids.DropDownPlaylist.select(playlist_button.text)
                    )
                    self.ids.DropDownPlaylist.add_widget(playlist_button)
        except Exception as e:
            logger.exception(e)

    def playlist_player(self, music_folder):
        try:
            music_path = Path(f"{PLAYLIST_PATH}/{music_folder}/")
            self.player.add_playlist(music_path)
        except Exception as e:
            logger.exception(e)

    def player_play(self):
        try:
            self.player.play()
        except Exception as e:
            logger.exception(e)

    def player_next(self):
        self.player.next()

    def player_pause(self):
        self.player.pause()

    def player_previous(self):
        self.player.previous()

    def player_stop(self):
        self.player.stop()

    def player_volume(self, volume):
        self.player.set_volume(volume)


class BluetoothScreen(Screen):
    def bluetooth_on(self):
        try:
            run(['rfkill', 'unblock', 'bluetooth'], check=True)
            sleep(0.5)
            run(['sudo', 'systemctl', 'restart', 'bluetooth.service'], check=True)

        except Exception as e:
            logger.exception(e)


    def bluetooth_off(self):
        try:
            run(['rfkill', 'block', 'bluetooth'], check=True)
        except Exception as e:
            logger.exception(e)


class ShutdownScreen(Screen):
    def musicbox_off(self):
        run(['sudo', 'shutdown', '-h', '0'], check=True)


class AnimWidget(Widget):
    pass


class ImageButton(ButtonBehavior, Image):
    pass


class VolumeSlider(Slider):
    pass


class DropDownMenu(DropDown):
    pass


class DropDownPlaylist(DropDown):
    pass


kv = Builder.load_file("musicbox.kv")


class MusicBox(App):
    def build(self):
        screenmanager = ScreenManager()
        screenmanager.add_widget(MainScreen(name='main'))
        screenmanager.add_widget(RadioScreen(name='internetradio'))
        screenmanager.add_widget(PlaylistScreen(name='playlist'))
        screenmanager.add_widget(BluetoothScreen(name='bluetooth'))
        screenmanager.add_widget(ShutdownScreen(name='shutdown'))
        return screenmanager

def main():
    logger.add("/home/pi/Python/MusicBox.log", format="{time:YYYY-MM-DD at HH:mm:ss} | {level} | {message}")
    MusicBox().run()


if __name__ == '__main__':
    main()
Über eure Hilfe wäre ich sehr dankbar.

Grüße
Dennis
“A ship is always safe at the shore, but that is not what it is built for.”
Benutzeravatar
Dennis89
User
Beiträge: 219
Registriert: Freitag 11. Dezember 2020, 15:13

Freitag 7. Mai 2021, 05:59

Guten Morgen,

ich wollte mal nach fragen ob nicht vielleicht doch einer eine Idee hat?
Wäre toll wenn das doch noch irgendwie klappen könnte.


Danke und Grüße
Dennis
“A ship is always safe at the shore, but that is not what it is built for.”
__deets__
User
Beiträge: 10095
Registriert: Mittwoch 14. Oktober 2015, 14:29

Freitag 7. Mai 2021, 06:43

Diese Verzeichnisse werden von systemd angelegt. Vielleicht reicht es, statt CRON eine Unit zu benutzen. Und sonst die Variable einfach mal auf /tmp zu setzen.
Sirius3
User
Beiträge: 14826
Registriert: Sonntag 21. Oktober 2012, 17:20

Freitag 7. Mai 2021, 07:01

Was passiert denn, wenn Du Dein Programm von der Konsole startest?
__deets__
User
Beiträge: 10095
Registriert: Mittwoch 14. Oktober 2015, 14:29

Freitag 7. Mai 2021, 08:38

Benutzeravatar
Dennis89
User
Beiträge: 219
Registriert: Freitag 11. Dezember 2020, 15:13

Freitag 7. Mai 2021, 20:03

Guten Abend und Danke für eure Antworten.

Wenn ich das Programm manuell starte, also direkt mit angeschlossener Tastatur am Pi, erhalte ich folgende Ausgabe:

Code: Alles auswählen

[INFO   ] [Logger      ] Record log in /home/pi/.kivy/logs/kivy_21-05-07_3.txt
[INFO   ] [Kivy        ] v2.1.0.dev0
[INFO   ] [Kivy        ] Installed at "/home/pi/.local/lib/python3.7/site-packages/kivy/__init__.py"
[INFO   ] [Python      ] v3.7.3 (default, Jan 22 2021, 20:04:44) 
[GCC 8.3.0]
[INFO   ] [Python      ] Interpreter at "/usr/bin/python3"
[INFO   ] [Logger      ] Purge log fired. Processing...
[INFO   ] [Logger      ] Purge finished!
[INFO   ] [Factory     ] 189 symbols loaded
[INFO   ] [Image       ] Providers: img_tex, img_dds, img_sdl2, img_pil (img_ffpyplayer ignored)
[INFO   ] [Window      ] Provider: sdl2
* failed to add service - already in use?
Dabei ist es egal ob ich das Programm über './MusicBox_GUI.py' oder über 'python3 /home/pi/Python/MusicBox_GUI.py' starte.

Wenn ich versuche das Programm mit einem Systemd-Service starten zu lassen, dann passiert auf dem Display gar nichts. Die Ausgabe von 'systemctl status MusicBox.service' enthält einen seltsamen Python-Fehler:

Code: Alles auswählen

pi@raspberrypi:~ $ sudo systemctl status MusicBox.service
* MusicBox.service - Start MusicBox
   Loaded: loaded (/etc/systemd/system/MusicBox.service; static; vendor preset: enabled)
   Active: failed (Result: exit-code) since Fri 2021-05-07 19:37:35 BST; 50s ago
  Process: 384 ExecStart=/home/pi/Python/MusicBox_GUI.py (code=exited, status=1/FAILURE)
 Main PID: 384 (code=exited, status=1/FAILURE)

Mai 07 19:37:35 raspberrypi systemd[1]: Started Start MusicBox.
Mai 07 19:37:35 raspberrypi MusicBox_GUI.py[384]: Traceback (most recent call last):
Mai 07 19:37:35 raspberrypi MusicBox_GUI.py[384]:   File "/home/pi/Python/MusicBox_GUI.py", line 3, in <module>
Mai 07 19:37:35 raspberrypi MusicBox_GUI.py[384]:     from kivy.app import App
Mai 07 19:37:35 raspberrypi MusicBox_GUI.py[384]: ModuleNotFoundError: No module named 'kivy'
Mai 07 19:37:35 raspberrypi systemd[1]: MusicBox.service: Main process exited, code=exited, status=1/FAILURE
Mai 07 19:37:35 raspberrypi systemd[1]: MusicBox.service: Failed with result 'exit-code'.
Kivy ist aber installiert, sonst wäre der Fehler bei den manuellen Versuchen oder den Versuchen mit Cron ja auch gekommen und ich bin mir auch sicher das ich es installiert habe, weil ich mir den Link zur Installation gespeichert habe:
https://matham-kivy.readthedocs.io/en/l ... n-rpi.html

Die *.service Datei sieht so aus:

Code: Alles auswählen

[Unit]
Description=Start MusicBox

[Service]
ExecStart=/home/pi/Python/MusicBox_GUI.py
Ich erhalte die gleiche Fehlermeldung, wenn ich in den 'ExecStart'- Aufruf noch 'python3' voransetze. Also 'ExecStart=python3 /home/pi/Python/MusicBox_GUI.py'

Die *.timer Datei sieht so aus:

Code: Alles auswählen

[Unit]
Description=Start MusicBox

[Timer]
OnBootSec=5
Unit=MusicBox.service

[Install]
WantedBy=multi-user.target
Aus dem Link von @__deets__ konnte ich keine Informationen finden, mit denen ich etwas anfangen kann. Vermutlich habe ich nicht wirklich verstanden, um was es da genau geht und wie das mit mir zusammenhängt.
Was meinst du mit die Variable auf /tmp zu setzen? Den Ordner tmp habe ich und der hat bei mir folgenden Inhalt:

Code: Alles auswählen

pi@raspberrypi:/tmp $ ls -la
insgesamt 32
drwxrwxrwt  8 root root 4096 Mai  7 19:42 .
drwxr-xr-x 18 root root 4096 M�r  4 23:04 ..
drwxrwxrwt  2 root root 4096 Mai  7 19:41 .font-unix
drwxrwxrwt  2 root root 4096 Mai  7 19:41 .ICE-unix
drwx------  3 root root 4096 Mai  7 19:41 systemd-private-d3333b818c524731bdf6cfb9b53c9290-systemd-timesyncd.service-EmskQF
drwxrwxrwt  2 root root 4096 Mai  7 19:41 .Test-unix
drwxrwxrwt  2 root root 4096 Mai  7 19:41 .X11-unix
drwxrwxrwt  2 root root 4096 Mai  7 19:41 .XIM-unix
Ich nutze mittlerweile privat so gut wie nur noch Linux, trotzdem mache ich nur ganz kleine Fortschritte. Ich verstehe gerade nicht, welche Variable und was ich damit genau machen soll.

Vielen Dank nochmals und Grüße
Dennis
“A ship is always safe at the shore, but that is not what it is built for.”
Benutzeravatar
Dennis89
User
Beiträge: 219
Registriert: Freitag 11. Dezember 2020, 15:13

Freitag 7. Mai 2021, 21:09

Nachtrag: Ich hatte keinen User angegeben und das Programm wurde als root ausgeführt. Ich habe die *.service - Datei um 'User=pi' erweitert und erhalte jetzt mit 'systemctl status MusicBox.service' wieder die alte Fehlermeldung:

Code: Alles auswählen

* MusicBox.service - Start MusicBox
   Loaded: loaded (/etc/systemd/system/MusicBox.service; static; vendor preset: enabled)
   Active: failed (Result: exit-code) since Fri 2021-05-07 21:06:48 BST; 40s ago
  Process: 388 ExecStart=/home/pi/Python/MusicBox_GUI.py (code=exited, status=1/FAILURE)
 Main PID: 388 (code=exited, status=1/FAILURE)

Mai 07 21:06:46 raspberrypi MusicBox_GUI.py[388]: [INFO   ] [Python      ] Interpreter at "/usr/bin/python3"
Mai 07 21:06:46 raspberrypi MusicBox_GUI.py[388]: [INFO   ] [Logger      ] Purge log fired. Processing...
Mai 07 21:06:46 raspberrypi MusicBox_GUI.py[388]: [INFO   ] [Logger      ] Purge finished!
Mai 07 21:06:46 raspberrypi MusicBox_GUI.py[388]: [INFO   ] [Factory     ] 189 symbols loaded
Mai 07 21:06:47 raspberrypi MusicBox_GUI.py[388]: [INFO   ] [Image       ] Providers: img_tex, img_dds, img_sdl2, img_pil (img_ffpyplayer ignored)
Mai 07 21:06:48 raspberrypi MusicBox_GUI.py[388]: [INFO   ] [Window      ] Provider: sdl2
Mai 07 21:06:48 raspberrypi MusicBox_GUI.py[388]: error: XDG_RUNTIME_DIR not set in the environment.
Mai 07 21:06:48 raspberrypi MusicBox_GUI.py[388]: * failed to add service - already in use?
Mai 07 21:06:48 raspberrypi systemd[1]: MusicBox.service: Main process exited, code=exited, status=1/FAILURE
Mai 07 21:06:48 raspberrypi systemd[1]: MusicBox.service: Failed with result 'exit-code'.
Grüße
Dennis
“A ship is always safe at the shore, but that is not what it is built for.”
__deets__
User
Beiträge: 10095
Registriert: Mittwoch 14. Oktober 2015, 14:29

Montag 10. Mai 2021, 12:59

Es handelt sich um eine Umgebungsvariable. Die kann man in der Shell oder auch dem Programm setzten.

Und bei dem Link wird ein ggf verwandtes Problem durch einen Library loading wrapper gelöst. Mehr Details kenne ich auch nicht, ich habe das nur durch die Fehlermeldung gefunden.
Benutzeravatar
Dennis89
User
Beiträge: 219
Registriert: Freitag 11. Dezember 2020, 15:13

Montag 10. Mai 2021, 22:03

Guten Abend und danke für deine Antwort.

Ich habe nochmal weiter gesucht und einen Befehl gesucht, wie ich die Variable setzen kann und habe diesen gefunden:

Code: Alles auswählen

echo "XDG_RUNTIME_DIR=/run/user/$(id -u)" >> ~/tmp
Das hat aber leider nichts geändert.

Dann habe ich mein Programm mit folgenden Codezeilen erweitert:

Code: Alles auswählen

import os
os.environ['XDG_RUNTIME_DIR'] = '/run/user/1000'
('os' habe ich einfach mal so aus der Python-Doku übernommen um keine zusätzlichen Fehler einzubauen. Wenn es klappt werde ich es durch 'pathlib' ersetzen.)

Jetzt ist der Fehler mit 'XDG_RUNTIME_DIR' weg! Leider startet Kivy-Programm noch nicht, jetzt sehen die Fehlermeldungen so aus:

Code: Alles auswählen

pi@raspberrypi:~ $ sudo systemctl status MusicBox.service
* MusicBox.service - Start MusicBox
   Loaded: loaded (/etc/systemd/system/MusicBox.service; static; vendor preset: enabled)
   Active: failed (Result: exit-code) since Mon 2021-05-10 21:53:39 BST; 1min 9s ago
  Process: 374 ExecStart=/home/pi/Python/MusicBox_GUI.py (code=exited, status=1/FAILURE)
 Main PID: 374 (code=exited, status=1/FAILURE)

Mai 10 21:53:36 raspberrypi MusicBox_GUI.py[374]: [GCC 8.3.0]
Mai 10 21:53:36 raspberrypi MusicBox_GUI.py[374]: [INFO   ] [Python      ] Interpreter at "/usr/bin/python3"
Mai 10 21:53:36 raspberrypi MusicBox_GUI.py[374]: [INFO   ] [Logger      ] Purge log fired. Processing...
Mai 10 21:53:36 raspberrypi MusicBox_GUI.py[374]: [INFO   ] [Logger      ] Purge finished!
Mai 10 21:53:37 raspberrypi MusicBox_GUI.py[374]: [INFO   ] [Factory     ] 189 symbols loaded
Mai 10 21:53:38 raspberrypi MusicBox_GUI.py[374]: [INFO   ] [Image       ] Providers: img_tex, img_dds, img_sdl2, 
Mai 10 21:53:39 raspberrypi MusicBox_GUI.py[374]: [INFO   ] [Window      ] Provider: sdl2
Mai 10 21:53:39 raspberrypi MusicBox_GUI.py[374]: * failed to add service - already in use?
Mai 10 21:53:39 raspberrypi systemd[1]: MusicBox.service: Main process exited, code=exited, status=1/FAILURE
Mai 10 21:53:39 raspberrypi systemd[1]: MusicBox.service: Failed with result 'exit-code'.
Könnt ihr mir sagen wie ich raus finden kann, welcher Service da gemeint sein könnte? Was sagt mir der '*' ?
Grundsätzlich verstehe ich, dass die Fehlermeldung mir sagt, dass ein Service nicht hinzugefügt werden kann, weil er vielleicht schon benutzt wird. Nur was für ein Service?

Schönen Abend und Grüße
Dennis
“A ship is always safe at the shore, but that is not what it is built for.”
__deets__
User
Beiträge: 10095
Registriert: Mittwoch 14. Oktober 2015, 14:29

Dienstag 11. Mai 2021, 08:05

Genau die Meldung findet sich doch in dem Link, den ich gepostet habe.
Benutzeravatar
Dennis89
User
Beiträge: 219
Registriert: Freitag 11. Dezember 2020, 15:13

Dienstag 11. Mai 2021, 11:35

Guten Morgen und Danke für deine Antwort.

Der Unterschied ist, dass in deinem Link "vor" der Service-Meldung ein ERORR auftaucht, in dem eine Datei oder Verzeichnis nicht gefunden wird und so weit ich das verstanden habe, beziehen sich die folgenden Beiträge darauf.

Werde heute Abend auch noch mal das Internet durchforsten.

Grüße
Dennis
“A ship is always safe at the shore, but that is not what it is built for.”
__deets__
User
Beiträge: 10095
Registriert: Mittwoch 14. Oktober 2015, 14:29

Dienstag 11. Mai 2021, 11:46

Es geht darum, auf dem PI 4 andere Bibliotheken zu laden. Damit fängt der Post an. Später kommen Leute, die es falsch machen oder bei denen etwas fehlt. Die dann deine Fehlermeldung bekommen.
__deets__
User
Beiträge: 10095
Registriert: Mittwoch 14. Oktober 2015, 14:29

Dienstag 11. Mai 2021, 17:56

Um das nochmal genauer zu erklären, während ich auf meinen anaphylaktischen Schock nach der Impfung warte: es geht um OpenGL Libs bei denen sich was geändert hat beim PI4. Und die braucht Kivy.
Benutzeravatar
Dennis89
User
Beiträge: 219
Registriert: Freitag 11. Dezember 2020, 15:13

Mittwoch 12. Mai 2021, 06:03

Guten Morgen und Danke für die Erklärung.

Ich hatte gestern nicht wirklich viel Zeit um mich darum zu kümmern.

Puh dein letzter Beitrag war vor 7 Stunden, ich hoffe du hast das Schlimmste überstanden.
PS: Wie ist der Radioempfang mit den gespritzten Chips?

Grüße
Dennis
“A ship is always safe at the shore, but that is not what it is built for.”
__deets__
User
Beiträge: 10095
Registriert: Mittwoch 14. Oktober 2015, 14:29

Mittwoch 12. Mai 2021, 07:31

Ich bekomme jetzt nur noch KenFM zu hören. Ein bisschen ironisch, dass gerade er davon profitiert. Aber so sindse.
Antworten