15 KiB
utils.py API Documentation
Comprehensive documentation for the utils.py module containing helper functions for file operations, DXF manipulation, and data processing.
Table of Contents
- File Operations
- Environment Variables
- JSON Operations
- DXF File Operations
- ezdxf Operations (High-Level DXF Helpers)
- Dictionary Operations
File Operations
check_file_in_work(work_dir, filename) → Tuple[Path, bool]
Check if a file exists, either at the given path or in the work directory.
Args:
work_dir(str | Path): Working directory to check if file not found at filename pathfilename(str | Path): Path to the file to check
Returns:
Tuple[Path, bool]: (resolved_path, exists_flag)resolved_path: Path where file was found (or would be)exists_flag: True if file exists, False otherwise
Example:
from utils import check_file_in_work
work_dir = Path("c:/work")
filepath, exists = check_file_in_work(work_dir, "test.dxf")
if exists:
print(f"File found at: {filepath}")
Environment Variables
check_environment_var(env_str: str) → Path
Get and validate an environment variable as a Path.
Args:
env_str(str): Name of the environment variable
Returns:
Path: Path object from the environment variable
Exits:
- Exits the program if the environment variable is not set or empty
Example:
from utils import check_environment_var
work_dir = check_environment_var('PROJECT_WORK')
config_dir = check_environment_var('PROJECT_CFG')
JSON Operations
to_json(d: Any, pretty: bool = True) → str
Convert a Python object to a JSON string.
Args:
d(Any): Object to convert to JSONpretty(bool): If True, format with indentation for readability (default: True)
Returns:
str: JSON string representation of the object
Note:
Uses ensure_ascii=False to properly display German umlauts (ä, ö, ü)
Example:
from utils import to_json
data = {"name": "Test", "wert": 42}
json_str = to_json(data)
print(json_str)
load_json(jsonfilename: str) → dict
Load JSON data from a file.
Args:
jsonfilename(str): Path to the JSON file
Returns:
dict: Parsed JSON data as a dictionary
Example:
from utils import load_json
config = load_json("config.json")
write_results(jsn_results: str, out_dir: Path, filename: str) → None
Write JSON results to a file.
Args:
jsn_results(str): JSON string to writeout_dir(Path): Output directory pathfilename(str): Name of the output file
Example:
from utils import write_results, to_json
from pathlib import Path
data = {"results": [1, 2, 3]}
write_results(to_json(data), Path("output"), "results.json")
DXF File Operations
dxf_is_binary(dxf_path: Path) → bool
Check if a DXF file is in binary format.
Args:
dxf_path(Path): Path to the DXF file
Returns:
bool: True if the file is a binary DXF, False otherwise
Example:
from utils import dxf_is_binary
from pathlib import Path
if dxf_is_binary(Path("drawing.dxf")):
print("Binary DXF file detected")
else:
print("ASCII DXF file")
get_dxf_file(filepath: Path) → ezdxf.Drawing
Load a DXF file using ezdxf.
Args:
filepath(Path): Path to the DXF file
Returns:
ezdxf.Drawing: ezdxf Drawing object
Exits:
- Exit code 1: I/O errors
- Exit code 2: Invalid/corrupted DXF files
Example:
from utils import get_dxf_file
from pathlib import Path
doc = get_dxf_file(Path("drawing.dxf"))
msp = doc.modelspace()
ezdxf Operations
High-level helper functions for working with ezdxf objects.
Attribute Extraction
extract_insert_attributes(insert, error_collector=None) → dict
Extrahiert alle Attribute aus einem INSERT-Block. Unterstützt zweistufige Blockstruktur.
Args:
insert: INSERT-Entity des Blockserror_collector(optional): ErrorCollector für Warnungen
Returns:
dict: Dictionary mit allen gefundenen Attributen{tag: text}
Note:
- Funktioniert ohne explizites doc-Objekt wenn
insert.docverfügbar - Für neue Code: Verwenden Sie
extract_insert_attributes_with_doc()
Example:
from utils import extract_insert_attributes
for entity in msp.query('INSERT'):
attributes = extract_insert_attributes(entity)
if 'IO' in attributes:
print(f"IO: {attributes['IO']}")
extract_insert_attributes_with_doc(doc, insert, error_collector=None) → dict
Extrahiert alle Attribute aus einem INSERT-Block mit explizitem doc-Parameter. Empfohlen für neuen Code - bessere Unterstützung für zweistufige Blockstrukturen.
Args:
doc: DXF-Dokumentinsert: INSERT-Entity des Blockserror_collector(optional): ErrorCollector für Warnungen
Returns:
dict: Dictionary mit allen gefundenen Attributen{tag: text}
Example:
from utils import extract_insert_attributes_with_doc
doc = get_dxf_file(Path("drawing.dxf"))
msp = doc.modelspace()
for entity in msp.query('INSERT'):
attributes = extract_insert_attributes_with_doc(doc, entity)
print(f"Block: {entity.dxf.name}, Attributes: {attributes}")
extract_attributes_with_positions(insert_iterable, round_decimals=1) → Tuple[list, list]
Wandelt eine Iterable von INSERT-Objekten in zwei Listen um.
Kompatibilität mit getpositions.py attribs_to_dicts().
Args:
insert_iterable: Iterable von INSERT-Entities (z.B. msp oder iterdxf.modelspace())round_decimals(int): Anzahl Nachkommastellen für Positions-Rundung (default: 1)
Returns:
Tuple[list, list]: (all_inserts, all_positions)all_inserts: Liste von Dicts mit Attribut-Tags und deren Textwertenall_positions: Liste von Dicts mit Attribut-Tags und deren (x, y, z)-Positionen
Note:
- Blöcke ohne Attribute werden übersprungen
- Ersetzt die alte
attribs_to_dicts()aus getpositions.py
Example:
from utils import extract_attributes_with_positions
all_inserts, all_positions = extract_attributes_with_positions(msp)
for insert_data, positions in zip(all_inserts, all_positions):
print(f"Attributes: {insert_data}")
print(f"Positions: {positions}")
Layer Operations
ensure_layer_exists(doc, layer_name, color=None, linetype=None, lineweight=None, description=None) → Layer
Stellt sicher, dass ein Layer existiert und erstellt ihn falls nötig.
Args:
doc: DXF-Dokumentlayer_name(str): Name des Layerscolor(int, optional): ACI Color Index (1-255)linetype(str, optional): Name des Linientyps (z.B. 'CONTINUOUS', 'DASHED')lineweight(int, optional): Linienbreite in mm*100 (z.B. 25 für 0.25mm)description(str, optional): Layer-Beschreibung
Returns:
Layer: Layer-Objekt (existierend oder neu erstellt)
Example:
from utils import ensure_layer_exists
# Einfacher Layer
ensure_layer_exists(doc, 'ILS_MOTOR')
# Layer mit Farbe und Beschreibung
ensure_layer_exists(doc, 'ILS_CABLE', color=5, description='Cable routing layer')
# Layer mit allen Optionen
ensure_layer_exists(doc, 'ILS_SPECIAL',
color=3, linetype='DASHED',
lineweight=25, description='Special equipment')
create_layers_from_config(doc, layer_definitions) → None
Erstellt mehrere Layer aus einer Konfiguration.
Args:
doc: DXF-Dokumentlayer_definitions(dict): Dictionary mit Layer-Definitionen
Format:
{
'layer_name': {
'color': int (optional),
'linetype': str (optional),
'lineweight': int (optional),
'description': str (optional)
},
...
}
Example:
from utils import create_layers_from_config
layer_config = {
'ILS_MOTOR': {'color': 1, 'description': 'Motor layer'},
'ILS_SENSOR': {'color': 3, 'lineweight': 25},
'ILS_CABLE': {'color': 5, 'linetype': 'DASHED'}
}
create_layers_from_config(doc, layer_config)
Geometry Creation
add_rectangle_lwpolyline(msp, x, y, width, height, layer='0', color=None, rgb=None, closed=True) → LWPolyline
Fügt eine rechteckige LWPOLYLINE hinzu.
Args:
msp: Modelspace des DXF-Dokumentsx(float): X-Koordinate der unteren linken Eckey(float): Y-Koordinate der unteren linken Eckewidth(float): Breite des Rechtecksheight(float): Höhe des Rechteckslayer(str): Layer-Name (default: '0')color(int, optional): ACI Color Index (1-255)rgb(tuple, optional): RGB-Tuple (r, g, b) mit Werten 0-255closed(bool): Ob die Polylinie geschlossen sein soll (default: True)
Returns:
LWPolyline: LWPOLYLINE-Entity
Example:
from utils import add_rectangle_lwpolyline
# Einfaches Rechteck
add_rectangle_lwpolyline(msp, 0, 0, 100, 50, layer='ILS_FRAME')
# Rechteck mit Farbe
add_rectangle_lwpolyline(msp, 10, 10, 200, 100, color=5)
# Rechteck mit RGB-Farbe
add_rectangle_lwpolyline(msp, 20, 20, 150, 75, rgb=(255, 0, 0))
add_text_with_alignment(msp, text, x, y, height=50, layer='0', color=None, halign=0, valign=0, rotation=0.0, style='Standard') → Text
Fügt einen Text mit Ausrichtung hinzu.
Args:
msp: Modelspace des DXF-Dokumentstext(str): Textinhaltx(float): X-Koordinatey(float): Y-Koordinateheight(float): Texthöhe (default: 50)layer(str): Layer-Name (default: '0')color(int, optional): ACI Color Index (1-255)halign(int): Horizontale Ausrichtung (0=links, 1=mitte, 2=rechts)valign(int): Vertikale Ausrichtung (0=basis, 1=unten, 2=mitte, 3=oben)rotation(float): Rotation in Grad (default: 0.0)style(str): Textstil-Name (default: 'Standard')
Returns:
Text: TEXT-Entity
Example:
from utils import add_text_with_alignment
# Einfacher Text links unten
add_text_with_alignment(msp, "Test", 100, 100, layer='ILS_TEXT')
# Zentrierter Text
add_text_with_alignment(msp, "Centered", 200, 200, halign=1, valign=2)
# Rotierter Text mit Farbe
add_text_with_alignment(msp, "Rotated", 300, 300,
color=3, rotation=45.0, height=75)
Block Operations
add_blockref_with_attributes(msp, block_name, insert_point, attributes=None, layer='0', scale=1.0, rotation=0.0) → Insert
Fügt eine Block-Referenz (INSERT) mit Attributen hinzu.
Args:
msp: Modelspace des DXF-Dokumentsblock_name(str): Name des Blocksinsert_point(tuple): Einfügepunkt als (x, y) oder (x, y, z) Tupleattributes(dict, optional): Dictionary mit Attribut-Namen und -Wertenlayer(str): Layer-Name (default: '0')scale(float): Skalierungsfaktor (default: 1.0)rotation(float): Rotation in Grad (default: 0.0)
Returns:
Insert: INSERT-Entity mit gesetzten Attributen
Example:
from utils import add_blockref_with_attributes
# Block ohne Attribute
add_blockref_with_attributes(msp, "MOTOR_SYMBOL", (100, 100))
# Block mit Attributen
attrs = {"IO": "MA0001", "KENNZEICHNUNG": "=A01+UH00"}
add_blockref_with_attributes(msp, "io", (200, 200), attributes=attrs)
# Block mit Skalierung und Rotation
add_blockref_with_attributes(msp, "SENSOR", (300, 300),
scale=0.5, rotation=90.0, layer='ILS_SENSOR')
Entity Queries
query_entities_by_layer(msp, entity_type=None, layers=None, exclude_layers=None) → list
Filtert Entities nach Typ und Layer.
Args:
msp: Modelspace des DXF-Dokumentsentity_type(str, optional): Entity-Typ (z.B. 'INSERT', 'LINE', 'TEXT'), None für allelayers(list, optional): Liste der zu inkludierenden Layer (None = alle außer exclude_layers)exclude_layers(list, optional): Liste der zu exkludierenden Layer (None = keine Exklusion)
Returns:
list: Liste der gefilterten Entities
Examples:
from utils import query_entities_by_layer
# Alle INSERTs auf Layer 'ILS_MOTOR'
motors = query_entities_by_layer(msp, 'INSERT', layers=['ILS_MOTOR'])
# Alle Entities außer auf Layer '0'
entities = query_entities_by_layer(msp, exclude_layers=['0'])
# Alle LINEs auf ILS_* Layern (mit zusätzlichem Filtern)
all_lines = query_entities_by_layer(msp, 'LINE')
ils_lines = [e for e in all_lines if e.dxf.layer.startswith('ILS_')]
# Alle INSERTs auf mehreren Layern
inserts = query_entities_by_layer(msp, 'INSERT',
layers=['ILS_MOTOR', 'ILS_SENSOR'])
Dictionary Operations
merge_two_dicts(x, y) → dict
Merge two dictionaries, with values from y overwriting those in x.
Args:
x(dict): First dictionary (base)y(dict): Second dictionary (override values)
Returns:
dict: New dictionary with merged values
Example:
from utils import merge_two_dicts
defaults = {"color": 1, "layer": "0", "scale": 1.0}
overrides = {"color": 5, "scale": 2.0}
result = merge_two_dicts(defaults, overrides)
# Result: {"color": 5, "layer": "0", "scale": 2.0}
Migration Guide
From old attribs_to_dicts() to new API
Old code (getpositions.py):
from getpositions import attribs_to_dicts
all_inserts, all_positions = attribs_to_dicts(msp)
New code:
from utils import extract_attributes_with_positions
all_inserts, all_positions = extract_attributes_with_positions(msp)
From old extract_block_attributes() to new API
Old code (create_numbers.py):
def extract_block_attributes(doc, insert, error_collector=None):
# 48 lines of code...
return attributes
New code:
from utils import extract_insert_attributes_with_doc as extract_block_attributes
# Direct usage - no local function needed
Performance Notes
extract_attributes_with_positions: O(n) where n = number of INSERT entitiesquery_entities_by_layer: O(n) where n = total entities in mspensure_layer_exists: O(1) - uses dict lookupcreate_layers_from_config: O(m) where m = number of layers to create
Error Handling
Most functions follow these error handling patterns:
- Silent fallback: Functions like
extract_insert_attributes()return empty dict on error - ErrorCollector integration: Functions accepting
error_collectorparameter log warnings - System exit:
check_environment_var()andget_dxf_file()exit on critical errors - Exception propagation: JSON and file operations raise native Python exceptions
Best Practices
- Always use
extract_insert_attributes_with_doc()for new code (better error handling) - Use
ensure_layer_exists()before adding entities to avoid missing layer errors - Prefer
query_entities_by_layer()over manual filtering for better readability - Use
create_layers_from_config()when setting up multiple layers from configuration files - Pass
error_collectorwhen available for comprehensive error tracking
Version History
- v1.2.0 (2026-01-30): Added 8 new ezdxf helper functions
extract_insert_attributes_with_doc()extract_attributes_with_positions()ensure_layer_exists()add_rectangle_lwpolyline()add_text_with_alignment()add_blockref_with_attributes()query_entities_by_layer()create_layers_from_config()
- v1.1.0: Refactored
extract_insert_attributes()to support two-level blocks - v1.0.0: Initial release with file, JSON, and DXF operations