This is an unashamed request for a bit of scripting to make my life easier
What I would like to do is to be able to pick an object, preferably a number of objects, and get the layer name as a text block, positioned in the middle of the object (or thereabouts). The text should all be on a separate layer, with a predefined name (or if need be on the current layer). The text properties such as font and size can be the current text settings, no need to request those as part of the script.
I have dabbled with Python scripts in the past, but I am a long way off creating something like this on my own (besides refreshing the bit I did study at the time), so I would be grateful for any help I can get.
Not very professional, but since I was trying to do some Rhino-Python anyway, this gave me an objective. try this, it only does one object for now.
I always have Rhino-Python page open in a tab for reference.
#create TextObject from Object's layer, Randy Gregory 2015
import rhinoscriptsyntax as rs
#text height
tHt=0.75
#Make layer for TextObject
rs.AddLayer("Layer ID Text", (255,38,0))
#Object to get for layer ID
originalObject=rs.GetObject("Select Objects for Layer ID", filter=16, preselect=False, select=True)
if originalObject:
Layer=rs.ObjectLayer(originalObject)
#create point for textObject placement
point = rs.GetPoint("Point for text insertion")
if point:
tObjs=[]
#create your text string
textString=Layer
TextObject = rs.AddText(textString,point,tHt)
#change layer of text string
rs.ObjectLayer(TextObject, "Layer ID Text")
Thanks Randy. I had to add the standard line :āimport rhinoscriptsyntax as rsā, but after that it works just fine. A question though (for my Python-re-education): what does
if point:
tObjs=[]
do?
It is creating the point object from GetPoint so AddText has physical point for insertion placement. AddText needs a point or plane, see syntax below.
I am still a beginner at using Python and sometimes I see code that works and donāt really know why
AddText - Adds a text string to the document.
Syntax
rhinoscriptsyntax.AddText ( text, point_or_plane, height=1.0, font=āArialā, font_style=0, justification=None )
This might be useful if you need to place layer name at object centre .
Thanks again, Randy. I think you have rekindled my interest in Python scripting, I looked up the script that I made before (investigating duplicate line objects), and I immediately saw some things I would like to use in your script, like creating a new layer if one does not exist yet, and making it the current layer from the start. I will struggle forward, thanks for your kick-off.
So Iām a Python greenhorn, too, and Iām not following the tObjs=[] thing either. Mostly because I donāt see where you ever use tObjs after you create it.
FYI, it seems that GetPoint is sufficient, the AddText will use the acquired coordinates without creating tObs first. I hatched out the lines for now, works OK.
Yes, i had a multiline string in my file I pulled that from and it was used for that reason. So I was adding multiple inputs into one text string to place as textObject.
The difficult part was to realize that GetObjects results in a tuplet of Guids, but you have to convert them in a list to scan through them to get to the properties of each Guid.
Max.
(how do you show the .py file, rather than just a link?)
Copy code, paste into this window. Then select it here and then click the icon to the left of the Upload icon.
Cool ā¦ Ā«Randy
Just trying here on a few closed polysurfaces and it does not work. If I explode and select surfaces it works great. just for you to know. I thought it nay have been the rs.SurfaceVolumeCentroid
#Create Layer Name Text Object for Surfaces, Max Zuijdendorp, March 2015
import rhinoscriptsyntax as rs
# Check if a layer "Layer Names" exists, if not, add it.
if not rs.IsLayer("Layer Names"):
rs.AddLayer("Layer Names", (0,0,0))
# Make "Layer Names" the current layer
rs.CurrentLayer("Layer Names")
# Pick a set of surfaces to be identified with the layer name
SelectIds = rs.GetObjects("Select Surfaces to label", filter=8, preselect=True, select=False)
List=[]
for i in range(0,len(SelectIds)):
Laag=rs.ObjectLayer(SelectIds[i])
origin = rs.SurfaceAreaCentroid(SelectIds[i])
point = rs.AddPoint(origin[0])
#create your text object
rs.AddText(Laag,point,height=8,font="Arial",font_style=0,justification=2)
I might use the objectās bounding box center for the labelā¦ This will work with non-surface objects like curves as well.
āMitch
import rhinoscriptsyntax as rs
def TextLabelBBCtr():
objs=rs.GetObjects("Select objects to label", 4+8+16+32, preselect=True)
if not objs: return
label_layer="Text_Labels"
#if layer doesn't already exist, add it.
if not rs.IsLayer(label_layer): rs.AddLayer(label_layer)
"""text plane is either active view's CPlane or text plane is always World XY
comment/uncomment one or the other of the next two lines"""
#text_plane=rs.ViewCPlane()
text_plane=rs.WorldXYPlane()
#text height is constant
text_ht=1.0
for obj in objs:
bb=rs.BoundingBox(obj)
#move text plane origin to bounding box center point
text_plane.Origin=(bb[0]+bb[6])/2
#get object's layer name
obj_layer=rs.ObjectLayer(obj)
#add the text
rs.AddText(obj_layer,text_plane,text_ht, justification=2+131072)
TextLabelBBCtr()
Thanks, well done. A definition question, if you do not mind?
I had some help with a script the other day, and I asked about using definitions for scripts, the answer I got was that it can be reused in the script, but can a definition be called from another python script in the same folder?
for obj in objs - works the code after on each object, saw this before and now I get it more clearly.
It is possible, but I donāt recommend itā¦ It makes your script less portable should you give it out to someone, or change the directory where the imported module is locatedā¦ Unless itās something really, really complex, I prefer to keep the function definitions inside the main script file, that way the script is self-contained.
My simple way to explain the use of function definitions on a basic level is that they work just like the normal rhinoscriptsyntax methods - except that you write the method yourself. The function definition will execute an operation or series of operations when you call it inside your main script. You can have as many input arguments as you want (or none, theyāre optional); the function can return something or not, also optional.
The advantage of encapsulating operations sequences inside function definitions is that it can make your code more readable/understandable, and the definitions can be re-used as many times as you need using only one line of code in the main script body.
Edit:
Also, you will see that even the main body of the script is encapsulated in a function definition. This is done for one primary reason, to be able to āescapeā the script gracefully with āreturnā if something goes wrong or the user decides to abort it for some reasonā¦
Thanks for your alternative script, Mitch. I was just about to create a second script for polysurfaces, this time using leaders i.s.o. text blocks. I had found the BoundingBox syntax, but you showed me a simpler way of referring to a midway point than I had in mind ( intersecting curves and all).
Also, I learned from it how to simplify stepping through a multi object variable. Thanks.