7.2.2 在窗体上显示绘图

7.2.2 在窗体上显示绘图


绘图与第四章的示例类似。因为绘图需要一定的时间,我们将在内存中创建位图,绘制好文档,然后,在窗体上显示位图,而不是每次窗体失效时都绘制文档。我们先看一下非常有用的函数式编程模式,这一节就将使用。


“Hole in the Middle(中间有洞)”模式


[真心不知道,Hole in the Middle 是什么意思?]
写代码的一个常见情形,是首先进行初始化,然后,是函数的核心部分,最后,是一些清理工作;在程序中的多个位置,重复执行类似的操作,初始化和清理部分没有变化,而只有核心部分不同。在内存中绘制位图的示例,用 C# 写的代码可能像这样:


var bmp = new Bitmap(width, height)
using(var gr = Graphics.FromImage(bmp)) { 
  (...)
}


这里,代码的核心部分是第三行的占位符,我们将用值 gr 来绘图。问题是,只使用面向对象的编程概念,不能简单地把执行初始化和清理的代码,包装成一个子程序,实现在绘制不同图形的所有地方共享。C# 语言支持这种模式众所周知,通常使用各种初始化和清理的类型;using 构造就是这种情况。我们如何为自己的代码模式实现类似的功能呢?
用函数式编程,解决起来很简单;可以写一个高阶函数,把核心部分包装到 lambda 函数中,并把它作为高阶函数的参数值:


var bmp = DrawImage(width, height, gr => {
    (...)
  });


从函数编程的角度来看,这仅是一个使用高阶函数的无趣示例,但是,它描述了这样一种情况,先执行一些初始化,接着是核心部分,然后是清理,而且这种情况非常普遍,所以,需要有一个专门的名字。这个名字是由 Brian Hurt 在他的博客《“Hole in the Middle(中间有洞)”模式》[Hurt, 2007] 中首次使用,它很好地描述了这种情况,即,每次使用代码,只有中间的部分需要用不同的功能填充。


清单 7.7 是用 F# 实现的函数,类似于补充材料“Hole in the Middle(中间有洞)模式”中的 DrawImage,有一点不同,用两个参数指定创建位图的大小,还能够指定图像边框的边距。


清单 7.7 绘制图形的函数 (F# Interactive)
 
> let drawImage (width:int, height:int) space coreDrawingFunc =
     let bmp = new Bitmap(width, height)
     use gr = Graphics.FromImage(bmp)    [1]
     gr.Clear(Color.White)
     gr.TranslateTransform(space, space)    <-- 移动整个图像
     coreDrawingFunc(gr)    [2]
     bmp
;;
val drawImage : int * int -> float32 -> (Graphics -> unit) –> Bitmap  [3]


我们使用这个函数来绘制图像,绘图的核心部分是作为最后参数给定的函数,从类型签名[3]可以看出,这个函数的参数为 Graphics 值,不返回结果;在代码的中间[2],创建位图和 Graphics 对象之后,被调用。在初始化阶段,我们还调用 TranslateTransform,提供图像的边距。
我们创建的 Graphics 对象实现了 IDisposable,所以,需要在完成绘图后释放。在 C# 中,我们使用知名的 using 构造;在 F# 中,用 use 关键字[1],可以做类似的工作。在清单 7.7 中,在返回结果的位图之前,自动释放 Graphics 对象。use 关键字的与 using 有点不同,我们将在第九章讨论。
最后,我们需要看代码的实际效果。现在,我们将用交互方式创建并测试窗体。清单 7.8 演示了如何绘制清单 7.5 的屏幕元素,以及把文档显示在窗体上。


清单 7.8 使用 WinForms 绘制文档 (F# Interactive)
> let docImage = drawImage (450, 400) 20.0f (drawElements elements)  [1]
val docImg : Bitmap


> open System.Windows.Forms
let main = new Form(Text = "Document", BackgroundImage = docImage,    | 创建有文档
                                Width = docImage.Width, Height = docImage.Height)   | 的窗体
main.Show();;                                                    |


绘制位图[1]的这一行可能要解释一下。我们调用 drawImage,它的最后一个参数是函数,指定了绘图的核心部分。因为我们曾经在 drawElements中实现了这个函数,你可能希望能够将它直接作为最后一个参数值来传递;然而,drawElements 有两个参数,而 drawImage 函数只有一个参数(需要绘制的 Graphics 对象)。我们使用散函数应用指定有 ScreenElement 值的列表;散应用的结果是函数,以 Graphics 对象,并绘制文档,这正是我们需要的 (图 7.2)。
 


图 7.2 保存在屏幕元素列表中的示例文档,使用 drawElements 函数在窗体上绘制。



我们以前使用的文档表示方式,虽然使创建文档的代码是有点笨拙,但是,能够轻松地实现绘制。在函数式编程中,我们经常会发现,不同的上下文需要不同数据结构:在某种程度上,期望的用法决定好的表示形式;对于函数式程序,同样的信息,在同一个程序中,有不同的表示形式,并不少见。现在,我们已经找到了适合绘制图像的表示形式,那么,还需要设计适合构造和处理的表示形式,然后,再写一个转换函数,实现两种表示形式的转换。
identity 身份认证 购VIP最低享 7 折! triangle vip 30元优惠券将在 04:24:36 后过期 去使用 triangle 数据可视化是将复杂的数据集通过图表、图像等视觉元素进行呈现,以便于人们更容易地理解和解读数据。在“数据可视化期末课设~学生成绩可视化分析.zip”这个压缩包中,我们可以看到一系列与数据可视化相关的资源,包括Jupyter代码、HTML图片、答辩PPT以及Word文档,这些内容涵盖了数据可视化的基础到高级应用,适合于完成一个全面的期末课程设计项目。 Jupyter代码是使用Python编程语言进行数据处理和可视化的主要工具。在这个项目中,学生可能使用了pandas库来加载和清洗数据,可能涉及到的数据处理步骤包括去除重复值、处理缺失值以及数据类型转换等。接着,他们可能使用matplotlib或seaborn库来创建各种图表,如直方图、散点图、箱线图等,以展示学生成绩的分布、对比和趋势。此外,更高级的可视化库如plotly或bokeh可能也被用来实现交互式图表,增加用户对数据的理解深度。 保存的HTML图片是Jupyter Notebook的输出结果,它展示了代码运行后的可视化效果。这些图片可以直观地揭示学生成绩的统计特征,例如平均分、标准差、最高分和最低分等。通过颜色编码或者图例,我们可以识别出不同科目或者不同班级的表现,帮助分析教学质量和学生学习情况。 答辩PPT则可能包含项目的概述、目的、方法、结果和结论。在PPT中,学生可能会详细阐述他们选择特定可视化方法的理由,如何解读图表,以及从数据中得出的洞察。此外,PPT的制作也是展示其表达和沟通能力的重要部分,要求清晰、有逻辑地组织信息。 Word文档可能是项目报告,详细记录了整个过程,包括数据来源、预处理步骤、使用的可视化技术、分析结果以及可能遇到的问题和解决方案。报告中的数据分析部分会详细解释图表背后的含义,例如通过对比不同学科的分数分布,找出哪些科目可能存在困难,或者分析成绩与特定因素(如性别、年级等)的关系。 这个压缩包提供了完整的数据可视化项目实例,涉及了数据获取、处理、可视化和解释的一系列步骤,对于学习和掌握数据可视化技能非常有价值。通过这样的练习,学生不仅能够提高编程技巧,还能培养数据驱动思维和问题解决能力,为未来从事数据分析或相关领域的工作打下坚实的基础。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值