用PySimpleGui做户籍资料查询工具
前言
最近手里有一张几千人的户籍信息表格,突发奇想,想用python做一个小工具,可以按照人名搜索,获取该人所在户的所有人信息,然后显示图片,个人详情页,方便查询
以前用tkinter做过一个从xls表格获取行信息填入doc,并批量打印的小工具,tkinter是比较好用,可是想做好看真心不容易,搜索之下发现了PySimpleGui,这个更加简单
学习python属于个人兴趣,没有接受系统学习,风格也是野路子,写下这篇心得纯粹是记录学习内容,毕竟功能实现都是自己一点点想的,没有参考别人的案例。同为初学者的可以看看,抛砖引玉。大神也请赐教。
逻辑部分
数据源
我手里有一份整村的村民户籍表,不是很完整,错误也比较多,没有身份证号,所以没有泄密嫌疑。表格是xls的,为了更好操作,先手动转成csv格式,当数据库用了。表格内容较多,我们主要用到的是姓名、性别、户主关系、电话号码,几项
需求分析
我想要实现的一个主要功能是,输入一个人名,在数据表中搜索到所有这个名字的人(当然有重名的),然后根据他的户主关系,找到他整户的人,并打印出来。这样有几个技术问题需要解决。。。(我本人纯属爱好,没有接触过算法,都是用最笨的方法解决)
思路及方法
获取csv所有行的内容
这是csv基础操作了,上代码
def open_csv():
with open('sunzhuang.csv', 'r') as csvfile:
reader = csv.DictReader(csvfile)
return reader
根据每行里“户主关系”内容,找到一共有多少户主,并将所有户主的序号记录
这个就是突发奇想了,我找到所有的户主,并把它们的序号单独存一个list,这样确定了被搜索的人的序号,就可以通过序号,确定他在哪两位“户主”之间,从而找到所有的家庭成员。ok,上代码
# 获取所有属性是户主的村民序号 存到huzhu_list列表并返回
def get_huzhu_list():
with open('sunzhuang.csv', 'r') as csvfile:
reader = csv.DictReader(csvfile)
for row in reader:
if row['与户主关系'] == '户主':
# print(row)
huzhu_list.append(row['序号'])
return huzhu_list
找到被搜索人在csv中的序号
# 根据姓名 获取她的序号
# 考虑到会有重名的情况,所以用一个列表name_id保存了所有了该名字的序号
def get_name_id(name):
name_id = []
with open('sunzhuang.csv', 'r') as csvfile:
reader = csv.DictReader(csvfile)
for row in reader:
if row['姓名'] == name:
name_id.append(row['序号'])
# print('序号是:' + name_id)
return name_id
接下来,就是找到被搜索人全家了
# 这是核心了,通过用户序号 找到该户的户主和下一户的户主序号,从而计算出该户所有人的序号
def get_hole_family_id(huzhu_list, name_id):
first = 0
last = 0
for i in huzhu_list:
# 首先,huzhu_list里边是所有户主的id
# 如果搜索的人就是户主,那么他的id就是该户的第一个人,
# 先用huzhu_list.index(i)找到他在户主列表里的位置,+1 找到下一个户主的id
# 然后在通过这个位置,找到下一个户主的id 说起来有点绕
# 这里只是定位到下一个户主的位置,而不用-1 因为下边用到的
# range(first,last)函数构建列表时正好是 first到last-1
if int(i) == int(name_id):
first = int(i)
last = int(huzhu_list[huzhu_list.index(i) + 1])
break
# 如果要搜索的人不是户主,那么先找他的下一个户主的位置,再通过-1找到本户主的位置
elif int(i) > int(name_id):
last = int(huzhu_list[huzhu_list.index(i)])
first = int(huzhu_list[huzhu_list.index(i) - 1])
break
family_id_list = [i for i in range(first, last)]
return family_id_list
这样,我们得到了被搜索人全家的人员序号
再用两个方法,根据序号,获取csv文件里的整行信息
# 根据序号 定位到csv文件中的行
def get_content(name_id):
with open('sunzhuang.csv', 'r') as csvfile:
reader = csv.DictReader(csvfile)
for row in reader:
if row['序号'] == name_id:
print(row)
# 通过全家人的序号,获取全家人的信息,并全部添加到列表all_familly_list中
def get_hole_family_cont(family_id_list):
all_familly_list = []
with open('sunzhuang.csv', 'r') as csvfile:
reader = csv.DictReader(csvfile)
for row in reader:
for id in family_id_list:
if row['序号'] == str(id):
# row 是一个字典格式,需要用dict.values()将键值转化为列表
list3 = list(row.values())
# 从列表中找出我们需要的信息,从新创建一个列表,就得到了该人的信息
family_list = [list3[3], list3[4], list3[7], list3[6]]
all_familly_list.append(family_list)
return all_familly_list
至此,通过一个人名,我们得到了他家所有人的信息,并全部装入一个列表里。下面就是做一个页面,把他们显示出来
界面部分
PySimpleGui介绍
网上介绍很多,我摘抄了一点过来,实现很简单,布局采用layout行式布局,很直观
1.import 库
2.layout UI布局
3.window 窗口显示
4.Event loop 事件循环,用户持续交互
5.close 关闭窗口
# 1 - The import
import PySimpleGUI as sg
# 2 - Layout definition
layout = [[sg.Text('My layout')],
[sg.Input(key='-INPUT-')],
[sg.Button('OK'), sg.Button('Cancel')] ]
# 3 - Create window
window = sg.Window('Design Pattern 3 - Persistent Window', layout)
# 4 - Event Loop
while True:
event, values = window.read()
if event in (None, 'Cancel'):
break
# 5 - Close window
window.close()
效果是
基本很容易实现我想要的,其他不再累述,需要的可以去csdn找,很多很多
界面布局
通过修改一个案例,一番折腾,得到了我想要的布局如下
代码如下:
import PySimpleGUI as sg
from PySimpleGUI import InputCombo, Combo, Multiline, ML, MLine, Checkbox, CB, Check, Button, B, Btn, ButtonMenu, \
Canvas, Column, Col, Combo, Frame, Graph, Image, InputText, Input, In, Listbox, LBox, Menu, Multiline, ML, MLine, \
OptionMenu, Output, Pane, ProgressBar, Radio, Slider, Spin, StatusBar, Tab, TabGroup, Table, Text, Txt, T, Tree, \
TreeData, VerticalSeparator, Window, Sizer
sg.theme('GreenTan')
list = [[' ',' ','户主',' ']]
col3 = Column([[Frame('Information:', [[Text(), Column([[Text('户 数: ')],
[Text('人 数: ')],
[Text('低保人数: ')],
[Text('五保人数: ')],],size=(210, 120), pad=(0, 0))]
]
)
]], pad=(0, 0))
col2 = Column([[Frame('Accounts:', [[Column([
[Table(values=list,
headings=[' 姓 名 ',' 性 别 ',' 属 性 ',' 电 话 '],
max_col_width=300,
auto_size_columns=True,#自动调整列宽(根据上面第一次的values默认值为准,update时不会调整)
display_row_numbers=True,#序号
justification='center',#字符排列 left right center
num_rows=12,#行数
row_height=30,#行高
key='_table_',
font=('微软雅黑', 12),
text_color='black',
background_color='white',
enable_events=True,
bind_return_key=True,
tooltip='This is a table')]
, ], size=(450, 400),pad=(0, 0))]])]],pad=(0, 0))
col1 = Column([
# Information frame
[Frame('Search:', [[Text(), Column([[Text('输入姓名:')],
[Input(key='-USERID-IN-', size=(16, 1)),
Button('Search', key='-SEAR-')],
], size=(195, 120), pad=(0, 0))]])], ], pad=(0, 0))
layout = [[col1,col3], [col2]]
window = Window('人员信息查询', layout)
while True:
event, values = window.read()
print(event, values)
if event == sg.WIN_CLOSED:
break
elif event == '-SEAR-':
#print(values['-LIST-'][0])
sg.popup('This is {}'.format(values['_table_'][0]))
window.close()
现在逻辑部分和界面都有了,把他们融合
这一点颇费脑筋,无奈基础太差,暂时不用面向对象了
基本方法是通过按钮事件的触发,调用逻辑代码,实现功能,并通过更新窗口,使结果显示出来
小技巧
不算技巧吧,是PySimpleGui一个比较方便的地方,每个窗口组件都有一个key属性和一个value属性(text没有),key是组件的名字,value是他要显示的内容,通过window[‘key’],可以定位该组件,通过window[‘key’].update(‘value’),可以直接将value更新到组件的value属性
最终界面
完整代码
import csv
import PySimpleGUI as sg
from PySimpleGUI import InputCombo, Combo, Multiline, ML, MLine, Checkbox, CB, Check, Button, B, Btn, ButtonMenu, \
Canvas, Column, Col, Combo, Frame, Graph, Image, InputText, Input, In, Listbox, LBox, Menu, Multiline, ML, MLine, \
OptionMenu, Output, Pane, ProgressBar, Radio, Slider, Spin, StatusBar, Tab, TabGroup, Table, Text, Txt, T, Tree, \
TreeData, VerticalSeparator, Window, Sizer
num = 0
# 搜索所有的户主 并将他们的序号填入列表 用来调取整户信息
huzhu_list = []
# 通过DictReader方法打开csv文件,可以做到搜索功能
def open_csv():
with open('sunzhuang.csv', 'r') as csvfile:
reader = csv.DictReader(csvfile)
return reader
# 获得总户数
def get_familly_num():
familly_num = 0
with open('sunzhuang.csv', 'r') as csvfile:
reader = csv.DictReader(csvfile)
for row in reader:
if row['与户主关系'] == '户主':
familly_num += 1
# print(row)
return familly_num
# 获得总人数
def get_people_num():
people_num = 0
with open('sunzhuang.csv', 'r') as csvfile:
reader = csv.DictReader(csvfile)
for row in reader:
people_num += 1
# print(row)
return people_num
# 获取所有属性是户主的村民序号 存到huzhu_list列表并返回
def get_huzhu_list():
with open('sunzhuang.csv', 'r') as csvfile:
reader = csv.DictReader(csvfile)
for row in reader:
if row['与户主关系'] == '户主':
# print(row)
huzhu_list.append(row['序号'])
return huzhu_list
# 根据姓名 获取她的序号
# 考虑到会有重名的情况,所以用一个列表name_id保存了所有了该名字的序号
def get_name_id(name):
name_id = []
with open('sunzhuang.csv', 'r') as csvfile:
reader = csv.DictReader(csvfile)
for row in reader:
if row['姓名'] == name:
name_id.append(row['序号'])
# print('序号是:' + name_id)
return name_id
# 根据序号 定位到csv文件中的行
def get_content(name_id):
with open('sunzhuang.csv', 'r') as csvfile:
reader = csv.DictReader(csvfile)
for row in reader:
if row['序号'] == name_id:
print(row)
# 这是核心了,通过用户序号 找到该户的户主和下一户的户主序号,从而计算出该户所有人的序号
def get_hole_family_id(huzhu_list, name_id):
first = 0
last = 0
for i in huzhu_list:
# 首先,huzhu_list里边是所有户主的id
# 如果搜索的人就是户主,那么他的id就是该户的第一个人,
# 先用huzhu_list.index(i)找到他在户主列表里的位置,+1 找到下一个户主的id
# 然后在通过这个位置,找到下一个户主的id 说起来有点绕
# 这里只是定位到下一个户主的位置,而不用-1 因为下边用到的
# range(first,last)函数构建列表时正好是 first到last-1
if int(i) == int(name_id):
first = int(i)
last = int(huzhu_list[huzhu_list.index(i) + 1])
break
# 如果要搜索的人不是户主,那么先找他的下一个户主的位置,再通过-1找到本户主的位置
elif int(i) > int(name_id):
last = int(huzhu_list[huzhu_list.index(i)])
first = int(huzhu_list[huzhu_list.index(i) - 1])
break
family_id_list = [i for i in range(first, last)]
return family_id_list
# 通过全家人的序号,获取全家人的信息,并全部添加到列表all_familly_list中
def get_hole_family_cont(family_id_list):
all_familly_list = []
with open('sunzhuang.csv', 'r') as csvfile:
reader = csv.DictReader(csvfile)
for row in reader:
for id in family_id_list:
if row['序号'] == str(id):
# row 是一个字典格式,需要用dict.values()将键值转化为列表
list3 = list(row.values())
# 从列表中找出我们需要的信息,从新创建一个列表,就得到了该人的信息
family_list = [list3[3], list3[4], list3[7], list3[6]]
all_familly_list.append(family_list)
return all_familly_list
# print(first)
# print(huzhu_list)
sg.theme('GreenTan')
col3 = Column([[Frame('Information:', [[Text(), Column([[Text('户 数: '), Input(key='-hushu-', size=(10, 1))],
[Text('人 数: '), Input(key='-renshu-', size=(10, 1))],
[Text('低保人数: '), Input(key='-dibao-', size=(10, 1))],
[Text('五保人数: '), Input(key='-wubao-', size=(10, 1))], ],
size=(210, 120), pad=(0, 0))]
]
)
]], pad=(0, 0))
col2 = Column([[Frame('Accounts:', [[Column([
[Table(values=[['', '', '', '']],
headings=[' 姓 名 ', ' 性 别 ', ' 属 性 ', ' 身 份 证 号 '],
max_col_width=300,
auto_size_columns=True, # 自动调整列宽(根据上面第一次的values默认值为准,update时不会调整)
display_row_numbers=True, # 序号
justification='center', # 字符排列 left right center
num_rows=12, # 行数
row_height=30, # 行高
key='_table_',
font=('微软雅黑', 12),
text_color='black',
background_color='white',
enable_events=True,
bind_return_key=True,
tooltip='This is a table')]
, ], size=(450, 400), pad=(0, 0))]])]], pad=(0, 0))
col1 = Column([
# Information frame
[Frame('Search:', [[Text(), Column([[Text('输入姓名:')],
[Input(key='-USERID-IN-', size=(15, 1)),
Button('Search', key='-SEAR-')],
], size=(195, 120), pad=(0, 0))]])], ], pad=(0, 0))
layout = [[col1, col3], [col2]]
window = Window('人员信息查询', layout)
while True:
event, values = window.read()
print(event, values)
familly_num = get_familly_num()
people_num = get_people_num()
window['-hushu-'].update(familly_num)
window['-renshu-'].update(people_num)
if event == sg.WIN_CLOSED:
break
elif event == '-SEAR-':
# print(values['-LIST-']])
if values['-USERID-IN-'] != '':
name = values['-USERID-IN-']
else:
sg.popup('输入一个人名,比如:张三')
name = '张三'
id_list = get_name_id(name)
list1 = get_huzhu_list()
all_list = []
num = 0
for id in id_list:
family_id_list = get_hole_family_id(list1, id)
familly_list = get_hole_family_cont(family_id_list)
num += 1
for i in familly_list:
all_list.append(i)
window['_table_'].Update(all_list)
sg.popup('一共找到{}户'.format(num))
window.close()
总结
写这个小东西,从构思,查资料,写代码,查bug,用了一整天的时间,对我一个小白爱好者来说还是挺有成就感的。后续将会继续添加照片显示,家庭成员详细信息页面,和添加、删除、修改功能,实现了再分享。