Saving the Python output as textfile?

Hi to all!

I’m wondering if there is any possibility to save the output as a textfile. With output, I mean this:

It would be great, it I could save the file to a defined path; and it would be even better, when the file automatically opens after the script has come to an end …

Perhaps, anybody knows how to do this?:slight_smile:

Thanks a lot!
Phil

So, what I would do is create an empty string at the beginning of the script, then start adding to it all the different lines of text that you want to output at the end - with a '\n' after each line to create a line break.

When you’re done, you’ll have one multiline string to output. You can then either write that to a text file with normal python text file writing methods or output to a Rhino message box. Python file write:

#filename is complete path to file plus name and .txt extension
file = open('filename', 'w')
file.write(your_multiline_string)
file.close()

Unfortunately, the TextOut() command - which writes text to a text window inside Rhino - is not implemented yet in Python rhinoscriptsyntax, all you have is MessageBox() currently.

–Mitch

1 Like

Super cool, thanks a lot :smiley:

Mitch, I dug up this post from you. I have written a script which works OK, the requested data is printed in Rhino:

import rhinoscriptsyntax as rs

point_list=rs.GetObjects("pick input points (24x)", filter=1, maximum_count=24)

curveA = rs.GetCurveObject("Select servo A curve")

linkA = rs.GetCurveObject("Select link A curve")
radiusA= rs.CurveLength(linkA[0])

curveB = rs.GetCurveObject("Select servo B curve")

linkB = rs.GetCurveObject("Select link B curve")
radiusB= rs.CurveLength(linkB[0])

#text=" \n"



def Servo_Positions():
	for point in point_list:
		circle=rs.AddCircle(point, radiusA)
		intersect_list=rs.CurveCurveIntersection(curveA[0], circle)
		if not intersect_list:
			rs.MessageBox( "No intersection possible for curve A")
			rs.SelectObject(point)
			break
		first_intersect=intersect_list[0]
		normparaA=rs.CurveNormalizedParameter(curveA[0],first_intersect[5])
		rs.DeleteObject(circle)
		rs.AddLine(point,first_intersect[2])
		
		circle=rs.AddCircle(point, radiusB)
		intersect_list=rs.CurveCurveIntersection(curveB[0], circle)
		if not intersect_list:
			rs.MessageBox( "No intersection possible for curve B")
			rs.SelectObject(point)
			break
		first_intersect=intersect_list[0]
		normparaB=rs.CurveNormalizedParameter(curveB[0],first_intersect[5])
		rs.DeleteObject(circle)
		rs.AddLine(point,first_intersect[2])
		
		name=rs.ObjectName(point)
		
		textline=name+" "+str(normparaA)+" "+str(normparaB)+'\n'
#		text=text+textline
			

		print "Curve parameters: ",textline
		

	
Servo_Positions()
#f=open("Servo_Positions.txt",'w')
#f.write(text)
#f.close()

But as soon as I un-hatch the hatched lines I am getting this message:

I have been searching the Python library for a clou, but for the life of me I cannot find it.
Can you help?

Max.

Hi Max

As a first double check did you unhatch all hatched (5 in total)

So also the 2 higher up?

-Willem

Hi Willem, yes I did (I wrote the script myself, so I knew where they were :wink:)

Max.

@maxz

Since it’s inside a function, I think you’ll have to do a global declaration of text.

  def Servo_Positions():
    global text

That does it! Thanks so much!

Max.

On the other hand, I still don’t understand what is happening. I had understood that defining the ‘text’-stringvariable in the initialization part defined it as a global variable anyway. I tried a variant where the ‘text’ variable was an argument of the ‘Servo_Positions’ function. That did not get me an error message, but also not the expected entries in my .txt file, just the initialization line.

Max.

You can’t exactly make it work like that… In order for your function definition to work, you need to pass in the variables used inside it as arguments. You need to look at the concept of scope of variables. I recommend you don’t work with global variables, so if you are needing variables inside a function definition that have been defined outside of it, you need to pass them in as arguments.

And I’m not sure what you want to write to the text file… one line or a whole bunch of lines?

You’re going to end up with something like this (my guess):

import rhinoscriptsyntax as rs
def Servo_Positions(point,curveA,radiusA,curveB,radiusB):
    #do a bunch of stuff with the input
    #create some text
    return your_text

def RunYourScript():
    point_list=rs.GetObjects("pick input points (24x)", filter=1, maximum_count=24)
    curveA = rs.GetCurveObject("Select servo A curve")
    linkA = rs.GetCurveObject("Select link A curve")
    radiusA= rs.CurveLength(linkA[0])
    curveB = rs.GetCurveObject("Select servo B curve")
    linkB = rs.GetCurveObject("Select link B curve")
    radiusB= rs.CurveLength(linkB[0])
    
    
    f=open("Servo_Positions.txt",'w')
    for point in point_list:
        text=Servo_Positions(point,curveA,radiusA,curveB,radiusB)
        f.write(text)
    f.close()

–Mitch

1 Like

Mitch, thanks for your reply, but I seem to be missing the point. Sorry.
Why do I need the RunYourScript() function? ServoPositions() in my script worked ok by just initializing or picking objects outside the function, and use them inside. Maybe I could just add the ‘return your_text’-bit and write it like text=Servo_Positions()? Edit: and the answer is no, I am getting the same error message again.
My text is a whole bunch of lines, but I followed the advice you gave in your older post, concatenating them with end-of-line separators inside the function. I only was not able to get it out of there for writing it to an external file, until I added the global declaration (not saying I insist on keeping that, just trying to understand).
Where is the RunYourScript function called? Or is this standard practice for creating modules?

Max.

@maxz Mitch’s idea of passing arguments certainly seems like better programming practice, but just with regard to your question, what happens is that when you call a variable inside a function, it is assumed to be a local variable. So, by default your function will not associate your local variable ‘text’ with the global variable ‘text’.

@krsnadas Thank you. I will for now just stick with the global variable. If you read this post a little earlier you will notice that I edited it. Turns out my in-between solution did not work after all.

My next script will follow the example Mitch gave. Thanks for helping me along.
I will now concentrate on learning Arduino programming, let’s hope I don’t get confused too much :smiley:
Max.

Yes, as you defined all the variables that did work at the top of the script outside of any definition, that made them global. The only one which did not work was the variable defined inside the definition, which was automatically made local to the definition - that is, unknown outside of it.

You do not have to have a function definition at all in a python script, you can just run it all as one main routine. In which case you would eliminate your ServoPositions() definition and just lay all that code out in the main script body. Then all the variables will be valid for the entire scope of the script and it should work.

While there is no obligation to use function definitions in a Python script, there are cases where it’s excellent practice to do so.

  1. If you want to be able to “escape” from a script gracefully, the whole main script needs to be encapsulated in a function definition so that you can use “return” to get out. Otherwise, if something goes wrong, the user fails to input something, whatever, you will get a script error message box instead of a nice quiet cancel.

  2. If you want to reuse a piece of code repeatedly. In that case it’s good to define it as a function and call it as many times as you need. You only write the function code once, and it offers a lot of flexibility on how and when you can use it. If you need to modify it later, you do that inside the function definition, you do not have to search for multiple places in your code where you might have used something “longhand”.

  3. It’s often a lot easier to read code that is made up of some clear, single purpose function definitions that are called in the main body rather than the whole thing being one continuous text.

Function definitions can take arguments (input) or none, and can return something (or not). If you need to get data that is stored in a non-global variable into a function definition, you need to pass it in as an argument. That automatically defines the variable in the function and passes in the data at the same time. The result of your function definition can be output with “return blablabla”. “blablabla” can only be one thing, but it can be a number, a GUID, a True/False value, a piece of geometry, or a single list containing multiple things.

To answer your question about the “RunYourScript” function, this is called a “wrapper function” for executing some other pieces of encapsulated code. This is fairly common practice, where the wrapper part simply collects user data (if needed ) and then launches the main script, passing in the collected data if any.

def DoSomethingWithMyData(data0,data1,data2):
    obj=MakeSomething(data0,data1)
    transform=TransformObject(obj,data2)
    return transform

def InputDataAndLaunch():
    #get some user data
    itemA=rs.GetSomething()
    itemB=rs.GetSomethingElse()
    itemC=rs.GetAThirdThing()
    
    #check for validity and run the function
    if itemA and itemB and itemC:
        result=DoSomethingWithMyData(itemA, itemB, itemC):
    else:
        #not valid, exit gracefully
        return
    if result:
        print result
    else:
        print "Something went wrong"

#you need to call the wrapper function here
InputDataAndLaunch()

–Mitch