一个小工具的开发日记

前言

7月4日
新开这本日记,也为了督促自己下个学期多下些苦功。先要读完手边的莎士比亚的《亨利八世》。
7月13日
打牌。
7月14日
打牌。
7月15日
打牌。
7月16日
胡适之啊胡适之!你怎么能如此堕落!先前订下的学习计划你都忘了吗?子曰:“吾日三省吾身。”不能再这样下去了!
7月17日
打牌。
7月18日
打牌。

以上摘自胡适先生的留学日记,明明是别人的故事,主人公却好像是自己。

于是抽空就有了此文,虽然还是咸鱼,但至少死鱼眼里泛出些许光彩了。

正文

闲话不提,直接进入正文。故事的起因就是最近常跑人资办公室的我被人事姐姐们拜托帮忙做一个小工具,希望能够在培训会上用来随机抽问题然后找人回答,然后希望这个小工具能够尽快做好给她们。

我寻思这不挺简单么,也就顺口答应了下来。事后证明……

真的很简单,不然本咸鱼为什么用这个故事来水文呢!?

事情虽然简单,该走的流程还是要走的,我们按照软件开发流程一步步来。

需求分析

一番沟通,确认了人事姐姐们的主要需求如下:

  • 随机抽号:可以设置抽取数字范围抽取数字个数,点开始按钮数字开始滚动,点结束按钮出现抽号结果
  • 背景与标题设置:可以设置抽号程序的背景界面大标题,让抽号程序显示内容和会议更加贴合

设计

考虑开发速度和复杂度,这个小工具简单地用python开发就完事了,毕竟人生苦短,我用python

技术选型

  • 开发语言:python
  • 程序架构:cs架构(一个自带GUI的小工具而已,没必要搞bs架构)
  • 主要python工具包:tkinter(标准库),Pillow(图像处理库)

界面设计

灵魂画师上线!

设计稿1

设计稿2

开发

又到了我们最爱的coding环节,请各位打开浏览器,面向搜索引擎(最好是Google)开始编写代码。

具体的我们就不讲了,源码都在这,tkinter不会用请看官方文档,就主要说一下一些有趣的细节。

如何设置一个弹出窗口用于接收参数设置:

其实就是编写一个主窗口类(继承tk.TK类),一个弹出窗口类(继承tk.Toplevel类)。
大致的流程是这样的:

  1. 创建主窗口实例;
  2. 触发绑定在主窗口实例上的触发式组件;
  3. 创建弹出窗口实例;
  4. 通过弹出窗口会话收集数据;
  5. 主窗口实例等待弹出窗口会话销毁;
  6. 主窗口实例获取弹出窗口实例收集到的数据。

sample:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
import tkinter as tk


# 主窗
class MainWindow(tk.Tk):
def __init__(self):
super().__init__()
self.title('主窗口')
# 程序参数/数据
self.name = tk.StringVar()
self.name.set('张三')
self.age = tk.IntVar()
self.age.set(30)
# 设置主窗口界面
self.setupUI()

def setupUI(self):
row1 = tk.Frame(self)
row1.pack(fill="x")
tk.Label(row1, text='姓名:', width=8).pack(side=tk.LEFT)
self.l1 = tk.Label(row1, textvariable=self.name, width=20)
self.l1.pack(side=tk.LEFT)
row2 = tk.Frame(self)
row2.pack(fill="x")
tk.Label(row2, text='年龄:', width=8).pack(side=tk.LEFT)
self.l2 = tk.Label(row2, textvariable=self.age, width=20)
self.l2.pack(side=tk.LEFT)
row3 = tk.Frame(self)
row3.pack(fill="x")
tk.Button(row3, text="设置", command=self.setup_config).pack(side=tk.RIGHT) # 通过Button触发

# 设置参数
def setup_config(self):
# 接收弹窗的数据
res = self.ask_userinfo()
if res is None:
return
# 更改参数,更新界面
self.name.set(res[0])
self.age.set(res[1])

# 弹窗
def ask_userinfo(self):
popup_window = PopupWindow() # 创建弹窗实例
self.wait_window(popup_window) # 等待弹窗会话关闭
return popup_window.userinfo # 获取绑定在弹窗实例私有变量上的数据


# 弹窗
class PopupWindow(tk.Toplevel):
def __init__(self):
super().__init__()
self.title('弹出窗口')
# 设置弹窗界面
self.setup_UI()

def setup_UI(self):
row1 = tk.Frame(self)
row1.pack(fill="x")
tk.Label(row1, text='姓名:', width=8).pack(side=tk.LEFT)
self.name = tk.StringVar()
tk.Entry(row1, textvariable=self.name, width=20).pack(side=tk.LEFT)
row2 = tk.Frame(self)
row2.pack(fill="x", ipadx=1, ipady=1)
tk.Label(row2, text='年龄:', width=8).pack(side=tk.LEFT)
self.age = tk.IntVar()
tk.Entry(row2, textvariable=self.age, width=20).pack(side=tk.LEFT)
row3 = tk.Frame(self)
row3.pack(fill="x")
tk.Button(row3, text="确定", command=self.save).pack(side=tk.LEFT)
tk.Button(row3, text="取消", command=self.cancel).pack(side=tk.LEFT)

def save(self):
self.userinfo = [self.name.get(), self.age.get()] # 将用户输入的数据绑定到实例私有变量上
self.destroy() # 销毁窗口

def cancel(self):
self.userinfo = None
self.destroy()


if __name__ == '__main__':
app = MainWindow()
app.mainloop()

如何设置程序背景图片

tkinter在界面美化这方面其实并不是很好用,在设置程序图片这一块遇上了不少坑,权且记录一下:

  1. 无法直接对tk.TK或者tk.Frame这类容器设置背景图片,最多设置背景色(至少我没找到什么好办法),解决方法就是搞一个tk.Label平铺在整个tk.TK上,然后通过tk.Label的image属性设置背景图片;
  2. 在按照上述方法设置背景图片的过程中发现图片并未平铺到整个tk.Label,需要手动设置图片大小;且需要预先使用PIL对图片进行处理,否则某些图片格式tkinter不支持;

sample:

1
2
3
4
5
6
7
8
9
10
11
import tkinter as tk
from PIL import ImageTk,Image

root=tk.Tk(bg='blue') # 设置一个背景色方便看出问题
width,height=root.maxsize() # 获取屏幕大小
root.geometry('{}x{}'.format(width,height)) # 最大化程序窗口
image=Image.open('1.jpg')
image=image.resize((width,height),Image.ANTIALIAS) # 不手动调整图片尺寸的话会发现图片不会平铺到整个tk.Label上
bg_image=ImageTk.PhotoImage(image) # 利用PIL形成转换管道的方式才能正确读取jpg图片
l=tk.Label(root,bg='white',image=bg_image)
l.pack(expand=True,fill=tk.BOTH) # 确认label已经平铺到整个程序窗口

测试与发布

  • 测试:xjbd一遍,OK了;
  • 发布:利用pyinstaller将程序打包成exe即可。

总结

我对自己说:“张杰啊张杰!你怎么能如此堕落!先前立的flag你都忘了吗?子曰:‘吾日三省吾身。’不能再这样下去了!”