Crashing rhino with eto

I am trying to work on a dialog box, using eto example files that are out there.
Right now when I click either OK or Cancel button it forces rhino to crash completely…
Clearly, I am missing something important here. Can anyone point it into the direction of what is causing fatalities here?
Much appreciated!

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

__commandname__ = "viAttributeTEMP2"

class viAttributeForm(forms.Form):

	def __init__(self):
		self.Initialize()

	# Basic form initialization
	def Initialize(self):
		self.Title = 'Attribute Form'
		self.Padding = drawing.Padding(5)
		self.Resizable = True
		self.Maximizable = False
		self.Minimizable = False
		self.ShowInTaskbar = True
		self.MinimumSize = drawing.Size(200, 150)
		
		# Create a label control
		label = forms.Label()
		label.Text = 'Select Part Number'
		# Create dynamic layout control
		layout = forms.DynamicLayout()
		layout.Padding = drawing.Padding(5)
		layout.Spacing = drawing.Size(5, 5)
		# Add controls to layout
		layout.AddRow(label)
		layout.AddRow(self.CreateListBox())
		layout.AddRow(None) # spacer
		layout.AddRow(self.CreateButtons())

		# Set the dialog content
		self.Content = layout

		# FormClosed event handler
		self.Closed += self.OnFormClosed

	def CreateListBox(self):
		#Create ListBox
		listbox = forms.ListBox()
		listbox.DataStore = self.organizedKeys()
		listbox.SelectedIndex = 0
		self.m_listbox = listbox
		return self.m_listbox

	# OK button click handler
	def OnOkButtonClick(self, sender, e):
		self.m_selected_index = self.m_listbox.SelectedIndex
		self.Close(True)
	
	# Cancel button click handler
	def OnCancelButtonClick(self, sender, e):
		self.Close(False)

	# Create button controls
	def CreateButtons(self):
		# Create the default button
		self.DefaultButton = forms.Button(Text = 'OK')
		self.DefaultButton.Click += self.OnOkButtonClick
		# Create the abort button
		self.AbortButton = forms.Button(Text = 'Cancel')
		self.AbortButton.Click += self.OnCancelButtonClick
		# Create button layout
		button_layout = forms.DynamicLayout()
		button_layout.Spacing = drawing.Size(5, 5)
		button_layout.AddRow(None, self.DefaultButton, self.AbortButton, None)
		return button_layout

	# Find all objects with a beetle_PartNum partNum_key
	def findObjects(self):
		# Get all objects in file
		ids = rs.AllObjects(include_lights=False, include_grips=False)
		
		# Set some variables
		partNum_key = 'beetle_PartNum'
	
		PartNumAndGUIDs = {}
		# for every id given run part finding
		for id in ids:
			# Get partNum from every object
			partNumFound = rs.GetUserText(id, partNum_key, False)
			# If part number is not empty make a dictionary 
			if partNumFound != None:
				# Make a dictionary with part Number as Key and GUIDs as a list of values
				PartNumAndGUIDs.setdefault(partNumFound, []).append(id)
		
		# Return dictionary once loop is over
		return PartNumAndGUIDs

	def organizedKeys(self):
		mainDictionary = self.findObjects()
		# Get all keys from dictionary and organize them by numerical value
		organizedKeys = sorted(list(mainDictionary), key=int)
		return organizedKeys

	# Form Closed event handler
	def OnFormClosed(self, sender, e):
		# Remove the events added in the initializer
		#self.RemoveEvents()
		# Dispose of the form and remove it from the sticky dictionary
		if sc.sticky.has_key('attribute_form'):
			form = sc.sticky['attribute_form']
			if form:
				form.Dispose()
				form = None
			sc.sticky.Remove('attribute_form')

################################################################################
# Creating a dialog instance and displaying the dialog.
################################################################################
def TestViAttributeForm():
	# See if the form is already visible
	if sc.sticky.has_key('attribute_form'):
		return
	
	# Create and show form
	form = viAttributeForm()
	form.Owner = Rhino.UI.RhinoEtoApp.MainWindow
	form.Show()
	# Add the form to the sticky dictionary so it
	# survives when the main function ends.
	sc.sticky['attribute_form'] = form


# RunCommand is the called when the user enters the command name in Rhino.
# The command name is defined by the filname minus "_cmd.py"
def RunCommand( is_interactive ):
	print "Running", __commandname__



	# For every organized key print guids
	#for key in organizedKeys:
		
	#	print 'Part #: {} | Qty: {} | GUIDs: {}'.format(key, len(mainDictionary.get(key)), mainDictionary.get(key))
	#return sorted(removeDup, key=int)

	TestViAttributeForm()

if __name__ == "__main__":
	RunCommand(True)

Also, have a follow-up question.
I want to make something that looks like this.
ImageButtons
Where the black squares are image buttons.
step 1 How do I add a column?
I figure I might need to use groups? So I was looking at https://developer.rhino3d.com/guides/rhinopython/eto-controls-python/#groupbox and after adding drawing. to the Size(3,3) so the code doesn’t just error out. it doesn’t really appear on the form.
step 2 how do one assign an image to the forms.Button() and size?

Side note. It be awesome if someone put together some tutorials for using eto in python for super noobs… liket myself :wink:

Still looking for the solution on the crash issue.

Hi @mikhail, there are multiple causes. I can repeat the crashes and saw a log file was created on my desktop… So i’ve removed the argument from the close call (line 56 and line 60) and changed it just to:

self.Close()

It then still crashed Rhino6, cause is the disposing of the form in your OnFormClosed function. after commenting out line 111:

#form.Dispose()

i can open the form and click OK without crashing.

_
c.

1 Like

@clement
Thank you for looking this over! It seemed to have solved the issues on my end as well. :smile:

Hi @mikhail, below is an example using a bitmap derived from the graphics object. You can create the bitmap or use an existing on which you’ll need to reference. The process is the same:

import Eto
import Rhino

class Form1(Eto.Forms.Dialog[bool]):
    def __init__(self):
        self.Size = Eto.Drawing.Size(200, 200)
        self.Padding = Eto.Drawing.Padding(5)
        
        color = Eto.Drawing.Colors.White
        brush = Eto.Drawing.SolidBrush(color)
        pen = Eto.Drawing.Pen(Eto.Drawing.Colors.Black, 5)
        pixelformat = Eto.Drawing.PixelFormat.Format32bppRgba
        bitmap = Eto.Drawing.Bitmap(self.Size, pixelformat)
        
        graphics = Eto.Drawing.Graphics(bitmap)
        graphics.FillEllipse(brush, 30, 30, 140, 140)
        graphics.DrawEllipse(pen, 30, 30, 140, 140)
        graphics.Dispose()
        
        self.button = Eto.Forms.Button(Text="Hello World")
        self.button.Image = bitmap
        self.button.ImagePosition = Eto.Forms.ButtonImagePosition.Overlay
        
        layout = Eto.Forms.DynamicLayout()
        layout.AddRow(self.button)
        self.Content = layout
        self.Title = "Button Image"

dialog = Form1()
rc = dialog.ShowModal(Rhino.UI.RhinoEtoApp.MainWindow)
dialog.Dispose()

_
c.

1 Like

@clement Thank you for the information!