Python Help Setting Current View Within Loop / Inserting Block

Hello,

I have a bit of python code I’m working on that inserts blocks into the current layout view but am trying to insert a block into each layout view within the list of input views.

The psuedo code is as follows:

get layout names
for each layout name
set current view
insert block

Expected result:

Input L = list of layout names
Input BN = block name
Input B = boolean toggle to activate script

Return = block inserted at each layout view

Here is the code thus far:

import Rhino
import scriptcontext as sc
#import System.Drawing
import rhinoscriptsyntax as rs
#import os

def layouts_get_by_names(layout_names):
    layouts = []
    for layout_name in layout_names:
        view = Rhino.RhinoDoc.ActiveDoc.Views.Find(layout_name, True)
        if isinstance(view, Rhino.Display.RhinoPageView):
            layouts.append(view)
    return layouts


if B:
    #setup variables
    block_name = BN
    #loop through created layouts & insert block
    for i, view in enumerate(layouts_get_by_names(L)):
        print "Layout Names: " + str(L)
        rs.CurrentView = L[i]
        print L[i]
        rs.InsertBlock( block_name, insertion_point=[0,0,0] )

Currently it inserts the blocks at the current view only, I am trying to iterate through the list of layout names and insert 1 block at each layout.

As it stands if I have say 5 layouts in the input, currently it inserts 5 blocks into the first layout only and thats only if in Rhino I’m already in that view.

I’m stuck on trying to make each layout view the current view, spawn the block, move onto the next and repeat until the list is exhausted.

Obviously I’m new to this and any help is much appreciated, I’ve been scouring the forums and web tutorials but can’t seem to get past this loop issue.

Thank you for your time!

Hi @michaelvollrath,

Maybe something more like this?

import Rhino
import scriptcontext as sc

def test_insert_title_block():
    
    idef_name = "TitleBlock"
    idef = sc.doc.InstanceDefinitions.Find(idef_name)
    if not idef:
        print("{0} does not exist.".format(idef_name))
        return
    
    page_views = sc.doc.Views.GetViewList(False, True)
    if not page_views or len(page_views) == 0:
        print("No page views")
        return
    
    xform = Rhino.Geometry.Transform.Identity
    
    attribs = sc.doc.CreateDefaultAttributes()
    attribs.Space = Rhino.DocObjects.ActiveSpace.PageSpace
    
    for pv in page_views:
        attribs.ViewportId = pv.MainViewport.Id
        sc.doc.Objects.AddInstanceObject(idef.Index, xform, attribs)
        pv.Redraw()

if __name__ == "__main__":
    test_insert_title_block()

– Dale

Amazing, this works great, thank you so much Dale!

I modified it to receive a boolean trigger and block name input and this works well!
I am also trying to modify it to only populate on layouts named in the “LayoutNames” input. My idea is that I may generate different layouts with different sheet sizes and therefore use different title blocks per that sheet “set”.

Ideally the inputs could be grafted lists as exampled with some pretend values below:

LayoutNames =

{0;0} Part01
{0;1} Part02
{0;2} Part03

{1;0} SubAssembly01
{1;1} SubAssembly02

{2;0} SomeOtherSheetName

BlockNames =

{0} Arch D - Title Block 01

{1} Arch E1 - Title Block 02b

{2} Tabloid - Title Block 11x17

Currently, when I update the realtime layout baker upstream, it also inserts the title blocks onto the new sheets automatically which is great.

Now I’m just trying to modify the code to use the LayoutNames only.

I’m querying page views with the new Rhino 8 “Query Pages node”, getting a guid from that, and passing it into the script and I think that part is working okay.

I’m stuck with a runtime error on line 16 and unsure of the first step in tackling the layout names problem as mentioned above.

Code Thus Far:

import Rhino
import scriptcontext as sc

def test_insert_title_block():

    idef_name = BlockName
    layout_names = LayoutNames
    idef = sc.doc.InstanceDefinitions.Find(idef_name)
    if not idef:
        print("{0} does not exist.".format(idef_name))
        return
    
#    page_views = sc.doc.Views.GetViewList(False, True)
# Get LayoutNames Input To Set page_views
    page_views = sc.doc.Views.Find(layout_names)
    if not page_views or len(page_views) == 0:
        print("No page views")
        return
    
    xform = Rhino.Geometry.Transform.Identity
    
    attribs = sc.doc.CreateDefaultAttributes()
    attribs.Space = Rhino.DocObjects.ActiveSpace.PageSpace
    
    for pv in page_views:
        attribs.ViewportId = pv.MainViewport.Id
        sc.doc.Objects.AddInstanceObject(idef.Index, xform, attribs)
        pv.Redraw()

if __name__ == "__main__" and Activate:
    test_insert_title_block()

Thank you for any further help or info pointed in the right direction!

Okay I got it working with the following modification at line 16

Now I can just feed it a list of layout names as strings and skip the whole guid step.

ChatGPT to the rescue on this part lol.

Thanks Dale for your help!

Here’s the updated code thus far:

import Rhino
import scriptcontext as sc

def test_insert_title_block(BlockName, LayoutNames=[]):

    idef_name = BlockName
    idef = sc.doc.InstanceDefinitions.Find(idef_name)
    if not idef:
        print("{0} does not exist.".format(idef_name))
        return
    
    inserted = False
    for layout_name in LayoutNames:
        page_view = sc.doc.Views.Find(layout_name, False)
        if not page_view:
            print("Layout '{0}' not found".format(layout_name))
            continue
        
        existing_objs = sc.doc.Objects.FindByUserString("TitleBlockLayout", layout_name, True)
        if not any(obj.InstanceDefinition == idef for obj in existing_objs):
            xform = Rhino.Geometry.Transform.Identity
            attribs = sc.doc.CreateDefaultAttributes()
            attribs.Space = Rhino.DocObjects.ActiveSpace.PageSpace
            attribs.ViewportId = page_view.MainViewport.Id
            sc.doc.Objects.AddInstanceObject(idef.Index, xform, attribs)
            page_view.Redraw()
            inserted = True
    
    if not inserted:
        print("Block '{0}' already exists on all specified layouts.".format(idef_name))

if __name__ == "__main__" and Activate:
    test_insert_title_block(BlockName, LayoutNames)


Last step is creating a logic check to prevent title blocks being inserted again if the layouts are dynamically updated upstream.

In lines 18-31 I’m attempting to check if a block matching the title block definition has already been established on the layout and if so, skip inserting that block again, else if no block is present, go ahead and insert the block.

It doesn’t throw any errors but it also seems to still duplicate the blocks anyways if upstream logic is re-ran.

Any ideas on that part? Thank you!

Hi @michaelvollrath,

Use InstanceDefinition.GetReferences to get all of the instance objects that reference the title block definition:

idef_name = "TitleBlock"

idef = sc.doc.InstanceDefinitions.Find(idef_name)
if not idef:
    print("{0} does not exist.".format(idef_name))
    return

irefs = idef.GetReferences(0)
if len(irefs) == 0:
    print("{0} is unused.".format(idef_name))
    return

Now that you have a list of instance references, search for ones whose ObjectAttributes.Space is Rhino.DocObjects.ActiveSpace.PageSpace and whose ObjectAttributes.ViewportId matches a page view’s main viewport id.

– Dale

Thank you Dale!

That helped and I’ve got it working as expected now.

It will only add title blocks to sheets without an existing title block.

Here is resulting code I ended up with:

import Rhino
import scriptcontext as sc

sc.doc = Rhino.RhinoDoc.ActiveDoc

def test_insert_title_block(BlockName, LayoutNames=[]):

    idef_name = BlockName
    idef = sc.doc.InstanceDefinitions.Find(idef_name)
    if not idef:
        print("{0} does not exist.".format(idef_name))
        return
    
    inserted = False
    for layout_name in LayoutNames:
        page_view = sc.doc.Views.Find(layout_name, False)
        if not page_view:
            print("Layout '{0}' not found".format(layout_name))
            continue
        
        existing_objs = sc.doc.Objects.FindByUserString("TitleBlockLayout", layout_name, True)
        existing_instance = None
        for obj in existing_objs:
            if obj.InstanceDefinition == idef and obj.Attributes.ViewportId == page_view.MainViewport.Id:
                existing_instance = obj
                break
                
        if existing_instance:
            print("Block '{0}' already exists on layout '{1}'.".format(idef_name, layout_name))
        else:
            xform = Rhino.Geometry.Transform.Identity
            attribs = sc.doc.CreateDefaultAttributes()
            attribs.Space = Rhino.DocObjects.ActiveSpace.PageSpace
            attribs.ViewportId = page_view.MainViewport.Id
            attribs.SetUserString("TitleBlockLayout", layout_name)
            sc.doc.Objects.AddInstanceObject(idef.Index, xform, attribs)
            page_view.Redraw()
            inserted = True
    
    if not inserted:
        print("Block '{0}' already exists on all specified layouts.".format(idef_name))

if __name__ == "__main__" and Activate:
    test_insert_title_block(BlockName, LayoutNames)

sc.doc = ghdoc

Thanks again for the help!