Sub Menu “ Tema Tampilan"
Isi Menu "Setting"
(Konten Sub Menu "Tema Tampilan")
5. Sub Menu “ Tema Tampilan"
Sekarang kita masuk ke Menu "Pengaturan " , kita lanjut membuat konten Sub Menu "Tema Tampilan".
Mantap 🚀, berarti kita sudah punya struktur Menu Utama → Pengaturan → Tema Tampilan.
Saya bikinkan dulu sub_menu_tema_tampilan.py yang rapi dan sinkron dengan sistem translasi (get_text / set_language).
Nanti pengguna bisa pilih tema terang atau tema gelap, terus disimpan ke state (setting_state.py) supaya saat aplikasi dibuka lagi, tema tetap konsisten.
📂 File: sub_menu_tema_tampilan.py
import tkinter as tk
from tkinter import ttk, messagebox
from Setting.utils_text import get_text
from Setting.setting_state import get_tema_aktif, set_tema_aktif
class SubMenuTemaTampilan(tk.Frame):
def __init__(self, parent, controller=None):
super().__init__(parent)
self.controller = controller
# Judul Halaman
lbl_title = tk.Label(
self,
text=get_text("Tema Tampilan"),
font=("Arial", 16, "bold")
)
lbl_title.pack(pady=15)
# Label Pilih Tema
lbl_pilih = tk.Label(self, text=get_text("Pilih Tema:"))
lbl_pilih.pack(pady=5)
# Combobox Tema
self.var_tema = tk.StringVar(value=get_tema_aktif() or "Terang")
self.cmb_tema = ttk.Combobox(
self,
textvariable=self.var_tema,
values=["Terang", "Gelap"],
state="readonly",
width=15
)
self.cmb_tema.pack(pady=5)
# Tombol Simpan
btn_simpan = tk.Button(
self,
text=get_text("Simpan"),
command=self.simpan_tema,
bg="#4CAF50",
fg="white",
relief="raised",
padx=10, pady=5
)
btn_simpan.pack(pady=10)
# Tombol Kembali
btn_back = tk.Button(
self,
text=get_text("Kembali"),
command=self.kembali,
bg="#f44336",
fg="white",
relief="raised",
padx=10, pady=5
)
btn_back.pack(pady=5)
def simpan_tema(self):
"""Simpan pilihan tema ke state"""
tema = self.var_tema.get()
set_tema_aktif(tema)
messagebox.showinfo(
get_text("Informasi"),
get_text("Tema berhasil diubah ke {tema}", tema=tema)
)
def kembali(self):
"""Kembali ke menu pengaturan"""
if self.controller:
self.controller.show_frame("SubMenuPengaturan")
else:
self.destroy()
📂 Tambahan di utils_text.py
Supaya sinkron, kita tambahkan teks translasi untuk Tema Tampilan:
# Tambahan di kamus translations
"Tema Tampilan": "Tema Tampilan",
"Pilih Tema:": "Pilih Tema:",
"Tema berhasil diubah ke {tema}": "Tema berhasil diubah ke {tema}",
📌 Versi bahasa lain juga sama:
- EN →
"Display Theme","Choose Theme:","Theme successfully changed to {tema}". - MS →
"Tema Tampilan","Pilih Tema:","Tema berjaya ditukar ke {tema}".
📂 Tambahan di setting_state.py
Untuk simpan state tema:
# setting_state.py
_tema_aktif = "Terang" # default
def get_tema_aktif():
return _tema_aktif
def set_tema_aktif(tema: str):
global _tema_aktif
_tema_aktif = tema
⚡ Dengan ini:
- Di Menu Pengaturan, kalau klik Tema Tampilan, terbuka halaman pilihan tema.
- Tema bisa dipilih → Simpan → muncul notifikasi pakai
get_text(multibahasa). - Tersimpan di
setting_state.
Mau saya bikinkan contoh integrasi sub_menu_tema_tampilan.py ini ke dalam SubMenuPengaturan, biar tombol "Tema Tampilan" langsung jalan?
Saya sudah membuat kode isi file menu_pengaturan.py berikut:
menu_pengaturan.py
import tkinter as tk
from Setting.sub_menu_ubah_profil_pengguna import show_sub_menu_profil_pengguna
from Setting.sub_menu_preferensi_nutrisi_mmag import show_sub_menu_preferensi_nutrisi_mma
from Setting.sub_menu_bahasa_aplikasi import open_sub_menu_bahasa_aplikasi
from Setting.sub_menu_tema_tampilan import SubMenuTemaTampilan
def show_menu_pengaturan(root_frame):
from menu_utama import tampilkan_halaman_utama # Import lokal agar aman
# Bersihkan frame sebelum tampilkan isi baru
for widget in root_frame.winfo_children():
widget.destroy()
# Judul
label_title = tk.Label(
root_frame,
text="Pengaturan Aplikasi",
font=("Arial", 16, "bold"),
bg="light grey"
)
label_title.pack(pady=10)
# Submenu daftar
submenus = [
("Ubah Profil Pengguna", lambda: show_sub_menu_profil_pengguna(root_frame)),
("Setelan Preferensi Nutrisi MMAG", lambda: show_sub_menu_preferensi_nutrisi_mmag(root_frame)),
("Filter Resep Sesuai Preferensi", lambda: show_sub_menu_filter_resep_mmag(root_frame)),
("Bahasa Aplikasi", lambda: open_sub_menu_bahasa_aplikasi(root_frame, lambda: show_menu_pengaturan(root_frame))),
("Tema Tampilan", lambda: SubMenuTemaTampilan(root_frame)),
]
for submenu in submenus:
if isinstance(submenu, tuple):
text, command = submenu
btn = tk.Button(
root_frame,
text=text,
font=("Arial", 12),
width=35,
height=2,
bg="#fce4ec",
fg="dark green",
command=command
)
else:
# Jika hanya string biasa
btn = tk.Button(
root_frame,
text=submenu,
font=("Arial", 12),
width=35,
height=2,
bg="#fce4ec",
fg="dark green"
)
btn.pack(pady=5)
# Tombol kembali
tombol_kembali = tk.Button(
root_frame,
text="Kembali ke Menu Utama",
font=("Arial", 12),
bg="#d9534f",
fg="white",
command=lambda: tampilkan_halaman_utama(root_frame)
)
tombol_kembali.pack(pady=30)
👍 Saya lihat struktur menu_pengaturan.py-nya sudah rapi, tapi ada beberapa hal yang perlu direvisi agar integrasi dengan SubMenuTemaTampilan berjalan mulus:
1. Bug Import
Di bagian ini ada typo:
from Setting.sub_menu_preferensi_nutrisi_mmag import show_sub_menu_preferensi_nutrisi_mma
➡️ harusnya konsisten namanya show_sub_menu_preferensi_nutrisi_mmag (pakai mmag, bukan mma).
2. Integrasi SubMenuTemaTampilan
Kelas SubMenuTemaTampilan adalah tk.Frame, jadi kalau langsung dipanggil dengan SubMenuTemaTampilan(root_frame) saja, dia belum ditampilkan ke layar. Kita perlu:
- Bersihkan frame (
destroy children) - Instansiasi
SubMenuTemaTampilan pack()supaya muncul
3. Sinkron Translasi
Karena kita sudah pakai utils_text.get_text, sebaiknya label dan tombol juga pakai itu biar otomatis ikut bahasa.
🔧 Revisi menu_pengaturan.py
# menu_pengaturan.py
import tkinter as tk
from Setting.sub_menu_ubah_profil_pengguna import show_sub_menu_profil_pengguna
from Setting.sub_menu_preferensi_nutrisi_mmag import show_sub_menu_preferensi_nutrisi_mmag
from Setting.sub_menu_bahasa_aplikasi import open_sub_menu_bahasa_aplikasi
from Setting.sub_menu_tema_tampilan import SubMenuTemaTampilan
from Setting.utils_text import get_text
def show_menu_pengaturan(root_frame):
from menu_utama import tampilkan_halaman_utama # Import lokal agar aman
# Bersihkan frame sebelum tampilkan isi baru
for widget in root_frame.winfo_children():
widget.destroy()
# Judul
label_title = tk.Label(
root_frame,
text=get_text("Pengaturan Aplikasi"),
font=("Arial", 16, "bold"),
bg="light grey"
)
label_title.pack(pady=10)
# Handler buka Tema Tampilan
def open_tema_tampilan():
for widget in root_frame.winfo_children():
widget.destroy()
frame_tema = SubMenuTemaTampilan(root_frame)
frame_tema.pack(fill="both", expand=True)
# Submenu daftar
submenus = [
(get_text("Ubah Profil Pengguna"), lambda: show_sub_menu_profil_pengguna(root_frame)),
(get_text("Setelan Preferensi Nutrisi MMAG"), lambda: show_sub_menu_preferensi_nutrisi_mmag(root_frame)),
(get_text("Filter Resep Sesuai Preferensi"), lambda: show_sub_menu_filter_resep_mmag(root_frame)),
(get_text("Bahasa Aplikasi"), lambda: open_sub_menu_bahasa_aplikasi(root_frame, lambda: show_menu_pengaturan(root_frame))),
(get_text("Tema Tampilan"), open_tema_tampilan),
]
for text, command in submenus:
btn = tk.Button(
root_frame,
text=text,
font=("Arial", 12),
width=35,
height=2,
bg="#fce4ec",
fg="dark green",
command=command
)
btn.pack(pady=5)
# Tombol kembali
tombol_kembali = tk.Button(
root_frame,
text=get_text("Kembali ke Menu Utama"),
font=("Arial", 12),
bg="#d9534f",
fg="white",
command=lambda: tampilkan_halaman_utama(root_frame)
)
tombol_kembali.pack(pady=30)
📌 Dengan revisi ini:
- Semua teks ikut translasi (
get_text). - Submenu Tema Tampilan sekarang tampil penuh dan bisa kembali ke
Menu Pengaturan. - Typo import sudah beres.
Mau saya bikinkan juga show_sub_menu_filter_resep_mmag (yang sekarang dipanggil tapi belum ada di kodenya) supaya nggak error saat klik Filter Resep Sesuai Preferensi?
Kode isi file utils_text.py apakah begini:
Setting/utils_text.py
from Setting.translations import translations
from Setting.setting_state import get_bahasa_aktif
==============================
Kamus Translasi Lengkap
==============================
translations = {
"id": {
# Umum
"Informasi": "Informasi",
"Berhasil": "Berhasil",
"Gagal": "Gagal",
"Kembali": "Kembali",
"Simpan": "Simpan",
"Reset": "Reset",
"Ya": "Ya",
"Tidak": "Tidak",
# Menu Utama
"Menu Utama": "Menu Utama",
"Rekomendasi Resep": "Rekomendasi Resep",
"Fitur Lengkap Resep MMAG_Human": "Fitur Lengkap Resep MMAG_Human",
"Pengaturan": "Pengaturan",
"Keluar": "Keluar",
# Menu Pengaturan
"Pengaturan Aplikasi": "Pengaturan Aplikasi",
"Ubah Profil Pengguna": "Ubah Profil Pengguna",
"Setelan Preferensi Nutrisi MMAG": "Setelan Preferensi Nutrisi MMAG",
"Filter Resep Sesuai Preferensi": "Filter Resep Sesuai Preferensi",
"Bahasa Aplikasi": "Bahasa Aplikasi",
"Tema Tampilan": "Tema Tampilan",
# Bahasa
"Pengaturan Bahasa": "Pengaturan Bahasa",
"Menu Bahasa Aplikasi": "Menu Bahasa Aplikasi",
"Pilih Bahasa:": "Pilih Bahasa:",
"Bahasa berhasil diubah ke {lang}": "Bahasa berhasil diubah ke {lang}",
# Resep
"Hitung Nutrisi": "Hitung Nutrisi",
"Preferensi Nutrisi": "Preferensi Nutrisi",
"Cari Resep": "Cari Resep",
"Hasil Rekomendasi": "Hasil Rekomendasi",
"Kategori Resep": "Kategori Resep",
"Bahan": "Bahan",
"Takaran": "Takaran",
"Tambah Bahan": "Tambah Bahan",
"Hapus Bahan": "Hapus Bahan",
"Simpan Resep": "Simpan Resep",
"Resep Tersimpan": "Resep Tersimpan",
"Tidak ada resep ditemukan": "Tidak ada resep ditemukan",
# Nutrisi
"Kalori": "Kalori",
"Protein": "Protein",
"Karbohidrat": "Karbohidrat",
"Lemak": "Lemak",
"Vitamin": "Vitamin",
"Mineral": "Mineral",
"Total Nutrisi": "Total Nutrisi",
"Preferensi Nutrisi Tersimpan": "Preferensi Nutrisi Tersimpan",
# Contoh format dinamis
"{jumlah} kalori": "{jumlah} kalori",
"Resep ditemukan: {n}": "Resep ditemukan: {n}",
"Total protein: {p} gram": "Total protein: {p} gram",
},
"en": {
# General
"Informasi": "Information",
"Berhasil": "Success",
"Gagal": "Failed",
"Kembali": "Back",
"Simpan": "Save",
"Reset": "Reset",
"Ya": "Yes",
"Tidak": "No",
# Main Menu
"Menu Utama": "Main Menu",
"Rekomendasi Resep": "Recipe Recommendations",
"Fitur Lengkap Resep MMAG_Human": "Full Recipe Features MMAG_Human",
"Pengaturan": "Settings",
"Keluar": "Exit",
# Settings Menu
"Pengaturan Aplikasi": "Application Settings",
"Ubah Profil Pengguna": "Edit User Profile",
"Setelan Preferensi Nutrisi MMAG": "MMAG Nutrition Preferences",
"Filter Resep Sesuai Preferensi": "Filter Recipes by Preference",
"Bahasa Aplikasi": "Application Language",
"Tema Tampilan": "Theme",
# Language
"Pengaturan Bahasa": "Language Settings",
"Menu Bahasa Aplikasi": "Application Language Menu",
"Pilih Bahasa:": "Choose Language:",
"Bahasa berhasil diubah ke {lang}": "Language successfully changed to {lang}",
# Recipes
"Hitung Nutrisi": "Calculate Nutrition",
"Preferensi Nutrisi": "Nutrition Preferences",
"Cari Resep": "Search Recipe",
"Hasil Rekomendasi": "Recommendation Results",
"Kategori Resep": "Recipe Category",
"Bahan": "Ingredient",
"Takaran": "Portion",
"Tambah Bahan": "Add Ingredient",
"Hapus Bahan": "Remove Ingredient",
"Simpan Resep": "Save Recipe",
"Resep Tersimpan": "Recipe Saved",
"Tidak ada resep ditemukan": "No recipes found",
# Nutrition
"Kalori": "Calories",
"Protein": "Protein",
"Karbohidrat": "Carbohydrates",
"Lemak": "Fat",
"Vitamin": "Vitamins",
"Mineral": "Minerals",
"Total Nutrisi": "Total Nutrition",
"Preferensi Nutrisi Tersimpan": "Nutrition Preferences Saved",
# Dynamic text
"{jumlah} kalori": "{jumlah} calories",
"Resep ditemukan: {n}": "Recipes found: {n}",
"Total protein: {p} gram": "Total protein: {p} grams",
},
"ms": {
# Umum
"Informasi": "Maklumat",
"Berhasil": "Berjaya",
"Gagal": "Gagal",
"Kembali": "Kembali",
"Simpan": "Simpan",
"Reset": "Set Semula",
"Ya": "Ya",
"Tidak": "Tidak",
# Menu Utama
"Menu Utama": "Menu Utama",
"Rekomendasi Resep": "Cadangan Resipi",
"Fitur Lengkap Resep MMAG_Human": "Ciri Lengkap Resipi MMAG_Human",
"Pengaturan": "Tetapan",
"Keluar": "Keluar",
# Menu Pengaturan
"Pengaturan Aplikasi": "Tetapan Aplikasi",
"Ubah Profil Pengguna": "Ubah Profil Pengguna",
"Setelan Preferensi Nutrisi MMAG": "Tetapan Preferensi Nutrisi MMAG",
"Filter Resep Sesuai Preferensi": "Tapis Resipi Ikut Preferensi",
"Bahasa Aplikasi": "Bahasa Aplikasi",
"Tema Tampilan": "Tema",
# Bahasa
"Pengaturan Bahasa": "Tetapan Bahasa",
"Menu Bahasa Aplikasi": "Menu Bahasa Aplikasi",
"Pilih Bahasa:": "Pilih Bahasa:",
"Bahasa berhasil diubah ke {lang}": "Bahasa berjaya ditukar ke {lang}",
# Resipi
"Hitung Nutrisi": "Kira Nutrisi",
"Preferensi Nutrisi": "Preferensi Nutrisi",
"Cari Resep": "Cari Resipi",
"Hasil Rekomendasi": "Hasil Cadangan",
"Kategori Resep": "Kategori Resipi",
"Bahan": "Bahan",
"Takaran": "Sukatan",
"Tambah Bahan": "Tambah Bahan",
"Hapus Bahan": "Padam Bahan",
"Simpan Resep": "Simpan Resipi",
"Resep Tersimpan": "Resipi Disimpan",
"Tidak ada resep ditemukan": "Tiada resipi dijumpai",
# Nutrisi
"Kalori": "Kalori",
"Protein": "Protein",
"Karbohidrat": "Karbohidrat",
"Lemak": "Lemak",
"Vitamin": "Vitamin",
"Mineral": "Mineral",
"Total Nutrisi": "Jumlah Nutrisi",
"Preferensi Nutrisi Tersimpan": "Preferensi Nutrisi Disimpan",
# Dynamic text
"{jumlah} kalori": "{jumlah} kalori",
"Resep ditemukan: {n}": "Resipi dijumpai: {n}",
"Total protein: {p} gram": "Jumlah protein: {p} gram",
}
}
def set_language(lang_code: str):
"""Set bahasa aplikasi"""
global current_lang
if lang_code in translations:
current_lang = lang_code
else:
current_lang = "id" # fallback
==============================
Fungsi Akses
==============================
def get_text(key: str, **kwargs) -> str:
"""Ambil teks sesuai bahasa aktif dengan dukungan format dinamis"""
lang = get_bahasa_aktif() or "id"
lang_dict = translations.get(lang, translations["id"])
text = lang_dict.get(key, key) # fallback ke key jika tidak ada
if kwargs:
try:
return text.format(**kwargs)
except Exception:
return text # kalau format gagal, kembalikan plain
return text
Tambahan di kamus translations
"Tema Tampilan": "Tema Tampilan",
"Pilih Tema:": "Pilih Tema:",
"Tema berhasil diubah ke {tema}": "Tema berhasil diubah ke {tema}",
Saya sudah review kode utils_text.py yang Anda tuliskan. Struktur utamanya sudah benar dan lengkap, hanya ada beberapa poin kecil yang perlu dirapikan:
Catatan:
-
Duplikasi import
translations
Di awal Anda tulis:from Setting.translations import translationslalu di bawah Anda definisikan ulang
translations = {...}.
Ini akan menimpa import tadi. Jadi, sebaiknya hapus baris import pertama agar tidak membingungkan. -
Penambahan entri tema
Di bagian paling bawah Anda tambahkan string seperti ini:"Tema Tampilan": "Tema Tampilan", "Pilih Tema:": "Pilih Tema:", "Tema berhasil diubah ke {tema}": "Tema berhasil diubah ke {tema}",Penempatan ini keliru, karena ada di luar dictionary
translations.
Harusnya masuk ke masing-masing blok bahasa (id,en,ms).
Revisi utils_text.py yang lebih rapi:
# Setting/utils_text.py
from Setting.setting_state import get_bahasa_aktif
# ==============================
# Kamus Translasi Lengkap
# ==============================
translations = {
"id": {
# Umum
"Informasi": "Informasi",
"Berhasil": "Berhasil",
"Gagal": "Gagal",
"Kembali": "Kembali",
"Simpan": "Simpan",
"Reset": "Reset",
"Ya": "Ya",
"Tidak": "Tidak",
# Menu Utama
"Menu Utama": "Menu Utama",
"Rekomendasi Resep": "Rekomendasi Resep",
"Fitur Lengkap Resep MMAG_Human": "Fitur Lengkap Resep MMAG_Human",
"Pengaturan": "Pengaturan",
"Keluar": "Keluar",
# Menu Pengaturan
"Pengaturan Aplikasi": "Pengaturan Aplikasi",
"Ubah Profil Pengguna": "Ubah Profil Pengguna",
"Setelan Preferensi Nutrisi MMAG": "Setelan Preferensi Nutrisi MMAG",
"Filter Resep Sesuai Preferensi": "Filter Resep Sesuai Preferensi",
"Bahasa Aplikasi": "Bahasa Aplikasi",
"Tema Tampilan": "Tema Tampilan",
# Bahasa
"Pengaturan Bahasa": "Pengaturan Bahasa",
"Menu Bahasa Aplikasi": "Menu Bahasa Aplikasi",
"Pilih Bahasa:": "Pilih Bahasa:",
"Bahasa berhasil diubah ke {lang}": "Bahasa berhasil diubah ke {lang}",
# Tema
"Pilih Tema:": "Pilih Tema:",
"Tema berhasil diubah ke {tema}": "Tema berhasil diubah ke {tema}",
# Resep
"Hitung Nutrisi": "Hitung Nutrisi",
"Preferensi Nutrisi": "Preferensi Nutrisi",
"Cari Resep": "Cari Resep",
"Hasil Rekomendasi": "Hasil Rekomendasi",
"Kategori Resep": "Kategori Resep",
"Bahan": "Bahan",
"Takaran": "Takaran",
"Tambah Bahan": "Tambah Bahan",
"Hapus Bahan": "Hapus Bahan",
"Simpan Resep": "Simpan Resep",
"Resep Tersimpan": "Resep Tersimpan",
"Tidak ada resep ditemukan": "Tidak ada resep ditemukan",
# Nutrisi
"Kalori": "Kalori",
"Protein": "Protein",
"Karbohidrat": "Karbohidrat",
"Lemak": "Lemak",
"Vitamin": "Vitamin",
"Mineral": "Mineral",
"Total Nutrisi": "Total Nutrisi",
"Preferensi Nutrisi Tersimpan": "Preferensi Nutrisi Tersimpan",
# Contoh format dinamis
"{jumlah} kalori": "{jumlah} kalori",
"Resep ditemukan: {n}": "Resep ditemukan: {n}",
"Total protein: {p} gram": "Total protein: {p} gram",
},
"en": {
# General
"Informasi": "Information",
"Berhasil": "Success",
"Gagal": "Failed",
"Kembali": "Back",
"Simpan": "Save",
"Reset": "Reset",
"Ya": "Yes",
"Tidak": "No",
# Main Menu
"Menu Utama": "Main Menu",
"Rekomendasi Resep": "Recipe Recommendations",
"Fitur Lengkap Resep MMAG_Human": "Full Recipe Features MMAG_Human",
"Pengaturan": "Settings",
"Keluar": "Exit",
# Settings Menu
"Pengaturan Aplikasi": "Application Settings",
"Ubah Profil Pengguna": "Edit User Profile",
"Setelan Preferensi Nutrisi MMAG": "MMAG Nutrition Preferences",
"Filter Resep Sesuai Preferensi": "Filter Recipes by Preference",
"Bahasa Aplikasi": "Application Language",
"Tema Tampilan": "Theme",
# Language
"Pengaturan Bahasa": "Language Settings",
"Menu Bahasa Aplikasi": "Application Language Menu",
"Pilih Bahasa:": "Choose Language:",
"Bahasa berhasil diubah ke {lang}": "Language successfully changed to {lang}",
# Theme
"Pilih Tema:": "Choose Theme:",
"Tema berhasil diubah ke {tema}": "Theme successfully changed to {tema}",
# Recipes
"Hitung Nutrisi": "Calculate Nutrition",
"Preferensi Nutrisi": "Nutrition Preferences",
"Cari Resep": "Search Recipe",
"Hasil Rekomendasi": "Recommendation Results",
"Kategori Resep": "Recipe Category",
"Bahan": "Ingredient",
"Takaran": "Portion",
"Tambah Bahan": "Add Ingredient",
"Hapus Bahan": "Remove Ingredient",
"Simpan Resep": "Save Recipe",
"Resep Tersimpan": "Recipe Saved",
"Tidak ada resep ditemukan": "No recipes found",
# Nutrition
"Kalori": "Calories",
"Protein": "Protein",
"Karbohidrat": "Carbohydrates",
"Lemak": "Fat",
"Vitamin": "Vitamins",
"Mineral": "Minerals",
"Total Nutrisi": "Total Nutrition",
"Preferensi Nutrisi Tersimpan": "Nutrition Preferences Saved",
# Dynamic text
"{jumlah} kalori": "{jumlah} calories",
"Resep ditemukan: {n}": "Recipes found: {n}",
"Total protein: {p} gram": "Total protein: {p} grams",
},
"ms": {
# Umum
"Informasi": "Maklumat",
"Berhasil": "Berjaya",
"Gagal": "Gagal",
"Kembali": "Kembali",
"Simpan": "Simpan",
"Reset": "Set Semula",
"Ya": "Ya",
"Tidak": "Tidak",
# Menu Utama
"Menu Utama": "Menu Utama",
"Rekomendasi Resep": "Cadangan Resipi",
"Fitur Lengkap Resep MMAG_Human": "Ciri Lengkap Resipi MMAG_Human",
"Pengaturan": "Tetapan",
"Keluar": "Keluar",
# Menu Pengaturan
"Pengaturan Aplikasi": "Tetapan Aplikasi",
"Ubah Profil Pengguna": "Ubah Profil Pengguna",
"Setelan Preferensi Nutrisi MMAG": "Tetapan Preferensi Nutrisi MMAG",
"Filter Resep Sesuai Preferensi": "Tapis Resipi Ikut Preferensi",
"Bahasa Aplikasi": "Bahasa Aplikasi",
"Tema Tampilan": "Tema",
# Bahasa
"Pengaturan Bahasa": "Tetapan Bahasa",
"Menu Bahasa Aplikasi": "Menu Bahasa Aplikasi",
"Pilih Bahasa:": "Pilih Bahasa:",
"Bahasa berhasil diubah ke {lang}": "Bahasa berjaya ditukar ke {lang}",
# Tema
"Pilih Tema:": "Pilih Tema:",
"Tema berhasil diubah ke {tema}": "Tema berjaya ditukar ke {tema}",
# Resipi
"Hitung Nutrisi": "Kira Nutrisi",
"Preferensi Nutrisi": "Preferensi Nutrisi",
"Cari Resep": "Cari Resipi",
"Hasil Rekomendasi": "Hasil Cadangan",
"Kategori Resep": "Kategori Resipi",
"Bahan": "Bahan",
"Takaran": "Sukatan",
"Tambah Bahan": "Tambah Bahan",
"Hapus Bahan": "Padam Bahan",
"Simpan Resep": "Simpan Resipi",
"Resep Tersimpan": "Resipi Disimpan",
"Tidak ada resep ditemukan": "Tiada resipi dijumpai",
# Nutrisi
"Kalori": "Kalori",
"Protein": "Protein",
"Karbohidrat": "Karbohidrat",
"Lemak": "Lemak",
"Vitamin": "Vitamin",
"Mineral": "Mineral",
"Total Nutrisi": "Jumlah Nutrisi",
"Preferensi Nutrisi Tersimpan": "Preferensi Nutrisi Disimpan",
# Dynamic text
"{jumlah} kalori": "{jumlah} kalori",
"Resep ditemukan: {n}": "Resipi dijumpai: {n}",
"Total protein: {p} gram": "Jumlah protein: {p} gram",
}
}
# ==============================
# Fungsi Set dan Get Bahasa
# ==============================
current_lang = "id"
def set_language(lang_code: str):
"""Set bahasa aplikasi"""
global current_lang
if lang_code in translations:
current_lang = lang_code
else:
current_lang = "id" # fallback
def get_text(key: str, **kwargs) -> str:
"""Ambil teks sesuai bahasa aktif dengan dukungan format dinamis"""
lang = get_bahasa_aktif() or current_lang
lang_dict = translations.get(lang, translations["id"])
text = lang_dict.get(key, key) # fallback ke key jika tidak ada
if kwargs:
try:
return text.format(**kwargs)
except Exception:
return text # kalau format gagal, kembalikan plain
return text
Mau saya bikinkan juga contoh nyata integrasi get_text untuk submenu Tema Tampilan supaya langsung terlihat bagaimana Pilih Tema: dan Tema berhasil diubah ke {tema} bekerja?
Ketika tombol "Kembali" ditekan, program tidak mau kembali ke Menu Pengaturan.
Oke 👍 saya paham masalahnya.
Penyebab utama biasanya karena tombol "Kembali" di dalam sub_menu_bahasa_aplikasi.py (atau sub_menu_tema_tampilan.py) tidak diarahkan ke show_menu_pengaturan(root_frame), melainkan hanya root_frame.destroy() atau tidak ada callback yang benar.
Cara perbaikan
Supaya tombol Kembali bisa benar-benar balik ke Menu Pengaturan, kita perlu:
-
Pastikan
show_menu_pengaturandi-import secara lokal agar tidak menimbulkan circular import.
Misalnya:from menu_pengaturan import show_menu_pengaturan -
Pada tombol "Kembali", ganti command menjadi:
command=lambda: show_menu_pengaturan(root_frame)
Contoh revisi di sub_menu_bahasa_aplikasi.py
import tkinter as tk
from tkinter import ttk, messagebox
from Setting.utils_text import get_text, set_language
from Setting.setting_state import set_bahasa_aktif, get_bahasa_aktif
def open_sub_menu_bahasa_aplikasi(root_frame, on_back_callback=None):
# Bersihkan frame
for widget in root_frame.winfo_children():
widget.destroy()
# Judul
label_title = tk.Label(
root_frame,
text=get_text("Pengaturan Bahasa"),
font=("Arial", 16, "bold")
)
label_title.pack(pady=10)
# Dropdown bahasa
label_bahasa = tk.Label(root_frame, text=get_text("Pilih Bahasa:"))
label_bahasa.pack(pady=5)
combo = ttk.Combobox(
root_frame,
values=["id", "en", "ms"],
state="readonly"
)
combo.set(get_bahasa_aktif() or "id")
combo.pack(pady=5)
def simpan_bahasa():
selected = combo.get()
set_bahasa_aktif(selected)
set_language(selected)
messagebox.showinfo(get_text("Informasi"), get_text("Bahasa berhasil diubah ke {lang}", lang=selected))
btn_simpan = tk.Button(root_frame, text=get_text("Simpan"), command=simpan_bahasa)
btn_simpan.pack(pady=10)
# Tombol kembali
if on_back_callback:
btn_back = tk.Button(
root_frame,
text=get_text("Kembali"),
bg="#d9534f", fg="white",
command=on_back_callback # ← langsung panggil callback dari menu_pengaturan
)
else:
from menu_pengaturan import show_menu_pengaturan
btn_back = tk.Button(
root_frame,
text=get_text("Kembali"),
bg="#d9534f", fg="white",
command=lambda: show_menu_pengaturan(root_frame) # fallback
)
btn_back.pack(pady=20)
👉 Jadi ketika Anda buka sub_menu_bahasa_aplikasi dari menu_pengaturan.py, panggil begini:
("Bahasa Aplikasi", lambda: open_sub_menu_bahasa_aplikasi(root_frame, lambda: show_menu_pengaturan(root_frame))),
Mau saya buatkan juga revisi serupa untuk sub_menu_tema_tampilan.py supaya tombol "Kembali" di sana juga benar-benar balik ke Menu Pengaturan?
Maksud saya, pada sub_menu_tema_tampilan.py in, Ketika tombol "Kembali" ditekan, program tidak mau kembali ke Menu Pengaturan.
Kode:
import tkinter as tk
from tkinter import ttk, messagebox
from Setting.utils_text import get_text
from Setting.setting_state import get_tema_aktif, set_tema_aktif
class SubMenuTemaTampilan(tk.Frame):
def init(self, parent, controller=None):
super().init(parent)
self.controller = controller
# Judul Halaman
lbl_title = tk.Label(
self,
text=get_text("Tema Tampilan"),
font=("Arial", 16, "bold")
)
lbl_title.pack(pady=15)
# Label Pilih Tema
lbl_pilih = tk.Label(self, text=get_text("Pilih Tema:"))
lbl_pilih.pack(pady=5)
# Combobox Tema
self.var_tema = tk.StringVar(value=get_tema_aktif() or "Terang")
self.cmb_tema = ttk.Combobox(
self,
textvariable=self.var_tema,
values=["Terang", "Gelap"],
state="readonly",
width=15
)
self.cmb_tema.pack(pady=5)
# Tombol Simpan
btn_simpan = tk.Button(
self,
text=get_text("Simpan"),
command=self.simpan_tema,
bg="#4CAF50",
fg="white",
relief="raised",
padx=10, pady=5
)
btn_simpan.pack(pady=10)
# Tombol Kembali
btn_back = tk.Button(
self,
text=get_text("Kembali"),
command=self.kembali,
bg="#f44336",
fg="white",
relief="raised",
padx=10, pady=5
)
btn_back.pack(pady=5)
def simpan_tema(self):
"""Simpan pilihan tema ke state"""
tema = self.var_tema.get()
set_tema_aktif(tema)
messagebox.showinfo(
get_text("Informasi"),
get_text("Tema berhasil diubah ke {tema}", tema=tema)
)
def kembali(self):
"""Kembali ke menu pengaturan"""
if self.controller:
self.controller.show_frame("SubMenuPengaturan")
else:
self.destroy()
Oke 👍 saya paham masalahnya.
Penyebab tombol "Kembali" tidak berfungsi biasanya ada di bagian controller. Dari kode yang kamu kirim, self.controller.show_frame("SubMenuPengaturan") dipanggil, tapi kalau:
controllertidak pernah diberikan saat membuatSubMenuTemaTampilan, makaself.controller=None.- Atau nama frame yang dipanggil (
"SubMenuPengaturan") tidak sama dengan nama key yang terdaftar dicontroller.frames.
Solusi
Pastikan di menu_utama.py atau modul utama yang mengatur navigasi frame:
SubMenuPengaturanmemang sudah ditambahkan ke dalamframes.- Nama key yang dipakai cocok (case sensitive).
- Saat membuat
SubMenuTemaTampilan,controllerbenar-benar diberikan.
Contoh revisi sub_menu_tema_tampilan.py supaya lebih aman:
import tkinter as tk
from tkinter import ttk, messagebox
from Setting.utils_text import get_text
from Setting.setting_state import get_tema_aktif, set_tema_aktif
class SubMenuTemaTampilan(tk.Frame):
def __init__(self, parent, controller=None):
super().__init__(parent)
self.controller = controller
# Judul Halaman
lbl_title = tk.Label(
self,
text=get_text("Tema Tampilan"),
font=("Arial", 16, "bold")
)
lbl_title.pack(pady=15)
# Label Pilih Tema
lbl_pilih = tk.Label(self, text=get_text("Pilih Tema:"))
lbl_pilih.pack(pady=5)
# Combobox Tema
self.var_tema = tk.StringVar(value=get_tema_aktif() or "Terang")
self.cmb_tema = ttk.Combobox(
self,
textvariable=self.var_tema,
values=["Terang", "Gelap"],
state="readonly",
width=15
)
self.cmb_tema.pack(pady=5)
# Tombol Simpan
btn_simpan = tk.Button(
self,
text=get_text("Simpan"),
command=self.simpan_tema,
bg="#4CAF50",
fg="white",
relief="raised",
padx=10, pady=5
)
btn_simpan.pack(pady=10)
# Tombol Kembali
btn_back = tk.Button(
self,
text=get_text("Kembali"),
command=self.kembali,
bg="#f44336",
fg="white",
relief="raised",
padx=10, pady=5
)
btn_back.pack(pady=5)
def simpan_tema(self):
"""Simpan pilihan tema ke state"""
tema = self.var_tema.get()
set_tema_aktif(tema)
messagebox.showinfo(
get_text("Informasi"),
get_text("Tema berhasil diubah ke {tema}", tema=tema)
)
def kembali(self):
"""Kembali ke menu pengaturan"""
if self.controller:
try:
self.controller.show_frame("SubMenuPengaturan")
except KeyError:
messagebox.showwarning(
get_text("Peringatan"),
get_text("Frame 'SubMenuPengaturan' tidak ditemukan di controller")
)
else:
self.destroy()
👉 Jadi yang perlu dicek:
- Apakah
SubMenuPengaturansudah ada di controller?
Misalnya dimenu_utama.py:self.frames["SubMenuPengaturan"] = SubMenuPengaturan(parent=self.container, controller=self) - Apakah nama string
"SubMenuPengaturan"sesuai? (jangan salah tulis"submenuPengaturan"atau lain).
Mau saya bikinkan contoh potongan menu_utama.py biar jelas gimana cara mendaftarkan SubMenuPengaturan dan SubMenuTemaTampilan di controller?
Untuk Fungsi tombol "Kembali" gunakan kode yang ini saja:
def kembali():
for w in root_frame.winfo_children():
w.destroy()
from menu_pengaturan import show_menu_pengaturan
show_menu_pengaturan(root_frame)
tk.Button(frame_btn_bottom, text="↩ Kembali", width=12, bg="#757575", fg="white", command=kembali).pack(pady=5)
Sip ✅ kalau begitu berarti konsep navigasi aplikasimu bukan pakai controller/show_frame, tapi pakai destroy + load ulang frame (manual switching).
Maka fungsi kembali() di sub_menu_tema_tampilan.py sebaiknya langsung pakai pola yang kamu kasih. Contohnya:
import tkinter as tk
from tkinter import ttk, messagebox
from Setting.utils_text import get_text
from Setting.setting_state import get_tema_aktif, set_tema_aktif
class SubMenuTemaTampilan(tk.Frame):
def __init__(self, root_frame):
super().__init__(root_frame)
self.root_frame = root_frame
# Judul Halaman
lbl_title = tk.Label(
self,
text=get_text("Tema Tampilan"),
font=("Arial", 16, "bold")
)
lbl_title.pack(pady=15)
# Label Pilih Tema
lbl_pilih = tk.Label(self, text=get_text("Pilih Tema:"))
lbl_pilih.pack(pady=5)
# Combobox Tema
self.var_tema = tk.StringVar(value=get_tema_aktif() or "Terang")
self.cmb_tema = ttk.Combobox(
self,
textvariable=self.var_tema,
values=["Terang", "Gelap"],
state="readonly",
width=15
)
self.cmb_tema.pack(pady=5)
# Tombol Simpan
btn_simpan = tk.Button(
self,
text=get_text("Simpan"),
command=self.simpan_tema,
bg="#4CAF50",
fg="white",
relief="raised",
padx=10, pady=5
)
btn_simpan.pack(pady=10)
# Tombol Kembali
btn_back = tk.Button(
self,
text="↩ " + get_text("Kembali"),
command=self.kembali,
bg="#757575",
fg="white",
width=12,
relief="raised",
padx=10, pady=5
)
btn_back.pack(pady=5)
def simpan_tema(self):
"""Simpan pilihan tema ke state"""
tema = self.var_tema.get()
set_tema_aktif(tema)
messagebox.showinfo(
get_text("Informasi"),
get_text("Tema berhasil diubah ke {tema}", tema=tema)
)
def kembali(self):
"""Kembali ke menu pengaturan"""
for w in self.root_frame.winfo_children():
w.destroy()
from menu_pengaturan import show_menu_pengaturan
show_menu_pengaturan(self.root_frame)
🔑 Perbedaan utama:
- Konstruktor hanya butuh
root_frame(nggak pakai controller lagi). - Tombol kembali menghancurkan (
destroy) semua widget diroot_frame, lalu load ulangmenu_pengaturan.
Mau saya bikinkan juga contoh file menu_pengaturan.py dengan fungsi show_menu_pengaturan(root_frame) supaya langsung nyambung dengan kode ini?
Pada sub_menu_tema_tampilan.py, Ketika tema di combobox dipilih, misalnya: tema gelap, lalu tombol "Simpan" ditekan, maka tidak ada perubahan apa apa yang terjadi
Betul 🔍, penyebabnya karena di kode sub_menu_tema_tampilan.py kamu sudah simpan state tema (set_tema_aktif(tema)) dan munculkan popup, tapi belum ada fungsi yang benar-benar mengubah warna tema pada seluruh elemen Tkinter.
Jadi meskipun nilai tersimpan, UI tetap sama.
📌 Solusi:
Kita perlu tambahkan fungsi untuk menerapkan tema (ubah background, warna teks, warna tombol, dsb.) setelah tombol Simpan ditekan.
Contoh revisi sub_menu_tema_tampilan.py:
import tkinter as tk
from tkinter import ttk, messagebox
from Setting.utils_text import get_text
from Setting.setting_state import get_tema_aktif, set_tema_aktif
class SubMenuTemaTampilan(tk.Frame):
def __init__(self, root_frame):
super().__init__(root_frame)
self.root_frame = root_frame
# Judul Halaman
lbl_title = tk.Label(
self,
text=get_text("Tema Tampilan"),
font=("Arial", 16, "bold")
)
lbl_title.pack(pady=15)
# Label Pilih Tema
lbl_pilih = tk.Label(self, text=get_text("Pilih Tema:"))
lbl_pilih.pack(pady=5)
# Combobox Tema
self.var_tema = tk.StringVar(value=get_tema_aktif() or "Terang")
self.cmb_tema = ttk.Combobox(
self,
textvariable=self.var_tema,
values=["Terang", "Gelap"],
state="readonly",
width=15
)
self.cmb_tema.pack(pady=5)
# Tombol Simpan
btn_simpan = tk.Button(
self,
text=get_text("Simpan"),
command=self.simpan_tema,
bg="#4CAF50",
fg="white",
relief="raised",
padx=10, pady=5
)
btn_simpan.pack(pady=10)
# Tombol Kembali
btn_back = tk.Button(
self,
text=get_text("Kembali"),
command=lambda: self.kembali(root_frame),
bg="#f44336",
fg="white",
relief="raised",
padx=10, pady=5
)
btn_back.pack(pady=5)
def simpan_tema(self):
"""Simpan pilihan tema ke state dan terapkan"""
tema = self.var_tema.get()
set_tema_aktif(tema)
# Terapkan tema ke root
self.terapkan_tema(tema)
# Info popup
messagebox.showinfo(
get_text("Informasi"),
get_text("Tema berhasil diubah ke {tema}", tema=tema)
)
def terapkan_tema(self, tema):
"""Update warna background/foreground seluruh root_frame"""
if tema == "Gelap":
bg_color = "#2e2e2e"
fg_color = "white"
btn_bg = "#444"
btn_fg = "white"
else: # default terang
bg_color = "white"
fg_color = "black"
btn_bg = "#f0f0f0"
btn_fg = "black"
# Update root_frame dan anak-anaknya
root = self.root_frame.winfo_toplevel()
root.configure(bg=bg_color)
self.update_colors(self.root_frame, bg_color, fg_color, btn_bg, btn_fg)
def update_colors(self, widget, bg, fg, btn_bg, btn_fg):
"""Recursive: update semua child widgets"""
for child in widget.winfo_children():
if isinstance(child, tk.Frame):
child.configure(bg=bg)
self.update_colors(child, bg, fg, btn_bg, btn_fg)
elif isinstance(child, tk.Label):
child.configure(bg=bg, fg=fg)
elif isinstance(child, tk.Button):
child.configure(bg=btn_bg, fg=btn_fg)
elif isinstance(child, ttk.Combobox):
# ttk widget agak terbatas warnanya
style = ttk.Style()
style.theme_use("default")
style.configure("TCombobox", fieldbackground=bg, foreground=fg)
else:
try:
child.configure(bg=bg, fg=fg)
except:
pass
def kembali(self, root_frame):
"""Kembali ke menu pengaturan"""
for w in root_frame.winfo_children():
w.destroy()
from menu_pengaturan import show_menu_pengaturan
show_menu_pengaturan(root_frame)
📌 Yang ditambahkan:
terapkan_tema(tema)→ fungsi utama ubah warna root + widget.update_colors(widget, ...)→ rekursif supaya semua widget anak juga ikut berubah.- Panggil
self.terapkan_tema(tema)setelah tombol Simpan ditekan.
Mau saya bikinkan juga contoh integrasi awal tema (supaya saat app pertama kali dibuka, tema aktif otomatis diterapkan sesuai setting_state)?
Mau kamu bikinkan contoh integrasi awal tema (supaya saat app pertama kali dibuka, tema aktif otomatis diterapkan sesuai setting_state).
Oke 👍
Kalau kamu ingin tema otomatis diterapkan sejak awal aplikasi dibuka, berarti setelah root = tk.Tk() dibuat, kamu harus cek tema aktif dari setting_state → lalu langsung jalankan fungsi terapkan_tema.
Berikut contoh integrasi awalnya:
🔹 File app_main.py (contoh entry point utama)
import tkinter as tk
from Setting.setting_state import get_tema_aktif
from Setting.sub_menu_tema_tampilan import SubMenuTemaTampilan
from menu_utama import tampilkan_halaman_utama
def terapkan_tema_awal(root, tema):
"""Terapkan tema aktif saat startup"""
if tema == "Gelap":
bg_color = "#2e2e2e"
fg_color = "white"
btn_bg = "#444"
btn_fg = "white"
else: # default Terang
bg_color = "white"
fg_color = "black"
btn_bg = "#f0f0f0"
btn_fg = "black"
root.configure(bg=bg_color)
# Fungsi helper rekursif
def update_colors(widget):
for child in widget.winfo_children():
if isinstance(child, tk.Frame):
child.configure(bg=bg_color)
update_colors(child)
elif isinstance(child, tk.Label):
child.configure(bg=bg_color, fg=fg_color)
elif isinstance(child, tk.Button):
child.configure(bg=btn_bg, fg=btn_fg)
else:
try:
child.configure(bg=bg_color, fg=fg_color)
except:
pass
update_colors(root)
def main():
root = tk.Tk()
root.title("Aplikasi Nutrisi MMAG")
# 🔹 Ambil tema aktif dari setting_state
tema = get_tema_aktif() or "Terang"
# 🔹 Terapkan tema awal
terapkan_tema_awal(root, tema)
# Tampilkan menu utama
tampilkan_halaman_utama(root)
root.mainloop()
if __name__ == "__main__":
main()
🔹 Integrasi dengan SubMenuTemaTampilan
Karena SubMenuTemaTampilan tadi sudah punya terapkan_tema(tema) dan update_colors, maka:
- Saat user klik Simpan, perubahan langsung kelihatan.
- Saat app dibuka kembali,
app_main.pylangsung bacaget_tema_aktif()dan panggilterapkan_tema_awal.
📌 Dengan begini, user akan melihat tema terakhir yang dipilih langsung diterapkan saat aplikasi dibuka kembali.
Mau saya rapikan juga supaya fungsi terapkan_tema_awal dan update_colors digabungkan ke dalam satu modul theme_utils.py, biar tidak ada duplikasi antara app_main.py dan sub_menu_tema_tampilan.py?
Bagaimana cara menerapkan tema tampilan ini pada fitur menu atau sub menu lainnya yang ada di dalam applikasi ini.
Mantap, ini pertanyaan penting 👍
Supaya tema tampilan konsisten ke semua menu / sub menu, prinsipnya begini:
🔑 Prinsip Penerapan Tema
- Tema aktif disimpan di
setting_state.py(misalnyatema_aktif = "Gelap"). - Buat satu fungsi global misalnya
terapkan_tema(root_frame)ditheme_utils.py.- Fungsi ini akan mewarnai ulang semua widget (Label, Button, Frame, dsb).
- Setiap kali kamu membuka menu / submenu baru (misalnya
show_menu_pengaturan,show_sub_menu_bahasa_aplikasi, dll), langsung panggilterapkan_tema(root_frame)setelah membangun layout.
🔹 Contoh Implementasi
1. File Setting/theme_utils.py
import tkinter as tk
from Setting.setting_state import get_tema_aktif
def terapkan_tema(widget):
"""Terapkan tema ke widget dan turunannya"""
tema = get_tema_aktif() or "Terang"
if tema == "Gelap":
bg_color = "#2e2e2e"
fg_color = "white"
btn_bg = "#444"
btn_fg = "white"
else: # Terang
bg_color = "white"
fg_color = "black"
btn_bg = "#f0f0f0"
btn_fg = "black"
# Atur warna widget sesuai tipenya
if isinstance(widget, tk.Frame):
widget.configure(bg=bg_color)
elif isinstance(widget, tk.Label):
widget.configure(bg=bg_color, fg=fg_color)
elif isinstance(widget, tk.Button):
widget.configure(bg=btn_bg, fg=btn_fg)
# Rekursif ke anak-anaknya
for child in widget.winfo_children():
terapkan_tema(child)
2. Pemakaian di menu_pengaturan.py
from Setting.theme_utils import terapkan_tema
def show_menu_pengaturan(root_frame):
from menu_utama import tampilkan_halaman_utama
for widget in root_frame.winfo_children():
widget.destroy()
label_title = tk.Label(root_frame, text="Pengaturan Aplikasi", font=("Arial", 16, "bold"))
label_title.pack(pady=10)
submenus = [
("Ubah Profil Pengguna", lambda: show_sub_menu_profil_pengguna(root_frame)),
("Setelan Preferensi Nutrisi MMAG", lambda: show_sub_menu_preferensi_nutrisi_mmag(root_frame)),
("Bahasa Aplikasi", lambda: open_sub_menu_bahasa_aplikasi(root_frame, lambda: show_menu_pengaturan(root_frame))),
("Tema Tampilan", lambda: SubMenuTemaTampilan(root_frame)),
]
for text, command in submenus:
btn = tk.Button(root_frame, text=text, width=35, height=2, command=command)
btn.pack(pady=5)
tombol_kembali = tk.Button(
root_frame,
text="Kembali ke Menu Utama",
command=lambda: tampilkan_halaman_utama(root_frame)
)
tombol_kembali.pack(pady=30)
# 🔹 Terapkan tema setelah semua widget dibuat
terapkan_tema(root_frame)
3. Pemakaian di Sub Menu lain (misalnya sub_menu_bahasa_aplikasi.py)
from Setting.theme_utils import terapkan_tema
def open_sub_menu_bahasa_aplikasi(root_frame, go_back_callback):
for w in root_frame.winfo_children():
w.destroy()
lbl_title = tk.Label(root_frame, text="Pengaturan Bahasa", font=("Arial", 16, "bold"))
lbl_title.pack(pady=10)
# tombol kembali
btn_back = tk.Button(root_frame, text="Kembali", command=go_back_callback)
btn_back.pack(pady=10)
# 🔹 Terapkan tema di sini juga
terapkan_tema(root_frame)
📌 Hasilnya
- Tema selalu mengikuti setting_state (Gelap / Terang).
- Saat user berpindah menu, tema langsung diterapkan tanpa perlu bikin logika pewarnaan di tiap file.
- Kalau user ganti tema lewat SubMenuTemaTampilan, cukup panggil
terapkan_tema(root_frame)di akhir fungsi simpan.
Apakah kamu mau saya bikinkan contoh refaktor SubMenuTemaTampilan juga, supaya saat klik Simpan, dia langsung panggil terapkan_tema(root_frame) ke seluruh halaman aktif?
Berikut ini hasil akhir yang diperoleh:
- Kode isi file menu_pengaturan.py:
# menu_pengaturan.py
import tkinter as tk
from Setting.sub_menu_ubah_profil_pengguna import show_sub_menu_profil_pengguna
from Setting.sub_menu_preferensi_nutrisi_mmag import show_sub_menu_preferensi_nutrisi_mmag
from Setting.sub_menu_bahasa_aplikasi import open_sub_menu_bahasa_aplikasi
from Setting.sub_menu_tema_tampilan import SubMenuTemaTampilan
from Setting.utils_text import get_text
def show_menu_pengaturan(root_frame):
from menu_utama import tampilkan_halaman_utama # Import lokal agar aman
# Bersihkan frame sebelum tampilkan isi baru
for widget in root_frame.winfo_children():
widget.destroy()
# Judul
label_title = tk.Label(
root_frame,
text=get_text("Pengaturan Aplikasi"),
font=("Arial", 16, "bold"),
bg="light grey"
)
label_title.pack(pady=10)
# Handler buka Tema Tampilan
def open_tema_tampilan():
for widget in root_frame.winfo_children():
widget.destroy()
frame_tema = SubMenuTemaTampilan(root_frame)
frame_tema.pack(fill="both", expand=True)
# Submenu daftar
submenus = [
(get_text("Ubah Profil Pengguna"), lambda: show_sub_menu_profil_pengguna(root_frame)),
(get_text("Setelan Preferensi Nutrisi MMAG"), lambda: show_sub_menu_preferensi_nutrisi_mmag(root_frame)),
(get_text("Filter Resep Sesuai Preferensi"), lambda: show_sub_menu_filter_resep_mmag(root_frame)),
(get_text("Bahasa Aplikasi"), lambda: open_sub_menu_bahasa_aplikasi(root_frame, lambda: show_menu_pengaturan(root_frame))),
(get_text("Tema Tampilan"), open_tema_tampilan),
]
for text, command in submenus:
btn = tk.Button(
root_frame,
text=text,
font=("Arial", 12),
width=35,
height=2,
bg="#fce4ec",
fg="dark green",
command=command
)
btn.pack(pady=5)
# Tombol kembali
tombol_kembali = tk.Button(
root_frame,
text=get_text("Kembali ke Menu Utama"),
font=("Arial", 12),
bg="#d9534f",
fg="white",
command=lambda: tampilkan_halaman_utama(root_frame)
)
tombol_kembali.pack(pady=30)
- Kode isi file sub_menu_tema_tampilan.py:
import tkinter as tk
from tkinter import ttk, messagebox
from Setting.utils_text import get_text
from Setting.setting_state import get_tema_aktif, set_tema_aktif
class SubMenuTemaTampilan(tk.Frame):
def __init__(self, root_frame):
super().__init__(root_frame)
self.root_frame = root_frame
# Judul Halaman
lbl_title = tk.Label(
self,
text=get_text("Tema Tampilan"),
font=("Arial", 16, "bold")
)
lbl_title.pack(pady=15)
# Label Pilih Tema
lbl_pilih = tk.Label(self, text=get_text("Pilih Tema:"))
lbl_pilih.pack(pady=5)
# Combobox Tema
self.var_tema = tk.StringVar(value=get_tema_aktif() or "Terang")
self.cmb_tema = ttk.Combobox(
self,
textvariable=self.var_tema,
values=["Terang", "Gelap"],
state="readonly",
width=15
)
self.cmb_tema.pack(pady=5)
# Tombol Simpan
btn_simpan = tk.Button(
self,
text=get_text("Simpan"),
command=self.simpan_tema,
bg="#4CAF50",
fg="white",
relief="raised",
padx=10, pady=5
)
btn_simpan.pack(pady=10)
# Tombol Kembali
btn_back = tk.Button(
self,
text=get_text("Kembali"),
command=lambda: self.kembali(root_frame),
bg="#f44336",
fg="white",
relief="raised",
padx=10, pady=5
)
btn_back.pack(pady=5)
def simpan_tema(self):
"""Simpan pilihan tema ke state dan terapkan"""
tema = self.var_tema.get()
set_tema_aktif(tema)
# Terapkan tema ke root
self.terapkan_tema(tema)
# Info popup
messagebox.showinfo(
get_text("Informasi"),
get_text("Tema berhasil diubah ke {tema}", tema=tema)
)
def terapkan_tema(self, tema):
"""Update warna background/foreground seluruh root_frame"""
if tema == "Gelap":
bg_color = "#2e2e2e"
fg_color = "white"
btn_bg = "#444"
btn_fg = "white"
else: # default terang
bg_color = "white"
fg_color = "black"
btn_bg = "#f0f0f0"
btn_fg = "black"
# Update root_frame dan anak-anaknya
root = self.root_frame.winfo_toplevel()
root.configure(bg=bg_color)
self.update_colors(self.root_frame, bg_color, fg_color, btn_bg, btn_fg)
def update_colors(self, widget, bg, fg, btn_bg, btn_fg):
"""Recursive: update semua child widgets"""
for child in widget.winfo_children():
if isinstance(child, tk.Frame):
child.configure(bg=bg)
self.update_colors(child, bg, fg, btn_bg, btn_fg)
elif isinstance(child, tk.Label):
child.configure(bg=bg, fg=fg)
elif isinstance(child, tk.Button):
child.configure(bg=btn_bg, fg=btn_fg)
elif isinstance(child, ttk.Combobox):
# ttk widget agak terbatas warnanya
style = ttk.Style()
style.theme_use("default")
style.configure("TCombobox", fieldbackground=bg, foreground=fg)
else:
try:
child.configure(bg=bg, fg=fg)
except:
pass
def kembali(self, root_frame):
"""Kembali ke menu pengaturan"""
for w in root_frame.winfo_children():
w.destroy()
from menu_pengaturan import show_menu_pengaturan
show_menu_pengaturan(root_frame)
- Kode isi file theme_utils.py:
import tkinter as tk
from Setting.setting_state import get_tema_aktif
def terapkan_tema(widget):
"""Terapkan tema ke widget dan turunannya"""
tema = get_tema_aktif() or "Terang"
if tema == "Gelap":
bg_color = "#2e2e2e"
fg_color = "white"
btn_bg = "#444"
btn_fg = "white"
else: # Terang
bg_color = "white"
fg_color = "black"
btn_bg = "#f0f0f0"
btn_fg = "black"
# Atur warna widget sesuai tipenya
if isinstance(widget, tk.Frame):
widget.configure(bg=bg_color)
elif isinstance(widget, tk.Label):
widget.configure(bg=bg_color, fg=fg_color)
elif isinstance(widget, tk.Button):
widget.configure(bg=btn_bg, fg=btn_fg)
# Rekursif ke anak-anaknya
for child in widget.winfo_children():
terapkan_tema(child)
- Tambahan Kode pada isi file utils_text.py:
# Setting/utils_text.py
from Setting.setting_state import get_bahasa_aktif
# ==============================
# Kamus Translasi Lengkap
# ==============================
translations = {
"id": {
# Umum
"Informasi": "Informasi",
"Berhasil": "Berhasil",
"Gagal": "Gagal",
"Kembali": "Kembali",
"Simpan": "Simpan",
"Reset": "Reset",
"Ya": "Ya",
"Tidak": "Tidak",
# Menu Utama
"Menu Utama": "Menu Utama",
"Rekomendasi Resep": "Rekomendasi Resep",
"Fitur Lengkap Resep MMAG_Human": "Fitur Lengkap Resep MMAG_Human",
"Pengaturan": "Pengaturan",
"Keluar": "Keluar",
# Menu Pengaturan
"Pengaturan Aplikasi": "Pengaturan Aplikasi",
"Ubah Profil Pengguna": "Ubah Profil Pengguna",
"Setelan Preferensi Nutrisi MMAG": "Setelan Preferensi Nutrisi MMAG",
"Filter Resep Sesuai Preferensi": "Filter Resep Sesuai Preferensi",
"Bahasa Aplikasi": "Bahasa Aplikasi",
"Tema Tampilan": "Tema Tampilan",
# Bahasa
"Pengaturan Bahasa": "Pengaturan Bahasa",
"Menu Bahasa Aplikasi": "Menu Bahasa Aplikasi",
"Pilih Bahasa:": "Pilih Bahasa:",
"Bahasa berhasil diubah ke {lang}": "Bahasa berhasil diubah ke {lang}",
# Tema
"Pilih Tema:": "Pilih Tema:",
"Tema berhasil diubah ke {tema}": "Tema berhasil diubah ke {tema}",
# Resep
"Hitung Nutrisi": "Hitung Nutrisi",
"Preferensi Nutrisi": "Preferensi Nutrisi",
"Cari Resep": "Cari Resep",
"Hasil Rekomendasi": "Hasil Rekomendasi",
"Kategori Resep": "Kategori Resep",
"Bahan": "Bahan",
"Takaran": "Takaran",
"Tambah Bahan": "Tambah Bahan",
"Hapus Bahan": "Hapus Bahan",
"Simpan Resep": "Simpan Resep",
"Resep Tersimpan": "Resep Tersimpan",
"Tidak ada resep ditemukan": "Tidak ada resep ditemukan",
# Nutrisi
"Kalori": "Kalori",
"Protein": "Protein",
"Karbohidrat": "Karbohidrat",
"Lemak": "Lemak",
"Vitamin": "Vitamin",
"Mineral": "Mineral",
"Total Nutrisi": "Total Nutrisi",
"Preferensi Nutrisi Tersimpan": "Preferensi Nutrisi Tersimpan",
# Contoh format dinamis
"{jumlah} kalori": "{jumlah} kalori",
"Resep ditemukan: {n}": "Resep ditemukan: {n}",
"Total protein: {p} gram": "Total protein: {p} gram",
},
"en": {
# General
"Informasi": "Information",
"Berhasil": "Success",
"Gagal": "Failed",
"Kembali": "Back",
"Simpan": "Save",
"Reset": "Reset",
"Ya": "Yes",
"Tidak": "No",
# Main Menu
"Menu Utama": "Main Menu",
"Rekomendasi Resep": "Recipe Recommendations",
"Fitur Lengkap Resep MMAG_Human": "Full Recipe Features MMAG_Human",
"Pengaturan": "Settings",
"Keluar": "Exit",
# Settings Menu
"Pengaturan Aplikasi": "Application Settings",
"Ubah Profil Pengguna": "Edit User Profile",
"Setelan Preferensi Nutrisi MMAG": "MMAG Nutrition Preferences",
"Filter Resep Sesuai Preferensi": "Filter Recipes by Preference",
"Bahasa Aplikasi": "Application Language",
"Tema Tampilan": "Theme",
# Language
"Pengaturan Bahasa": "Language Settings",
"Menu Bahasa Aplikasi": "Application Language Menu",
"Pilih Bahasa:": "Choose Language:",
"Bahasa berhasil diubah ke {lang}": "Language successfully changed to {lang}",
# Theme
"Pilih Tema:": "Choose Theme:",
"Tema berhasil diubah ke {tema}": "Theme successfully changed to {tema}",
# Recipes
"Hitung Nutrisi": "Calculate Nutrition",
"Preferensi Nutrisi": "Nutrition Preferences",
"Cari Resep": "Search Recipe",
"Hasil Rekomendasi": "Recommendation Results",
"Kategori Resep": "Recipe Category",
"Bahan": "Ingredient",
"Takaran": "Portion",
"Tambah Bahan": "Add Ingredient",
"Hapus Bahan": "Remove Ingredient",
"Simpan Resep": "Save Recipe",
"Resep Tersimpan": "Recipe Saved",
"Tidak ada resep ditemukan": "No recipes found",
# Nutrition
"Kalori": "Calories",
"Protein": "Protein",
"Karbohidrat": "Carbohydrates",
"Lemak": "Fat",
"Vitamin": "Vitamins",
"Mineral": "Minerals",
"Total Nutrisi": "Total Nutrition",
"Preferensi Nutrisi Tersimpan": "Nutrition Preferences Saved",
# Dynamic text
"{jumlah} kalori": "{jumlah} calories",
"Resep ditemukan: {n}": "Recipes found: {n}",
"Total protein: {p} gram": "Total protein: {p} grams",
},
"ms": {
# Umum
"Informasi": "Maklumat",
"Berhasil": "Berjaya",
"Gagal": "Gagal",
"Kembali": "Kembali",
"Simpan": "Simpan",
"Reset": "Set Semula",
"Ya": "Ya",
"Tidak": "Tidak",
# Menu Utama
"Menu Utama": "Menu Utama",
"Rekomendasi Resep": "Cadangan Resipi",
"Fitur Lengkap Resep MMAG_Human": "Ciri Lengkap Resipi MMAG_Human",
"Pengaturan": "Tetapan",
"Keluar": "Keluar",
# Menu Pengaturan
"Pengaturan Aplikasi": "Tetapan Aplikasi",
"Ubah Profil Pengguna": "Ubah Profil Pengguna",
"Setelan Preferensi Nutrisi MMAG": "Tetapan Preferensi Nutrisi MMAG",
"Filter Resep Sesuai Preferensi": "Tapis Resipi Ikut Preferensi",
"Bahasa Aplikasi": "Bahasa Aplikasi",
"Tema Tampilan": "Tema",
# Bahasa
"Pengaturan Bahasa": "Tetapan Bahasa",
"Menu Bahasa Aplikasi": "Menu Bahasa Aplikasi",
"Pilih Bahasa:": "Pilih Bahasa:",
"Bahasa berhasil diubah ke {lang}": "Bahasa berjaya ditukar ke {lang}",
# Tema
"Pilih Tema:": "Pilih Tema:",
"Tema berhasil diubah ke {tema}": "Tema berjaya ditukar ke {tema}",
# Resipi
"Hitung Nutrisi": "Kira Nutrisi",
"Preferensi Nutrisi": "Preferensi Nutrisi",
"Cari Resep": "Cari Resipi",
"Hasil Rekomendasi": "Hasil Cadangan",
"Kategori Resep": "Kategori Resipi",
"Bahan": "Bahan",
"Takaran": "Sukatan",
"Tambah Bahan": "Tambah Bahan",
"Hapus Bahan": "Padam Bahan",
"Simpan Resep": "Simpan Resipi",
"Resep Tersimpan": "Resipi Disimpan",
"Tidak ada resep ditemukan": "Tiada resipi dijumpai",
# Nutrisi
"Kalori": "Kalori",
"Protein": "Protein",
"Karbohidrat": "Karbohidrat",
"Lemak": "Lemak",
"Vitamin": "Vitamin",
"Mineral": "Mineral",
"Total Nutrisi": "Jumlah Nutrisi",
"Preferensi Nutrisi Tersimpan": "Preferensi Nutrisi Disimpan",
# Dynamic text
"{jumlah} kalori": "{jumlah} kalori",
"Resep ditemukan: {n}": "Resipi dijumpai: {n}",
"Total protein: {p} gram": "Jumlah protein: {p} gram",
}
}
# ==============================
# Fungsi Set dan Get Bahasa
# ==============================
current_lang = "id"
def set_language(lang_code: str):
"""Set bahasa aplikasi"""
global current_lang
if lang_code in translations:
current_lang = lang_code
else:
current_lang = "id" # fallback
def get_text(key: str, **kwargs) -> str:
"""Ambil teks sesuai bahasa aktif dengan dukungan format dinamis"""
lang = get_bahasa_aktif() or current_lang
lang_dict = translations.get(lang, translations["id"])
text = lang_dict.get(key, key) # fallback ke key jika tidak ada
if kwargs:
try:
return text.format(**kwargs)
except Exception:
return text # kalau format gagal, kembalikan plain
return text
-
- Kode pada isi file setting_state.py:
# Setting/setting_state.py
preferensi_aktif = None
bahasa_aktif = "Indonesia"
def set_preferensi_aktif(nama: str):
global preferensi_aktif
preferensi_aktif = nama
def get_preferensi_aktif():
return preferensi_aktif
def set_bahasa_aktif(lang: str):
global bahasa_aktif
bahasa_aktif = lang
def get_bahasa_aktif():
return bahasa_aktif
# setting_state.py
_tema_aktif = "Terang" # default
def get_tema_aktif():
return _tema_aktif
def set_tema_aktif(tema: str):
global _tema_aktif
_tema_aktif = tema
Demikian, semoga bermanfaat.
Comments
Post a Comment