大家好,我是溪夜。
当作为声音设计师或技术音频的你完成了 Python 脚本时,除了感叹这门语言的便利性和丰富的第三方库,可能也会思考,为什么现有的 Python GUI 库都这么麻烦?毕竟要为团队中非程序背景的同事开发工具,看得见的界面无疑比命令行的用户体验更加友好。
这一系列分享的目的,就是为了打通脚本和 GUI 程序之间的障碍,把开发图形界面这个浪费时间的过程彻底简单化,让技术音频工作中的小工具开发变的门槛更低一些。其实使用 Tkinter、PyQt、wxPython 等来开发 GUI 也没什么难度,参考实例几分钟就能上手,一下午就能熟悉。但它们使用起来语法繁琐,不太直观,非常浪费时间。
作为生产力解放狂魔,我认为应该把宝贵的精力应该放在更重要的事上,而不是费劲吧啦的在控件和语法间挣扎。这也是我个人的学习理念,其一为多向自己提问并解答之,其二是遇到重复、繁琐、有潜力被自动化的东西就喜欢思考去优化。
希望此文能给大家提供一套高效的工作流,人人都能轻松定制自己想要的工具。
本系列文章阅读需要的前置知识:
- 会配置开发环境,掌握 Python、Anaconda、VS Code、PyCharm 的安装配置。
- 掌握简单的 Python 语法和 WAAPI 知识。作为一门写工具时要求技术水平不是很高的脚本语言,没接触过的同学可以敲几天《Python编程:从入门到实践》,或看看廖雪峰老师的课程。对于 WAAPI,照着例子调用几个 API 即可轻松上手。
- 掌握 GUI 基础概念。了解窗体、面板、构件等即可,有志向深入研究的朋友可翻翻 UI、UX 相关书籍。
第二版更新内容:
- 改进文字表述方面的细节。
- 增加了与更多框架的对比。
- 详细描述了 PySimpleGUI 的设计逻辑。
文章目录:
- 基本概念与示例
- 配置开发环境与 PySimpleGUI
- 一个完整的程序例子
- 接下来讲什么?
基本概念与示例
对于 GUI 而言,只需了解它是一个具有图形界面的应用程序外壳即可。至于事件循环、布局构建、回调函数等概念,大可先把它们当成黑箱。只管使用,不必探究深层原理。
1.1. UI 和程序之间需要“钩上”才能产生互动
图形界面之所以能够让程序发挥功能,是因为其组件被用户触发后,与程序的功能代码产生了互动。
例如可以用一个按钮点击代表一个函数的执行,用一个弹出式窗口显示 Print() 函数的打印信息,每一个行为与背后的代码关联在一起。我们实际要做的就是设计好壳子,再用钩子把它们连接起来。
1.2. 举个最简单的例子(另类的 Hello World)
现在讨论一个最简单的程序。用户输入内容,点击按钮后,程序会把用户输入值通过窗口打印出来。
我们为它设计一个 GUI 原型:
上图上部分是程序打开时的界面,当用户输入完内容点击 Print 后,下部分会出现弹出式窗口告知用户刚输入的值。
这个例子说明了什么?
看三个重要的功能组件:
- Input 文本框:用于暂存用户输入的数据
- Print 按钮:用于把 Input 文本框中的数据传进程序内(把用户输入的值存到变量中)
- Input is XXXXXXXXX:通过弹出式窗口输出用户输入值
通过这个例子类比到工具的真实需求上
图形界面下 Input() 和 Print() 等函数组成的程序就会是我们的功能脚本,而图形界面上的控件就是本文的讨论方向,我们要用一种高效优雅的方法将两者连接。
1.3. 代码实现:
下面我写两段简单的代码,分别把这个程序用 Tkinter 和 PySimpleGUI 实现,并附上效果截图。
使用 Tkinter
1 | from tkinter import * |
使用 PySimpleGUI
1 | import PySimpleGUI as sg |
大家认为谁更简单一些?
相信诸君无论熟悉 Python 与否,都能看出 PySimpleGUI 的代码具有高度可读性,更符合自然语义。不需要过多的设置即可做到优雅的主题、窗口排布。
这段代码给一个从未接触过 PySimpleGUI 的人看,一般也可轻松看出上半部分是布局实现,下半部分的循环是 Event Loop。
回过头来再看 Tkinter 的实现,对初学者来说很容易不知所云。
关于 QtDesigner,其实它的操作和语法也没有 PySimpleGUI 简单直观。不过下一篇中我会提到通过 QtDesigner 快速创建 Layout 代码,也算一种另类的物尽其用。
配置开发环境与 PySimpleGUI
这部分我不会说废话重复造轮子,那样有凑字数之嫌,直接放出比较好的文章供大家参考(感谢下文中作者们的贡献)。希望读者诸君自我实现一遍,加深印象。
因为个人习惯用 VS Code + Anaconda 配置开发环境,使用 pip、PyCharm 等的朋友可自行配置所需组件。关于 Anaconda、VS Code 和 PyCharm 的使用疑问请自行参考下述文档解决,其中的内容比较具体。关于各种开发环境的优劣性对比,请自行用搜索引擎解答疑问。
2.1. Windows 下配置
安装 Anaconda
https://segmentfault.com/a/1190000022797661
配置 Anaconda 并在本地环境中添加 PySimpleGUI
https://zhuanlan.zhihu.com/p/25198543
配置 VS Code 开发环境
https://zhuanlan.zhihu.com/p/30324113
2.2. macOS 下配置
安装 Anaconda
https://blog.csdn.net/lq_547762983/article/details/81003528
配置 Anaconda 并在本地环境中添加 PySimpleGUI
配置 VS Code开发环境
此两步可参考上面的链接,操作类似。
绘制 GUI 原型
对于绘制原型,有些人立刻会想到 Sketch 或 Axure 这样的重型武器,但它们学起来时间成本过高。还有一些 Web 端收费的原型制作工具,但那些在做 APP 原型设计才比较常用,做桌面端小工具开发也没必要。
在寻求解决方案的时候我发现了一个很棒的工具,对于简单的绘制 UI 原型的需求,推荐大家使用 Pencil,这是一款免费、开源、轻量、跨平台(Windows、macOS、Linux、火狐拓展)的原型设计工具。
附上样图一张
下载地址:https://pencil.evolus.vn/
在“举个最简单的例子(另类的 Hello World)”处的黑白稿 UI 原型图,即使用 Pencil 所绘。相对于糟糕的手绘稿或请公司的 UI 设计师帮忙,不如花点时间学会 Pencil 后自己画。
如何学习 Pencil 呢?在官网文档中有比较简单的入门介绍。或者我建议大家可直接拖拽控件试一试,很容易上手。
一个完整的例子
文档“翻译官“是种非常无聊的行为。下面我就直接通过一个具有实际应用意义的小工具模型,来讲述脚本代码通过 PySimpleGUI 如何优雅且快速的变成图形界面小工具。
4.1. 程序目的
把一个文件夹内的 wav 文件根据声道数,对文件自动进行重命名加到后缀上。非常简单的需求,下面我先写出功能代码。
4.2. 编写功能代码
1 | import os |
4.3. PySimpleGUI 基础
那么接下来就要使用 PySimpleGUI 添加图形界面了,在这之前需要知道一些基础的概念。
PySimpleGUI 基于什么构建?
虽然 PySimpleGUI 默认基于 Tkinter,但实际上还具有 WxPython、QT 和 Remi 等版本。具体的区别可参考官方的“散文“文档。其中对于 Remi 的支持是十分方便的。这代表着当代码进行以下简单的引用库修改后,程序可直接生成 Web 端在浏览器中运行。对于某些轻需求下的开发,省去了大量的时间和精力。1
2
3import PySimpleGUI # tkinter version
import PySimpleGUIWeb # Web(Remi) version
# and PySimpleGUI27, PySimpleGUIWx, PySimpleGUIQt
如何实现一个基础的 GUI 结构
这里我直接引用官方的示例代码并加上注释,一个简单的窗口布局结构代码其实很清晰:
- sg.theme 行代表使用哪个主题,这也是 PySimpleGUI 一开始就很注重的一点。程序的美观性很重要,而且要轻松的被实现。
- layout 使用列表来定义布局,其中的每个子列表代表当前行的元素(即 Widgets,PySimpleGUI 中称控件为 Elements,本系列教程也会统一使用此术语),每个控件的属性中亦可设置各种所需的参数。
- window 行创建了窗体,默认情况下自动适应控件所需的尺寸。
- Event Loop 用于支撑软件功能运行到窗体关闭或点击 Cancel 退出。
1 | import PySimpleGUI as sg |
PySimpleGUI 能做出什么样的程序?
在这里我放几张官方的图片供大家观看,相信诸位能看出它内置丰富控件元素及优雅设计。
所有内置的主题
引入机器学习模块判断车辆的存在
用到了13种元素的示例程序(实际上还有更多)
多窗口示例程序
4.4. 构建 GUI 代码
回到我们的程序设计上。对于这个简单的程序,我们需要程序中包含路径输入框、处理按钮、处理提示文本框等。为此,我简单绘制一个原型。
有了原型,下面我写一段简单的 GUI 实现并附上注释:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21# 设计布局为四行元素,分别用到文字、输入框、文件夹浏览器、输出、按钮等元素。
layout = [ [sg.Text('Browse Folders')],
[sg.Input(), sg.FolderBrowse('Browse', key='folder')],
[sg.Output(size=(70, 20), font=("宋体", 10))],
[sg.Button('Cock it'), sg.Button('Cancel')] ]
# 构建窗体 GUI Enemy No. 1
window = sg.Window('GUI Enemy No. 1', layout)
# 创建 Event Loop
while True:
event, values = window.read()
if event in (None, 'Cancel'): # 如果用户点击“Cancel”按钮或者关闭窗口,退出循环并关闭窗口
break
if event == 'Cock it': # 如果用户点击“Cock it”按钮,就执行判断体判断是否为文件夹输入,是的话输出文字并调用 wav_rename() 函数进行操作
if values['folder']:
print('{0}Renaming{0}'.format('*'*10))
wav_rename(values['folder'])
print('{0}Done{0}'.format('*'*10))
else:
print('Choose some folder first!')
window.close()
4.5. 组装代码
写完功能代码和 GUI 代码,肯定需要把它们组装到一起。根据项目的复杂程度,你也可以决定是否按模块整理代码。
注:为了避免浪费篇幅,其中函数体直接省略。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18import PySimpleGUI as sg
import os
import wave
def nchannels_rename(input_path):
Function body
def wav_rename(input_path):
Function body
def gui():
Function body
def main():
gui()
if __name__ == '__main__':
main()
4.6. 执行效果
我稍微修改了一下代码,让被修改后的文件名输出到文本框中。从输出结果可见,程序如预先构思执行,我们仅用不到20行代码就优雅的完成了这个工具。
接下来讲什么?
聪明的读者一定很好奇这些部分:
- 如何添加菜单栏?
- 窗体内元素能否实现双栏或不对称布局?
- 之前你提到过用 QtDesigner 能快速生成布局?不用自己写布局代码了吗?
- 有没有对声音设计师来说更实用或更复杂的程序案例详解(例如跟 WAAPI 或 ReaScript 联动)?
在之后的文章中我会就这些部分继续展开介绍,欢迎诸位持续关注。