Schedule Formatting (Note Block Schedules)

Has anyone tried to do something with place schedule formatting.

So I have these two identical schedules with one being less narrow and the other wider. They have this drag handle. I’m poking around with RevitLookup. The formatting I would be interested is a match width. Thought I’d ask to see if anyone has done something similar.

Is this the function you are looking for? SetColumnWidth Method, there is also the matching GetColumnWidth method to allow you to match one schedule to another.

so something i did notice, even if i manually make all widths the same size when i open the schedule, it will not resize on the sheet. It seems like once the schedule is manually stretched by using the drag handle, it will not resize. the only way is to delete it from the sheet, and then place it back again. I am not sure if this is specifically for Note Block schedules because with Schedule/Quantities schedules, if I resize in the schedule editor, it resizes on the sheet, and vice versa. I am curious if Note Block schedules function differently since you can have it placed on multiple sheets and if you resize one, you would not want all others to resize or certain ones to resize just due to how the sheet is laid out.

One the sheet itself, I will end up resize the NoteBlock schedule like this long (example)

image

but in the editor it really only is like (narrow)

It is only when you drag and drop to the sheet that the schedule size is what the editor size what at.

i did manage to make a “collect all others placed, copy anchor/paste to target locations, delete old version” that is doing the trick.

here is the code for that if anyone needs it. And if there is someone who could do a true “match to selected size” version, please share

# -*- coding: utf-8 -*-

import clr
clr.AddReference("RevitAPI")
clr.AddReference("RevitAPIUI")

from Autodesk.Revit.DB import *
from Autodesk.Revit.UI.Selection import ObjectType, ISelectionFilter
from Autodesk.Revit.Exceptions import OperationCanceledException
from System.Collections.Generic import List
from pyrevit import revit, forms, script
from pyrevit.framework import Media


# ============================================================
# COLOR DEFINITIONS
# ============================================================
BLUE   = Media.Color.FromArgb(255, 0, 128, 255)


doc = revit.doc
uidoc = revit.uidoc
view = doc.ActiveView
output = script.get_output()
output.set_width(1400)

try:
    output.window.Topmost = True
except:
    pass


# ============================================================
# FILTER
# ============================================================
class ScheduleInstanceSelectionFilter(ISelectionFilter):
    def AllowElement(self, element):
        return isinstance(element, ScheduleSheetInstance)

    def AllowReference(self, reference, point):
        return True


# ============================================================
# HELPERS
# ============================================================
def fmt_xyz(pt):
    if not pt:
        return "None"
    return "({0:.9f}, {1:.9f}, {2:.9f})".format(pt.X, pt.Y, pt.Z)


def get_schedule_view(inst):
    try:
        return doc.GetElement(inst.ScheduleId)
    except:
        return None


def get_schedule_name(inst):
    sched = get_schedule_view(inst)
    if sched:
        try:
            return sched.Name
        except:
            pass
    return "<Unknown>"


def get_bbox_width(inst, owner_view):
    try:
        bbox = inst.get_BoundingBox(owner_view)
        if bbox:
            return bbox.Max.X - bbox.Min.X
    except:
        pass
    return None


def get_all_schedule_instances_on_sheet(sheet):
    items = []
    try:
        collector = list(
            FilteredElementCollector(doc, sheet.Id).OfClass(ScheduleSheetInstance)
        )
        for inst in collector:
            try:
                if inst.OwnerViewId == sheet.Id and not inst.IsTitleblockRevisionSchedule:
                    items.append(inst)
            except:
                pass
    except:
        pass
    return items


def report_inst(label, inst, owner_sheet):
    output.print_md("## {0}".format(label))
    output.print_md("- **Instance Id:** {0}".format(inst.Id.IntegerValue))
    output.print_md("- **ScheduleId:** {0}".format(inst.ScheduleId.IntegerValue))
    output.print_md("- **Schedule Name:** {0}".format(get_schedule_name(inst)))
    output.print_md("- **Sheet:** {0} - {1}".format(owner_sheet.SheetNumber, owner_sheet.Name))

    # try:
        # output.print_md("- **Point:** `{0}`".format(fmt_xyz(inst.Point)))
    # except:
        # output.print_md("- **Point:** `N/A`")

    # w = get_bbox_width(inst, owner_sheet)
    # if w is not None:
        # output.print_md("- **BBox Width:** `{0:.9f}`".format(w))
    # else:
        # output.print_md("- **BBox Width:** `N/A`")

    output.print_md("")


# ============================================================
# ACTIVE VIEW CHECK
# ============================================================
if not isinstance(view, ViewSheet):
    forms.alert("Active view must be a sheet.", exitscript=True)


# ============================================================
# PICK ANCHOR
# ============================================================
sel_filter = ScheduleInstanceSelectionFilter()

wb1 = forms.WarningBar(title="Select ANCHOR schedule on Active Sheet")
wb1.Resources["pyRevitAccentBrush"] = Media.SolidColorBrush(BLUE)

try:
    with wb1:
        anchor_ref = uidoc.Selection.PickObject(
            ObjectType.Element,
            sel_filter,
            "Pick ANCHOR placed schedule on the active sheet"
        )
except OperationCanceledException:
    script.exit()

anchor = doc.GetElement(anchor_ref.ElementId)
if not anchor or not isinstance(anchor, ScheduleSheetInstance):
    forms.alert("Anchor selection was not a placed schedule.", exitscript=True)

if anchor.OwnerViewId != view.Id:
    forms.alert("Anchor schedule must be on the active sheet.", exitscript=True)

anchor_sheet = view
anchor_name = get_schedule_name(anchor)

if not anchor_name or anchor_name == "<Unknown>":
    forms.alert("Could not determine anchor schedule name.", exitscript=True)

try:
    anchor_pt = anchor.Point
except:
    forms.alert("Could not read anchor schedule point.", exitscript=True)


# ============================================================
# PICK TARGET SHEETS
# ============================================================
sel_sheets = forms.select_sheets(
    title="Select target sheets",
    use_selection=True,
    include_placeholder=False
)

if not sel_sheets:
    script.exit()


# ============================================================
# REPORT INPUTS
# ============================================================
output.print_md("# Match Schedule Width")
output.print_md("")
report_inst("Anchor Schedule", anchor, anchor_sheet)

output.print_md("## Match Rule")
output.print_md("- Only placed schedules with name **{0}** will be replaced.".format(anchor_name))
output.print_md("- Selected sheets: **{0}**".format(len(sel_sheets)))
output.print_md("")


# ============================================================
# PROCESS
# ============================================================
results = []
total_replaced = 0
total_skipped = 0
total_errors = 0

t = Transaction(doc, "Match Schedule Width Across Sheets")
t.Start()

try:
    for sheet in sel_sheets:
        sheet_matches = []
        sheet_skips = []
        sheet_errors = []

        schedule_instances = get_all_schedule_instances_on_sheet(sheet)

        for target in schedule_instances:
            try:
                target_name = get_schedule_name(target)

                if target_name != anchor_name:
                    continue

                if sheet.Id == anchor_sheet.Id and target.Id == anchor.Id:
                    sheet_skips.append("Skipped anchor instance itself")
                    total_skipped += 1
                    continue

                target_id = target.Id
                target_id_int = target_id.IntegerValue
                target_point = target.Point
                target_sheet_num = sheet.SheetNumber
                target_sheet_name = sheet.Name

                translation = target_point - anchor_pt

                ids_to_copy = List[ElementId]()
                ids_to_copy.Add(anchor.Id)

                cp_options = CopyPasteOptions()

                copied_id_list = ElementTransformUtils.CopyElements(
                    anchor_sheet,
                    ids_to_copy,
                    sheet,
                    Transform.CreateTranslation(translation),
                    cp_options
                )

                if not copied_id_list or copied_id_list.Count == 0:
                    raise Exception("Copy operation returned no new elements.")

                doc.Regenerate()

                new_sched = None
                for eid in copied_id_list:
                    elem = doc.GetElement(eid)
                    if isinstance(elem, ScheduleSheetInstance):
                        new_sched = elem
                        break

                if not new_sched:
                    raise Exception("Copy succeeded, but no ScheduleSheetInstance was found in copied elements.")

                try:
                    if target.Pinned:
                        target.Pinned = False
                except:
                    pass

                doc.Delete(target_id)
                doc.Regenerate()

                new_width = get_bbox_width(new_sched, sheet)
                width_note = ""
                if new_width is not None:
                    width_note = " | new width = {0:.9f}".format(new_width)

                # sheet_matches.append(
                    # "Updated schedule {0} on sheet {1} - {2}{3}".format(
                        # target_id_int,
                        # target_sheet_num,
                        # target_sheet_name,
                        # width_note
                    # )
                # )
                # total_replaced += 1

                sheet_matches.append(
                    "Updated schedule(s) on sheet {0} - {1}".format(
                        target_sheet_num,
                        target_sheet_name
                    )
                )
                total_replaced += 1

            except Exception as ex:
                try:
                    tid = target.Id.IntegerValue
                except:
                    tid = -1
                sheet_errors.append("Target {0}: {1}".format(tid, ex))
                total_errors += 1

        results.append((sheet, sheet_matches, sheet_skips, sheet_errors))

    t.Commit()

except Exception as ex:
    try:
        t.RollBack()
    except:
        pass
    forms.alert("Operation failed and was rolled back:\n\n{0}".format(ex), exitscript=True)


# ============================================================
# REPORT
# ============================================================
output.print_md("## Summary")
output.print_md("- **Anchor Schedule Name:** {0}".format(anchor_name))
output.print_md("- **Sheets Selected:** {0}".format(len(sel_sheets)))
output.print_md("- **Schedules Replaced:** {0}".format(total_replaced))
output.print_md("- **Skipped:** {0}".format(total_skipped))
output.print_md("- **Errors:** {0}".format(total_errors))
output.print_md("")

for sheet, matches, skips, errors in results:
    output.print_md("### Sheet: {0} - {1}".format(sheet.SheetNumber, sheet.Name))

    if matches:
        output.print_md("**Updated:**")
        for m in matches:
            output.print_md("- {0}".format(m))

    if skips:
        output.print_md("**Skipped:**")
        for s in skips:
            output.print_md("- {0}".format(s))

    if errors:
        output.print_md("**Errors:**")
        for e in errors:
            output.print_md("- `{0}`".format(e))

    if not matches and not skips and not errors:
        output.print_md("- No matching schedules found.")

    output.print_md("")