# elemente/utils.py # Gemeinsame Hilfsfunktionen fuer alle ILS-Elemente (Kreisel, Eckrad, etc.) # ============================================ import os import json import math import PyDb as Db import PyGe as Ge # Globale Zaehler pro Komponententyp (pro Sitzung) _letzte_nummern = {} def get_data_path(): """Daten-Pfad aus Umgebungsvariable DXFM_DATA lesen.""" dp = os.environ.get("DXFM_DATA", "") if not dp: print("\nFEHLER: DXFM_DATA nicht gesetzt!") return None return dp.replace("\\", "/").rstrip("/") def load_json_data(rel_path): """JSON-Datei relativ zum DXFM_DATA-Pfad laden. Parameter: rel_path - relativer Pfad, z.B. "json/omniflo_boegen.json" Rueckgabe: geparste Daten oder leere Liste bei Fehler """ dp = get_data_path() if not dp: return [] path = f"{dp}/{rel_path}" try: with open(path, "r", encoding="utf-8") as f: return json.load(f) except (FileNotFoundError, json.JSONDecodeError) as e: print(f"\nFEHLER beim Laden von {path}: {e}") return [] def get_block_path(): """Block-Pfad aus Umgebungsvariable DXFM_BLOCKS lesen.""" bp = os.environ.get("DXFM_BLOCKS", "") if not bp: bp = "C:/Users/y.wang/Documents/dxfmakros/Blocks/" print(f"\nUmgebungsvariable DXFM_BLOCKS nicht gefunden. Nutze Standardpfad: {bp}") bp = bp.replace("\\", "/").rstrip("/") + "/" return bp def component_next_number(db, prefix): """Naechste freie Nummer fuer einen Komponententyp ermitteln. Scannt alle INSERT-Entities im Modelspace deren Blockname mit `prefix` beginnt (z.B. "ECKRAD_", "KREISEL_") und liest das NUMMER-Attribut aus. Gibt die naechste freie Nummer zurueck. Entspricht component-next-number aus KreiselInsert.lsp. Parameter: db - aktuelle Datenbank prefix - Blockname-Praefix, z.B. "ECKRAD_" oder "KREISEL_" Rueckgabe: naechste freie Nummer (int) """ global _letzte_nummern prefix_upper = prefix.upper() max_nr = 0 model = Db.BlockTableRecord(db.modelSpaceId(), Db.OpenMode.kForRead) for obj_id in model: ent = Db.Entity(obj_id, Db.OpenMode.kForRead) if not ent.isKindOf(Db.BlockReference.desc()): continue bref = Db.BlockReference(obj_id, Db.OpenMode.kForRead) btr = Db.BlockTableRecord(bref.blockTableRecord(), Db.OpenMode.kForRead) bname = btr.getName() if not bname.upper().startswith(prefix_upper): continue for att_id in bref.attributeIds(): att = Db.AttributeReference(att_id, Db.OpenMode.kForRead) if att.tag().upper() == "NUMMER": try: val = int(att.textString()) if val > max_nr: max_nr = val except ValueError: pass if max_nr == 0: max_nr = _letzte_nummern.get(prefix_upper, 0) _letzte_nummern[prefix_upper] = max_nr + 1 return _letzte_nummern[prefix_upper] def merge_attribs(overrides, attrib_defs): """Attribut-Defaults mit Ueberschreibungen zusammenfuehren. Parameter: overrides - dict mit Ueberschreibungen (darf None sein) attrib_defs - Liste von (TAG, DEFAULT) Tupeln Rueckgabe: dict mit allen Tags """ result = {tag: default for tag, default in attrib_defs} if overrides: result.update(overrides) return result def read_block_attribs(bref): """Alle Attribute einer BlockReference als dict lesen.""" attribs = {} for att_id in bref.attributeIds(): att = Db.AttributeReference(att_id, Db.OpenMode.kForRead) attribs[att.tag()] = att.textString() return attribs def ensure_layer(db, layer_name, color_index, set_active=False): """Layer anlegen falls nicht vorhanden, optional aktiv setzen.""" lt = Db.LayerTable(db.layerTableId(), Db.OpenMode.kForRead) if not lt.has(layer_name): lt.upgradeOpen() lr = Db.LayerTableRecord() lr.setName(layer_name) c = Db.Color() c.setColorIndex(int(color_index)) lr.setColor(c) lt.add(lr) if set_active: layer_id = lt.getAt(layer_name) db.setClayer(layer_id) def ensure_textstyle(db, style_name="Kreisel_Arial", font="Arial"): """Textstil anlegen falls nicht vorhanden.""" tst = Db.TextStyleTable(db.textStyleTableId(), Db.OpenMode.kForRead) if not tst.has(style_name): tst.upgradeOpen() ts = Db.TextStyleTableRecord() ts.setName(style_name) ts.setFont(font, False, False, 0, 0) tst.add(ts) return style_name def ensure_block_from_dwg(db, dwg_name, block_path=None): """Externen DWG-Block in die Datenbank laden falls nicht vorhanden.""" if block_path is None: block_path = get_block_path() bt = Db.BlockTable(db.blockTableId(), Db.OpenMode.kForRead) base_name = os.path.splitext(dwg_name)[0] if not bt.has(base_name): dwg_path = block_path + dwg_name if not os.path.isfile(dwg_path): print(f"\nFehler: {dwg_name} nicht gefunden unter: {dwg_path}") return False aux_db = Db.Database(False, True) aux_db.readDwg(dwg_path) db.insert(aux_db, base_name) return True def ensure_block_from_dxf(db, dxf_path, block_name=None): """DXF-Datei als Block-Definition laden falls nicht vorhanden. Parameter: db - aktuelle Datenbank dxf_path - absoluter Pfad zur DXF-Datei block_name - Name der Block-Definition (default: Dateiname ohne .dxf) Rueckgabe: True wenn erfolgreich, False bei Fehler """ if block_name is None: block_name = os.path.splitext(os.path.basename(dxf_path))[0] bt = Db.BlockTable(db.blockTableId(), Db.OpenMode.kForRead) if not bt.has(block_name): if not os.path.isfile(dxf_path): print(f"\nFehler: DXF nicht gefunden: {dxf_path}") return False aux_db = Db.Database(False, True) aux_db.dxfIn(dxf_path) db.insert(aux_db, block_name) return True def create_attrib_defs(btr, attrib_defs, text_height=50.0): """Unsichtbare Attribut-Definitionen in einer BlockTableRecord erzeugen. Parameter: btr - Db.BlockTableRecord (kForWrite) attrib_defs - Liste von (TAG, DEFAULT) Tupeln text_height - Texthoehe der ATTDEFs """ y_pos = 0.0 for tag, default in attrib_defs: attdef = Db.AttributeDefinition() attdef.setPosition(Ge.Point3d(0, y_pos, 0)) attdef.setHeight(text_height) attdef.setTextString(default) attdef.setPrompt(tag) attdef.setTag(tag) attdef.setInvisible(True) btr.appendAcDbEntity(attdef) y_pos -= text_height * 2.0 def insert_block_with_attribs(db, bname, insert_point, rotation_deg, attrib_values): """Block einfuegen und Attribute aus dict setzen. Parameter: db - aktuelle Datenbank bname - Name der Blockdefinition insert_point - Ge.Point3d rotation_deg - Drehwinkel in Grad attrib_values - dict {TAG: Wert} Rueckgabe: ObjectId der eingefuegten BlockReference """ bt = Db.BlockTable(db.blockTableId(), Db.OpenMode.kForRead) block_id = bt.getAt(bname) model = Db.BlockTableRecord(db.modelSpaceId(), Db.OpenMode.kForWrite) bref = Db.BlockReference(insert_point, block_id) bref.setRotation(math.radians(rotation_deg)) model.appendAcDbEntity(bref) # Attribute aus Block-Definition erzeugen und Werte setzen btr = Db.BlockTableRecord(block_id, Db.OpenMode.kForRead) for ent_id in btr: ent = Db.Entity(ent_id, Db.OpenMode.kForRead) if ent.isKindOf(Db.AttributeDefinition.desc()): attdef = Db.AttributeDefinition(ent_id, Db.OpenMode.kForRead) attref = Db.AttributeReference() attref.setPropertiesFrom(attdef) attref.setTag(attdef.tag()) attref.setInvisible(attdef.isInvisible()) attref.setPosition(attdef.position()) attref.setHeight(attdef.height()) val = attrib_values.get(attdef.tag(), attdef.textString()) attref.setTextString(val) bref.appendAttribute(attref) return bref.objectId()