import math
import time
import datetime
import customtkinter as ctk
from tkinter import messagebox
# ==========================================
# KONFIGURASI TEMA UI MODERN
# ==========================================
ctk.set_appearance_mode("Light")
ctk.set_default_color_theme("blue")
class PrayerTimeApp(ctk.CTk):
def __init__(self):
super().__init__()
# Setup Jendela Utama (Lebih Compact)
self.title("Dashboard Jadwal Shalat v1.0")
self.geometry("400x550") # Dipersempit agar pas 1 layar
self.minsize(380, 500)
self.configure(fg_color="#f8fafc")
self.grid_columnconfigure(0, weight=1)
self.setup_ui()
self.update_schedule()
def setup_ui(self):
# --- HEADER ---
header_frame = ctk.CTkFrame(self, fg_color="transparent")
# pady dikurangi signifikan
header_frame.grid(row=0, column=0, padx=20, pady=(15, 5), sticky="ew")
title_label = ctk.CTkLabel(header_frame, text="PRAYER TIMES", font=ctk.CTkFont(family="Segoe UI", size=22, weight="bold"), text_color="#2563eb")
title_label.pack()
self.lbl_date = ctk.CTkLabel(header_frame, text="Memuat Tanggal...", font=ctk.CTkFont(size=12), text_color="#64748b")
self.lbl_date.pack(pady=0)
# --- FORM INPUT ---
input_frame = ctk.CTkFrame(self, corner_radius=12, fg_color="#f1f5f9", border_width=1, border_color="#e2e8f0")
input_frame.grid(row=1, column=0, padx=25, pady=5, sticky="ew")
input_frame.grid_columnconfigure((0, 1), weight=1)
# Baris 1: Lintang & Bujur (Jarak dipersempit)
ctk.CTkLabel(input_frame, text="LINTANG (LAT)", font=ctk.CTkFont(size=10, weight="bold"), text_color="#475569").grid(row=0, column=0, padx=(15, 5), pady=(10, 0), sticky="w")
self.inp_lat = ctk.CTkEntry(input_frame, height=28, justify="center")
self.inp_lat.grid(row=1, column=0, padx=(15, 5), pady=(2, 5), sticky="ew")
self.inp_lat.insert(0, "-7.0")
ctk.CTkLabel(input_frame, text="BUJUR (LON)", font=ctk.CTkFont(size=10, weight="bold"), text_color="#475569").grid(row=0, column=1, padx=(5, 15), pady=(10, 0), sticky="w")
self.inp_lon = ctk.CTkEntry(input_frame, height=28, justify="center")
self.inp_lon.grid(row=1, column=1, padx=(5, 15), pady=(2, 5), sticky="ew")
self.inp_lon.insert(0, "110.4")
# Baris 2: TZ & Elevasi
ctk.CTkLabel(input_frame, text="TZ (GMT+)", font=ctk.CTkFont(size=10, weight="bold"), text_color="#475569").grid(row=2, column=0, padx=(15, 5), pady=0, sticky="w")
self.inp_tz = ctk.CTkEntry(input_frame, height=28, justify="center")
self.inp_tz.grid(row=3, column=0, padx=(15, 5), pady=(2, 10), sticky="ew")
self.inp_tz.insert(0, "7")
ctk.CTkLabel(input_frame, text="ELEVASI (m)", font=ctk.CTkFont(size=10, weight="bold"), text_color="#475569").grid(row=2, column=1, padx=(5, 15), pady=0, sticky="w")
self.inp_elv = ctk.CTkEntry(input_frame, height=28, justify="center")
self.inp_elv.grid(row=3, column=1, padx=(5, 15), pady=(2, 10), sticky="ew")
self.inp_elv.insert(0, "10")
# Tombol Update (Tinggi dikurangi)
self.btn_calc = ctk.CTkButton(input_frame, text="UPDATE JADWAL", height=32, font=ctk.CTkFont(weight="bold", size=12), fg_color="#2563eb", hover_color="#1d4ed8", command=self.update_schedule)
self.btn_calc.grid(row=4, column=0, columnspan=2, padx=15, pady=(0, 10), sticky="ew")
# --- KARTU HASIL ---
self.result_frame = ctk.CTkFrame(self, fg_color="transparent")
self.result_frame.grid(row=2, column=0, padx=25, pady=5, sticky="nsew")
self.result_frame.grid_columnconfigure(0, weight=1)
self.prayer_labels = {}
prayers = ["Subuh", "Zuhur", "Ashar", "Maghrib", "Isya"]
for i, name in enumerate(prayers):
# Tinggi kartu dikurangi, jarak antar kartu (pady) dikurangi
card = ctk.CTkFrame(self.result_frame, height=36, corner_radius=8, fg_color="#ffffff", border_width=1, border_color="#e2e8f0")
card.grid(row=i, column=0, pady=2, sticky="ew")
card.grid_columnconfigure(1, weight=1)
lbl_name = ctk.CTkLabel(card, text=name, font=ctk.CTkFont(size=14, weight="bold"), text_color="#1e293b")
lbl_name.grid(row=0, column=0, padx=15, pady=6, sticky="w")
lbl_time = ctk.CTkLabel(card, text="--:--", font=ctk.CTkFont(family="Courier New", size=16, weight="bold"), text_color="#2563eb")
lbl_time.grid(row=0, column=1, padx=15, pady=6, sticky="e")
self.prayer_labels[name] = lbl_time
# ==========================================
# LOGIKA ASTRONOMI & ALGORITMA SHALAT
# ==========================================
def get_julian_date(self):
return (time.time() / 86400.0) + 2440587.5
def format_time(self, decimal_hours):
h = (decimal_hours + 24.0) % 24.0
hours = int(h)
minutes = int((h - hours) * 60)
return f"{hours:02d}:{minutes:02d}"
def get_T(self, h, lat, dec):
rad = math.pi / 180.0
deg = 180.0 / math.pi
val = (math.sin(h * rad) - math.sin(lat * rad) * math.sin(dec * rad)) / (math.cos(lat * rad) * math.cos(dec * rad))
if val < -1.0: val = -1.0
if val > 1.0: val = 1.0
return math.acos(val) * deg / 15.0
def update_schedule(self):
try:
lat = float(self.inp_lat.get())
lon = float(self.inp_lon.get())
tz = float(self.inp_tz.get())
elv = float(self.inp_elv.get())
now = datetime.datetime.now()
days = ["Senin", "Selasa", "Rabu", "Kamis", "Jumat", "Sabtu", "Minggu"]
months = ["Januari", "Februari", "Maret", "April", "Mei", "Juni", "Juli", "Agustus", "September", "Oktober", "November", "Desember"]
date_str = f"{days[now.weekday()]}, {now.day} {months[now.month-1]} {now.year}"
self.lbl_date.configure(text=date_str)
rad = math.pi / 180.0
deg = 180.0 / math.pi
jd = self.get_julian_date()
d = jd - 2451545.0
g = (357.529 + 0.98560028 * d) % 360
q = (280.459 + 0.98564736 * d) % 360
L = (q + 1.915 * math.sin(g * rad) + 0.020 * math.sin(2 * g * rad)) % 360
e = 23.439 - 0.00000036 * d
ra = math.atan2(math.cos(e * rad) * math.sin(L * rad), math.cos(L * rad)) * deg / 15.0
dec = math.asin(math.sin(e * rad) * math.sin(L * rad)) * deg
eqt = (q / 15.0) - ra
transit = 12.0 + tz - (lon / 15.0) - eqt
val_ashar = math.sin(math.atan(1.0 / (1.0 + math.tan(abs(lat - dec) * rad))))
cos_t_ashar = (val_ashar - math.sin(lat * rad) * math.sin(dec * rad)) / (math.cos(lat * rad) * math.cos(dec * rad))
if cos_t_ashar < -1.0: cos_t_ashar = -1.0
elif cos_t_ashar > 1.0: cos_t_ashar = 1.0
t_ashar = math.acos(cos_t_ashar) * deg / 15.0
times = {
"Subuh": transit - self.get_T(-20.0, lat, dec),
"Zuhur": transit + (2.0 / 60.0),
"Ashar": transit + t_ashar,
"Maghrib": transit + self.get_T(-0.8333 - 0.0347 * math.sqrt(elv), lat, dec),
"Isya": transit + self.get_T(-18.0, lat, dec)
}
for name, time_val in times.items():
self.prayer_labels[name].configure(text=self.format_time(time_val))
except ValueError:
messagebox.showerror("Kesalahan", "Pastikan semua input berupa angka (desimal).")
if __name__ == "__main__":
app = PrayerTimeApp()
app.mainloop()