Script Grasshopper and Eto

I’m starting to create scripts with grasshopper and Eto, but I’m having some problems, I need help, I have some problems with Eto and Rhino closes when I try to run the preview

import Rhino
import rhinoscriptsyntax as rs
import Rhino.UI
import Eto.Drawing as drawing
import Eto.Forms as forms
import ghpythonlib.components as ghcomp
import Rhino.Geometry as rg

class CirculosInterface(forms.Form):

    def __init__(self):
        super().__init__()
        self.Title = "Generate Circles"
        self.ClientSize = drawing.Size(300, 200)
        
        self.curva_input = None
        self.preview_output = None
        self.final_output = None

        self.count_label = forms.Label(Text="Number of Points")
        self.count_box = forms.NumericUpDown(Value=10, MinValue=1, MaxValue=100)
        
        self.radius_label = forms.Label(Text="Radius of Circles:")
        self.radius_box = forms.NumericUpDown(Value=1, MinValue=0.1, MaxValue=10)

        self.seed_label = forms.Label(Text="Seeds:")
        self.seed_box = forms.NumericUpDown(Value=1, MinValue=1, MaxValue=100)

        self.preview_button = forms.Button(Text="Preview")
        self.preview_button.Click += self.on_preview_click

        self.generate_button = forms.Button(Text="Generate")
        self.generate_button.Click += self.on_generate_click

        layout = forms.DynamicLayout()
        layout.Spacing = drawing.Size(5, 5)
        layout.AddRow(self.count_label, self.count_box)
        layout.AddRow(self.radius_label, self.radius_box)
        layout.AddRow(self.seed_label, self.seed_box)
        layout.AddRow(self.preview_button, self.generate_button)
        
        self.Content = layout

    def on_preview_click(self, sender, e):
        count = int(self.count_box.Value)
        radius = float(self.radius_box.Value)
        seed = int(self.seed_box.Value)

        if self.curva_input is None:
            self.curva_input = rs.GetObject("Select a curve", rs.filter.curve)

        if self.curva_input:
            curva = rs.coercecurve(self.curva_input)
            points = ghcomp.PopulateGeometry(curva, count, seed)

            if self.preview_output:
                rs.DeleteObjects(self.preview_output)

            preview_circles = []
            for pt in points:
                plane = rg.Plane(pt, rg.Vector3d.ZAxis)
                circle = rg.Circle(plane, radius)
                preview_circles.append(rs.AddCircle(circle))

            self.preview_output = preview_circles

    def on_generate_click(self, sender, e):
        count = int(self.count_box.Value)
        radius = float(self.radius_box.Value)
        seed = int(self.seed_box.Value)

        if self.curva_input:
            curva = rs.coercecurve(self.curva_input)
            points = ghcomp.PopulateGeometry(curva, count, seed)

            goals = []
            for pt in points:
                goal = ghcomp.SphereCollide(pt, radius, 1)
                goals.append(goal)

            curve_goal = ghcomp.CurvePointCollide(points, curva, True, 1)
            goals.append(curve_goal)

            result = ghcomp.KangarooSolver(goals, True, 0.1, 10)
            result_points = result[0]

            if self.final_output:
                rs.DeleteObjects(self.final_output)

            final_circles = []
            for pt in result_points:
                plane = rg.Plane(pt, rg.Vector3d.ZAxis)
                circle = rg.Circle(plane, radius)
                final_circles.append(rs.AddCircle(circle))

            self.final_output = final_circles

form = CirculosInterface()
form.Owner = Rhino.UI.RhinoEtoApp.MainWindow
form.Show()

Hi @Roberto_da_Silva_Kum,

The crash is giving the following error, looks like you need an extra argument in AddCircle method.

[ERROR] FATAL UNHANDLED EXCEPTION: Microsoft.Scripting.ArgumentTypeException: AddCircle() takes exactly 2 arguments (1 given)

You can update your line here to take the plane and radius variables as arguments instead of passing the “circle” as the argument:

            preview_circles = []
            for pt in points:
                plane = rg.Plane(pt, rg.Vector3d.ZAxis)
                circle = rg.Circle(plane, radius)
                preview_circles.append(rs.AddCircle(plane, radius))  # This appears to work
                # preview_circles.append(rs.AddCircle(circle))  # Results in Rhino crash

This appears to solve your preview button crashing Rhino, however the Generate button will now crash Rhino with this error:

[ERROR] FATAL UNHANDLED EXCEPTION: System.MissingMemberException: 'module' object has no attribute 'SphereCollide'

You are calling SphereCollide as a method of ghcomp but SphereCollide is a method of Kangaroo2Component so you need to call that first like so

            goals = []
            for pt in points:
                #goal = ghcomp.SphereCollide(pt, radius, 1)
                goal = ghcomp.Kangaroo2Component.SphereCollide(pt, radius, 1)
                goals.append(goal)

Moving forward I would look at the crash messages you get.

When Rhino crashes, before you close out of the “rhino crashed dialog” open up the RhinoDotNetCrash.txt in notepad or another text editor and just take a look at the first few lines and that should be enough to pinpoint your error or at least start you on the hunt!

Alternatively if you know a certain function will crash Rhino or just want to handle cases where things may… you can use Try/Except blocks like so…

#! python3

def MyFunction():
    try:
        riskyVariable = "scary, could crash, hope it doesn't"
        SomethingRisky(riskyVariable)

    except Exception as ex:
        print(f"MyFunction Exception: {ex}")


def SomethingRisky(text):
    print(text)


if __name__ == "__main__":
    MyFunction()

This will “try” whatever it is you are doing and instead of crashing Rhino it will print the error message and say "hey this didn’t work because of ‘x’ ".

I like to use text formatting in the exception statement to see which function the exception was called in so I can track it down easier… if you have tons of functions with no explanation of “where” it is firing from sometimes it’s hard to find.

Here’s a few modifications to your code to help debug:

#! python 2

import Rhino
import rhinoscriptsyntax as rs
import Rhino.UI
import Eto.Drawing as drawing
import Eto.Forms as forms
import ghpythonlib.components as ghcomp
import Rhino.Geometry as rg


class CirculosInterface(forms.Form):

    def __init__(self):
        # super().__init__()
        self.Title = "Generate Circles"
        #self.ClientSize = drawing.Size(300, 150)
        self.Size = drawing.Size(280, 150)  # Changed to size for more direct control over form size
        
        self.curva_input = None
        self.preview_output = None
        self.final_output = None

        self.count_label = forms.Label()
        self.count_label.Text = "Number of Points"
        self.count_box = forms.NumericUpDown(Value=10, MinValue=1, MaxValue=100)
        
        self.radius_label = forms.Label()
        self.radius_label.Text = "Radius of Circles:"
        self.radius_box = forms.NumericUpDown(Value=1, MinValue=0.1, MaxValue=10)

        self.seed_label = forms.Label()
        self.seed_label.Text = "Seeds:"
        self.seed_box = forms.NumericUpDown(Value=1, MinValue=1, MaxValue=100)

        self.preview_button = forms.Button()
        self.preview_button.Text = "Preview"
        self.preview_button.Click += self.on_preview_click

        self.generate_button = forms.Button()
        self.generate_button.Text = "Generate"
        self.generate_button.Click += self.on_generate_click

        layout = forms.DynamicLayout()
        layout.Spacing = drawing.Size(5, 5)
        layout.AddRow(None, self.count_label, self.count_box)
        layout.AddRow(None, self.radius_label, self.radius_box)
        layout.AddRow(None, self.seed_label, self.seed_box)
        layout.AddRow(None)  # Add "spacer"
        layout.AddRow(None, self.preview_button, self.generate_button, None)
        layout.AddRow(None)  # Add "spacer"
        
        self.Content = layout

    def on_preview_click(self, sender, e):
        count = int(self.count_box.Value)
        radius = float(self.radius_box.Value)
        seed = int(self.seed_box.Value)

        try:
            if self.curva_input is None:
                self.curva_input = rs.GetObject("Select a curve", rs.filter.curve)

            if self.curva_input:
                curva = rs.coercecurve(self.curva_input)
                points = ghcomp.PopulateGeometry(curva, count, seed)

                if self.preview_output:
                    rs.DeleteObjects(self.preview_output)

                preview_circles = []
                for pt in points:
                    plane = rg.Plane(pt, rg.Vector3d.ZAxis)
                    circle = rg.Circle(plane, radius)
                    preview_circles.append(rs.AddCircle(plane, radius))
                    # preview_circles.append(rs.AddCircle(circle))

                self.preview_output = preview_circles

        except Exception as ex:
            Rhino.RhinoApp.WriteLine("on_preview_click exception: {}".format(ex))


    def on_generate_click(self, sender, e):
        count = int(self.count_box.Value)
        radius = float(self.radius_box.Value)
        seed = int(self.seed_box.Value)

        # Define the World XY plane
        worldXY = rg.Plane.WorldXY

        try:
            if self.curva_input:
                # curva = rs.coercecurve(self.curva_input)
                points = ghcomp.PopulateGeometry(geometry=self.curva_input, count=count, seed=seed)
            else:
                Rhino.RhinoApp.WriteLine("curva failed")

                ghcomp.PopulateGeometry()

                goals = []
                if points:
                    for pt in points:
                        goal = ghcomp.Kangaroo2Component.SphereCollide(points=pt, radius=radius, strength=1)  # Updated arguments and method to inherit from Kangaroo2Component
                        goals.append(goal)
                else:
                    print("no points for sphere collide")


                curve_goal = ghcomp.Kangaroo2Component.CurvePointCollide(points=points, plane=worldXY, curve=curva, interior=True, strength=1)  # Updated arguments and method to inherit from Kangaroo2Component
                result_points = result[0]
                goals.append(curve_goal)

                # result = ghcomp.KangarooSolver(goals, True, 0.1, 10)
                result = ghcomp.Kangaroo2Component.Solver(goalobjects=goals, reset=False, on=True, threshold=0.1, tolerance=10)  # Updated arguments and method to inherit from Kangaroo2Component
                result_points = result[0]

                if self.final_output:
                    rs.DeleteObjects(self.final_output)

                final_circles = []
                for pt in result_points:
                    plane = rg.Plane(pt, rg.Vector3d.ZAxis)
                    circle = rg.Circle(plane, radius)
                    final_circles.append(rs.AddCircle(circle))

                self.final_output = final_circles
            
        except Exception as ex:
            Rhino.RhinoApp.WriteLine("on_generate_click exception: {}".format(ex))


form = CirculosInterface()
form.Owner = Rhino.UI.RhinoEtoApp.MainWindow
form.Show()

I’m not longer getting crashes after the modifications I added but I also have no idea what you are trying to do with this script so I’m just guessing here… haha

Anyways…

image

Hope something in here helps!

If you get stuck as you work through some of those errors reach back out on here,

Cheers!

4 Likes