You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
335 lines
13 KiB
335 lines
13 KiB
|
|
from tkinter import *
|
|
from tkinter import font
|
|
from tkinter import messagebox
|
|
from os import path
|
|
from os import remove
|
|
|
|
|
|
# 添加、编辑联系人弹出框类
|
|
class PopupWindow(object):
|
|
# 初始化构造及添加组件到弹出框
|
|
def __init__(self, master, main_window, title, contact=None):
|
|
self.main_window = main_window
|
|
top = self.top = Toplevel(master)
|
|
top.title(title)
|
|
top.resizable(False, False)
|
|
w = 280
|
|
h = 320
|
|
top.geometry('%dx%d+%d+%d' % (w, h, (ws - w) / 2, (hs - h) / 2))
|
|
top.bind('<Escape>', lambda event: top.destroy())
|
|
|
|
m_font = font.Font(size=16)
|
|
l = Label(top, text="姓名:", font=m_font)
|
|
l.pack(side=TOP, pady=5)
|
|
self.e1 = Entry(top)
|
|
self.e1.pack(side=TOP, padx=16, ipady=3, fill=X)
|
|
self.e1.focus()
|
|
if contact is not None:
|
|
self.e1.insert(0, contact.name)
|
|
|
|
l2 = Label(top, text="电话号码:", font=m_font)
|
|
l2.pack(side=TOP, pady=5)
|
|
self.e2 = Entry(top)
|
|
self.e2.pack(side=TOP, padx=16, ipady=3, fill=X)
|
|
if contact is not None:
|
|
self.e2.insert(0, contact.phone_number)
|
|
|
|
l2 = Label(top, text="工作单位:", font=m_font)
|
|
l2.pack(side=TOP, pady=5)
|
|
self.e3 = Entry(top)
|
|
self.e3.pack(side=TOP, padx=16, ipady=3, fill=X)
|
|
if contact is not None:
|
|
self.e3.insert(0, contact.work_place)
|
|
|
|
l2 = Label(top, text="地址:", font=m_font)
|
|
l2.pack(side=TOP, pady=5)
|
|
self.e4 = Entry(top)
|
|
self.e4.pack(side=TOP, padx=16, ipady=3, fill=X)
|
|
if contact is not None:
|
|
self.e4.insert(0, contact.address)
|
|
|
|
if contact is None:
|
|
b2 = Button(top, text='添加', width=12, command=lambda: self.add_click(None))
|
|
self.e4.bind('<Return>', self.add_click)
|
|
else:
|
|
b2 = Button(top, text='编辑', width=12, command=lambda: self.edit_click(None))
|
|
self.e4.bind('<Return>', self.edit_click)
|
|
b2.pack(side=LEFT, pady=10, padx=20)
|
|
b3 = Button(top, text='取消', width=12, command=lambda: top.destroy())
|
|
b3.pack(side=RIGHT, pady=10, padx=20)
|
|
|
|
top.grab_set()
|
|
|
|
# 点击编辑联系人按钮
|
|
def edit_click(self, event):
|
|
e1_name = self.e1.get()
|
|
if not e1_name:
|
|
messagebox.showinfo("出错了", '名字不能为空!')
|
|
return
|
|
e2_name = self.e2.get()
|
|
if not e2_name:
|
|
messagebox.showinfo("出错了", '电话号码不能为空!')
|
|
return
|
|
e3_name = self.e3.get()
|
|
e4_name = self.e4.get()
|
|
self.main_window.edit_value(e1_name, e2_name, e3_name, e4_name)
|
|
self.top.destroy()
|
|
|
|
# 点击添加联系人按钮
|
|
def add_click(self, event):
|
|
e1_name = self.e1.get()
|
|
if not e1_name:
|
|
messagebox.showinfo("出错了", '名字不能为空!')
|
|
return
|
|
e2_name = self.e2.get()
|
|
if not e2_name:
|
|
messagebox.showinfo("出错了", '电话号码不能为空!')
|
|
return
|
|
e3_name = self.e3.get()
|
|
e4_name = self.e4.get()
|
|
self.main_window.add_value(e1_name, e2_name, e3_name, e4_name)
|
|
self.top.destroy()
|
|
|
|
|
|
# 主界面类
|
|
class MainWindow(object):
|
|
# 默认初始化构造
|
|
def __init__(self, root):
|
|
self.contacts = []
|
|
self.root = root
|
|
self.add_btn_widget()
|
|
self.add_search_widget()
|
|
self.add_listbox_widget()
|
|
self.add_statusbar_widget()
|
|
self.read_save_contacts()
|
|
self.sel_item = 0
|
|
|
|
# 添加操作按钮
|
|
def add_btn_widget(self):
|
|
frame = Frame(self.root)
|
|
frame.pack(pady=8)
|
|
self.addBtn = Button(frame, text='添加联系人', width=15, command=lambda: self.popup("添加联系人"))
|
|
self.addBtn.pack(padx=5, fill=X, side=LEFT)
|
|
self.delAllBtn = Button(frame, text='删除所有联系人', width=15, command=self.del_all_contacts)
|
|
self.delAllBtn.pack(padx=5, fill=X, side=LEFT)
|
|
self.saveAllBtn = Button(frame, text='保存所有联系人', width=15, command=self.save_all_contacts)
|
|
self.saveAllBtn.pack(padx=5, fill=X, side=LEFT)
|
|
|
|
# 添加搜索框
|
|
def add_search_widget(self):
|
|
frame = Frame(self.root)
|
|
frame.pack(pady=8)
|
|
entry1 = self.input_view = Entry(frame, width=34)
|
|
entry1.insert(0, '输入部分姓名或电话号码按回车查询')
|
|
entry1.bind("<Button-1>", self.click_input)
|
|
entry1.bind("<FocusOut>", self.focusout_input)
|
|
entry1.bind('<Return>', self.search_contact)
|
|
entry1.bind('<Escape>', self.cancel_search)
|
|
entry1.pack(ipady=3, padx=5, side=LEFT)
|
|
entry1.selection_range(0, len(entry1.get()))
|
|
entry1.focus()
|
|
command4 = self.search_btn = Button(frame, text='清空输入', width=15, command=lambda: self.cancel_search(None))
|
|
command4["state"] = "disabled"
|
|
command4.pack(padx=5, side=LEFT)
|
|
|
|
# 点击输入框清空内容
|
|
def click_input(self, event):
|
|
if self.input_view.get() == '输入部分姓名或电话号码按回车查询':
|
|
self.input_view.delete(0, END)
|
|
|
|
# 输入框失去焦点时
|
|
def focusout_input(self, event):
|
|
if len(self.input_view.get()) == 0:
|
|
self.input_view.insert(0, '输入部分姓名或电话号码按回车查询')
|
|
|
|
# 添加列表及滚动条
|
|
def add_listbox_widget(self):
|
|
frame = Frame(self.root)
|
|
frame.pack(pady=8)
|
|
bolded = font.Font(size=20)
|
|
self.lb = Listbox(frame, font=bolded, height=14, width=25, borderwidth=0)
|
|
scrollbar = Scrollbar(frame, orient=VERTICAL)
|
|
scrollbar.config(command=self.lb.yview)
|
|
scrollbar.pack(side=RIGHT, fill=Y)
|
|
self.lb.config(yscrollcommand=scrollbar.set, activestyle='none')
|
|
scrollbar2 = Scrollbar(frame, orient=HORIZONTAL)
|
|
scrollbar2.config(command=self.lb.xview)
|
|
scrollbar2.pack(side=BOTTOM, fill=X)
|
|
self.lb.config(xscrollcommand=scrollbar2.set, activestyle='none')
|
|
self.lb.pack(fill=BOTH)
|
|
self.lb.bind('<Double-1>', self.dbclick)
|
|
self.lb.bind('<Button-3>', self.rclick_popup)
|
|
|
|
# 添加界面底部联系人数
|
|
def add_statusbar_widget(self):
|
|
frame = Frame(self.root)
|
|
frame.pack(pady=8, side=LEFT)
|
|
self.label = Label(frame, text='>系统现有 0 位联系人<')
|
|
self.label.pack()
|
|
|
|
# 右键菜单
|
|
def rclick_popup(self, event):
|
|
a_menu = Menu(self.root, tearoff=0)
|
|
a_menu.add_command(label='编辑选中的联系人', command=self.edit_contact)
|
|
a_menu.add_command(label='删除选中的联系人', command=self.del_contact)
|
|
a_menu.post(event.x_root, event.y_root)
|
|
|
|
# 右键编辑选中的联系人
|
|
def edit_contact(self):
|
|
selection = self.lb.curselection()
|
|
if len(selection) == 0:
|
|
messagebox.showerror("出错了", '请先左键选中待操作的联系人!')
|
|
return
|
|
self.sel_item = selection[0]
|
|
self.right_clidk_reset()
|
|
contact = self.contacts[self.sel_item]
|
|
self.popup("编辑联系人", contact=contact)
|
|
|
|
# 右键删除选中的联系人
|
|
def del_contact(self):
|
|
selection = self.lb.curselection()
|
|
if len(selection) == 0:
|
|
messagebox.showerror("出错了", '请先左键选中待操作的联系人!')
|
|
return
|
|
self.right_clidk_reset()
|
|
answer = messagebox.askyesno("提示", "您确定要删除此联系人吗?")
|
|
if answer:
|
|
self.lb.delete(self.sel_item, self.sel_item)
|
|
self.contacts.pop(self.sel_item)
|
|
self.label.config(text='系统现有 %d 位联系人' % len(self.contacts))
|
|
messagebox.showinfo('提示', '联系人从列表删除成功!\n若需要保存操作结果,请点击“保存所有联系人”')
|
|
|
|
# 若是搜索后右键,则操作重置列表
|
|
def right_clidk_reset(self, is_dbclick=False):
|
|
b_text = self.search_btn["state"]
|
|
if b_text == "normal":
|
|
ic = -1
|
|
item = self.lb.selection_get()
|
|
if not is_dbclick:
|
|
self.cancel_search(None)
|
|
for ct in self.contacts:
|
|
ic += 1
|
|
if (ct.name in item) and (ct.phone_number in item):
|
|
break
|
|
self.sel_item = ic
|
|
self.lb.selection_set(ic, ic)
|
|
|
|
# 双击联系人条目
|
|
def dbclick(self, event):
|
|
selection = self.lb.curselection()
|
|
self.sel_item = selection[0]
|
|
self.right_clidk_reset(is_dbclick=True)
|
|
contact = self.contacts[self.sel_item]
|
|
wp = contact.work_place if len(contact.work_place) != 0 else '空'
|
|
ad = contact.address if len(contact.address) != 0 else '空'
|
|
msg = '姓名:%s\n电话:%s\n工作单位:%s\n地址:%s' % (contact.name, contact.phone_number, wp, ad)
|
|
messagebox.showinfo("详细信息", msg)
|
|
|
|
# 添加、编辑联系人弹窗
|
|
def popup(self, title, contact=None):
|
|
self.cancel_search(None)
|
|
self.w = PopupWindow(self.root, self, title, contact)
|
|
self.addBtn["state"] = "disabled"
|
|
self.root.wait_window(self.w.top)
|
|
self.addBtn["state"] = "normal"
|
|
|
|
# 删除所有联系人
|
|
def del_all_contacts(self):
|
|
self.cancel_search(None)
|
|
answer = messagebox.askyesno("提示", "您确定要删除所有联系人吗?")
|
|
if answer:
|
|
self.contacts.clear()
|
|
self.lb.delete(0, END)
|
|
remove("contacts.csv")
|
|
self.label.config(text='系统现有 %d 位联系人' % len(self.contacts))
|
|
|
|
# 保存联系人到文件
|
|
def save_all_contacts(self):
|
|
self.cancel_search(None)
|
|
f = open("contacts.csv", "w", encoding='utf-8')
|
|
for contact in self.contacts:
|
|
str = '%s,%s,%s,%s\n' % (contact.name, contact.phone_number, contact.work_place, contact.address)
|
|
f.write(str)
|
|
f.close()
|
|
messagebox.showinfo('提示', '保存 %d 位联系人到文件成功!' % len(self.contacts))
|
|
|
|
# 读取保存在文件的联系人
|
|
def read_save_contacts(self):
|
|
if not path.exists('contacts.csv'):
|
|
return
|
|
f = open("contacts.csv", "r", encoding='utf-8')
|
|
for line in f:
|
|
array = line.strip().split(',')
|
|
contact = Contact(array[0], array[1], array[2], array[3])
|
|
self.contacts.append(contact)
|
|
self.lb.insert(END, '%s Tel:%s' % (contact.name, contact.phone_number))
|
|
self.label.config(text='系统现有 %d 位联系人' % len(self.contacts))
|
|
f.close()
|
|
|
|
# 添加联系人回调
|
|
def add_value(self, name, phone_number, work_place, address):
|
|
contact = Contact(name, phone_number, work_place, address)
|
|
self.contacts.append(contact)
|
|
self.lb.insert(END, '%s Tel:%s' % (name, phone_number))
|
|
self.label.config(text='系统现有 %d 位联系人' % len(self.contacts))
|
|
|
|
# 编辑联系回调
|
|
def edit_value(self, name, phone_number, work_place, address):
|
|
contact = self.contacts[self.sel_item]
|
|
contact.name = name
|
|
contact.phone_number = phone_number
|
|
contact.work_place = work_place
|
|
contact.address = address
|
|
self.lb.delete(0, END)
|
|
for contact in self.contacts:
|
|
self.lb.insert(END, '%s Tel:%s' % (contact.name, contact.phone_number))
|
|
self.label.config(text='系统现有 %d 位联系人' % len(self.contacts))
|
|
|
|
# 搜索联系人方法
|
|
def search_contact(self, event):
|
|
self.search_btn["state"] = "normal"
|
|
self.lb.delete(0, END)
|
|
key = self.input_view.get().strip()
|
|
ci = 0
|
|
for contact in self.contacts:
|
|
if (key in contact.name) or (key in contact.phone_number):
|
|
self.lb.insert(END, '%s Tel:%s' % (contact.name, contact.phone_number))
|
|
ci += 1
|
|
self.label.config(text='查询到 %d 位联系人' % ci)
|
|
|
|
# 取消搜索
|
|
def cancel_search(self, event):
|
|
b_state = self.search_btn["state"]
|
|
if b_state == "normal":
|
|
self.search_btn["state"] = "disabled"
|
|
self.lb.delete(0, END)
|
|
self.input_view.delete(0, END)
|
|
self.input_view.insert(0, '输入部分姓名或电话号码按回车查询')
|
|
for contact in self.contacts:
|
|
self.lb.insert(END, '%s Tel:%s' % (contact.name, contact.phone_number))
|
|
self.label.config(text='系统现有 %d 位联系人' % len(self.contacts))
|
|
self.input_view.selection_range(0, len(self.input_view.get()))
|
|
|
|
|
|
# 联系人类对象
|
|
class Contact:
|
|
def __init__(self, name, phone_number, work_place, address):
|
|
self.name = name
|
|
self.phone_number = phone_number
|
|
self.work_place = work_place
|
|
self.address = address
|
|
|
|
|
|
# 程序启动入口
|
|
if __name__ == "__main__":
|
|
root = Tk()
|
|
root.wm_resizable(False, False)
|
|
root.title('通讯录管理系统')
|
|
w = 380
|
|
h = 560
|
|
ws = root.winfo_screenwidth()
|
|
hs = root.winfo_screenheight()
|
|
root.geometry('%dx%d+%d+%d' % (w, h, (ws - w) / 2, (hs - h) / 2))
|
|
m = MainWindow(root)
|
|
root.mainloop() |