Skip to content

Explicit update of the view template#3192

Merged
jmcouffin merged 6 commits intopyrevitlabs:developfrom
thumDer:3110-template-handling
Mar 31, 2026
Merged

Explicit update of the view template#3192
jmcouffin merged 6 commits intopyrevitlabs:developfrom
thumDer:3110-template-handling

Conversation

@thumDer
Copy link
Copy Markdown
Contributor

@thumDer thumDer commented Mar 23, 2026

Explicit update of the view template

Description

As an addition to #3155 the view range update is explicitly executed on the template itself.


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.

Related Issues


Additional Notes

Include any additional context, screenshots, or considerations for reviewers.


Thank you for contributing to pyRevit! 🎉

@devloai
Copy link
Copy Markdown
Contributor

devloai bot commented Mar 23, 2026

Unable to trigger custom agent "Code Reviewer". You have run out of credits 😔
Please upgrade your plan or buy additional credits from the subscription page.

jmcouffin
jmcouffin previously approved these changes Mar 24, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR extends the View Range tool behavior so that when a selected plan view’s view range is controlled by a view template, the update is applied directly to the template (matching the intent introduced in #3155 / issue #3110).

Changes:

  • When a controlling view template is detected, the script updates the template’s ViewRange instead of the view’s.
  • Uses a single resolved view target (template vs. source view) for GetViewRange() / SetViewRange() inside the transaction.

@porrt23
Copy link
Copy Markdown

porrt23 commented Mar 24, 2026

@thumDer I greatly appreciate your efforts in this. I copied the updated script provided with this pull request to test it out. I was running into an exception so I added a print statement to see what the problem is in the function "_update_view_range_internal" (starts about line 116). What I am seeing is that with view templates, all of the plane ids return as an invalid "-3". Most of the planes get updated as it goes through the function, except for CutPlane.

view_range = view.GetViewRange() # Line 127
planes = [
    DB.PlanViewPlane.TopClipPlane,
    DB.PlanViewPlane.CutPlane,
    DB.PlanViewPlane.BottomClipPlane,
    DB.PlanViewPlane.ViewDepthPlane,
]
print("-----Before Changing values----")
for p in planes:
    lvl = view_range.GetLevelId(p)
    off = view_range.GetOffset(p)
    print("Plane: {}, LevelId: {}, Offset: {}".format(p, lvl.IntegerValue, off))
image

So I thought I would try to merge the two methods slightly just to see if this would update the view template correctly. I took the source view's cut plane and set it as the cut plane of the view template. This ended up breaking the view, and view template range. "View Template" greys out (only on the source view, not all views with this template) and "View Range" property appears. If I open up the view template's view range, instead of "Associated Level", they are all explicitly set to the source view's cut plane. I did discover that Revit does actually accept the view templates view range with the "-3" levelid as I commented out everything between getting and setting the view range and forced the offset to be set to "-6.0" which differs from the "-5.0" just to see if we can update the offsets, and that worked. Maybe we have to ignore the Level Id for view templates and only take the "Offset from Level value"? What are your thoughts here?

image image image

Before updating view template view range
image

After updating view template view range. "Associated level" is really just "Level 6"
image

image

@porrt23
Copy link
Copy Markdown

porrt23 commented Mar 24, 2026

@jmcouffin I wouldn't merge this in yet by the way, due to the issues above,.

@thumDer thumDer marked this pull request as draft March 24, 2026 16:13
@thumDer
Copy link
Copy Markdown
Contributor Author

thumDer commented Mar 24, 2026

@porrt23 thanks for testing the change, it is somewhat unexpected that it doesn't work, I will have another look at it. FYI the -3 Id for the level is not invalid in this case, this is used internally as one of the relative levels (Current / Above / Below / Unlimited) as you can see here under the static properties of the PlanViewRange class: https://apidocs.co/apps/revit/2025.3/5768cb89-bf98-c58b-e3de-d6b4459f2a87.htm

@porrt23
Copy link
Copy Markdown

porrt23 commented Mar 24, 2026

FYI the -3 Id for the level is not invalid in this case, this is used internally as one of the relative levels (Current / Above / Below / Unlimited)

@thumDer Yeah, I figured that out shortly after I commented. It seems that Revit doesn't like mixing the relative levels with actual level ids? So I'm wondering if for templates, it ignores levels and only focuses on the offsets? Or maybe have to update the pulldowns to match when a template is selected so the user sees the same options (Current / Above / Below / Unlimited) that they would for a normal view template. Let me know if you want me to look into anything specific to help with it more. I'm not as familiar with Pull Requests/github so currently the best I can do is upload the code edits here and have you update this pull request.

@thumDer
Copy link
Copy Markdown
Contributor Author

thumDer commented Mar 29, 2026

FYI the -3 Id for the level is not invalid in this case, this is used internally as one of the relative levels (Current / Above / Below / Unlimited)

@thumDer Yeah, I figured that out shortly after I commented. It seems that Revit doesn't like mixing the relative levels with actual level ids? So I'm wondering if for templates, it ignores levels and only focuses on the offsets? Or maybe have to update the pulldowns to match when a template is selected so the user sees the same options (Current / Above / Below / Unlimited) that they would for a normal view template. Let me know if you want me to look into anything specific to help with it more. I'm not as familiar with Pull Requests/github so currently the best I can do is upload the code edits here and have you update this pull request.

You can try, and let me know if you have something. I did some experiments, and views always return explicit levels, while templates return relative levels, with the negative ids. This means that fixing this is slightly more complicated, we need different set of levels for views, and templates, and on views with templates we have to override the level options to the relative ones. I'll also give it a try.

@thumDer
Copy link
Copy Markdown
Contributor Author

thumDer commented Mar 29, 2026

@porrt23 please test my latest commit, I only had time for a quick test, and it seems to be working, but would like you to test it as well.

@thumDer thumDer marked this pull request as ready for review March 29, 2026 11:37
@porrt23
Copy link
Copy Markdown

porrt23 commented Mar 30, 2026

@thumDer I'm planning on testing within the next few hours. Will keep you updated.

@porrt23
Copy link
Copy Markdown

porrt23 commented Mar 30, 2026

@thumDer this works for me! The only check I would add is for the Cut Plane Level Name. For view templates, I was consistently getting "Unknown" as the level name. The fix I found is to get GenLevel from the source_view. This was giving me the level name as expected. Screenshot and code below. Thanks for fixing this!

                elif plane == DB.PlanViewPlane.CutPlane:
                    # For Cut Plane, show the level name as read-only text
                    if level_id and level_id != DB.ElementId.InvalidElementId:
                        level = self.source_view.Document.GetElement(level_id)
                        if not level:
                            level = self.source_view.GenLevel
                        self.view_model.cutplane_level_name = (
                            level.Name if level else "Unknown"
                        )
                    else:
                        self.view_model.cutplane_level_name = "Unlimited"
image

As a side note (and probably a separate issue to be logged), if you happen to open two View Range Editor windows, the planes continue to update in the 3D view as if the Editor is still open even after you close both windows. I don't think that would be related to this fix. I just found it odd.

@thumDer
Copy link
Copy Markdown
Contributor Author

thumDer commented Mar 30, 2026

I would rather put Current in case of a template to keep it consistent. Would that be acceptable?

Please create a separate issue for the two windows problem. Currently I have a quite dirty solution for that in one of my other tools, but I'm not sure I would admit it publicly :D pyRevit should have a built-in solution for that, following the smartbutton analogy. I will try to figure out something.

@porrt23
Copy link
Copy Markdown

porrt23 commented Mar 30, 2026

Like this instead? Yeah, I would be fine with that to keep everything consistent.

image image

@jmcouffin
Copy link
Copy Markdown
Contributor

Let me know when ready for review @thumDer

@jmcouffin jmcouffin marked this pull request as draft March 31, 2026 15:34
@jmcouffin jmcouffin added the Tools Issues related to pyRevit commands [subsystem] label Mar 31, 2026
@jmcouffin jmcouffin requested a review from Copilot March 31, 2026 15:34
@jmcouffin jmcouffin self-requested a review March 31, 2026 15:34
@thumDer thumDer marked this pull request as ready for review March 31, 2026 15:36
@thumDer
Copy link
Copy Markdown
Contributor Author

thumDer commented Mar 31, 2026

@jmcouffin it is ready

Copy link
Copy Markdown
Contributor

@jmcouffin jmcouffin left a comment

Choose a reason for hiding this comment

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

looks good

@jmcouffin jmcouffin merged commit dddee2c into pyrevitlabs:develop Mar 31, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated 6 comments.

Comment on lines +488 to +492
# above
elif get_elementid_value(level_id) == -2:
plane_level = self._ordered_levels[
ordered_level_ids.index(view_level_id) + 1
]
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

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

Resolving relative level ids via ordered_level_ids.index(view_level_id) + 1 can raise ValueError/IndexError (e.g., if GenLevel isn’t found in _ordered_levels, or when the view’s level is the topmost level and the template uses "Level Above"). Consider precomputing the index once, validating bounds, and falling back to a safe state (e.g., treat as Unlimited/N/A) instead of throwing inside context_changed().

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This could be improved, related to #3192 (comment)

Comment on lines +500 to +502
plane_level = self._ordered_levels[
ordered_level_ids.index(view_level_id) - 1
]
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

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

The "Level Below" mapping uses ordered_level_ids.index(view_level_id) - 1, which will silently wrap to the last item if the index is 0 (Python negative indexing), producing an incorrect level instead of indicating that there is no level below. Add an explicit lower-bound check before indexing.

Suggested change
plane_level = self._ordered_levels[
ordered_level_ids.index(view_level_id) - 1
]
view_level_idx = ordered_level_ids.index(view_level_id)
if view_level_idx == 0:
# No level below the lowest level; treat as unset
plane_level = None
else:
plane_level = self._ordered_levels[view_level_idx - 1]

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

@thumDer thumDer Mar 31, 2026

Choose a reason for hiding this comment

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

This is somewhat valid though, if current is the lowest level, then "Below" can't be interpreted. needs an extra check

Comment on lines 974 to 978
@events.handle("doc-changed")
def doc_changed(sender, args):
try:
context.collect_levels(args.Document)
affected_ids = list(args.GetModifiedElementIds()) + list(
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

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

_source_template is only computed in the source_view setter. If the selected view’s template assignment changes later (or the template’s controlled parameters change) the tool can keep reading/updating the stale template object. Since this PR now applies updates directly to _source_template, this can result in modifying an unintended template. Consider recomputing _source_template inside context_changed()/doc_changed() when the source view or its template-related parameters are affected.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is valid, will handle this too.

@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

@thumDer
Copy link
Copy Markdown
Contributor Author

thumDer commented Mar 31, 2026

since it was merged I will create an additional PR to include the suggested valid improvements.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Tools Issues related to pyRevit commands [subsystem]

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants