MoveTitleBlock.py

# purpose and IO: IN[0]=run (bool, optional), IN[1]=sheet Ids (list/int/ElementId or None), OUT=report list of dicts # Moves all paper-space elements so the largest title block's bounding-box Min becomes (0,0) per sheet. # Skips CAD Import/Link instances named like "0,0,0" or "0,0-Origin". One transaction across all sheets. # Note: To avoid CPython generic-binding issues, we try batch MoveElements; on failure we fall back to per-element MoveElement. import clr clr.AddReference('RevitAPI') clr.AddReference('RevitServices') from Autodesk.Revit.DB import ( FilteredElementCollector, ViewSheet, FamilyInstance, BuiltInCategory, ImportInstance, ElementTransformUtils, XYZ, ElementId ) from RevitServices.Persistence import DocumentManager from RevitServices.Transactions import TransactionManager # Optional: if batch move works in your CPython, you can enable the List+XYZ path below. try: from System.Collections.Generic import List as ClrList HAS_CLR_LIST = True except: HAS_CLR_LIST = False doc = DocumentManager.Instance.CurrentDBDocument # ---------------- inputs ---------------- run = True if (len(IN) == 0 or IN[0] in (True, None)) else False input_ids = IN[1] if len(IN) > 1 else None def _to_elemid(e): if isinstance(e, ElementId): return e try: return ElementId(int(e)) except: return None target_sheet_ids = None if input_ids: ids = [] seq = input_ids if isinstance(input_ids, (list, tuple)) else [input_ids] for x in seq: eid = _to_elemid(x) if eid: ids.append(eid) if ids: target_sheet_ids = set(ids) # ---------------- helpers ---------------- def tblocks_on_sheet(sheet): return list( FilteredElementCollector(doc, sheet.Id) .OfCategory(BuiltInCategory.OST_TitleBlocks) .OfClass(FamilyInstance) .WhereElementIsNotElementType() ) def bbox_area_in_view(elem, view): bb = elem.get_BoundingBox(view) if not bb: return 0.0 return abs((bb.Max.X - bb.Min.X) * (bb.Max.Y - bb.Min.Y)) def largest_titleblock(sheet): tbs = tblocks_on_sheet(sheet) if not tbs: return None, None ranked = [(bbox_area_in_view(tb, sheet), tb) for tb in tbs] ranked.sort(key=lambda x:x[0], reverse=True) tb = ranked[0][1] bb = tb.get_BoundingBox(sheet) return tb, bb def is_origin_cad_import(elem): # True for ImportInstance whose instance/type name includes 0,0,0 or 0,0-origin (spaces ignored) if not isinstance(elem, ImportInstance): return False name = "" try: name = elem.Name or "" except: name = "" if not name: try: sym = doc.GetElement(elem.GetTypeId()) name = sym.Name if sym else "" except: name = "" s = name.lower().replace(" ", "") return ("0,0,0" in s) or ("0,0-origin" in s) or ("00,0-origin" in s) def paper_elems(sheet): # All view-owned (paper-space) elements return list(FilteredElementCollector(doc, sheet.Id).WhereElementIsNotElementType()) # ---------------- main ---------------- report = [] if not run: OUT = ["Set IN[0]=True to run."] else: sheets = list(FilteredElementCollector(doc).OfClass(ViewSheet)) if target_sheet_ids: sheets = [s for s in sheets if s.Id in target_sheet_ids] TransactionManager.Instance.EnsureInTransaction(doc) for s in sheets: tb, tb_bb = largest_titleblock(s) if not tb or not tb_bb: report.append({"sheet": s.SheetNumber, "name": s.Name, "status": "no title block"}) continue move = XYZ(-tb_bb.Min.X, -tb_bb.Min.Y, 0.0) if move.IsZeroLength(): report.append({"sheet": s.SheetNumber, "name": s.Name, "status": "already at (0,0)"}) continue all_elems = paper_elems(s) elems = [e for e in all_elems if not is_origin_cad_import(e)] skipped = len(all_elems) - len(elems) if not elems: report.append({"sheet": s.SheetNumber, "name": s.Name, "status": "no movable paper elements"}) continue # Unpin movables pinned = [] for e in elems: try: if getattr(e, "Pinned", False): e.Pinned = False pinned.append(e) except: pass moved = 0 tried_batch = False batch_ok = False # Try batch (works in some CPython setups) if HAS_CLR_LIST: try: id_list = ClrList[ElementId]() for e in elems: id_list.Add(e.Id) # Prefer XYZ overload; CPython should bind to ICollection[ElementId], XYZ ElementTransformUtils.MoveElements(doc, id_list, move) moved = len(elems) tried_batch = True batch_ok = True except: batch_ok = False # Fallback: move per element (robust in CPython; still one transaction) if not batch_ok: for e in elems: try: ElementTransformUtils.MoveElement(doc, e.Id, move) moved += 1 except: pass # Restore pins for e in pinned: try: e.Pinned = True except: pass report.append({ "sheet": s.SheetNumber, "name": s.Name, "status": "moved", "moved_count": moved, "skipped_origin_cad": skipped, "batch_tried": tried_batch, "batch_ok": batch_ok, "delta": (round(move.X,6), round(move.Y,6), 0.0) }) TransactionManager.Instance.TransactionTaskDone() OUT = report

Comments

Popular posts from this blog

Revit area plans adding new types and references (Gross and rentable)

Revit CSV file manager for families and re-exporting to a CSV file