Why is Python showing the first calculations but not the last?

Rhino shows the 20 or so first calculations, then suddenly jumps to the end of the calculations.
Why is that?

import rhinoscriptsyntax as rs
import random
import time
    
time1=time.time()
    
rs.EnableRedraw (False)
arrPT=[]
for i in range(40):
    for ii in range(40):
        arrPT.append(rs.AddPoint((i,ii,0)))
    
rs.SelectObjects(arrPT)
    
rs.EnableRedraw (True)
rs.EnableRedraw (False)
    
for n in range(40):
    print n
    rs.EnableRedraw (True)
    rs.EnableRedraw (False)
    for ii in range(len(arrPT)):
        rs.MoveObject(arrPT[ii],(0,0,0.5-random.random()))
    
rs.EnableRedraw (True)
    
time2=time.time()
print round(time2-time1,2)

I solve it by adding

Rhino.RhinoApp.Wait()

after rs.MoveObject(…)

But I don’t understand why I have to add that. Why does Rhino suddenly run along to the finish?
(Remember I am used to the simple logic of RhinoScript, so Python is still alien to me)

In a nutshell, your “for n in…” loop is “too tight” and does give Rhino the opportunity to repaint the screen in between object movements. By adding the RhinoApp.Wait, you are telling Rhino to flush its message queue before continuing. Thus, it has time to process the paint messages required for redrawing.

RhinoScript did the RhinoApp.Wait for you (in most cases), which is somewhat inefficient but more simple.

Ok, thanks. I’ll get used to using RhinoApp.Wait().
But why is this working ok on the first runs, and not the last?

I’ll probably eventually add the RhinoApp.Wait function calls for you internally, but this won’t happen until at least Rhino 6.

Rhino redraws when it handles paint events sent to the application by the operating system. I’m not exactly sure why the paint messages are processed in the first few iterations.

Oh, and by the way I edited your post so the python code has syntax highlighting.

Ok,it kind of makes me glad that it doesn’t make sense to you either.
I am also having issues with rs.Redraw() if I use rs.EnableRedraw(False)
So I use rs.EnableRedraw(True); rs.EnableRedraw(False) instead.

#testing formatting
def PythonFormat():
     print "Only way to learn is to repeat..."

Ok, might remember it now :smile:

I don’t understand. Is Redraw not working?

Well, at least it’s not working the way I am using it:

import rhinoscriptsyntax as rs
import random
import time
import Rhino

time1=time.time()

rs.EnableRedraw (False)
arrPT=[]
for i in range(40):
    for ii in range(40):
        arrPT.append(rs.AddPoint((i,ii,0)))

rs.Redraw ()
#rs.EnableRedraw (True); rs.EnableRedraw (False)

for n in range(40):
    print n+1
    rs.Redraw ()
    #rs.EnableRedraw (True); rs.EnableRedraw (False)
    for ii in range(len(arrPT)):
        rs.MoveObject(arrPT[ii],(0.5-random.random(),0.5-random.random(),0.5-random.random()))
        Rhino.RhinoApp.Wait()

rs.EnableRedraw (True)

time2=time.time()
print round(time2-time1,2)

Now I understand, thanks. I chatted with Dale Fugier about what RhinoScript does and it appears that he forces Redraw to work even when EnableRedraw is set to False. I’ll adjust the python library to work the same way.

Great, thanks. I thought that was the way it was supposed to work, but learning Python and understanding the Rhino.Wait thing kind of makes me understand that Python is fundamentally different in some areas.

That script was a test to see if I could make it into a multi-core script, but the one I got working has a tendency to crash Rhino or to generate ghost objects. There are some issues, probably caused by the way I think… Making a new document prior to running the script makes it work ok, but hitting undo takes a long time, or selecting all and deleting can cause the issues.

import rhinoscriptsyntax as rs
import System.Threading.Tasks as tasks
import random
import time
import Rhino

time1=time.time()

rs.EnableRedraw (False)
arrPT=[]
for i in range(40):
    for ii in range(40):
        arrPT.append(rs.AddPoint((i,ii,0)))

#rs.SelectObjects(arrPT)

rs.EnableRedraw (True)
rs.EnableRedraw (False)

def MovePoint(i):
    rs.MoveObject(i,(0.5-random.random(),0.5-random.random(),0.5-random.random()))

for n in range(40):
    print n+1
    rs.EnableRedraw (True)
    rs.EnableRedraw (False)
    tasks.Parallel.ForEach(arrPT, MovePoint)
    #Rhino.RhinoApp.Wait()

rs.EnableRedraw (True)

time2=time.time()
print round(time2-time1,2)

Edit: just ran that script and Rhino crashed at the 27’th “frame”.

Most of the rhinoscriptsyntax functions are not thread safe since they work on the Rhino document. Adding or moving objects should all be done on the main thread.

Ok, that makes sense in a way.
So I can do multiple calculations i multiple threads, and then let the main tread update and create geometry after those calculations are done.

Can I safely append info from multiple threads to one single array?

Appending depends on if the array class is thread safe. In cases where I know how many iterations are going to occur I typically pre-allocate enough space in the array to hold the elements I need and then access the elements of the array by index. The append function on python lists are supposed to be thread safe according to my quick search on the internet.

Ok.
I feared it wasn’t a simple answer. Thank for looking into it, I don’t have a huge need for it right now, but it is one thing I’d like to “master”.

I just updated Redraw() tol force a redraw no matter what the state of EnableRedraw is and without any requirement of RhinoApp.Wait(). This will be available in SR9 (we’re too late to get this in to SR8)

1 Like