Untitled

mail@pastecode.io avatar
unknown
plain_text
22 days ago
38 kB
1
Indexable
Never
import sqlite3
import sys

import numpy as np
from PyQt5.QtCore import Qt, pyqtSignal
from PyQt5.QtGui import QColor
from PyQt5.QtWidgets import QApplication, QWidget, QDialog, QVBoxLayout, QHBoxLayout, QPushButton, QFormLayout, \
    QLineEdit, QTableWidget, QTableWidgetItem, QTabWidget, QMenu, QAction, QComboBox, QButtonGroup, QLabel, QRadioButton
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure


def create_connection():
    try:
        return sqlite3.connect('nutrition.db')
    except sqlite3.Error as e:
        print(e)
        return None


def create_table(conn):
    try:
        cursor = conn.cursor()
        cursor.execute("""CREATE TABLE IF NOT EXISTS foods (
                            name TEXT NOT NULL,
                            calories REAL,
                            lipids REAL,
                            carbohydrates REAL,
                            protein REAL,
                            price REAL
                        );""")
        conn.commit()
    except sqlite3.Error as e:
        print(e)


def insert_food(conn, food):
    try:
        cursor = conn.cursor()
        cursor.execute("INSERT INTO foods(name, calories, lipids, carbohydrates, protein, price) VALUES (?, ?, ?, ?, ?, ?)", food)
        conn.commit()
    except sqlite3.Error as e:
        print(e)


def get_foods(conn):
    try:
        cursor = conn.cursor()
        cursor.execute("SELECT * FROM foods ORDER BY name ASC")
        return cursor.fetchall()
    except sqlite3.Error as e:
        print(e)
        return []


def delete_food(conn, food_name):
    try:
        cursor = conn.cursor()
        cursor.execute("DELETE FROM foods WHERE name = ?", (food_name,))
        conn.commit()
    except sqlite3.Error as e:
        print(e)


def update_food(conn, old_name, new_food):
    try:
        cursor = conn.cursor()
        cursor.execute("""UPDATE foods
                          SET name = ?, calories = ?, lipids = ?, carbohydrates = ?, protein = ?, price = ?
                          WHERE name = ?""",
                       (*new_food, old_name))
        conn.commit()
    except sqlite3.Error as e:
        print(e)


def create_progress_table(conn):
    try:
        cursor = conn.cursor()
        cursor.execute("""
            CREATE TABLE IF NOT EXISTS progress (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                date TEXT NOT NULL,
                weight REAL,
                body_fat REAL,
                muscle_mass REAL,
                waist_size REAL,  -- Add waist_size column
                chest_size REAL,
                thigh_size REAL,
                biceps_size REAL,
                notes TEXT
            );
        """)
        conn.commit()
    except sqlite3.Error as e:
        print(e)


def insert_progress(conn, progress):
    try:
        cursor = conn.cursor()
        cursor.execute("INSERT INTO progress(date, weight, body_fat, muscle_mass, waist_size, chest_size, thigh_size, biceps_size,  notes) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", progress)
        conn.commit()
    except sqlite3.Error as e:
        print(e)


def get_progress(conn):
    try:
        cursor = conn.cursor()
        cursor.execute("SELECT * FROM progress ORDER BY date ASC")
        return cursor.fetchall()
    except sqlite3.Error as e:
        print(e)
        return []


def delete_progress(conn, progress_id):
    try:
        cursor = conn.cursor()
        cursor.execute("DELETE FROM progress WHERE id = ?", (progress_id,))
        conn.commit()
    except sqlite3.Error as e:
        print(e)


def update_progress(conn, progress_id, progress):
    try:
        cursor = conn.cursor()
        cursor.execute("""
            UPDATE progress 
            SET date = ?, weight = ?, body_fat = ?, muscle_mass = ?, waist_size = ?, chest_size = ?, Thigh_size = ?, Biceps_size = ?, notes = ? 
            WHERE id = ?
        """, (*progress, progress_id))
        conn.commit()
    except sqlite3.Error as e:
        print(e)


class AddFoodDialog(QDialog):
    def __init__(self, parent=None, food=None):
        super(AddFoodDialog, self).__init__(parent)
        self.setWindowTitle('Add Food Item' if food is None else 'Edit Food Item')
        self.layout = QFormLayout(self)
        self.food = food

        self.nameEdit = QLineEdit(self)
        self.caloriesEdit = QLineEdit(self)
        self.lipidsEdit = QLineEdit(self)
        self.carbsEdit = QLineEdit(self)
        self.proteinEdit = QLineEdit(self)
        self.priceEdit = QLineEdit(self)
        self.nameEdit.setStyleSheet("background-color: lightyellow; font-weight: bold;")

        if food:
            self.nameEdit.setText(food[0])
            self.caloriesEdit.setText(str(food[1]))
            self.lipidsEdit.setText(str(food[2]))
            self.carbsEdit.setText(str(food[3]))
            self.proteinEdit.setText(str(food[4]))
            self.priceEdit.setText(str(food[5]))

        self.layout.addRow('Name:', self.nameEdit)
        self.layout.addRow('Calories:', self.caloriesEdit)
        self.layout.addRow('Lipids:', self.lipidsEdit)
        self.layout.addRow('Carbohydrates:', self.carbsEdit)
        self.layout.addRow('Protein:', self.proteinEdit)
        self.layout.addRow('Price:', self.priceEdit)

        self.addButton = QPushButton('Add' if food is None else 'Update', self)
        self.cancelButton = QPushButton('Cancel', self)
        self.addButton.clicked.connect(self.add_food)
        self.cancelButton.clicked.connect(self.reject)
        buttonLayout = QHBoxLayout()
        buttonLayout.addWidget(self.addButton)
        buttonLayout.addWidget(self.cancelButton)
        self.layout.addRow(buttonLayout)

    def add_food(self):
        try:
            name = self.nameEdit.text()
            calories = float(self.caloriesEdit.text())
            lipids = float(self.lipidsEdit.text())
            carbs = float(self.carbsEdit.text())
            protein = float(self.proteinEdit.text())
            price = float(self.priceEdit.text())
            food = (name, calories, lipids, carbs, protein, price)
            if self.food:
                old_name = self.food[0]
                update_food(conn, old_name, food)
            else:
                insert_food(conn, food)
            self.accept()  # Close the dialog on success
        except ValueError:
            # Handle invalid input
            pass


class FoodDatabaseDialog(QDialog):
    foodAdded = pyqtSignal()  # Signal to emit when food is added or updated
    foodDeleted = pyqtSignal()  # New signal for when a food is deleted

    def __init__(self, parent=None):
        super(FoodDatabaseDialog, self).__init__(parent)
        self.setWindowTitle('Food Database')
        self.setGeometry(300, 300, 500, 400)
        self.layout = QVBoxLayout(self)

        addButton = QPushButton('Add Food', self)
        deleteButton = QPushButton('Delete Selected Food', self)
        addButton.setStyleSheet("background-color: navy; color: white;")
        deleteButton.setStyleSheet("background-color: red; color: white;")
        addButton.clicked.connect(self.open_add_dialog)
        deleteButton.clicked.connect(self.delete_food)

        buttonLayout = QHBoxLayout()
        buttonLayout.addWidget(addButton)
        buttonLayout.addWidget(deleteButton)
        self.layout.addLayout(buttonLayout)

        self.table = QTableWidget(self)
        self.table.setColumnCount(6)
        self.table.setHorizontalHeaderLabels(['Name', 'Calories', 'Lipids', 'Carbohydrates', 'Protein', 'Price'])
        self.table.setContextMenuPolicy(Qt.CustomContextMenu)
        self.table.customContextMenuRequested.connect(self.show_context_menu)
        self.update_table()
        self.layout.addWidget(self.table)

    def show_context_menu(self, position):
        menu = QMenu(self)
        edit_action = QAction("Edit", self)
        edit_action.triggered.connect(self.edit_food)
        menu.addAction(edit_action)
        menu.exec_(self.table.mapToGlobal(position))

    def edit_food(self):
        selected_row = self.table.currentRow()
        if selected_row >= 0:
            name = self.table.item(selected_row, 0).text()
            calories = float(self.table.item(selected_row, 1).text())
            lipids = float(self.table.item(selected_row, 2).text())
            carbs = float(self.table.item(selected_row, 3).text())
            protein = float(self.table.item(selected_row, 4).text())
            price = float(self.table.item(selected_row, 5).text())
            food = (name, calories, lipids, carbs, protein, price)
            dialog = AddFoodDialog(self, food)
            if dialog.exec_():
                self.update_table()
                self.foodAdded.emit()  # Emit the signal after the dialog has successfully added or edited a food

    def open_add_dialog(self):
        dialog = AddFoodDialog(self)
        if dialog.exec_():
            self.update_table()
            self.foodAdded.emit()  # Emit the signal after the dialog has successfully added a food

    def delete_food(self):
        selected_row = self.table.currentRow()
        if selected_row >= 0:
            food_name = self.table.item(selected_row, 0).text()
            delete_food(conn, food_name)
            self.update_table()
            self.foodDeleted.emit()  # Emit the signal after the food is deleted

    def update_table(self):
        foods = get_foods(conn)
        self.table.setRowCount(len(foods))
        for i, (name, calories, lipids, carbs, protein, price) in enumerate(foods):
            nameItem = QTableWidgetItem(name)
            nameItem.setBackground(QColor(173, 216, 230))
            self.table.setItem(i, 0, nameItem)
            self.table.setItem(i, 1, QTableWidgetItem(str(calories)))
            self.table.setItem(i, 2, QTableWidgetItem(str(lipids)))
            self.table.setItem(i, 3, QTableWidgetItem(str(carbs)))
            self.table.setItem(i, 4, QTableWidgetItem(str(protein)))
            self.table.setItem(i, 5, QTableWidgetItem(str(price)))


class MealPlannerDialog(QDialog):
    def __init__(self, parent=None):
        super(MealPlannerDialog, self).__init__(parent)
        self.setWindowTitle('Meal Planner')
        self.setGeometry(300, 300, 600, 500)
        self.layout = QVBoxLayout(self)

        self.conn = create_connection()

        self.genderCombo = QComboBox(self)
        self.genderCombo.addItems(['Male', 'Female'])
        self.weightEdit = QLineEdit(self)
        self.heightEdit = QLineEdit(self)
        self.ageEdit = QLineEdit(self)
        self.bodyFatEdit = QLineEdit(self)
        self.activityCombo = QComboBox(self)
        self.activityCombo.addItems([
            'Sedentary (little or no exercise)',
            'Lightly active (exercise 1-3 times/week)',
            'Active (daily exercise or intense exercise 3-4 times/week)',
            'Very active (intense exercise 6-7 times/week)',
            'Super active (very intense exercise daily, or physical job)'
        ])
        self.primaryGoalCombo = QComboBox(self)
        self.primaryGoalCombo.addItems([
            'Build Muscle (Male - Body Fat % ≤ 8-12, Female - Body Fat % ≤ 18-22)',
            'Lose Fat & Build Muscle Equally (Male - Body Fat % ≈ 12-18, Female - Body Fat % ≈ 22-28)',
            'Lose Fat (Male - Body Fat % ≥ 18-20+, Female - Body Fat % ≥ 28-30+)'
        ])
        self.trainingExperienceGroup = QButtonGroup(self)
        self.beginnerRadio = QRadioButton("Beginner (0-2 years of lifting)")
        self.intermediateRadio = QRadioButton("Intermediate (2-5 years of lifting)")
        self.advancedRadio = QRadioButton("Advanced (5+ years of lifting)")
        self.trainingExperienceGroup.addButton(self.beginnerRadio)
        self.trainingExperienceGroup.addButton(self.intermediateRadio)
        self.trainingExperienceGroup.addButton(self.advancedRadio)

        calculatorLayout = QFormLayout()
        calculatorLayout.addRow('Gender:', self.genderCombo)
        calculatorLayout.addRow('Weight (kg):', self.weightEdit)
        calculatorLayout.addRow('Height (cm):', self.heightEdit)
        calculatorLayout.addRow('Age (years):', self.ageEdit)
        calculatorLayout.addRow('Body Fat %:', self.bodyFatEdit)
        calculatorLayout.addRow('Activity Level:', self.activityCombo)
        calculatorLayout.addRow('Primary Goal:', self.primaryGoalCombo)
        calculatorLayout.addRow("Training Experience:", self.beginnerRadio)
        calculatorLayout.addRow("", self.intermediateRadio)
        calculatorLayout.addRow("", self.advancedRadio)

        self.layout.addLayout(calculatorLayout)

        self.totalPriceLabel = QLabel('Total Price: €0.00', self)  # Add this line
        self.layout.addWidget(self.totalPriceLabel)  # Add this line
        self.needsDisplay = QLabel('Daily Needs: ', self)
        calculatorLayout.addRow(self.needsDisplay)

        self.mealTypeCombo = QComboBox(self)
        self.mealTypeCombo.addItems(['Breakfast', 'Lunch', 'Snack', 'Dinner'])

        self.foodCombo = QComboBox(self)
        self.foodCombo.setMinimumWidth(200)  # Set the minimum width to 200 pixels or another value that fits your needs
        self.quantityEdit = QLineEdit(self)
        self.addButton = QPushButton('Add Meal', self)
        self.clearAllButton = QPushButton('Clear All', self)
        self.deleteMealButton = QPushButton('Delete Meal', self)
        self.clearAllButton.clicked.connect(self.clear_meals)
        self.clearAllButton.setFixedSize(100, 30)
        self.addButton.clicked.connect(self.add_food_to_meal)
        self.deleteMealButton.clicked.connect(self.delete_selected_meal)

        self.mealTable = QTableWidget(self)
        self.mealTable.setColumnCount(8)  # Adjusted for detailed nutritional info
        self.mealTable.setHorizontalHeaderLabels(
            ['Meal', 'Food', 'Quantity (g)', 'Calories (kcal)', 'Proteins (g)', 'Carbs (g)', 'Fats (g)', 'Price (€)'])

        # Layout for Meal Creation
        mealCreationLayout = QHBoxLayout()
        mealCreationLayout.addWidget(self.mealTypeCombo)
        mealCreationLayout.addWidget(self.foodCombo, 1)  # Stretch factor for better spacing
        mealCreationLayout.addWidget(self.quantityEdit)
        mealCreationLayout.addWidget(self.addButton)
        mealCreationLayout.addWidget(self.deleteMealButton)  # Add the delete button to the layout
        mealCreationLayout.addWidget(self.clearAllButton)

        self.layout.addLayout(mealCreationLayout)
        self.layout.addWidget(self.mealTable)

        # Ensure this is called to fetch database information
        self.populate_food_combo()

        self.total_calories = 0.0
        self.total_proteins = 0.0
        self.total_carbs = 0.0
        self.total_fats = 0.0
        self.daily_caloric_needs = 0.0

        self.layout.addLayout(mealCreationLayout)
        self.layout.addWidget(self.mealTable)

        # Connect signals to slots
        self.connect_signals()

    def connect_signals(self):
        # Connect signals from input widgets to the calculate_needs method
        self.genderCombo.currentIndexChanged.connect(self.calculate_needs)
        self.weightEdit.textChanged.connect(self.calculate_needs)
        self.heightEdit.textChanged.connect(self.calculate_needs)
        self.ageEdit.textChanged.connect(self.calculate_needs)
        self.bodyFatEdit.textChanged.connect(self.calculate_needs)
        self.activityCombo.currentIndexChanged.connect(self.calculate_needs)
        self.primaryGoalCombo.currentIndexChanged.connect(self.calculate_needs)
        self.trainingExperienceGroup.buttonClicked.connect(self.calculate_needs)

    def populate_food_combo(self):
        foods = get_foods(self.conn)
        self.foodCombo.clear()  # Clear existing items before adding new ones
        for food in foods:
            self.foodCombo.addItem(food[0])

    def add_food_to_meal(self):
        try:
            meal_type = self.mealTypeCombo.currentText()
            food_name = self.foodCombo.currentText()

            # Check if quantity input is empty
            if self.quantityEdit.text().strip() == '':
                raise ValueError("Quantity cannot be empty")

            quantity = float(self.quantityEdit.text())
            food_nutrition = self.get_nutritional_values(food_name, quantity)

            # Calculate total price
            total_price = food_nutrition['price'] * quantity

            # Update total nutritional values and total price
            self.total_calories += food_nutrition['calories']
            self.total_proteins += food_nutrition['proteins']
            self.total_carbs += food_nutrition['carbs']
            self.total_fats += food_nutrition['fats']
            self.update_intake_ratio_display()

            # Add meal data to the table
            row_position = self.mealTable.rowCount()
            self.mealTable.insertRow(row_position)
            self.mealTable.setItem(row_position, 0, QTableWidgetItem(meal_type))
            self.mealTable.setItem(row_position, 1, QTableWidgetItem(food_name))
            self.mealTable.setItem(row_position, 2, QTableWidgetItem(str(quantity)))
            self.mealTable.setItem(row_position, 3, QTableWidgetItem(str(food_nutrition['calories'])))
            self.mealTable.setItem(row_position, 4, QTableWidgetItem(str(food_nutrition['proteins'])))
            self.mealTable.setItem(row_position, 5, QTableWidgetItem(str(food_nutrition['carbs'])))
            self.mealTable.setItem(row_position, 6, QTableWidgetItem(str(food_nutrition['fats'])))
            self.mealTable.setItem(row_position, 7, QTableWidgetItem(str(total_price)))  # Display total price

            # Recalculate nutritional needs
            self.calculate_needs()

            # Update total price label
            self.update_total_price()

        except ValueError as ve:
            print(f"Error adding food to meal: {ve}")

    def update_total_price(self):
        total_price = sum(float(self.mealTable.item(row, 7).text()) for row in range(self.mealTable.rowCount()))
        self.totalPriceLabel.setText(f'Total Price: €{total_price:.2f}')  # Update the label here

    def get_nutritional_values(self, food_name, quantity):
        food_data = next((food for food in get_foods(self.conn) if food[0] == food_name), None)
        if food_data:
            return {
                'calories': food_data[1] * quantity,
                'proteins': food_data[4] * quantity,
                'carbs': food_data[3] * quantity,
                'fats': food_data[2] * quantity,
                'price': food_data[5]  # Fetch price from database
            }
        else:
            return {'calories': 0, 'proteins': 0, 'carbs': 0, 'fats': 0, 'price': 0}

    def update_intake_ratio_display(self):
        if self.daily_caloric_needs > 0:
            # Assuming self.daily_protein_needs, self.daily_carbs_needs, and self.daily_fats_needs are also calculated and stored
            caloric_ratio = self.total_calories / self.daily_caloric_needs
            protein_ratio = self.total_proteins / self.daily_protein_needs if self.daily_protein_needs > 0 else 0
            carbs_ratio = self.total_carbs / self.daily_carbs_needs if self.daily_carbs_needs > 0 else 0
            fats_ratio = self.total_fats / self.daily_fats_needs if self.daily_fats_needs > 0 else 0

            self.needsDisplay.setText(f"Daily Nutritional Needs:\n"
                                      f"Calories: {self.total_calories:.2f} kcal / {self.daily_caloric_needs:.2f} kcal\n"
                                      f"Protein: {self.total_proteins:.2f} g / {self.daily_protein_needs:.2f} g\n"
                                      f"Carbs: {self.total_carbs:.2f} g / {self.daily_carbs_needs:.2f} g\n"
                                      f"Fats: {self.total_fats:.2f} g / {self.daily_fats_needs:.2f} g")
        else:
            self.needsDisplay.setText("Daily Nutritional Needs: N/A")

    def calculate_needs(self):
        try:
            gender = self.genderCombo.currentText()
            weight = float(self.weightEdit.text())  # Weight in kilograms
            height = float(self.heightEdit.text())  # Height in centimeters
            age = int(self.ageEdit.text())
            activity_level = self.activityCombo.currentText()
            primary_goal = self.primaryGoalCombo.currentText()
            training_experience = self.get_selected_training_experience()

            # Calculate BMR using Mifflin-St Jeor formula
            bmr = 10 * weight + 6.25 * height - 5 * age + (5 if gender == 'Male' else -161)

            # Adjust for activity level
            activity_factors = {
                'Sedentary (little or no exercise)': 1.2,
                'Lightly active (exercise 1-3 times/week)': 1.375,
                'Active (daily exercise or intense exercise 3-4 times/week)': 1.55,
                'Very active (intense exercise 6-7 times/week)': 1.725,
                'Super active (very intense exercise daily, or physical job)': 1.9
            }
            maintenance_calories = bmr * activity_factors[activity_level]

            # Adjust caloric needs based on primary goal and training experience
            if 'Lose Fat & Build Muscle Equally' in primary_goal:
                self.daily_caloric_needs = maintenance_calories
            elif 'Build Muscle' in primary_goal:
                if training_experience == 'Beginner (0-2 years of lifting)':
                    self.daily_caloric_needs = maintenance_calories * 1.25
                elif training_experience == 'Intermediate (2-5 years of lifting)':
                    self.daily_caloric_needs = maintenance_calories * 1.175
                else:  # Advanced (5+ years of lifting)
                    self.daily_caloric_needs = maintenance_calories * 1.15
            else:  # 'Lose Fat'
                self.daily_caloric_needs = maintenance_calories * 0.85  # Adjust as needed

            # Example calculations for protein, carbs, and fat needs
            self.daily_protein_needs = weight * 1.6  # Example: 1.6g per kg of body weight
            self.daily_carbs_needs = (self.daily_caloric_needs * 0.5) / 4  # Example: 50% of calories from carbs
            self.daily_fats_needs = (self.daily_caloric_needs * 0.25) / 9  # Example: 25% of calories from fats

            self.needsDisplay.setText(f"Daily Nutritional Needs:\n"
                                      f"Calories: {self.daily_caloric_needs:.2f} kcal\n"
                                      f"Protein: {self.daily_protein_needs:.2f} g\n"
                                      f"Carbs: {self.daily_carbs_needs:.2f} g\n"
                                      f"Fats: {self.daily_fats_needs:.2f} g")
            self.update_intake_ratio_display()
        except ValueError:
            self.needsDisplay.setText("Invalid input. Please enter correct values.")

    def get_selected_training_experience(self):
        selected_button = self.trainingExperienceGroup.checkedButton()
        if selected_button:
            return selected_button.text()
        return "Unknown"

    def delete_selected_meal(self):
        selected_row = self.mealTable.currentRow()
        if selected_row != -1:  # -1 means no row is selected
            # Update total price by subtracting the price of the deleted meal
            deleted_price = float(self.mealTable.item(selected_row, 7).text())
            self.total_calories -= float(self.mealTable.item(selected_row, 3).text())
            self.total_proteins -= float(self.mealTable.item(selected_row, 4).text())
            self.total_carbs -= float(self.mealTable.item(selected_row, 5).text())
            self.total_fats -= float(self.mealTable.item(selected_row, 6).text())
            self.mealTable.removeRow(selected_row)
            self.update_total_price()  # Update the total price after deletion
            # Recalculate nutritional needs
            self.calculate_needs()

    def clear_meals(self):
        # Clear the meal table
        self.mealTable.setRowCount(0)

        # Reset total nutritional values
        self.total_calories = 0.0
        self.total_proteins = 0.0
        self.total_carbs = 0.0
        self.total_fats = 0.0

        # Update the display
        self.update_intake_ratio_display()

        # Update the total price label
        self.update_total_price()

    def update_food_combo(self):
        self.foodCombo.clear()  # Clear the existing items
        self.populate_food_combo()  # Repopulate the combo box

    def update_total_nutrition(self):
        # Reset total nutritional values
        self.total_calories = 0.0
        self.total_proteins = 0.0
        self.total_carbs = 0.0
        self.total_fats = 0.0

        # Recalculate totals based on remaining items in the table
        for row in range(self.mealTable.rowCount()):
            # Assuming calories, proteins, carbs, and fats are in columns 3, 4, 5, and 6
            self.total_calories += float(self.mealTable.item(row, 3).text())
            self.total_proteins += float(self.mealTable.item(row, 4).text())
            self.total_carbs += float(self.mealTable.item(row, 5).text())
            self.total_fats += float(self.mealTable.item(row, 6).text())

        # Update display of totals
        self.update_intake_ratio_display()


class AddProgressDialog(QDialog):
    def __init__(self, parent=None, progress=None):
        super(AddProgressDialog, self).__init__(parent)
        self.setWindowTitle('Add Measurement Entry' if progress is None else 'Edit Measurement Entry')
        self.layout = QFormLayout(self)
        self.progress = progress

        self.dateEdit = QLineEdit(self)
        self.weightEdit = QLineEdit(self)
        self.bodyFatEdit = QLineEdit(self)
        self.muscleMassEdit = QLineEdit(self)
        self.waistSizeEdit = QLineEdit(self)
        self.chestSizeEdit = QLineEdit(self)
        self.thighSizeEdit = QLineEdit(self)
        self.bicepsSizeEdit = QLineEdit(self)
        self.notesEdit = QLineEdit(self)  # Define notesEdit

        if progress:
            self.dateEdit.setText(progress[1])
            self.weightEdit.setText(str(progress[2]))
            self.bodyFatEdit.setText(str(progress[3]))
            self.muscleMassEdit.setText(str(progress[4]))
            self.waistSizeEdit.setText(str(progress[5]))
            self.chestSizeEdit.setText(str(progress[6]))
            self.thighSizeEdit.setText(str(progress[7]))
            self.bicepsSizeEdit.setText(str(progress[8]))
            self.notesEdit.setText(progress[9])  # Set text for notesEdit

        self.layout.addRow('Date:', self.dateEdit)
        self.layout.addRow('Weight (kg):', self.weightEdit)
        self.layout.addRow('Body Fat (%):', self.bodyFatEdit)
        self.layout.addRow('Muscle Mass (%):', self.muscleMassEdit)
        self.layout.addRow('Waist Size (cm) :', self.waistSizeEdit)
        self.layout.addRow('Chest Size (cm) :', self.chestSizeEdit)
        self.layout.addRow('Thigh Size (cm) :', self.thighSizeEdit)
        self.layout.addRow('Biceps Size (cm) :', self.bicepsSizeEdit)
        self.layout.addRow('Notes:', self.notesEdit)  # Add notesEdit to layout

        self.addButton = QPushButton('Add' if progress is None else 'Update', self)
        self.cancelButton = QPushButton('Cancel', self)
        self.addButton.clicked.connect(self.add_progress)
        self.cancelButton.clicked.connect(self.reject)
        buttonLayout = QHBoxLayout()
        buttonLayout.addWidget(self.addButton)
        buttonLayout.addWidget(self.cancelButton)
        self.layout.addRow(buttonLayout)

    def add_progress(self):
        try:
            date = self.dateEdit.text()
            weight = float(self.weightEdit.text())
            body_fat = float(self.bodyFatEdit.text())
            muscle_mass = float(self.muscleMassEdit.text())
            waist_size = float(self.waistSizeEdit.text())
            chest_size = float(self.chestSizeEdit.text())
            thigh_size = float(self.thighSizeEdit.text())
            biceps_size = float(self.bicepsSizeEdit.text())
            notes = self.notesEdit.text()
            progress = (date, weight, body_fat, muscle_mass, waist_size, chest_size, thigh_size, biceps_size, notes)
            if self.progress:
                update_progress(conn, self.progress[0], progress)
            else:
                insert_progress(conn, progress)
            self.accept()
        except ValueError:
            pass


class ProgressDialog(QDialog):
    def __init__(self, parent=None):
        super(ProgressDialog, self).__init__(parent)
        self.setWindowTitle('Body Measurements Tracker')
        self.setGeometry(300, 300, 600, 400)
        self.layout = QVBoxLayout(self)

        addButton = QPushButton('Add Measurement Entry', self)
        deleteButton = QPushButton('Delete Selected Entry', self)
        bodyCompChartsButton = QPushButton('Body Composition Charts', self)
        bodyMeasurementsChartsButton = QPushButton('Body Measurements Charts', self)

        addButton.clicked.connect(self.open_add_dialog)
        deleteButton.clicked.connect(self.delete_progress)
        bodyCompChartsButton.clicked.connect(self.show_body_composition_charts)
        bodyMeasurementsChartsButton.clicked.connect(self.show_body_measurements_charts)

        buttonLayout = QHBoxLayout()
        buttonLayout.addWidget(addButton)
        buttonLayout.addWidget(deleteButton)
        self.layout.addLayout(buttonLayout)

        chartsButtonLayout = QHBoxLayout()
        chartsButtonLayout.addWidget(bodyCompChartsButton)
        chartsButtonLayout.addWidget(bodyMeasurementsChartsButton)
        self.layout.addLayout(chartsButtonLayout)

        self.table = QTableWidget(self)
        self.table.setColumnCount(10)  # Adjust column count to match the number of columns
        self.table.setHorizontalHeaderLabels(['ID', 'Date', 'Weight', 'Body Fat', 'Muscle Mass', 'Waist Size', 'Chest Size', 'Thigh Size', 'Biceps Size', 'Notes'])
        self.table.setContextMenuPolicy(Qt.CustomContextMenu)
        self.table.customContextMenuRequested.connect(self.show_context_menu)
        self.update_table()
        self.layout.addWidget(self.table)

    def show_context_menu(self, position):
        menu = QMenu(self)
        edit_action = QAction("Edit", self)
        edit_action.triggered.connect(self.edit_progress)
        menu.addAction(edit_action)
        menu.exec_(self.table.mapToGlobal(position))

    def edit_progress(self):
        selected_row = self.table.currentRow()
        if selected_row >= 0:
            progress_id = int(self.table.item(selected_row, 0).text())
            date = self.table.item(selected_row, 1).text()
            weight = float(self.table.item(selected_row, 2).text())
            body_fat = float(self.table.item(selected_row, 3).text())
            muscle_mass = float(self.table.item(selected_row, 4).text())
            waist_size = float(self.table.item(selected_row, 5).text())
            chest_size = float(self.table.item(selected_row, 6).text())
            thigh_size = float(self.table.item(selected_row, 7).text())
            biceps_size = float(self.table.item(selected_row, 8).text())
            notes = self.table.item(selected_row, 9).text()
            progress = (progress_id, date, weight, body_fat, muscle_mass, waist_size, chest_size, thigh_size, biceps_size, notes)
            dialog = AddProgressDialog(self, progress)
            if dialog.exec_():
                self.update_table()

    def open_add_dialog(self):
        dialog = AddProgressDialog(self)
        if dialog.exec_():
            self.update_table()

    def delete_progress(self):
        selected_row = self.table.currentRow()
        if selected_row >= 0:
            progress_id = int(self.table.item(selected_row, 0).text())
            delete_progress(conn, progress_id)
            self.update_table()

    def update_table(self):
        progress_data = get_progress(conn)
        self.table.setRowCount(len(progress_data))
        for i, (progress_id, date, weight, body_fat, muscle_mass, waist_size, chest_size, thigh_size, biceps_size, notes) in enumerate(progress_data):
            self.table.setItem(i, 0, QTableWidgetItem(str(progress_id)))
            self.table.setItem(i, 1, QTableWidgetItem(date))
            self.table.setItem(i, 2, QTableWidgetItem(str(weight)))
            self.table.setItem(i, 3, QTableWidgetItem(str(body_fat)))
            self.table.setItem(i, 4, QTableWidgetItem(str(muscle_mass)))
            self.table.setItem(i, 5, QTableWidgetItem(str(waist_size)))
            self.table.setItem(i, 6, QTableWidgetItem(str(chest_size)))
            self.table.setItem(i, 7, QTableWidgetItem(str(thigh_size)))
            self.table.setItem(i, 8, QTableWidgetItem(str(biceps_size)))
            self.table.setItem(i, 9, QTableWidgetItem(notes))
        self.table.hideColumn(0)

    def show_body_composition_charts(self):
        data = self.get_table_data()
        dialog = BodyCompositionChartsDialog(data)
        dialog.exec_()

    def show_body_measurements_charts(self):
        # Code to display body measurements charts
        pass

    def get_table_data(self):
        rows = self.table.rowCount()
        data = []
        for row in range(rows):
            date = self.table.item(row, 1).text()
            weight = float(self.table.item(row, 2).text())
            body_fat = float(self.table.item(row, 3).text())
            muscle_mass = float(self.table.item(row, 4).text())
            data.append((date, weight, body_fat, muscle_mass))
        return data


class BodyCompositionChartsDialog(QDialog):
    def __init__(self, data, parent=None):
        super(BodyCompositionChartsDialog, self).__init__(parent)
        self.setWindowTitle('Body Composition Charts')
        self.layout = QVBoxLayout(self)

        fig = Figure()
        self.canvas = FigureCanvas(fig)
        self.layout.addWidget(self.canvas)

        self.plot_data(fig, data)

    def plot_data(self, fig, data):
        dates, weights, body_fats, muscle_masses = zip(*data)
        dates = np.array(dates)
        weights = np.array(weights)
        body_fats = np.array(body_fats)
        muscle_masses = np.array(muscle_masses)

        ax1 = fig.add_subplot(211)
        ax2 = fig.add_subplot(212)

        # Plot body fat and muscle mass
        ax1.plot(dates, body_fats, 'ro-', label='Body Fat (%)')
        ax1.plot(dates, muscle_masses, 'go-', label='Muscle Mass (%)')
        ax1.legend(loc='center left', bbox_to_anchor=(1, 0.5))
        ax1.set_xticks([dates[0], dates[-1]])
        ax1.set_xticklabels([dates[0], dates[-1]])
        ax1.set_title('Body Fat and Muscle Mass over Time')
        ax1.set_xlabel('Date')
        ax1.set_ylim(0, 100)  # Ensure y-axis goes from 0 to 100%

        # Plot weight
        ax2.plot(dates, weights, 'bo-', label='Weight (kg)')
        ax2.legend(loc='center left', bbox_to_anchor=(1, 0.5))
        ax2.set_xticks([dates[0], dates[-1]])
        ax2.set_xticklabels([dates[0], dates[-1]])
        ax2.set_title('Weight over Time')
        ax2.set_xlabel('Date')
        max_weight = max(weights)
        ax2.set_ylim(0, max_weight * 1.2)  # Set y-axis to 20% higher than the maximum weight

        fig.tight_layout()
        self.canvas.draw()


class App(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.setGeometry(300, 300, 800, 600)
        self.setWindowTitle('NutriGeniePro')

        # Create the tab widget
        tabWidget = QTabWidget(self)
        foodDatabaseTab = FoodDatabaseDialog()
        mealPlannerTab = MealPlannerDialog()
        progressTab = ProgressDialog()

        # Connect signals to slots if necessary
        foodDatabaseTab.foodAdded.connect(mealPlannerTab.update_food_combo)
        foodDatabaseTab.foodDeleted.connect(mealPlannerTab.update_food_combo)

        # Add tabs to the tab widget
        tabWidget.addTab(foodDatabaseTab, "Food Database")
        tabWidget.addTab(mealPlannerTab, "Meal Planner")
        tabWidget.addTab(progressTab, "Body Measurements Tracker")

        # Set the layout
        layout = QVBoxLayout(self)
        layout.addWidget(tabWidget)
        self.setLayout(layout)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    conn = create_connection()
    create_table(conn)
    create_progress_table(conn)
    mainApp = App()
    mainApp.show()
    sys.exit(app.exec_())
Leave a Comment