Files
dxfmakros/lib/elemente/utils.py
T

246 lines
8.2 KiB
Python

# 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()