I have written a python script that exports thousands of objects to ai files and it takes a few minutes. I’d like to use the progress bar in my script, but I can’t quite seem to get it to work. Would anyone have a small example script of how I can show progress of a python script? Thanks.
Hi @dustin,
Give this a whirl:
import Rhino
import scriptcontext as sc
import System
def test_progressbar():
sn = sc.doc.RuntimeSerialNumber
min = 0
max = 100
percent = False
Rhino.UI.StatusBar.ShowProgressMeter(sn, min, max, "Starting", True, percent)
for i in range(min, max + 1):
Rhino.RhinoApp.Wait()
System.Threading.Thread.Sleep(100)
if i == 10:
Rhino.UI.StatusBar.UpdateProgressMeter("Calculating", i, True)
elif i == 50:
Rhino.UI.StatusBar.UpdateProgressMeter("Processing", i, True)
elif i == 90:
Rhino.UI.StatusBar.UpdateProgressMeter("Finishing", i, True)
else:
Rhino.UI.StatusBar.UpdateProgressMeter(i, True)
System.Threading.Thread.Sleep(750) #ms
Rhino.UI.StatusBar.HideProgressMeter(sn)
if __name__ == "__main__":
test_progressbar()
– Dale
4 Likes
I will post my entire script here, maybe someone can tell me why this doesn’t seem to work.
It does not error out, but I just don’t see any progress bar. This is too much info, but I think it might provide a clue. Thanks.
import Rhino
import rhinoscriptsyntax as rs
import scriptcontext as sc
import re
import os
from fractions import Fraction
_nsre = re.compile('([0-9]+)')
def natural_sort_key(s):
return [int(text) if text.isdigit() else text.lower()
for text in re.split(_nsre, s)]
def test():
categories = {}
max = 0
ids = rs.AllObjects()
for id in ids:
if rs.IsText(id):
text = rs.TextObjectText(id)
idx = text.find("-")
if idx != -1:
name = text[:idx-1]
rs.ObjectName(id, name)
cat = rs.GetUserText(id, 'Cat')
subcat = rs.GetUserText(id, 'SubCat')
max = max + 1
if not cat:
rs.MessageBox(text + ' does not have a Cat tag.')
if not subcat:
subcat = '---'
if cat:
if not categories.has_key(cat):
categories[cat] = {}
if not categories[cat].has_key(subcat):
categories[cat][subcat] = [id]
else:
categories[cat][subcat].append(id)
Rhino.UI.StatusBar.ShowProgressMeter(sc.doc.RuntimeSerialNumber, 0, max, "Processing", True, True)
count = 0
csv_txt = 'Category,Subcategory,Order,Filename,Profile Name,Height,Width\n';
rs.EnableRedraw(True)
export_root = rs.WorkingFolder() + "/exports"
# loop over each category
for cat in categories:
for subcat in categories[cat]:
ids = categories[cat][subcat]
if not ids: return
dict = {}
distances = []
names = set([rs.ObjectName(id) for id in ids])
for name in names:
# parse the text object text to get the long dimension then sort by that number
ids = rs.ObjectsByName(name)
if not ids: continue
if len(ids) != 2: continue
bb = rs.BoundingBox(ids)
for id in ids:
if rs.IsText(id):
text = rs.TextObjectText(id).splitlines()[0]
# get the last element in the split array, don't read the last character either
widthText = text.split("x ")[-1][:-1]
dist = float(sum(Fraction(s) for s in widthText.split()))
if dict.has_key(dist):
temp = dict[dist]
temp = temp + "," + name
else:
temp = name
dict[dist] = temp
partList = sorted(dict.keys())
# for now, we just prompt the user for a category and subcategory name, later we can put these in the attributes on each text object instead
export_folder = export_root
units = rs.UnitSystemName()
for item in partList:
test = dict[item]
names = dict[item].split(",")
names.sort(key=natural_sort_key)
for name in names:
ids = rs.ObjectsByName(name)
fileName = str(count) + "_" + name + ".ai"
if not ids: continue
if len(ids) != 2: continue
bb = rs.BoundingBox(ids)
vecDir = Rhino.Geometry.Point3d(0,0,0) - bb[0]
rs.MoveObjects(ids, vecDir)
rs.UnselectAllObjects()
rs.SelectObjects(ids)
if not os.path.exists(export_folder):
os.makedirs(export_folder)
rs.Command("-Export " + chr(34) + export_folder + "/" + fileName + chr(34) + " PreserveUnits=Yes AIScale=1 Unit=" + units + " RhinoScale=1 Enter")
vecDir.Reverse()
rs.MoveObjects(ids, vecDir)
count = count + 1
w = h = 0
# we need to check the 2 objects to find out which is the text label
for id in ids:
if rs.IsText(id):
# we found the text so let's break out the width and height values
text = rs.TextObjectText(id)
idx = text.find(" - ")
if idx != -1:
dimension_str = text[idx+3:]
w = dimension_str.split(" x ")[1][:-1]
h = dimension_str.split(" x ")[0][:-1]
csv_txt += cat + ',' + subcat + ',' + str(count) + ',"' + fileName + '","' + name + '",' + str(h) + ',' + str(w) + '\n'
Rhino.UI.StatusBar.UpdateProgressMeter("Processing", count, True)
# Rhino.UI.StatusBar.UpdateProgressMeter(count, True)
rs.EnableRedraw(True)
Rhino.UI.StatusBar.HideProgressMeter(sc.doc.RuntimeSerialNumber)
# write out the CSV string to the target CSV file
file = open(export_root + "/indesign_data.csv", "w")
file.write(csv_txt)
file.close()
if __name__ == "__main__":
test()
1 Like
@dustin This is a long script and is supposed to pull data from a rhino model that I don’t have. Would you mind sharing the model so I can run the script?
Here are some changes I made to make it work but I do not have a model to test:
import Rhino
import rhinoscriptsyntax as rs
import scriptcontext as sc
import re
import os
import os.path as op
from fractions import Fraction
_nsre = re.compile('([0-9]+)')
def natural_sort_key(s):
return [int(text) if text.isdigit() else text.lower()
for text in re.split(_nsre, s)]
def test():
categories = {}
max = 0
ids = rs.AllObjects()
for obj_id in ids:
if rs.IsText(obj_id):
text = rs.TextObjectText(obj_id)
idx = text.find("-")
if idx != -1:
name = text[:idx-1]
rs.ObjectName(obj_id, name)
cat = rs.GetUserText(obj_id, 'Cat')
subcat = rs.GetUserText(obj_id, 'SubCat')
max = max + 1
if not cat:
rs.MessageBox(text + ' does not have a Cat tag.')
if not subcat:
subcat = '---'
if cat:
if cat not in categories:
categories[cat] = {}
if subcat not in categories[cat]:
categories[cat][subcat] = [obj_id]
else:
categories[cat][subcat].append(obj_id)
Rhino.UI.StatusBar.ShowProgressMeter(sc.doc.RuntimeSerialNumber, 0, max, "Processing", True, True)
count = 0
csv_txt = 'Category,Subcategory,Order,Filename,Profile Name,Height,Width\n';
rs.EnableRedraw(False)
export_dir = op.dirname(__file__)
export_root = op.join(export_dir, "exports")
if not op.exists(export_root):
os.makedirs(export_root)
# loop over each category
for cat in categories:
for subcat in categories[cat]:
ids = categories[cat][subcat]
if not ids: return
dict = {}
distances = []
names = set([rs.ObjectName(obj_id) for obj_id in ids])
for name in names:
# parse the text object text to get the long dimension then sort by that number
ids = rs.ObjectsByName(name)
if not ids: continue
if len(ids) != 2: continue
bb = rs.BoundingBox(ids)
for obj_id in ids:
if rs.IsText(obj_id):
text = rs.TextObjectText(obj_id).splitlines()[0]
# get the last element in the split array, don't read the last character either
widthText = text.split("x ")[-1][:-1]
dist = float(sum(Fraction(s) for s in widthText.split()))
if dict.has_key(dist):
temp = dict[dist]
temp = temp + "," + name
else:
temp = name
dict[dist] = temp
partList = sorted(dict.keys())
# for now, we just prompt the user for a category and subcategory name, later we can put these in the attributes on each text object instead
export_folder = export_root
units = rs.UnitSystemName()
for item in partList:
test = dict[item]
names = dict[item].split(",")
names.sort(key=natural_sort_key)
for name in names:
ids = rs.ObjectsByName(name)
fileName = str(count) + "_" + name + ".ai"
if not ids: continue
if len(ids) != 2: continue
bb = rs.BoundingBox(ids)
vecDir = Rhino.Geometry.Point3d(0,0,0) - bb[0]
rs.MoveObjects(ids, vecDir)
rs.UnselectAllObjects()
rs.SelectObjects(ids)
if not os.path.exists(export_folder):
os.makedirs(export_folder)
rs.Command("-Export " + chr(34) + export_folder + "/" + fileName + chr(34) + " PreserveUnits=Yes AIScale=1 Unit=" + units + " RhinoScale=1 Enter")
vecDir.Reverse()
rs.MoveObjects(ids, vecDir)
count = count + 1
w = h = 0
# we need to check the 2 objects to find out which is the text label
for obj_id in ids:
if rs.IsText(obj_id):
# we found the text so let's break out the width and height values
text = rs.TextObjectText(obj_id)
idx = text.find(" - ")
if idx != -1:
dimension_str = text[idx+3:]
w = dimension_str.split(" x ")[1][:-1]
h = dimension_str.split(" x ")[0][:-1]
csv_txt += cat + ',' + subcat + ',' + str(count) + ',"' + fileName + '","' + name + '",' + str(h) + ',' + str(w) + '\n'
Rhino.UI.StatusBar.UpdateProgressMeter("Processing", count, True)
Rhino.UI.StatusBar.UpdateProgressMeter(count, True)
rs.EnableRedraw(True)
Rhino.UI.StatusBar.HideProgressMeter(sc.doc.RuntimeSerialNumber)
# write out the CSV string to the target CSV file
with open(op.join(export_root, "indesign_data.csv"), "w") as f:
f.write(csv_txt)
if __name__ == "__main__":
test()
See my changes in this diff view
This is how you can use the progress bar very simply in a for loop:
import rhinoscriptsyntax as rs
from Rhino import RhinoApp
MAX = 1000
rs.StatusBarProgressMeterShow("Progress", 0, MAX)
for i in range(0, MAX):
rs.StatusBarProgressMeterUpdate(i)
RhinoApp.Wait()
rs.StatusBarProgressMeterHide()
1 Like
I added a page in the develope rdocs on Asynchronous Execution that has a section on reporting progress as well.
2 Likes