1.概述
本文主要讲述利用Python来编写一个可以计算跳过带有特定数字的车位数量小程序。并且又利用第三方库wxPython做出GUI界面,打包成可执行文件。
现实中有这样的实际案例,某楼盘的地下车库的数量有2000多个车位,车位分成了5个分区,每个分区有400-500个车位数量不等。并且车位的编号已经在图上一一编好了,比如起始号是001,终止号是720。但是营销人员在编写车位号码时避开了带有数字4和数字18的车位号,例如4、14、18、24、40、118、114等等,这样我们就不能直接用终止号减起始号再加一的算法来算出来车位的数量了。
因此为了方便统计这种跳号的车位总数,我写了这么一段简单的代码。本代码利用的是python非常基础的知识,例如字符串的操作、列表的操作、for循环、input函数、自定义函数等等,由浅入深逐步完善整个代码。非常适合Python小白刚学完列表和字符串相关操作后练手使用。wxPython部分的应用也是非常基础的用法,主要是用户输入文本框、静态文本以及按钮控件的基础知识。
为了方便给不会使用Python,电脑上也没有Python开发环境的人使用这个小工具,就需要我们做出GUI界面,并且打包成可执行文件。
2.代码实现
(1)Python环境代码实现
此部分内容详见本人另一篇文章:Python编写的计算跳过带有特定数字的车位数量小程序,并利用wxPython做成GUI界面打包成可执行文件(Part1)
(2)wxPython创建GUI界面
①wxPython简介
wxPython是Python语言的一套优秀的GUI图形库。允许Python程序员很方便的创建完整的、功能键全的GUI用户界面。(# 来源于百度百科)
②安装wxPython
wxPython是Python的第三方库,需要用pip安装,如下代码:
pip install wxpython
③创建一个简单的窗口
如下代码是创建一个窗口的基本代码,具体可详见代码中注释内容:
-
import wx # 导入wxpython模块
-
-
-
class MyFrame(): # 定义一个的子类,以便我们更容量控制窗口的内容和外观
-
def __init__(self): # 定义初始化方法
-
# 设置框架的标题和尺寸
-
.__init__(self, None, -1, title='计算车位数小程序', size=(400, 300))
-
-
-
# 以主程序运行
-
if __name__ == '__main__':
-
app = () # 创建一个应用程序实例
-
frame = MyFrame() # 创建窗口的实例
-
() # 使窗口可见
-
() # 调用应用程序实例的MainLoop()方法,进入主事件循环
上述代码运行后会创建一个窗口,如下图所示:
④在窗口上增加控件
接下来我们在这个窗口上添加控件,添加控件需要先添加画布,把所有的控件都布局到画布中,添加画布的代码如下,其中-1为ID值,可以自定义,也可以用-1由wxPython自动生成一个新的ID。
panel = (self, -1)
(panel, -1, '请输入起始车位号', pos=(30, 30))
self.start_no_text = (panel, -1, '001', pos=(150, 30), size=(100, -1))
代码运行后如下图所示:
-
# 添加静态文本,设定父容器为panel,ID值为-1,显示文本内容及位置
-
(panel, -1, '请输入起始车位号', pos=(30, 30))
-
(panel, -1, '请输入终止车位号', pos=(30, 60))
-
(panel, -1, '输入号码中不包含的数字,用逗号隔开', pos=(30, 90))
-
(panel, -1, '输入号码中要跳过的数字,用逗号隔开', pos=(30, 150))
-
(panel, -1, '车位总数为', pos=(30, 240))
-
-
# 添加输入文本框,设定父容器为panel,ID值为-1,默认文本内容,位置及尺寸
-
self.start_no_text = (panel, -1, '001', pos=(150, 25), size=(100, -1))
-
self.end_no_text = (panel, -1, pos=(150, 55), size=(100, -1))
-
self.not_in_no_text = (panel, -1, pos=(30, 115), size=(220, -1))
-
self.pass_no_text = (panel, -1, pos=(30, 175), size=(220, -1))
-
self.total_no_text = (panel, -1, pos=(150, 235), size=(100, -1))
运行后效果如下:
下面我再添加按钮控件,构造函数可以创建按钮,它的构造函数如下:
= (panel, -1, '点击我进行计算', pos=(30, 205), size=(220, -1))
添加后运行效果如下:
从效果上看按钮与文本框看上去太接近了,为了凸显按钮,我们创建一个字体,字体是类的实例,创建一个字体实例,要使用如下的构造函数:
如下是创建字体的代码:
font = (10, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD)
设置字体可以使用SetFont()方法来设置,如下代码:
(font)
运行效果如下:
之后我们再添加一个多行文本控件,用来显示所有的车位数,多行文本控件在创建是指定样式为wx.TE_MULTILINE即可,如下代码:
-
# 创建文本框,指定样式wx.TE_MULTILINE为多行文本,wx.TE_READONLY为只读
-
self.every_no_text = (panel, -1, pos=(30, 295), size=(220, 220),
-
style=wx.TE_MULTILINE | wx.TE_READONLY)
运行结果如下:
以上便是GUI完整的界面,整体框架我们算是创建完成,接下是我们做相应用户事件的相关代码,不然我们这个窗口文件没办法使用。以下是我们前面GUI界面的完整代码,可以供研究学习:
-
import wx # 导入wxpython模块
-
-
-
class MyFrame(): # 定义一个的子类,以便我们更容量控制窗口的内容和外观
-
def __init__(self): # 定义初始化方法
-
# 设置框架的标题和尺寸
-
.__init__(self, None, -1, title='计算车位数小程序', size=(300, 600),
-
style= | wx.CLOSE_BOX)
-
# 添加画布,画布是所有控件的容器,我们添加的文本框、按钮等控件都在画布上进行布局
-
panel = (self, -1)
-
-
# 添加静态文本,设定父容器为panel,ID值为-1,显示文本内容及位置
-
(panel, -1, '请输入起始车位号', pos=(30, 30))
-
(panel, -1, '请输入终止车位号', pos=(30, 60))
-
(panel, -1, '输入号码中不包含的数字,用逗号隔开', pos=(30, 90))
-
(panel, -1, '输入号码中要跳过的数字,用逗号隔开', pos=(30, 150))
-
(panel, -1, '车位总数为', pos=(30, 240))
-
(panel, -1, '每个车位具体车位号如下', pos=(30, 270))
-
-
# 添加输入文本框,设定父容器为panel,ID值为-1,默认文本内容,位置及尺寸
-
self.start_no_text = (panel, -1, '001', pos=(150, 25), size=(100, -1))
-
self.end_no_text = (panel, -1, pos=(150, 55), size=(100, -1))
-
self.not_in_no_text = (panel, -1, pos=(30, 115), size=(220, -1))
-
self.pass_no_text = (panel, -1, pos=(30, 175), size=(220, -1))
-
self.total_no_text = (panel, -1, pos=(150, 235), size=(100, -1))
-
# 创建文本框,指定样式wx.TE_MULTILINE为多行文本,wx.TE_READONLY为只读
-
self.every_no_text = (panel, -1, pos=(30, 295), size=(220, 220),
-
style=wx.TE_MULTILINE | wx.TE_READONLY)
-
-
# 创建字体
-
font = (10, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD)
-
-
# 添加按钮控件到panel中
-
= (panel, -1, '点击我进行计算', pos=(30, 205), size=(220, -1))
-
# 设置字体
-
(font)
⑤编写逻辑代码
由于在一篇章中Python编写的计算跳过带有特定数字的车位数量小程序,并利用wxPython做成GUI界面打包成可执行文件(Part1)已经在python环境下可以完美实现我们想要的功能,所以大部分代码是可以直接使用的。在wxPython中控件如果获取控件中的文字内容可以用GetValue()方法,如果要设置文字内容需要用SetValue()方法。这样就对应我们上一篇章中的input函数和print()函数。
首先我们要先给按钮绑定单击事件方法,如下代码:
(wx.EVT_BUTTON, , )
我们还需要定义单击事件方法,这个方法内的我们主要的代码,大多是直接粘贴自上一篇章中的代码,只是在个别的地方进行了适应性调整,如下代码:
-
def OnClick(self, event):
-
-
# 获取用户输入的数据
-
start_no = self.start_no_text.GetValue()
-
end_no = self.end_no_text.GetValue()
-
pass_no = self.pass_no_text.GetValue()
-
not_in_no = self.not_in_no_text.GetValue()
-
# 创建一个空列表,来存储车位号
-
parking_no_lst = []
-
# 创建一个空列表,用来存储not_in_no
-
not_in_no_lst = []
-
# 将用户输入的数据以逗号为分割,分别放在列表中
-
if not_in_no != "": # 增加这个if语句可以判断只有not_in_no中有数据才会执行以下代码
-
for item in not_in_no.replace(",", ",").split(","):
-
not_in_no_lst.append(item)
-
-
# 创建一个空列表,用来存储pass_no
-
pass_no_lst = []
-
# 将用户输入的数据以逗号为分割,分别放在列表中
-
if pass_no != "": # 增加这个if语句可以判断只有pass_no中有数据才会执行以下代码
-
for item in pass_no.replace(",", ",").split(','):
-
pass_no_lst.append(item)
-
# 获取用户输入的车位长度
-
l = len(start_no)
-
-
# 用for循序来遍历起始号到终止号之间所有的数字
-
# 由于input函数获取的数据格式为字符串格式,因此需要用int()函数转换成整数类型
-
for i in range(int(start_no), int(end_no) + 1):
-
# 判断
-
if self.is_lst_in_str(not_in_no_lst, str(i)) or self.is_no_in_lst(str(i), pass_no_lst):
-
continue
-
else:
-
parking_no_lst.append("0" * (l - len(str(i))) + str(i))
-
# 用len函数计算列表长度,得出车位总数,用SetValue方法设置total_no_text控件的显示文本
-
self.total_no_text.SetValue(str(len(parking_no_lst)))
-
# 输出所有的车位号
-
self.every_no_text.Clear() # 先清空内容
-
for i in range(len(parking_no_lst) - 1):
-
self.every_no_text.AppendText(parking_no_lst[i] + ',')
-
self.every_no_text.AppendText(parking_no_lst[-1])
-
-
def is_no_in_lst(self, no, lst):
-
if no in lst:
-
return True
-
else:
-
return False
-
-
# 定义一个函数,用来判断列表中的元素是否在字符串中
-
def is_lst_in_str(self, lst, input_str):
-
for item in lst:
-
if item in input_str:
-
return True
-
return False
代码运行如下,如果不输入跳号内容也不会报错,也能正确运行:
最后还有对输入的内容进行验证,要符合我们要求的格式,需要使用wxPython中的验证器(Validator),验证器(Validator)属于高阶用法,本文不展开叙述,请自行研究。以下是完整代码。
-
import wx # 导入wxpython模块
-
-
-
class MyFrame(): # 定义一个的子类,以便我们更容量控制窗口的内容和外观
-
def __init__(self): # 定义初始化方法
-
# 设置框架的标题和尺寸
-
.__init__(self, None, -1, title='计算车位数小程序', size=(300, 600),
-
style= | wx.CLOSE_BOX)
-
# 添加画布,画布是所有控件的容器,我们添加的文本框、按钮等控件都在画布上进行布局
-
panel = (self, -1)
-
-
# 创建字体
-
font = (10, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD)
-
-
# 添加静态文本,设定父容器为panel,ID值为-1,显示文本内容及位置
-
(panel, -1, '请输入起始车位号', pos=(30, 30))
-
(panel, -1, '请输入终止车位号', pos=(30, 60))
-
(panel, -1, '输入号码中不包含的数字,用逗号隔开', pos=(30, 90))
-
(panel, -1, '输入号码中要跳过的数字,用逗号隔开', pos=(30, 150))
-
(panel, -1, '车位总数为', pos=(30, 240))
-
(panel, -1, '每个车位具体车位号如下', pos=(30, 270))
-
-
# 添加输入文本框,设定父容器为panel,ID值为-1,默认文本内容,位置及尺寸
-
self.start_no_text = (panel, -1, '001', pos=(150, 25), size=(100, -1))
-
self.end_no_text = (panel, -1, pos=(150, 55), size=(100, -1))
-
self.not_in_no_text = (panel, -1, pos=(30, 115), size=(220, -1))
-
self.pass_no_text = (panel, -1, pos=(30, 175), size=(220, -1))
-
self.total_no_text = (panel, -1, pos=(150, 235), size=(100, -1))
-
self.total_no_text.SetFont(font)
-
# 创建文本框,指定样式wx.TE_MULTILINE为多行文本,wx.TE_READONLY为只读
-
self.every_no_text = (panel, -1, pos=(30, 295), size=(240, 220),
-
style=wx.TE_MULTILINE | wx.TE_READONLY)
-
-
# 添加按钮控件到panel中
-
= (panel, -1, '点击我进行计算', pos=(30, 205), size=(220, -1))
-
# 设置字体
-
(font)
-
# 为按钮绑定事件
-
(wx.EVT_BUTTON, , )
-
-
# 定义事件方法
-
def OnClick(self, event):
-
-
# 获取用户输入的数据
-
start_no = self.start_no_text.GetValue()
-
end_no = self.end_no_text.GetValue()
-
pass_no = self.pass_no_text.GetValue()
-
not_in_no = self.not_in_no_text.GetValue()
-
# 创建一个空列表,来存储车位号
-
parking_no_lst = []
-
# 创建一个空列表,用来存储not_in_no
-
not_in_no_lst = []
-
# 将用户输入的数据以逗号为分割,分别放在列表中
-
if not_in_no != "": # 增加这个if语句可以判断只有not_in_no中有数据才会执行以下代码
-
for item in not_in_no.replace(",", ",").split(","):
-
not_in_no_lst.append(item)
-
-
# 创建一个空列表,用来存储pass_no
-
pass_no_lst = []
-
# 将用户输入的数据以逗号为分割,分别放在列表中
-
if pass_no != "": # 增加这个if语句可以判断只有pass_no中有数据才会执行以下代码
-
for item in pass_no.replace(",", ",").split(','):
-
pass_no_lst.append(item)
-
# 获取用户输入的车位长度
-
l = len(start_no)
-
-
# 用for循序来遍历起始号到终止号之间所有的数字
-
# 由于input函数获取的数据格式为字符串格式,因此需要用int()函数转换成整数类型
-
for i in range(int(start_no), int(end_no) + 1):
-
# 判断
-
if self.is_lst_in_str(not_in_no_lst, str(i)) or self.is_no_in_lst(str(i), pass_no_lst):
-
continue
-
else:
-
parking_no_lst.append("0" * (l - len(str(i))) + str(i))
-
# 用len函数计算列表长度,得出车位总数,用SetValue方法设置total_no_text控件的显示文本
-
self.total_no_text.SetValue(str(len(parking_no_lst)))
-
# 输出所有的车位号
-
self.every_no_text.Clear() # 先清空内容
-
for i in range(len(parking_no_lst) - 1):
-
self.every_no_text.AppendText(parking_no_lst[i] + ',')
-
self.every_no_text.AppendText(parking_no_lst[-1])
-
-
def is_no_in_lst(self, no, lst):
-
if no in lst:
-
return True
-
else:
-
return False
-
-
# 定义一个函数,用来判断列表中的元素是否在字符串中
-
def is_lst_in_str(self, lst, input_str):
-
for item in lst:
-
if item in input_str:
-
return True
-
return False
-
-
-
if __name__ == '__main__': # 以主程序运行
-
app = () # 创建一个应用程序实例
-
frame = MyFrame() # 创建窗口的实例
-
() # 使窗口可见
-
() # 调用应用程序实例的MainLoop()方法,进入主事件循环
⑥pyinstaller 来打包生成exe文件
最后我们可以用pyinstaller 来打包生成exe文件,在pycharm的控制终端用如下代码就可以生成了:
Pyinstaller -F -w 计算车位数小程序.py
注意Pyinstaller是第三方库,需要提前pip安装。
最后我们会得到一个exe文件,如下图,文件我也上传了,如有需要请自行下载。
3.总结
这次更新的博文虽然分成了两篇文章,但是每个都在一万字以上,这个小工具实现的功能很简单,但是可以让人们跟随本文章从无到有一步一步的来完成整个代码的编写。希望能给读者带来一些帮助。