Skip to content

Latest commit

 

History

History
393 lines (276 loc) · 24.9 KB

File metadata and controls

393 lines (276 loc) · 24.9 KB

二、使用 Tkinter 设计 GUI 应用

软件应用的开发分为三个重复阶段:理解问题、设计解决方案和实施解决方案。这些阶段在应用的整个生命周期中重复,对其进行细化和磨练,直到其达到最佳状态或过时。

在本章中,我们将学习以下主题:

  • 介绍和分析工作场所中需要软件解决方案的场景
  • 记录解决方案的需求
  • 为实现解决方案的软件开发设计

ABQ AgriLabs 的一个问题

祝贺你的 Python 技能让你在 ABQ AgriLabs 找到了一份出色的数据分析师工作。到目前为止,您的工作相当简单:对实验室数据输入人员每天发送给您的 CSV 文件进行整理和简单的数据分析。

不过,有一个问题。您沮丧地注意到,来自实验室的 CSV 文件的质量令人遗憾地不一致。数据丢失,打字错误比比皆是,而且通常需要在耗时的过程中重新输入文件。实验室主任也注意到了这一点,并且知道您是一名熟练的 Python 程序员,她认为您可能能够提供帮助。

您已被招募编写一个解决方案,该解决方案将允许数据输入人员以较少的错误将实验室数据输入到 CSV 文件中。您的应用需要简单,并允许尽可能少的错误空间。

评估问题

电子表格通常是需要跟踪数据的计算机用户的第一站。它们的表格式布局和计算功能似乎使它们非常适合这项任务。然而,随着一组数据的增长并被多个用户添加到其中,电子表格的缺点变得显而易见:它们不强制执行数据完整性,在处理长行稀疏或不明确的数据时,它们的表格式布局可能会在视觉上造成混乱,如果用户不小心的话,可以很容易地删除或覆盖数据。

为了改善这种情况,您建议实现一个简单的 GUI 数据输入表单,以我们需要的格式将数据附加到 CSV 文件中。表单可以通过以下几种方式帮助提高数据完整性:

  • 仅允许输入正确类型的数据(例如,仅允许数字字段中的数字)
  • 仅将选项限制为有效选项
  • 自动填充信息,如当前日期、时间等
  • 验证输入的数据是否在预期范围内或与预期模式匹配
  • 确保已填写所有数据

通过实现这样一个表单,我们可以大大减少数据输入人员输入的错误数量。

收集有关问题的信息

要构建数据输入表单应用,您需要收集有关它需要完成什么的详细信息。幸运的是,您已经知道了方程式的输出部分:您需要一个 CSV 文件,其中包含关于每个实验室地块中生长的植物以及每个地块的环境条件的数据。您每天都在处理这些文件,因此对字段布局非常熟悉。

但是,您并不完全了解数据或输入数据的过程;您需要与其他相关员工交谈,以了解更多信息。

首先,您需要了解有关正在记录的数据的更多细节。这并不像听起来那么容易。软件在处理数据时需要绝对的黑白规则;另一方面,人们倾向于泛泛地思考他们的数据,他们通常不考虑限制或边缘情况的确切细节而不需要一些提示。

作为一名程序员,你的工作就是提出问题,从而提供你所需要的信息。

你决定从实验室技术人员开始,了解他们收集的更多数据。您会提出以下问题:

  • 每个字段可以接受哪些值?是否有字段约束为一组值?
  • 每个数字字段表示什么单位?
  • 数字字段真的是数字字段吗?他们需要字母或符号吗?
  • 每个数字字段可接受的数字范围是多少?
  • 如何记录数据,需要多长时间?

数据不是唯一的考虑因素。如果我们正在制作一个帮助减少用户错误的程序,我们还必须了解这些用户以及他们是如何工作的。在本应用中,我们的用户将是数据输入人员。我们需要向他们询问有关其需求和工作流程的问题,以了解如何创建适合他们的应用。

我们提出了以下问题清单:

  • 您输入的数据是什么格式的?
  • 何时收到数据,多久输入?最新的输入时间是多少?
  • 是否有可以自动填充的字段?用户是否应该能够覆盖自动值?
  • 用户的总体技术能力如何?
  • 您喜欢当前解决方案的哪些方面?你不喜欢什么?
  • 使用者是否有视觉或手部损伤,应予以适应?

最后,我们需要了解操作应用所涉及的技术——用于完成任务的计算机、网络、服务器和平台。

您决定添加以下问题,当您与数据输入人员会面时,您将对这些问题进行评估:

  • 数据输入使用什么类型的计算机?
  • 它运行在哪个平台上?
  • 它有多快或多强大?
  • Python 在这些系统上可用吗?
  • 哪些 Python 库可用?

你发现了什么

首先,写下你所知道的关于 ABQ 的以下基本知识:

  • 您的 ABQ 设施有五个温室,每个温室在不同的气候下运行,标记为 a、B、C、D 和 E
  • 每个温室有 20 个地块(标记为 1 到 20)
  • 目前有四个种子样本,每个样本用六个字符的标签编码
  • 每个地块有 20 颗给定样本的种子,以及自己的环境传感器单元

有关正在收集的数据的信息

你和实验室技术人员的谈话揭示了很多数据。一天四次,分别在 8:00、12:00、16:00 和 20:00,每个技术人员在一个或两个实验室检查绘图。他们使用纸质表格记录每个绘图的值,将所有值记录到小数点后两位。每个实验室通常需要 30 到 40 分钟,整个过程通常需要 90 分钟

每个地块都有一个环境传感器,用于检测地块的光线、温度和湿度。不幸的是,这些设备容易发生故障,由装置上的Equipment``Fault指示灯指示。技术人员记录此灯是否点亮,因为它会使环境数据无效。

最后,技师会告诉您字段的单位和可接受范围,您会将其记录在下表中:

| 字段 | 数据类型 | 注释 | | Date | Date | 数据收集日期。几乎总是当前日期 | | Time | Time | 测量期间的开始时间。8:00、12:00、16:00 或 20:00 之一 | | Lab | Character | 实验室 ID,将是 A 到 E | | Technician | Text | 记录数据的技师的姓名 | | Plot | Int | 绘图 ID,从 1 到 20 | | Seed Sample | Text | 种子样本的 ID 字符串。始终是包含数字 0 至 9 和大写字母 a 至 Z 的六字符代码 | | Fault | Boolean | 如果环境设备出现故障,则为 True,否则为 false | | Humidity | Decimal | 绝对湿度(单位:g/m³),大致在 0.5 和 52.0 之间 | | Light | Decimal | 绘图中心的阳光量(千勒克斯),介于 0 和 100 之间 | | Temperature | Decimal | 摄氏度,不应低于 4 度或高于 40 度 | | Blossoms | Int | 地块中的花朵数量必须为 0 或更多,但不太可能接近 1000 朵 | | Fruit | Int | 地块中的水果数量必须为 0 或更多,但不可能接近 1000 | | Plants | Int | 正在生长的植物数量,介于 0 和 20 之间。 | | Max height | Decimal | 最高植物的高度,以厘米为单位。至少为 0,不太可能接近 1000。 | | Median height | Decimal | 地块中植物的平均高度,单位为 cm。至少为 0,不太可能接近 1000 | | Min height | Decimal | 最小植物的高度,以厘米为单位。至少为 0,不太可能接近 1000 | | Notes | Long Text | 关于工厂、数据、仪器等的附加观察 |

有关应用用户的信息

您与数据输入人员的对话产生了关于他们的工作流程、需求和技术的良好信息。

实验室的技术人员在填写完表格后,把他们的纸质表格交了下来。数据通常是立即输入的,并且通常是在提交数据的同一天输入的。

技术人员目前正在使用 Debian Linux 工作站上的 LibreOffice 输入数据。使用复制和粘贴,他们可以使用重复的数据(如日期、时间和技术人员)批量填充字段。LibreOffice 的自动完成功能通常对文本字段很有帮助,但有时会导致数字字段中的意外数据错误。

正在使用的工作站已有几年的历史,但性能良好。您有机会查看一下,发现 Python 和 Tkinter 已经安装好了。

共有四名数据录入员,但每次只有一名;在采访职员时,你会发现其中一人患有红绿色盲,另一人由于 RSI 问题而无法使用鼠标。所有人都相当精通计算机。

记录规范要求

现在,您已经收集了有关应用的数据,现在是编写规范的时候了。软件规范可以是非常正式的合同文档,包括时间估计和截止日期,也可以是程序员打算构建的一组简单描述。规范的目的是为项目中涉及的每个人提供开发人员将创建的参考点。它详细说明了要解决的问题、所需的功能以及程序应该和不应该做什么的范围。

您的场景非常非正式,应用也很简单,因此在本例中不需要详细的正式规范。然而,对你所知道的进行一次基本的总结可以确保你、你的老板和用户都在同一页上。

简单规范的内容

我们将从以下需要编写的项目概要开始我们的规范:

  • 说明:这是一句或两句描述应用的主要目的、功能和目标的句子。可以将其视为程序的任务陈述。

  • 所需功能:本节列出了程序需要能够实现最低功能的具体事项。它既可以包括硬需求,如详细的输出和输入格式,也可以包括软需求目标,这些目标无法量化实现,但程序应努力实现(例如,“尽可能减少用户错误”)。

  • 功能不需要:本节列出了程序不需要做的事情;它的存在是为了明确软件的范围,并确保没有人期望从应用中得到不合理的东西。

  • 限制:这是程序必须在技术和人力条件下运行的限制列表。

  • 数据字典:这是应用将处理的数据字段及其参数的详细列表。这些文件可能会很长,但随着应用的扩展和数据在其他上下文中的使用,它们是一个关键的参考文件。

编写 ABQ 数据输入程序规范

你可以用你最喜欢的文字处理器编写一个规范,但理想情况下,规范是你代码的一部分;它需要与代码一起保存,并与应用的任何更改同步。因此,我们将使用reStructuredText标记语言在文本编辑器中编写它。

For Python documentation, reStructuredText, or reST, is the  official markup language. The Python community encourages the use of reST to document Python projects, and many packaging and publication tools used in the Python community expect the reST format. We'll cover reST in more depth in Chapter 5, Planning for the Expansion of Our Application, but you can find the official documentation at http://docutils.sourceforge.net/rst.html.

让我们开始编写规范,一次编写一节,如下所示:

  1. 以应用的名称和简短描述开始规范。其中应包含计划目的的摘要,如下所示:
======================================
 ABQ Data Entry Program specification
======================================

Description
-----------
The program is being created to minimize data entry errors for laboratory measurements.
  1. 现在,让我们列出要求。记住,硬需求是客观上可实现的目标输入和输出需求、必须完成的计算、必须呈现的特性,而软需求是主观的或尽力而为的目标。从最后一节看你的发现,并考虑哪些需要是什么。

您应该提出如下建议:

Functionality Required
----------------------

The program must:

* allow all relevant, valid data to be entered, as per the field chart
* append entered data to a CSV file
  - The CSV file must have a filename
    of abq_data_record_CURRENTDATE.csv, where 
    CURRENTDATE is the date of the checks in 
    ISO format (Year-month-day)
  - The CSV file must have all the fields as per the chart
* enforce correct datatypes per field

The program should try, whenever possible, to:

* enforce reasonable limits on data entered
* Auto-fill data
* Suggest likely correct values
* Provide a smooth and efficient workflow
  1. 接下来,我们将在Functionality Not Required部分的程序范围内进行控制。请记住,这只是目前的一份报名表;编辑或删除将在电子表格应用中处理。我们将澄清如下:
Functionality Not Required
--------------------------

The program does not need to:

* Allow editing of data. This can be done in LibreOffice if necessary.
* Allow deletion of data.
  1. 对于Limitations部分,请记住,我们有一些用户具有物理约束,以及硬件和操作系统约束。添加如下:
Limitations
-----------

The program must:

* Be efficiently operable by keyboard-only users.
* Be accessible to color blind users.
* Run on Debian Linux.
* Run acceptably on a low-end PC.
  1. 最后是数据字典,这基本上是我们之前制作的表,但我们将对范围、数据类型和单位进行细分,以便快速参考,如下所示:
+------------+----------+------+--------------+---------------------+
|Field       | Datatype | Units| Range        |Descripton           |
+============+==========+======+==============+=====================+
|Date        |Date      |      |              |Date of record       |
+------------+----------+------+--------------+---------------------+
|Time        |Time      |      |8, 12, 16, 20 |Time period          |
+------------+----------+------+--------------+---------------------+
|Lab         |String    |      | A - E        |Lab ID               |
+------------+----------+------+--------------+---------------------+
|Technician  |String    |      |              |Technician name      |
+------------+----------+------+--------------+---------------------+
|Plot        |Int       |      | 1 - 20       |Plot ID              |
+------------+----------+------+--------------+---------------------+
|Seed        |String    |      |              |Seed sample ID       |
|sample      |          |      |              |                     |
+------------+----------+------+--------------+---------------------+
|Fault       |Bool      |      |              |Fault on sensor      |
+------------+----------+------+--------------+---------------------+
|Light       |Decimal   |klx   | 0 - 100      |Light at plot        |
+------------+----------+------+--------------+---------------------+
|Humidity    |Decimal   |g/m³  | 0.5 - 52.0   |Abs humidity at plot |
+------------+----------+------+--------------+---------------------+
|Temperature |Decimal   |°C    | 4 - 40       |Temperature at plot  |
+------------+----------+------+--------------+---------------------+
|Blossoms    |Int       |      | 0 - 1000     |# blossoms in plot   |
+------------+----------+------+--------------+---------------------+
|Fruit       |Int       |      | 0 - 1000     |# fruits in plot     |
+------------+----------+------+--------------+---------------------+
|Plants      |Int       |      | 0 - 20       |# plants in plot     |
+------------+----------+------+--------------+---------------------+
|Max height  |Decimal   |cm    | 0 - 1000     |Ht of tallest plant  |
+------------+----------+------+--------------+---------------------+
|Min height  |Decimal   |cm    | 0 - 1000     |Ht of shortest plant |
+------------+----------+------+--------------+---------------------+
|Median      |Decimal   |cm    | 0 - 1000     |Median ht of plants  |
|height      |          |      |              |                     |
+------------+----------+------+--------------+---------------------+
|Notes       |String    |      |              |Miscellaneous notes  |
+------------+----------+------+--------------+---------------------+

这就是我们现在的规格!当我们发现新的需求时,规范很可能会在复杂性上增长、变化或演变。

设计应用

有了我们的规范和明确的要求,是时候开始设计我们的解决方案了。我们将从表单 GUI 组件本身开始。

我们将通过以下三个步骤为表单创建基本设计:

  1. 为每个数据字段确定合适的input小部件
  2. 将相关项目组合在一起,形成一种组织感
  3. 在表单上按组布局小部件

探索 Tkinter 输入小部件

与所有工具包一样,Tkinter 为不同类型的数据提供了多种input小部件。然而,ttk提供了额外的小部件类型,并增强了 Tkinter 的一些(但不是全部!)原生小部件。下表提供了关于哪些小部件最适合不同类型的数据输入的建议:

| 小部件 | 说明 | 用于 | | ttk.Entry | 基本文本输入 | 单行字符串 | | ttk.Spinbox | 带有递增/递减箭头的文本输入 | 数字 | | Tkinter.Listbox | 包含选项列表的框 | 在几个值之间进行选择 | | Tkinter.OptionMenu | 带有选项的下拉列表 | 在几个值之间进行选择 | | ttk.Combobox | 带有可选文本输入的下拉列表 | 在多个值和文本输入之间进行选择 | | ttk.Checkbutton | 带标签的复选框 | 布尔值 | | ttk.Radiobutton | 与复选框相似,但只能选择一组中的一个 | 在一小组值之间进行选择 | | Tkiner.Text | 多行文本输入框 | 长的多行字符串 | | Tkinter.Scale | 鼠标操作滑块 | 有界数数据 |

让我们考虑哪些小部件适合于需要输入的数据:

  • 有几个Decimal字段,许多字段具有清晰的边界范围,分别为Min heightMax heightMedian heightHumidityTemperatureLight。您可以使用Scale小部件进行这些操作,但它并不适合精确的数据输入,因为它需要仔细定位才能获得精确的值。它也是鼠标操作的,这违反了您的规范要求。相反,使用Spinbox小部件进行这些操作。
  • 还有一些Int字段,如PlantsBlossomsFruit。同样,Spinbox小部件是正确的选择。
  • 有两个字段具有有限的可能值集-TimeLabRadiobuttonListbox小部件可以用于这些功能,但它们都占用大量空间,而且键盘友好性较差,因为它们需要使用箭头键进行选择。还有OptionMenu,但也只是鼠标键或箭头键。对于这些,请使用Combobox小部件。
  • 阴谋是个棘手的案子。从表面上看,它看起来像一个Int字段,但请仔细想想。这些情节也可以用字母、符号或名字来识别。数字恰好是一组易于分配任意标识符的值。Plot IDLab ID一样,是一组受约束的值;因此,在这里使用Combobox小部件更有意义。
  • Notes字段是多行文本,因此Text小部件适用于此处。
  • 有一个Boolean字段Fault。它可以用RadiobuttonCombobox处理,但Checkbutton是最佳选择,它紧凑且键盘友好。
  • 其余的行是简单的单行字符字段。我们将在这些字段中使用Entry
  • 您可能想知道Date字段。Tkinter 没有日期的特殊小部件;因此,我们暂时在这里使用一个通用的Entry小部件。

我们的最终分析如下:

| 字段 | 小部件类型 | | Blossoms | ttk.Spinbox | | Date | ttk.Entry | | Fault | ttk.Checkbutton | | Fruit | ttk.Spinbox | | Humidity | ttk.Spinbox | | Lab | ttk.Combobox | | Light | ttk.Spinbox | | Max height | ttk.Spinbox | | Median height | ttk.Spinbox | | Min height | ttk.Spinbox | | Notes | Tkinter.Text | | Plants | ttk.Spinbox | | Plot | ttk.Combobox | | Seed Sample | ttk.Entry | | Technician | ttk.Entry | | Temperature | ttk.Spinbox | | Time | ttk.Combobox |

分组我们的领域

当人类盯着一堵没有特定顺序的巨大输入墙时,往往会感到困惑。您可以通过将输入表单拆分为一组相关字段来帮助用户。当然,这假设您的数据有相关的字段集,不是吗?

查看字段后,您可以确定以下相关组:

  • DateLabPlotSeed SampleTechnicianTime字段用于标识记录本身的数据或元数据。你可以把它们放在一个标题下,比如Record information
  • BlossomsFruit、三个Height字段和Plants字段都是与Plot字段中的植物有关的测量。您可以将这些组合为Plant data
  • HumidityLightTemperatureEquipment``Fault字段都是来自环境传感器的信息。你可以把它们归为Environmental data
  • Notes字段可以与任何内容相关,因此它属于自己的一个类别。

要在 Tkinter 中对前面的字段进行分组,我们可以在每组字段之间插入标签,但值得探索将小部件分组在一起的各种选项:

| 小部件 | 说明 | | ttk.LabelFrame | 带有标签文本和可选边框的框架 | | ttk.NoteBook | 允许多页的选项卡式小部件 | | Tkinter.PanedWindow | 允许水平或垂直排列的多个可重新调整大小的框架 |

我们不希望表单出现在多个页面上,用户也不需要调整分区大小,但是LabelFrame小部件听起来非常适合我们的需要。

布置表格

到目前为止,我们知道我们有 17 个输入,其分组如下:

  • Record information下的六个字段
  • Environmental data下的四个字段
  • Plant data下的六个字段
  • 一个大的Notes字段

我们希望使用LabelFrame对前面的输入进行分组。

请注意,前三个部分中有两个部分的小部件是三的倍数。这意味着我们可以将它们排列成一个网格,其中包含三个项目。我们应该如何对每个组中的字段进行排序?

字段的排序似乎是一个微不足道的项目,但对于用户来说,它可以在可用性方面产生显著的差异。必须随意跳转表单以匹配工作流的用户更容易出错。

如您所知,数据是从实验室技术人员填写的纸质表格中输入的。您获得了该表单的副本,如以下屏幕截图所示:

看起来项目的分组方式与记录的分组方式基本相同,因此我们将使用此表单上的排序来对字段进行排序。这样,数据录入员就可以快速浏览表单,而无需在屏幕上来回弹跳。

When designing a new application to replace some part of an existing workflow, it's important to learn and respect that workflow. While we'll have to adjust that workflow to actually improve it, we don't want to make another part of someone's job harder just to make the part we're working on simpler.

在我们的设计中,最后一个需要考虑的问题是在何处放置与字段相关的字段标签。UI 设计界对标签的最佳放置方式存在大量争论,但一致认为以下两种选择之一是最佳的:

  • 字段上方的标签
  • 字段左侧的标签

您可以尝试绘制这两个字段,以查看您更喜欢哪一个,但对于此应用,由于以下原因,上述字段的标签可能会更好地工作:

  • 由于字段和标签的形状都是矩形的,因此通过堆叠它们,我们的表单将更加紧凑
  • 使布局工作起来容易得多,因为我们不必找到一个适用于所有标签的标签宽度,而不会使它们与字段相距太远

一个例外是检查按钮字段;检查按钮通常标记在小部件的右侧。

花点时间用纸和铅笔或绘图程序对你的表格做一个模型。您的表格应如下所示:

规划应用

考虑到表单的设计,现在是考虑应用 GUI 的其余部分的时候了:

  • 您需要一个保存按钮来触发输入数据的存储
  • 有时,我们可能需要向用户提供状态信息;应用通常有一个状态栏,显示这类消息
  • 最后,最好有一个标题来指示表单是什么

将以下内容添加到我们的草图中,我们有如下截图:

看起来不错!这绝对是我们可以在 Tkinter 中实现的一种形式。最后一步是向用户和主管展示这些设计,以获得任何反馈或批准。

让涉众尽可能多地参与应用设计过程。这降低了以后必须返回并重新设计应用的可能性。

总结

在本章中,您完成了应用开发的前两个阶段:理解问题和设计解决方案。您了解了如何通过访问用户、检查数据和需求、为用户创建最佳表单布局来开发应用规范,还了解了 Tkinter 中可用于处理不同类型输入数据的小部件。最重要的是,您了解到开发应用不是从代码开始,而是从研究和规划开始。

在下一章中,您将使用 Tkinter 和 Python 创建设计的基本实现。我们将熟悉创建表单、构建表单以及在应用中放置表单所需的 Tkinter 小部件。我们还将学习如何使表单触发回调操作,并了解如何构造代码以确保效率和一致性。