Python tkinter实现图形界面通讯录,使用txt文本存储数据

1 实验目的与要求

实验目的:设计一个实用的小型通讯录程序

实验要求:最后的通讯录要写入文件中保存起来

2 实验内容

本次实验内容如下:

设计一个实用的小型通讯录程序,具有添加,查询和删除功能。由姓名,籍贯,电话号码1,电话号码2,电子邮箱组成,姓名可以由字符和数字混合编码。电话号码可由字符和数字组成。

要求:最后的通讯录要写入的文件中保存起来;

实现功能:

(1)系统以菜单方式工作

(2)信息录入功能

(3)信息浏览功能

(4)信息查询功能

(5)信息修改功能

(6)系统退出功能

3 主要实验步骤

(1)导入所需的库:tkinter.messagebox用于显示消息框,json用于处理JSON数据,os用于文件操作,tkinter用于创建GUI界面。

(2)创建主窗口:root = tkinter.Tk()创建一个主窗口,并设置标题和大小。

(3)添加滚动条:创建一个垂直滚动条并将其与列表框关联。

(4)创建通讯录显示区域:使用Frame在主窗口中创建一个框架用于显示通讯录信息,包括联系人姓名、手机号等。

(5)打开通讯录文件:在当前目录下打开名为"通讯录.txt"的文件,如果文件不存在则创建一个新文件。

(6)定义函数showinfo():用于显示通讯录中的联系人信息。

(7)创建按钮和输入框:包括添加联系人、删除联系人、修改联系人、查找联系人和退出按钮,以及一个用于输入联系人姓名的输入框。

(8)绑定按钮事件:分别绑定添加联系人、删除联系人、修改联系人、查找联系人和退出按钮的点击事件,点击按钮时会执行相应的函数。

(9)运行主循环:使用“root.mainloop()”来运行主窗口的事件循环,使窗口能够响应用户操作。

4 实验过程

4.1 导入所需模块

导入三个模块:tkinter.messagebox、json和os。

import tkinter.messagebox

import json

import os

import tkinter

其中:

tkinter.messagebox模块提供了一些用于在GUI程序中显示消息框的函数;

json模块提供了一些用于读取和编写JSON数据的函数;

os模块提供了一些与操作系统交互的函数。

这里还导入了一个名为tkinter的模块,它是Python内置的一个图形用户界面(GUI)工具包,在开发桌面应用程序时经常被使用。

4.2 创建主窗口

使用tkinter.Tk()创建一个名为root的主窗口,并设置窗口的标题为"通讯录",以及窗口的大小为550×500。

root = tkinter.Tk()

root.title('通讯录')

root.geometry("550×500")

这段代码是使用Python中的tkinter模块创建一个名为"root"的窗口(或者称为主窗口)。其中:

tkinter.Tk()方法创建了一个顶层窗口对象,并将其赋值给变量root,这个窗口对象将作为所有其他小组件的父容器。

root.title()方法设置主窗口的标题为“通讯录”。

root.geometry()方法用于设置主窗口的大小为宽550像素和高500像素。在Python中,你可以通过字符串来指定窗口的大小、位置等参数。例如,本段代码中的"550×500"字符串表示设置窗口宽度为550像素,高度为500像素。

4.3 添加滚动条

创建一个垂直滚动条,并将其放置在主窗口的右侧。

scrollbar = tkinter.Scrollbar(root)

scrollbar.pack(side=tkinter.RIGHT, fill=tkinter.Y)

这段代码使用tkinter模块在root窗口(主窗口)中创建了一个名为"scrollbar"的滚动条对象。其中:

tkinter.Scrollbar()方法创建了一个Scrollbar组件对象,并将其赋值给变量scrollbar。

scrollbar.pack()方法用于将滚动条小部件放置在主窗口的右侧,并充满其中的Y轴。

这里pack()方法是tkinter中最简单的一种布局管理器,在GUI开发中比较常用,可以让小部件自适应大小并排列在一起,但是它只能实现比较简单的布局需求。如果需要更加灵活和高级的布局控制,可以使用其他的布局管理器,如grid()或place()等。

4.4 创建联系人列表框

创建一个框架Frame_info,用于容纳联系人列表框。在框架中创建一个列表框listbox,并将垂直滚动条与列表框绑定起来。

Frame_info = tkinter.Frame(root, height=150, width=180)

Frame_info.place(x=40, y=80)

listbox = tkinter.Listbox(Frame_info, yscrollcommand=scrollbar.set, font=('宋体', 15))

listbox.grid(row=2, column=0, columnspan=5, sticky=tkinter.NSEW)

scrollbar.config(command=listbox.yview)

这段代码使用tkinter模块在root窗口(主窗口)中创建了一个名为"Frame_info"的框架对象,以及一个名为"listbox"的列表框对象,并将它们放置在主窗口的指定位置上。其中:

tkinter.Frame()方法创建了一个Frame组件对象,并将其赋值给变量Frame_info。该组件通常用于作为其他小部件的容器,以便更好地管理它们的布局。

Frame_info.place()方法用于将Frame_info组件放置在主窗口(x=40, y=80)的指定位置上。

listbox = tkinter.Listbox(Frame_info,…)方法创建了一个Listbox组件对象,并将其赋值给变量listbox。该组件用于显示一个可滚动的文本列表。

在listbox.grid(…)方法中的设置参数用于设置listbox对象在Frame_info框架内的偏移和大小等显示属性。

scrollbart.config(command=listbox.yview)用于实现关联滚动条与对应的文本列表。

这里sticky=tkinter.NSEW表示使组件能够在框架中展开并填充空间,以便自适应大小,从而占据整个格子。如果不指定sticky,则只会占据所需的最小空间,无法自适应扩展。

4.5 打开通讯录文件并显示联系人信息:

file = open("通讯录.txt", mode='a', encoding='utf-8')

file.close()

这里尝试以追加模式打开名为"通讯录.txt"的文件,如果文件不存在则会创建一个空文件。然后立即关闭文件。

def showinfo():

    listbox.delete(0, tkinter.END)

    file = open("通讯录.txt", mode='r', encoding='utf-8')

    if len(file.read()) != 0:

        file.seek(0, 0)

        file_data = file.read()

        split_info = file_data.split('\n')

        split_info.remove(split_info[len(split_info) – 1])

        name_li = []

        all_info_li = []

        for i in split_info:

            dict_info = json.loads(i.replace("\'", '\"'))

            all_info_li.append(dict_info)

            listbox.insert(tkinter.END, dict_info['姓名'] + ' ' + dict_info['手机号1'] + ' ' + dict_info['手机号2'] + ' ' +

                           dict_info['邮箱'] + ' ' + dict_info['地址'])

file.close()

定义了一个showinfo()函数,用于显示联系人信息。函数首先清空列表框listbox的内容,然后打开通讯录文件并读取其中的信息。通过解析JSON字符串,将每个联系人的信息添加到all_info_li列表中,并将姓名和其他信息拼接后插入到列表框中。最后关闭文件。其中:

listbox.delete(0, tkinter.END)方法用于清空之前列表框中的所有数据。

file = open("通讯录.txt", mode='r', encoding='utf-8')打开名为“通讯录.txt”的文件,并将文件对象赋值给变量file。mode='r'表示以只读模式打开文件,encoding='utf-8'则指定了文件的编码格式。

len(file.read()) != 0用于判断当前文件是否为空。如果不为空,则说明存在通讯录信息需要进行展示。

file.seek(0, 0)表示将文件指针移动到文件开头。

file_data = file.read()将整个文件读取到一个字符串变量file_data中。

split_info = file_data.split('\n')使用split()方法将文件内容按照换行符分割成一个个子字符串,并返回一个列表split_info。

split_info.remove(split_info[len(split_info) – 1])移除列表split_info的最后一个空字符串元素。

在for循环中,逐一遍历列表split_info中的每一个子字符串i。首先使用json.loads()方法解析该字符串中的JSON格式数据,然后将其添加到列表all_info_li中保存。接着,将解析好的通讯录资料信息按照规定的格式插入listbox中。

最后要记得关闭文件,防止文件句柄资源泄漏。

4.6 添加联系人功能

def add_def():

    add_info = {'姓名': '', '手机号1': '', '手机号2': '', '邮箱': '', '地址': ''}

    add_info['姓名'] = name_input.get()

    if add_info['姓名'] == '':

        tkinter.messagebox.showinfo('提示', '请输入姓名!')

        return

    add_info['手机号1'] = pho_input.get()

    add_info['手机号2'] = pho_input2.get()

    add_info['邮箱'] = email_input.get()

    add_info['地址'] = address_input.get()

    with open("通讯录.txt", 'a+', encoding='utf-8') as file:

        file.write(str(add_info) + '\n')

    showinfo()

    name_input.delete(0, tkinter.END)

    pho_input.delete(0, tkinter.END)

    pho_input2.delete(0, tkinter.END)

    email_input.delete(0, tkinter.END)

address_input.delete(0, tkinter.END)

定义了一个add_def()函数,用于添加联系人。函数首先创建一个空的联系人信息字典add_info,然后从输入框中获取联系人的姓名、手机号、邮箱和地址,并将其填充到字典中。接下来,将联系人信息以字符串形式写入到通讯录文件中。最后,调用showinfo()函数刷新列表框内容,并清空输入框中的内容。其中:

add_info = {'姓名': '', '手机号1': '', '手机号2': '', '邮箱': '', '地址': ''}创建一个空的字典add_info。

add_info['姓名'] = name_input.get()从图形界面中获取姓名输入框中的值,并将其保存到字典add_info的“姓名”字段中。

if add_info['姓名'] == '':用于判断姓名是否为空。如果为空,则弹出提示框并结束函数。

add_info['手机号1'] = pho_input.get()从图形界面中获取手机号1输入框中的值,并将其保存到字典add_info的“手机号1”字段中。

add_info['手机号2'] = pho_input2.get()从图形界面中获取手机号2输入框中的值,并将其保存到字典add_info的“手机号2”字段中。

add_info['邮箱'] = email_input.get()从图形界面中获取邮箱输入框中的值,并将其保存到字典add_info的“邮箱”字段中。

add_info['地址'] = address_input.get()从图形界面中获取地址输入框中的值,并将其保存到字典add_info的“地址”字段中。

with open("通讯录.txt", 'a+', encoding='utf-8') as file: 打开名为“通讯录.txt”的文件,并将文件对象赋值给变量file。mode='a+'表示以追加模式打开文件,并且指定了文件的编码格式为utf-8。

file.write(str(add_info) + '\n')将字典add_info以字符串形式写入文件中,并在末尾添加一个换行符。

showinfo()调用“showinfo()”函数,用于刷新通讯录数据展示部分。

最后,清空各个输入框中的数据并继续等待用户输入。

4.7 删除联系人功能

def del_def():

    selected_item = listbox.curselection()

    if not selected_item:

        tkinter.messagebox.showinfo('提示', '请选择要删除的联系人!')

        return

    else:

        file = open("通讯录.txt", mode='r', encoding='utf-8')

        read_data = file.readlines()

        file.close()

        selected_index = selected_item[0]

        del read_data[selected_index]

        file = open("通讯录.txt", mode='w', encoding='utf-8')

        file.writelines(read_data)

        file.close()

        showinfo()

定义了一个del_def()函数,用于删除选定的联系人。首先检查是否选定了联系人,如果没有选定则弹出提示框。如果有选定的联系人,则打开通讯录文件并逐行读取其中的内容。找到选定联系人的索引后,删除相应的行,并将剩余的内容重新写入文件中。最后,调用showinfo()函数刷新列表框内容。其中:

selected_item = listbox.curselection()通过“curselection()”方法获取用户在图形界面中选择的联系人,并将其保存到变量selected_item中。

if not selected_item:判断用户是否选择了要删除的联系人。如果没有选择,则弹出提示框并结束函数。

file = open("通讯录.txt", mode='r', encoding='utf-8')打开名为“通讯录.txt”的文件,并将文件对象赋值给变量file。mode='r'表示以只读模式打开文件,并且指定了文件的编码格式为utf-8。注意,在此之前需要确认该文件已被创建。

read_data = file.readlines()读取文件中的所有行,并将其保存到列表read_data中。

file.close()关闭文件。

selected_index = selected_item[0]获取用户选择的联系人的索引值,并将其保存到变量selected_index中。

del read_data[selected_index]从列表read_data中删除用户选择的联系人的数据。

file = open("通讯录.txt", mode='w', encoding='utf-8')重新以写入模式打开文件,并将文件对象赋值给变量file。mode='w'表示以覆盖模式打开文件,并且指定了文件的编码格式为utf-8。

file.writelines(read_data)将处理后的全部数据重新写入文件中。

file.close()关闭文件。

showinfo()调用“showinfo()”函数,用于刷新通讯录数据展示部分。

4.8 修改联系人功能

def update_def():

    selected_item = listbox.curselection()

    if not selected_item:

        tkinter.messagebox.showinfo('提示', '请选择要修改的联系人!')

        return

    else:

        file = open("通讯录.txt", mode='r', encoding='utf-8')

        read_data = file.readlines()

        file.close()

        selected_index = selected_item[0]

        selected_info = json.loads(read_data[selected_index].replace("\'", '\"'))

        name_input.insert(0, selected_info['姓名'])

        pho_input.insert(0, selected_info['手机号1'])

        pho_input2.insert(0, selected_info['手机号2'])

        email_input.insert(0, selected_info['邮箱'])

        address_input.insert(0, selected_info['地址'])

        del read_data[selected_index]

        file = open("通讯录.txt", mode='w', encoding='utf-8')

        file.writelines(read_data)

        file.close()

        showinfo()

定义了一个update_def()函数,用于修改选定的联系人。首先检查是否选定了联系人,如果没有选定则弹出提示框。如果有选定的联系人,则打开通讯录文件并逐行读取其中的内容。找到选定联系人的索引后,将其信息解析为字典,并将对应的值填充到相应的输入框中。接着删除选定的联系人信息,并将剩余的内容重新写入文件中。最后,调用showinfo()函数刷新列表框内容。

这段代码定义了一个名为"update_def()"的函数,该函数实现了修改通讯录信息的功能。其中:

selected_item = listbox.curselection()通过“curselection()”方法获取用户在图形界面中选择的联系人,并将其保存到变量selected_item中。

if not selected_item:判断用户是否选择了要修改的联系人。如果没有选择,则弹出提示框并结束函数。

file = open("通讯录.txt", mode='r', encoding='utf-8')打开名为“通讯录.txt”的文件,并将文件对象赋值给变量file。mode='r'表示以只读模式打开文件,并且指定了文件的编码格式为utf-8。注意,在此之前需要确认该文件已被创建。

read_data = file.readlines()读取文件中的所有行,并将其保存到列表read_data中。

file.close()关闭文件。

selected_index = selected_item[0]获取用户选择的联系人的索引值,并将其保存到变量selected_index中。

selected_info= json.loads(read_data[selected_index].replace("\'", '\"'))从read_data中获取用户选择的联系人的信息,并转换成字典格式的数据(因为使用了单引号,需要先将其替换成双引号)。

name_input.insert(0, selected_info['姓名'])将选中的联系人的姓名信息显示在对应的文本框中。

pho_input.insert(0, selected_info['手机号1'])将选中的联系人的手机号1信息显示在对应的文本框中。

pho_input2.insert(0, selected_info['手机号2'])将选中的联系人的手机号2信息显示在对应的文本框中。

email_input.insert(0, selected_info['邮箱'])将选中的联系人的邮箱信息显示在对应的文本框中。

address_input.insert(0, selected_info['地址'])将选中的联系人的地址信息显示在对应的文本框中。

del read_data[selected_index]从列表read_data中删除用户选择的联系人的数据。

file = open("通讯录.txt", mode='w', encoding='utf-8')重新以写入模式打开文件,并将文件对象赋值给变量file。mode='w'表示以覆盖模式打开文件,并且指定了文件的编码格式为utf-8。

file.writelines(read_data)将处理后的全部数据重新写入文件中。

file.close()关闭文件。

showinfo()调用“showinfo()”函数,用于刷新通讯录数据展示部分。

4.9 创建各个组件

name_label = tkinter.Label(root, text='姓名:', font=('宋体', 15))

name_label.place(x=40, y=280)

name_input = tkinter.Entry(root, font=('宋体', 15))

name_input.place(x=90, y=280)

pho_label = tkinter.Label(root, text='手机号1:', font=('宋体', 15))

pho_label.place(x=10, y=330)

pho_input = tkinter.Entry(root, font=('宋体', 15))

pho_input.place(x=90, y=330)

pho_label2 = tkinter.Label(root, text='手机号2:', font=('宋体', 15))

pho_label2.place(x=10, y=380)

pho_input2 = tkinter.Entry(root, font=('宋体', 15))

pho_input2.place(x=90, y=380)

email_label = tkinter.Label(root, text='邮箱:', font=('宋体', 15))

email_label.place(x=40, y=430)

email_input = tkinter.Entry(root, font=('宋体', 15))

email_input.place(x=90, y=430)

address_label = tkinter.Label(root, text='地址:', font=('宋体', 15))

address_label.place(x=40, y=480)

address_input = tkinter.Entry(root, font=('宋体', 15))

address_input.place(x=90, y=480)

add_button = tkinter.Button(root, text='添加', font=('宋体', 15), command=add_def)

add_button.place(x=420, y=280)

del_button = tkinter.Button(root, text='删除', font=('宋体', 15), command=del_def)

del_button.place(x=420, y=330)

update_button = tkinter.Button(root, text='修改', font=('宋体', 15), command=update_def)

update_button.place(x=420, y=380)

quit_button = tkinter.Button(root, text='退出', font=('宋体', 15), command=root.quit)

quit_button.place(x=420, y=430)

创建了标签、输入框和按钮等各种GUI组件,并使用place()方法将它们放置在主窗口的指定位置。其中:

Label 创建文本标签,用于显示文本内容。

Entry 创建文本框,用于输入和显示文本内容。

Button 创建按钮,用于触发相应的操作。

place(x, y) 方法用于设置组件在界面上的位置坐标。

具体地:

name_label 创建了一个名为“姓名”的文本标签,并将其放置在位置(40, 280)的坐标处。

name_input 创建了一个用于输入姓名的文本框,并将其放置在位置(90, 280)的坐标处。

pho_label 创建了一个名为“手机号1”的文本标签,并将其放置在位置(10, 330)的坐标处。

pho_input 创建了一个用于输入手机号码的文本框,并将其放置在位置(90, 330)的坐标处。

pho_label2 创建了一个名为“手机号2”的文本标签,并将其放置在位置(10, 380)的坐标处。

pho_input2 创建了另一个用于输入手机号码的文本框,并将其放置在位置(90, 380)的坐标处。

email_label 创建了一个名为“邮箱”的文本标签,并将其放置在位置(40, 430)的坐标处。

email_input 创建了一个用于输入邮箱的文本框,并将其放置在位置(90, 430)的坐标处。

address_label 创建了一个名为“地址”的文本标签,并将其放置在位置(40, 480)的坐标处。

address_input 创建了一个用于输入地址的文本框,并将其放置在位置(90, 480)的坐标处。

接下来是四个按钮:

add_button 创建了一个名为“添加”的按钮,并将其放置在位置(420,280)的坐标处。该按钮触发了 add_def() 函数,实现添加联系人信息的功能。

del_button 创建了一个名为“删除”的按钮,并将其放置在位置(420,330)的坐标处。该按钮触发了 del_def() 函数,实现删除联系人信息的功能。

update_button 创建了一个名为“更新”的按钮,并将其放置在位置(420,380)的坐标处。该按钮触发了 update_def() 函数,实现修改联系人信息的功能。

quit_button 创建了一个名为“添加”的按钮,并将其放置在位置(420,430)的坐标处。该按钮实现退出功能。

4.10 显示联系人信息

showinfo()

调用showinfo()函数,初始化时显示已有的联系人信息。

4.11 运行主循环

root.mainloop()

通过调用mainloop()方法启动GUI应用程序的主循环,等待用户交互事件的发生。

以上就是代码的详细实现过程。通过创建GUI窗口、读取文件、添加、删除和修改联系人等功能,实现了一个简单的通讯录应用程序。用户可以在界面上输入联系人信息,然后通过按钮操作实现对联系人的增删改查。

【完整代码】

import tkinter.messagebox
import json
import os
import tkinter

root = tkinter.Tk()
root.title('通讯录')
root.geometry("550x500")

# 添加滚动条
scrollbar = tkinter.Scrollbar(root)
scrollbar.pack(side=tkinter.RIGHT, fill=tkinter.Y)

Frame_info = tkinter.Frame(root, height=150, width=180)
Frame_info.place(x=40, y=80)#滚动框位置

# 绑定滚动条和列表
listbox = tkinter.Listbox(Frame_info, yscrollcommand=scrollbar.set, font=('宋体', 15))
listbox.grid(row=2, column=0, columnspan=5, sticky=tkinter.NSEW)

# 设置滚动条和列表的关系
scrollbar.config(command=listbox.yview)
#

#标题属性的另一个设置方法
# name_label = tkinter.Label(Frame_info, text="名字", font=('宋体', 15))
# name_label.grid(row=1, column=0)
# phone_label = tkinter.Label(Frame_info, text="手机号1", font=('宋体', 15))
# phone_label.grid(row=1, column=1)
# phone_label = tkinter.Label(Frame_info, text="手机号2", font=('宋体', 15))
# phone_label.grid(row=1, column=2)
# mail_label = tkinter.Label(Frame_info, text="邮箱", font=('宋体', 15))
# mail_label.grid(row=1, column=3)
# address_label = tkinter.Label(Frame_info, text="地址", font=('宋体', 15))
# address_label.grid(row=1, column=4)


file = open("通讯录.txt", mode='a', encoding='utf-8')
file.close()

def showinfo():
    # 清空列表
    listbox.delete(0, tkinter.END)

    file = open("通讯录.txt", mode='r', encoding='utf-8')
    if len(file.read()) != 0:
        file.seek(0, 0)
        file_data = file.read()
        split_info = file_data.split('\n')
        split_info.remove(split_info[len(split_info) - 1])
        name_li = []  # 用于存储联系人姓名的列表
        all_info_li = []  # 用于存储所有信息的列表
        for i in split_info:
            dict_info = json.loads(i.replace("\'", '\"'))
            all_info_li.append(dict_info)
            # 添加到列表中
            listbox.insert(tkinter.END, dict_info['姓名'] + ' ' + dict_info['手机号1'] + ' ' + dict_info['手机号2'] + ' ' +
                           dict_info['邮箱'] + ' ' + dict_info['地址'])
            row_count = 0
            column_count = 0

        # for i in all_info_li:
        #     # row_count += 1
        #     column_count += 1

            # for title, info_value in person_info.items():
            #     tktest = tkinter.Label(Frame_info, text=info_value, font=('宋体', 15, 'bold'))
            #     tktest.grid(row=row_count, column=column_count)
            #     column_count += 1
            # row_count += 1
            tktest = tkinter.Label(Frame_info, text=" " *42, font=('宋体', 15, 'bold'))#设置滚动框显示的宽度
            tktest.grid(row=row_count, column=column_count)


showinfo()

# 添加
def add_def(event):
    def add_man(event):
        name = name_new.get()
        phone1 = phone_new1.get()
        phone2 = phone_new2.get()
        mail = mail_new.get()
        address = adr_new.get()
        if name == "" or phone1 == " " or phone2 == " " or mail == " " or address == " ":
            tkinter.messagebox.showerror('错误', '所填信息都不能为空')
        else:
            card_dict = {"姓名": name, "手机号1": phone1, "手机号2": phone2,
                         "邮箱": mail, "地址": address}
            f = open("通讯录.txt", mode='a+', encoding='utf-8')
            f.write(str(card_dict) + '\n')
            f.close()
            tkinter.messagebox.showinfo('消息提示框', f'添加“{name}“为联系人成功!')
            showinfo()
            window_add.destroy()



    # 弹出框
    window_add = tkinter.Toplevel(root)
    window_add.geometry('300x250')
    # 姓名
    name_new = tkinter.StringVar()
    tkinter.Label(window_add, text='新联系人姓      名:').place(x=10, y=10)
    tkinter.Entry(window_add, textvariable=name_new).place(x=130, y=10)
    # 手机号1
    phone_new1 = tkinter.StringVar()
    tkinter.Label(window_add, text='新联系人手机号1:').place(x=10, y=50)
    tkinter.Entry(window_add, textvariable=phone_new1).place(x=130, y=50)
    # 手机号2
    phone_new2 = tkinter.StringVar()
    tkinter.Label(window_add, text='新联系人手机号2:').place(x=10, y=90)
    tkinter.Entry(window_add, textvariable=phone_new2).place(x=130, y=90)
    # 邮箱
    mail_new = tkinter.StringVar()
    tkinter.Label(window_add, text='新联系人邮      箱:').place(x=10, y=130)
    tkinter.Entry(window_add, textvariable=mail_new).place(x=130, y=130)
    # 地址
    adr_new = tkinter.StringVar()
    tkinter.Label(window_add, text='新联系人地      址:').place(x=10, y=170)
    tkinter.Entry(window_add, textvariable=adr_new).place(x=130, y=170)
    # 确认
    confirm_button = tkinter.Button(window_add, text='确认添加', font=('宋体', 15))
    confirm_button.bind("<Button-1>", add_man)
    confirm_button.place(x=100, y=200)

# 删除
def del_def(event):
    name = man_name.get()
    file = open("通讯录.txt", mode='r+', encoding='utf-8')
    if len(file.read()) != 0:
        file.seek(0, 0)
        file_data = file.read()
        split_info = file_data.split('\n')
        split_info.remove(split_info[len(split_info) - 1])
        name_li = []
        all_info_li = []
        for i in split_info:
            dict_info = json.loads(i.replace("\'", '\"'))
            all_info_li.append(dict_info)
            name_li.append(dict_info['姓名'])
        if name in name_li:
            通讯录_copy = open('通讯录_copy.txt', mode='w+', encoding="utf-8")
            for person_info in all_info_li:
                if name not in str(person_info):
                    通讯录_copy.write(str(person_info) + '\n')
            通讯录_copy.close()
            file.close()
            os.rename('通讯录.txt', '通讯录_del.txt')
            os.rename('通讯录_copy.txt', '通讯录.txt')
            os.remove('通讯录_del.txt')
            tkinter.messagebox.showinfo('消息提示', f'删除“{name}“成功!')
            showinfo()
        else:
            tkinter.messagebox.showinfo('消息提示', '查无此人!')


# 修改
def update_def(event):
    def update_man(event):
        name = name_new.get()
        phone1 = phone_new1.get()
        phone2 = phone_new2.get()
        mail = mail_new.get()
        address = address_new.get()
        if name != "" and phone1 != "" and phone2 != "" and mail != "" and address != "":
            通讯录_copy = open('通讯录_copy.txt', mode='w', encoding="utf-8")
            # 将数据封装到字典中
            card_dict = {"姓名": name, "手机号1": phone1, "手机号2": phone2,
                         "邮箱": mail, "地址": address}
            for person_info in all_info_li:
                if name_old in str(person_info):
                    person_info = str(card_dict)
                通讯录_copy.write(str(person_info) + '\n')
            通讯录_copy.close()
            file.close()
            os.rename('通讯录.txt', '通讯录_del.txt')
            os.rename('通讯录_copy.txt', '通讯录.txt')
            os.remove('通讯录_del.txt')
            tkinter.messagebox.showinfo('消息提示框', '更新成功!')
            showinfo()
        else:
            tkinter.messagebox.showinfo('消息提示框', '请输入正确信息,或输入完整信息')
    name_old = man_name.get()
    file = open("通讯录.txt", mode='r+', encoding='utf-8')
    if len(file.read()) != 0:
        file.seek(0, 0)
        file_data = file.read()
        split_info = file_data.split('\n')
        split_info.remove(split_info[len(split_info) - 1])
        name_li = []
        all_info_li = []
        for i in split_info:
            dict_info = json.loads(i.replace("\'", '\"'))
            all_info_li.append(dict_info)
            name_li.append(dict_info['姓名'])
        if name_old in name_li:
            window_update = tkinter.Toplevel(root)
            window_update.geometry('280x200')
            # 输入更新的信息
            name_new = tkinter.StringVar()
            phone_new1 = tkinter.StringVar()
            phone_new2 = tkinter.StringVar()
            mail_new = tkinter.StringVar()
            address_new = tkinter.StringVar()
            # 输入更改后的信息
            tkinter.Label(window_update, text='姓      名:').place(x=20, y=0)
            tkinter.Entry(window_update, textvariable=name_new).place(x=90, y=0)
            tkinter.Label(window_update, text='手机号1:').place(x=20, y=30)
            tkinter.Entry(window_update, textvariable=phone_new1).place(x=90, y=30)
            tkinter.Label(window_update, text='手机号2:').place(x=20, y=60)
            tkinter.Entry(window_update, textvariable=phone_new2).place(x=90, y=60)
            tkinter.Label(window_update, text='邮      箱:').place(x=20, y=90)
            tkinter.Entry(window_update, textvariable=mail_new).place(x=90, y=90)
            tkinter.Label(window_update, text='地      址:').place(x=20, y=120)
            tkinter.Entry(window_update, textvariable=address_new).place(x=90, y=120)
            # 确认
            confirm_button = tkinter.Button(window_update, text='确认修改', font=('宋体', 15))
            confirm_button.bind("<Button-1>", update_man)
            confirm_button.place(x=100, y=150)
        else:
            tkinter.messagebox.showinfo('消息提示', '通讯录中查无此人')

# 查找
def find_def(event):
    name = man_name.get()
    file = open("通讯录.txt", mode='r+', encoding='utf-8')
    if len(file.read()) != 0:
        file.seek(0, 0)
        file_data = file.read()
        split_info = file_data.split('\n')
        split_info.remove(split_info[len(split_info) - 1])
        name_li = []
        all_info_li = []
        for i in split_info:
            dict_info = json.loads(i.replace("\'", '\"'))
            all_info_li.append(dict_info)
            name_li.append(dict_info['姓名'])
        if name in name_li:
            man_find = tkinter.Toplevel(root)
            man_find.geometry('400x140')
            for person_info in all_info_li:
                if name in str(person_info):
                    for title, info_value in person_info.items():
                        tem_text = title + ":" + info_value + " " *10
                        tktest = tkinter.Label(man_find, text=tem_text, font=('宋体', 15))
                        tktest.pack(side="top", anchor='w')
        else:
            tkinter.messagebox.showinfo('消息提示', '通讯录中查无此人')

# 退出
def over_def(event):
    root.destroy()

# 输入框
man_nametitle = tkinter.Label(root,text="请输入联系人姓名:", font=('宋体', 10)).place(x=40,y=358)#x为距离左侧距离
man_name = tkinter.StringVar()
tkinter.Entry(root, textvariable=man_name, width=28).place(x=160, y=355)#输入框位置

# 联系人名单标题
man_nametitle = tkinter.Label(root,text="联系人名单列表", font=('宋体', 20, 'bold')).place(x=180,y=35)
man_nametitle = tkinter.Label(root,text="-"*150,).place(x=0,y=10)

#属性标题
man_nametitle = tkinter.Label(root,text="姓名    手机号1    手机号2      邮箱   地址", font=('宋体', 15, 'bold')).place(x=44,y=75)
man_nametitle = tkinter.Label(root,text="-"*150,).place(x=0,y=10)
# 添加
button1 = tkinter.Button(root, text="添加联系人", width=10, height=2,font=('宋体', 15))
button1.place(x=40, y=400)
button1.bind('<Button-1>', add_def)

# 删除
button3 = tkinter.Button(root, text="删除联系人", width=10,height=2, font=('宋体', 15))
button3.place(x=280, y=400)
button3.bind('<Button-1>', del_def)

# 修改
button4 = tkinter.Button(root, text="修改联系人", width=10,height=2, font=('宋体', 15))
button4.place(x=160, y=400)
button4.bind('<Button-1>', update_def)

# 查找
button5 = tkinter.Button(root, text="查找联系人", width=10, font=('宋体', 15))
button5.place(x=398, y=350)
button5.bind('<Button-1>', find_def)

# 退出
button6 = tkinter.Button(root, text="退出", width=10, height=2, font=('宋体', 15))
button6.place(x=398, y=400)
button6.bind('<Button-1>', over_def)

root.mainloop()  # 显示主窗体

5 实验结果

6 实验总结

本实验设计过程可以说是历经各种坎坷,代码量也挺多,共293行(含注释)。

其中文件的存入及读取操作设计部分,由于本人采用字典操作,故需要特别转化进行存入与读取,涉及到的算法经过不断调试最终成功通过。

在可视化设计过程中,考虑了多项因素,包括按钮,文本框的布局、函数的内部传递参数、特殊情况的程序处理、菜单的设计等等。菜单设计过程中,通过查找大量的博客、阅读他人代码学习到传递位置参量的设计方法并在此次设计实验中得到了应用与体现。而在界面设计过程中,由于python中可调布局的函数(pack(),grid(),place()仅有三种,同时函数的使用需要输入不同的参数,控制其显示位置,故在此过程中,本人为使得到的界面尽量精美,经过了一个漫长的布局过程。不过仍有不足的地方无法调试,例如修改弹窗中各个按钮及文本框的布局中仍有一些空白部分无法填充。

总体过程虽有一些坎坷,好在尽力都解决了已知的问题。此次设计实验充分锻炼了自己调用tkinter模块设计布局的能力,有一个显著的提升,同时也发现,用python环境做可视化设计确实略有一些困难,其内部的一些模块函数还需要持续更新,设计布局函数方面不够人性化,也不够便捷。不过本人为充分锻炼自己的python编程能力,披荆斩棘,也算取得胜利。

物联沃分享整理
物联沃-IOTWORD物联网 » Python tkinter实现图形界面通讯录,使用txt文本存储数据

发表评论