{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "798a2ed2",
   "metadata": {},
   "source": [
    "# 16-31 · Tip Calculator Pro\n",
    "\n",
    "Praktyka do sekcji [„Tip Calculator Pro: wersja ostateczna”"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5b73df63",
   "metadata": {},
   "source": [
    "## Cel\n",
    "\n",
    "Złóż ostateczną klasę aplikacji wraz z walidacją i ustawieniami."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "89dea921",
   "metadata": {},
   "source": [
    "## Pro mainloop() w tym laptopie\n",
    "\n",
    "W normalnym życiu `.py`- pliku aplikacja jest uruchamiana przez `root.mainloop()` - To polecenie „zamraża” `root.mainloop()` zadzwonimy `root.update()` (obsługuje zdarzenia raz, bez oczekiwania) a następnie `root.destroy()` — interfejs buduje się dokładnie tak samo, po prostu bez nieskończonego oczekiwania na końcu."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "1b722f24",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-08-18T21:45:26.634487Z",
     "iopub.status.busy": "2026-08-18T21:45:26.634388Z",
     "iopub.status.idle": "2026-08-18T21:45:26.784771Z",
     "shell.execute_reply": "2026-08-18T21:45:26.784313Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "75.00\n",
      "Введите целое число\n"
     ]
    }
   ],
   "source": [
    "import json\n",
    "import tkinter as tk\n",
    "from tkinter import ttk\n",
    "from pathlib import Path\n",
    "\n",
    "SETTINGS_PATH = Path(\"tip_calculator_settings_test.json\")\n",
    "SETTINGS_PATH.unlink(missing_ok=True)\n",
    "DEFAULT_SETTINGS = {\"last_percent\": \"15\"}\n",
    "\n",
    "def load_settings():\n",
    "    if not SETTINGS_PATH.exists():\n",
    "        return dict(DEFAULT_SETTINGS)\n",
    "    with SETTINGS_PATH.open(\"r\", encoding=\"utf-8\") as f:\n",
    "        return json.load(f)\n",
    "\n",
    "def save_settings(settings):\n",
    "    with SETTINGS_PATH.open(\"w\", encoding=\"utf-8\") as f:\n",
    "        json.dump(settings, f, ensure_ascii=False, indent=2)\n",
    "\n",
    "def calculate_tip(amount, percent, people):\n",
    "    return (amount * percent / 100) / people\n",
    "\n",
    "def parse_number(text):\n",
    "    text = text.strip()\n",
    "    if not text:\n",
    "        return False, None, \"Поле не должно быть пустым\"\n",
    "    try:\n",
    "        return True, float(text), \"\"\n",
    "    except ValueError:\n",
    "        return False, None, \"Введите число\"\n",
    "\n",
    "def validate_positive_amount(text):\n",
    "    ok, value, message = parse_number(text)\n",
    "    if not ok:\n",
    "        return False, message\n",
    "    if value <= 0:\n",
    "        return False, \"Число должно быть больше нуля\"\n",
    "    return True, \"\"\n",
    "\n",
    "def validate_positive_int(text):\n",
    "    ok, value, message = parse_number(text)\n",
    "    if not ok:\n",
    "        return False, message\n",
    "    if value != int(value):\n",
    "        return False, \"Введите целое число\"\n",
    "    if int(value) < 1:\n",
    "        return False, \"Число должно быть не меньше 1\"\n",
    "    return True, \"\"\n",
    "\n",
    "class TipCalculatorApp:\n",
    "    def __init__(self, root):\n",
    "        self.root = root\n",
    "        self.settings = load_settings()\n",
    "        self.amount_var = tk.StringVar()\n",
    "        self.percent_var = tk.StringVar(value=self.settings[\"last_percent\"])\n",
    "        self.people_var = tk.StringVar(value=\"1\")\n",
    "        self.result_var = tk.StringVar()\n",
    "        ttk.Entry(root, textvariable=self.amount_var).pack()\n",
    "        ttk.Entry(root, textvariable=self.people_var).pack()\n",
    "        ttk.Button(root, text=\"Считать\", command=self.on_calculate).pack()\n",
    "\n",
    "    def on_calculate(self):\n",
    "        ok, message = validate_positive_amount(self.amount_var.get())\n",
    "        if not ok:\n",
    "            self.result_var.set(message)\n",
    "            return\n",
    "        ok_people, message_people = validate_positive_int(self.people_var.get())\n",
    "        if not ok_people:\n",
    "            self.result_var.set(message_people)\n",
    "            return\n",
    "        chaevye = calculate_tip(\n",
    "            float(self.amount_var.get()),\n",
    "            float(self.percent_var.get()),\n",
    "            int(self.people_var.get()),\n",
    "        )\n",
    "        self.result_var.set(f\"{chaevye:.2f}\")\n",
    "        self.settings[\"last_percent\"] = self.percent_var.get()\n",
    "\n",
    "root = tk.Tk()\n",
    "app = TipCalculatorApp(root)\n",
    "app.amount_var.set(\"1000\")\n",
    "app.people_var.set(\"2\")\n",
    "app.on_calculate()\n",
    "result_with_valid_people = app.result_var.get()\n",
    "\n",
    "app.people_var.set(\"2.5\")\n",
    "app.on_calculate()\n",
    "result_with_invalid_people = app.result_var.get()\n",
    "\n",
    "save_settings(app.settings)\n",
    "root.update()\n",
    "print(result_with_valid_people)\n",
    "print(result_with_invalid_people)\n",
    "root.destroy()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Cartesian Python 3.14",
   "language": "python",
   "name": "cartesian-python314"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.14.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
