用PySimpleGui做户籍资料查询工具

用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()

效果是
用PySimpleGui做户籍资料查询工具
基本很容易实现我想要的,其他不再累述,需要的可以去csdn找,很多很多

界面布局

通过修改一个案例,一番折腾,得到了我想要的布局如下

用PySimpleGui做户籍资料查询工具
代码如下:

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属性

最终界面

用PySimpleGui做户籍资料查询工具

完整代码


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,用了一整天的时间,对我一个小白爱好者来说还是挺有成就感的。后续将会继续添加照片显示,家庭成员详细信息页面,和添加、删除、修改功能,实现了再分享。

上一篇:Python操作Sharepoint实现目录创建、文件上传


下一篇:GoLang设计模式05 - 原型模式