Image inside the exception box

It’s my first developer post here, and I try to illustrate my question as clear as possible. I hope someone can kindly guide me.
I want to put a picture inside the exception dialogbox, similar to GL Mesh Shader did. I noticed that GL Mesh Shader display the active view inside the exception box, while I just want to put a static picture in it.

  • OpenGL Drawing mesh with a shader: this line should be Description
  • And I guest the window below should originally belong the Exception

I gues you’re looking for GH_ComponentAttributes.SetupTooltip method:
image

using System;
using System.Drawing;
using Grasshopper.GUI;
using Grasshopper.Kernel;
using Grasshopper.Kernel.Attributes;

namespace BitmapTooltip
{
    public class BitmapTooltipComponent : GH_Component
    {
        public BitmapTooltipComponent()
          : base("BitmapTooltip", "Nickname", "Description",
              "Category", "Subcategory") {}
        protected override void RegisterInputParams(GH_InputParamManager pManager) {}
        protected override void RegisterOutputParams(GH_OutputParamManager pManager) {}
        protected override void SolveInstance(IGH_DataAccess DA) {}
        protected override Bitmap Icon => null;
        public override Guid ComponentGuid => new Guid("2f48bacb-0dac-4d2b-9a49-96e9790cc781");
        public override void CreateAttributes()
        {
            Attributes = new BitmapTooltipAttributes(this);
        }
    }
    public class BitmapTooltipAttributes : GH_ComponentAttributes
    {
        public BitmapTooltipAttributes(IGH_Component component) : base(component)
        {
        }
        public override void SetupTooltip(PointF point, GH_TooltipDisplayEventArgs e)
        {
            base.SetupTooltip(point,e);
            e.Description = "";
            e.Diagram = Properties.Resources.SampleImage;
        }
    }
}

BitmapTooltip.gha (41 KB)
Source: BitmapTooltip.zip (167.4 KB)

3 Likes

Wow, thanks! That’s exact what I need. Helps a lot!!

1 Like

I would like to ask for help again.
I tried to change the tooltip.Diagram whenever the value of input slider changed, but I think I am facing a problem that

  • public override void CreateAttributes() runs right after the component be created in canvas

To illustrate my question more clearly, I want to achieve the tooltip Diagram can changed according to the slider number(in the red rectangle)

I have many template shapes, for example.

And I think I cannot safely called CreateAttributes() inside SolveInstance(), where I use if/else to determine the shapeType from the slider input. So this means CreateAttribute() seems always run before the SolveInstance run. I thought this might be related to eventHandler, whenever the Shape_Type value changed, CreateAttribute() can be called and show the corresponding diagram.

You must only ever once set the attributes of an object. The code inside your custom attributes needs to be smart enough to generate different images for the tooltip whenever asked.

Basically the SetupTooltip() method has to inspect the component which owns the attributes, draw the appropriate image and assign it to the Diagram field.

It sounds as though putting bitmaps in your resources isn’t going to cut it, you’ll have to create the images at runtime using code.

Edit: oh I misread, you just want a different image per type, you don’t want the dimensions to be correct as well, so you probably can get away with a bunch of precanned images in the resources.

1 Like

Thank you for the response. Yes, pictures inside the Resources folder is the way I plan to do. But the problem is:
Can I change the picture shown in the tooltip in runtime(to be more specific inside the SolveInstance() method, where I determine the shape type)? The way I put this image using CreateAttribute() and I cannot figure it out the other way to get access the GH_TooltipDisplayEventArg which owns e.Diagram this property.
As far as I know, I now can only get/set the value with e.Diagram.
I am not familiar with the behavior related to event/delegate . But I will try to illustrate what I thought about the SetupTooltip behavior here.
If there’s something wrong, please give me a lead.
Inside the GH_ComponentAttribute class there’s a SetupTooltip method, since self-defined class,BitmapTooltipAttributes, override it by this line

public override void CreateAttributes(){
  Attributes = new BitmapToolAttributes(this);
}

I generated one BitmapToolAttributes object, which own one override function SetupTooltip, and this SetupTooltip send the information carried by GH_TooltipDislayEventArgs e to somewhere the very first stage while component be created.

Not sure what’s the somewhere I mentioned above. But all this setup functions runs before SolveInstance() even start, so…
If there’s somehow a way I missed can change the Tooltip diagram while changing the input value.

Thank you for you quick response, and I am still new about programming and grasshopper, might loss something.

using System;
using System.Drawing;
using Grasshopper.GUI;
using Grasshopper.Kernel;
using Grasshopper.Kernel.Attributes;
namespace BitmapTooltip
{
    public class BitmapTooltipComponent : GH_Component
    {
        public Bitmap Tooltip;
        public BitmapTooltipComponent()
          : base("BitmapTooltip", "Nickname", "Description",
              "Category", "Subcategory") {}
        protected override void RegisterInputParams(GH_InputParamManager pManager)
        {
            pManager.AddIntegerParameter("i", "i", "i", GH_ParamAccess.item, 0);
        }
        protected override void RegisterOutputParams(GH_OutputParamManager pManager) {}
        protected override void SolveInstance(IGH_DataAccess DA)
        {
            var i = 0;
            DA.GetData(0, ref i);
            i = Rhino.RhinoMath.Clamp(i, 0, 6);
            Tooltip = (Bitmap) Properties.Resources.ResourceManager.GetObject($"_{i}");

        }
        protected override Bitmap Icon => null;
        public override Guid ComponentGuid => new Guid("2f48bacb-0dac-4d2b-9a49-96e9790cc781");
        public override void CreateAttributes()
        {
            Attributes = new BitmapTooltipAttributes(this);
        }
    }
    public class BitmapTooltipAttributes : GH_ComponentAttributes
    {
        public BitmapTooltipAttributes(IGH_Component component) : base(component) { }
        public override void SetupTooltip(PointF point, GH_TooltipDisplayEventArgs e)
        {
            base.SetupTooltip(point,e);
            e.Description = "";
            e.Diagram = ((BitmapTooltipComponent)Owner).Tooltip;
        }
    }
}

BitmapTooltip.gha (104 KB)
BitmapTooltip.zip (348.5 KB)

3 Likes

As @Mahdiyar said, put the logic inside the SetupTooltip() method. This method is called every time the tooltip is shown.

To reduce a bunch of bitmap overhead though, I’d recommend putting all the heavy lifting in the SetupTooltip() method. Inside SolveInstance(), set some cheap public property to whatever the current shape is. This could be an integer, or an enum, or a string. Then inside SetupTooltip() read this field and load the appropriate bitmap from the resources.

2 Likes

You mean like this?

using System;
using System.Drawing;
using Grasshopper.GUI;
using Grasshopper.Kernel;
using Grasshopper.Kernel.Attributes;
namespace BitmapTooltip
{
    public class BitmapTooltipComponent : GH_Component
    {
        public int Index { get; private set; }
        public BitmapTooltipComponent()
          : base("BitmapTooltip", "Nickname", "Description",
              "Category", "Subcategory") {}
        protected override void RegisterInputParams(GH_InputParamManager pManager)
        {
            pManager.AddIntegerParameter("i", "i", "i", GH_ParamAccess.item, 0);
        }
        protected override void RegisterOutputParams(GH_OutputParamManager pManager) {}
        protected override void SolveInstance(IGH_DataAccess DA)
        {
            var i = 0;
            DA.GetData(0, ref i);
            i = Rhino.RhinoMath.Clamp(i, 0, 6);
            Index = i;

        }
        protected override Bitmap Icon => null;
        public override Guid ComponentGuid => new Guid("2f48bacb-0dac-4d2b-9a49-96e9790cc781");
        public override void CreateAttributes()
        {
            Attributes = new BitmapTooltipAttributes(this);
        }
    }
    public class BitmapTooltipAttributes : GH_ComponentAttributes
    {
        public BitmapTooltipAttributes(IGH_Component component) : base(component) { }
        public override void SetupTooltip(PointF point, GH_TooltipDisplayEventArgs e)
        {
            base.SetupTooltip(point,e);
            e.Description = "";
            e.Diagram = (Bitmap)Properties.Resources.ResourceManager.GetObject($"_{((BitmapTooltipComponent)Owner).Index}");
        }
    }
}

BitmapTooltip.gha (104 KB)
BitmapTooltip.zip (349.2 KB)

2 Likes

Thank you @Mahdiyar @DavidRutten, this is exact what I want to achieve! I did not know there’s a ResourceManager, a lot of things needed to learn… Thank you a lot!!!

Hi, I want to ask for a bit clear explanation about why can we binding the code

Tooltip = (Bitmap)Properties.Resources.ResourceManager.GetObject($"_{i}")

to the code

 public override void SetupTooltip(PointF point, GH_TooltipDisplayEventArgs e)
        {
            ...
            e.Diagram = ((BitmapTooltipComponent)Owner).Tooltip;
        }

I cannot fully understand when SetupTooltip run.
From my point of view, the we used

Tooltip =(Bitmap)Properties.Resources.ResourceManager.GetObject($"_{i}")

to assign the correspond Bitmap to the Tooltip object. How can our component knows after this new assignment should use override SetupTooltip again? This is the thing I still want to figure out, hope you can kindly guide me. All the way, you have been very helpful and I am thankful for the response post you gave. Thank you again!