- 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:
+260
-75
@@ -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 ===")
|
||||
|
||||
Reference in New Issue
Block a user