Konten Sub Menu "Kondisi Khusus"
Isi Menu "Profil Kesehatan"
(Konten Sub Menu "Kondisi Khusus")
7. Sub Menu “Kondisi Khusus”
Lanjut kita membuat konten Sub Menu "Kondisi Khusus"
Baik, kita akan buat Sub Menu "Kondisi Khusus" di Tkinter, mirip format sub menu sebelumnya, dengan fitur:
- Form input untuk mengisi keterangan kondisi khusus (misalnya penyakit tertentu, alergi, kehamilan, dsb).
- Data disimpan ke SQLite di folder database aplikasi.
- Riwayat kondisi khusus tampil di layar.
- Ada tombol kembali ke menu profil kesehatan.
- Tampilan dibuat rapi, label rata kiri, entry di bawah label, dan bisa scroll vertikal & horizontal untuk daftar riwayat.
Berikut contoh kode sub_menu_kondisi_khusus.py:
import tkinter as tk
from tkinter import ttk, messagebox
import sqlite3
from datetime import datetime
DATABASE_PATH = "/storage/emulated/0/AppJadi/SmartNutriChefAI/Database/kondisi_khusus.db"
def buat_tabel_kondisi():
conn = sqlite3.connect(DATABASE_PATH)
c = conn.cursor()
c.execute('''CREATE TABLE IF NOT EXISTS kondisi_khusus (
id INTEGER PRIMARY KEY AUTOINCREMENT,
deskripsi TEXT NOT NULL,
tanggal TEXT NOT NULL
)''')
conn.commit()
conn.close()
def simpan_kondisi(deskripsi):
tanggal = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
conn = sqlite3.connect(DATABASE_PATH)
c = conn.cursor()
c.execute("INSERT INTO kondisi_khusus (deskripsi, tanggal) VALUES (?, ?)", (deskripsi, tanggal))
conn.commit()
conn.close()
def ambil_kondisi():
conn = sqlite3.connect(DATABASE_PATH)
c = conn.cursor()
c.execute("SELECT deskripsi, tanggal FROM kondisi_khusus ORDER BY id DESC")
hasil = c.fetchall()
conn.close()
return hasil
def show_sub_menu_kondisi_khusus(root_frame):
from menu_profil_kesehatan import show_menu_profil_kesehatan
buat_tabel_kondisi()
for widget in root_frame.winfo_children():
widget.destroy()
# Judul
tk.Label(root_frame, text="Kondisi Khusus", font=("Arial", 18, "bold"),
bg="green", fg="orange", pady=10).pack(pady=20)
# Frame form input
form_frame = tk.Frame(root_frame)
form_frame.pack(pady=10, fill="x")
tk.Label(form_frame, text="Deskripsi Kondisi Khusus:", font=("Arial", 12), anchor="w").pack(anchor="w", padx=5)
entry_kondisi = tk.Text(form_frame, width=40, height=3)
entry_kondisi.pack(padx=5, pady=5)
def simpan_dan_refresh():
deskripsi = entry_kondisi.get("1.0", tk.END).strip()
if deskripsi:
simpan_kondisi(deskripsi)
entry_kondisi.delete("1.0", tk.END)
tampilkan_kondisi()
else:
messagebox.showwarning("Validasi", "Deskripsi tidak boleh kosong.")
ttk.Separator(root_frame, orient='horizontal').pack(fill='x', padx=40, pady=2)
# Tombol simpan
tk.Button(root_frame, text="Simpan Kondisi", font=("Arial", 12),
bg="#4CAF50", fg="white", command=simpan_dan_refresh).pack(pady=10)
# Scrollable frame untuk riwayat
container = tk.Frame(root_frame)
container.pack(fill="both", expand=True, padx=10, pady=10)
canvas = tk.Canvas(container)
scrollbar_y = tk.Scrollbar(container, orient="vertical", command=canvas.yview)
scrollbar_x = tk.Scrollbar(container, orient="horizontal", command=canvas.xview)
scrollable_frame = tk.Frame(canvas)
scrollable_frame.bind(
"<Configure>",
lambda e: canvas.configure(scrollregion=canvas.bbox("all"))
)
canvas.create_window((0, 0), window=scrollable_frame, anchor="nw")
canvas.configure(yscrollcommand=scrollbar_y.set, xscrollcommand=scrollbar_x.set)
canvas.pack(side="left", fill="both", expand=True)
scrollbar_y.pack(side="right", fill="y")
scrollbar_x.pack(side="bottom", fill="x")
label_kondisi = tk.Label(scrollable_frame, text="", justify="left", font=("Arial", 11), anchor="w")
label_kondisi.pack(anchor="w", padx=10, pady=10)
def tampilkan_kondisi():
kondisi_list = ambil_kondisi()
if not kondisi_list:
label_kondisi.config(text="Belum ada kondisi khusus yang tersimpan.")
else:
teks = "Daftar Kondisi Khusus:\n\n"
for deskripsi, tanggal in kondisi_list:
teks += f"- {deskripsi} ({tanggal})\n"
label_kondisi.config(text=teks)
tampilkan_kondisi()
# Tombol kembali
tk.Button(root_frame, text="Kembali", font=("Arial", 12),
bg="gray", fg="white",
command=lambda: show_menu_profil_kesehatan(root_frame)).pack(pady=20)
🔹 Fitur kode ini:
- Database baru
kondisi_khusus.dbotomatis dibuat jika belum ada. - Form input: Text box tepat di bawah label, label rata kiri.
- Simpan data ke database dan tampilkan langsung tanpa popup.
- Frame daftar riwayat bisa scroll horizontal & vertikal.
- Tombol Kembali langsung kembali ke
menu_profil_kesehatan.
Kalau mau, saya bisa langsung buatkan perubahan di menu_profil_kesehatan.py supaya tombol "Kondisi Khusus" muncul di menu.
Mau saya buatkan sekarang?
Buatkan perubahan di menu_profil_kesehatan.py supaya tombol "Kondisi Khusus" muncul di menu.
Oke, berikut adalah perubahan yang perlu kamu tambahkan pada menu_profil_kesehatan.py supaya tombol "Kondisi Khusus" muncul di menu utama profil kesehatan.
Saya buatkan bagian tombolnya menyatu dengan tombol-tombol sub menu yang sudah ada sebelumnya.
Misalnya kalau menu_profil_kesehatan.py sebelumnya seperti ini:
import tkinter as tk
from sub_menu_data_diri import show_sub_menu_data_diri
from sub_menu_berat_tinggi_badan import show_sub_menu_berat_tinggi_badan
from sub_menu_indeks_massa_tubuh import show_sub_menu_indeks_massa_tubuh
from sub_menu_riwayat_kesehatan import show_sub_menu_riwayat_kesehatan
from sub_menu_kebutuhan_kalori import show_sub_menu_kebutuhan_kalori
from sub_menu_status_gizi import show_sub_menu_status_gizi
def show_menu_profil_kesehatan(root_frame):
for widget in root_frame.winfo_children():
widget.destroy()
tk.Label(root_frame, text="Menu Profil Kesehatan", font=("Arial", 18, "bold"), bg="green", fg="orange", pady=10).pack(pady=20)
# Tombol sub menu
tk.Button(root_frame, text="Data Diri", font=("Arial", 14), width=25, command=lambda: show_sub_menu_data_diri(root_frame)).pack(pady=5)
tk.Button(root_frame, text="Berat & Tinggi Badan", font=("Arial", 14), width=25, command=lambda: show_sub_menu_berat_tinggi_badan(root_frame)).pack(pady=5)
tk.Button(root_frame, text="Indeks Massa Tubuh (IMT)", font=("Arial", 14), width=25, command=lambda: show_sub_menu_indeks_massa_tubuh(root_frame)).pack(pady=5)
tk.Button(root_frame, text="Riwayat Kesehatan", font=("Arial", 14), width=25, command=lambda: show_sub_menu_riwayat_kesehatan(root_frame)).pack(pady=5)
tk.Button(root_frame, text="Kebutuhan Kalori Harian", font=("Arial", 14), width=25, command=lambda: show_sub_menu_kebutuhan_kalori(root_frame)).pack(pady=5)
tk.Button(root_frame, text="Status Gizi", font=("Arial", 14), width=25, command=lambda: show_sub_menu_status_gizi(root_frame)).pack(pady=5)
# Tombol kembali
tk.Button(root_frame, text="Kembali", font=("Arial", 14), width=25, bg="gray", fg="white", command=root_frame.quit).pack(pady=30)
Kita cukup tambahkan import dan buat tombolnya seperti ini:
from sub_menu_kondisi_khusus import show_sub_menu_kondisi_khusus
Lalu tambahkan tombolnya di antara tombol "Status Gizi" dan tombol "Kembali":
tk.Button(root_frame, text="Kondisi Khusus", font=("Arial", 14), width=25,
command=lambda: show_sub_menu_kondisi_khusus(root_frame)).pack(pady=5)
✅ Hasil akhir menu_profil_kesehatan.py (dengan tombol Kondisi Khusus):
import tkinter as tk
from sub_menu_data_diri import show_sub_menu_data_diri
from sub_menu_berat_tinggi_badan import show_sub_menu_berat_tinggi_badan
from sub_menu_indeks_massa_tubuh import show_sub_menu_indeks_massa_tubuh
from sub_menu_riwayat_kesehatan import show_sub_menu_riwayat_kesehatan
from sub_menu_kebutuhan_kalori import show_sub_menu_kebutuhan_kalori
from sub_menu_status_gizi import show_sub_menu_status_gizi
from sub_menu_kondisi_khusus import show_sub_menu_kondisi_khusus # ← Tambahan baru
def show_menu_profil_kesehatan(root_frame):
for widget in root_frame.winfo_children():
widget.destroy()
tk.Label(root_frame, text="Menu Profil Kesehatan", font=("Arial", 18, "bold"),
bg="green", fg="orange", pady=10).pack(pady=20)
# Tombol sub menu
tk.Button(root_frame, text="Data Diri", font=("Arial", 14), width=25,
command=lambda: show_sub_menu_data_diri(root_frame)).pack(pady=5)
tk.Button(root_frame, text="Berat & Tinggi Badan", font=("Arial", 14), width=25,
command=lambda: show_sub_menu_berat_tinggi_badan(root_frame)).pack(pady=5)
tk.Button(root_frame, text="Indeks Massa Tubuh (IMT)", font=("Arial", 14), width=25,
command=lambda: show_sub_menu_indeks_massa_tubuh(root_frame)).pack(pady=5)
tk.Button(root_frame, text="Riwayat Kesehatan", font=("Arial", 14), width=25,
command=lambda: show_sub_menu_riwayat_kesehatan(root_frame)).pack(pady=5)
tk.Button(root_frame, text="Kebutuhan Kalori Harian", font=("Arial", 14), width=25,
command=lambda: show_sub_menu_kebutuhan_kalori(root_frame)).pack(pady=5)
tk.Button(root_frame, text="Status Gizi", font=("Arial", 14), width=25,
command=lambda: show_sub_menu_status_gizi(root_frame)).pack(pady=5)
tk.Button(root_frame, text="Kondisi Khusus", font=("Arial", 14), width=25, # ← Tambahan tombol
command=lambda: show_sub_menu_kondisi_khusus(root_frame)).pack(pady=5)
# Tombol kembali
tk.Button(root_frame, text="Kembali", font=("Arial", 14), width=25,
bg="gray", fg="white", command=root_frame.quit).pack(pady=30)
Kalau kamu mau, saya bisa langsung bikin file sub_menu_kondisi_khusus.py ini terhubung otomatis ke database data_diri.db supaya bisa ikut memanfaatkan informasi pengguna yang sudah tersimpan.
Mau saya sambungkan sekalian?
Untuk tethubung otomatis ke database data_difi.db supaya mendapatkan informasi data pengguna yang tersimpan, kita gunakan file sub_menu_kondisi_khusus.py yang berikut ini saja. Namun kode perlu diperbaiki karena muncul error: Data pengguna tidak ditemukan.
Kode:
import tkinter as tk
from tkinter import ttk, messagebox
import sqlite3
from datetime import datetime
DATABASE_PATH = "/storage/emulated/0/AppJadi/SmartNutriChefAI/Database/kondisi_khusus.db"
def buat_tabel_kondisi():
conn = sqlite3.connect(DATABASE_PATH)
c = conn.cursor()
c.execute('''CREATE TABLE IF NOT EXISTS kondisi_khusus (
id INTEGER PRIMARY KEY AUTOINCREMENT,
deskripsi TEXT NOT NULL,
tanggal TEXT NOT NULL
)''')
conn.commit()
conn.close()
Ambil data pengguna dari database data_diri.db
def ambil_data_pengguna():
try:
conn = sqlite3.connect(DATA_DIRI_DB)
c = conn.cursor()
c.execute("SELECT berat, tinggi, usia, jenis_kelamin FROM data_diri ORDER BY id DESC LIMIT 1")
data = c.fetchone()
conn.close()
return data
except Exception as e:
messagebox.showerror("Error", f"Gagal mengambil data: {e}")
return None
def simpan_kondisi(deskripsi):
tanggal = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
conn = sqlite3.connect(DATABASE_PATH)
c = conn.cursor()
c.execute("INSERT INTO kondisi_khusus (deskripsi, tanggal) VALUES (?, ?)", (deskripsi, tanggal))
conn.commit()
conn.close()
def ambil_kondisi():
conn = sqlite3.connect(DATABASE_PATH)
c = conn.cursor()
c.execute("SELECT deskripsi, tanggal FROM kondisi_khusus ORDER BY id DESC")
hasil = c.fetchall()
conn.close()
return hasil
def show_sub_menu_kondisi_khusus(root_frame):
from menu_profil_kesehatan import show_menu_profil_kesehatan
buat_tabel_kondisi()
for widget in root_frame.winfo_children():
widget.destroy()
# Judul
tk.Label(root_frame, text="Kondisi Khusus", font=("Arial", 18, "bold"),
bg="green", fg="orange", pady=10).pack(pady=20)
# Tampilkan data pengguna
data_pengguna = ambil_data_pengguna()
if data_pengguna:
berat, tinggi, usia, jk = data_pengguna
info = f"Berat: {berat} kg | Tinggi: {tinggi} cm | Usia: {usia} th | Gender: {jk}"
else:
info = "Data pengguna tidak ditemukan."
tk.Label(root_frame, text=info, font=("Arial", 12), fg="blue").pack(pady=5)
# Frame form input
form_frame = tk.Frame(root_frame)
form_frame.pack(pady=10, fill="x")
tk.Label(form_frame, text="Deskripsi Kondisi Khusus:", font=("Arial", 12), anchor="w").pack(anchor="w", padx=5)
entry_kondisi = tk.Text(form_frame, width=40, height=3)
entry_kondisi.pack(padx=5, pady=5)
def simpan_dan_refresh():
deskripsi = entry_kondisi.get("1.0", tk.END).strip()
if deskripsi:
simpan_kondisi(deskripsi)
entry_kondisi.delete("1.0", tk.END)
tampilkan_kondisi()
else:
messagebox.showwarning("Validasi", "Deskripsi tidak boleh kosong.")
ttk.Separator(root_frame, orient='horizontal').pack(fill='x', padx=40, pady=2)
# Tombol simpan
tk.Button(root_frame, text="Simpan Kondisi", font=("Arial", 12),
bg="#4CAF50", fg="white", command=simpan_dan_refresh).pack(pady=10)
# Scrollable frame untuk riwayat
container = tk.Frame(root_frame)
container.pack(fill="both", expand=True, padx=10, pady=10)
canvas = tk.Canvas(container)
scrollbar_y = tk.Scrollbar(container, orient="vertical", command=canvas.yview)
scrollbar_x = tk.Scrollbar(container, orient="horizontal", command=canvas.xview)
scrollable_frame = tk.Frame(canvas)
scrollable_frame.bind(
"<Configure>",
lambda e: canvas.configure(scrollregion=canvas.bbox("all"))
)
canvas.create_window((0, 0), window=scrollable_frame, anchor="nw")
canvas.configure(yscrollcommand=scrollbar_y.set, xscrollcommand=scrollbar_x.set)
canvas.pack(side="left", fill="both", expand=True)
scrollbar_y.pack(side="right", fill="y")
scrollbar_x.pack(side="bottom", fill="x")
label_kondisi = tk.Label(scrollable_frame, text="", justify="left", font=("Arial", 11), anchor="w")
label_kondisi.pack(anchor="w", padx=10, pady=10)
def tampilkan_kondisi():
kondisi_list = ambil_kondisi()
if not kondisi_list:
label_kondisi.config(text="Belum ada kondisi khusus yang tersimpan.")
else:
teks = "Daftar Kondisi Khusus:\n\n"
for deskripsi, tanggal in kondisi_list:
teks += f"- {deskripsi} ({tanggal})\n"
label_kondisi.config(text=teks)
tampilkan_kondisi()
# Tombol kembali
tk.Button(root_frame, text="Kembali", font=("Arial", 12),
bg="gray", fg="white",
command=lambda: show_menu_profil_kesehatan(root_frame)).pack(pady=20)
Saya sudah lihat masalahnya.
Error "Data pengguna tidak ditemukan" terjadi karena di kode Anda, variabelDATA_DIRI_DBbelum pernah didefinisikan, tetapi dipanggil di fungsiambil_data_pengguna().Selain itu, jalur database
data_diri.dbtidak diarahkan otomatis di skrip tersebut.
Kita perlu mendefinisikan path lengkap databasedata_diri.dbseperti yang ada di struktur folder Anda.Berikut perbaikan kodenya supaya:
- Terkoneksi otomatis ke
data_diri.dbuntuk mengambil data pengguna.- Tidak error karena variabel belum didefinisikan.
- Struktur dan fungsi lainnya tetap sama seperti yang Anda buat.
Perbaikan
sub_menu_kondisi_khusus.pyimport tkinter as tk from tkinter import ttk, messagebox import sqlite3 from datetime import datetime # Path database DATA_DIRI_DB = "/storage/emulated/0/AppJadi/SmartNutriChefAI/Database/data_diri.db" DATABASE_PATH = "/storage/emulated/0/AppJadi/SmartNutriChefAI/Database/kondisi_khusus.db" def buat_tabel_kondisi(): conn = sqlite3.connect(DATABASE_PATH) c = conn.cursor() c.execute('''CREATE TABLE IF NOT EXISTS kondisi_khusus ( id INTEGER PRIMARY KEY AUTOINCREMENT, deskripsi TEXT NOT NULL, tanggal TEXT NOT NULL )''') conn.commit() conn.close() # Ambil data pengguna dari database data_diri.db def ambil_data_pengguna(): try: conn = sqlite3.connect(DATA_DIRI_DB) c = conn.cursor() c.execute("SELECT berat, tinggi, usia, jenis_kelamin FROM data_diri ORDER BY id DESC LIMIT 1") data = c.fetchone() conn.close() return data except Exception as e: messagebox.showerror("Error", f"Gagal mengambil data: {e}") return None def simpan_kondisi(deskripsi): tanggal = datetime.now().strftime("%Y-%m-%d %H:%M:%S") conn = sqlite3.connect(DATABASE_PATH) c = conn.cursor() c.execute("INSERT INTO kondisi_khusus (deskripsi, tanggal) VALUES (?, ?)", (deskripsi, tanggal)) conn.commit() conn.close() def ambil_kondisi(): conn = sqlite3.connect(DATABASE_PATH) c = conn.cursor() c.execute("SELECT deskripsi, tanggal FROM kondisi_khusus ORDER BY id DESC") hasil = c.fetchall() conn.close() return hasil def show_sub_menu_kondisi_khusus(root_frame): from menu_profil_kesehatan import show_menu_profil_kesehatan buat_tabel_kondisi() for widget in root_frame.winfo_children(): widget.destroy() # Judul tk.Label(root_frame, text="Kondisi Khusus", font=("Arial", 18, "bold"), bg="green", fg="orange", pady=10).pack(pady=20) # Tampilkan data pengguna data_pengguna = ambil_data_pengguna() if data_pengguna: berat, tinggi, usia, jk = data_pengguna info = f"Berat: {berat} kg | Tinggi: {tinggi} cm | Usia: {usia} th | Gender: {jk}" else: info = "Data pengguna tidak ditemukan." tk.Label(root_frame, text=info, font=("Arial", 12), fg="blue").pack(pady=5) # Frame form input form_frame = tk.Frame(root_frame) form_frame.pack(pady=10, fill="x") tk.Label(form_frame, text="Deskripsi Kondisi Khusus:", font=("Arial", 12), anchor="w").pack(anchor="w", padx=5) entry_kondisi = tk.Text(form_frame, width=40, height=3) entry_kondisi.pack(padx=5, pady=5) def simpan_dan_refresh(): deskripsi = entry_kondisi.get("1.0", tk.END).strip() if deskripsi: simpan_kondisi(deskripsi) entry_kondisi.delete("1.0", tk.END) tampilkan_kondisi() else: messagebox.showwarning("Validasi", "Deskripsi tidak boleh kosong.") ttk.Separator(root_frame, orient='horizontal').pack(fill='x', padx=40, pady=2) # Tombol simpan tk.Button(root_frame, text="Simpan Kondisi", font=("Arial", 12), bg="#4CAF50", fg="white", command=simpan_dan_refresh).pack(pady=10) # Scrollable frame untuk riwayat container = tk.Frame(root_frame) container.pack(fill="both", expand=True, padx=10, pady=10) canvas = tk.Canvas(container) scrollbar_y = tk.Scrollbar(container, orient="vertical", command=canvas.yview) scrollbar_x = tk.Scrollbar(container, orient="horizontal", command=canvas.xview) scrollable_frame = tk.Frame(canvas) scrollable_frame.bind( "<Configure>", lambda e: canvas.configure(scrollregion=canvas.bbox("all")) ) canvas.create_window((0, 0), window=scrollable_frame, anchor="nw") canvas.configure(yscrollcommand=scrollbar_y.set, xscrollcommand=scrollbar_x.set) canvas.pack(side="left", fill="both", expand=True) scrollbar_y.pack(side="right", fill="y") scrollbar_x.pack(side="bottom", fill="x") label_kondisi = tk.Label(scrollable_frame, text="", justify="left", font=("Arial", 11), anchor="w") label_kondisi.pack(anchor="w", padx=10, pady=10) def tampilkan_kondisi(): kondisi_list = ambil_kondisi() if not kondisi_list: label_kondisi.config(text="Belum ada kondisi khusus yang tersimpan.") else: teks = "Daftar Kondisi Khusus:\n\n" for deskripsi, tanggal in kondisi_list: teks += f"- {deskripsi} ({tanggal})\n" label_kondisi.config(text=teks) tampilkan_kondisi() # Tombol kembali tk.Button(root_frame, text="Kembali", font=("Arial", 12), bg="gray", fg="white", command=lambda: show_menu_profil_kesehatan(root_frame)).pack(pady=20)
🔹 Perubahan yang saya lakukan:
- Menambahkan:
supaya koneksiDATA_DIRI_DB = "/storage/emulated/0/AppJadi/SmartNutriChefAI/Database/data_diri.db"ambil_data_pengguna()tidak error.- Jalur database
kondisi_khusus.dbtetap seperti yang Anda buat.- Struktur fungsi tetap sama, jadi integrasinya aman.
Kalau mau, saya bisa tambahkan fitur filter supaya riwayat kondisi khusus bisa ditampilkan per bulan/tahun di sini.
Itu akan memudahkan pencarian catatan lama.
Tolong tombol "kembali" dinaikkan agak ke atas, Tolong tombol "kembali" dinaikkan agak ke atas, buatkan tombol "Kembali" ini fixed position di bawah frame, sehingga posisinya tidak berubah walaupun isi frame di-scroll. Itu membuatnya lebih nyaman dipakai.
Oke, kalau tombol "Kembali" ingin posisinya tetap di bawah dan tidak ikut bergeser saat bagian riwayat di-scroll, caranya adalah memisahkan layout menjadi dua bagian:
- Bagian atas → berisi semua konten yang bisa di-scroll.
- Bagian bawah → berisi tombol "Kembali" yang fixed.
Berarti pada show_sub_menu_kondisi_khusus kita ganti struktur pack() menjadi model Frame atas + Frame bawah seperti ini:
Kode yang sudah diperbaiki (potongan relevan)
def show_sub_menu_kondisi_khusus(root_frame):
from menu_profil_kesehatan import show_menu_profil_kesehatan
buat_tabel_kondisi()
for widget in root_frame.winfo_children():
widget.destroy()
# ====== Bagi frame menjadi atas dan bawah ======
top_frame = tk.Frame(root_frame)
top_frame.pack(fill="both", expand=True)
bottom_frame = tk.Frame(root_frame)
bottom_frame.pack(fill="x")
# Judul
tk.Label(top_frame, text="Kondisi Khusus", font=("Arial", 18, "bold"),
bg="green", fg="orange", pady=10).pack(pady=20)
# Data pengguna
data_pengguna = ambil_data_pengguna()
if data_pengguna:
berat, tinggi, usia, jk = data_pengguna
info = f"Berat: {berat} kg | Tinggi: {tinggi} cm | Usia: {usia} th | Gender: {jk}"
else:
info = "Data pengguna tidak ditemukan."
tk.Label(top_frame, text=info, font=("Arial", 12), fg="blue").pack(pady=5)
# Form input
form_frame = tk.Frame(top_frame)
form_frame.pack(pady=10, fill="x")
tk.Label(form_frame, text="Deskripsi Kondisi Khusus:", font=("Arial", 12), anchor="w").pack(anchor="w", padx=5)
entry_kondisi = tk.Text(form_frame, width=40, height=3)
entry_kondisi.pack(padx=5, pady=5)
def simpan_dan_refresh():
deskripsi = entry_kondisi.get("1.0", tk.END).strip()
if deskripsi:
simpan_kondisi(deskripsi)
entry_kondisi.delete("1.0", tk.END)
tampilkan_kondisi()
else:
messagebox.showwarning("Validasi", "Deskripsi tidak boleh kosong.")
ttk.Separator(top_frame, orient='horizontal').pack(fill='x', padx=40, pady=2)
# Tombol simpan
tk.Button(top_frame, text="Simpan Kondisi", font=("Arial", 12),
bg="#4CAF50", fg="white", command=simpan_dan_refresh).pack(pady=10)
# Scrollable frame untuk riwayat
container = tk.Frame(top_frame)
container.pack(fill="both", expand=True, padx=10, pady=10)
canvas = tk.Canvas(container)
scrollbar_y = tk.Scrollbar(container, orient="vertical", command=canvas.yview)
scrollbar_x = tk.Scrollbar(container, orient="horizontal", command=canvas.xview)
scrollable_frame = tk.Frame(canvas)
scrollable_frame.bind(
"<Configure>",
lambda e: canvas.configure(scrollregion=canvas.bbox("all"))
)
canvas.create_window((0, 0), window=scrollable_frame, anchor="nw")
canvas.configure(yscrollcommand=scrollbar_y.set, xscrollcommand=scrollbar_x.set)
canvas.pack(side="left", fill="both", expand=True)
scrollbar_y.pack(side="right", fill="y")
scrollbar_x.pack(side="bottom", fill="x")
label_kondisi = tk.Label(scrollable_frame, text="", justify="left", font=("Arial", 11), anchor="w")
label_kondisi.pack(anchor="w", padx=10, pady=10)
def tampilkan_kondisi():
kondisi_list = ambil_kondisi()
if not kondisi_list:
label_kondisi.config(text="Belum ada kondisi khusus yang tersimpan.")
else:
teks = "Daftar Kondisi Khusus:\n\n"
for deskripsi, tanggal in kondisi_list:
teks += f"- {deskripsi} ({tanggal})\n"
label_kondisi.config(text=teks)
tampilkan_kondisi()
# ===== Tombol kembali di frame bawah (fixed) =====
tk.Button(bottom_frame, text="Kembali", font=("Arial", 12),
bg="gray", fg="white",
command=lambda: show_menu_profil_kesehatan(root_frame)).pack(pady=5)
Bedanya dengan kode sebelumnya:
top_frameuntuk semua konten yang bisa di-scroll.bottom_framehanya untuk tombol "Kembali" sehingga posisinya selalu tetap di bawah.- Saat scroll di area riwayat, tombol tidak ikut bergerak.
Kalau mau, saya bisa tambahkan border + warna background di bagian bawah untuk membuat tombol "Kembali" lebih menonjol seperti panel footer aplikasi.
Muncul error:
Exception in Tkinter callback
Traceback (most recent call last):
File "/data/user/0/ru.iiec.pydroid3/files/aarch64-linux-android/lib/python3.11/tkinter/init.py", line 1948, in call
return self.func(*args)
^^^^^^^^^^^^^^^^
File "/storage/emulated/0/AppJadi/SmartNutriChefAI/menu_profil_kesehatan.py", line 52, in
command=lambda f=fungsi: f(root_frame)
^^^^^^^^^^^^^
File "/storage/emulated/0/AppJadi/SmartNutriChefAI/sub_menu_kondisi_khusus.py", line 3, in show_sub_menu_kondisi_khusus
buat_tabel_kondisi()
^^^^^^^^^^^^^^^^^^
NameError: name 'buat_tabel_kondisi' is not defined
Oke, error itu muncul karena urutan definisi fungsi di sub_menu_kondisi_khusus.py tidak tepat.
Python mengeksekusi dari atas ke bawah, dan di kode kamu, fungsi show_sub_menu_kondisi_khusus() dipanggil sebelum buat_tabel_kondisi() terdefinisi.
Akibatnya saat tombol diklik, Python belum mengenali fungsi tersebut.
Solusi:
Pastikan semua fungsi (buat_tabel_kondisi, ambil_data_pengguna, simpan_kondisi, ambil_kondisi) didefinisikan di atas show_sub_menu_kondisi_khusus().
Juga, tadi di kode kamu ada bug lain — variabel DATA_DIRI_DB belum ada. Itu harus ditambahkan.
Berikut versi perbaikan full supaya:
- Urutan fungsi benar.
- Variabel
DATA_DIRI_DBada. - Tombol Kembali fixed di bawah.
import tkinter as tk
from tkinter import ttk, messagebox
import sqlite3
from datetime import datetime
# Path database
DATABASE_PATH = "/storage/emulated/0/AppJadi/SmartNutriChefAI/Database/kondisi_khusus.db"
DATA_DIRI_DB = "/storage/emulated/0/AppJadi/SmartNutriChefAI/Database/data_diri.db"
# ===== Fungsi Database =====
def buat_tabel_kondisi():
conn = sqlite3.connect(DATABASE_PATH)
c = conn.cursor()
c.execute('''CREATE TABLE IF NOT EXISTS kondisi_khusus (
id INTEGER PRIMARY KEY AUTOINCREMENT,
deskripsi TEXT NOT NULL,
tanggal TEXT NOT NULL
)''')
conn.commit()
conn.close()
def ambil_data_pengguna():
try:
conn = sqlite3.connect(DATA_DIRI_DB)
c = conn.cursor()
c.execute("SELECT berat, tinggi, usia, jenis_kelamin FROM data_diri ORDER BY id DESC LIMIT 1")
data = c.fetchone()
conn.close()
return data
except Exception as e:
messagebox.showerror("Error", f"Gagal mengambil data: {e}")
return None
def simpan_kondisi(deskripsi):
tanggal = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
conn = sqlite3.connect(DATABASE_PATH)
c = conn.cursor()
c.execute("INSERT INTO kondisi_khusus (deskripsi, tanggal) VALUES (?, ?)", (deskripsi, tanggal))
conn.commit()
conn.close()
def ambil_kondisi():
conn = sqlite3.connect(DATABASE_PATH)
c = conn.cursor()
c.execute("SELECT deskripsi, tanggal FROM kondisi_khusus ORDER BY id DESC")
hasil = c.fetchall()
conn.close()
return hasil
# ===== Fungsi Tampilan =====
def show_sub_menu_kondisi_khusus(root_frame):
from menu_profil_kesehatan import show_menu_profil_kesehatan
buat_tabel_kondisi()
for widget in root_frame.winfo_children():
widget.destroy()
# Frame atas untuk konten scrollable
top_frame = tk.Frame(root_frame)
top_frame.pack(fill="both", expand=True)
# Frame bawah untuk tombol fixed
bottom_frame = tk.Frame(root_frame, bg="#f0f0f0")
bottom_frame.pack(fill="x")
# Judul
tk.Label(top_frame, text="Kondisi Khusus", font=("Arial", 18, "bold"),
bg="green", fg="orange", pady=10).pack(pady=20)
# Data pengguna
data_pengguna = ambil_data_pengguna()
if data_pengguna:
berat, tinggi, usia, jk = data_pengguna
info = f"Berat: {berat} kg | Tinggi: {tinggi} cm \n| Usia: {usia} th | Gender: {jk}"
else:
info = "Data pengguna tidak ditemukan."
tk.Label(top_frame, text=info, font=("Arial", 12), fg="blue").pack(pady=5)
# Form input
form_frame = tk.Frame(top_frame)
form_frame.pack(pady=10, fill="x")
tk.Label(form_frame, text="Deskripsi Kondisi Khusus:", font=("Arial", 12), anchor="w").pack(anchor="w", padx=5)
entry_kondisi = tk.Text(form_frame, width=40, height=3)
entry_kondisi.pack(padx=5, pady=5)
def simpan_dan_refresh():
deskripsi = entry_kondisi.get("1.0", tk.END).strip()
if deskripsi:
simpan_kondisi(deskripsi)
entry_kondisi.delete("1.0", tk.END)
tampilkan_kondisi()
else:
messagebox.showwarning("Validasi", "Deskripsi tidak boleh kosong.")
ttk.Separator(top_frame, orient='horizontal').pack(fill='x', padx=40, pady=2)
# Tombol simpan
tk.Button(top_frame, text="Simpan Kondisi", font=("Arial", 12),
bg="#4CAF50", fg="white", command=simpan_dan_refresh).pack(pady=10)
# Scrollable frame untuk riwayat
container = tk.Frame(top_frame)
container.pack(fill="both", expand=True, padx=10, pady=10)
canvas = tk.Canvas(container)
scrollbar_y = tk.Scrollbar(container, orient="vertical", command=canvas.yview)
scrollbar_x = tk.Scrollbar(container, orient="horizontal", command=canvas.xview)
scrollable_frame = tk.Frame(canvas)
scrollable_frame.bind(
"<Configure>",
lambda e: canvas.configure(scrollregion=canvas.bbox("all"))
)
canvas.create_window((0, 0), window=scrollable_frame, anchor="nw")
canvas.configure(yscrollcommand=scrollbar_y.set, xscrollcommand=scrollbar_x.set)
canvas.pack(side="left", fill="both", expand=True)
scrollbar_y.pack(side="right", fill="y")
scrollbar_x.pack(side="bottom", fill="x")
label_kondisi = tk.Label(scrollable_frame, text="", justify="left", font=("Arial", 11), anchor="w")
label_kondisi.pack(anchor="w", padx=10, pady=10)
def tampilkan_kondisi():
kondisi_list = ambil_kondisi()
if not kondisi_list:
label_kondisi.config(text="Belum ada kondisi khusus yang tersimpan.")
else:
teks = "Daftar Kondisi Khusus:\n\n"
for deskripsi, tanggal in kondisi_list:
teks += f"- {deskripsi} ({tanggal})\n"
label_kondisi.config(text=teks)
tampilkan_kondisi()
# Tombol kembali fixed di bawah
tk.Button(bottom_frame, text="Kembali", font=("Arial", 12),
bg="gray", fg="white",
command=lambda: show_menu_profil_kesehatan(root_frame)).pack(pady=5)
Dengan urutan seperti ini:
- Error
NameError: buat_tabel_kondisihilang. - Variabel
DATA_DIRI_DBsudah jelas. - Tombol Kembali posisinya fixed di bawah frame dan tidak ikut bergeser saat di-scroll.
Kalau mau, aku bisa tambahkan garis border atas di bottom_frame biar tombol "Kembali" kelihatan seperti panel footer.
Mau sekalian aku buatkan biar tampilannya lebih elegan?
Sampai saat ini perubahan, penyempurnaan, dan tambahan file pada applikasi SmartNutriChefAI adalah:
1. menu_profil_kesehatan.py
Kode programnya adalah berikut:
# menu_profil_kesehatan
import tkinter as tk
from tkinter import ttk
from sub_menu_data_diri import show_sub_menu_data_diri
from sub_menu_berat_tinggi_badan import show_sub_menu_berat_tinggi_badan
from sub_menu_indeks_massa_tubuh import show_sub_menu_indeks_massa_tubuh
from sub_menu_riwayat_kesehatan import show_sub_menu_riwayat_kesehatan
from sub_menu_kebutuhan_kalori import show_sub_menu_kebutuhan_kalori
from sub_menu_status_gizi import show_sub_menu_status_gizi
from sub_menu_kondisi_khusus import show_sub_menu_kondisi_khusus
def show_menu_profil_kesehatan(root_frame):
from menu_utama import tampilkan_halaman_utama # Hindari circular import
for widget in root_frame.winfo_children():
widget.destroy()
# Judul
tk.Label(
root_frame,
text="Profil Kesehatan",
font=("Arial", 18, "bold"),
bg="green",
fg="orange",
padx=10,
pady=10
).pack(pady=20)
# Daftar submenu sebagai list tuple: (teks tombol, fungsi yang dipanggil)
submenus = [
("Data Diri", show_sub_menu_data_diri),
("Berat & Tinggi Badan", show_sub_menu_berat_tinggi_badan),
# Tambahkan submenu lain di sini, contoh:
# ("Kalori Harian", show_sub_menu_kalori_harian),
# ("Riwayat BMI", show_sub_menu_riwayat_bmi),
("Indeks Massa Tubuh \n(IMT)", show_sub_menu_indeks_massa_tubuh), # Tambahkan ini
("Riwayat Kesehatan", show_sub_menu_riwayat_kesehatan),
("Kebutuhan Kalori Harian", show_sub_menu_kebutuhan_kalori),
("Status Gizi", show_sub_menu_status_gizi),
("Kondisi Khusus", show_sub_menu_kondisi_khusus),
]
# Buat tombol-tombol sub menu
for teks, fungsi in submenus:
tk.Button(
root_frame,
text=teks,
font=("Arial", 12),
bg="light blue",
fg="black",
command=lambda f=fungsi: f(root_frame)
).pack(pady=10)
# Tambahkan separator antar tombol (kecuali terakhir)
ttk.Separator(root_frame, orient='horizontal').pack(fill='x', padx=40, pady=2)
# Tombol kembali
tk.Button(
root_frame,
text="Kembali ke Menu Utama",
font=("Arial", 12),
bg="gray",
fg="white",
command=lambda: tampilkan_halaman_utama(root_frame)
).pack(pady=30)
2. sub_menu_kondisi_khusus.py
Kode programnya adalah berikut:
# Sub Menu Kondisi Khusus
import tkinter as tk
from tkinter import ttk, messagebox
import sqlite3
from datetime import datetime
# Path database
DATABASE_PATH = "/storage/emulated/0/AppJadi/SmartNutriChefAI/Database/kondisi_khusus.db"
DATA_DIRI_DB = "/storage/emulated/0/AppJadi/SmartNutriChefAI/Database/data_diri.db"
# ===== Fungsi Database =====
def buat_tabel_kondisi():
conn = sqlite3.connect(DATABASE_PATH)
c = conn.cursor()
c.execute('''CREATE TABLE IF NOT EXISTS kondisi_khusus (
id INTEGER PRIMARY KEY AUTOINCREMENT,
deskripsi TEXT NOT NULL,
tanggal TEXT NOT NULL
)''')
conn.commit()
conn.close()
def ambil_data_pengguna():
try:
conn = sqlite3.connect(DATA_DIRI_DB)
c = conn.cursor()
c.execute("SELECT berat, tinggi, usia, jenis_kelamin FROM data_diri ORDER BY id DESC LIMIT 1")
data = c.fetchone()
conn.close()
return data
except Exception as e:
messagebox.showerror("Error", f"Gagal mengambil data: {e}")
return None
def simpan_kondisi(deskripsi):
tanggal = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
conn = sqlite3.connect(DATABASE_PATH)
c = conn.cursor()
c.execute("INSERT INTO kondisi_khusus (deskripsi, tanggal) VALUES (?, ?)", (deskripsi, tanggal))
conn.commit()
conn.close()
def ambil_kondisi():
conn = sqlite3.connect(DATABASE_PATH)
c = conn.cursor()
c.execute("SELECT deskripsi, tanggal FROM kondisi_khusus ORDER BY id DESC")
hasil = c.fetchall()
conn.close()
return hasil
# ===== Fungsi Tampilan =====
def show_sub_menu_kondisi_khusus(root_frame):
from menu_profil_kesehatan import show_menu_profil_kesehatan
buat_tabel_kondisi()
for widget in root_frame.winfo_children():
widget.destroy()
# Frame atas untuk konten scrollable
top_frame = tk.Frame(root_frame)
top_frame.pack(fill="both", expand=True)
# Frame bawah untuk tombol fixed
bottom_frame = tk.Frame(root_frame, bg="#f0f0f0")
bottom_frame.pack(fill="x")
# Judul
tk.Label(top_frame, text="Kondisi Khusus", font=("Arial", 18, "bold"),
bg="green", fg="orange", pady=10).pack(pady=20)
# Data pengguna
data_pengguna = ambil_data_pengguna()
if data_pengguna:
berat, tinggi, usia, jk = data_pengguna
info = f"Berat: {berat} kg | Tinggi: {tinggi} cm \n| Usia: {usia} th | Gender: {jk}"
else:
info = "Data pengguna tidak ditemukan."
tk.Label(top_frame, text=info, font=("Arial", 12), fg="blue").pack(pady=5)
# Form input
form_frame = tk.Frame(top_frame)
form_frame.pack(pady=10, fill="x")
tk.Label(form_frame, text="Deskripsi Kondisi Khusus:", font=("Arial", 12), anchor="w").pack(anchor="w", padx=5)
entry_kondisi = tk.Text(form_frame, width=40, height=3)
entry_kondisi.pack(padx=5, pady=5)
def simpan_dan_refresh():
deskripsi = entry_kondisi.get("1.0", tk.END).strip()
if deskripsi:
simpan_kondisi(deskripsi)
entry_kondisi.delete("1.0", tk.END)
tampilkan_kondisi()
else:
messagebox.showwarning("Validasi", "Deskripsi tidak boleh kosong.")
ttk.Separator(top_frame, orient='horizontal').pack(fill='x', padx=40, pady=2)
# Tombol simpan
tk.Button(top_frame, text="Simpan Kondisi", font=("Arial", 12),
bg="#4CAF50", fg="white", command=simpan_dan_refresh).pack(pady=10)
# Scrollable frame untuk riwayat
container = tk.Frame(top_frame)
container.pack(fill="both", expand=True, padx=10, pady=10)
canvas = tk.Canvas(container)
scrollbar_y = tk.Scrollbar(container, orient="vertical", command=canvas.yview)
scrollbar_x = tk.Scrollbar(container, orient="horizontal", command=canvas.xview)
scrollable_frame = tk.Frame(canvas)
scrollable_frame.bind(
"<Configure>",
lambda e: canvas.configure(scrollregion=canvas.bbox("all"))
)
canvas.create_window((0, 0), window=scrollable_frame, anchor="nw")
canvas.configure(yscrollcommand=scrollbar_y.set, xscrollcommand=scrollbar_x.set)
canvas.pack(side="left", fill="both", expand=True)
scrollbar_y.pack(side="right", fill="y")
scrollbar_x.pack(side="bottom", fill="x")
label_kondisi = tk.Label(scrollable_frame, text="", justify="left", font=("Arial", 11), anchor="w")
label_kondisi.pack(anchor="w", padx=10, pady=10)
def tampilkan_kondisi():
kondisi_list = ambil_kondisi()
if not kondisi_list:
label_kondisi.config(text="Belum ada kondisi khusus yang tersimpan.")
else:
teks = "Daftar Kondisi Khusus:\n\n"
for deskripsi, tanggal in kondisi_list:
teks += f"- {deskripsi} ({tanggal})\n"
label_kondisi.config(text=teks)
tampilkan_kondisi()
# Tombol kembali fixed di bawah
tk.Button(bottom_frame, text="Kembali", font=("Arial", 12),
bg="gray", fg="white",
command=lambda: show_menu_profil_kesehatan(root_frame)).pack(pady=5)
Bersambung Ke: Isi Menu "Data Bahan Makanan"
Comments
Post a Comment