Phase 2: Creating an effective GUI using the predefined health metric functions
Phase 2 focuses on developing a Graphic User Interface (GUI) using PyQt5, transforming the mathematical functions from Phase 1 into a visual application that users can easily interact with.
The interface features a clean layout with input fields for key metrics: weight, height, age, sex, and activity level. To collect this data, the design incorporates several PyQt5 widgets:
Results appear in a read-only text display area (QTextEdit) that shows all health metrics clearly formatted with appropriate units. This display updates dynamically whenever new calculations are performed.
Here are the imports required for this project, all PyQt5 imports are for the application code and the health_metric import is bringing in the aggregated function from the Phase 1 project
from PyQt5.QtWidgets import *
from PyQt5.QtCore import Qt
from PyQt5.QtGui import *
import requests
from health_metrics_calc import health_metric
This defines the start of the application code using 'app=' & 'window=', we also set the size & name of the application when launched using setGeometry & setWindowTitle
def main():
app = QApplication([])
window = QWidget()
window.setGeometry(400, 400, 400, 570)
window.setWindowTitle("Health Metrics Calculator")
layout = QVBoxLayout()
This adds the first input widget, a textbox input for weight in KG. This allows the user to input any type of text but will only accept an integer
label_weight = QLabel(window)
label_weight.setText("Weight [kg]:")
label_weight.setFont(QFont("Helvetica", 16, QFont.Bold))
textbox_weight = QLineEdit()
Similar to the first widget, we create another textbox input for height in CM. Which again allows the user to input any type of text but will give errors if not an integer
label_height = QLabel(window)
label_height.setText("Height [cm]:")
label_height.setFont(QFont("Helvetica", 16, QFont.Bold))
textbox_height = QLineEdit()
The next input required is either male or female, and so we can use a radio button to allow users to easily pick one of these options instead of a textbox. This code defined the options, the starting selection and order of the radio buttons
label_sex = QLabel(window)
label_sex.setText("Sex:")
label_sex.setFont(QFont("Helvetica", 16, QFont.Bold))
male_radio = QRadioButton("Male")
female_radio = QRadioButton("Female")
sex_group = QButtonGroup(window)
sex_group.addButton(male_radio, 1) # ID 1 for male
sex_group.addButton(female_radio, 2) # ID 2 for female
male_radio.setChecked(True)
The Age input again is a textbox input and has the same implementation as weight & height
label_age = QLabel(window)
label_age.setText("Age:")
label_age.setFont(QFont("Helvetica", 16, QFont.Bold))
textbox_age = QLineEdit()
Because exercise level is an arbitrary metric, we define 5 excerice levels which can be easy to calculate. The best way to communicate the options to the user is with a dropdown menu, which the options are defined in the 'levels' object. However, an issue caused by this implementation is the function requires just a number and not a number + text which is whats included in this drop down, so this will be considered in the process function later.
label_exercise = QLabel(window)
label_exercise.setText("Weekly Exercise Level:")
label_exercise.setFont(QFont("Helvetica", 16, QFont.Bold))
textbox_exercise = QComboBox()
levels = ["1 = Sedentary", "2 = Lightly active", "3 = Moderately active", "4 = Very active", "5 = Super active"]
textbox_exercise.addItems(levels)
To give users control on when they want to process their inputs, we add a push button called 'Run'
run_button = QPushButton("Run", window)
To display results to users, we can add a readonly textbox. The reason for the textbox is because the process function used later will allow us to text the text within this textbox to the results string defined in the Phase 1 project
result_label = QLabel("Results:")
result_label.setFont(QFont("Helvetica", 16, QFont.Bold))
result_display = QTextEdit()
result_display.setReadOnly(True)
This code is how we give a layout to the GUI, with each line defining the order for each widget to appear
layout.addWidget(label_weight)
layout.addWidget(textbox_weight)
layout.addWidget(label_height)
layout.addWidget(textbox_height)
layout.addWidget(label_sex)
layout.addWidget(male_radio)
layout.addWidget(female_radio)
layout.addWidget(label_age)
layout.addWidget(textbox_age)
layout.addWidget(label_exercise)
layout.addWidget(textbox_exercise)
layout.addWidget(run_button)
layout.addWidget(result_label)
layout.addWidget(result_display)
As previously mentioned, the exercise dropdown input is now provide our function with a number + text when it can only read the numbers 1-5. This function translates the dropdown inputs and returns the number value required for our function
def translate_exercise_level(input):
"""Translate exercise level from string to integer."""
if input == "1 = Sedentary":
return 1
elif input == "2 = Lightly active":
return 2
elif input == "3 = Moderately active":
return 3
elif input == "4 = Very active":
return 4
elif input == "5 = Super active":
return 5
else:
raise ValueError("Invalid exercise level")
This is the main processing code for the application, which takes all the user inputs and applies them to the functions defined in Phase 1 of this project. To ensure proper data handling, each input is converted to the appropriate type - either integers for numerical values or strings for text inputs - based on what the underlying functions require. After processing the inputs through the health_metric function, the resulting formatted string is displayed in the results textbox, providing users with a clear presentation of their calculated health metrics.
def process_input():
try:
# Get input text
weight_value = textbox_weight.text()
height_value = textbox_height.text()
sex_value = sex_group.checkedId()
age_value = textbox_age.text()
exercise_value = textbox_exercise.currentText()
exercise_value_translate = translate_exercise_level(exercise_value)
if sex_value == 1:
sex_value_new = "m"
elif sex_value == 2:
sex_value_new = "f"
# Convert to appropriate types
weight_value = int(weight_value)
height_value = int(height_value)
sex_value = str(sex_value_new)
age_value = int(age_value)
exercise_value = int(exercise_value_translate)
# Process the input
result = health_metric(weight_value, height_value, sex_value, age_value, exercise_value)
# Display the result
result_display.setText(result)
except Exception as e:
# Show error in the result display
result_display.setText(f"Error: {str(e)}")
This code connects the run button to the process function, allowing the user to choose when to process their inputted metrics. The other lines below this define the layout & displays the window when required
run_button.clicked.connect(process_input)
window.setLayout(layout)
window.show()
app.exec()
if __name__ == '__main__':
main()
After running all of this code together, we get an effective GUI that presents health metrics to users in a clear and accessible format: