Почему не срабатывает Tkinter при автозапуске скрипта на Kali из systemctl

Есть скрипт на питоне, который отлично работает на Kali, если его запускать через контекстное меню. Но при попытке запуска через systemctl сервис - вылетает ошибка, связанная с Tkinter. Гугление ничего не дало(или я недопонял). Прошу не кидаться тапками - с Kali, да и вообще с линуксом работаю недавно.

Скрипт на питоне:

import json
import os
import pickle
import sys
from threading import Thread
from tkinter import *
from tkinter.ttk import *

import requests

import Variables
import streams


def load_settings():
    with open("settings.txt", "r", encoding="UTF8") as setings:
        f = setings.read()
        lines = f.split(';')
        print(lines[0])
        Variables.ip = lines[0]


def on_start():
    if os.path.exists('data.pickle'):
        with open('data.pickle', 'rb') as f:  
            try:
                data_new = pickle.load(f)
            except:
                data_new = []
                pass
        Variables.list_of_saved_data = data_new
        entry.delete(1.0, END)
        for element in Variables.list_of_saved_data:
            entry.insert(1.0, element + '\n')
        print(Variables.list_of_saved_data)
    else:
        with open('data.pickle', "w"):
            pass
    load_settings()


def general_title_state():
    value1 = []
    if Variables.code_of_response_server == 200:
        Variables.root_title = "My notes sync LAN (connected)"
        root.title(Variables.root_title)
        val = entry1.get(0, END)
        value1 = list(val)
        # value1.reverse()
        # for i in enumerate(val):
        #     if val[0] == '':
        #          del val[0]

        # value1 = val
        print("value = " + str(val))
        if Variables.list_of_get_data != value1:
            write_server_data_to_entry()

    else:
        Variables.root_title = "My notes sync LAN (no connect)"
        root.title(Variables.root_title)
        pass
    pass
    root.after(2000, general_title_state)


def write_server_data_to_entry():
    b = Variables.list_of_get_data
    entry1.delete(0, END)
    for i in b:
        entry1.insert(END, i)
    # for index, element in enumerate(b):
    #     if index < len(b) and element != '':
    #         entry1.insert(1.0, element )


def write_local_data():
    val = list((entry.get(1.0, END)).split())
    val.reverse()
    Variables.list_of_saved_data = val
    with open('data.pickle', "wb") as f:
        pickle.dump(Variables.list_of_saved_data, f)
    root.after(2000, write_local_data)


def set_data():
    try:
        dict = {}
        lis = list((entry.get(1.0, END)).split('\n'))
        if lis[-1] == '\n' or lis[-1] == '':
            del lis[-1]
        for index, val in enumerate(lis):
            dict[index] = val
        print(dict)
        r = requests.get("http://" + str(Variables.ip) + "/data_json_set", json=(dict))
        if r.status_code == 200:
            print("set_data " + str(lis))
    except:
        print("EXcept of set data!!!")
        pass


def onSelect(val):
    sender = val.widget
    idx = sender.curselection()
    try:
        value = sender.get(idx)
    except:
        value = ""
        pass
    from tkinter import Tk
    root = Tk()
    root.withdraw()
    root.clipboard_clear()
    root.clipboard_append(value)
    print(value)


def get_clipboard_text(event):
    from tkinter import Tk
    root = Tk()
    root.withdraw()
    s = root.clipboard_get()
    entry.insert(END, s)


def view_my_notes(event):
    t = Toplevel()
    t.wm_title("My notes")
    l = Text(t, font=("Arial Bold", 12))
    l.pack(side="top", fill="both", expand=True, padx=5, pady=5)
    aaa = entry2.get(1.0, END)
    l.insert(1.0, aaa)


root = Tk()
root.iconphoto(True, PhotoImage(file='icon.png'))
root.title(Variables.root_title)
root.geometry('300x155-0-40')
root.resizable(True, True)

f_top = Frame(root)
f_bot = Frame(root)
f_my = Frame(root)
f_top.pack(fill=BOTH, expand=True)
f_bot.pack(fill=BOTH, expand=True)
f_my.pack(fill=BOTH, expand=True)
entry = Text(f_top, font=("Arial Bold", 12), height=2, width=27)
entry.bind('<Button-3>', get_clipboard_text)
entry.pack(fill=X, expand=True)
button = Button(f_top, text="Send", width=2, command=set_data)
button.pack(fill=X, expand=True)
entry1 = Listbox(f_bot, font=("Arial Bold", 12), height=4)
entry1.bind("<<ListboxSelect>>", onSelect)
entry1.pack(fill=BOTH, expand=True)
entry2 = Text(f_bot, font=("Arial Bold", 12), height=2)
entry2.bind('<Button-3>', view_my_notes)
entry2.pack(fill=BOTH, expand=True)

th1 = Thread(target=streams.get_data, daemon=True)
th1.start()
root.after(0, on_start)
root.after(0, general_title_state)
root.after(0, write_local_data)
root.mainloop()

Сама ошибка:

test.service - My test
     Loaded: loaded (/etc/systemd/system/test.service; enabled; preset: disabled)
     Active: failed (Result: exit-code) since Mon 2022-12-05 19:24:19 +05; 1min 9s ago
   Duration: 638ms
    Process: 3427 ExecStart=/usr/bin/python3.10 /home/qwerty/clients1/client_main.py (code=exited, status=1/FAILURE)
   Main PID: 3427 (code=exited, status=1/FAILURE)
        CPU: 502ms

дек 05 19:24:19 kali01 systemd[1]: Started My test.
дек 05 19:24:19 kali01 python3.10[3427]: Traceback (most recent call last):
дек 05 19:24:19 kali01 python3.10[3427]:   File "/home/qwerty/clients1/client_main.py", line 138, in <module>
дек 05 19:24:19 kali01 python3.10[3427]:     root = Tk()
дек 05 19:24:19 kali01 python3.10[3427]:   File "/usr/lib/python3.10/tkinter/__init__.py", line 2299, in __init__
дек 05 19:24:19 kali01 python3.10[3427]:     self.tk = _tkinter.create(screenName, baseName, className, interactive, wantobjects, >
дек 05 19:24:19 kali01 python3.10[3427]: _tkinter.TclError: no display name and no $DISPLAY environment variable
дек 05 19:24:19 kali01 systemd[1]: test.service: Main process exited, code=exited, status=1/FAILURE
дек 05 19:24:19 kali01 systemd[1]: test.service: Failed with result 'exit-code'.
lines 1-17/17 (END)

Ответы (2 шт):

Автор решения: Ret7020

Проблема в том, что все приложения, которым нужен display manager и в принципе экран не знают какой display использовать, так как берут его из enviroment переменной. Как я вижу вы написали сервис для systemd. Внутрь конфига этого сервиса в секцию [Service] добавьте следующий код: Environment=DISPLAY=:0.0 Это конкретно укажет куда выводить картинку. Насколько я помню в kali по-дефолту стоит xfce. Можно попробовать через штатные утилиты xfce настроить авто-запуск.

→ Ссылка
Автор решения: eri

положи скрипт в пользовательскую сессию ~/.config/systemd/user/default.target.wants/test.service - запустится вместе с рабочим столом

→ Ссылка