import customtkinter as ctk
import math
from datetime import datetime
# Set tema dan warna
ctk.set_appearance_mode("light")
ctk.set_default_color_theme("blue")
class FalakCalculator(ctk.CTk):
def __init__(self):
super().__init__()
self.title("Kalkulator Falak Komprehensif (50 Parameter)")
self.geometry("1200x900")
# Konstan Astronomi
self.D2R = math.pi / 180
self.R2D = 180 / math.pi
self.setup_ui()
def setup_ui(self):
# Frame Utama dengan Scroll
self.main_container = ctk.CTkScrollableFrame(self, fg_color="#f0f2f5")
self.main_container.pack(fill="both", expand=True, padx=20, pady=20)
# Title
title_label = ctk.CTkLabel(self.main_container, text="Kalkulator Falak 50 Parameter",
font=("Segoe UI", 28, "bold"), text_color="#1a73e8")
title_label.pack(pady=(10, 30))
# Grid Input
input_frame = ctk.CTkFrame(self.main_container, fg_color="#f8f9fa", corner_radius=15)
input_frame.pack(fill="x", padx=10, pady=10)
for i in range(5): input_frame.grid_columnconfigure(i, weight=1)
self.inputs = {}
fields = [
("Lintang (Lat)", "lat", "-6.9667"),
("Bujur (Lon)", "lon", "110.4167"),
("Tinggi (m)", "alt", "10"),
("Zona Waktu", "tz", "7"),
("Tanggal (YYYY-MM-DD)", "date", datetime.now().strftime("%Y-%m-%d"))
]
for i, (label, key, default) in enumerate(fields):
container = ctk.CTkFrame(input_frame, fg_color="transparent")
container.grid(row=0, column=i, padx=10, pady=15, sticky="nsew")
ctk.CTkLabel(container, text=label, font=("Segoe UI", 12, "bold")).pack(anchor="w")
entry = ctk.CTkEntry(container, placeholder_text=default)
entry.insert(0, default)
entry.pack(fill="x", pady=5)
self.inputs[key] = entry
# Tombol Hitung
self.btn_calc = ctk.CTkButton(self.main_container, text="HITUNG SELURUH PARAMETER",
height=50, font=("Segoe UI", 16, "bold"),
command=self.hitung_utama)
self.btn_calc.pack(fill="x", padx=10, pady=20)
# Area Output (Grid)
self.output_container = ctk.CTkFrame(self.main_container, fg_color="transparent")
self.output_container.pack(fill="both", expand=True)
def rev(self, angle):
return angle - 360 * math.floor(angle / 360)
def format_jam(self, decimal):
if decimal is None: return "--:--:--"
decimal = self.rev(decimal * 15) / 15 # Normalisasi ke 24 jam
h = int(decimal)
m = int((decimal - h) * 60)
s = int(((decimal - h) * 60 - m) * 60)
return f"{h:02d}:{m:02d}:{s:02d}"
def get_h(self, h, d, p):
val = (math.sin(h * self.D2R) - math.sin(p * self.D2R) * math.sin(d * self.D2R)) / (math.cos(p * self.D2R) * math.cos(d * self.D2R))
if val > 1 or val < -1: return None
return math.acos(val) * self.R2D / 15
def hitung_utama(self):
# Bersihkan output
for widget in self.output_container.winfo_children():
widget.destroy()
try:
lat = float(self.inputs['lat'].get())
lon = float(self.inputs['lon'].get())
alt = float(self.inputs['alt'].get())
tz = float(self.inputs['tz'].get())
d_str = self.inputs['date'].get()
d_in = datetime.strptime(d_str, "%Y-%m-%d")
Y, M, D = d_in.year, d_in.month, d_in.day
if M <= 2: Y -= 1; M += 12
# 1. WAKTU DASAR
A = math.floor(Y / 100)
B = 2 - A + math.floor(A / 4)
JD = math.floor(365.25 * (Y + 4716)) + math.floor(30.6001 * (M + 1)) + D + B - 1524.5
T = (JD - 2451545.0) / 36525
gmst = self.rev(280.46061837 + 360.98564736629 * (JD - 2451545.0))
# 2. PARAMETER MATAHARI
L0 = self.rev(280.46646 + 36000.76983 * T)
Msun = self.rev(357.52911 + 35999.05029 * T)
e = 0.016708634 - 0.000042037 * T
C = (1.914602 - 0.004817 * T) * math.sin(Msun * self.D2R) + (0.019993 - 0.000101 * T) * math.sin(2 * Msun * self.D2R)
sunTrueLong = L0 + C
eps0 = 23.439291 - 0.0130041 * T
delta = math.asin(math.sin(eps0 * self.D2R) * math.sin(sunTrueLong * self.D2R)) * self.R2D
RA = math.atan2(math.cos(eps0 * self.D2R) * math.sin(sunTrueLong * self.D2R), math.cos(sunTrueLong * self.D2R)) * self.R2D / 15
EoT = (L0 / 15 - RA) * 60
R_sun = (1.00014 - 0.01671 * math.cos(Msun * self.D2R) - 0.00014 * math.cos(2 * Msun * self.D2R))
SD_sun = 0.2666 / R_sun
# 3. PARAMETER BULAN
Lp = self.rev(218.316 + 481267.881 * T)
Dmoon = self.rev(297.85 + 445267.11 * T)
Mmoon = self.rev(134.96 + 477198.86 * T)
Fmoon = self.rev(93.27 + 483202.01 * T)
Node = self.rev(125.04 - 1934.136 * T)
HP_moon = 0.9507 / R_sun # Sederhana
# 4. KOREKSI UFUK
Dip = 1.76 / 60 * math.sqrt(alt)
Refr = 34.5 / 60
h_maghrib = -(SD_sun + Refr + Dip)
# 5. WAKTU SALAT
Transit = (12 + tz - lon / 15 - EoT / 60)
H_subuh = self.get_h(-20.0, delta, lat)
H_isya = self.get_h(-18.0, delta, lat)
H_maghrib = self.get_h(h_maghrib, delta, lat)
H_syuruq = self.get_h(h_maghrib, delta, lat) # Menggunakan h yang sama dengan maghrib (terbit)
H_ashar = self.get_h(self.R2D * math.atan(1 + math.tan(abs(lat - delta) * self.D2R)), delta, lat)
# 6. KIBLAT
latK, lonK = 21.4225, 39.8262
dL = (lon - lonK) * self.D2R
Q = math.atan2(math.sin(dL), math.cos(lat * self.D2R) * math.tan(latK * self.D2R) - math.sin(lat * self.D2R) * math.cos(dL)) * self.R2D
AzKiblat = self.rev(360 - Q)
distK = math.acos(math.sin(latK*self.D2R)*math.sin(lat*self.D2R) + math.cos(latK*self.D2R)*math.cos(lat*self.D2R)*math.cos(dL)) * 6371
# MAPPING SEMUA 50 PARAMETER (SESUAI JS)
display_data = [
("DATA WAKTU & KALENDER", "S"),
("Julian Day", f"{JD:.4f}"), ("Julian Century (T)", f"{T:.8f}"), ("GMST (Greenwich)", f"{gmst:.4f}°"),
("Equation of Time", f"{EoT:.2f} m"), ("Delta T (Est)", "69.0 s"), ("Tahun Astronomi", f"{Y}"),
("PARAMETER MATAHARI", "S"),
("Bujur Rata (L0)", f"{L0:.4f}°"), ("Anomali Rata (M)", f"{Msun:.4f}°"), ("Eksentrisitas (e)", f"{e:.7f}"),
("Persamaan Pusat (C)", f"{C:.4f}°"), ("Bujur Hakiki (Θ)", f"{sunTrueLong:.4f}°"), ("Obliquity (ε)", f"{eps0:.4f}°"),
("Deklinasi (δ)", f"{delta:.4f}°"), ("Right Ascension (α)", f"{RA:.4f} j"), ("Jarak Bumi-Sun", f"{R_sun:.6f} AU"),
("Semidiameter Sun", f"{SD_sun:.4f}°"), ("Nutasi Bujur", "Pending"), ("Paralaks Sun", "0.0024°"),
("PARAMETER BULAN", "S"),
("Bujur Rata Bulan", f"{Lp:.4f}°"), ("Elongasi (D)", f"{Dmoon:.4f}°"), ("Anomali Bulan (M')", f"{Mmoon:.4f}°"),
("Argumen Lintang (F)", f"{Fmoon:.4f}°"), ("Simpul Node (Ω)", f"{Node:.4f}°"), ("HP Bulan", f"{HP_moon:.4f}°"),
("Fase Bulan", "Coming Soon"), ("Bujur Ecl. Bulan", "Pending"), ("Lintang Ecl. Bulan", "Pending"),
("KOREKSI UFUK & LOKAL", "S"),
("Kerendahan Ufuk (Dip)", f"{Dip:.4f}°"), ("Refraksi Atmosfer", f"{Refr:.4f}°"), ("Tinggi Maghrib (h)", f"{h_maghrib:.4f}°"),
("Tinggi Subuh", "-20.0000°"), ("Tinggi Isya", "-18.0000°"), ("Sudut Syuruq", f"{h_maghrib:.4f}°"),
("WAKTU SALAT (ISTIWA)", "S"),
("Istiwa / Zuhur", self.format_jam(Transit)),
("Subuh (-20°)", self.format_jam(Transit - H_subuh if H_subuh else 0)),
("Syuruq (Terbit)", self.format_jam(Transit - H_syuruq if H_syuruq else 0)),
("Ashar (Syafi'i)", self.format_jam(Transit + H_ashar if H_ashar else 0)),
("Maghrib", self.format_jam(Transit + H_maghrib if H_maghrib else 0)),
("Isya (-18°)", self.format_jam(Transit + H_isya if H_isya else 0)),
("ARAH KIBLAT", "S"),
("Azimut Kiblat", f"{AzKiblat:.2f}° U-T"), ("Jarak ke Ka'bah", f"{distK:.2f} km"),
("Lintang Ka'bah", "21.4225° N"), ("Bujur Ka'bah", "39.8262° E"), ("Sudut Q", f"{Q:.4f}°")
]
# Render Cards (4 Kolom agar muat banyak)
row, col = 0, 0
for item in display_data:
if item[1] == "S":
row += 1
section = ctk.CTkLabel(self.output_container, text=item[0], font=("Segoe UI", 14, "bold"),
fg_color="#e8f0fe", text_color="#1967d2", corner_radius=5, height=35)
section.grid(row=row, column=0, columnspan=4, sticky="ew", pady=(20, 10), padx=10)
row += 1
col = 0
else:
card = ctk.CTkFrame(self.output_container, fg_color="white", border_width=1, border_color="#e0e0e0")
card.grid(row=row, column=col, padx=8, pady=5, sticky="nsew")
ctk.CTkLabel(card, text=item[0].upper(), font=("Segoe UI", 10, "bold"), text_color="#1a73e8").pack(pady=(8,0), padx=10)
ctk.CTkLabel(card, text=item[1], font=("Courier New", 14, "bold"), text_color="#333").pack(pady=(2,8), padx=10)
col += 1
if col > 3: # 4 Kolom
col = 0
row += 1
except Exception as e:
from tkinter import messagebox
messagebox.showerror("Error", f"Terjadi kesalahan input: {str(e)}")
if __name__ == "__main__":
app = FalakCalculator()
app.mainloop()