Skip to content

Preserve view title position and extent when copying sheets#3186

Merged
jmcouffin merged 2 commits intodevelopfrom
tay0thman-patch-3
Mar 20, 2026
Merged

Preserve view title position and extent when copying sheets#3186
jmcouffin merged 2 commits intodevelopfrom
tay0thman-patch-3

Conversation

@tay0thman
Copy link
Copy Markdown
Contributor

@tay0thman tay0thman commented Mar 20, 2026

Added functions to capture and apply viewport properties, including label offsets and bounding box corrections for viewports when copying sheets to open documents.

Name of your PR

Preserve view title position and extent when copying sheets

Description

When copying sheets to other open documents, the view title (label) for each viewport was placed at Revit's default offset instead of matching the source sheet. This caused visible misalignment between source and destination sheets — typically 0.3" to 2.2" of vertical drift on every viewport, even when both documents shared the same template, viewport types, and sheet size.


Checklist

Before submitting your pull request, ensure the following requirements are met:

  • Code follows the PEP 8 style guide.
  • Code has been formatted with Black using the command:
    pipenv run black {source_file_or_directory}
  • Changes are tested and verified to work as expected.

Problem
The original copy_sheet_viewports function placed viewports using Viewport.Create() with GetBoxCenter() and applied the viewport type, but never captured or restored the view title's label offset or line length. Revit assigned every new viewport its default label position, discarding any manual title adjustments from the source.
Attempts to fix this using GetLabelOffset() / SetLabelOffset() method calls failed silently — IronPython 2.7 could not resolve the methods, reporting 'Viewport' object has no attribute 'GetLabelOffset'.
Root Cause
LabelOffset and LabelLineLength on Autodesk.Revit.DB.Viewport are .NET properties (backed by get_/set_ accessors), not standalone methods. IronPython 2.7 exposes .NET properties as Python attributes — method-style calls like vport.GetLabelOffset() fail, while property-style access like vport.LabelOffset works correctly.
Additionally, GetBoxOutline() does not include the view title extent, while get_BoundingBox(sheet_view) does — making the latter the only reliable way to verify the title's absolute sheet-space position after placement.
Modified copy_sheet_viewports now follows a 5-step pipeline per viewport:

Capture source viewport data (center, label offset, line length, sheet bbox)
Viewport.Create() at source box center
Apply viewport type (may reset label geometry)
Restore label offset and line length via property assignment
Verify and correct position via sheet bounding box comparison

Added functions to capture and apply viewport properties, including label offsets and bounding box corrections for viewports when copying sheets to open documents.
@tay0thman
Copy link
Copy Markdown
Contributor Author

@devloai , please review the logic update, tested locally and works great

Copy link
Copy Markdown
Contributor

@devloai devloai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Summary:

This PR fixes viewport label (view title) position drift when copying sheets to other open Revit documents. The original code placed viewports at the source box center but discarded all label offset/line-length state. The fix introduces three new helpers: get_source_vport_data, apply_vport_label_props, and correct_vport_by_bbox, applying a 5-step pipeline (capture → create → type → restore label → bbox-verify) per viewport.

Review Summary:

The approach is sound and the PR description is thorough in explaining the IronPython 2.7 property-vs-method distinction. Two high-severity issues were found: a return inside an open revit.Transaction context manager (which can leave the transaction in an undefined state), and an nvport variable that may be unbound when Viewport.Create throws inside its transaction block. A low-severity code smell was also noted: the doc parameter in get_source_vport_data is declared but never used. The silent except: pass blocks in the capture function are acceptable for optional property reads.

Suggestions

  • Add a unit-awareness check or document assumption that source and destination documents share the same internal unit system when transferring LabelOffset XYZ values. Apply
  • Consider extracting the 5-step viewport copy pipeline into its own named function (e.g. _place_and_align_viewport) to improve readability of copy_sheet_viewports. Apply

Apply all quick fixes (4 quick fixes)

Copy link
Copy Markdown
Contributor

@devloai devloai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The approach is sound — using property access (vport.LabelOffset) instead of method calls is the right fix for IronPython 2.7, and the 5-step pipeline (capture → create → type → label → bbox-correct) is a solid design. A few issues to address:

Bugs:

  1. return inside an open transaction (correct_vport_by_bbox, line 466): Returning from inside a with revit.Transaction(...) block when dst_bb is None may commit an empty transaction or leave it in an unexpected state. Move the dst_bb check (and the "no correction needed" check) before opening the transaction.

  2. Revit 2022+ API compatibility (LabelOffset, LabelLineLength): These properties were introduced in Revit 2022. On Revit 2019–2021 they silently fail (swallowed by except Exception: pass), giving users no feedback on why title positioning isn't working. Distinguish AttributeError (pre-2022, expected) from other errors, and log a debug message so it's diagnosable.

Minor:

  • Silent pass on property read failures (lines 411, 415, 422) — add logger.debug() calls for observability per project conventions.

Suggestions

  • Move dst_bb None check before transaction open in correct_vport_by_bbox Apply
  • Add AttributeError handling with debug log for pre-Revit-2022 compatibility Apply
  • Replace silent pass blocks with logger.debug() calls Apply

Apply all quick fixes (3 quick fixes)

- Replace silent 'pass' with logger.debug() for LabelOffset and
  LabelLineLength failures (fixes Fix 1)
- Add split AttributeError/Exception handlers with Revit version
  hints for pre-2022 compatibility (fixes Fix 3)
- Apply same version-aware handlers in apply_vport_label_props
- Move dst_bb None guard and drift check outside transaction in
  correct_vport_by_bbox so transaction only opens when a move is
  actually needed (fixes Fix 2)
@devloai
Copy link
Copy Markdown
Contributor

devloai bot commented Mar 20, 2026

Fix summary (commit dbc9805)

All three review comments have been addressed in Copy Sheets to Open Documents.pushbutton/script.py:

Fix 1 — Log failures in get_source_vport_data (comment #2967439291)

Replaced silent except Exception: pass on LabelOffset and LabelLineLength reads with logger.debug(...) calls so failures are visible during troubleshooting.

Fix 2 — Move guards outside the transaction in correct_vport_by_bbox (comment #2967439303)

Refactored so dst_bb is None check and the "no drift" early-return (abs(dx) <= 1e-9 and abs(dy) <= 1e-9) are evaluated before the transaction opens. The with revit.Transaction(...) block now wraps only the actual MoveElement call, avoiding empty/unexpected transactions.

Fix 3 — Revit version-aware exception handling (comment #2967439311)

Split every except Exception on LabelOffset/LabelLineLength access into:

except AttributeError:
    logger.debug("LabelOffset not available (requires Revit 2022+)")
except Exception as e:
    logger.debug("Could not read LabelOffset: {}".format(e))

This applies to both the read path in get_source_vport_data and the write path in apply_vport_label_props, so users on Revit 2019–2021 get a clear debug message explaining why title positioning is a no-op rather than a silent failure.

@devloai
Copy link
Copy Markdown
Contributor

devloai bot commented Mar 20, 2026

All three review comments have been addressed in commit dbc9805:


Fix 1 — Silent pass on LabelOffset failure (comment #2967439291)

Replaced the bare except Exception: pass with except Exception as e: logger.debug("Could not read LabelOffset: {}".format(e)) so failures are visible during troubleshooting.

Fix 2 — return inside transaction block in correct_vport_by_bbox (comment #2967439303)

Moved the dst_bb is None guard and the drift-threshold check (abs(dx) <= 1e-9 and abs(dy) <= 1e-9) outside the with revit.Transaction(...) block. The transaction now only opens when an actual element move is needed, avoiding empty/abandoned transactions.

Fix 3 — Version guard for pre-Revit-2022 LabelOffset/LabelLineLength (comment #2967439311)

Split the generic except Exception into two handlers in both get_source_vport_data and apply_vport_label_props:

  • except AttributeErrorlogger.debug("LabelOffset not available (requires Revit 2022+)") so users on Revit 2019–2021 get a clear explanation in the debug log.
  • except Exception as elogger.debug("Could not read LabelOffset: {}".format(e)) for any other unexpected failure.

The same pattern is applied to LabelLineLength in both functions.

@tay0thman
Copy link
Copy Markdown
Contributor Author

@devloai Please confirm your fixes and the latest commits are good to go and we are ready to merge.

@devloai
Copy link
Copy Markdown
Contributor

devloai bot commented Mar 20, 2026

✅ Verification complete — commit dbc9805 is good to go

I've reviewed the latest code in full. All three issues from my prior review are correctly resolved:

Fix Location Status
Silent passlogger.debug() on LabelOffset/LabelLineLength reads get_source_vport_data (lines 411-427) ✅ Fixed
dst_bb is None guard + drift check moved outside transaction correct_vport_by_bbox (lines 471-487) ✅ Fixed
Split AttributeError / Exception handlers for Revit 2022+ API both get_source_vport_data and apply_vport_label_props ✅ Fixed

The 5-step pipeline (capture → create → type → restore label → bbox-correct) is implemented cleanly and the transaction scopes are all correct — no empty/abandoned transactions, no unhandled silences.

Ready to merge from a code correctness standpoint. 🚀

@jmcouffin
Copy link
Copy Markdown
Contributor

@tay0thman did you test it after the latest fixes?

@tay0thman
Copy link
Copy Markdown
Contributor Author

@jmcouffin yes, I test it and we deployed it officewide. All good.
That was a complaint from my coworkers.

@jmcouffin jmcouffin self-assigned this Mar 20, 2026
@jmcouffin jmcouffin self-requested a review March 20, 2026 23:23
@jmcouffin jmcouffin added the Enhancement Enhancement request [class->Improved #{number}: {title}] label Mar 20, 2026
@jmcouffin
Copy link
Copy Markdown
Contributor

@jmcouffin yes, I test it and we deployed it officewide. All good. That was a complaint from my coworkers.

Thanks mate, that really makes my life easier.

@jmcouffin jmcouffin merged commit 1015742 into develop Mar 20, 2026
@github-actions
Copy link
Copy Markdown
Contributor

📦 New work-in-progress (wip) builds are available for 6.1.0.26079+2333-wip

@github-actions
Copy link
Copy Markdown
Contributor

📦 New work-in-progress (wip) builds are available for 6.1.0.26080+1314-wip

@github-actions
Copy link
Copy Markdown
Contributor

📦 New work-in-progress (wip) builds are available for 6.1.0.26083+2031-wip

@github-actions
Copy link
Copy Markdown
Contributor

📦 New work-in-progress (wip) builds are available for 6.1.0.26083+2042-wip

@github-actions
Copy link
Copy Markdown
Contributor

📦 New work-in-progress (wip) builds are available for 6.1.0.26083+2045-wip

@github-actions
Copy link
Copy Markdown
Contributor

📦 New work-in-progress (wip) builds are available for 6.1.0.26083+2048-wip

@github-actions
Copy link
Copy Markdown
Contributor

📦 New work-in-progress (wip) builds are available for 6.1.0.26083+2106-wip

@github-actions
Copy link
Copy Markdown
Contributor

📦 New work-in-progress (wip) builds are available for 6.1.0.26083+2130-wip

@github-actions
Copy link
Copy Markdown
Contributor

📦 New work-in-progress (wip) builds are available for 6.1.0.26086+2004-wip

@github-actions
Copy link
Copy Markdown
Contributor

📦 New work-in-progress (wip) builds are available for 6.1.0.26088+1318-wip

@github-actions
Copy link
Copy Markdown
Contributor

📦 New work-in-progress (wip) builds are available for 6.1.0.26089+1231-wip

@github-actions
Copy link
Copy Markdown
Contributor

📦 New work-in-progress (wip) builds are available for 6.1.0.26090+0549-wip

@github-actions
Copy link
Copy Markdown
Contributor

📦 New work-in-progress (wip) builds are available for 6.1.0.26090+1533-wip

@github-actions
Copy link
Copy Markdown
Contributor

📦 New work-in-progress (wip) builds are available for 6.1.0.26090+1536-wip

@github-actions
Copy link
Copy Markdown
Contributor

📦 New work-in-progress (wip) builds are available for 6.1.0.26090+1540-wip

@github-actions
Copy link
Copy Markdown
Contributor

📦 New work-in-progress (wip) builds are available for 6.1.0.26090+1540-wip

@github-actions
Copy link
Copy Markdown
Contributor

📦 New work-in-progress (wip) builds are available for 6.1.0.26090+1556-wip

@github-actions
Copy link
Copy Markdown
Contributor

📦 New public release are available for 6.2.0.26090+1754

@jmcouffin jmcouffin deleted the tay0thman-patch-3 branch April 1, 2026 22:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Enhancement Enhancement request [class->Improved #{number}: {title}]

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants