Small GetOption question


#1

I’m looking to have the user either pick a point in a viewport or simply key in a Z value. I have something that’s working 99%, however, it has one small problem. It’s necessary to specify a default value for the ZLevel with

dblOption=Rhino.Input.Custom.OptionDouble(default)

I would like to return this default value if Enter is pressed. But I would also like to return None if Esc is pressed. BUt I don’t know how to do both. In the following, if I use return default, I will get the default whether the user presses Enter or Esc. If I use just return, I don’t get the default value with Enter… there must be a simple solution to this. I guess I could use an Esc key test in there, but isn’t there a better way with soe GetOption option that I missed?

Thx, --Mitch

import Rhino
import scriptcontext as sc
 
def GetPointOrValue(msg,prompt,default):
    #default is float or integer - shown on command line
    gp = Rhino.Input.Custom.GetPoint()
    gp.SetCommandPrompt(msg)
    dblOption = Rhino.Input.Custom.OptionDouble(default)
    gp.AddOptionDouble(prompt, dblOption)
    while True:
        get_rc = gp.Get()
        if gp.CommandResult() == Rhino.Commands.Result.Success:
            if get_rc==Rhino.Input.GetResult.Point:
                return gp.Point()
            elif get_rc==Rhino.Input.GetResult.Option:
                return dblOption.CurrentValue
        return
        #or, 
        #return default

msg="Pick point or key in value for Z level"
prompt="ZValue"

result=GetPointOrValue(msg,prompt,0.0)
if isinstance(result,Rhino.Geometry.Point3d):
    sc.doc.Objects.AddPoint(result)
    sc.doc.Views.Redraw()
elif isinstance(result,float) or isinstance(result,int):
    print "Z value is {}".format(result)
else:
    print "Error"

#2

OK, got it… gp.SetDefaultNumber(default)


#3

Well, sorta got it…

import Rhino
import scriptcontext as sc

def GetPointOrValue(msg,prompt,default):
    #default is float or integer - shown on command line
    gp = Rhino.Input.Custom.GetPoint()
    gp.SetCommandPrompt(msg)
    dblOption = Rhino.Input.Custom.OptionDouble(default)
    gp.AddOptionDouble(prompt, dblOption)
    gp.SetDefaultNumber(default)
    while True:
        get_rc = gp.Get()
        if gp.CommandResult() == Rhino.Commands.Result.Success:
            
            if get_rc==Rhino.Input.GetResult.Point:
                return gp.Point()
            elif get_rc==Rhino.Input.GetResult.Option:
                return dblOption.CurrentValue
        else: return


msg="Pick point or key in value for Z level"
prompt="ZValue"

result=GetPointOrValue(msg,prompt,0.0)
if isinstance(result,Rhino.Geometry.Point3d):
    sc.doc.Objects.AddPoint(result)
    sc.doc.Views.Redraw()
elif isinstance(result,float) or isinstance(result,int):
    print "Z value is {}".format(result)
else:
    print "Error"

It works correctly with Esc and Enter, but the problem here is I get a double prompt:

Pick point or key in value for Z level <0.000> ( ZValue=0 ):

How do I get just:

Pick point or key in value for Z level <0.000> :

–Mitch


#4

And of course, I’m not finished yet… What I really want to do is also have a Boolean option on the command line as well. Something like:

import Rhino
import scriptcontext as sc

def GetPointOrZ():
    gp = Rhino.Input.Custom.GetPoint()
    gp.SetCommandPrompt("Pick point or key in Z value")
    
    dblOption = Rhino.Input.Custom.OptionDouble(0.0)
    boolOption = Rhino.Input.Custom.OptionToggle(True, "Individually", "AsGroup")
    gp.AddOptionDouble("ZValue", dblOption)
    gp.AddOptionToggle("TransformObjects", boolOption)
    
    point=None
    while True:
        get_rc = gp.Get()
        if get_rc==Rhino.Input.GetResult.Point:
            point = gp.Point()
        elif get_rc==Rhino.Input.GetResult.Option:
            continue
        break
    return (point,dblOption.CurrentValue,boolOption.CurrentValue)

result=GetPointOrZ()
if result[0]:
    strPt="({:.2f},{:.2f},{:.2f})".format(result[0].X,result[0].Y,result[0].Z)
else:
    strPt="No point picked"
if result[2]: strTrans="AsGroup"
else: strTrans="Individually"
print "Point={}, Z={}, TransformObjects={}".format(strPt,result[1],strTrans)

The problem here again is how to differentiate between Esc and Enter. It’s not a really big deal, but I was just kind of hoping to have the finesse to be able to execute the command with its default values if Enter is pressed, and do nothing if Esc is pressed.

–Mitch


(Menno Deij - van Rijswijk) #5

This had me cornered a while ago as well. You need to enable gp.AcceptNothing(true). Then Enter will return Nothing and Esc will return Cancel. Otherwise both return Cancel and you can’t distinguish.


#6

Brilliant, that works great, thanks Menno! --Mitch


#7

Continuing in the same vein… Now trying to get objects plus have a Boolean toggle on the command line simultaneously… My problem is now if I select some objects and then click the toggle, the selection is lost… I must be structuring this wrong… Here’s what I have so far:

import rhinoscriptsyntax as rs
import Rhino

def GetObjsPlusBool(prompt,b_opts,def_bool):
    #prompt==command line prompt
    #b_opts==tuple of strings for Boolean choices - (Name,FalseValue,TrueValue)
    #def_bool==default value for Boolean
    #returns None if Esc is pressed or no objects are chosen
    #otherwise, returns tuple of ([object ids], Boolean)
    
    go = Rhino.Input.Custom.GetObject()
    go.SetCommandPrompt(prompt)
    boolOption = Rhino.Input.Custom.OptionToggle(def_bool,b_opts[1],b_opts[2])
    go.AddOptionToggle(b_opts[0], boolOption)
    go.AcceptNothing(True)
    
    while True:
        get_rc = go.GetMultiple(1,0)
        if get_rc==Rhino.Input.GetResult.Option: continue
        elif get_rc==Rhino.Input.GetResult.Cancel: return
        elif get_rc==Rhino.Input.GetResult.Nothing: break
        elif get_rc==Rhino.Input.GetResult.Object: break
    ids=go.Objects()
    return (ids,boolOption.CurrentValue)
    
def TestGetObjsPlusBool():
    prompt="Pick some objects"
    bool_opts=("BooleanChoice","ChooseFalse","ChooseTrue")
    result=GetObjsPlusBool(prompt,bool_opts,True)
    if result is None: print "Function cancelled" ; return
    if result[0] is None: obj_count="No"
    else: obj_count=len(result[0])
    print "{} objects chosen, Boolean value={}".format(obj_count,result[1])
    
TestGetObjsPlusBool()

Do I have to go through something as elaborate as this to be able to keep the objects selected?

Thanks, --Mitch


(Menno Deij - van Rijswijk) #8

I think this is normal Rhino behavior. For example, try the Split command, select a surface to be split and a line to split it with. Then click the option Shrink. The line will be deselected.


#9

Hmmm, not convinced on that one. For example with ExtrudeCrv, any of the command-line toggles such as Solid=Yes/No or BothSides can be hit without killing a preselection…

–Mitch


(Menno Deij - van Rijswijk) #10

Yes, but then the selection has been made already (“Press Enter when done” will finish the object selection stage of the command).

So, you could structure the command like so:

  1. select objects using GetObject
  2. present user with options while selection is already made with GetOption

#11

OK, Extrude wasn’t a good example… Try Trim with a preselected curve… --Mitch


(Menno Deij - van Rijswijk) #12

Maybe the following is what you want

ids = []
 
while True:
  gr = go.Get() #select one object
  if gr == Rhino.Input.GetResult.Option: continue
  if gr == Rhino.Input.GetResult.Cancel: 
    for id in ids:
      id.Object().Highlight(False) #unhighlight before returning
    return
  if gr == Rhino.Input.GetResult.Nothing: break
  if gr == Object:
    id = go.Object(0) # get first object ref
    ids.extend(id)
    id.Object().Select(False) #unselect to prevent selecting it again
    id.Object().Highlight(True) #highlight object to show it is selected

for id in ids:
  id.Object.Highlight(False) #unhighlight before returning

return (ids, boolOption.CurrentValue)

Basically, you select objects one-at-a-time, then deselect and highlight them (the user will see this a being selected, but the object will not be in a selected state, it will be in a highlighted state).

Ideally you would also create a custom selection filter function that does not allow the selection of objects that have at one point been selected.

(as an aside: how do you get your code to format nicely? I can’t seem to get it right)


#13

OK, thanks Menno, I’ll have to try in that direction… It will finally sink in somehow…

I just use

```python
<paste script in here>
```

Plus, I guess I always make sure my line length is 80 or less. I think it wraps here at about 85. I do this anyway for most of my code.

Cheers, --Mitch


#14

@menno Well, I didn’t really get your code to work… :confused:

The further I get into it, the more lost I get it seems, so I think I’m going to quit for the day…

–Mitch


(Menno Deij - van Rijswijk) #15

My apologies, I typed the python code without actually testing it and it contained a few errors. Below you find a revised version that I tested just now.

import rhinoscriptsyntax as rs
import scriptcontext as sc
import Rhino

def getBoolAndObjects(prompt, b_opts,default):
    ids = []
    go = Rhino.Input.Custom.GetObject()
    go.SetCommandPrompt(prompt)
    boolOption = Rhino.Input.Custom.OptionToggle(default,b_opts[1],b_opts[2])
    go.AddOptionToggle(b_opts[0], boolOption)
    go.AcceptNothing(True)
    
    while True:
        gr = go.Get() #select one object
        if gr == Rhino.Input.GetResult.Option: continue
        if gr == Rhino.Input.GetResult.Cancel: 
            for gid in ids:
                gid.Object().Highlight(False) #unhighlight before returning
            return (None, boolOption.CurrentValue)
        if gr == Rhino.Input.GetResult.Nothing: break
        if gr == Rhino.Input.GetResult.Object:
            id = go.Object(0) # get first object ref
            ids.extend([id])
            id.Object().Select(False) #unselect to prevent selecting it again
            id.Object().Highlight(True) #highlight object to show it is selected
    
    for gid in ids:
      gid.Object().Highlight(False) #unhighlight before returning
    
    return (ids, boolOption.CurrentValue)
    
if __name__ == '__main__':
    (ids, opt) = getBoolAndObjects("prompt here", ("Choice", "No", "Yes"), False)
    if ids is None:
        Rhino.RhinoApp.WriteLine("canceled")
    else:
        Rhino.RhinoApp.WriteLine("{0} objects selected", len(ids))
        Rhino.RhinoApp.WriteLine("bool option {0}", opt)
        for gid in ids:
            Rhino.RhinoApp.WriteLine("object id {0}", gid.Object().Id)