Courses/Python Mastery/Module 3: Variables & Data Types
Module 3 · Lesson 820 minBeginner

🧪 Lab: BMI Calculator

What you'll build
Apply variables, numbers, and type conversion by building a real health calculator.

Introduction

The BMI calculator is the perfect *second* Streamlit app: one formula, two inputs, instant feedback — but it teaches a genuinely important pattern, unit systems that reconfigure the UI. Switch between metric and imperial and watch the inputs, labels, and math all change together. If you built the unit converter, this is its health-focused cousin.

Everything fits in about 70 lines, and by the end you'll be comfortable with conditional UI, radio-driven logic, and rendering a visual scale with a progress bar.

Features

  • Metric and imperial — kg/cm or lb/ft+in, switched by a radio.
  • Instant result — BMI computed live as inputs change.
  • Category feedback — Underweight / Normal / Overweight / Obese with colored status.
  • Visual scale — progress bar showing where you sit in the BMI range.
  • Healthy weight range — the weight band that would give a normal BMI for your height.
  • Prerequisites

  • Python 3.8+ — from python.org.
  • Streamlit — install with pip:
  • code
    pip install streamlit

    Step 1: Create the Script

    Save as bmi_app.py:

    code
    import streamlit as st
    
    st.set_page_config(page_title="BMI Calculator", page_icon="⚖️")
    st.title("⚖️ BMI Calculator")
    
    unit = st.radio("Unit system", ["Metric (kg, cm)", "Imperial (lb, ft/in)"], horizontal=True)
    
    if unit.startswith("Metric"):
        weight = st.number_input("Weight (kg)", min_value=1.0, max_value=500.0, value=70.0)
        height_cm = st.number_input("Height (cm)", min_value=50.0, max_value=280.0, value=170.0)
        height_m = height_cm / 100
    else:
        weight_lb = st.number_input("Weight (lb)", min_value=2.0, max_value=1100.0, value=154.0)
        col1, col2 = st.columns(2)
        feet = col1.number_input("Height (ft)", min_value=1, max_value=9, value=5)
        inches = col2.number_input("Extra inches", min_value=0, max_value=11, value=7)
        weight = weight_lb * 0.453592
        height_m = (feet * 12 + inches) * 0.0254
    
    if height_m > 0:
        bmi = weight / (height_m ** 2)
    
        if bmi < 18.5:
            category, color = "Underweight", "🟡"
        elif bmi < 25:
            category, color = "Normal weight", "🟢"
        elif bmi < 30:
            category, color = "Overweight", "🟠"
        else:
            category, color = "Obese", "🔴"
    
        st.metric("Your BMI", f"{bmi:.1f}")
        st.markdown(f"### {color} {category}")
    
        st.progress(min(bmi / 40, 1.0), text=f"BMI scale (0–40): you are at {bmi:.1f}")
    
        low = 18.5 * height_m ** 2
        high = 24.9 * height_m ** 2
        st.info(f"Healthy weight range for your height: {low:.1f}–{high:.1f} kg")

    Step 2: Run the App

    code
    streamlit run bmi_app.py

    Toggle between unit systems, drag the inputs, and watch the category, color, and scale respond live — no button needed.

    How It Works

    The whole app is a branching UI. The radio's value decides which st.number_input widgets render, and because Streamlit reruns the script on every interaction, switching units swaps the form instantly. The math normalizes both systems to kilograms and meters at the top, so the BMI formula and category thresholds are written exactly once — one source of truth instead of duplicated imperial math.

    The category ladder is a classic if/elif chain over WHO thresholds (18.5 / 25 / 30). The visual scale is st.progress with the BMI clamped to the 0–40 range — a one-line visualization that reads better than a number alone.

    The healthy-range footer inverts the formula: instead of BMI = weight / height², it solves weight = BMI × height² at the two boundary BMIs. Algebra doing UI work.

    Common Errors & Fixes

  • `ZeroDivisionError` — height defaulted to 0; guard with if height_m > 0: before dividing (already in the code above).
  • Imperial results look wrong — you mixed conversion constants; pounds→kg is 0.453592, feet+inches→meters is total_inches × 0.0254. Convert once, at the top.
  • Radio resets on interaction — give the radio a key="unit" so its selection survives reruns.
  • Progress bar overflowsst.progress expects 0.0–1.0; clamp with min(bmi / 40, 1.0).
  • Key Concepts

  • Conditional widgets — different inputs per unit system, swapped by rerun.
  • Normalize early — convert units once, keep the core formula single-sourced.
  • `st.metric` + `st.progress` — number plus visual context, zero CSS.
  • Inverting formulas — derive healthy weight ranges from the same equation.
  • What to Try Next

  • Add age and sex inputs and show BMI percentile context for children (WHO LMS data).
  • Store measurements over time in CSV and chart BMI history, like the expense tracker.
  • Add a BMR/calories tab using the Mifflin-St Jeor equation with st.tabs.
  • Localize labels with a language selectbox — the translator app can even translate them dynamically.
  • FAQ

    Is BMI accurate for athletes?

    No — BMI ignores muscle mass, so muscular people often read "overweight." Treat it as a quick population-level screen, not a diagnosis, and consider waist measurements alongside it.

    Why compute in metric internally?

    One internal unit system means one tested formula. Converting at the edges (UI in, display out) is the same pivot idea the unit converter uses for temperature.

    Can I use sliders instead of number inputs?

    Yes — st.slider with float steps feels great for exploration; keep st.number_input for precise entry. Offering both via a checkbox takes three lines.

    Adapted from: BMI Calculator using Python and Streamlit

    Checkpoint
    The calculator produces correct BMI in both metric and imperial units.
    What you learned
    • Variables and math operators in a real app
    • Type conversion where it matters
    • Reading a project's structure