一、问题描述


(一) 开发要求

1. 采用数据文件保存数据,而不是每次重新输入数据。

2. 实现基本的查询、插入、删除、修改功能,在此基础上可增加高级操作 (例如排序、统计等)。

3. 交互界面设计要直观体现系统功能选择,并可循环调用。


(二)设计要求

设计并实现一个人事管理系统,需要实现基本的信息添加、删除、查询、修改以及显示,并将所有信息存储在数据文件中。通过上述的开发要求,做如下的设计:

1. 可以将职员信息添加在数据文件中;

2. 可以进行单独或者整体的职员信息删除操作;

3. 可以通过编号或者姓名对职员信息进行查找;

4. 可以修改职员的信息并将其存储在数据文件中;

5. 可以统计并显示总体的职员人数。


(三)系统开发语言及开发环境的介绍

本项目开发是基于python的GUI界面进行开发,开发语言为python语言。系统运行要求为Windows7以上,开发利用的编程环境为PyCharm 2020.1.2 x64版本。

代码实现:

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 = 500
        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.number)

        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.name)

        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.sex)

        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.birth)

        l2 = Label(top, text="入职时间:", font=m_font)
        l2.pack(side=TOP, pady=5)
        self.e5 = Entry(top)
        self.e5.pack(side=TOP, padx=16, ipady=3, fill=X)
        if contact is not None:
            self.e5.insert(0, contact.entry_time)

        l2 = Label(top, text="职位:", font=m_font)
        l2.pack(side=TOP, pady=5)
        self.e6 = Entry(top)
        self.e6.pack(side=TOP, padx=16, ipady=3, fill=X)
        if contact is not None:
            self.e6.insert(0, contact.position)

        l2 = Label(top, text="工资:", font=m_font)
        l2.pack(side=TOP, pady=5)
        self.e7 = Entry(top)
        self.e7.pack(side=TOP, padx=16, ipady=3, fill=X)
        if contact is not None:
            self.e7.insert(0, contact.salary)

        if contact is None:
            b2 = Button(top, text='添加', width=12, command=lambda: self.add_click(None))
            self.e7.bind('<Return>', self.add_click)
        else:
            b2 = Button(top, text='编辑', width=12, command=lambda: self.edit_click(None))
            self.e7.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_number= self.e2.get()
        if not e1_number:
            messagebox.showinfo("出错了", '名字不能为空!')
            return
        e2_name = self.e3.get()
        if not e2_name:
            messagebox.showinfo("出错了", '性别不能为空!')
            return
        e3_name = self.e3.get()
        e4_name = self.e4.get()
        e5_name = self.e5.get()
        e6_name = self.e6.get()
        e7_name = self.e7.get()
        self.main_window.edit_value(e1_number, e2_name, e3_name, e4_name, e5_name, e6_name, e7_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()
        e5_name = self.e5.get()
        e6_name = self.e6.get()
        e7_name = self.e7.get()
        self.main_window.add_value(e1_name, e2_name, e3_name, e4_name, e5_name, e6_name, e7_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=10, command=lambda: self.popup("添加职员信息"))
        self.addBtn.pack(padx=5, fill=X, side=LEFT)
        self.delAllBtn = Button(frame, text='删除所有信息', width=10, command=self.del_all_contacts)
        self.delAllBtn.pack(padx=5, fill=X, side=LEFT)
        self.saveAllBtn = Button(frame, text='保存所有信息', width=10, 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"
        assert isinstance(LEFT, object)
        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.sex if len(contact.sex) != 0 else '空'
        em = contact.birth if len(contact.birth) != 0 else '空'
        ab=contact.entry_time if len(contact.entry_time) != 0 else '空'
        lh=contact.position if len(contact.position) != 0 else '空'
        mn=contact.salary if len(contact.salary) != 0 else '空'
        msg = '编号:%s\n姓名:%s\n性别:%s\n出生日期:%s\n入职时间:%s\n职位:%s\n工资:%s\n' % (contact.number, contact.name, wp, em,ab,lh,mn)
        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 = '{},{},{},{}\n'.format(contact.number, contact.name, contact.sex, contact.birth, contact.entry_time,
                                         contact.position, contact.salary)
            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], array[4], array[5], array[6])
            self.contacts.append(contact)
            self.lb.insert(END, '编号:%s 姓名:%s性别:%s出生日期:%s入职时间:%s职位:%s工资:%s' % (contact.number, contact.name,contact.sex, contact.birth, contact.entry_time, contact.position, contact.salary))
        self.label.config(text='系统现有 %d 位职员' % len(self.contacts))
        f.close()

    # 添加职员信息回调
    def add_value(self, number, name, sex, birth, entry_time, position, salary):
        contact = Contact(number, name, sex, birth, entry_time, position, salary)
        self.contacts.append(contact)
        self.lb.insert(END, '编号:%s 姓名:%s性别:%s出生日期:%s入职时间:%s职位:%s工资:%s' % (contact.number, contact.name,contact.sex, contact.birth, contact.entry_time, contact.position, contact.salary))
        self.label.config(text='系统现有 %d 位职员' % len(self.contacts))

    # 编辑职员信息回调
    def edit_value(self, number, name, sex, birth, entry_time, position, salary):
        contact = self.contacts[self.sel_item]
        contact.number = number
        contact.name = name
        contact.sex = sex
        contact.birth = birth
        contact.entry_time = entry_time
        contact.position = position
        contact.salary = salary
        self.lb.delete(0, END)
        for contact in self.contacts:
            self.lb.insert(END, '编号:%s 姓名:%s性别:%s出生日期:%s入职时间:%s职位:%s工资:%s' % (contact.number, contact.name,contact.sex, contact.birth, contact.entry_time, contact.position, contact.salary))
        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.number) or (key in contact.name):
                self.lb.insert(END, '编号:%s 姓名:%s性别:%s出生日期:%s入职时间:%s职位:%s工资:%s' % (contact.number, contact.name,contact.sex, contact.birth, contact.entry_time, contact.position, contact.salary))
                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 姓名:%s性别:%s出生日期:%s入职时间:%s职位:%s工资:%s' % (contact.number, contact.name,contact.sex, contact.birth, contact.entry_time, contact.position, contact.salary))
            self.label.config(text='系统现有 %d 位职员' % len(self.contacts))
            self.input_view.selection_range(0, len(self.input_view.get()))


# 职员类对象
class Contact:
    def __init__(self, number: object, name: object, sex: object, birth: object, entry_time: object, position: object,
                 salary: object ) -> object:
        """

        :rtype: object
        """
        self.number = number
        self.name = name
        self.sex = sex
        self.birth = birth
        self.entry_time = entry_time
        self.position = position
        self.salary = salary


# 程序启动入口
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()

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐