5.3.1 统计图表

图形的一个重要用途是为数据提供可视的表示,这在统计、汇总性质的应用程序中尤其 重要,因为汇总数据几乎都可以利用图形来改善表示。下面我们编写一个简单的统计汇总程 序,以演示图形编程在数据可视化方面的应用。

假设某高校的老师在考试后需要根据学生的考试成绩来分析试卷,以判断试卷是偏难、 偏容易还是适中。难度适中的试卷应该导致正态分布的成绩。为帮助老师完成试卷分析,我 们编写一个统计汇总程序,其功能是:老师输入考试分数(百分制),然后程序将分数换算 成等级制(分为 A、B、C、D、F 五等)并统计各等级分数的人数,最后画一个饼图来直观 地给出各等级人数的比例。

程序规格

输入:考试分数。

输出:以饼图表示的各分数段所占比例。

算法设计

本程序在算法上很简单,属于典型的 IPO(输入-处理-输出)模式。不过虽然算法很 简单,但是在绘制图形方面需要花费大量精力,因为绘图涉及精确的坐标、形状、颜色等细 节,还需要整个图形画面看上去整齐、匀称、美观。可以说,图形编程中大量时间都花在了 这类“美工”任务之上。

首先,由用户输入每个学生的分数(百分制)。然后根据该分数所对应的等级去累加各 等级人数变量。输入结束后,总人数和各等级人数就确定了。

其次,计算各分数等级人数占总人数的比例。

然后,根据比例绘制饼图。在 Tkinter 编程中,这需要先创建窗口和画布,然后利用画 布的 create_arc()方法绘制代表五个等级的五个扇形。扇形的角度反映了各分数等级的

比例,扇形具有不同填充色以相互区分。为了显示各扇形对应的等级,还需要绘制图例。 最后,用户通过饼图各扇形的大小只能看出各分数等级所占的大致比例。精确的比例值

当然可以固定显示在画面中,不过我们采用另一种更有趣的设计:当用户将鼠标指针移入某 个扇形中时,画布上就显示该扇形所代表的比例值。

以上步骤还需要进一步明确细节,最主要的就是窗口、画布的大小和各图形项的精确位 置等。通过用草图等手段做一些计算和试验,最终确定如图 5.18 所示的设计:

5.3.1 统计图表 - 图1

图 5.18 画布图形项设计

至此,可以写出本程序的算法伪代码。

  1. 算法:
  2. 用户输入考试分数 mark,并根据 mark 对应的等级累加各等级的人数 abcdf
  3. 创建窗口和大小为 300x200 的画布;
  4. 计算各分数等级的比例(a/n 等),并据此确定每个扇形的起止角度(sAeA 等);
  5. 绘制各个扇形;
  6. 绘制图例; 为各扇形绑定“鼠标进入”事件,并定义事件处理函数(inPieA()等);
  7. 进入主事件循环。

代码实现

从上面的算法很容易翻译成 Python 代码。程序 5.2 中所用到的知识都在前面介绍过, 只有“鼠标进入”事件的处理需要说明一下。

当鼠标指针移到某个图形项上面时即发生事件”“,这时系统触发所绑定的事 件处理函数(如 inPieA),这些函数的功能是计算该图形项对应的比例值,然后显示在画 布上的指定位置。另外由于事件处理函数中需要引用画布对象和各图形项,所以我们将这些 函数的定义放在了 main()函数内部,以便它们能引用 main()中定义的变量,即 cv、 piepct、a、b、c、d、f 和 n。

【程序 5.2】piechart.py

  1. from Tkinter import *
  2. def getMarks():
  3. a,b,c,d,f = 0,0,0,0,0
  4. mark = input("Enter a mark: ")
  5. while mark >= 0:
  6. if mark >= 90:
  7. a = a + 1
  8. elif mark >= 80:
  9. b = b + 1
  10. elif mark >= 70:
  11. c = c + 1
  12. elif mark >= 60:
  13. d = d + 1
  14. else:
  15. f = f + 1
  16. mark = input("Enter a mark: ")
  17. return a,b,c,d,f
  18. def main():
  19. a,b,c,d,f = getMarks()
  20. win = Tk()
  21. cv = Canvas(win,width=300,height=200,bg="white")
  22. cv.pack()
  23. n = a+b+c+d+f
  24. eA,sA = 360.0*a/n,0
  25. eB,sB = 360.0*b/n,eA
  26. eC,sC = 360.0*c/n,eA+eB
  27. eD,sD = 360.0*d/n,eA+eB+eC
  28. eF,sF = 360.0*f/n,eA+eB+eC+eD
  29. bb = (90,40,210,160)
  30. pieA = cv.create_arc(bb,start=sA,extent=eA,fill="yellow")
  31. pieB = cv.create_arc(bb,start=sB,extent=eB,fill="green")
  32. pieC = cv.create_arc(bb,start=sC,extent=eC,fill="black")
  33. pieD = cv.create_arc(bb,start=sD,extent=eD,fill="gray")
  34. pieF = cv.create_arc(bb,start=sF,extent=eF,fill="red")
  35. cv.create_rectangle(240,40,260,50,fill="yellow") cv.create_rectangle(240,40+24,260,50+24,fill="green") cv.create_rectangle(240,40+48,260,50+48,fill="black") cv.create_rectangle(240,40+72,260,50+72,fill="gray") cv.create_rectangle(240,40+96,260,50+96,fill="red")
  36. cv.create_text(270,40,text="A",anchor=N)
  37. cv.create_text(270,40+24,text="B",anchor=N) cv.create_text(270,40+48,text="C",anchor=N) cv.create_text(270,40+72,text="D",anchor=N) cv.create_text(270,40+96,text="F",anchor=N)
  38. piepct = cv.create_text(40,100,text="")
  39. def inPieA(event):
  40. pct = "%5.1f%%" % (100.0*a/n)
  41. cv.itemconfig(piepct,text=pct)
  42. def inPieB(event):
  43. pct = "%5.1f%%" % (100.0*b/n)
  44. cv.itemconfig(piepct,text=pct)
  45. def inPieC(event):
  46. pct = "%5.1f%%" % (100.0*c/n)
  47. cv.itemconfig(piepct,text=pct)
  48. def inPieD(event):
  49. pct = "%5.1f%%" % (100.0*d/n)
  50. cv.itemconfig(piepct,text=pct)
  51. def inPieF(event):
  52. pct = "%5.1f%%" % (100.0*f/n)
  53. cv.itemconfig(piepct,text=pct)
  54. cv.tag_bind(pieA,"<Enter>",inPieA)
  55. cv.tag_bind(pieB,"<Enter>",inPieB)
  56. cv.tag_bind(pieC,"<Enter>",inPieC)
  57. cv.tag_bind(pieD,"<Enter>",inPieD)
  58. cv.tag_bind(pieF,"<Enter>",inPieF)
  59. win.mainloop()
  60. main()

程序 5.2 的一次运行结果如图 5.19 所示。

5.3.1 统计图表 - 图2

图 5.19 程序 5.2 的一次执行结果