Eto for Python

Hello,
now i would like to use Eto for interface my python script.
I would like to display a text in a textbox, that i can change to return it in a “user document text”.
for example, a script calculates the area of a surface and places it in a userdocument text,
with eto i display a window with a text box where is display the value, i can change it manualy, and validate, and return the new value in the User doc text… for the window, the button, the text box, it’s ok, but the value in the text box… no!
I was thinking that a textbox1 = forms.TextBox(self.Args.Area) will be ok, but no argument is accepted by textbox, what is the way…

in fact i use:textbox1 = forms.TextBox(SelectedText=self.Args.Area)

I suspect you need a type cast. Try this:
textbox1 = forms.TextBox(SelectedText=str(self.Args.Area))

Couldn’t you just use a PropertyListBox in this case?

import rhinoscriptsyntax as rs

init_val=300
obj=rs.GetObject()
rs.SetUserText(obj,"TestValue",init_val)

curr_txt=rs.GetUserText(obj,"TestValue")
result=rs.PropertyListBox(["TestValue"],[curr_txt])
if result:
    rs.SetUserText(obj,"TestValue",result[0])
    new_txt=rs.GetUserText(obj,"TestValue")
    print "Orig user text: {} ; New user text: {}".format(curr_txt,new_txt)

Obviously if you need something more sophisticated, ETO is probably the way to go, but for just changing one value…

To instantiate with some text then:

forms.TextBox(Text=self.Args.Area)

I think you can pass in properties as keyword args, they just have to match.

Also you could:

textbox1 = forms.TextBox()
textbox1.Text = self.Args.Area

If you aren’t aware there is a pretty good eto + python guide:

in fact for each project in my job i fill a paper doc, with the owner name, contact , project name, tel… area of surfaces of the project, and a lot of chekbox… so i think eto is a good solution, but it’s really difficult for me, because i descover class, and argument… with “self” that i’m not sure too understand…

yes thank you, but since this morning i’m in this doc…

i try with 2 Textbox and 1 combobox, to get userdocumenttext, modify it in the window, but when i validate i don’t manage to get the new values to set userdocumenttext…

# Imports
import Rhino
import scriptcontext as sc
import Rhino.UI
import Eto.Drawing as drawing
import Eto.Forms as forms
import rhinoscriptsyntax as rs



################################################################################
# Class to hold PROJECT arguments
################################################################################
class ProjArg():
    
    # Initializer
    def __init__(self):
        self.Client = 'client'
        self.Projet = 'projet'



################################################################################
# Project dialog class
################################################################################
class Projforms(forms.Dialog[bool]):
    
    # Initializer
    def __init__(self, args):
        self.Args = args
        # Initialize dialog box
        self.Title = 'Remplissage FORMULAIRE PROJET'
        self.Padding = drawing.Padding(5)
        # Create layout
        layout = forms.DynamicLayout()
        layout.Padding = drawing.Padding(5)
        layout.Spacing = drawing.Size(5, 5)
        layout.AddRow(self.CreateClient())
        layout.AddRow(None) # spacer
        layout.AddRow(self.Prest())
        layout.AddRow(None) # spacer
        layout.AddRow(self.CreateButtons())
        # Set the dialog content
        self.Content = layout
        
    #CLIENT & PROJET
    def CreateClient(self):
        label1 = forms.Label(Text = 'Cient:')
        textbox1 = forms.TextBox(SelectedText=self.Args.Client)
        label2=forms.Label(Text = 'Projet:')
        textbox2= forms.TextBox(SelectedText=self.Args.Projet)
        layout = forms.DynamicLayout()
        layout.Spacing = drawing.Size(5, 5)
        layout.AddRow(label1,textbox1,label2,textbox2)
        return layout
    
    def Prest(self):
        prest_label='Prestation'
        m_combobox = forms.ComboBox()
        m_combobox.DataStore = ['2D', '3D', 'Mixte 2D-3D']
        layout = forms.DynamicLayout()
        layout.Spacing = drawing.Size(5, 5)
        layout.AddRow(None,prest_label,m_combobox,None)
        return layout
        
    # Create the dialog buttons
    def CreateButtons(self):
        # Create the validate button
        self.ValidateButton=forms.Button(Text = 'Validate!')
        self.ValidateButton.Click += self.OnValidateButtonClick 
        # Create the abort button
        self.AbortButton = forms.Button(Text = 'Close')
        self.AbortButton.Click += self.OnCloseButtonClick
        
        # Create button layout
        button_layout = forms.DynamicLayout()
        button_layout.Spacing = drawing.Size(5, 5)
        button_layout.AddRow(None, self.ValidateButton, self.AbortButton, None)
        return button_layout

    # Close button click handler
    def OnCloseButtonClick(self, sender, e):
        self.Close(False)
        
    #Validate Button click handler
    def OnValidateButtonClick(self,sender,e):
        self.Close(True)
        
    def ApplyModif(self):
        pass
        rs.SetDocumentUserText('Client',self.textbox1.Text)
        rs.SetDocumentUserText('Projet',self.textbox2.Text)
        rs.SetDocumentUserText('Prestation',self.m_combobox.Text)
        
        




        
        
def TestEtoform():
    
    args = ProjArg()
    args.Client = rs.GetDocumentUserText('Client')
    args.Projet = rs.GetDocumentUserText('Projet')
    dialog = Projforms(args);
    rc = dialog.ShowModal(Rhino.UI.RhinoEtoApp.MainWindow)
    if rc:
        dialog.ApplyModif()

if __name__ == "__main__":
    TestEtoform()

First gotcha: Looks like you can’t pass None to the textbox.Text, so make sure to protect against the document not having any usertexts, something like:

def TestEtoform():

args = ProjArg()
client = rs.GetDocumentUserText('Client')
project = rs.GetDocumentUserText('Projet')
if client: 
    args.Client = client
else:
    args.Client = 'no client'
if project:
    args.Projet = project
else:
    args.Projet = 'no project'
...

Second gotcha is when you create the texboxes/comboboxes, you didn’t assign them to self, but in applymodif you called them self.##, so make sure you create the components as self.##.

Yes right!!!

I tried with and without… It don’t run, it said that projforms don’t have attributes client or something like this…

no It’s OK!!!

someone can explain what is self… in French, i read a lot of thing about, but my english is so bad…

and thank you for your time!

self veut dire l’objet lui-même.

Lorsque on crée un nouveau objet de type ProjArg, on appel d’office sa méthode __init__, qui crée un attribut Client et un attribut Projet sur ce nouvel objet.

1 Like

hello,
on my forms there is a button select, and when i click on it, i would like to close the window, execute a function, like get objets, calculate the area, and return the value, to the eto form…
what is the way to do it, i didn’t find any example, all buttons i found, validate or closed the forms, without return value…

@dale…??

i try to test the SampleEtoCollapsibleDialog.py on GitHub
https://github.com/mcneel/rhino-developer-samples/blob/6/rhinopython/SampleEtoCollapsibleDialog.py
it’s the last example of eto doc

but it doesn’t work,there is an error on self.ClientSize.Height - self.collapsePanel.Height

I know a little bit of vbscript, I’m starting python, but I must be missing a lot about the philosophy of programming …
I don’t understand how “class” works, all the examples on the internet, or in the books are based on humans, animals, buildings … I can’t compare to a Eto window.

to select my surface, i have to close the EtoForm, can i use:
self.Close(False)
call a Function
def selectSurf()


and call again
rc = dialog.ShowModal(Rhino.UI.RhinoEtoApp.MainWindow)

for make an example, can you said to me how can i run this script:

# coding: utf-8
# Imports
import Rhino
import scriptcontext as sc
import Rhino.UI
import Eto.Drawing as drawing
import Eto.Forms as forms
import rhinoscriptsyntax as rs

class SName(forms.Dialog[bool]):
    
    # Initializer
    def __init__(self):
        self.SelectObj=None
        # Initialize dialog box
        self.Title = 'Test selection'
        self.Padding = drawing.Padding(5)
        
        # Create layout
        layout = forms.DynamicLayout()
        layout.Padding = drawing.Padding(5)
        layout.Spacing = drawing.Size(5, 5)
        layout.AddRow(self.CreateLabel())
        layout.AddRow(self.CreateButtons())
        # Set the dialog content
        self.Content = layout
    
    def CreateLabel(self):
        self.LabelText=forms.Label()
        if self.SelectObj:
            self.LabelText.Text=str(self.SelectObj)
        else:
            self.LabelText='Click'
        layout = forms.DynamicLayout()
        layout.Spacing = drawing.Size(5, 5)
        layout.AddRow(None,self.LabelText,None)
        return layout
    
    def CreateButtons(self):
        # Create the validate button
        self.SelectButton=forms.Button(Text = 'Select!')
        self.SelectButton.Click += self.OnSelectButtonClick 
        # Create the abort button
        self.AbortButton = forms.Button(Text = 'Close')
        self.AbortButton.Click += self.OnCloseButtonClick
        layout = forms.DynamicLayout()
        layout.Spacing = drawing.Size(5, 5)
        layout.AddRow(self.SelectButton,None,self.AbortButton)
        return layout
        
    def OnSelectButtonClick(self,sender,e):
        #### Code for select object and return ID
        
    def OnCloseButtonClick(self,sender,e):
        self.Close(False)
        
def LForms():
    
    dialog=SName()
    rc = dialog.ShowModal(Rhino.UI.RhinoEtoApp.MainWindow)

if __name__ == "__main__":
    LForms()

it’s just a form, with 1 LABEL and 2 BUTTONS, first for select an object, the second for close.
when i click on select button, it close the form, i can select an object, open the form and display the name of the selected object in the Label.

i don’t know how can i use: Eto.Forms documentation

I think you will need to use a modeless form to interact with the doc/Rhino while the form is active. There is an example:

Also, be sure to ‘remove events’ as they may not be cleaned up if not explicitly removed.

thank you… but it’s not what i search…

i’ve got this form:
image
and when i click on select. Button, i could get a surface, calculate is area, and return it to the textbox near to the button…

Hello!
I have advanced my script well, but since I added the surface selection button, the user data documents do not update …FILLPROJ5.py (10.9 KB)

if someone can have a look on my script…

for the test, just create a surface in rhino.
first test:
run the script,
enter a character in each textbox,
then the button to validate,
result: all user text documents are modified,

second test:
we run the script,
all textbox are filled with the new value changed in the previous test,
modify some
click on the select button,
select the surface, 4 times…
and when we return to the form, the textbox are filled with the old values
and if we validate the new textbox values are modified but not the surfaces …

when using rs.SetDocumentUserText, the value is not changed at this point in the document, but only at the end of the script.

So you call Calculate Surface which then creates a new form with the initial values.

call calculate surface with the dictionary result from the form being pass through it.

def OnbspieceClick(self,sender,e):
    self.Close(True)
    pass
    Dict equals values in current form.
    CalculateSurface(Dict)
    pass

def CalculateSurface(dict):
    normal code
    TestEtoform(dict)

def TestEtoform(dict):
    if not dict:
       get values from document.
    Call form with dictionary values.

thank you, but what does mean

i have to make a dictionary with all values of all “textbox”?
are you sure this is the solution?

When are “UserTextDocument” defined? during the script? at the end?

It is applied when ApplyModif is called. Which is only called if Validate button is clicked rather than the close button.
The reason being when you click OnbspieceClick, it calls calculate surface which opens a new instance of the form with the original values.

yes but i use rs.SetUserDocumentText in CalculateSurface()

and i hopped that when i click OnbspieceClick it called ApplyModif, but no…

I tried your solution, but it doesn’t run… he asked me an argument at the beginning:

def TestEtoform(dict):
     if not dict:
         get values from document.
    Call form with dictionary values.

give dict a default value of none.
OnBSpice calls close which should close the form. I don’t even know why it doesn’t though.
In fact the script is behaving very weirdly in my editor.

I think you should instead return an integer, if 0, don’t update, if 1 Apply Modif, if 2, call Calculate surface.
Put this all in a loop inside Test Eto Form. Such that it Will loop until you return 0 or 1.

def TestEtoform():
    
    keys=('Client','Projet','Devis','Delais','Prestation',
    'Contact','Tel','Mobile','Usinage','Offset','Homothetie',
    'SPiece','SPlage','SPateProj','SPolyure')
    dict_input={}
    for key in keys:
        if rs.GetDocumentUserText(key):
            dict_input[key]=rs.GetDocumentUserText(key)
        else:
            dict_input[key]='a definir'
            
    run = 2
    while (run > 1):
        dialog = Projforms(dict_input)
        rc = dialog.ShowModalAsync(Rhino.UI.RhinoEtoApp.MainWindow)
        if rc == 1:
            dialog.ApplyModif()
        elif rc == 2:
            dict_input = dialog.dict_current
            CalculateSurface()
            keys=('SPiece','SPlage','SPateProj','SPolyure')
            for key in keys:
                if rs.GetDocumentUserText(key):
                    dict_input[key]=rs.GetDocumentUserText(key)
        run = rc

And for the button code.

# Close button click handler
def OnCloseButtonClick(self, sender, e):
    self.Close(0)
    
#Validate Button click handler
def OnValidateButtonClick(self,sender,e):
    self.Close(1)

    
def OnbspieceClick(self,sender,e):
    self.dict_current = {}
    self.dict_current['Contact'] = textbox_contact.Text
    etc
    self.Close(2)

All of the above is untested and is just a suggestion.

me too!!!
ok i 'll try it!

thank you!

Edit:
I tried, but it doesn’t run… i launch script, but it runs in loop without ever opening the form, i cannot stop without crash rhino…

I found something, since your code With Close(0) or Close(1) or Close(2), the script open the form, but continu the script, so when the value of run is 2, the loop begin, but never stop, because there is no time to open the form, i changeed the condition of the while:


    keys=('Client','Projet','Devis','Delais','Prestation',
    'Contact','Tel','Mobile','Usinage','Offset','Homothetie',
    'SPiece','SPlage','SPateProj','SPolyure')
    dict_input={}
    for key in keys:
        if rs.GetDocumentUserText(key):
            dict_input[key]=rs.GetDocumentUserText(key)
        else:
            dict_input[key]='a definir'
                
    run = 2
    
    while run:
        dialog = Projforms(dict_input)
        rc = dialog.ShowModalAsync(Rhino.UI.RhinoEtoApp.MainWindow)
        if rc == 1:
            dialog.ApplyModif()
            break
        elif rc == 2:
            dict_input = dialog.dict_current
            CalculateSurface()
            keys=('SPiece','SPlage','SPateProj','SPolyure')
            for key in keys:
                if rs.GetDocumentUserText(key):
                    dict_input[key]=rs.GetDocumentUserText(key)
        else:
            break

with this script, it open the form, but it finish the script, without i click on close or validate buton…