├── LICENSE ├── README.md ├── haarcascade_frontalface_default.xml └── main.py /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Shubham Kumar 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Face_recognition_based_attendance_system 2 | A python GUI integrated attendance system using face recognition to take attendance. 3 | 4 | In this python project, I have made an attendance system which takes attendance by using face recognition technique. I have also intergrated it with GUI (Graphical user interface) so it can be easy to use by anyone. GUI for this project is also made on python using tkinter. 5 | 6 | TECHNOLOGY USED: 7 | 1) tkinter for whole GUI 8 | 2) OpenCV for taking images and face recognition (cv2.face.LBPHFaceRecognizer_create()) 9 | 3) CSV, Numpy, Pandas, datetime etc. for other purposes. 10 | 11 | FEATURES: 12 | 1) Easy to use with interactive GUI support. 13 | 2) Password protection for new person registration. 14 | 3) Creates/Updates CSV file for details of students on registration. 15 | 4) Creates a new CSV file everyday for attendance and marks attendance with proper date and time. 16 | 5) Displays live attendance updates for the day on the main screen in tabular format with Id, name, date and time. 17 | 18 | ### For more information: 19 | https://machinelearningprojects.net/face-recognition-based-attendance-system/ 20 | 21 | # SCREENSHOTS 22 | MAIN SCREEN: 23 | ![Screenshot (9)](https://user-images.githubusercontent.com/37211676/58502148-97ec2a00-81a3-11e9-963e-674b9c3e05dc.png) 24 | 25 | TAKING ATTENDANCE: 26 | ![Screenshot (10)](https://user-images.githubusercontent.com/37211676/58502149-97ec2a00-81a3-11e9-9658-8968da396c2e.png) 27 | 28 | SHOWING ATTENDANCE TAKEN: 29 | ![Screenshot (11)](https://user-images.githubusercontent.com/37211676/58502151-9884c080-81a3-11e9-9a90-fec29940ee5a.png) 30 | 31 | HELP OPTION IN MENUBAR: 32 | ![Screenshot (12)](https://user-images.githubusercontent.com/37211676/58502152-991d5700-81a3-11e9-861a-9115526010c2.png) 33 | 34 | CHANGE PASSWORD OPTION: 35 | ![Screenshot (13)](https://user-images.githubusercontent.com/37211676/58502146-97539380-81a3-11e9-8536-0c68160ecc55.png) 36 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | ############################################# IMPORTING ################################################ 2 | import tkinter as tk 3 | from tkinter import ttk 4 | from tkinter import messagebox as mess 5 | import tkinter.simpledialog as tsd 6 | import cv2,os 7 | import csv 8 | import numpy as np 9 | from PIL import Image 10 | import pandas as pd 11 | import datetime 12 | import time 13 | 14 | ############################################# FUNCTIONS ################################################ 15 | 16 | def assure_path_exists(path): 17 | dir = os.path.dirname(path) 18 | if not os.path.exists(dir): 19 | os.makedirs(dir) 20 | 21 | ################################################################################## 22 | 23 | def tick(): 24 | time_string = time.strftime('%H:%M:%S') 25 | clock.config(text=time_string) 26 | clock.after(200,tick) 27 | 28 | ################################################################################### 29 | 30 | def contact(): 31 | mess._show(title='Contact us', message="Please contact us on : 'xxxxxxxxxxxxx@gmail.com' ") 32 | 33 | ################################################################################### 34 | 35 | def check_haarcascadefile(): 36 | exists = os.path.isfile("haarcascade_frontalface_default.xml") 37 | if exists: 38 | pass 39 | else: 40 | mess._show(title='Some file missing', message='Please contact us for help') 41 | window.destroy() 42 | 43 | ################################################################################### 44 | 45 | def save_pass(): 46 | assure_path_exists("TrainingImageLabel/") 47 | exists1 = os.path.isfile("TrainingImageLabel\psd.txt") 48 | if exists1: 49 | tf = open("TrainingImageLabel\psd.txt", "r") 50 | key = tf.read() 51 | else: 52 | master.destroy() 53 | new_pas = tsd.askstring('Old Password not found', 'Please enter a new password below', show='*') 54 | if new_pas == None: 55 | mess._show(title='No Password Entered', message='Password not set!! Please try again') 56 | else: 57 | tf = open("TrainingImageLabel\psd.txt", "w") 58 | tf.write(new_pas) 59 | mess._show(title='Password Registered', message='New password was registered successfully!!') 60 | return 61 | op = (old.get()) 62 | newp= (new.get()) 63 | nnewp = (nnew.get()) 64 | if (op == key): 65 | if(newp == nnewp): 66 | txf = open("TrainingImageLabel\psd.txt", "w") 67 | txf.write(newp) 68 | else: 69 | mess._show(title='Error', message='Confirm new password again!!!') 70 | return 71 | else: 72 | mess._show(title='Wrong Password', message='Please enter correct old password.') 73 | return 74 | mess._show(title='Password Changed', message='Password changed successfully!!') 75 | master.destroy() 76 | 77 | ################################################################################### 78 | 79 | def change_pass(): 80 | global master 81 | master = tk.Tk() 82 | master.geometry("400x160") 83 | master.resizable(False,False) 84 | master.title("Change Password") 85 | master.configure(background="white") 86 | lbl4 = tk.Label(master,text=' Enter Old Password',bg='white',font=('times', 12, ' bold ')) 87 | lbl4.place(x=10,y=10) 88 | global old 89 | old=tk.Entry(master,width=25 ,fg="black",relief='solid',font=('times', 12, ' bold '),show='*') 90 | old.place(x=180,y=10) 91 | lbl5 = tk.Label(master, text=' Enter New Password', bg='white', font=('times', 12, ' bold ')) 92 | lbl5.place(x=10, y=45) 93 | global new 94 | new = tk.Entry(master, width=25, fg="black",relief='solid', font=('times', 12, ' bold '),show='*') 95 | new.place(x=180, y=45) 96 | lbl6 = tk.Label(master, text='Confirm New Password', bg='white', font=('times', 12, ' bold ')) 97 | lbl6.place(x=10, y=80) 98 | global nnew 99 | nnew = tk.Entry(master, width=25, fg="black", relief='solid',font=('times', 12, ' bold '),show='*') 100 | nnew.place(x=180, y=80) 101 | cancel=tk.Button(master,text="Cancel", command=master.destroy ,fg="black" ,bg="red" ,height=1,width=25 , activebackground = "white" ,font=('times', 10, ' bold ')) 102 | cancel.place(x=200, y=120) 103 | save1 = tk.Button(master, text="Save", command=save_pass, fg="black", bg="#3ece48", height = 1,width=25, activebackground="white", font=('times', 10, ' bold ')) 104 | save1.place(x=10, y=120) 105 | master.mainloop() 106 | 107 | ##################################################################################### 108 | 109 | def psw(): 110 | assure_path_exists("TrainingImageLabel/") 111 | exists1 = os.path.isfile("TrainingImageLabel\psd.txt") 112 | if exists1: 113 | tf = open("TrainingImageLabel\psd.txt", "r") 114 | key = tf.read() 115 | else: 116 | new_pas = tsd.askstring('Old Password not found', 'Please enter a new password below', show='*') 117 | if new_pas == None: 118 | mess._show(title='No Password Entered', message='Password not set!! Please try again') 119 | else: 120 | tf = open("TrainingImageLabel\psd.txt", "w") 121 | tf.write(new_pas) 122 | mess._show(title='Password Registered', message='New password was registered successfully!!') 123 | return 124 | password = tsd.askstring('Password', 'Enter Password', show='*') 125 | if (password == key): 126 | TrainImages() 127 | elif (password == None): 128 | pass 129 | else: 130 | mess._show(title='Wrong Password', message='You have entered wrong password') 131 | 132 | ###################################################################################### 133 | 134 | def clear(): 135 | txt.delete(0, 'end') 136 | res = "1)Take Images >>> 2)Save Profile" 137 | message1.configure(text=res) 138 | 139 | 140 | def clear2(): 141 | txt2.delete(0, 'end') 142 | res = "1)Take Images >>> 2)Save Profile" 143 | message1.configure(text=res) 144 | 145 | ####################################################################################### 146 | 147 | def TakeImages(): 148 | check_haarcascadefile() 149 | columns = ['SERIAL NO.', '', 'ID', '', 'NAME'] 150 | assure_path_exists("StudentDetails/") 151 | assure_path_exists("TrainingImage/") 152 | serial = 0 153 | exists = os.path.isfile("StudentDetails\StudentDetails.csv") 154 | if exists: 155 | with open("StudentDetails\StudentDetails.csv", 'r') as csvFile1: 156 | reader1 = csv.reader(csvFile1) 157 | for l in reader1: 158 | serial = serial + 1 159 | serial = (serial // 2) 160 | csvFile1.close() 161 | else: 162 | with open("StudentDetails\StudentDetails.csv", 'a+') as csvFile1: 163 | writer = csv.writer(csvFile1) 164 | writer.writerow(columns) 165 | serial = 1 166 | csvFile1.close() 167 | Id = (txt.get()) 168 | name = (txt2.get()) 169 | if ((name.isalpha()) or (' ' in name)): 170 | cam = cv2.VideoCapture(0) 171 | harcascadePath = "haarcascade_frontalface_default.xml" 172 | detector = cv2.CascadeClassifier(harcascadePath) 173 | sampleNum = 0 174 | while (True): 175 | ret, img = cam.read() 176 | gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) 177 | faces = detector.detectMultiScale(gray, 1.3, 5) 178 | for (x, y, w, h) in faces: 179 | cv2.rectangle(img, (x, y), (x + w, y + h), (255, 0, 0), 2) 180 | # incrementing sample number 181 | sampleNum = sampleNum + 1 182 | # saving the captured face in the dataset folder TrainingImage 183 | cv2.imwrite("TrainingImage\ " + name + "." + str(serial) + "." + Id + '.' + str(sampleNum) + ".jpg", 184 | gray[y:y + h, x:x + w]) 185 | # display the frame 186 | cv2.imshow('Taking Images', img) 187 | # wait for 100 miliseconds 188 | if cv2.waitKey(100) & 0xFF == ord('q'): 189 | break 190 | # break if the sample number is morethan 100 191 | elif sampleNum > 100: 192 | break 193 | cam.release() 194 | cv2.destroyAllWindows() 195 | res = "Images Taken for ID : " + Id 196 | row = [serial, '', Id, '', name] 197 | with open('StudentDetails\StudentDetails.csv', 'a+') as csvFile: 198 | writer = csv.writer(csvFile) 199 | writer.writerow(row) 200 | csvFile.close() 201 | message1.configure(text=res) 202 | else: 203 | if (name.isalpha() == False): 204 | res = "Enter Correct name" 205 | message.configure(text=res) 206 | 207 | ######################################################################################## 208 | 209 | def TrainImages(): 210 | check_haarcascadefile() 211 | assure_path_exists("TrainingImageLabel/") 212 | recognizer = cv2.face_LBPHFaceRecognizer.create() 213 | harcascadePath = "haarcascade_frontalface_default.xml" 214 | detector = cv2.CascadeClassifier(harcascadePath) 215 | faces, ID = getImagesAndLabels("TrainingImage") 216 | try: 217 | recognizer.train(faces, np.array(ID)) 218 | except: 219 | mess._show(title='No Registrations', message='Please Register someone first!!!') 220 | return 221 | recognizer.save("TrainingImageLabel\Trainner.yml") 222 | res = "Profile Saved Successfully" 223 | message1.configure(text=res) 224 | message.configure(text='Total Registrations till now : ' + str(ID[0])) 225 | 226 | ############################################################################################3 227 | 228 | def getImagesAndLabels(path): 229 | # get the path of all the files in the folder 230 | imagePaths = [os.path.join(path, f) for f in os.listdir(path)] 231 | # create empth face list 232 | faces = [] 233 | # create empty ID list 234 | Ids = [] 235 | # now looping through all the image paths and loading the Ids and the images 236 | for imagePath in imagePaths: 237 | # loading the image and converting it to gray scale 238 | pilImage = Image.open(imagePath).convert('L') 239 | # Now we are converting the PIL image into numpy array 240 | imageNp = np.array(pilImage, 'uint8') 241 | # getting the Id from the image 242 | ID = int(os.path.split(imagePath)[-1].split(".")[1]) 243 | # extract the face from the training image sample 244 | faces.append(imageNp) 245 | Ids.append(ID) 246 | return faces, Ids 247 | 248 | ########################################################################################### 249 | 250 | def TrackImages(): 251 | check_haarcascadefile() 252 | assure_path_exists("Attendance/") 253 | assure_path_exists("StudentDetails/") 254 | for k in tv.get_children(): 255 | tv.delete(k) 256 | msg = '' 257 | i = 0 258 | j = 0 259 | recognizer = cv2.face.LBPHFaceRecognizer_create() # cv2.createLBPHFaceRecognizer() 260 | exists3 = os.path.isfile("TrainingImageLabel\Trainner.yml") 261 | if exists3: 262 | recognizer.read("TrainingImageLabel\Trainner.yml") 263 | else: 264 | mess._show(title='Data Missing', message='Please click on Save Profile to reset data!!') 265 | return 266 | harcascadePath = "haarcascade_frontalface_default.xml" 267 | faceCascade = cv2.CascadeClassifier(harcascadePath); 268 | 269 | cam = cv2.VideoCapture(0) 270 | font = cv2.FONT_HERSHEY_SIMPLEX 271 | col_names = ['Id', '', 'Name', '', 'Date', '', 'Time'] 272 | exists1 = os.path.isfile("StudentDetails\StudentDetails.csv") 273 | if exists1: 274 | df = pd.read_csv("StudentDetails\StudentDetails.csv") 275 | else: 276 | mess._show(title='Details Missing', message='Students details are missing, please check!') 277 | cam.release() 278 | cv2.destroyAllWindows() 279 | window.destroy() 280 | while True: 281 | ret, im = cam.read() 282 | gray = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY) 283 | faces = faceCascade.detectMultiScale(gray, 1.2, 5) 284 | for (x, y, w, h) in faces: 285 | cv2.rectangle(im, (x, y), (x + w, y + h), (225, 0, 0), 2) 286 | serial, conf = recognizer.predict(gray[y:y + h, x:x + w]) 287 | if (conf < 50): 288 | ts = time.time() 289 | date = datetime.datetime.fromtimestamp(ts).strftime('%d-%m-%Y') 290 | timeStamp = datetime.datetime.fromtimestamp(ts).strftime('%H:%M:%S') 291 | aa = df.loc[df['SERIAL NO.'] == serial]['NAME'].values 292 | ID = df.loc[df['SERIAL NO.'] == serial]['ID'].values 293 | ID = str(ID) 294 | ID = ID[1:-1] 295 | bb = str(aa) 296 | bb = bb[2:-2] 297 | attendance = [str(ID), '', bb, '', str(date), '', str(timeStamp)] 298 | 299 | else: 300 | Id = 'Unknown' 301 | bb = str(Id) 302 | cv2.putText(im, str(bb), (x, y + h), font, 1, (255, 255, 255), 2) 303 | cv2.imshow('Taking Attendance', im) 304 | if (cv2.waitKey(1) == ord('q')): 305 | break 306 | ts = time.time() 307 | date = datetime.datetime.fromtimestamp(ts).strftime('%d-%m-%Y') 308 | exists = os.path.isfile("Attendance\Attendance_" + date + ".csv") 309 | if exists: 310 | with open("Attendance\Attendance_" + date + ".csv", 'a+') as csvFile1: 311 | writer = csv.writer(csvFile1) 312 | writer.writerow(attendance) 313 | csvFile1.close() 314 | else: 315 | with open("Attendance\Attendance_" + date + ".csv", 'a+') as csvFile1: 316 | writer = csv.writer(csvFile1) 317 | writer.writerow(col_names) 318 | writer.writerow(attendance) 319 | csvFile1.close() 320 | with open("Attendance\Attendance_" + date + ".csv", 'r') as csvFile1: 321 | reader1 = csv.reader(csvFile1) 322 | for lines in reader1: 323 | i = i + 1 324 | if (i > 1): 325 | if (i % 2 != 0): 326 | iidd = str(lines[0]) + ' ' 327 | tv.insert('', 0, text=iidd, values=(str(lines[2]), str(lines[4]), str(lines[6]))) 328 | csvFile1.close() 329 | cam.release() 330 | cv2.destroyAllWindows() 331 | 332 | ######################################## USED STUFFS ############################################ 333 | 334 | global key 335 | key = '' 336 | 337 | ts = time.time() 338 | date = datetime.datetime.fromtimestamp(ts).strftime('%d-%m-%Y') 339 | day,month,year=date.split("-") 340 | 341 | mont={'01':'January', 342 | '02':'February', 343 | '03':'March', 344 | '04':'April', 345 | '05':'May', 346 | '06':'June', 347 | '07':'July', 348 | '08':'August', 349 | '09':'September', 350 | '10':'October', 351 | '11':'November', 352 | '12':'December' 353 | } 354 | 355 | ######################################## GUI FRONT-END ########################################### 356 | 357 | window = tk.Tk() 358 | window.geometry("1280x720") 359 | window.resizable(True,False) 360 | window.title("Attendance System") 361 | window.configure(background='#262523') 362 | 363 | frame1 = tk.Frame(window, bg="#00aeff") 364 | frame1.place(relx=0.11, rely=0.17, relwidth=0.39, relheight=0.80) 365 | 366 | frame2 = tk.Frame(window, bg="#00aeff") 367 | frame2.place(relx=0.51, rely=0.17, relwidth=0.38, relheight=0.80) 368 | 369 | message3 = tk.Label(window, text="Face Recognition Based Attendance System" ,fg="white",bg="#262523" ,width=55 ,height=1,font=('times', 29, ' bold ')) 370 | message3.place(x=10, y=10) 371 | 372 | frame3 = tk.Frame(window, bg="#c4c6ce") 373 | frame3.place(relx=0.52, rely=0.09, relwidth=0.09, relheight=0.07) 374 | 375 | frame4 = tk.Frame(window, bg="#c4c6ce") 376 | frame4.place(relx=0.36, rely=0.09, relwidth=0.16, relheight=0.07) 377 | 378 | datef = tk.Label(frame4, text = day+"-"+mont[month]+"-"+year+" | ", fg="orange",bg="#262523" ,width=55 ,height=1,font=('times', 22, ' bold ')) 379 | datef.pack(fill='both',expand=1) 380 | 381 | clock = tk.Label(frame3,fg="orange",bg="#262523" ,width=55 ,height=1,font=('times', 22, ' bold ')) 382 | clock.pack(fill='both',expand=1) 383 | tick() 384 | 385 | head2 = tk.Label(frame2, text=" For New Registrations ", fg="black",bg="#3ece48" ,font=('times', 17, ' bold ') ) 386 | head2.grid(row=0,column=0) 387 | 388 | head1 = tk.Label(frame1, text=" For Already Registered ", fg="black",bg="#3ece48" ,font=('times', 17, ' bold ') ) 389 | head1.place(x=0,y=0) 390 | 391 | lbl = tk.Label(frame2, text="Enter ID",width=20 ,height=1 ,fg="black" ,bg="#00aeff" ,font=('times', 17, ' bold ') ) 392 | lbl.place(x=80, y=55) 393 | 394 | txt = tk.Entry(frame2,width=32 ,fg="black",font=('times', 15, ' bold ')) 395 | txt.place(x=30, y=88) 396 | 397 | lbl2 = tk.Label(frame2, text="Enter Name",width=20 ,fg="black" ,bg="#00aeff" ,font=('times', 17, ' bold ')) 398 | lbl2.place(x=80, y=140) 399 | 400 | txt2 = tk.Entry(frame2,width=32 ,fg="black",font=('times', 15, ' bold ') ) 401 | txt2.place(x=30, y=173) 402 | 403 | message1 = tk.Label(frame2, text="1)Take Images >>> 2)Save Profile" ,bg="#00aeff" ,fg="black" ,width=39 ,height=1, activebackground = "yellow" ,font=('times', 15, ' bold ')) 404 | message1.place(x=7, y=230) 405 | 406 | message = tk.Label(frame2, text="" ,bg="#00aeff" ,fg="black" ,width=39,height=1, activebackground = "yellow" ,font=('times', 16, ' bold ')) 407 | message.place(x=7, y=450) 408 | 409 | lbl3 = tk.Label(frame1, text="Attendance",width=20 ,fg="black" ,bg="#00aeff" ,height=1 ,font=('times', 17, ' bold ')) 410 | lbl3.place(x=100, y=115) 411 | 412 | res=0 413 | exists = os.path.isfile("StudentDetails\StudentDetails.csv") 414 | if exists: 415 | with open("StudentDetails\StudentDetails.csv", 'r') as csvFile1: 416 | reader1 = csv.reader(csvFile1) 417 | for l in reader1: 418 | res = res + 1 419 | res = (res // 2) - 1 420 | csvFile1.close() 421 | else: 422 | res = 0 423 | message.configure(text='Total Registrations till now : '+str(res)) 424 | 425 | ##################### MENUBAR ################################# 426 | 427 | menubar = tk.Menu(window,relief='ridge') 428 | filemenu = tk.Menu(menubar,tearoff=0) 429 | filemenu.add_command(label='Change Password', command = change_pass) 430 | filemenu.add_command(label='Contact Us', command = contact) 431 | filemenu.add_command(label='Exit',command = window.destroy) 432 | menubar.add_cascade(label='Help',font=('times', 29, ' bold '),menu=filemenu) 433 | 434 | ################## TREEVIEW ATTENDANCE TABLE #################### 435 | 436 | tv= ttk.Treeview(frame1,height =13,columns = ('name','date','time')) 437 | tv.column('#0',width=82) 438 | tv.column('name',width=130) 439 | tv.column('date',width=133) 440 | tv.column('time',width=133) 441 | tv.grid(row=2,column=0,padx=(0,0),pady=(150,0),columnspan=4) 442 | tv.heading('#0',text ='ID') 443 | tv.heading('name',text ='NAME') 444 | tv.heading('date',text ='DATE') 445 | tv.heading('time',text ='TIME') 446 | 447 | ###################### SCROLLBAR ################################ 448 | 449 | scroll=ttk.Scrollbar(frame1,orient='vertical',command=tv.yview) 450 | scroll.grid(row=2,column=4,padx=(0,100),pady=(150,0),sticky='ns') 451 | tv.configure(yscrollcommand=scroll.set) 452 | 453 | ###################### BUTTONS ################################## 454 | 455 | clearButton = tk.Button(frame2, text="Clear", command=clear ,fg="black" ,bg="#ea2a2a" ,width=11 ,activebackground = "white" ,font=('times', 11, ' bold ')) 456 | clearButton.place(x=335, y=86) 457 | clearButton2 = tk.Button(frame2, text="Clear", command=clear2 ,fg="black" ,bg="#ea2a2a" ,width=11 , activebackground = "white" ,font=('times', 11, ' bold ')) 458 | clearButton2.place(x=335, y=172) 459 | takeImg = tk.Button(frame2, text="Take Images", command=TakeImages ,fg="white" ,bg="blue" ,width=34 ,height=1, activebackground = "white" ,font=('times', 15, ' bold ')) 460 | takeImg.place(x=30, y=300) 461 | trainImg = tk.Button(frame2, text="Save Profile", command=psw ,fg="white" ,bg="blue" ,width=34 ,height=1, activebackground = "white" ,font=('times', 15, ' bold ')) 462 | trainImg.place(x=30, y=380) 463 | trackImg = tk.Button(frame1, text="Take Attendance", command=TrackImages ,fg="black" ,bg="yellow" ,width=35 ,height=1, activebackground = "white" ,font=('times', 15, ' bold ')) 464 | trackImg.place(x=30,y=50) 465 | quitWindow = tk.Button(frame1, text="Quit", command=window.destroy ,fg="black" ,bg="red" ,width=35 ,height=1, activebackground = "white" ,font=('times', 15, ' bold ')) 466 | quitWindow.place(x=30, y=450) 467 | 468 | ##################### END ###################################### 469 | 470 | window.configure(menu=menubar) 471 | window.mainloop() 472 | 473 | #################################################################################################### 474 | --------------------------------------------------------------------------------