此代码用于员工管理系统。我试图达到的目标是使管理人员更轻松地访问其员工的数据。输入的数据将保存到CSV文件中,并且我拥有合适的系统,这些管理器将使那些经理可以通过快速搜索文件来访问其员工的数据。我16岁,是个初学者,请向大家解释一些大家推荐的简洁代码概念。

'''
Employee System 

By Ronald Colyar : 1/2/2018


'''
#our modules for this project
import csv
from tkinter import *
from tkinter import ttk



#main class gui
class employee_gui :


    def __init__(self , master):

        self.master = master

        #configuring the title of the main window , aswell as the background color
        master.title(string = 'Pha<n>tex Employee Management System')
        master.configure(background = 'black')

        #employee first name
        self.firstname_label = Label(master, text = 'Employee First Name ***' ,bg= 'black', fg ='white')
        self.firstname_label.grid(row = 1 , column = 0 , sticky ='we', padx=5, pady=5)



        self.first_name = ttk.Entry(master)
        self.first_name.grid(row = 2 ,column  = 0 , sticky = 'we',padx=5, pady=5 )

        #employee lastname
        self.lastname_label = Label(master, text = 'Employee last Name ***',bg= 'black', fg ='white')
        self.lastname_label.grid(row = 1 , column = 1 , sticky ='we',padx=5, pady=5)


        self.last_name = ttk.Entry(master)
        self.last_name.grid(row = 2 ,column  = 1 , sticky = 'we',padx=5, pady=5)

        #employee email 
        self.employee_email = Label(master, text = 'Employee Email ***',bg= 'black', fg ='white')
        self.employee_email.grid(row = 3 , column  = 0 , columnspan = 2 , sticky = 'we')

        self.employee_email_entry = ttk.Entry(master)
        self.employee_email_entry.grid(row = 4  , column = 0 , columnspan = 3 , sticky = 'we', padx=5, pady=5)







                                                                   #day
        self.DAY= Label(master, text = 'Day**',bg= 'black', fg ='white')
        self.DAY.grid(row = 5 , column = 0 , sticky = 'we',padx=5, pady=5)
        #day options
        self.dayoptions = ['1', '2' , '3' , '4' , '5' , '6' , '7' , '8' , '9', '10' , '11' , '12' , '13' , '14' , '15' , '16' , '17' , '18' , '19', '20' , '21' , '22' , '23' , '24' , '25' , '26' , '27' , '28' , '29' , '30' , '31']
        #day container
        self.dayvar= StringVar()
        self.dayvar.set('none')
        #day option menu
        self.dropdown_day = ttk.OptionMenu(master,self.dayvar , *self.dayoptions)
        self.dropdown_day.grid(row = 6 , column = 0 , sticky= 'we')



                                                                     #month    
        self.month= Label(master, text = 'month**',bg= 'black', fg ='white')
        self.month.grid(row = 5 , column = 1 , sticky = 'we',padx=5, pady=5)
        #month options
        self.monthoptions = ['1' , '2' , '3' , '4' , '5' , '6' , '7' , '8' , '9' , '10' , '11' , '12']

        #month container
        self.monthvar = StringVar()
        self.monthvar.set('select')

        #MONTH DROPDOWN MENU
        self.dropdown_month = ttk.OptionMenu(master, self.monthvar , *self.monthoptions)
        self.dropdown_month.grid(row = 6 , column = 1 , sticky = 'we',padx=5, pady=5)


                                                                         # year



        #year options
        self.years_unsplit       =  '2029 - 2028 - 2027 - 2026 - 2025 - 2024 - 2023 - 2022 - 2021 -2020 - 2019 - 2018 - 2017 - 2016 - 2015 - 2014 - 2013 - 2012 - 2011 -2010 - 2009 - 2008 - 2007 - 2006 - 2005 - 2004 - 2003 - 2002 - 2001 -2000 - 1999 - 1998 - 1997 - 1996 - 1995 - 1994 - 1993 - 1992 - 1991 - 1990 - 1989 - 1988 - 1987 - 1986 - 1985 - 1984 - 1983 - 1982 - 1981 -1980 - 1979 - 1978 - 1977 - 1976 - 1975 - 1974 - 1973 - 1972 - 1971 -1970 - 1969 - 1968 - 1967 - 1966 - 1965 - 1964 - 1963 - 1962 - 1961 -1960 - 1959 - 1958 - 1957 - 1956 - 1955 - 1954 - 1953 - 1952 - 1951 -1950 - 1949 - 1948 - 1947 - 1946 - 1945 - 1944 - 1943 - 1942 - 1941 -1940 - 1939 - 1938 - 1937 - 1936 - 1935 - 1934 - 1933 - 1932 - 1931 -1930 - 1929 - 1928 - 1927 - 1926 - 1925 - 1924 - 1923 - 1922 - 1921'

        self.yearoptions2 = self.years_unsplit.split('-')
        #year variable
        self.yearvar = StringVar()

        self.dropdown_year = ttk.OptionMenu(master , self.yearvar , *self.yearoptions2)
        self.dropdown_year.grid(row = 6 ,column = 2 , sticky = 'we')

        self.year = Label(master, text = 'Year**' ,bg= 'black', fg ='white')
        self.year.grid(row = 5 , column = 2, sticky = 'we', padx=5, pady=5)





        #the address section
        self.Address = Label(master ,text = 'Address(optional)*** Example: 110 s. grove street',bg= 'black', fg ='white')
        self.Address.grid(row = 7 , column = 0 ,sticky = 'we',padx=5, pady=5)
        self.Address_entry = ttk.Entry(master)
        self.Address_entry.grid(row = 8 , column = 0 , columnspan = 3, sticky = 'we',padx=5, pady=5)


        #the position_ occupation section
        self.Position = Label(master , text= 'Position/Occupation***' , bg= 'black', fg ='white')
        self.Position.grid(row = 9 , column = 0 , sticky = 'we',padx=5, pady=5)
        self.Position_entry = ttk.Entry(master)
        self.Position_entry.grid(row = 10 , column = 0 ,columnspan = 3, sticky = 'we',padx=5, pady=5)


        #thesalary section
        self.salary = Label(master , text = 'Employee Salary***',bg= 'black', fg ='white')
        self.salary.grid(row = 11 , column = 0 , sticky = 'we',padx=5, pady=5)

        self.salary_entry = ttk.Entry(master)
        self.salary_entry.grid(row = 12 , column = 0 , sticky = 'we',padx=5, pady=5)

        #the employee id informaiton


        self.employee_id_label = Label(master, text = 'Employee Id **VERY IMPORTANT**',bg= 'black', fg ='white')
        self.employee_id_label.grid(row = 13 , column = 0 , sticky = 'we',padx=5, pady=5)

        self.employee_id_entry = ttk.Entry(master)
        self.employee_id_entry.grid(row = 14 , column = 0 , sticky = 'we' ,padx=5, pady=5 )


        #the main menu bar

        self.titlebaroptions = Menu(master)
        #the file section in the menu bar
        self.filesystem = Menu(master,tearoff=False)
        self.filesystem.add_command(label = 'Display Employee Information'  , command =display_information_window)
        self.savesystem = Menu(master,tearoff=False)
        self.savesystem.add_command(label= 'Save Employee File' , command= save_information_window)
        self.deletesystem =Menu(master,tearoff=False)
        self.deletesystem.add_command(label = 'Delete Employee' , command = delete_information_window)
        self.help = Menu(master, tearoff = False)
        self.help.add_command(label= 'Help' , command = help_window)





        #adding the sections  to the main menu bar
        self.titlebaroptions.add_cascade(label = 'Open' , menu = self.filesystem)
        self.titlebaroptions.add_cascade(label = 'Save' , menu = self.savesystem)
        self.titlebaroptions.add_cascade(label = 'Remove Employee' , menu =self.deletesystem)
        self.titlebaroptions.add_cascade(label = 'Help' ,  menu = self.help)


        # adding main menu to the master window
        master.config(menu = self.titlebaroptions)
        #the icon for the window 
        master.iconbitmap('employeeicon.ico')

        #logos
        self.photo = PhotoImage(file = 'phantexlogo.png')
        self.phantexlogo = Label(master, image = self.photo, bg= 'black', fg ='white')
        self.phantexlogo.image = self.photo
        self.phantexlogo.grid(row = 15 , column = 0 , sticky = 'we')



        self.photo2 = PhotoImage(file = 'phantexlogo.png')
        self.phantexlogo2 = Label(master, image = self.photo, bg= 'black', fg ='white')
        self.phantexlogo2.image = self.photo
        self.phantexlogo2.grid(row = 15 , column = 1 , sticky = 'we')







def delete_information_method():
    #a tkinter entry
    global delete_entry, csv_writer1
    #opening csv in read
    with open('employees.csv' , 'r', newline='') as emp_read:
        #creating our dictreader
        csv_dictreader = csv.DictReader(emp_read)
        fieldnames = csv_dictreader.fieldnames
        contents = [line for line in csv_dictreader]
    #opening csv file in write mode
    with open ('employees.csv' , 'w', newline='') as emp_write:
        #creating our writer
        csv_writer1 = csv.DictWriter(emp_write, fieldnames=fieldnames)
        csv_writer1.writeheader()
        #our loop to check each line inside of our csv file is not equal to what is inside the delete entry
        for line in contents:
            if line['employee id'] != str(delete_entry.get()):
                csv_writer1.writerow(line)

     #grabbing all the employee data and inserting it inside of the listbox
def all_emp_search():
    global information_box
    #deleting data out of the listbox , that was previously there
    information_box.delete(0 , 'end')
    #our read file
    with open ('employees.csv' , 'r') as employee_read_file:
        #inserting all information inside of the csv file into the listbox
        for line in employee_read_file:
            information_box.insert(END , line)
    #grabbing a single employee data and inserting it inside the listbox
def single_emp_search():

    global search_label_entry,information_box
    #deleting data out of the listbox , that was previously there
    information_box.delete(0 , END)
    #our read file
    with open ('employees.csv' , 'r') as employee_read_file:
         '''

        searching to see if the contents of the csv file matches what is put inside of the entry 
        and if so inserting the line into the listbox
            '''
        for line in employee_read_file:
            if line.find(str(search_label_entry.get())) > -1:
                information_box.insert(END , line)



    #the information deletion window 

def delete_information_window():
    global delete_entry
    delete_frame = Toplevel()
    delete_frame.config(background = 'black')

    #intro label(title)
    delete_intro_header = Label(delete_frame , text = 'Welcome to the Delete section' , font = 'times 14 bold',bg= 'black', fg ='white')
    delete_intro_header.grid(row  = 2 , column = 0 , sticky = 'we')

    #The entry label/header
    delete_entry_header = Message(delete_frame , text = 'Enter in the employee ID , you would like to remove , if you dont recall , you can access the employee information , by going to Mainpage/Open/Display Employee Information, here you can search for an employee name , and all the information including the ID will be present',bg= 'black', fg ='white')
    delete_entry_header.grid(row = 3, column = 0 , sticky= 'we')


    # delete entry
    delete_note_header = Label(delete_frame , text = 'Note: Once You Delete An Employee There is no recovery , be careful with this process',bg= 'black', fg ='white')
    delete_note_header.grid(row = 4, column = 0 , sticky = 'we')

    delete_entry = ttk.Entry(delete_frame )
    delete_entry.grid(row = 5, column = 0 , sticky  = 'we')
    #the delete button(submit button)
    delete_button = Button(delete_frame , text = 'Delete Employee' ,  fg = 'white', bg = 'black',command = delete_information_method)
    delete_button.grid(row = 5 , column = 1, sticky = 'we')













#the information display window

def display_information_window():
    global search_label_entry,information_box
    display_frame = Toplevel()
    display_frame.config(background = 'black')

    #the intro message(title)
    intro_message = Label(display_frame, text = 'Welcome to the Display section' , font = 'times 14 bold',bg= 'black', fg ='white')

    #the header for the search_label_entry
    search_label = Label(display_frame , text = 'Search for one Employee information',bg= 'black', fg ='white')
    search_label.grid(row = 3 , column = 0 , sticky = 'we')
    #the search entry
    search_label_entry = ttk.Entry(display_frame)
    search_label_entry.grid(row = 4, column = 0 , sticky = 'we')



    all_information_label = Label(display_frame , text = 'All employee information',bg= 'black', fg ='white')
    all_information_label.grid(row = 3 , column =2 , sticky = 'we')

    #our listbox
    information_box = Listbox(display_frame, bd = 0 , width = 70)
    information_box.grid(row  = 6 , column = 0, sticky= 'we')
    #all information button
    all_information_button = Button(display_frame , text = 'All Information',bg = 'black' , fg = 'white',  command = all_emp_search)
    all_information_button.grid(row = 4 , column = 2 , sticky = 'we')
    #emp single search Button
    search_single_emp = Button(display_frame , text  = 'Search Single Employee', command  = single_emp_search,bg = 'black' , fg = 'white' )

    search_single_emp.grid(row = 5 , column = 2 , sticky = 'we')







#help window
def help_window():
    help_frame = Toplevel()

    intro_message = Label(help_frame, text = 'Welcome to the help section' , font = 'times 14 bold')
    intro_message.grid(row = 3  , column = 0 , sticky = 'we')

    mainmessage = Message(help_frame, text = 'The way this program works , is it allows you to  store your employee information , and go back and later access it , using the employee ID feature. The Employee ID makes accessing your information more smooth and manageable , feel free to use this program for your buisnesses and ect. Try to keep the employee IDs different for management purposes')
    mainmessage.grid(row = 4 , column = 0 , sticky  ='we')

    help_frame.iconbitmap('employeeicon.ico')



#saving the information to the csv file method
def save_information_window():
    global csvwriter
    #grabbing all the information from our entrys
    first_info = mainmenu_submit.first_name.get()
    last_info = mainmenu_submit.last_name.get()
    email_info = mainmenu_submit.employee_email_entry.get()
    day_info = mainmenu_submit.dayvar.get()
    month_info = mainmenu_submit.monthvar.get()
    year_info = mainmenu_submit.yearvar.get()
    position_info = mainmenu_submit.Position_entry.get()
    employee_salary_info = mainmenu_submit.salary_entry.get()
    employee_id_info = mainmenu_submit.employee_id_entry.get()
    adress_info = mainmenu_submit.Address_entry.get()

    #putting our information into a list of strings , 
    fieldnames_list = ['first name' , 'last name' , 'email' , 'DOB' ,'adress' ,'position' , 'employee salary' , 'employee id'  ]
    whole_information = [str(first_info) , str(last_info) , str(email_info) , str(month_info + '-' +day_info +'-' + year_info ) ,str(adress_info),  str(position_info) , str(employee_salary_info) , str(employee_id_info) ]
    #creating our dictonary , using the fieldnames as the key and the whole information as the value of the dictionary
    my_whole_info_dict = dict(zip(fieldnames_list , whole_information))

    #opening our csv file in write mode and adding the data from the entrys



    with open('employees.csv', 'a' ,newline = "") as employee_file:
                    csvwriter = csv.DictWriter(employee_file, fieldnames = fieldnames_list , delimiter = ',')



                    csvwriter.writeheader()



                    csvwriter.writerow(my_whole_info_dict)











#main window
root = Tk()

#employee_gui object    
mainmenu_submit = employee_gui(root)


#constant loop
root.mainloop()


#1 楼

欢迎使用代码审查!编写大型程序是很荣幸的。


程序中会弹出很多内容。但是,首先要注意几件事。如果使用任何智能编辑器;请查看是否可以在其中获得python linter(或PEP-8集成)。 PEP-8是python的样式指南,它使代码一致,因此可读/可维护。


我注意到您正在创建monthsdaysyears列表。使用range功能。它将为您生成整个列表。您可以稍后在列表中将每个值转换为字符串(如果tkinter需要字符串):

self.yearoptions2 = map(str, range(2029, 1921, -1))



不要依赖全局变量。随着代码库的增长;很难理解哪些全局变量引用了什么。将mapdelete_entry等值作为参数传递给您的函数。


information_box块中嵌套tkinter应用程序的执行: />

我还没有执行您的程序。但是,我还建议您不要使用出于数据库目的而维护CSV的方法,而应使用实际的DBMS。 python开箱即用,为您提供sqlite3程序包。

当您的员工记录达到1000位并且您想要过滤薪水范围或类似分析观察结果中的员工时,这将有助于提高绩效。


您可以遵循一些重要的PEP-8指针:


一条语句/代码行最多应包含80个字符

在下列情况下避免空格: >

用于表示关键字参数时,请勿在if __name__符号周围使用空格


评论


\ $ \ begingroup \ $
哇,您有很多要点,所以您认为sqlite3会更好? ,我将崇高文字3用作环境
\ $ \ endgroup \ $
–Rons辖区
18年1月10日在9:38



\ $ \ begingroup \ $
,您是否真的认为如果名称为主要陈述是必要的,还是您将其作为未来的证明?
\ $ \ endgroup \ $
–Rons辖区
18年1月10日在9:41

\ $ \ begingroup \ $
@RonComputing如果是__name__ ==“ __main__”:构造是必要的(除了看起来比不使用它还要整洁得多,因为它会立即告诉读者代码的顶层是什么)从其他地方导入您的代码。如果您不这样做,则所有代码都将在导入时执行。有了这样的子句,只有在文件本身被执行而不是被另一个文件导入的情况下,它才会被执行。
\ $ \ endgroup \ $
–桅杆
18年1月10日在9:44

\ $ \ begingroup \ $
@RonComputing packagecontrol.io/packages/Python%20PEP8%20Autoformat sqlite3只是一个建议。维护CSV很难,并且出于分析目的效率低下。由于您是初学者,因此sqlite3将是最容易学习的DBMS。
\ $ \ endgroup \ $
– hjpotter92
18年1月10日在9:48

\ $ \ begingroup \ $
@RonComputing只是谈论了一下sqlite。在“现实世界”中,大多数程序将以某种方式处理SQL,因此大多数开发人员将需要至少了解一点SQL。 sqlite是最简单的SQL数据库设置,它比MySQL或Postgres之类的轻巧得多。
\ $ \ endgroup \ $
–曼上尉
18年1月10日在14:22

#2 楼

不要进行通配符导入

使用import tkinter as tk,然后在所有tk类和命令前加上tk.(例如tk.Tk()tk.Frame(...)等)。

PEP8阻止通配符的导入,并且有充分的理由。它们污染了全局名称空间,并且可以在不知情的情况下覆盖变量和类。由于tk和ttk都用相同的名称定义了类,因此Tkinter特别容易受到这种影响。

使用PEP8命名约定

您应该采用PEP8命名约定。具体来说,以大写字母开头的类名称并使用CamelCase(例如:class EmployeeGUI)。

不要添加无用的评论

评论很重要,但它们也可能成为噪音来源。在创建名为#employee first name的变量之前,像self.firstname_label这样的注释是毫无意义的。

将布局语句分组在一起

您应该将小部件的创建与小部件的布局分开。通过将布局代码分组在一起,可以更轻松地实现可视化和修改。注意不一致的地方也比较容易,例如为某些小部件使用略有不同的颜色或填充。我的经验告诉我,在开发过程中布局可能会经常更改。

将它们组织成与UI中出现的相同的组。例如,如果所有标签和条目都位于UI的一个大块中,请在代码中将它们设为一个大块。如果将它们分成单独的UI部分(例如,地址块,名称块等),则可以按照这种方式进行组织。

例如:

    self.firstname_label = Label(...)
    self.first_name = ttk.Entry(...)
    self.lastname_label = Label(...)
    self.last_name = ttk.Entry(...)
    self.employee_email = Label(...)
    self.employee_email_entry = ttk.Entry(...)
    ...

    self.firstname_label.grid(....)
    self.first_name.grid(...)
    self.lastname_label.grid(...)
    self.last_name.grid(...)
    self.employee_email.grid(...)
    self.employee_email_entry.grid(...)
    ...


您是想做“标签,条目,标签,条目...”还是“标签,标签,标签,...,条目,条目,条目...”使代码更易于理解。

评论


\ $ \ begingroup \ $
很好的一点是,我将研究如何以分组的形式组织我的代码,好像这将更易于管理
\ $ \ endgroup \ $
–Rons辖区
18年1月10日在12:22

\ $ \ begingroup \ $
我完全错过了通配符导入。好抓住:)
\ $ \ endgroup \ $
– hjpotter92
18年1月10日在13:05

\ $ \ begingroup \ $
我实际上不介意tkinter中的通配符导入-在这里是惯用的(即使在其他地方是惯用的)。
\ $ \ endgroup \ $
–亚当·史密斯(Adam Smith)
18年1月10日在18:24

\ $ \ begingroup \ $
@AdamSmith:我不会说这很惯用。可能是无所不在的,可能是因为一个人在编写第一个教程之一时选择了这样做,而每个人都从那个来源复制而来。出于同样的原因,它对Tkinter不利,而对其他任何方面也不利–它污染了全局名称空间,并使代码更难理解。但是,归根结底,这主要是个人喜好。
\ $ \ endgroup \ $
–布莱恩·奥克利(Bryan Oakley)
18年1月10日在18:55

\ $ \ begingroup \ $
@BryanOakley嗯,虽然不是吗?惯用语的意思是“在上下文中无处不在”,尽管我同意在一般情况下使用显式命名间隔更好,但“愚蠢的一致性”对我而言是成功的。如果您正在编写tkinter GUI,请像编写tkinter GUI一样编写它(以我的主观经验),其中包括tkinter import *。那就是说:我知道我有点骑自行车了;)
\ $ \ endgroup \ $
–亚当·史密斯(Adam Smith)
18年1月10日在20:52



#3 楼

除了前面的两个答案之外,您还应该保持初始化器(__init__())的轻便和简约,并将大部分工作委派给各自实现一个简单目标的不同功能(您可以浏览一下Tkinter最佳实践)。另外,我发现各个函数相当长,改进的设计会导致摆脱使用中的全局变量

#4 楼

使用文件存储大量数据可能会引起数据歧义,数据孤岛等问题。最好使用设计良好的数据库,然后使用SQL对其进行操作。查看堆栈溢出的dba部分以获取更多信息。您可能还想用HTML编写用户界面,因此公司无需安装python和tk即可轻松使用它。

评论


\ $ \ begingroup \ $
我计划在出售它或允许公司使用它之前将其转换为exe。基于Windows的计算机无需下载任何与程序相关的内容。
\ $ \ endgroup \ $
–Rons辖区
18年1月11日在12:20

\ $ \ begingroup \ $
我对HTML的客户端-服务器体系结构有些偏见。不必使用它,但是如果没有它,您将不得不管理原本不需要的某些事情。我想到的一些事情是:在多台计算机上进行版本控制和安装,安全性(可以在哪台计算机上使用密码将它们安装在vs https上)以及通过FTP或其他方式调用远程数据库服务器。当然,使用exe也具有优势。最终,您必须自己做出这些决定。
\ $ \ endgroup \ $
–mikeLundquist
18年1月11日在17:13

\ $ \ begingroup \ $
不,那是一个好点,我没有考虑使用多平台安装该应用程序的可能性
\ $ \ endgroup \ $
–Rons辖区
18年1月12日在12:35