Thank you for the clarification.
It doesn’t necessarily need to be compiled as a .ghpy file — packaging it as standard Rhino and Grasshopper plugin binaries (.rhp and .gha) works perfectly fine for me, honestly even better.
What I’m really trying to understand is how to migrate my existing IronPython scripts — which I previously compiled into .ghpy components — into this new plugin-based workflow. Many of those scripts subclassed GH_Component directly to override rendering behavior, customize attributes, and modify component (for example, custom graphics, etc.).
So my main question is: when moving to the new plugin packaging approach, is subclassing GH_Component and overriding attributes/graphics still supported? Or are those kinds of UI customizations no longer possible in the newer system?
I’d really appreciate any clarification/steps guide on how this maps to the current Rhino 8 / Grasshopper development workflow.
The structure of my script is really similar to the post I also referenced where it subclassed the GH_Component for adding a button. I’ll copy paste it below for reference. If I had something similar would I be able to use the packaging method to a plugin binaries using the script editor? If so what’s the proper approach?
from ghpythonlib.componentbase import executingcomponent as component
import Grasshopper, GhPython
import System
class Button_UI(Grasshopper.Kernel.Attributes.GH_ComponentAttributes): # inherits all methods of GH_ComponentAttributes
def __init__(self, owner):
super(Button_UI, self).__init__(owner)
self.component = owner
self.button_down = False
def Layout(self): # override inherited method Layout
Grasshopper.Kernel.Attributes.GH_ComponentAttributes.Layout(self)
component_rect = Grasshopper.Kernel.GH_Convert.ToRectangle(self.Bounds)
component_rect.Height += 22
self.button_rect = System.Drawing.Rectangle(component_rect.X, component_rect.Bottom-22, component_rect.Width, 22)
self.button_rect.Inflate(-2, -2)
Bounds = component_rect
self.Bounds=Bounds
ButtonBounds = self.button_rect
self.ButtonBounds=ButtonBounds
def Render(self,canvas, graphics, channel): # Override Render method
Grasshopper.Kernel.Attributes.GH_ComponentAttributes.Render(self, canvas, graphics, channel)
if channel == Grasshopper.GUI.Canvas.GH_CanvasChannel.Objects:
color = Grasshopper.GUI.Canvas.GH_Palette.Grey if self.button_down else Grasshopper.GUI.Canvas.GH_Palette.Black
button = Grasshopper.GUI.Canvas.GH_Capsule.CreateTextCapsule(self.ButtonBounds, self.ButtonBounds, color, "Button", 2, 0)
button.Render(graphics, self.Selected, self.Owner.Locked, False)
button.Dispose()
def RespondToMouseDown(self,sender,e):
if e.Button == System.Windows.Forms.MouseButtons.Left:
rec = self.button_rect
pt = System.Drawing.Point.Round(e.CanvasLocation)
if not self.button_down:
if pt.X > rec.X and pt.X < rec.X+rec.Width and pt.Y > rec.Y and pt.Y < rec.Y+rec.Height:
self.button_down = True
self.component.OnDisplayExpired(False)
return Grasshopper.GUI.Canvas.GH_ObjectResponse.Capture
return super(Button_UI, self).RespondToMouseDown(sender, e)
def RespondToMouseUp(self,sender,e):
if e.Button == System.Windows.Forms.MouseButtons.Left:
if self.button_down:
self.button_down = False
self.component.OnDisplayExpired(False)
rec = self.button_rect
pt = System.Drawing.Point.Round(e.CanvasLocation)
if pt.X > rec.X and pt.X < rec.X+rec.Width and pt.Y > rec.Y and pt.Y < rec.Y+rec.Height:
self.component.click_response()
return Grasshopper.GUI.Canvas.GH_ObjectResponse.Release
return super(Button_UI, self).RespondToMouseUp(sender, e)
class MyComponent(component):
def __init__(self):
self.click_count = 0
def CreateAttributes(self): # Override inherited method create attributes
self.m_attributes=Button_UI(self)
def RunScript(self, x, y):
return self.click_count
def click_response(self):
self.click_count += 1
self.ExpireSolution(True)