From 21d9936138859033c274ad538e1c08a0aca93f27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Mon, 29 Jun 2026 15:04:02 +0200 Subject: [PATCH] Kleinigkeiten --- AstroMac.json | 6 + README.md | 248 +++++++++++++++++++++ __pycache__/astrometry_gui.cpython-312.pyc | Bin 97192 -> 102575 bytes astrometry_gui.py | 111 +++++++-- ssh_zugangsdaten.json | 6 + 5 files changed, 356 insertions(+), 15 deletions(-) create mode 100644 AstroMac.json create mode 100644 README.md create mode 100644 ssh_zugangsdaten.json diff --git a/AstroMac.json b/AstroMac.json new file mode 100644 index 0000000..281147c --- /dev/null +++ b/AstroMac.json @@ -0,0 +1,6 @@ +{ + "host": "10.42.42.102", + "port": 22, + "user": "tm", + "password": "heute1234" +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..619e083 --- /dev/null +++ b/README.md @@ -0,0 +1,248 @@ +# Solve-Field-GUI + +Grafische Windows-Oberfläche für `solve-field` aus Astrometry.net mit Ausführung auf einem Linux-Server per SSH. + +Die Software richtet sich an Anwender, die Astrometrie komfortabel über eine GUI steuern möchten, ohne die `solve-field`-Befehle jedes Mal von Hand zusammenzubauen. + +## Was die Software macht + +Die Anwendung läuft lokal unter Windows und verbindet sich per SSH mit einem entfernten Linux-Server. + +Dort kann sie: + +- Bilddateien auf den Server hochladen +- `solve-field` mit vielen Parametern starten +- die Ausgabe live im Log anzeigen +- Ergebnisdateien im Remote-Verzeichnis anzeigen +- einzelne oder mehrere Ergebnisdateien wieder lokal herunterladen +- Serverfunktionen wie SExtractor-Prüfung und Diagnose ausführen + +## Hauptfunktionen + +- SSH-Verbindung zu einem Linux-Server mit Benutzername, Passwort und Port +- Upload lokaler Bilddateien per SFTP +- grafische Konfiguration fast aller wichtigen `solve-field`-Parameter +- automatische Befehlsvorschau des erzeugten `solve-field`-Aufrufs +- Unterstützung für Maßstab, RA/Dek, Suchradius, Downsampling, CPU-Limit, Tweak-Order und viele weitere Optionen +- Unterstützung für Source Extractor per `--use-source-extractor` +- automatische Berechnung von `--plot-scale` aus der lokalen Bildgröße +- serverseitige Diagnose für `solve-field`, `astrometry.cfg`, Indexdateien, SExtractor und Speicherplatz +- Remote-Ergebnis-Tab mit Dateiliste +- Mehrfachdownload ausgewählter Ergebnisdateien +- Profilspeicherung und Profilladen für komplette GUI-Einstellungen + +## Unterstützte Dateitypen + +Als Eingabedatei lokal: + +- FITS +- FIT +- FTS +- JPG / JPEG +- PNG +- TIFF / TIF + +Für die automatische Plot-Skalierung kann die Anwendung Bildgrößen direkt aus mehreren Formaten lesen, darunter FITS, PNG, JPG, GIF und BMP. + +## Voraussetzungen + +## Lokal unter Windows + +- Python 3 +- `asyncssh` +- Netzwerkzugriff auf den Linux-Server per SSH + +Installation von `asyncssh`: + +```bash +pip install asyncssh +``` + +## Auf dem Linux-Server + +- Astrometry.net mit `solve-field` +- passende Indexdateien +- SSH-Zugang + +Optional: + +- `source-extractor`, wenn SExtractor zur Quellenerkennung genutzt werden soll + +## Starten + +Direkt mit Python: + +```bash +python astrometry_gui.py +``` + +Oder unter Windows über: + +```bash +astrometry_gui.bat +``` + +## Bedienung + +## 1. SSH eintragen + +Oben in der GUI: + +- Host +- Port +- Benutzer +- Passwort + +Mit `Verbinden & testen` kann geprüft werden, ob der Server erreichbar ist und `solve-field` vorhanden ist. + +## 2. Bild auswählen und hochladen + +Im Tab `Input / Output`: + +- lokale Datei auswählen +- Remote-Verzeichnis festlegen +- Datei per `Hochladen` auf den Server übertragen + +Nach dem Upload wird der Remote-Dateipfad automatisch als Eingabedatei gesetzt. + +## 3. Solver-Parameter einstellen + +Je nach Bedarf können unter anderem gesetzt werden: + +- Maßstabsbereich +- RA / Dek / Radius +- CPU-Limit +- Downsampling +- Plot-Skalierung +- Tweak-Order +- Verifikation +- Invertierung +- SExtractor-Nutzung + +## 4. Befehl prüfen und starten + +Im Tab `Befehlsvorschau` sieht man den tatsächlich erzeugten `solve-field`-Befehl. + +Mit `solve-field starten` wird der Befehl auf dem Server ausgeführt. + +## Tabs im Überblick + +## Input / Output + +- lokale Bilddatei +- Remote-Verzeichnis +- Upload +- Eingabedatei auf dem Server +- Ausgabeoptionen wie `--wcs`, `--corr`, `--rdls`, `--match`, `--new-fits` + +## Maßstab & Position + +- `--scale-low` +- `--scale-high` +- `--scale` +- `--scale-err` +- `--ra` +- `--dec` +- `--radius` + +## Solver-Parameter + +- `--depth` +- `--cpulimit` +- `--plot-scale` +- `--downsample` +- `--tweak-order` +- `--odds-*` +- `--objs` +- `--pixel-error` +- zahlreiche Flags wie `--no-plots`, `--invert`, `--no-verify` + +Zusätzlich gibt es einen Plot-Helfer: + +- automatische Berechnung von `plot-scale` +- Zielkantenlänge einstellbar + +## Erweitert + +- manuelle Indexdateien +- zusätzliche freie Kommandozeilenargumente + +## Befehlsvorschau + +- zeigt den generierten Befehl +- kann aktualisiert werden +- kann in die Zwischenablage kopiert werden + +## Server Setup + +- SExtractor prüfen +- SExtractor installieren +- Server-Diagnose ausführen + +## Remote-Ergebnisse + +- zeigt Dateien im aktuellen Remote-Verzeichnis +- lädt den Inhalt beim Öffnen des Tabs automatisch neu +- erlaubt Auswahl und Download von einer oder mehreren Dateien + +## Remote-Ergebnisse herunterladen + +Im Tab `Remote-Ergebnisse`: + +- eine oder mehrere Dateien markieren +- `Herunterladen` klicken +- Zielordner auswählen + +Standardmäßig wird als Zielordner der Ordner der ursprünglich lokal ausgewählten Bilddatei vorgeschlagen. + +## Remote-Verzeichnis leeren + +Im Tab `Input / Output` gibt es die Funktion: + +- `Remote-Inhalt löschen` + +Dabei wird nur der Inhalt des Remote-Verzeichnisses gelöscht, nicht das Verzeichnis selbst. + +## Profile speichern und laden + +Über die Menühleiste: + +- `Profil -> Profil speichern…` +- `Profil -> Profil laden…` + +Gespeichert werden: + +- SSH-Daten +- Solver-Parameter +- Ein-/Ausgabe-Einstellungen +- Zusatzargumente +- Indexliste + +Das Passwort wird nicht im Klartext gespeichert, sondern mit einer einfachen internen Verschlüsselung abgelegt. + +## Typischer Workflow + +1. Serververbindung eintragen +2. Lokales Bild auswählen +3. Datei hochladen +4. Maßstab und Solver-Parameter setzen +5. Optional SExtractor aktivieren +6. `solve-field` starten +7. Ergebnisse im Tab `Remote-Ergebnisse` prüfen +8. Gewünschte Dateien lokal herunterladen + +## Hinweise + +- Die GUI baut nur den Befehl und führt ihn per SSH aus. Die eigentliche Astrometrie läuft vollständig auf dem Server. +- Die installierte Astrometry-Version auf dem Server bestimmt, welche Optionen tatsächlich verfügbar sind. +- Für SExtractor muss auf dem Server in der Regel `source-extractor` installiert sein. +- Für gute Ergebnisse müssen die passenden Indexdateien serverseitig vorhanden und in `astrometry.cfg` korrekt eingebunden sein. + +## Dateien im Projekt + +- [astrometry_gui.py](C:/Users/ich/source/repos/Solve-Field-GUI/astrometry_gui.py) +- [astrometry_gui.bat](C:/Users/ich/source/repos/Solve-Field-GUI/astrometry_gui.bat) + +## Lizenz / Nutzung + +Dieses Repository enthält eine GUI zur Bedienung von Astrometry.net. Die Lizenz und Bedingungen von Astrometry.net sowie von eventuell installierten Serverpaketen gelten zusätzlich zu diesem Projekt. diff --git a/__pycache__/astrometry_gui.cpython-312.pyc b/__pycache__/astrometry_gui.cpython-312.pyc index 84a327f48c0b120c7a4398e74e65f1f8185f99f9..70b2bbd97f7e1d8f4c3a0cb787c498eba0bfce8f 100644 GIT binary patch delta 14981 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 NF*8w*|1O2_{{c&l>wW+L diff --git a/astrometry_gui.py b/astrometry_gui.py index fa15cd1..43aadcf 100644 --- a/astrometry_gui.py +++ b/astrometry_gui.py @@ -11,6 +11,7 @@ import asyncio import base64 import json import tkinter as tk +import tkinter.font as tkfont from tkinter import ttk, filedialog, messagebox, scrolledtext import threading import os @@ -26,6 +27,9 @@ except ImportError: HAS_ASYNCSSH = False +SSH_CREDENTIALS_JSON = "ssh_zugangsdaten.json" + + # --------------------------------------------------------------------------- # Tooltip # --------------------------------------------------------------------------- @@ -215,6 +219,7 @@ class AstrometryGUI: self.v_port = tk.IntVar(value=22) self.v_user = tk.StringVar(value="eskimue") self.v_pass = tk.StringVar(value="Preside") + self._load_startup_credentials() self.v_local_file = tk.StringVar() self.v_local_file.trace_add("write", self._on_local_file_changed) @@ -275,6 +280,27 @@ class AstrometryGUI: self.v_extra = tk.StringVar() self.results_files: dict[str, dict] = {} + def _load_startup_credentials(self): + path = os.path.join(os.getcwd(), SSH_CREDENTIALS_JSON) + if not os.path.isfile(path): + return + try: + self._load_credentials_from_file(path) + except Exception: + pass + + def _load_credentials_from_file(self, path: str): + with open(path, "r", encoding="utf-8") as f: + data = json.load(f) + if "host" in data: + self.v_host.set(str(data["host"])) + if "port" in data: + self.v_port.set(int(data["port"])) + if "user" in data: + self.v_user.set(str(data["user"])) + if "password" in data: + self.v_pass.set(str(data["password"])) + # ----------------------------------------------------------------------- # UI aufbauen # ----------------------------------------------------------------------- @@ -345,6 +371,8 @@ class AstrometryGUI: menubar = tk.Menu(self.root) profile_menu = tk.Menu(menubar, tearoff=False) + profile_menu.add_command(label="Verbindung laden…", command=self._menu_load_credentials) + profile_menu.add_separator() profile_menu.add_command(label="Profil speichern…", command=self._save_profile) profile_menu.add_command(label="Profil laden…", command=self._load_profile) menubar.add_cascade(label="Profil", menu=profile_menu) @@ -399,6 +427,9 @@ class AstrometryGUI: "Öffnet einen Datei-Dialog um die lokale Astro-Bild-Datei auszuwählen." ).grid(row=0, column=2, padx=4) + self.local_image_size_label = ttk.Label(uf, text="Größe: —", foreground="gray") + self.local_image_size_label.grid(row=0, column=3, sticky=tk.W, padx=4, pady=3) + ttk.Label(uf, text="Remote-Verzeichnis:").grid(row=1, column=0, sticky=tk.W, padx=4, pady=3) tip(ttk.Entry(uf, textvariable=self.v_remote_dir), "Verzeichnis auf dem Server wohin die Datei hochgeladen wird.\n" @@ -772,6 +803,9 @@ class AstrometryGUI: tip(ttk.Button(head, text="Aktualisieren", command=self._refresh_results_tab), "Liest den Inhalt des aktuell eingestellten Remote-Verzeichnisses erneut vom Server ein." ).pack(side=tk.RIGHT, padx=4) + tip(ttk.Button(head, text="Remote-Inhalt löschen", command=self._delete_remote_dir), + "Löscht den Inhalt des aktuell eingestellten Remote-Verzeichnisses auf dem Server." + ).pack(side=tk.RIGHT, padx=4) tip(ttk.Button(head, text="Herunterladen", command=self._download_selected_result), "Lädt die ausgewählte Remote-Datei per SFTP herunter." ).pack(side=tk.RIGHT, padx=4) @@ -780,7 +814,7 @@ class AstrometryGUI: self.results_info.pack(anchor=tk.W, pady=(0, 4)) cols = ("name", "size", "remote") - self.results_tree = ttk.Treeview(parent, columns=cols, show="headings", height=16) + self.results_tree = ttk.Treeview(parent, columns=cols, show="headings", height=16, selectmode="extended") self.results_tree.heading("name", text="Datei") self.results_tree.heading("size", text="Größe") self.results_tree.heading("remote", text="Remote-Pfad") @@ -1060,6 +1094,20 @@ class AstrometryGUI: entry["remote"], ), ) + self._autosize_results_columns() + + def _autosize_results_columns(self): + body_font = tkfont.nametofont("TkDefaultFont") + head_font = tkfont.nametofont("TkHeadingFont") + padding = 24 + + for col in ("name", "size", "remote"): + header = self.results_tree.heading(col, "text") or col + width = head_font.measure(header) + padding + for item_id in self.results_tree.get_children(): + value = self.results_tree.set(item_id, col) + width = max(width, body_font.measure(str(value)) + padding) + self.results_tree.column(col, width=width) def _clear_wait_cursor(self): self.root.config(cursor="") @@ -1084,13 +1132,16 @@ class AstrometryGUI: def _download_selected_result(self): selection = self.results_tree.selection() if not selection: - messagebox.showwarning("Keine Datei ausgewählt", "Bitte zuerst eine Datei im Ergebnis-Tab auswählen.") + messagebox.showwarning("Keine Datei ausgewählt", "Bitte zuerst eine oder mehrere Dateien im Ergebnis-Tab auswählen.") return - entry = self.results_files.get(selection[0]) - if not entry: - messagebox.showwarning("Datei fehlt", "Die ausgewählte Datei konnte nicht gefunden werden.") + entries = [] + for item_id in selection: + entry = self.results_files.get(item_id) + if entry: + entries.append(entry) + if not entries: + messagebox.showwarning("Datei fehlt", "Die ausgewählten Dateien konnten nicht gefunden werden.") return - suggested = os.path.basename(entry["remote"]) source_dir = os.path.dirname(self.v_local_file.get().strip()) if self.v_local_file.get().strip() else "" initial_dir = source_dir if source_dir and os.path.isdir(source_dir) else os.getcwd() local_dir = filedialog.askdirectory( @@ -1099,20 +1150,25 @@ class AstrometryGUI: ) if not local_dir: return - local_path = os.path.normpath(os.path.join(local_dir, suggested)) - self._submit(self._async_download_result(entry["remote"], local_path)) + downloads = [] + for entry in entries: + suggested = os.path.basename(entry["remote"]) + local_path = os.path.normpath(os.path.join(local_dir, suggested)) + downloads.append((entry["remote"], local_path)) + self._submit(self._async_download_results(downloads)) - async def _async_download_result(self, remote_path: str, local_path: str): - self._log(f"[INFO] Lade Ergebnis herunter: {remote_path} → {local_path}\n", "info") + async def _async_download_results(self, downloads: list[tuple[str, str]]): try: - os.makedirs(os.path.dirname(local_path), exist_ok=True) ssh = self._make_ssh() await ssh.connect() - await ssh.download(remote_path, local_path) + for remote_path, local_path in downloads: + self._log(f"[INFO] Lade Ergebnis herunter: {remote_path} → {local_path}\n", "info") + os.makedirs(os.path.dirname(local_path), exist_ok=True) + await ssh.download(remote_path, local_path) + if not os.path.isfile(local_path): + raise FileNotFoundError(f"Datei nach Download nicht gefunden: {local_path}") + self._log(f"[OK] Download abgeschlossen: {local_path}\n", "ok") await ssh.close() - if not os.path.isfile(local_path): - raise FileNotFoundError(f"Datei nach Download nicht gefunden: {local_path}") - self._log(f"[OK] Download abgeschlossen: {local_path}\n", "ok") except Exception as exc: self._log(f"[FEHLER] Download: {exc}\n", "error") @@ -1159,6 +1215,15 @@ class AstrometryGUI: self.v_new_fits.set(auto_path) def _on_local_file_changed(self, *_): + path = self.v_local_file.get().strip() + if not path or not os.path.isfile(path): + self.local_image_size_label.config(text="Größe: —") + else: + try: + width, height = self._read_image_size(path) + self.local_image_size_label.config(text=f"Größe: {width} × {height}") + except Exception: + self.local_image_size_label.config(text="Größe: unbekannt") if self.v_plot_scale_auto.get(): self._recalculate_plot_scale() @@ -1285,6 +1350,8 @@ class AstrometryGUI: ) if path: self.v_local_file.set(path) + if messagebox.askyesno("Datei hochladen", "Soll die ausgewählte Datei jetzt direkt auf den Server hochgeladen werden?"): + self._upload() def _refresh_cmd(self): cmd = self._build_command() @@ -1385,6 +1452,20 @@ class AstrometryGUI: except Exception as exc: messagebox.showerror("Profil laden", f"Laden fehlgeschlagen:\n{exc}") + def _menu_load_credentials(self): + path = filedialog.askopenfilename( + title="Zugangsdaten laden", + initialdir=os.getcwd(), + filetypes=[("JSON-Dateien", "*.json"), ("Alle Dateien", "*.*")], + ) + if not path: + return + try: + self._load_credentials_from_file(path) + self._log(f"[OK] Zugangsdaten geladen: {path}\n", "ok") + except Exception as exc: + messagebox.showerror("Zugangsdaten laden", f"Laden fehlgeschlagen:\n{exc}") + # ----------------------------------------------------------------------- # Log (thread-sicher via Queue) # ----------------------------------------------------------------------- diff --git a/ssh_zugangsdaten.json b/ssh_zugangsdaten.json new file mode 100644 index 0000000..646df60 --- /dev/null +++ b/ssh_zugangsdaten.json @@ -0,0 +1,6 @@ +{ + "host": "10.42.44.25", + "port": 22, + "user": "eskimue", + "password": "Preside" +}