RhinoDoc.CreateHeadless duplicates template viewports and SaveAs writes an invalid ActiveViewId
I think I’ve found a Rhino 9 WIP issue involving RhinoDoc.CreateHeadless() and viewport state.
When I create a headless document from a 3DM template containing the standard four viewports, the resulting document contains eight viewports. After saving it, the output file still contains eight viewports, and its ActiveViewId does not match any saved viewport ID.
This means the active/maximized viewport from the template is not restored when the resulting file is opened normally.
This is with Rhino 9 WIP, RhinoCommon 9.0.26237.15343.
The CreateHeadless documentation says that the template initializes the new document. The lower-level document-creation documentation also says that default views are created only when the template does not contain views: CRhinoCreateDocumentOptions.
To reproduce, first save a normal empty Rhino document with its standard four viewports as C:\Temp\template.3dm, then run this in the Python 3 Script Editor:
import os
import tempfile
import Rhino
folder = os.path.join(tempfile.gettempdir(), "RhinoHeadlessViewportRepro")
template_path = os.path.join(folder, "template.3dm")
output_path = os.path.join(folder, "output.3dm")
os.makedirs(folder, exist_ok=True)
def print_doc(label, doc):
views = list(doc.Views.GetStandardRhinoViews())
active = doc.Views.ActiveView
active_id = active.ActiveViewport.Id if active else None
ids = [view.ActiveViewport.Id for view in views]
print(label)
print("count:", len(views))
print("active:", active_id)
print("active present:", active_id in ids)
for view in views:
print(view.ActiveViewport.Name, view.ActiveViewport.Id, view.Maximized)
def print_file(label, path):
model = Rhino.FileIO.File3dm.Read(path)
try:
ids = [view.Viewport.Id for view in model.Views]
print(label)
print("count:", model.Views.Count)
print("active:", model.Settings.ActiveViewId)
print("active present:", model.Settings.ActiveViewId in ids)
for view in model.Views:
print(view.Name, view.Viewport.Id, view.Maximized)
finally:
model.Dispose()
source = Rhino.RhinoDoc.ActiveDoc
template = Rhino.FileIO.File3dm()
top_id = None
for source_view in source.Views.GetStandardRhinoViews():
view = Rhino.DocObjects.ViewInfo(source_view.ActiveViewport)
is_top = source_view.ActiveViewport.Name == "Top"
view.Maximized = is_top
template.Views.Add(view)
if is_top:
top_id = view.Viewport.Id
if top_id is None:
raise RuntimeError("The current document has no Top viewport.")
template.Settings.ActiveViewId = top_id
print("template saved:", template.Write(template_path, Rhino.RhinoApp.ExeVersion))
template.Dispose()
print_file("template file", template_path)
doc = Rhino.RhinoDoc.CreateHeadless(template_path)
try:
print_doc("headless document", doc)
print("output saved:", doc.SaveAs(output_path))
finally:
doc.Dispose()
print_file("saved file", output_path)
print("folder:", folder)
In my test:
- The template contains four viewports.
- The headless document contains eight.
- The saved file contains eight.
- The saved
ActiveViewIddoes not match any viewport in the file.
As a workaround, I reopen the saved file using File3dm, set Settings.ActiveViewId to the intended Top viewport ID, mark that viewport as maximized, and write the file again.
Does this look like a Rhino bug, or is there a different API that should be used to preserve the template’s viewport state in a headless document?