12月10, 2019

Python 做 UI 超 easy(5)——在本地编程,使用 Combo 组件

经过前面几期的努力,我们做了这些事:

  • 学习使用 Popup、Text、Edit、Button,创建视窗;
  • 处理视窗的返回值:event 和 values,它们在一个 tuple 中;
  • 用 requests 库请求网络资源——天气预报接口;
  • 做了一个简单的天气预报工具;
  • 让程序活的更长,变成持久化程序。

相比最开始学习 Python 只能得到一个黑窗输出一些字符,真的是已经相当 “现代化”了!

使用更专业的工具

之前我们的代码很多都托管在 repl.it 上,为了:

  • 免除 Python 环境搭建之苦;
  • 减少不一致性,确保同样的代码可以得到相同的结果,因为我们用的是同一套在线环境。

但是这个环境有点问题,对中文支持不好,还有点慢,而且 GUI 程序主要是在个人电脑上运行。我相信通过这么久的使用,对你来说安装 Python3 已经不再那么困难了。

TIPS
菜鸟教程 是我非常推荐的一个在线学习网站,它的环境搭建教程非常详细,希望可以帮到你。

安装好 Python3 以后,你就可以进入命令行输入

pip install PySimpleGUI
pip install requests

安装所需要的库。要记住:第三方库真的不在你的计算机里,在 “第三方” 那里。使用它们前一定要先安装,否则在代码中 import xxx 执行的时候就会报错 “No module named xxx”。

然后再安装 pycharm 或者 visual studio code,这些工具是更智能的开发工具,它会自动找到已经安装好的 Python。如果安装遇到问题,借助百度一般会得到很好的解决。

TIPS
我使用的是 visual studio code,如果你不确定,最好与我保持一致,也安装 visual studio code。

总之,是时候认真的编程了,在你的电脑上写程序、调试程序、使用程序,像一个真正的本地程序一样。

本地运行上一节代码

当你安装好 Python3,并且安装了 pycharm 或 visual studio code 之一作为开发工具,那么请新建一个目录 getWeatherTool,在其中创建一个 main.py 文件,粘贴下面的代码,这些代码是 上一节 的成果:

import PySimpleGUI as sg
import requests
import json

def get_weather(city):
    # 在线环境不支持中文输入,所以我改用城市编号的接口,如果你在电脑上开发,就不受影响
    r = requests.get("http://wthrcdn.etouch.cn/weather_mini?citykey=" + city)
    result = json.loads(r.text)
    return result["data"]["forecast"][0]["type"]


# 让所有文本居中
sg.SetOptions(text_justification='center') 

# 蓝图不需要放到循环中
layout = [ 
           [ sg.Text("City", size = (20, 1)), sg.Input(key = "-CITY-") ],
           [ sg.Text("Weather", size = (20, 1)), sg.Input(key = "-WEATHER-") ],
           [ sg.Button("Submit")]
         ]

# 视窗也只需要创建一次,不要放到循环里
window = sg.Window("Weather App", layout)

while True:
  event, values = window.read()

  print(event, values)

  # 当点击 window 右上角的 X,event 是 None,此时应当退出循环
  if event is None:
    break

  # values 是一个字典,访问可输入组件的 key(定义蓝图时指定了这个参数)可以获得组件的输入
  city = values["-CITY-"]

  weather = get_weather(city)

  print(weather)

  # 找到天气输入框
  weather_wind = window["-WEATHER-"]

  # 将天气更新到输入框
  weather_wind.update(weather)

  # 程序已经不会直接退出了,下一行就不需要了
  # window.read()

# 当退出循环的时候,就是程序退出之时,关掉 window
window.close()

点击调试运行就可以看到结果了,记住,现在用的天气预报接口里,城市要填写编号,北京的编号是 101010100

title

hu~ 北京的雾霾真是挥之不去~

一点改进

下面做一点改进,城市名称使用中文。

由于之前使用的是在线编辑环境,对中文支持不好,所以使用城市编码的形式。只要更换一个接口,就可以支持中文城市名查询了。

修改这个函数,使用新的接口:

def get_weather(city):
    # 在线环境不支持中文输入,所以我改用城市编号的接口,如果你在电脑上开发,就不受影响
    # 注释掉旧接口,不再使用城市编码查询天气
    # r = requests.get("http://wthrcdn.etouch.cn/weather_mini?citykey=" + city)
    r = requests.get("http://wthrcdn.etouch.cn/weather_mini?city=" + city)
    result = json.loads(r.text)
    return result["data"]["forecast"][0]["type"]

TIPS
看出来使用函数的好处了吧。 当要修改某个功能的时候,只要修改一个函数就可以。如果不用函数,就需要在代码各处修改,很麻烦,最糟糕的是容易出错。

再试着运行一次看看!这次我们输入城市名称就可以了,非常棒!

title

看,很多城市都有雾霾,武汉倒是多云,不错~

所有人都喜欢选择题

小时候考试对我一直都是梦魇,现在偶尔也会被惊醒。

即便如此,我对选择题也抱有特别对好感,即使完全不会也可以用 “学渣口诀” 蒙一个答案。

NOT TIPS
学渣口诀:
不是 A,就是 B,不会就蒙 C 和 D。
三长一短就选最短, 三短一长选最长,两长两短就选 B,参差不齐 C 无敌。

对于用户也是一样,选择要比填空更加友好。我们使用 Combo 替换 layout[0] 中的 Input。Combo 就是下拉框。

layout = [ 
           #[ sg.Text("City", size = (20, 1)), sg.Input(key = "-CITY-") ],
           [ sg.Text("City", size = (20, 1)), sg.Combo(("北京", "上海", "深圳"), size=(10, 1), default_value="上海", key = "-CITY-")]
           [ sg.Text("Weather", size = (20, 1)), sg.Input(key = "-WEATHER-") ],
           [ sg.Button("Submit")]
         ]

注意到 Combo 的初始化参数多了一些:

  • 第一项参数是一个 tuple,是下拉框里所有的选项,这里列出了 3 个城市。
  • 为了显示所有下拉框内容,我们将 size 指定为 (10, 1), 10 个英文字符宽,一个字符高。
  • 再没有选中的时候,Combo 默认显示 default_value 中的值。

再看看结果:

title

成功的把填空题变成了选择题,对我这样的 “学渣” 实在是太照顾了。

TIPS
其实在用户心理中,从填空到选择的变化,引起的是用户决策成本的变化:选择要比提建设意见消耗的脑细胞少的多,成本耕地,用户会觉得更舒适。
对用户好一点,记住,这是对软件开发者最重要的建议!可能比写代码更重要。

没有最懒,只有更懒!

那就再宠爱一点用户咯,送佛送上天嘛!记得在介绍 window.read() 的时候,我们提到过 Button 只是触发 read 函数返回的组件之一,其他的各种可输入组件都可以!比如这里的 Combo 组件被选中的时候。

但是需要做一点准备工作:

layout = [ 
           #[ sg.Text("City", size = (20, 1)), sg.Input(key = "-CITY-") ],
           [ sg.Text("City", size = (20, 1)), sg.Combo(("北京", "上海", "深圳"), size=(10, 1), default_value="上海", change_submits=True, key = "-CITY-")],
           [ sg.Text("Weather", size = (20, 1)), sg.Input(key = "-WEATHER-") ] #,
           #[ sg.Button("Submit")]
         ]

我们为 Combo 增加了 change_submits=True,意思是当它发生变化的时候,要提交 event。

这样 layout[2] 中的 Button 其实就不需要了。每次 Combo 发生变化的时候,我们直接执行 Submit 按钮触发的逻辑就行了。

我们原来的代码会接受除了 退出(event is None) 外所有 event,所以到底是 Butto 还是 Combo 触发的就没所谓,只要触发 read 函数就行,代码也无需做更多更改。

来吧,成败在此一举!

title

行了!胸dei~ 我们还减少了一个按钮,这意味着用户可以少一次点击,真不错!

TIPS
一种软件作品哲学,“少既是多“。
其实不止对于写程序,对职业、爱好、甚至人生,这都是一个不错的哲学,“大而全”好处没那么多,不如试试 “小而美”, 聚焦那些重要的小事儿上。

总结

今天内容有点紧凑啊,我并不打算做奶妈,每一步都点拨到位。学习中遇到困难,借助强大的搜索引擎,通过自己的努力解决它,才是真正的学习之路吧。

今天成果满满:

  • 在本地搭建了开发环境
  • 使用现代化的开发工具(IDE),推荐 pycharm 或者 visual studio code。
  • 在天气预报小工具中使用中文,
  • 和下拉选择框,
  • 另外取消了 Submit 按钮,整个界面十分清爽!

明天继续咯!

本文链接:http://www.thinkinpython.com/post/PySimpleDemo_5.html

-- EOF --