用 Python 开发名片管理系统

简介

这是一个用 Python 开发的可视化的名片管理系统,主要实现名片的添加、删除、修改三大功能。效果图如下所示


首页
添加页面

思路分析

界面实现:使用 Tkinter 库进行 GUI 界面设计开发。tkinter 是 python 内置的一套用于开发 GUI 程序的包。

数据存储:1.0版本的数据在内存中以列表形式存储,序列化到本地时使用文件存储方案

# 所有数据集合
self.all_items = []

代码结构:项目有三个窗口,分别是主界面、添加界面,修改界面,每一个界面使用一个类进行组织,并提供一个 show 方法,用于显示窗口,因为添加和修改界面可以公用,所以只需定义两个界面类即可。

  • 主界面:MainFrame
  • 添加/修改界面:InputFrame
    代码结构如下所示


    类结构

    程序启动时,新建主界面的实例对象,然后调用 show 方法即可运行程序。


    image.png

开发环境

  • 开发工具:Pycharm
  • 辅助包:tkinter

代码实现

from tkinter import *
from tkinter import messagebox

# 定义一个主窗口界面类,其父类为 object
class MainFrame(object):
    # 初始化主窗口
    def __init__(self):
        # 初始化所有数据集合列表
        self.all_items = []
        # 使用Tk()函数创建顶级窗口(主窗口)
        self.root = Tk()
        # 窗口自定义名称为“名片管理系统”
        self.root.title("名片管理系统")
        # 设置主窗口大小
        self.root.wm_minsize(width=400, height=300)

    # 定义 show 方法:显示主窗口布局
    def show(self):
        # 显示顶部区域信息
        self.addTopFrame()
        # 初始化信息列表区域
        self.initContent()
        # 从本地文件加载以存储的信息
        self.loadData()
        # 显示出所有的名片信息
        self.showAllItem()
        # 开启主界面,进入主循环
        self.root.mainloop()

    # 定义 showAllItem 方法,显示所有列表信息
    def showAllItem(self):
        # 遍历循环所有的列表信息
        for data in self.all_items:
            # 添加信息到列表界面
            self.showItem(data)

    # 定义 save_2_file 方法,将已经添加的名片信息存储到本地文件中
    def save_2_file(self):
        # 打开文件
        f = open("contacts.data", "w")
        # 将已经添加的信息以字符串形式写入次文件
        f.write(str(self.all_items))
        # 关闭文件
        f.close()

    # 定义一个loadData方法,加载本地存储文件中的所有数据
    def loadData(self):
        # 通过"try...except"异常点检验方法,检查存储名片信息的本地文件的存在性
        try:
            # 打开存储名片信息的本地文件
            f = open("contacts.data")
            # 读取本地文件中的所有数据
            self.all_items = eval(f.read())
            """eval 相关知识点以及用法
            功能:将字符串str当成有效的表达式来求值并返回计算结果。
          语法: eval(source[, globals[, locals]]) -> value
            参数:source:一个Python表达式或函数compile()返回的代码对象
                  globals:可选。必须是dictionary
                  locals:可选。任意map对象"""
            f.close()
        except Exception:
            pass

    # 添加主窗口布局需要的控件
    def addTopFrame(self):
        # 添加一个框架将首页的题目和添加按钮都放在一起
        topFrame = Frame(self.root)
        # 在次框架中添加一个标签,其题目:名片信息
        titlelable = Label(topFrame, text="名片信息")
        # 显示框架,并设定向左排列,x和y轴的宽度均为5个像素
        titlelable.pack(side=LEFT, padx=5, pady=6)

        # 定义一个添加按钮,其中command点击事件通过lambda函数实现:调用showInputFrame
        addButton = Button(topFrame, text="添加", command=lambda : self.showInputFrame("添加",None))
        # 显示按钮,并设定向左排列,x和y轴的宽度均为默认像素
        addButton.pack(side=RIGHT)

        # 设置此框架的背景颜色为蓝色
        topFrame.config(bg="#3f51b5")
        # 横向填充
        topFrame.pack(fill=X)

    # 显示添加页面
    def showInputFrame(self,title,dict):
        # 定义 inputFrame 实例,即创建一个添加界面
        self.inputFrame = InputFrame(self,title,dict)
        # 显示添加界面
        self.inputFrame.show()

    # 显示列表信息
    def initContent(self):
        # 初始化列表框架
        self.item_container = Frame(self.root)
        # 将这一框架整体整合
        self.item_container.pack()

    # 添加名片信息
    def addItem(self, dict):
        # 添加新信息到 all_items 列表
        self.all_items.append(dict)
        # 显示名片信息
        self.showItem(dict)
        # 将信息保存到本地文件夹
        self.save_2_file()

    # 修改信息
    def updateItem(self, origin_data,new_data):
        # 获取原始数据列表的索引
        index = self.all_items.index(origin_data)
        # 将需要修改的内容从数据列表中移除
        self.all_items.remove(origin_data)
        # 添加修改后的名片信息到数据列表
        self.all_items.insert(index,new_data)
        # 将修改后的信息保存到本地文件夹
        self.save_2_file()

        # 为了让修改后的信息整体生效,先将主界面上的所有信息都删除
        self.remove_allItem()
        # 然后在主界面上重新显示所有信息
        self.showAllItem()

    # 清空主界面上的每一行数据:
    def remove_allItem(self):
        # 通过for循环将主界面上的名片信息都逐一删除
        for child_item in self.item_container.winfo_children():
            child_item.destroy()

    # 在主界面上显示一行信息
    def showItem(self, dict):
        # 初始化主框架
        item_frame = Frame(self.item_container)

        # 添加姓名信息标签
        name_label = Label(item_frame, text=dict['name'])
        name_label.pack(side=LEFT, padx=5, pady=5)

        # 添加性别信息标签
        sex_label = Label(item_frame, text=dict['sex'])
        sex_label.pack(side=LEFT)

        # 添加手机号信息标签
        mob_label = Label(item_frame, text=dict['mobile'])
        mob_label.pack(side=LEFT)

        # 添加删除按钮
        delete_button = Button(item_frame, text="删除", command=lambda: self.delete_item(dict,item_frame))
        # delete_button = Button(f2, text="删除", command=lambda dict : self.all_items.remove(dict))
        delete_button.pack(side=LEFT)

        # 添加修改按钮
        update_button = Button(item_frame, text="修改",command=lambda : self.showInputFrame("修改",dict))
        update_button.pack(side=LEFT)

        # 将整个框架整体封装
        item_frame.pack()

    # 删除信息
    def delete_item(self, data,item_frame):
        # 从数据列表中删除数据
        self.all_items.remove(data)
        # 将删除后的信息存储到本地文件
        self.save_2_file()
        # 在主界面中将删除的这条信息移除
        item_frame.pack_forget()


# 定义了一个添加界面类,其父类为 object
class InputFrame(object):
    def __init__(self, mainFrame,title,dict):
        self.mainFrame = mainFrame
        self.data = dict
        # 初始化添加窗口
        self.inputRootFrame = Tk()
        # 设置这一窗口的题目
        self.inputRootFrame.title(title)
        # 设置窗口大小
        self.inputRootFrame.wm_minsize(width=200, height=250)


    def show(self):
        # 添加界面的展示信息
        self.addInputFrame()
        # 由于添加和编辑用了同一个界面,所以根据界面不同,按钮的文本需要改变
        if self.data == None:
            self.addButtonFrame('保存')
        else:
            self.addButtonFrame('确定修改')

        self.inputRootFrame.mainloop()

    # 添加界面布局信息展示
    def addInputFrame(self):
        # 初始化姓名框架以及界面
        fmname = Frame(self.inputRootFrame)
        # 添加姓名标签
        self.namelable = Label(fmname, text="姓名:")
        # 设置姓名标签的位置以及大小
        self.namelable.pack(side=LEFT, padx=5, pady=10)
        # 添加姓名输入框
        self.nameInput = Entry(fmname, width=50, textvariable=StringVar())
        # 设置姓名输入框的位置
        self.nameInput.pack(side=LEFT)
        # 将以前的姓名自动填写进来,便于用户核对方便修改
        if self.data != None:
            self.nameInput.insert(0, self.data['name'])
            self.nameInput['state'] = DISABLED
        fmname.pack()

        # 初始化性别框架以及信息
        fmSex = Frame(self.inputRootFrame)
        # 添加性别标签
        self.sexlable = Label(fmSex, text="性别:")
        # 设置性别标签的位置
        self.sexlable.pack(side=LEFT, padx=5, pady=10)

        # 添加性别输入框
        self.sexInput = Entry(fmSex, width=50, textvariable=StringVar())
        # 设置性别输入框的位置
        self.sexInput.pack(side=LEFT)
        # 将以前的性别自动填写进来,便于用户核对方便修改
        if self.data != None:
            self.sexInput.insert(0, self.data['sex'])
        fmSex.pack()

        # 初始化手机号的框架以及显示内容
        fmMob = Frame(self.inputRootFrame)
        # 添加手机号标签
        moblelable = Label(fmMob, text="手机号:")
        # 设置手机号标签的位置
        moblelable.pack(side=LEFT, padx=5, pady=10)
        # 添加手机号输入框
        self.mobleInput = Entry(fmMob, width=50, textvariable=StringVar())
        # 设置手机号输入框的位置
        self.mobleInput.pack(side=LEFT)
        # 将以前的手机号自动填写进来,便于用户核对方便修改
        if self.data != None:
            self.mobleInput.insert(0, self.data['mobile'])
        fmMob.pack()

    # 添加界面的按钮设置
    def addButtonFrame(self,title):
        # 初始化框架
        fmButton = Frame(self.inputRootFrame)
        # 设置框架位置
        fmButton.pack(side=BOTTOM, anchor=W, fill=X)

        # 添加保存按钮
        confrmButton = Button(fmButton, text=title, command=self.saveInput)
        confrmButton.pack(side=RIGHT, fill=BOTH)

        # 添加取消按钮
        cencelButton = Button(fmButton, text="取消", command=self.cancelInput)
        cencelButton.pack(side=RIGHT)

    # 名片信息存储
    def saveInput(self):
        # 获取输入的姓名
        name = self.nameInput.get()
        # 获取输入的性别
        sex = self.sexInput.get()
        # 获取输入的手机号
        mobile = self.mobleInput.get()

        # 确保输入的姓名不为空
        if name == None or name == '':
            messagebox.showinfo("提示","请输入姓名!")
            return
        # 确保输入的性别不为空
        if sex == None or sex == '':
            messagebox.showinfo("提示","请输入性别!")
            return
        # 确保输入的性别为“男” 或者 “女”
        if sex != '男' and sex != '女':
            messagebox.showinfo("提示","请选择男或者女!")
            return

        # 确保输入的手机号不为空
        if mobile == None or mobile == '':
            messagebox.showinfo("提示","请输入手机号!")
            return

        # 将输入的整条名片信息以字典的形式存储
        info = {'name': name, 'sex': sex, 'mobile': mobile}
        # 如果在添加页面添加数据成功后,就把数据更新到主界面
        if self.data == None:
            self.mainFrame.addItem(info)
        else:
            self.mainFrame.updateItem(self.data,info)
        # 返回主界面
        self.inputRootFrame.destroy()

    # 点击修改页面上的“取消”按钮,就返回到主界面上
    def cancelInput(self):
        self.inputRootFrame.destroy()


# 程序入口
if __name__ == '__main__':
    # 创建一个 main frame 对象,即创建一个主窗口实例
    mainFrame = MainFrame()
    # 调用主窗口的show()方法显示主窗口
    mainFrame.show()

总结

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,242评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,769评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,484评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,133评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,007评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,080评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,496评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,190评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,464评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,549评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,330评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,205评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,567评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,889评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,160评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,475评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,650评论 2 335

推荐阅读更多精彩内容