- Anpassungen an Konfigurations- und Bibliotheksdateien vorgenommen

- logging in dxf2lib.py implementiert
- Umgebungsvariablen in setenv.bat für log-Verzeichnis und Bibliothek-Pfad hinzugefügt
- Bibliothek-Pfad in plant2dxf.py angepasst
This commit is contained in:
2025-07-30 15:23:07 +02:00
parent eb63f0f97f
commit c38b9861d4
4 changed files with 285 additions and 77 deletions
+2
View File
@@ -12,8 +12,10 @@ set PROJECT_LIB=%PROJECT%\lib
set PROJECT_DATA=%PROJECT%\data
set PROJECT_WORK=%PROJECT%\work
set PROJECT_TEST=%PROJECT%\testdata
set PROJECT_LOG=%PROJECT%\log
if not exist %PROJECT%\data mkdir %PROJECT%\data
if not exist %PROJECT%\log mkdir %PROJECT%\log
set INSTALL_DIR="%ONEDRIVE%\Desktop\Kabeltool"
+21
View File
@@ -1,2 +1,23 @@
# Allgemeine Konfigurationsdatei, welche die einzulesenden Layer, sowie geometrische Abhängigkeiten steuert.
# General Config-File, that controls the Layers being read while processing a certain dxf file
[dxf2lib]
# Text-Positionierung und Formatierung
# Vertikaler Abstand des Texts über dem Block (in DXF-Einheiten)
text_offset_y = 175
# Horizontaler Abstand des Texts links vom Block (in DXF-Einheiten)
text_offset_x = -150
# Maximale Zeichenlänge pro Textzeile (bei Überschreitung wird umgebrochen)
max_len = 9
# Schriftgröße des Texts (in DXF-Einheiten)
text_height = 20
# Abstand zwischen zwei Textzeilen bei Umbrüchen (in DXF-Einheiten)
line_spacing = 22
# Block-Anordnung im Raster
# Horizontaler Abstand zwischen Blöcken in einer Reihe (in DXF-Einheiten)
block_spacing_x = 350
# Anzahl Blöcke pro Zeile im Raster
blocks_per_row = 20
# Vertikaler Abstand zwischen Zeilen im Raster (in DXF-Einheiten)
block_spacing_y = 500
+260 -75
View File
@@ -4,8 +4,25 @@ from ezdxf.math import Vec2
import argparse
import sys
import shutil
import datetime
import configparser
import logging
def check_environment_var(env_str: str, fallback: str) -> str:
"""
Prüft eine Umgebungsvariable und gibt den Wert oder einen Fallback zurück.
Args:
env_str (str): Name der zu prüfenden Umgebungsvariable
fallback (str): Fallback-Wert, falls die Variable nicht gesetzt ist
Returns:
str: Wert der Umgebungsvariable oder Fallback-Wert
Note:
Gibt eine Warnung aus, falls die Umgebungsvariable nicht gesetzt ist
"""
out_path = os.environ.get(env_str)
if out_path:
return out_path
@@ -13,6 +30,20 @@ def check_environment_var(env_str: str, fallback: str) -> str:
return fallback
def get_bbox(entities):
"""
Berechnet die Bounding Box für eine Liste von DXF-Entities.
Args:
entities: Liste von DXF-Entities (ezdxf entities)
Returns:
Vec2 or None: Zentrum der Bounding Box als Vec2-Objekt oder None,
falls keine gültige Geometrie gefunden wurde
Note:
Unterstützt POLYLINE, LWPOLYLINE und andere Entity-Typen.
Fehlerhafte Entities werden übersprungen und protokolliert.
"""
min_x, min_y = float('inf'), float('inf')
max_x, max_y = float('-inf'), float('-inf')
for e in entities:
@@ -43,6 +74,211 @@ def get_bbox(entities):
return Vec2((min_x + max_x) / 2, (min_y + max_y) / 2)
def setup_logger(log_dir):
"""
Erstellt und konfiguriert einen Logger für die DXF2LIB Verarbeitung.
Args:
log_dir (str): Verzeichnis für die Log-Dateien
Returns:
logging.Logger: Konfigurierter Logger
Note:
Erstellt eine Log-Datei mit Zeitstempel und formatiert die Ausgaben
"""
# Logger erstellen
logger = logging.getLogger('dxf2lib')
logger.setLevel(logging.INFO)
# Bestehende Handler entfernen (falls Logger bereits existiert)
for handler in logger.handlers[:]:
logger.removeHandler(handler)
# Zeitstempel für Log-Datei
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
log_file = os.path.join(log_dir, f"dxf2lib_{timestamp}.log")
# File Handler erstellen
file_handler = logging.FileHandler(log_file, encoding='utf-8')
file_handler.setLevel(logging.INFO)
# Console Handler erstellen
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)
# Formatter erstellen
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
file_handler.setFormatter(formatter)
console_handler.setFormatter(formatter)
# Handler zum Logger hinzufügen
logger.addHandler(file_handler)
logger.addHandler(console_handler)
return logger, log_file
def create_block_library(input_dir, output_file, config, logger=None):
"""
Erstellt eine DXF-Block-Bibliothek aus einzelnen DXF-Dateien.
Diese Funktion liest alle DXF-Dateien aus einem Verzeichnis und erstellt
daraus eine Bibliothek mit Blöcken. Die Blöcke werden in einem Raster
angeordnet und mit Beschriftungen versehen. Fehlerhafte Dateien werden
protokolliert.
Args:
input_dir (str): Verzeichnis mit den zu verarbeitenden DXF-Dateien
output_file (str): Pfad zur zu erstellenden Bibliotheks-DXF-Datei
config: ConfigParser-Objekt mit den Konfigurationswerten
logger: Optionaler Logger für Logging-Ausgaben
Note:
- Unterstützte Entity-Typen: LINE, LWPOLYLINE, POLYLINE, SPLINE, CIRCLE, ARC
- Blöcke werden zentriert und in einem 20x20 Raster angeordnet
- Erstellt eine Log-Datei mit Zeitstempel für fehlerhafte Dateien
- Automatische Erstellung des Output-Verzeichnisses falls nötig
"""
doc = ezdxf.new()
msp = doc.modelspace()
x_offset = 0
y_offset = 0
blocks_in_row = 0
error_files = []
processed_files = []
for filename in os.listdir(input_dir):
if not filename.lower().endswith(".dxf"):
continue
filepath = os.path.join(input_dir, filename)
name = os.path.splitext(filename)[0]
try:
src_doc = ezdxf.readfile(filepath)
src_msp = src_doc.modelspace()
entities = list(src_msp)
allowed_types = {"LINE", "LWPOLYLINE", "POLYLINE", "SPLINE", "CIRCLE", "ARC"}
filtered_entities = [e for e in entities if e.dxftype() in allowed_types]
entities = filtered_entities
except Exception as e:
error_msg = f"Fehler beim Lesen von {filename}: {e}"
if logger:
logger.error(error_msg)
else:
print(error_msg)
error_files.append((filename, error_msg))
continue
center = get_bbox(entities)
if center is None:
error_msg = f"Keine gültige Geometrie in {filename}"
if logger:
logger.error(error_msg)
else:
print(error_msg)
error_files.append((filename, error_msg))
continue
if name in doc.blocks:
doc.blocks.delete_block(name)
blk = doc.blocks.new(name=name, base_point=(0,0))
for e in entities:
try:
cp = e.copy()
cp.translate(-center.x, -center.y, 0) # Geometrie verschieben!
blk.add_entity(cp)
except Exception as err:
error_msg = f"Fehler beim Verarbeiten von Entity {e.dxftype()} in {filename}: {err}"
if logger:
logger.error(error_msg)
else:
print(error_msg)
error_files.append((filename, error_msg))
# Platzierung in Reihen und Spalten
msp.add_blockref(name, insert=(x_offset, y_offset))
# Text mit Blocknamen über dem Block
# Werte aus Config holen (Block: [dxf2lib])
section = "dxf2lib"
text_y = y_offset + get_cfg_value(section, "text_offset_y", DEFAULTS["text_offset_y"])
text_offset_x = get_cfg_value(section, "text_offset_x", DEFAULTS["text_offset_x"])
max_len = get_cfg_value(section, "max_len", DEFAULTS["max_len"])
text_height = get_cfg_value(section, "text_height", DEFAULTS["text_height"])
line_spacing = get_cfg_value(section, "line_spacing", DEFAULTS["line_spacing"])
block_spacing_x = get_cfg_value(section, "block_spacing_x", DEFAULTS["block_spacing_x"])
blocks_per_row = get_cfg_value(section, "blocks_per_row", DEFAULTS["blocks_per_row"])
block_spacing_y = get_cfg_value(section, "block_spacing_y", DEFAULTS["block_spacing_y"])
if len(name) > max_len:
first_line = name[:max_len]
second_line = name[max_len:]
# obere Zeile
msp.add_text(first_line, dxfattribs={'height': text_height, 'insert': (x_offset + text_offset_x, text_y)})
# untere Zeile, etwas niedriger (y kleiner)
msp.add_text(second_line, dxfattribs={'height': text_height, 'insert': (x_offset + text_offset_x, text_y - line_spacing)})
else:
msp.add_text(name, dxfattribs={'height': text_height, 'insert': (x_offset + text_offset_x, text_y)})
processed_files.append(filename)
blocks_in_row += 1
x_offset += block_spacing_x # Abstand zwischen Blöcken in einer Reihe
if blocks_in_row == blocks_per_row:
blocks_in_row = 0
x_offset = 0
y_offset -= block_spacing_y # Neue Zeile, nach unten versetzt
if logger:
logger.info(f"Bibliotheks-DXF gespeichert: {output_file}")
logger.info(f"Verarbeitete Dateien: {len(processed_files)}")
logger.info(f"Fehlerhafte Dateien: {len(error_files)}")
if error_files:
logger.warning(f"{len(error_files)} Dateien konnten nicht verarbeitet werden:")
for filename, error_msg in error_files:
logger.error(f"{filename}: {error_msg}")
else:
logger.info("Keine fehlerhaften Dateien gefunden.")
else:
print(f"Bibliotheks-DXF gespeichert: {output_file}")
print(f"Verarbeitete Dateien: {len(processed_files)}")
print(f"Fehlerhafte Dateien: {len(error_files)}")
if error_files:
print(f"Warnung: {len(error_files)} Dateien konnten nicht verarbeitet werden.")
output_dir = os.path.dirname(output_file)
if not os.path.exists(output_dir):
os.makedirs(output_dir, exist_ok=True)
doc.saveas(output_file)
# Standardwerte (falls nicht in der Config)
DEFAULTS = {
"text_offset_y": 175, # Vertikaler Abstand des Texts über dem Block (in DXF-Einheiten)
"text_offset_x": -150, # Horizontaler Abstand des Texts links vom Block (in DXF-Einheiten)
"max_len": 9, # Maximale Zeichenlänge pro Textzeile (bei Überschreitung wird umgebrochen)
"text_height": 20, # Schriftgröße des Texts (in DXF-Einheiten)
"line_spacing": 22, # Abstand zwischen zwei Textzeilen bei Umbrüchen (in DXF-Einheiten)
"block_spacing_x": 350, # Horizontaler Abstand zwischen Blöcken in einer Reihe (in DXF-Einheiten)
"blocks_per_row": 20, # Anzahl Blöcke pro Zeile im Raster
"block_spacing_y": 500, # Vertikaler Abstand zwischen Zeilen im Raster (in DXF-Einheiten)
}
def get_cfg_value(section, key, fallback):
try:
return int(config.get(section, key))
except Exception:
return fallback
if __name__ == "__main__":
# Argumentparser für Kommandozeilenoptionen
parser = argparse.ArgumentParser(description="SVG/XML zu DXF Konverter")
@@ -72,86 +308,35 @@ if __name__ == "__main__":
OUTPUT_FILE = os.path.join(check_environment_var("PROJECT_DATA", "."),"block_libaries", f"{args.name}.dxf")
doc = ezdxf.new()
msp = doc.modelspace()
# Prüfe und erstelle log-Verzeichnis falls nötig
log_dir = check_environment_var("PROJECT_LOG", ".")
if not os.path.exists(log_dir):
os.makedirs(log_dir, exist_ok=True)
print(f"Log-Verzeichnis erstellt: {log_dir}")
x_offset = 0
y_offset = 0
blocks_in_row = 0
# Logger Setup
logger, log_file = setup_logger(log_dir)
logger.info("=== DXF2LIB Verarbeitung gestartet ===")
logger.info(f"Input-Verzeichnis: {INPUT_DIR}")
logger.info(f"Output-Datei: {OUTPUT_FILE}")
for filename in os.listdir(INPUT_DIR):
if not filename.lower().endswith(".dxf"):
continue
filepath = os.path.join(INPUT_DIR, filename)
name = os.path.splitext(filename)[0]
try:
src_doc = ezdxf.readfile(filepath)
src_msp = src_doc.modelspace()
entities = list(src_msp)
allowed_types = {"LINE", "LWPOLYLINE", "POLYLINE", "SPLINE", "CIRCLE", "ARC"}
filtered_entities = [e for e in entities if e.dxftype() in allowed_types]
entities = filtered_entities
except Exception as e:
print(f"Fehler beim Lesen von {filename}: {e}")
continue
center = get_bbox(entities)
if center is None:
print(f"Keine gültige Geometrie in {filename}")
continue
if name in doc.blocks:
doc.blocks.delete_block(name)
blk = doc.blocks.new(name=name, base_point=(0,0))
for e in entities:
try:
cp = e.copy()
cp.translate(-center.x, -center.y, 0) # Geometrie verschieben!
blk.add_entity(cp)
except Exception as err:
print(f"Fehler in {filename}: {err}")
# Platzierung in Reihen und Spalten
msp.add_blockref(name, insert=(x_offset, y_offset))
# Text mit Blocknamen über dem Block
text_y = y_offset + 175 # 175 Einheiten über dem Block
max_len = 9
text_height = 20
line_spacing = 22 # Abstand zwischen den zwei Textzeilen
if len(name) > max_len:
first_line = name[:max_len]
second_line = name[max_len:]
# obere Zeile
msp.add_text(first_line, dxfattribs={'height': text_height, 'insert': (x_offset - 150, text_y)})
# untere Zeile, etwas niedriger (y kleiner)
msp.add_text(second_line, dxfattribs={'height': text_height, 'insert': (x_offset - 150, text_y - line_spacing)})
else:
msp.add_text(name, dxfattribs={'height': text_height, 'insert': (x_offset - 150, text_y)})
blocks_in_row += 1
x_offset += 350 # Abstand zwischen Blöcken in einer Reihe
if blocks_in_row == 20:
blocks_in_row = 0
x_offset = 0
y_offset -= 500 # Neue Zeile, nach unten versetzt
print(f"Bibliotheks-DXF gespeichert: {OUTPUT_FILE} \n")
output_dir = os.path.dirname(OUTPUT_FILE)
if not os.path.exists(output_dir):
os.makedirs(output_dir, exist_ok=True)
doc.saveas(OUTPUT_FILE)
# Lade Config-Datei
config = configparser.ConfigParser()
config_path = os.path.join(check_environment_var("PROJECT_CFG", "."), "allgemein.cfg")
config.read(config_path, encoding="utf-8")
logger.info(f"Config-Datei geladen: {config_path}")
# Erstelle die Block-Bibliothek
logger.info(f"Erstelle Block-Bibliothek aus {INPUT_DIR}...")
create_block_library(INPUT_DIR, OUTPUT_FILE, config, logger)
if not args.keep:
print(f"Keep-Argument nicht gesetzt, lösche Input-Verzeichnis: {INPUT_DIR} \n")
logger.info(f"Keep-Argument nicht gesetzt, lösche Input-Verzeichnis: {INPUT_DIR}")
try:
if os.path.exists(INPUT_DIR):
shutil.rmtree(INPUT_DIR)
print(f"Verzeichnis '{INPUT_DIR}' wurde gelöscht.")
logger.info(f"Verzeichnis '{INPUT_DIR}' wurde gelöscht.")
except Exception as err:
print(f"[WARN] Konnte '{INPUT_DIR}' nicht löschen: {err}")
logger.warning(f"Konnte '{INPUT_DIR}' nicht löschen: {err}")
logger.info("=== DXF2LIB Verarbeitung abgeschlossen ===")
+2 -2
View File
@@ -288,8 +288,8 @@ def main(csv_path: Path, lib_path: Path, cfg_path: Path,
except Exception as e:
sys.exit(f"Fehler beim Lesen der Bibliothek '{lib_path}': {e}")
else:
print(f"[INFO] Keine Bibliothek gefunden unter {lib_path}. "
"Komplexe Formen werden übersprungen.")
print(f"[INFO] Keine Bibliothek gefunden unter {lib_path}.")
exit(1)
# Neue Ziel­zeichnung (DXF R2018)
doc = ezdxf.new(dxfversion="R2018")