Python Script for RhinoAPI to Create Legend

Hello everyone!

I’m a newcomer to RhinoAPI and currently working on a Python script within Rhino (to be used in Grasshopper) for generating a legend similar to the image below:

Here’s the code I’ve written so far, but unfortunately, it’s not functioning as expected, and I’m struggling to identify the issue:

import Rhino
import System.Drawing
import rhinoscriptsyntax as rs
import scriptcontext

class CustomConduit(Rhino.Display.DisplayConduit):
    def __init__(self, colors = [(205,0,71),(255,127,70),(255,219,71),(205,254,113),(90,220,186),(0,128,229),(0,0,206)], scale_factor = 1.0, text_title_dim = 10, text_dim = 8, side_dim = 10, texts = ["0","0.2","0.4","0.6","0.8","1",">1"],title = "U Legend", subtitle = "Utilisation factor [-]", subsubtitle = "GSA Case ", case = "(A1)"):
        self.colors = colors
        self.scale_factor = scale_factor
        self.text_title_dim = text_title_dim
        self.text_dim = text_dim
        self.side_dim = side_dim
        self.texts = texts
        self.title = title
        self.subtitle = subtitle
        self.subsubtitle = subsubtitle = case
    def DrawForeground(self,e):
        bounds = e.Viewport.Bounds
        corner = Rhino.Geometry.Point2d(bounds.Top, bounds.Right)
        TR_corner = Rhino.Geometry.Point2d(corner.X - 50, corner.Y -50)
        x_TR = TR_corner.X
        y_TR = TR_corner.Y
        for i in range(len(self.colors)):
            rectangle = System.Drawing.Rectangle.FromLTRB(x_TR - self.side_dim, y_TR - self.side_dim*i , x_TR , (y_TR - self.side_dim*i) - self.side_dim * i)
            color = System.Drawing.Color.FromArgb(self.colors[i][0],self.colors[i][1],self.colors[i][2])

def showlegend():
    conduit = None
    if scriptcontext.sticky.has_key("myconduit"):
        conduit = scriptcontext.sticky["myconduit"]
        conduit = CustomConduit()
        scriptcontext.sticky["myconduit"] = conduit
    conduit.Enabled = not conduit.Enabled
    if conduit.Enabled:
        print "Legend shown"
        print "Legend hidden"

if __name__=="__main__":

I prefer not to use external plugins and am currently working with Rhino 7. Any help or suggestions would be greatly appreciated!

Have a look at this topic, this post demonstrates the basic methods I use when drawing legends/HUDs:

1 Like

Thanks for the answer @AndersDeleuran :upside_down_face:. I’ll go and check it for sure!

I need to thank you again @AndersDeleuran !
Now the script works flawlessly.
I’ll leave the gh file with the Python component here in case anybody needs it. (16.4 KB)
For those who are interested I’ll also leave here the Python script:

__author__ = "Paolo.Colombo"
__version__ = "2023.12.21"

import System
import Rhino as rc
import Grasshopper as gh
import GhPython.Assemblies.ExecutingComponent as component

# Some useful description for the component inputs
ghenv.Component.Description = "This component create a personalized Legend. Input all the parameters to see the legend on each Rhino View."
ghenv.Component.Params.Input[0].Description = "Title of the legend as string."
ghenv.Component.Params.Input[1].Description = "List of values to display. The number of values should be the same as the number of colors."
ghenv.Component.Params.Input[2].Description = "List of colors. The number of colors should be the same as the number of values."
ghenv.Component.Params.Input[3].Description = "Subtitle as string."
ghenv.Component.Params.Input[4].Description = "Second line for an additional subtitle as string."
ghenv.Component.Params.Input[5].Description = "Font as string."
ghenv.Component.Params.Input[6].Description = "Scale as a decimal number."
ghenv.Component.Params.Input[7].Description = "X position of the legend inside the rhino views. The value 0 correspond to the extreme left of each view."
ghenv.Component.Params.Input[8].Description = "Y position of the legend inside the rhino views. The value 0 correspond to the extreme top of each view."

class MyComponent(component):
    def RunScript(self, Title, Values, Color, SubTitle, SubSubTitle, Font, Scale, x_pos, y_pos):
        #rc.RhinoApp.WriteLine("A 2")
        # Assign input variables
        self.heigh = 10
        self.heighT = 8
        self.title = Title
        self.col = Color
        self.val = Values
        self.subtitle = SubTitle
        self.subsubtitle = SubSubTitle
        self.font = Font
        self.scale = Scale
        self.x = x_pos
        self.y = y_pos = System.Drawing.Color.Black
        self.yellow = System.Drawing.Color.Yellow
    def DrawForeground(self,sender,arg): # Modify the DrawForeground event to draw whatever we like on the canvas
        #rc.RhinoApp.WriteLine("A 3")
        # Check component preview/locked, active GH document and Rhino viewport
        if (not ghenv.Component.Hidden and not ghenv.Component.Locked 
            and ghenv.Component.OnPingDocument() == gh.Instances.ActiveCanvas.Document
            and len(self.val)==len(self.col)):
            #and arg.Viewport.Id == arg.RhinoDoc.ActiveDoc.Views.ActiveView.ActiveViewportID): #do not considerate this 
            #rc.RhinoApp.WriteLine("A 4")
            # Title is drawn in front of mesh
            pt = rc.Geometry.Point2d(self.x,self.y-self.heigh*self.scale)
            # Subtitle
            pt_sub = rc.Geometry.Point2d(self.x,self.y+self.heigh*(len(self.val)+0.5)*self.scale)
            # values
            for i in range(len(self.val)):
                po = rc.Geometry.Point2d(self.x + (self.heigh*self.scale)*3/2,self.y + self.heigh*(i)*self.scale+self.heighT*0.25*self.scale)
            pt_subsub = rc.Geometry.Point2d(self.x, self.y+self.heigh*(len(self.val)+1.5)*self.scale)
            # Rectangle is drawn in front of mesh
            for i in range(len(self.val)):
                rec2d = System.Drawing.Rectangle.FromLTRB(self.x, self.y + i*self.heigh*self.scale, self.x + self.heigh*self.scale, self.y + i*self.heigh*self.scale + self.heigh*self.scale)

    def __enter__(self):
        #rc.RhinoApp.WriteLine("A 1")
        rc.Display.DisplayPipeline.DrawForeground += self.DrawForeground
    def __exit__(self):
        #rc.RhinoApp.WriteLine("A 5")
        rc.Display.DisplayPipeline.DrawForeground -= self.DrawForeground

Here’s a screenshot of the legend working: