你已经在第 3 章“动手试一试”部分中建立了第一个温度转换程序。第 5 章中,我们又为它增加了用户输入,这样一来,需要转换的温度就不必硬编码写在程序中了。在第 6 章中,我们使用了 EasyGui 来得到输入并且显示输出。现在我们要使用 PythonCard 来建立这个温度转换程序的一个图形化版本。
TempGUI 组件
我们的温度转换 GUI 相当简单,只需要提供以下内容:
输入温度的位置(摄氏度和华氏度);
完成温度转换的按钮;
向用户显示有关信息的一些标签。
只是为了好玩,下面对摄氏度和华氏度分别使用两种不同类型的输入部件。在实际程序中绝对不要这么做(这只会把人们搞糊涂),不过这里我们的目的是学习如何使用这些部件!
术语箱
部件(widget)是对不同类型组件(按钮、滚动条、下拉列表等)的另一种称呼,有时也称为控件(control)。
建立了 GUI 布局后,会得到类似右图这样的结果:
也许你自己就可以完成,因为资源编辑器非常友好,很容易使用。不过没准你会需要一些帮助,所以这里还是会对相应步骤做些解释。这样也可以确保我们对组件使用相同的名字,便于更好地理解后面的代码。
并不要求组件完全对齐,也不必完全按这样来摆放组件,只要大致相同就可以了。
创建新的 GUI
第一步是建立一个新的 PythonCard 工程。打开资源编辑器,它会打开一个新的工程。如果前面的第一个 GUI 还是打开的,现在需要先关闭资源编辑器,然后再次打开。
下面开始增加组件:摄氏度输入框是一个 TextField,华氏度输入框是一个 Spinner,各个温度输入框下面的标签都是 StaticText 组件,另外还有两个 Button 组件。建立这个 GUI 的步骤如下。
选择 Component
Button。为按钮指定下面的属性。
点击 OK。把这个按钮拖到窗口中间的某个位置。
选择 Component
Button。为按钮指定下面的属性。
点击 OK。拖动这个按钮,把它放在前一个按钮的下面。
选择 Component
TextField。为这个文本域指定下面的属性。
保留这个文本域为空,点击 OK。把这个文本框向下拖一点,使它在 Celsius to Fahrenheit 按钮的左边。
选择 Components
Spinner。为这个微调控件(有时也称为一个微调框)指定以下名字。
点击 OK。把它向下拖一点,使它位于 Celsius to Fahrenheit 按钮的右边。
选择 Components
StaticText。保留原来的名字不变,不过要改变文本。
点击 OK。把这个 StaticText 拖到摄氏度文本域的下面。
选择 Components
StaticText。保留原来的名字不变,不过要改变文本。
点击 OK。把这个 StaticText 拖到华氏度微调框的下面。
现在所有 GUI 元素(组件,也称为控件或部件)都已经摆放好,并且赋予了我们想要的名字和标签。把这个资源文件保存为 TempGui.rsrc.py(在资源编辑器中选 择 File
Save As)。
接下来,在代码编辑器(SPE 或 IDLE)中新建一个文件,键入基本的 PythonCard 代码(或者也可以从我们的第一个程序复制):
from PythonCard import modelclass MainWindow(model.Background):app = model.Application(MainWindow)app.MainLoop
先不用考虑 pass 关键字,因为这只是块中没有定义任何内容时的一个占位符。我们将会为 MainWindow 类定义多个事件处理器。
摄氏度转换为华氏度
首先来完成摄氏度到华氏度的转换。将摄氏度转换为华氏度的公式是:
fahr = cel * 9.0 / 5 + 32
我们需要从 tfCel 文本框得到摄氏度,完成计算,再把结果放在 spinFahr 华氏度微调框中。这些应当在用户点击 Celsius to Fahrenheit 按钮时发生,所以要把完成这些工作的代码放在 Celsius to Fahrenheit 按钮的事件处理器中:
def on_btnCtoF_mouseClick(self, event):
为了从摄氏度框中得到值,我们使用了 self.components.tfCel.text。这个值是一个字符串,所以必须把它转换为一个浮点数:
cel = float(self.components.tfCel.text)
然后完成转换:
fahr = Cel * 9.0 / 5 + 32
接下来要把这个值放在华氏度框中。这里有一个小技巧:微调框中只能有整数值,不能有浮点数。所以把值放入微调框之前必须先把它转换为一个 int。微调框中显示的数是它的 value 属性,所以代码如下:
self.components.spinFahr.value = int(fahr)
华氏度转换为摄氏度
要完成另一个方向的转换(从华氏度转换为摄氏度),代码很类似。这个转换的公式是:
cel = (fahr - 32) * 5.0 / 9
这个代码要放在 Fahrenheit to Celsius 按钮的事件处理器中:
def on_btnFtoC_mouseClick(self, event):
首先从微调框得到华氏度:
fahr = self.components.spinFahr.value
这个值已经是一个整数,所以我们不必做任何类型的转换。然后应用公式:
cel = (fahr - 32) * 5.0 / 9
最后,把它转换为一个字符串,放在摄氏度文本框中:
self.components.tfCel.text = str(cel)
整个程序见代码清单 20-2。
代码清单 20-2 完成的温度转换程序
把这个程序保存为 TempGui.py。可以运行程序,试试这个 GUI。
小改进
运行这个程序时,你可能会注意到一点:将华氏度转换为摄氏度时,结果里有很多小数位,应该可以在文本框中去掉其中一些小数位。要解决这个问题有一个办法,称为打印格式化(print formatting)。我们还没有讨论到这个内容,你可以直接跳到第 21 章,那里对打印格式化的工作给出了一个完备的解释,或者也可以先直接键入这里给出的代码。在代码清单 20-2 的第 12 行(cel = (fahr - 32) * 5.0 / 9)和第 13 行(self.components.tfCel.text =str(cel))之间增加下面这行代码:
celStr = ‘%.2f’ % cel
这样显示一个数时会带两位小数。第 13 行中不再需要 str 函数(因为前面增加的代码已经为我们提供了一个字符串),所以代码应该变成这样:
self.components.tfCel.text = celStr
嗯……也许该调试了(debugging)。如果用户想转换南极洲的温度会怎么样呢?转换冥王星上的温度呢?
消灭错误
前面我们说过,要看程序中发生了什么,有一种很好的方法,就是在程序运行时打印出一些变量的值。下面就来试试看。
看起来是摄氏度到华氏度转换中的华氏度值有问题,所以就从这里开始。把下面这行代码增加到代码清单 20-2 的第 7 行 (fahr = cel * 9.0 / 5 + 32) 后面:
print \'cel = \', cel, \' fahr = \', fahr
现在,一旦点击 Celsius to Fahrenheit 按钮,可以看到 IDLE(或 SPE)shell 窗口中会打印出 cel 和 fahr 变量的值。对 cel 取几个不同的值,看看会发生什么。我得到了下面的结果:
>>> ============================ RESTART ============================>>>cel = 50.0 fahr = 122.0cel = 0.0 fahr = 32.0cel = -10.0 fahr = 14.0cel = -50.0 fahr = -58.0
看起来 fahr 值计算得很正确。那为什么华氏度框不能显示小于 0 的数呢?
再回到资源编辑器,点击用来显示华氏度的 spinFahr 微调框(必须点击带上下箭头的部分)。现在来看属性编辑器窗口,滚动属性编辑器查看不同的属性。有没有看到两个名为 min 和 max 的属性?它们的值是什么?现在你能不能猜出问题出在哪里?