Почему не срабатывает 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 шт):
Проблема в том, что все приложения, которым нужен display manager и в принципе экран не знают какой display использовать, так как берут его из enviroment переменной. Как я вижу вы написали сервис для systemd. Внутрь конфига этого сервиса в секцию [Service] добавьте следующий код:
Environment=DISPLAY=:0.0
Это конкретно укажет куда выводить картинку. Насколько я помню в kali по-дефолту стоит xfce. Можно попробовать через штатные утилиты xfce настроить авто-запуск.
положи скрипт в пользовательскую сессию
~/.config/systemd/user/default.target.wants/test.service - запустится вместе с рабочим столом