Python data to Rhino field in titleblock? Possible?

I’ve been trying to replicate an AutoCAD feature that reads a viewport/detail scale and assigns the info to a layout/paperspace field.

Recently, in a discussion on setting detail viewports using architectural scales, Gijs provided a python script. Basically, select the detail and then select the scale from a list. (I altered it a little to add more scale choices and a lock option after the choice)

Anyway, I was wondering if there was a way in python for the data in the scale choice to be used as input for a Rhino field in a title block? Could the “Key” in “User Text” be that output?

Discussion and Python script here.

Hi,

How about letting the script set a usertext on the detail:

the text is setup like this:

Using this fx button:

In python you have rs.SetUserText() method to add Usertext to an object.

I’m very new to python, but I’ll fiddle with this.

Great, let us know if you run into issues!
Good luck!

1 Like

Ok. I diddled but am at a loss. I was working from the script setDetailScaleYN.py originally by Gijs. (I cut and pasted for the Y/N at the end as that works better for my flow)

At any rate, I’m reminded that I don’t know what I’m doing in python and my attempts to get the results of the “scale” to be user text that displays on the layout has failed. I followed the above instruction, re user text, but couldn’t get the scale choice to become the user text. Any suggestions to a script that I can look at that does something similar? Takes the results of the routine and creates text based on it?

setDetailScaleYN.py (1.8 KB)

Hi @CalypsoArt, are you referring to the page to model ratio as displayed in the properties if you select a detail ? If yes, then you might get the required scale factor like below:

import Rhino
import scriptcontext
import rhinoscriptsyntax as rs

def DoSomething():
    
    detail_id = rs.GetObject("Pick detail", rs.filter.detail)
    if not detail_id: return
    
    # get detail geometry and page to model ratio
    detail_geo = rs.coercegeometry(detail_id, True)
    page_ratio = detail_geo.PageToModelRatio
    
    # round the ratio to 4 digits
    text = str(round(page_ratio, 4))
    
    # prompt for text insertion point
    pt = rs.GetPoint("Pick text location")
    if not pt: return
    
    # add the text
    text_id = rs.AddText( text, pt, height=10, font="Arial")
    
DoSomething()

Explanation: If your properties dialog for a detail shows eg:

1.00 millimeters on page
0.25 millimeters in model

above script would give a page to model ratio of 4. Does that help ?

_
c.

Thanks, It helps learn more of Python functions and syntax. However, the script I attached does everything I want other than place the results of the selection as text on a layout. That’s what I’m trying to solve.

After my detail boundary is selected, a popup box gives me the scale options in imperial, of which I choose one. The script then sets the detail to the scale. It seems to me, that the info of the choice should be able to create/write the text choice to a location on a title block. Basically a "Print scale choice text at location X on the layout. I think that was what Willem was referring to in his suggestion to use “rs.SetUserText”

Hi @CalypsoArt, if you do not have the _Text object inside your title block yet, you might first create it. This is shown in the last line of my example above how this could be done.

text_id = rs.AddText("Hello World", pt, height=10, font="Arial")

It requires a point (the variable pt) where the text should be created though. If you already have the text created eg. with the _Text command, you can set it’s content with your scale value like so:

import Rhino
import scriptcontext
import rhinoscriptsyntax as rs

def DoSomething():
    
    obj_id = rs.GetObject("Select text object", rs.filter.annotation)
    if not obj_id: return
    
    if not rs.IsText(obj_id): return
    
    # your scale value
    scale = 1.23456
    
    # set it to the text object
    rs.TextObjectText(obj_id, "MyScale: {}".format(scale))
    
DoSomething()

what @Willem suggested goes a bit further. It requires that you have access to your text object too, and set it’s UserText value to the scale value which then is used as the text which is displayed for this text object. Does this help ?

_
c.

So, you have a script that sets the detail to scale, based on a chosen scale.
What you would like to do next is edit a text on the title block displaying the scale of that detail.

Below is a single method you can insert in your script that sets the detail scale.
When the script has set the detail to scale and the layout and not the detail is active, you can call this method.

import rhinoscriptsyntax as rs



def set_namedtexts_on_active_layout(OBJ_NAME, NEW_TEXT):
    """
    find all text objects named OBJ_NAME that are on the current layout
    and set their text to NEW_TEXT
    """
    
    
    #collect all texts named 'detailscale'
    all_named = rs.ObjectsByName(OBJ_NAME)
    all_namedtexts = [id for id in all_named if rs.IsText(id)] 
    
    #get the id of the currently active layout view
    layout_id = rs.CurrentView(return_name=False)
    
    for scale_text_id in all_namedtexts:
        #get the rhino object from the id and check what viewport/layout id it is assigned to
        rhino_obj = rs.coercerhinoobject(scale_text_id)
        text_view_id = rhino_obj.Attributes.ViewportId
        
        #check if the layout_id equals the text object viewport id 
        if layout_id == text_view_id:
            #set the text to the new_text
            rs.TextObjectText(scale_text_id, NEW_TEXT)



set_namedtexts_on_active_layout('detailscale', 'scale= 1:4')

set_namedtexts_on_active_layout('myname', 'another new text')

layout_texts.3dm (39.4 KB)

If you run the script example above on the example 3dm the 2 texts on the layout will be changed.

Let me know if this makes sense or if I need to elaborate more.

_Willem

I got a moment to look at this again. I’m blindly swinging. Anyway, I added some of Clement’s code and now setting the scale in the titleblock works. However, it requires the step of selecting the text to be rewritten with the scale. This an improvement and forces me to correct the listed scale before the routine will end. No more forgetting and having the wrong text listed. Still, I’d love if this could be automatic.
I tried incorporating Willem’s code but did not get what I wanted. In my python ignorance, I get two different routines running. Willem’s that rewrites the TEXT “DetailScale”, etc., and the other that sets the detail/viewport scale. However, I’ve been unable to make the scale choice become the text on the layout.

Script with Clement’s code added attached.

setDetailScaleYN.py (2.0 KB)

Hi,

So in the code, you need a way to automatically identify the textobject to set the scale text to.
Currently you have it working so that you can select the text and have the text changed.

What would be a way for the code to identify the text object?
Suppose you name the textobject ‘detailscaletext’ than after changing the detail scale the script below searches for text objects on the current layout that are named ‘detailscaletext’ and sets the variable scale as the new text:

import Rhino
import rhinoscriptsyntax as rs
import scriptcontext as sc


def set_namedtexts_on_active_layout(OBJ_NAME, NEW_TEXT):
    """
    find all text objects named OBJ_NAME that are on the current layout
    and set their text to NEW_TEXT
    """
    
    
    #collect all texts named 'detailscale'
    all_named = rs.ObjectsByName(OBJ_NAME)
    all_namedtexts = [id for id in all_named if rs.IsText(id)] 
    
    #get the id of the currently active layout view
    layout_id = rs.CurrentView(return_name=False)
    
    for scale_text_id in all_namedtexts:
        #get the rhino object from the id and check what viewport/layout id it is assigned to
        rhino_obj = rs.coercerhinoobject(scale_text_id)
        text_view_id = rhino_obj.Attributes.ViewportId
        
        #check if the layout_id equals the text object viewport id 
        if layout_id == text_view_id:
            #set the text to the new_text
            rs.TextObjectText(scale_text_id, NEW_TEXT)



def setDetailScale():
    """
    this script changes the scale of a detail to a selected scale from a listbox
    version 1.1
    www.studiogijs.nl
    """
    detail = rs.GetObject("select detail", filter = 32768)
    if not detail:
        return
    detail = rs.coercerhinoobject(detail)
    if not detail.DetailGeometry.IsParallelProjection:
        return
    #modify the scale
    items = ["1/16\"=1'-0\"", "1/8\"=1'-0\"", "3/16\"=1'-0\"", "1/4\"=1'-0\"", "3/8\"=1'-0\"", "1/2\"=1'-0\"", "3/4\"=1'-0\"", "1\"=1'-0\"", "1-1/2\"=1'-0\"", "1\"=0'-6\"", "1:1"]
    scale = rs.ListBox(items, "Select scale", "Scale detail")
    if not scale:
        return
    if scale == "1/16\"=1'-0\"":
        intScale = 120
    elif scale == "1/8\"=1'-0\"":
        intScale = 96
    elif scale == "3/16\"=1'-0\"":
        intScale = 64
    elif scale == "1/4\"=1'-0\"":
        intScale = 48
    elif scale == "3/8\"=1'-0\"":
        intScale = 32
    elif scale == "1/2\"=1'-0\"":
        intScale = 24
    elif scale == "3/4\"=1'-0\"":
        intScale = 16
    elif scale == "1\"=1'-0\"":
        intScale = 12
    elif scale == "1-1/2\"=1'-0\"":
        intScale = 8
    elif scale == "1\"=0'-6\"":
        intScale = 6
    elif scale == "1:1":
        intScale = 1
    detail.DetailGeometry.SetScale(intScale, sc.doc.ModelUnitSystem, 1, sc.doc.PageUnitSystem)
    #lock detail
    items = ["YES","NO"]
    lock = rs.ListBox(items,"Lock Detail/s","YES","NO")
    if lock == "YES":
        detail.DetailGeometry.IsProjectionLocked = True
    elif lock == "NO":
        detail.DetailGeometry.IsProjectionLocked = False
    detail.CommitChanges()
    sc.doc.Views.Redraw()
        
    # Set Layout scale
    set_namedtexts_on_active_layout('detailscaletext', scale)
    

if __name__ == '__main__':
    setDetailScale()
    

Can you get this to work al your end?
I realize my code is not straightforward to comprehend so let me know if you want me to elaborate.

-Willem

Willem, this works! Thanks to you and all who helped. I look forward to this being built in to Rhino, but this solves for one of the features that keeps me with AC.

1 Like