作者:“咕咕咕?下一篇马上就写好了”

通过上一篇【基础10】的文章,大家已经了解到一个GH电池在画布上的样式是由其背后的 GH_Attribute 类实例来决定的,而大部分的GH电池都使用了它的一个派生类 GH_ComponentAttribute 来配置电池的外观。今天我们就继续上一篇的内容,通过它来给我们的电池配置一个按钮,当我们的按钮被按下去的时候,可以切换我们今天例子中电池的工作模式。

首先我们来介绍一下今天的例子,它将会是一个简单的求 某个数的平方根 的电池:

public class SqrtRootPosNeg : GH_Component
{public SqrtRootPosNeg(): base("SqrtRootPosNeg", "SRPN","Description","Params", "DigitalCrab") { }protected override void RegisterInputParams(GH_Component.GH_InputParamManager pManager){pManager.AddNumberParameter("A", "A", "", GH_ParamAccess.item);}protected override void RegisterOutputParams(GH_Component.GH_OutputParamManager pManager){pManager.AddNumberParameter("A", "A", "", GH_ParamAccess.item);}protected override void SolveInstance(IGH_DataAccess DA){double a = 0.0;if (!DA.GetData(0, ref a)) return;DA.SetData(0, Math.Sqrt(a));}protected override System.Drawing.Bitmap Icon => null;public override Guid ComponentGuid => Guid.Parse("请替换成自己生成的GUID");
}

从代码上也能看出来这是个十分简单的电池,仅有一个输入和一个输出,SolveInstance的过程也只是将输入的double变量直接求算术平方根。

但是,一个数的平方根有两个,一个是正数,一个负数,Math.Sqrt()函数给出的只是那个正的平方根,我们接下来就做一个电池,让这个电池上面有一个按钮,点一下之后,这个电池就能输出负的平方根,再点一下就切换回来输出正的平方根。简而言之,就是使用一个在电池上的按钮来切换电池的工作形态。

定义电池的工作形态

要让电池实现两种不同的工作模式,那必然是要在电池类上添加一个表达电池当前 状态 的某种“变量”,然后我们通过按钮来改变这个“变量”的值,就可以实现电池的工作状态切换了。

最简单的方式当然是给电池一个bool属性,true的时候输出正平方根,false的时候输出负平方根。但是嘛,bool值毕竟不太直观,直接上个enum,增强可阅读性。

那么,电池的非UI部分的主要逻辑(关于数据处理的逻辑)就完成了:

public class SqrtRootPosNeg : GH_Component
{public enum SqrtMode { Positive, Negative } /* 定义一个enum类型 */public SqrtMode CompWorkMode { get; set; } = SqrtMode.Positive; /* 使用这个enum类型来定义一个代表电池工作状态的变量 *//* 修改电池的SolveInstance逻辑,引入对电池工作状态的判断 */protected override void SolveInstance(IGH_DataAccess DA){double a = 0.0;if (!DA.GetData(0, ref a)) return;if (CompWorkMode == SqrtMode.Positive)DA.SetData(0, Math.Sqrt(a));elseDA.SetData(0, -Math.Sqrt(a));}/* ... 略 ... */
}

接下来的部分就是要实现电池上增加一个按钮的功能。

创建一个自定义Attribute

我们可以直接在同一个.cs文件内,在电池的class的外面再定义一个新的class,作为我们电池的Attribute。一般来说我个人建议是每个类都单独成为一个文件,这样的话,管理起来比较方便,也更直观。但是在本例中我们就直接放在一个.cs文件内吧,这样做也是可以被编译器所接受的。

public class SqrtRootPosNeg : GH_Component
{public override void CreateAttributes() /* 重写CreateAttribute方法以启用自定义电池外观 */{Attributes = new SqrtAttribute(this);}/* ... 略 ... */
}public class SqrtAttribute : GH_ComponentAttributes
{public SqrtAttribute(SqrtRootPosNeg component) : base(component) { }protected override void Layout(){base.Layout();}protected override void Render(GH_Canvas canvas, Graphics graphics, GH_CanvasChannel channel){base.Render(canvas, graphics, channel);}
}

如上面代码所示,我们需要创建一个类,继承自GH_ComponentAttribute,并给出一个继承的构造方法。下面就是重点,需要重写类的两个方法以实现我们增加按钮的目的。

重写 Layout() 方法

在【基础10】中我们提到了,电池在画布上具体是如何渲染是决定于Render()方法,但是为什么我们在这里要重写Layout()方法?这是因为,Layout()方法会在Render()方法之前被执行,用来决定这个电池的具体在画布上占用多大的空间,这个是由Bounds属性决定的。而想要改变这个属性,在Layout()属性中更改是最合适不过了。

我们常用的按钮大概高度可以在 16px (16像素)左右,考虑到按钮周围应该留出 1~2px 的空间(让电池看起来不是贴在电池边),我们这里设置将 Bounds 加高 20px。

public class SqrtAttribute : GH_ComponentAttributes
{public SqrtAttribute(SqrtRootPosNeg component) : base(component) { }protected override void Layout(){base.Layout(); /* 先执行base.Layout(),可以按GH电池默认方式计算电池的出/入口需要的高度,我们在下面基于这个高度进行更改 */Bounds = new RectangleF(Bounds.X, Bounds.Y, Bounds.Width, Bounds.Height + 20.0f);}protected override void Render(GH_Canvas canvas, Graphics graphics, GH_CanvasChannel channel){base.Render(canvas, graphics, channel);}
}

此时,我们的电池已经在常规电池大小的下方多出来一块地方来放置我们的按钮了。

重写 Render() 方法

在准备好渲染按钮的空间之后,接下来就可以真正地开始绘制和渲染电池了:

  • 使用 RectangleF 预先确定要绘制按钮的区域
  • 使用 GH_Capsule 创建并绘制一个按钮

这里的 GH_Capsule 是一个继承自 IDisposible 接口的对象,代表着它可以手动释放资源,为了能够正确释放资源,我们可以使用 using 关键词来自动处理。

public class SqrtAttribute : GH_ComponentAttributes
{public SqrtAttribute(SqrtRootPosNeg component) : base(component) { }protected override void Layout(){base.Layout();Bounds = new RectangleF(Bounds.X, Bounds.Y, Bounds.Width, Bounds.Height + 20.0f);}protected override void Render(GH_Canvas canvas, Graphics graphics, GH_CanvasChannel channel){base.Render(canvas, graphics, channel); /* 执行基本的电池渲染 *//* 额外的电池渲染,仅在“Objects”这个渲染轨道绘制 */if (channel == GH_CanvasChannel.Objects){RectangleF buttonRect = /* 按钮的位置 */ new RectangleF(Bounds.X, Bounds.Bottom - 20, Bounds.Width, 20.0f);/* 在X、Y方向分别留出2px的空隙,以免button贴住电池边 */buttonRect.Inflate(-2.0f, -2.0f);using (GH_Capsule capsule = GH_Capsule.CreateCapsule(buttonRect, GH_Palette.Black)){/* 按照该电池的“是否被选中”、“是否被锁定”、“是否隐藏”三个属性来决定渲染的按钮样式 *//* 这样可以使得我们的按钮更加贴合GH原生的样式 *//* 也可以自己换用其他的capsule.Render()重载,渲染不同样式电池 */capsule.Render(graphics, Selected, Owner.Locked, Owner.Hidden);}}}
}

好了,看看电池现在的状态把,我们的按钮应该就会被渲染出来了:

嗯…… 看起来好像还少了行字,让我们渲染个字上去吧。按钮上的字可以用来表示当前电池的工作状态,这样用户一看就知道目前电池是什么状态了。

public class SqrtAttribute : GH_ComponentAttributes
{/* ... 略 ... */protected override void Render(GH_Canvas canvas, Graphics graphics, GH_CanvasChannel channel){/* ... 略 ... */if (channel == GH_CanvasChannel.Objects){/* ... 略 ... */graphics.DrawString(((SqrtRootPosNeg)Owner).CompWorkMode.ToString(), new Font(GH_FontServer.ConsoleSmall, FontStyle.Bold), Brushes.White, buttonRect, new StringFormat(){Alignment = StringAlignment.Center,LineAlignment = StringAlignment.Center});}}
}

这回看起来好多了。

好了剩下的就是如何响应鼠标的点击了。

重写 RespondToMouseDown() 函数

当GH画布检测到鼠标事件之后,会检测画布当前位置是否存在电池(还记得【基础10】里的BoundsInPickRegion吗),如果存在,则会触发该电池的RespondToMouseDown()函数。当然,如果你没有重写这个函数的话,在Grasshopper的默认实现里是什么额外操作都不会做的,要实现在鼠标点击我们的按钮区域,切换电池工作状态的话,就需要重写这个逻辑,做自己的实现。

public class SqrtAttribute : GH_ComponentAttributes
{/* ... 略 ... */public override GH_ObjectResponse RespondToMouseDown(GH_Canvas sender, GH_CanvasMouseEvent e){return base.RespondToMouseDown(sender, e);}
}

整个鼠标事件大概是这样:我们先检测鼠标事件,是否是鼠标左键点击且在按钮的区域内,如果满足这俩条件,则改变电池工作状态并通知画布ExpireSolution,若这两条件有一个不满足,则无事发生。

public class SqrtAttribute : GH_ComponentAttributes
{/* ... 略 ... */public override GH_ObjectResponse RespondToMouseDown(GH_Canvas sender, GH_CanvasMouseEvent e){RectangleF buttonRect = /* -重新计算按钮的区域大小- */ new RectangleF(Bounds.X, Bounds.Bottom - 20, Bounds.Width, 20.0f);if (e.Button == MouseButtons.Left && buttonRect.Contains(e.CanvasLocation)){SqrtRootPosNeg comp = (SqrtRootPosNeg)Owner; /* 通过Owner属性来获得电池本身 *//* 依照电池当前工作状态来改变电池 */if (comp.CompWorkMode == SqrtRootPosNeg.SqrtMode.Negative)comp.CompWorkMode = SqrtRootPosNeg.SqrtMode.Positive;elsecomp.CompWorkMode = SqrtRootPosNeg.SqrtMode.Negative;/* 改变完电池后,重启计算 */comp.ExpireSolution(true);/* 结束鼠标事件处理,通知GH已经处理完毕 */return GH_ObjectResponse.Handled;}/* 若上述条件未满足,则直接返回“未处理” */return GH_ObjectResponse.Ignore;}
}

下面就是这个电池的整个工作状态了:

同样的方式,只需要计算好电池需要增加电池的区域,预先在Layout()中规划Bound属性,然后再在Render()方法中制作对应的GH_Capsule即可在任何地方添加按钮了(添加到电池外面也是可以的,任意位置……只要你想)。如果还想在电池中绘制奇奇怪怪的东西,可以参照微软对Graphcis类的教程,链接如下,里面详细说了如何绘制线、矩形、圆形、如何构建笔刷等等的步骤:

https://docs.microsoft.com/en-us/dotnet/desktop/winforms/advanced/how-to-draw-a-line-on-a-windows-form?view=netframeworkdesktop-4.8

老规矩,完整cs源码在最后,下次再继续。

【Grasshopper基础11】如何在GH电池上增加一个自己的按钮相关推荐

  1. 如何在ESXi5.1上运行一个虚拟的ESXi虚拟机并且在这个虚拟的ESXi虚拟机里运行64位的系统?...

    如何在ESXi5.1上运行一个虚拟的ESXi虚拟机并且在这个虚拟的ESXi虚拟机里运行64位的系统? 这个虚拟的ESXi虚拟机也叫作嵌入式ESXi,目前这个功能是一个体验版,仅用于测试或者教学演示用, ...

  2. 【Grasshopper基础5】在GH里看基金? —— 简单电池项目实战

    经过前面[Grasshopper基础1~4]的介绍,相信读者已经了解了如何在Visual Studio里创建电池.如何获取数据.如何传出数据. 那么在了解这些原理之后,就让我们来一起实现一个小的项目, ...

  3. html显示宇宙星星,css 如何在html页面上输出一个六角星星呢?

    摘要: 下文讲述使用css脚本在页面上绘制一个六角星星的方法分享,如下所示: 实现思路: 使用一个向上三星形和一个向下三角形进行叠加,即可形成一个六角的星星,如下所示: 例: 使用css绘制一个六角星 ...

  4. 如何在HP服务器上增加新的硬盘

    为了在HP服务器上增加新的硬盘,需要做两件事情:① 将新增硬盘挂到主板上: ② 在磁盘管理中,对新增的硬盘进行分区.那么我们下面介绍如何完成,上述两步. ① 将新增硬盘挂到主板上.插入新服务器硬盘,然 ...

  5. pytorch每日一学47(torch.unsqueeze())在指定维度上增加一个大小为1的维度

    第47个方法 torch.unsqueeze(input, dim) → Tensor 很明显这个方法是与 torch.squeeze()进行相反的操作,torch.squeeze()是将移出tens ...

  6. 如何在Win7电脑上增加新磁盘分区?

    我们在重装好系统Win7系统后有时会碰到需要新建磁盘分区的情况,这时我们再重装系统进行磁盘分区就有些过于麻烦了,其实我们可以利用Win7系统自身的磁盘管理功能来新建一个磁盘分区.下面好系统重装助手就来 ...

  7. Vue -- 如何在 span 标签上实现一个点击事件

    使用 JQuery 时,需要通过下述方式为选中的页面元素绑定单击事件 $(".myclass").on("click" , function(){ } 但在 V ...

  8. html在点击按钮变量加一,如何在html文件上按一个按钮将变量设置到flask服务器rou...

    我在这中间有点困惑,希望你能帮助我 我有一个与下面类似的文本文件:./Video/SetUp ./Video/NewRecordings ./Video/NewRecordings/20160113_ ...

  9. 如何在Mac电脑上创建一个签名身份(Signing Identity)

    在Mac电脑上可以很方便的为自己创建一个签名身份(即Signing Identity).所谓的签名身份其实就是自己创建的一个私钥和相应的证书对,保存在自己的机器上,并且这个签名身份可以用来对二进制文件 ...

最新文章

  1. VUE的组件DEMO
  2. 用easyui动态创建一个对话框
  3. List集合与List的子类
  4. Bash脚本教程之脚本入门
  5. for循环之性能优化
  6. SqlCommand.ExecuteReader 方法
  7. Struts(十二):异常处理:exception-mapping元素
  8. Redis高可用方案:sentinel(哨兵模式)以及springboot整合sentinel模式
  9. 嵌入式可视化编程软件选哪个好?(可视化编程平台介绍、测评与选择)【Scratch、Mind+、Mixly】
  10. U盘刻录系统之后变小
  11. 管家婆 凭证查找 Date exceeds maximum of 19-12-31 报错解决办法
  12. 【C语言】求方程式 ax^2+bx+c=0 的根, 分别考虑: 1、有两个不等的实根 2、有两个相等的实根
  13. CDH6.2.1安装Kudu maste无法启动 Unable to initialize catalog manager
  14. LT8911EXB MIPI DSI CSI转EDP
  15. C++程序设计三周教学记录
  16. C#毕业设计——基于C#+asp.net+sqlserver的证券术语解释及翻译系统设计与实现(毕业论文+程序源码)——翻译系统
  17. hadoop 2.7.3 版本下载
  18. Android鹰眼轨迹追踪
  19. 【教3妹学mysql】一条慢sql如何排查优化
  20. win7计算机管理找不到文件夹,win7系统中电脑文件夹选项不见了的具体解决方法...

热门文章

  1. Samtools view转sam为bam报错[main_samview] truncated file.
  2. html5 ul下的li重叠解决,firefox中div重叠覆盖之前ul的两种解决方法
  3. 计算机辅助设计建筑,建筑工程计算机辅助设计
  4. 音频放大器lm386
  5. mysql查询前5条记录_各个数据库中,查询前n条记录的方法
  6. springboot毕设项目心理咨询预约导诊系统4r699(java+VUE+Mybatis+Maven+Mysql)
  7. vulnhub靶机-Photographer: 1
  8. 戴尔笔记本电脑更换固态重装系统
  9. Matlab画三维图的方法
  10. Python如何创建一个单链表,以及实现单链表的增,删,改,查操作,以及对单链表的排序功能