类对象被引用为字符串

时间:2021-04-21 03:34:13

标签: python python-3.x

我正在编写代码来构建一个带有 GUI 的基本员工管理软件。当我加载示例数据时,我相信它们是作为类对象 (Employee) 创建的,因为在调试时我可以看到所有参数都已传递并创建了对象。但是,当我稍后尝试修改它们(例如已聘用状态)时,它会返回错误 'str' object has no attribute 'hired_status'

Employee 类:

"""Create Employee class so all employees can be Employee Objects"""
def __init__(self, fname='', lname='', phone='', email='', job='', interview_rating=0, hired_status='N'):
    self.first = fname
    self.last = lname
    self.phone_number = phone
    self.email_address = email
    self.interview = interview_rating
    self.__emp_job = job
    self.hired_status = hired_status

@property
def first_name(self):
    return self.__first

@first_name.setter
def first_name(self, fname):
    if fname.isalpha() and len(fname) >= 1:
        self.__first = fname.capitalize()
    else:
        self.__first = 'Unknown'

@property
def last_name(self):
    return self.__last

@last_name.setter
def last_name(self, lname):
    if lname.isalpha() and len(lname) >= 1:
        self.__last = lname.capitalize()
    else:
        self.__last = 'Unknown'

@property
def email_address(self):
    return self.__email_address

@email_address.setter
def email_address(self, email):
    if len(email) >=1 and '@' in email:
        self.__email_address = email
    else:
        self.__email_address = 'Unknown'

@property
def phone_number(self):
    return self.__phone_number.format(str)

@phone_number.setter
def phone_number(self, phone):
    if len(phone) >= 1:
        self.__phone_number = phone
    else:
        self.__phone_number = 'Unknown'

@property
def job(self):
    return self.__emp_job

@job.setter
def job(self, job):
    self.__emp_job = job

@property
def hired_status(self):
    return self.__hired_status

@hired_status.setter
def hired_status(self, hired):
    self.__hired_status = hired

def __str__(self):
    return f'{self.first_name},{self.last_name},{self.phone_number},{self.email_address},{self.job},' \
           f'{self.hired_status}'

主类:

def load_employees():
    """
    Loads same employees from the text file.
    - Creates an Employee object for each employee listed in the file.
    - Retrieves comma-separated values from each line and assigns it to the corresponding arg in Employee()
    - Adds the new employee object to the list of current employees.
    - Displays the employees on the screen for the user.
    """
    global first_name, last_name, phone, email, job, new_employee, current_employees, hired_status
    current_employees = []
    with open('employees.txt', 'r') as file:
        for f in file:
            first_name, last_name, phone, email, job, hired_status = f.split(',')
            sample_employee = Employee(first_name, last_name, phone, email, job, hired_status)
            sample_employee.first_name = first_name
            sample_employee.last_name = last_name
            sample_employee.phone_number = phone
            sample_employee.email_address = email
            sample_employee.job = job
            sample_employee.hired_status = hired_status
            current_employees.append(f)
            emp_list.insert(END, f)

尝试招聘

def hire_candidate():
    global first_name, last_name, phone, email, job, new_employee, current_employees, edit_mode, hired_status, emp_list
    new_employee = emp_list.get(ANCHOR)
    new_employee.hired_status = 'Y'

使用 GUI 添加新员工

def save_new_employee():
    """
    When user clicks "Save Employee" the program will create or edit an Employee object.
    - A new Employee will be added to the list of current employees and appear in the GUI.
    - If editing, the Employee object will be updated with the new information.
    - The user is prompted with a message saying they have added or updated an Employee.
    - The Employee Management form will then clear all textboxes.
    """
    global first_name, last_name, phone, email, job, new_employee, current_employees, edit_mode, hired_status
    global applicant_first_tbx, applicant_last_tbx, applicant_phone_tbx, applicant_email_tbx, jobs_cbx
    new_employee = None
    new_employee = Employee(first_name, last_name, phone, email, job, hired_status)

    new_employee.first_name = applicant_first_tbx.get()
    new_employee.last_name = applicant_last_tbx.get()
    new_employee.phone_number = applicant_phone_tbx.get()
    new_employee.email_address = applicant_email_tbx.get()
    new_employee.job = job

    if edit_mode:
        x = emp_list.curselection()
        current_employees[edit_index] = x
        emp_list.delete(x)
        emp_list.insert(edit_index, new_employee)
        current_employees.pop(edit_index)
        current_employees.append(new_employee)
        edit_mode = False
        messagebox.showinfo('Employee Information Updated.', f'Information for {new_employee.first_name} '
                                                             f'{new_employee.last_name} has been updated.')
    else:
        current_employees.append(new_employee)
        emp_list.insert(END, new_employee)
        messagebox.showinfo('New Employee Added', f'{new_employee.first_name} {new_employee.last_name} '
                                                  f'has been added to your roster.')

    applicant_first_tbx.delete(0, END)
    applicant_last_tbx.delete(0, END)
    applicant_phone_tbx.delete(0, END)
    applicant_email_tbx.delete(0, END)

创建相关的图形用户界面

# create the main program window
win = Tk()
win.title('Employee Management Form')
win.config(bg='skyblue', pady=20, padx=50)

# global variables
current_employees = []
edit_mode = False
first_name = StringVar()
last_name = StringVar()
phone = StringVar()
email = StringVar()
job = StringVar()
hired_status = StringVar()
job_titles = ['Cook', 'Dishwasher', 'Server']
food_handler = ['Yes', 'No']
cook_certs = {1: 'Basic', 2: 'Advanced'}
interview_scores = [1, 2, 3, 4, 5]
y_offset = 10
color = 'skyblue'
new_employee = None
edit_index = 0
employeeManagement = True

# options menu
menu_bar = Menu(win)
win.config(menu=menu_bar)
win.iconbitmap('emgmt.ico')

# Create a menu bar with required cascades and commands
file_menu = Menu(menu_bar, tearoff=False)
view = Menu(menu_bar, tearoff=False)
menu_bar.add_cascade(label='File', menu=file_menu)

# Saves the information entered into the textboxes as a new employee, or overwrites an employee if editing
file_menu.add_command(label='Save Employee', command=save_new_employee)

# Turns on Edit Mode so user can modify information of an existing employee
file_menu.add_command(label='Edit Employee', command=edit_employee)

# Deletes employee obj from the data structure and removes their name from the GUI
file_menu.add_command(label='Delete Employee', command=delete_employee)

# updates the text file of employees, so upon reopening the program, all changes made previously are reflected
file_menu.add_command(label='Save All Changes', command=save_all_changes)

# Adds the option for the user to switch between employee management and the interview scorecard
menu_bar.add_cascade(label='View', menu=view)
view.add_command(label='Employee Management', command=employee_mgmt)
view.add_command(label='Employee Rating', command=employee_rating)

# frame to display list of current employees. Always visible as per instructions
emp_list_frm = Frame(win, bg='skyblue')
emp_list_frm.pack()

# create the listbox of employees
emp_list_lbl = Label(emp_list_frm, text='Current Employees', bg='systembuttonface', width=30, justify=CENTER)
emp_list_lbl.grid(row=0, column=1, sticky=S)
emp_list = Listbox(emp_list_frm, width=75, listvariable=current_employees)
emp_list.grid(row=1, columnspan=3)

# employee management frame to add, edit, and delete employees
em_frame = Frame(win, bg='skyblue')
em_frame.pack()

# create layout for job application data
applicant_first_lbl = Label(em_frame, text='First Name:', justify=LEFT, pady=y_offset, bg=color)
applicant_first_lbl.grid(row=2, column=0, sticky=W)
applicant_first_tbx = Entry(em_frame, justify=LEFT, width=30, textvariable=first_name)
applicant_first_tbx.grid(row=2, column=1, sticky=W)
applicant_first_tbx.bind('<Key>', name_keys)

applicant_last_lbl = Label(em_frame, text='Last Name:', justify=LEFT, pady=y_offset, bg=color)
applicant_last_lbl.grid(row=3, column=0, sticky=W)
applicant_last_tbx = Entry(em_frame, justify=LEFT, width=30, textvariable=last_name)
applicant_last_tbx.grid(row=3, column=1, sticky=W)
applicant_last_tbx.bind('<Key>', name_keys)

applicant_phone_lbl = Label(em_frame, text='Phone Number:', justify=LEFT, pady=y_offset, bg=color)
applicant_phone_lbl.grid(row=4, column=0, sticky=W)
applicant_phone_tbx = Entry(em_frame, justify=LEFT, width=30, textvariable=phone)
applicant_phone_tbx.grid(row=4, column=1, sticky=W)
applicant_phone_tbx.bind('<Key>', phone_keys)

applicant_email_lbl = Label(em_frame, text='Email:', justify=LEFT, pady=y_offset, bg=color)
applicant_email_lbl.grid(row=5, column=0, sticky=W)
applicant_email_tbx = Entry(em_frame, justify=LEFT, width=30, textvariable=email)
applicant_email_tbx.grid(row=5, column=1, sticky=W)

# create dropdown list for food handlers card
food_handler_label = Label(em_frame, text='Food Handler Card:', justify=LEFT, pady=y_offset, bg=color)
food_handler_label.grid(row=6, column=0, sticky=W)
food_handler_cbx = ttk.Combobox(em_frame, values=food_handler, width=10)
food_handler_cbx.grid(row=7, column=0, sticky=W)

# create dropdown list for applied positions
job_applied_label = Label(em_frame, text='Select Job:', justify=LEFT, pady=y_offset, bg=color)
job_applied_label.grid(row=8, column=0, columnspan=3, sticky=W)

jobs_cbx = ttk.Combobox(em_frame, values=job_titles, width=15)
jobs_cbx.grid(row=9, column=0, sticky=W)
jobs_cbx.bind('<<ComboboxSelected>>', add_job_applied)

# Textbox showing list of all jobs the employee applied for
jobs_applied_txt = Entry(em_frame, justify=LEFT, width=25, textvariable=job_titles)
jobs_applied_txt.grid(row=9, columnspan=2, sticky=E)

# Highest Cook cert
cook_cert_label = Label(em_frame, text='If Cook, select highest certification:', justify=LEFT,
                        pady=y_offset, bg=color)
cook_cert_label.grid(row=11, column=0, columnspan=3, sticky=W)

# If applying for Cook, Buttons to show the highest level cooking cert
basic_btn = Radiobutton(em_frame, variable=cook_certs, text='Basic', value=1, bg=color, indicatoron=1, state=DISABLED)
basic_btn.grid(row=12, column=0, sticky=W)

advanced_btn = Radiobutton(em_frame, variable=cook_certs, text='Advanced', value=2, bg=color, indicatoron=1, padx=2,
                           state=DISABLED)
advanced_btn.grid(row=12, column=1, sticky=W)
# Creates a frame for Rating System
rating_frame = Frame(win, bg='skyblue')

rating_lbl = Label(rating_frame, bg=color, text='Interview Rating Form:\n'
                                                'Rate an Employee from 1 to 5, where 1 is the '
                                                'worst and 5 is the best'' in the following categories.')
rating_lbl.grid(row=2, column=0, columnspan=3)

comms_rate_lbl = Label(rating_frame, bg=color, text='1. Verbal/Communication Skills')
comms_rate_lbl.grid(row=3, column=0, sticky=W)
comms_cbx = ttk.Combobox(rating_frame, values=interview_scores, width=10)
comms_cbx.grid(row=4, column=0, sticky=W)

personal_rate_lbl = Label(rating_frame, bg=color, text='2. Interpersonal Skills and Friendliness')
personal_rate_lbl.grid(row=5, column=0, sticky=W)
personal_cbx = ttk.Combobox(rating_frame, values=interview_scores, width=10)
personal_cbx.grid(row=6, column=0, sticky=W)

math_lbl = Label(rating_frame, bg=color, text='3. Math/Problem Solving Skills')
math_lbl.grid(row=7, column=0, sticky=W)
math_cbx = ttk.Combobox(rating_frame, values=interview_scores, width=10)
math_cbx.grid(row=8, column=0, sticky=W)

exp_lbl = Label(rating_frame, bg=color, text='4. Applicable World Experience')
exp_lbl.grid(row=9, column=0, sticky=W)
exp_cbx = ttk.Combobox(rating_frame, values=interview_scores, width=10)
exp_cbx.grid(row=10, column=0, sticky=W)

interview_average_btn = Button(rating_frame, bg='gray', text='Calculate Interview Average',
                               command=lambda: calculate_interview_average(interview_average_btn))
interview_average_btn.grid(row=11, column=0, columnspan=3, sticky=W, pady=8,)
interview_average_tbx = Entry(rating_frame, bg='white', width=10)
interview_average_tbx.grid(row=11, column=0, sticky=E)

hire_btn = Button(rating_frame, bg='gold', text='Hire Candidate', state=DISABLED, command=hire_candidate)
hire_btn.grid(row=12, column=0, sticky=W)

这个想法是,您单击并让员工形成一个列表框,然后单击一个按钮来雇用他们。我认为它是从列表框中返回字符串,但我希望它返回该位置的对象。

错误信息

 File "C:\Users\Jack\PycharmProjects\Class_Project_jxhawki1\employee_management.py", line 188, in hire_candidate
    new_employee.hired_status = 'Y'
AttributeError: 'str' object has no attribute 'hired_status'

1 个答案:

答案 0 :(得分:0)

您显示的代码有几个问题。首先,在我看来,您的代码依赖于太多不必要或不需要的全局变量。其次,虽然从技术上讲,在同一窗口内混合网格和包几何的方式是可行的,但这是一种危险的做法。我更喜欢在所有事情中坚持使用一种几何。最后,我相信您的基本问题是您没有提供数据结构来将从列表框选择函数返回的字符串值关联到包含所选员工数据的特定员工实例。以下内容不是您整体应用程序需求的完整解决方案,但确实解决了上述几个问题。

首先,重写你的 Employee 类

class Employee:
    """Create Employee class so all employees can be Employee Objects"""
    def __init__(self, fname= 'Unknown', lname= 'Unknown', phone= 'Unknown', email= 'Unknown', job= '', interview_rating= 0, hired_status= False):
        self._first = None
        self._last = None
        self._emAdr = None
        self._phnNo = None
        self._status = False
        self._job = None
        self._intrvRate = int(interview_rating)
        self.first_name = fname
        self.last_name = lname
        self.phone_number = phone
        self.email_address = email
        self.employee_job = job
        self.hired_status = hired_status

    @property
    def first_name(self):
        return self._first
    
    @first_name.setter
    def first_name(self, fname):
        try:
            if fname.isalpha() and len(fname) >= 1:
                self._first = fname.capitalize()
        except:
            raise ValueError(f"First Name '{fname}' must be alpha and be at least 1 character long")
                
    @property
    def last_name(self):
        return self._last

    @last_name.setter
    def last_name(self, lname):
        if lname.isalpha() and len(lname) >= 1:
            self._last = lname.capitalize()
        else:
            raise ValueError(f"Last Name '{lname}' must be alpha and be at least 1 character long")
            
    @property
    def full_name(self):
        return f"{self.last_name}, {self.first_name}"
            
    @property
    def email_address(self):
        return self._emAdr

    @email_address.setter
    def email_address(self, email):
        if email == '':
            self.emAdr = 'Unknown'
        elif '@' in email and len(email) >= 1:
            self._emAdr = email
        else:
            raise ValueError(f"Email Address '{email}' must contain an '@' and be at least 1 character long")

    @property
    def phone_number(self):
        return self._phnNo

    @phone_number.setter
    def phone_number(self, phone):
        if len(phone) == 0:
            self._phnNo = 'Unknown'
        elif len(phone) >= 1:
            self._phnNo = phone
        else:
            raise ValueError(f"Phone Number '{phone}' must be at least 1 character long")

    @property
    def employee_job(self):
        return self._job

    @employee_job.setter
    def employee_job(self, job):
        self._job = job

    @property
    def hired_status(self):
        return self._status

    @hired_status.setter
    def hired_status(self, status):
        if isinstance(status, bool):
            self._status = status
        else:
            raise ValueError(f"Hiring Status '{status}' must be either 'True' or 'False'")
        
    @property
    def hire(self):
        self.hired_status = True

    @property
    def fire(self):
        self.hired_status = False
        
    def __repr__(self):
        return f'{self.first_name}, {self.last_name}, {self.phone_number}, {self.email_address}, {self.employee_job}, {self.hired_status}'

更新的加载数据函数:

def load_employees(fn):
    """
    Loads employees from text file fn.
    - Retrieves comma-separated values from each line and assigns it to the corresponding arg in Employee()
    - Adds the employee object to the employee_dict.
    """
    employee_dict = dict()
    with open(fn, 'r') as file:
        for l in file.readlines():
            itms = l.split(',')
            emp = Employee(fname= itms[0].strip(), 
                          lname = itms[1].strip(),
                          phone= itms[2].strip(),
                          email = itms[3].strip(),
                          job = itms[4].strip(),
                          interview_rating = itms[5].strip(),
                          hired_status = True if itms[6].strip().lower() == 'y'  else  False)
            employee_dict[emp.full_name] = emp           
    return employee_dict

几个枚举常量类

class job_titles(Enum):
    Cook = auto()
    Dishwasher = auto()
    Server = auto()

class food_handlers(Enum):
        Cook = True
        Dishwasher = False
        Server = True
        
class cook_certs(Enum):
    Basic = 1
    Advanced = 2

管理 GUI 界面的新类

# Class to display the Employee Gui and Manage all related employee methods
class EmployeeForm():

        
    def __init__(self, employee_list):
        self.emp_list = employee_list
        self.selected_employee = None
        self.win = tk.Tk()
        self.win.title("Employee Management Form")
        self.win.config(bg='skyblue', pady=20, padx=50)
        self.build_menuBar(self.win)
        self.build_list_frame(self.win)
        self.win.mainloop()

    def do_nothing(self):
        #Stub for menu items not yetbimplements 
        pass
    
    def get_currewnt_employees(self):
        pass
    
    
    def build_menuBar(self, frm):
        """ Format the Menu bar """
        
        def add_command(mnu, itm):
            mnu.add_command(label= itm[0], command= itm[1])
 
        def add_sub_menu(mnu, cmd_dict):
            for ky in cmd_dict.keys():
                if len(cmd_dict[ky]) > 0:
                    mi = tk.Menu(mnu, tearoff= 0)
                    mnu.add_cascade(label=ky, menu=mi)
                    for itm in cmd_dict[ky]:
                        if type(itm) == tuple:
                            add_command(mi, itm)
                        else:
                            add_sub_menu(mi, itm) 
                            
                         
        menuoptions = {'File': [('Save Employee', self.do_nothing),
                                     ('Edit Employee', self.do_nothing),
                                     ('Delete Employee', self.do_nothing),
                                     ('Save All Changes', self.do_nothing)],
                            'View': [('Employee Management', self.do_nothing),
                                     ('Employee Rating', self.do_nothing)],
                            }
        
        mbar = tk.Menu(frm)
        add_sub_menu(mbar, menuoptions)
        frm.config(menu=mbar)
        return mbar
    
    def select_employee(self, event):
        selection = event.widget.curselection()
        if self.selected_employee != self.cur_emps[selection[0]]:
            self.selected_employee = self.cur_emps[selection[0]]
            
        # if selection:
        #     index = selection[0]
        #     data = event.widget.get(index)
        
    
    def build_list_frame(self, parent):
        """ frame to display list of current employees. Always visible as per instructions""" 
        self.emp_list_frm = tk.Frame(parent, bg='skyblue', relief= tk.RAISED, borderwidth= 5)
        self.emp_list_frm.grid(row= 0 , sticky= (tk.E, tk.W))
        emp_list_lbl = tk.Label(self.emp_list_frm, text='Current Employees', 
                                bg='systembuttonface', justify= tk.CENTER)
        emp_list_lbl.grid(row=0, column=1, sticky= (tk.E, tk.W))
        self.cur_emps = list(x for x in sorted(self.emp_list.keys()))       
        self.emp_list_dsply  = tk.Listbox(self.emp_list_frm, width=75, 
                                          listvariable =tk.StringVar(value= self.cur_emps) )
        self.emp_list_dsply.grid(row=1, columnspan=3)
        self.emp_list_dsply.bind("<<ListboxSelect>>", self.select_employee)  

要运行上面的:

emp_file = 'employees.txt'
emplist = load_employees(emp_file)
EmployeeForm(load_employees(emp_file))

一旦您从列表框选择选项中选择了一个员工,self.selected_employee 的内容将反映该员工对象,然后可以被删除方法和编辑方法引用以进行进一步操作。

相关问题