Initial commit

This commit is contained in:
René Mathieu
2026-01-17 13:49:51 +01:00
commit 0fef8d96c5
1897 changed files with 396119 additions and 0 deletions

225
dicom2pacs.py Executable file
View File

@@ -0,0 +1,225 @@
import sys
import os
import asyncio
from gui import GUI
import file_management
import threading
import dicom_processing
import shutil
import network_utils
from tkinter import filedialog
import tkinter as tk
# Konfigurationsvariablen
HOME_DIRECTORY = os.path.expanduser('~')
TRANSFER_FOLDER = os.path.join(HOME_DIRECTORY, 'Downloads/D2OTrans')
AUSGABE_DATEI = os.path.join(HOME_DIRECTORY, 't2o_modality.txt')
name = None
vorname = None
geburtsdatum = None
patienten_id = None
CONFIG_PATH = os.path.join(HOME_DIRECTORY, '.dicom2pacs.conf')
# Standardkonfigurationswerte
DEFAULT_CONFIG = {
'server_url': 'http://192.168.188.23:8042/instances',
'server_username': 'orthanc',
'server_pw': 'Praxis'
}
def save_default_config():
"""Speichert die Standardkonfiguration in der Konfigurationsdatei."""
try:
with open(CONFIG_PATH, 'w', encoding='utf-8') as config_file:
for key, value in DEFAULT_CONFIG.items():
config_file.write(f'{key}={value}\n')
except Exception as e:
print(f"Fehler beim Speichern der Standardkonfiguration: {e}")
def load_config():
"""Lädt die Konfiguration aus der Konfigurationsdatei. Erstellt die Datei mit Standardwerten, falls nicht vorhanden."""
config = {}
try:
with open(CONFIG_PATH, 'r', encoding='utf-8') as config_file:
for line in config_file:
line = line.strip()
if not line or line.startswith('#'): # Überspringe leere Zeilen und Kommentare
continue
if '=' in line:
key, value = line.split('=', 1)
config[key.strip()] = value.strip()
except FileNotFoundError:
print("Konfigurationsdatei nicht gefunden. Erstelle eine mit Standardwerten.")
save_default_config()
config = DEFAULT_CONFIG.copy() # Verwende Kopie der Standardwerte
except Exception as e:
print(f"Fehler beim Laden der Konfiguration: {e}")
print("Verwende Standardwerte.")
config = DEFAULT_CONFIG.copy()
return config
config = load_config()
server_url = config.get('server_url')
server_username = config.get('server_username')
server_pw = config.get('server_pw')
loop = asyncio.new_event_loop() # Initialisiere loop hier
# Erstelle den Event-Loop für asynchronen Code
def start_asyncio_loop():
global loop # Verweise auf die globale Variable loop
asyncio.set_event_loop(loop)
loop.run_forever()
asyncio_thread = threading.Thread(target=start_asyncio_loop, daemon=True)
asyncio_thread.start()
async def on_folder_selected_coro(folder_path, gui):
print("on_folder_selected gestartet")
await get_patientfiles(folder_path, gui)
upload_files = file_management.get_files_in_folder(TRANSFER_FOLDER)
if upload_files == 'no_transfer_folder':
results, folder_path = gui.ask_for_more("Bisher keine Bilder ausgewählt.\nAndere Orte durchsuchen?")
if results is False:
gui.on_app_close()
else:
if folder_path:
await get_patientfiles(folder_path, gui)
else:
file_paths = upload_files
print(file_paths)
if not file_paths or len(file_paths) == 0:
gui.start_animation("Keine Dateien zum Hochladen gefunden")
print("Keine Dateien zum Hochladen gefunden")
gui.on_app_close()
return
if not server_url:
gui.start_animation("FEHLER: Server-URL nicht konfiguriert")
print("FEHLER: Server-URL nicht konfiguriert")
gui.on_app_close()
return
availability = await network_utils.check_server_availability(server_url)
if availability:
print("Server ist verfügbar. Dateien werden hochgeladen.")
# Hier wird die Funktion upload_single_file asynchron aufgerufen
# In dicom_processing.process_dicom_files
gui.start_animation("Lade DICOM Dateien zum PACS Server hoch")
print(f"Starte Upload von {len(file_paths)} Dateien...")
uploaded_count, failed_count = await network_utils.upload_multiple_files(file_paths, server_url, server_username, server_pw, gui.upload_success, gui.upload_progress)
gui.stop_animation()
if failed_count == 0:
gui.start_animation("Bilddaten Im PACS erfolgreich eingelesen!")
await asyncio.sleep(3)
else:
gui.start_animation(f"Upload abgeschlossen: {uploaded_count}/{len(file_paths)} Dateien hochgeladen")
await asyncio.sleep(5) # Mehr Zeit, damit Benutzer die Meldung lesen kann
shutil.rmtree(TRANSFER_FOLDER)
print(f"Transferordner {TRANSFER_FOLDER} wurde gelöscht.")
gui.on_app_close()
else:
gui.start_animation("PACS SERVER NICHT ERREICHBAR")
print("Server ist nicht verfügbar.")
print("on_folder_selected abgeschlossen") # Debugging-Ausgabe
async def get_patientfiles(folder_path, gui):
print("Debugging-Ausgabe vor der Suche") # Debugging-Ausgabe
gui.hint_label.pack_forget()
while True:
gui.disable_browse_button()
gui.start_animation("Suche nach DICOM Dateien") # Starte die Suchanimation
print(folder_path)
files_found = file_management.find_dicom_files(folder_path)
if files_found == 'non_found':
gui.stop_animation()
results, folder_path = gui.ask_for_more("In diesem Ordner wurde keine Bilddaten gefunden.\nAndere Orte durchsuchen?")
if not results: # Wenn der Benutzer 'Nein' auswählt
break
else:
dicom_files, total_files = files_found
print(dicom_files)
gui.stop_animation() # Erhalte Dateien und Anzahl
print(f"Dicom-Dateien gefunden: {total_files}")
gui.start_animation("Kopiere DICOM Dateien in den Transfer Ordner")
await file_management.copy_dicom_files(dicom_files, folder_path, TRANSFER_FOLDER, gui.update_progress)
gui.stop_animation()
print(f"Dicom-Dateien wurden nach {TRANSFER_FOLDER} kopiert.")
current_dat_folder = file_management.get_latest_dat_folder(TRANSFER_FOLDER)
if not current_dat_folder:
gui.stop_animation()
gui.start_animation("FEHLER: Transferordner nicht gefunden")
print("FEHLER: Transferordner nicht gefunden")
break
gui.start_animation("Patienten ID wird mit Tomedo ID aktualisiert")
dicom_processing.update_patient_id_in_transfer_folder(current_dat_folder, patienten_id)
print("ID wurde aktualisiert")
gui.stop_animation()
print("Erstelle Ausgabedatei")
acq_date, modality = dicom_processing.extract_modality(current_dat_folder)
if acq_date and modality:
file_management.write_to_file(acq_date, modality, AUSGABE_DATEI)
else:
print("Warnung: Konnte Modality-Daten nicht extrahieren")
# Verarbeite die kopierten DICOM-Dateien synchron
await dicom_processing.process_dicom_files(current_dat_folder, gui, name, vorname, geburtsdatum, server_url, server_username, server_pw)
gui.stop_animation()
gui.enable_browse_button()
file_management.eject_cd_if_volume_path(folder_path)
results, new_folder_path = gui.ask_for_more("Sollen mehr Bilddaten für den Patienten eingelesen werden?")
print(f"Dialogergebnis: {results}")
if results and new_folder_path: # Wenn der Benutzer 'Ja' auswählt
gui.reset_progress(total_files)
folder_path = new_folder_path
if not results: # Wenn der Benutzer 'Nein' auswählt
break
def main():
global config, name, vorname, geburtsdatum, patienten_id
# Argumente prüfen
if len(sys.argv) != 5:
print("Bitte genau vier Argumente angeben: Name, Vorname, Geburtsdatum, Patienten_ID")
sys.exit(1)
# Argumente zuweisen
_, name, vorname, geburtsdatum, patienten_id = sys.argv
print(f"Name: {name}, Vorname: {vorname}, Geburtsdatum: {geburtsdatum}, Patienten_ID: {patienten_id}")
config = load_config() # Stellen Sie sicher, dass config geladen wird, bevor GUI initiiert wird
gui = GUI(None, TRANSFER_FOLDER, lambda: None, config)
# Definiere den Callback nach der Erstellung der GUI-Instanz
def on_folder_selected_callback(folder):
# Plane die Coroutine im asyncio Event-Loop
asyncio.run_coroutine_threadsafe(on_folder_selected_coro(folder, gui), loop)
# Setze den Callback in der GUI-Instanz
gui.on_folder_selected = on_folder_selected_callback
# Funktion, die überprüft, ob eine CD eingelegt ist, und diese verarbeitet
def check_cd_and_select():
cd_path = file_management.check_for_cd_at_start()
if cd_path:
print(f"CD gefunden: {cd_path}")
gui.root.after(100, lambda: on_folder_selected_callback(cd_path))
else:
print("Keine CD gefunden, bitte Ordner manuell auswählen.")
# Stelle sicher, dass on_app_close als Methode der GUI-Klasse definiert ist
gui.root.protocol("WM_DELETE_WINDOW", gui.on_app_close)
# Überprüfe, ob eine CD eingelegt ist, bevor die GUI vollständig gestartet wird
# Überprüfe die Einstellung und führe ggf. die CD-Überprüfung aus
if config.get('check_for_cd', '0') == '1':
check_cd_and_select()
# Starte die GUI
gui.start()
if __name__ == "__main__":
main()