Files

120 lines
4.3 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Non-blocking controls for the system Syncthing instance."""
import queue
import subprocess
import threading
from tkinter import ttk, messagebox
SERVICE = "syncthing.service"
SYSTEMCTL = ("systemctl", "--user", "--no-ask-password")
def service_status():
result = subprocess.run(
[*SYSTEMCTL, "show", SERVICE, "--property=ActiveState", "--value"],
capture_output=True, text=True, timeout=10,
)
if result.returncode:
raise RuntimeError(result.stderr.strip() or "Statut indisponible")
return result.stdout.strip() or "unknown"
def control_service(action):
if action == "ensure" and service_status() == "active":
return "active"
command = "start" if action == "ensure" else action
result = subprocess.run(
[*SYSTEMCTL, command, SERVICE],
capture_output=True, text=True, timeout=60,
)
if result.returncode:
raise RuntimeError(result.stderr.strip() or f"Échec de systemctl {command}")
status = service_status()
if command == "start" and status != "active":
raise RuntimeError(f"Syncthing n’est pas actif : {status}")
return status
class SyncthingPanel(ttk.Frame):
def __init__(self, parent):
super().__init__(parent)
self.events = queue.Queue()
self.busy = False
self.pending_export = None
self.status = ttk.Label(self, text=f"{SERVICE} : vérification…")
self.status.pack(anchor="w")
controls = ttk.Frame(self)
controls.pack(anchor="w", pady=4)
self.start_button = ttk.Button(controls, text="Démarrer Syncthing",
command=lambda: self.request("start"))
self.start_button.pack(side="left")
self.stop_button = ttk.Button(controls, text="Arrêter Syncthing",
command=lambda: self.request("stop"))
self.stop_button.pack(side="left", padx=6)
self.timer = self.after(100, self.poll)
self.refresh_timer = None
self.bind("<Destroy>", self.cleanup, add=True)
self.request("status")
def ensure_started(self, callback):
self.pending_export = callback
if not self.busy:
self.request("ensure")
def request(self, action):
if self.busy:
return
self.busy = True
self.start_button.configure(state="disabled")
self.stop_button.configure(state="disabled")
if action != "status":
self.status.configure(text=f"{SERVICE} : opération en cours…")
def work():
try:
status = service_status() if action == "status" else control_service(action)
self.events.put((action, status, None))
except (OSError, RuntimeError, subprocess.TimeoutExpired) as exc:
self.events.put((action, None, str(exc)))
threading.Thread(target=work, daemon=True).start()
def poll(self):
try:
action, status, error = self.events.get_nowait()
except queue.Empty:
pass
else:
self.busy = False
self.start_button.configure(state="normal")
self.stop_button.configure(state="normal")
self.status.configure(text=f"{SERVICE} : {error or status}")
if action == "ensure":
callback, self.pending_export = self.pending_export, None
if error:
messagebox.showwarning(
"Syncthing",
f"Impossible de démarrer Syncthing : {error}\n\n"
"L’export va continuer.",
)
if callback:
callback()
elif error and action != "status":
messagebox.showerror("Syncthing", error)
elif self.pending_export:
self.request("ensure")
if self.winfo_exists():
self.timer = self.after(100, self.poll)
if self.refresh_timer is None:
self.refresh_timer = self.after(5000, self.refresh)
def refresh(self):
self.refresh_timer = None
self.request("status")
def cleanup(self, event):
if event.widget == self:
self.pending_export = None
for timer in (self.timer, self.refresh_timer):
if timer:
self.after_cancel(timer)