top of page
binary-code-background-vector.jpg
# USAGE # python3 train_mask_detector.py --dataset dataset # import the necessary packages from tensorflow.keras.preprocessing.image import ImageDataGenerator from tensorflow.keras.applications import MobileNetV2 from tensorflow.keras.layers import AveragePooling2D from tensorflow.keras.layers import Dropout from tensorflow.keras.layers import Flatten from tensorflow.keras.layers import Dense from tensorflow.keras.layers import Input from tensorflow.keras.models import Model from tensorflow.keras.optimizers import Adam from tensorflow.keras.applications.mobilenet_v2 import preprocess_input from tensorflow.keras.preprocessing.image import img_to_array from tensorflow.keras.preprocessing.image import load_img from tensorflow.keras.utils import to_categorical from sklearn.preprocessing import LabelBinarizer from sklearn.model_selection import train_test_split from sklearn.metrics import classification_report from imutils import paths import matplotlib.pyplot as plt import numpy as np import argparse import os # construct the argument parser and parse the arguments ap = argparse.ArgumentParser() ap.add_argument("-d", "--dataset", required=True, help="path to input dataset") ap.add_argument("-p", "--plot", type=str, default="plot.png", help="path to output loss/accuracy plot") ap.add_argument("-m", "--model", type=str, default="mask_detector.model", help="path to output face mask detector model") args = vars(ap.parse_args()) # initialize the initial learning rate, number of epochs to train for, # and batch size INIT_LR = 1e-4 EPOCHS = 20 BS = 32 # grab the list of images in our dataset directory, then initialize # the list of data (i.e., images) and class images print("[INFO] loading images...") imagePaths = list(paths.list_images(args["dataset"])) data = [] labels = [] # loop over the image paths for imagePath in imagePaths: # extract the class label from the filename label = imagePath.split(os.path.sep)[-2] # load the input image (224x224) and preprocess it image = load_img(imagePath, target_size=(224, 224)) image = img_to_array(image) image = preprocess_input(image) # update the data and labels lists, respectively data.append(image) labels.append(label) # convert the data and labels to NumPy arrays data = np.array(data, dtype="float32") labels = np.array(labels) # perform one-hot encoding on the labels lb = LabelBinarizer() labels = lb.fit_transform(labels) labels = to_categorical(labels) # partition the data into training and testing splits using 75% of # the data for training and the remaining 25% for testing (trainX, testX, trainY, testY) = train_test_split(data, labels, test_size=0.20, stratify=labels, random_state=42) # construct the training image generator for data augmentation aug = ImageDataGenerator( rotation_range=20, zoom_range=0.15, width_shift_range=0.2, height_shift_range=0.2, shear_range=0.15, horizontal_flip=True, fill_mode="nearest") # load the MobileNetV2 network, ensuring the head FC layer sets are # left off baseModel = MobileNetV2(weights="imagenet", include_top=False, input_tensor=Input(shape=(224, 224, 3))) # construct the head of the model that will be placed on top of the # the base model headModel = baseModel.output headModel = AveragePooling2D(pool_size=(7, 7))(headModel) headModel = Flatten(name="flatten")(headModel) headModel = Dense(128, activation="relu")(headModel) headModel = Dropout(0.5)(headModel) headModel = Dense(2, activation="softmax")(headModel) # place the head FC model on top of the base model (this will become # the actual model we will train) model = Model(inputs=baseModel.input, outputs=headModel) # loop over all layers in the base model and freeze them so they will # *not* be updated during the first training process for layer in baseModel.layers: layer.trainable = False # compile our model print("[INFO] compiling model...") opt = Adam(lr=INIT_LR, decay=INIT_LR / EPOCHS) model.compile(loss="binary_crossentropy", optimizer=opt,

metrics=["accuracy"]) # train the head of the network print("[INFO] training head...") H = model.fit( aug.flow(trainX, trainY, batch_size=BS), steps_per_epoch=len(trainX) // BS, validation_data=(testX, testY), validation_steps=len(testX) // BS, epochs=EPOCHS) # make predictions on the testing set print("[INFO] evaluating network...") predIdxs = model.predict(testX, batch_size=BS) # for each image in the testing set we need to find the index of the # label with corresponding largest predicted probability predIdxs = np.argmax(predIdxs, axis=1) # show a nicely formatted classification report print(classification_report(testY.argmax(axis=1), predIdxs, target_names=lb.classes_)) # serialize the model to disk print("[INFO] saving mask detector model...") model.save(args["model"], save_format="h5") # plot the training loss and accuracy N = EPOCHS plt.style.use("ggplot") plt.figure() plt.plot(np.arange(0, N), H.history["loss"], label="train_loss") plt.plot(np.arange(0, N), H.history["val_loss"], label="val_loss") plt.plot(np.arange(0, N), H.history["accuracy"], label="train_accuracy") plt.plot(np.arange(0, N), H.history["val_accuracy"], label="val_accuracy") plt.title("Training Loss and Accuracy") plt.xlabel("Epoch #") plt.ylabel("Loss/Accuracy") plt.legend(loc="lower left") plt.savefig(args["plot"])

import cv2 name = 'with_mask' #write to the with_mask folder cam = cv2.VideoCapture(0) cv2.namedWindow("press space to take a photo", cv2.WINDOW_NORMAL) cv2.resizeWindow("press space to take a photo", 500, 300) img_counter = 0 while True: ret, frame = cam.read() if not ret: print("failed to grab frame") break cv2.imshow("press space to take a photo", frame) k = cv2.waitKey(1) if k%256 == 27: # ESC pressed print("Escape hit, closing...") break elif k%256 == 32: # SPACE pressed img_name = "dataset/"+ name +"/image_{}.jpg".format(img_counter) cv2.imwrite(img_name, frame) print("{} written!".format(img_name)) img_counter += 1 cam.release() cv2.destroyAllWindows()

import cv2 name = 'without_mask' #write to the without_mask folder cam = cv2.VideoCapture(0) cv2.namedWindow("press space to take a photo", cv2.WINDOW_NORMAL) cv2.resizeWindow("press space to take a photo", 500, 300) img_counter = 0 while True: ret, frame = cam.read() if not ret: print("failed to grab frame") break cv2.imshow("press space to take a photo", frame) k = cv2.waitKey(1) if k%256 == 27: # ESC pressed print("Escape hit, closing...") break elif k%256 == 32: # SPACE pressed img_name = "dataset/"+ name +"/image_{}.jpg".format(img_counter) cv2.imwrite(img_name, frame) print("{} written!".format(img_name)) img_counter += 1 cam.release() cv2.destroyAllWindows()

# USAGE # python3 detect_mask_webcam.py # import the necessary packages from tensorflow.keras.applications.mobilenet_v2 import preprocess_input from tensorflow.keras.preprocessing.image import img_to_array from tensorflow.keras.models import load_model from imutils.video import VideoStream import numpy as np import argparse import imutils import time import cv2 import os from time import sleep #This imports the ServoKit class from Adafruit. They are the manufacture of this HAT that we are using. from adafruit_servokit import ServoKit #This is to specify how many channels the Raspberry Pi HAT has. This HAT has 16. kit = ServoKit(channels=16) #Channel 2 is Y-axis #Channel 1 is X-axis kit.servo[2].angle = 40 # 60 degrees is centered for Y-axis. kit.servo[1].angle = 70 # 70 degrees is centered for X-axis. def detect_and_predict_mask(frame, faceNet, maskNet, args): #grab the dimensions of the frame and then construct a blob # from it (h, w) = frame.shape[:2] blob = cv2.dnn.blobFromImage(frame, 1.0, (300, 300), (104.0, 177.0, 123.0)) # pass the blob through the network and obtain the face detections faceNet.setInput(blob) detections = faceNet.forward() # initialize our list of faces, their corresponding locations, # and the list of predictions from our face mask network faces = [] locs = [] preds = [] # loop over the detections for i in range(0, detections.shape[2]): # extract the confidence (i.e., probability) associated with # the detection confidence = detections[0, 0, i, 2] # filter out weak detections by ensuring the confidence is # greater than the minimum confidence if confidence > args["confidence"]: # compute the (x, y)-coordinates of the bounding box for # the object box = detections[0, 0, i, 3:7] * np.array([w, h, w, h]) (startX, startY, endX, endY) = box.astype("int") # ensure the bounding boxes fall within the dimensions of # the frame (startX, startY) = (max(0, startX), max(0, startY)) (endX, endY) = (min(w - 1, endX), min(h - 1, endY)) # extract the face ROI, convert it from BGR to RGB channel # ordering, resize it to 224x224, and preprocess it face = frame[startY:endY, startX:endX] face = cv2.cvtColor(face, cv2.COLOR_BGR2RGB) face = cv2.resize(face, (224, 224)) face = img_to_array(face) face = preprocess_input(face) # add the face and bounding boxes to their respective # lists faces.append(face) locs.append((startX, startY, endX, endY)) if len(faces) == 1: move_camera(w, h, startX, startY, endX, endY) # only make a predictions if at least one face was detected if len(faces) > 0: # for faster inference we'll make batch predictions on *all* # faces at the same time rather than one-by-one predictions # in the above `for` loop faces = np.array(faces, dtype="float32") preds = maskNet.predict(faces, batch_size=32) if len(faces) == 0: #Moves cameras back to home position when no face is detected after 5 seconds sleep(5) kit.servo[2].angle = 40 # 60 degrees is centered for Y-axis. kit.servo[1].angle = 70 # 70 degrees is centered for X-axis. # return a 2-tuple of the face locations and their corresponding # locations return (locs, preds)

def maskcamera(covidConditions): # construct the argument parser and parse the arguments ap = argparse.ArgumentParser() ap.add_argument("-f", "--face", type=str, default="face_detector", help="path to face detector model directory") ap.add_argument("-m", "--model", type=str, default="mask_detector.model", help="path to trained face mask detector model") ap.add_argument("-c", "--confidence", type=float, default=0.5, help="minimum probability to filter weak detections") args = vars(ap.parse_args()) # load our serialized face detector model from disk print("[INFO] loading face detector model...") prototxtPath = os.path.sep.join([args["face"], "deploy.prototxt"]) weightsPath = os.path.sep.join([args["face"], "res10_300x300_ssd_iter_140000.caffemodel"]) faceNet = cv2.dnn.readNet(prototxtPath, weightsPath) # load the face mask detector model from disk print("[INFO] loading face mask detector model...") maskNet = load_model(args["model"]) # initialize the video stream and allow the camera sensor to warm up print("[INFO] starting video stream...") vs = VideoStream(src=0).start() time.sleep(2.0) # loop over the frames from the video stream while True: try: # grab the frame from the threaded video stream and resize it # to have a maximum width of 400 pixels frame = vs.read() frame = imutils.resize(frame, width=500) # detect faces in the frame and determine if they are wearing a # face mask or not (locs, preds) = detect_and_predict_mask(frame, faceNet, maskNet, args) # loop over the detected face locations and their corresponding # locations for (box, pred) in zip(locs, preds): # unpack the bounding box and predictions (startX, startY, endX, endY) = box (mask, withoutMask) = pred # determine the class label and color we'll use to draw # the bounding box and text if mask > withoutMask: label = "Face Mask Detected" color = (0, 255, 0) covidConditions["mask"] = True print("Mask detected") else: label = "No Face Mask Detected" color = (0, 0, 255) covidConditions["mask"] = False print("No Face Mask detected") # display the label and bounding box rectangle on the output # frame cv2.putText(frame, label, (startX-50, startY - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.7, color, 2) cv2.rectangle(frame, (startX, startY), (endX, endY), color, 2) # show the output frame cv2.imshow("Face Mask Detector", frame) key = cv2.waitKey(1) & 0xFF # if the `q` key was pressed, break from the loop if key == ord("q"): break #Adding an exception to catch the error (cvtColor) to prevent the program from stopping except Exception as e: print(e) pass # do a bit of cleanup cv2.destroyAllWindows() vs.stop()

def move_camera(framex, framey, startX, startY, endX, endY): centerx = framex/2 centery = framey/2 center_facex = (startX + endX)/2 center_facey = (startY + endY)/2 #Defining the tolerance of the servo angles if abs(centerx - center_facex) >= 30: if centerx < center_facex: if kit.servo[1].angle >= 5: print(kit.servo[1].angle) print("X") kit.servo[1].angle -= 5 else: print("ATTEMPTED TO MOVE TO THE RIGHT, BUT") print(kit.servo[1].angle) print("IS TOO SHALLOW") elif centerx > center_facex: if kit.servo[1].angle <= 175: print(kit.servo[1].angle) print("X") kit.servo[1].angle += 5 else: print("ATTEMPTED TO MOVE TO THE LEFT, BUT") print(kit.servo[1].angle) print("IS TOO SHALLOW") if abs(centery - center_facey) >= 30: if centery > center_facey: if kit.servo[2].angle >= 5: print(kit.servo[2].angle) print("Y") kit.servo[2].angle -= 5 else: print("ATTEMPTED TO MOVE UP,BUT") print(kit.servo[2].angle) print("IS TOO SHALLOW") elif centery < center_facey: if kit.servo[2].angle <=175: print(kit.servo[2].angle) print("Y") kit.servo[2].angle += 5 else: print("ATTEMPTED TO MOVE DOWN, BUT") print(kit.servo[2].angle) print("IS TOO SHALLOW") #covidConditions = {"mask": False} #maskcamera(covidConditions)

########################################## # MLX90640 Thermal Camera w Raspberry Pi # -- 2Hz Sampling with Simple Routine ########################################## # import time,board,busio import numpy as np import adafruit_mlx90640 import matplotlib.pyplot as plt def tempcamera(covidConditions): print("Initializing MLX90640") i2c = busio.I2C(board.SCL, board.SDA, frequency=80000) #Setup I2C mlx = adafruit_mlx90640.MLX90640(i2c) #Begin MLX90640 with I2C comm mlx.refresh_rate = adafruit_mlx90640.RefreshRate.REFRESH_2_HZ #Set refresh rate mlx_shape = (24,32) print("Initialized") #Setup the figure for plotting plt.ion() #Enables interactive plotting fig,ax = plt.subplots(figsize=(12,7)) therm1 = ax.imshow(np.zeros(mlx_shape),vmin=0,vmax=60) #Start plot with zeros cbar = fig.colorbar(therm1) #Setup colorbar for temps cbar.set_label('Temperature [$^{\circ}$C]',fontsize=14) #Colorbar label frame = np.zeros((24*32,)) #Setup array for storing all 768 temperatures t_array = [] print("Starting loop") while True: t1 = time.monotonic() try: mlx.getFrame(frame) #Read MLX temperatures into frame var data_array = (np.reshape(frame,mlx_shape)) #Reshape to 24x32 therm1.set_data(np.fliplr(data_array)) #Flip left to right therm1.set_clim(vmin=np.min(data_array),vmax=np.max(data_array)*1.079) # set bounds cbar.update_normal(therm1) #Update colorbar range plt.title(f"Max Temp: {np.max(data_array)*1.079:.1f}C") plt.pause(0.001) #Required #fig.savefig('mlx90640_test_fliplr.png',dpi=300,facecolor='#FCFCFC', bbox_inches='tight') # comment out to speed up t_array.append(time.monotonic()-t1) #Multiplied by a factor of 1.079 to increase reading accuracy # This is set for a max of 38 degrees centigrade. 38C=100.4F which at #100.4, you are considered to have a fever. if (np.max(data_array)*1.079 < 38): covidConditions["thermal"] = True #For us to know if the conditions are correct or not print('Temperature Below 38C') else: covidConditions["thermal"] = False #For us to know if the conditions are correct or not print('Temperature Above 38C') print('Sample Rate: {0:2.1f}fps'.format(len(t_array)/np.sum(t_array))) except ValueError: continue # if error, just read again #covidConditions = {"thermal": False} #tempcamera(covidConditions)

from tkinter import * from tkinter import messagebox from tkinter import font as tkFont from vidstream import AudioSender from vidstream import AudioReceiver from time import * import threading import vlc ## These are different types of windows that can be used #showinfo, showwarning, showerror, askquestion, askokcancel, askyesno global fD fD = vlc.MediaPlayer("/home/charlie/Desktop/Outside_RaspberryPi/frontdesk.mp3") global q1 q1 = vlc.MediaPlayer("/home/charlie/Desktop/Outside_RaspberryPi/q1.mp3") global q2 q2 = vlc.MediaPlayer("/home/charlie/Desktop/Outside_RaspberryPi/q2.mp3") global q3 q3 = vlc.MediaPlayer("/home/charlie/Desktop/Outside_RaspberryPi/q3.mp3") global q4 q4 = vlc.MediaPlayer("/home/charlie/Desktop/Outside_RaspberryPi/q4.mp3") global aT aT = vlc.MediaPlayer("/home/charlie/Desktop/Outside_RaspberryPi/alltimes.mp3") global pP pP = vlc.MediaPlayer("/home/charlie/Desktop/Outside_RaspberryPi/pP.mp3") global cH cH = vlc.MediaPlayer("/home/charlie/Desktop/Outside_RaspberryPi/clickh.mp3") def popup(covidConditions, root): ##Need to define response q1.play() response = messagebox.askyesno("Questionnaire Window", "Have you traveled domestically or internationally in the last 30 days?") if response == 1: q1.stop() fD.play() messagebox.showwarning("Questionnaire Response","Please Contact Front Desk Staff") #messagebox.showwarning("Questionnaire Response","Please Contact Front Desk Staff",command = lambda: 'frontdesk') covidConditions["questions"] = False fD.stop() else: q1.stop() q2.play() response1 = messagebox.askyesno("Questionnaire Window", "Are you waiting for the results of a COVID-19 test?") if response1 == 1: q2.stop() fD.play() messagebox.showwarning("Questionnaire Response", "Please Contact Front Desk Staff") covidConditions["questions"] = False fD.stop() else: q2.stop() q3.play() response2 = messagebox.askyesno("Questionnaire Window", "Are you showing any symptoms of COVID-19?") if response2 == 1: q3.stop() fD.play() messagebox.showwarning("Questionnaire Response", "Please Contact Front Desk Staff") covidConditions["questions"] = False fD.stop() else: q3.stop() q4.play() response3 = messagebox.askyesno("Questionnaire Window", "Were you in close contact with someone known to have been infected with COVID-19?") if response3 == 1: q4.stop() fD.play() messagebox.showwarning("Questionnaire Response", "Please Contact Front Desk Staff") covidConditions["questions"] = False fD.stop() else: q4.stop() aT.play() response4 = messagebox.askyesno("Questionnaire Window", "In accordance with healthcare mandates,masking is required for entrance into this facility during your stay. Do you agree to these terms?") if response4 == 0: aT.stop() fD.play() messagebox.showwarning("Questionnaire Response", "Please Contact Front Desk Staff") covidConditions["questions"] = False fD.stop() else: aT.stop() pP.play() fD.stop() enter = messagebox.showinfo("Questionnaire Response", "Please Proceed To Enter The Building") covidConditions["questions"] = True

receiver = AudioReceiver('169.254.176.185', 9999) #This is the Inside Raspberry Pi (Pi4) sender = AudioSender('169.254.167.227', 5555) # This is the Outside Raspberry Pi (Pi3) def start_call(): receiver.start_server() sender.start_stream() def end_call(): receiver.stop_server() sender.stop_stream() def clickh(): cH = vlc.MediaPlayer("/home/charlie/Desktop/Outside_RaspberryPi/clickh.mp3") cH.play() fD.stop() #lambda is used here to prevent the threading from starting immediately without the button press. #It calls the thread but does not start it yet. def CovidQuestionnaire(covidConditions): root = Tk() #MAKES THIS FULLSCREEN WITH OUT THE ABILITY TO EXIT root.attributes('-fullscreen',True) label = Label(root, text = "Please stand on the green line then Press Click Here To Begin.\n \n If you need to speak to someone, please press 'Start Call'", height=8,width=100, command = clickh()).pack() click = Button(root,text= "Click Here To Begin",height=8,width=100, bg='#FA8128',fg='black', font= ('helvetica',11,'bold'),command=lambda: popup(covidConditions,root)).pack() start = Button(root,text= "Start Call", fg='#00CC00',height=5,width=100, font= ('helvetica',11,'bold'), command=lambda:start_call()).pack() end = Button(root,text= "End Call", fg='#FF4D4D',height=5,width=100, font= ('helvetica',11,'bold'),command=lambda: threading.Thread(target=end_call).start()).pack() mainloop() #The lines below are used to test this individual file and #does not affect calling the functions in the Unlock_Conditions file #Uncomment the two lines below to test this individual file #If uncommented, the other two threads (cameras) will not start #covidConditions = {"questions": False} #CovidQuestionnaire(covidConditions)

import cv2 from Thermal_Camera import* from Mask_Detector import* from Outside_GUI_1106 import* from threading import Thread from time import sleep import RPi.GPIO as GPIO from tkinter import * from tkinter import messagebox import vlc global nM nM = vlc.MediaPlayer("/home/charlie/Desktop/Outside_RaspberryPi/no_mask.mp3") global eT eT = vlc.MediaPlayer("/home/charlie/Desktop/Outside_RaspberryPi/elev_temp.mp3") #Raspberry Pi lets you know if the GPIO is already being used. A warning is displayed if it is. We wanted to mute this warning. GPIO.setwarnings(False) #BCM defines broadcom. Each GPIO is based on function number. GPIO.setmode(GPIO.BCM) #Defining GPIO21 as an output and it starts low. GPIO.setup(21, GPIO.OUT, initial=GPIO.LOW) #Defining all covid conditions as started as False covidConditions = {"thermal": True,"mask": False, "questions":False} #Targets are the definitions within the corresponding .py files t1 = Thread(target= maskcamera, args=(covidConditions, )) t2 = Thread(target= CovidQuestionnaire, args=(covidConditions, )) t3 = Thread(target= tempcamera, args=(covidConditions, )) #This starts the threads t1.start() t2.start() t3.start() def UnlockDoor(): GPIO21 = 21 GPIO.setmode(GPIO.BCM) GPIO.setup(21, GPIO.OUT, initial=GPIO.LOW) global GPIO21_state GPIO.output(21, 1) sleep(5) #Wait 5 seconds GPIO.output(21, 0) #Do not need the below code but keeping it just in case #global GPIO21_state #GPIO.output(21, True) #sleep(5) #GPIO.output(21, False) covidConditions["questions"]= False while True: if (covidConditions["thermal"] == True and covidConditions["mask"] == True and covidConditions["questions"] == True): UnlockDoor() print(covidConditions) #The following lines of code are for alerting the user of their mask or temperature status. elif (covidConditions["thermal"] == False): GPIO.output(21,0) #eT.play() messagebox.showwarning("Temperature Check Warning System","The system is detecting your temperature to be elevated. Please click 'Start Call' to speak to the front desk staff.") sleep(5) elif (covidConditions["mask"] == False): GPIO.output(21,0) #nM.play() messagebox.showwarning("Mask Check Warning System", "The system has detected that you are not wearing a mask. Please put on a mask.") sleep(5)

from gpiozero import * from gpiozero.pins.pigpio import PiGPIOFactory from tkinter import * from tkinter import messagebox from tkinter import font as tkFont from functools import partial from time import sleep from vidstream import AudioSender from vidstream import AudioReceiver import threading #This will work with either eth0 or wlan0 #Works both with ethernet or wifi but we do not want this on WiFi global fact #Outside Raspberry Pi eth0 ip fact = PiGPIOFactory(host='169.254.176.185') #Since we do not need PWM, calling the LED class is ideal for this instance global relay relay = LED(21,pin_factory=fact) receiver = AudioReceiver('169.254.167.227',5555) sender = AudioSender('169.254.176.185',9999) def start_call(): receiver.start_server() sender.start_stream() def end_call(): receiver.stop_server() sender.stop_stream() #receiver = AudioReceiver('169.254.167.227', 5555) #This will be changed on the other device #receive_thread = threading.Thread(target = receiver.start_server) #sender = AudioSender('169.254.176.185', 9999) #This will be changed on the other device #sender_thread = threading.Thread(target=sender.start_stream) #receive_thread.start() #sender_thread.start() def popup(buttons): opener = messagebox.askyesno("Door Open Button", "Are you sure you want to unlock the front door?") if opener == 1: #opens the door for 5 seconds relay.on() status = messagebox.showwarning("Door Status", "FRONT DOOR UNLOCKED!") sleep(5) relay.off() #This sleep is needed to end the script if not it gets stuck in a loop sleep(.1) else: #Makes sure that the door stays locked when "no" is selected relay.off() closed = messagebox.showwarning("Door Status", "FRONT DOOR STILL LOCKED.") def open_button(buttons): root = Tk() #Uncomment for uncloasable fullscreen root.attributes('-fullscreen',True) label = Label(root, text= "Front Door Remote Access", font=('helvetica',20,'bold')).pack() door = Button(root, text= "Open The Door",bg='orange',fg='black', font=('helvetica',40,'bold'),command=lambda:popup(buttons)).pack(fill=X,ipady=10) start_button = Button(root,text= "Start Call", fg = "green", font=('helvetica',40,'bold'), command=lambda: start_call()).pack(fill=X,ipady=10) end_button = Button(root,text= "End Call", fg = "red", font=('helvetica',40,'bold'), command=lambda: end_call()).pack(fill=X,ipady=10) mainloop() buttons = {"questions": False} open_button(buttons)

bottom of page