import customtkinter as ctk
import threading
import tkinter.messagebox as messagebox
from tkinter import filedialog
import platform
import webbrowser # Ditambahkan untuk membuka browser/aplikasi
import urllib.parse # Ditambahkan untuk format teks ke URL
# Perbaikan DPI agar font tajam di Windows
if platform.system() == "Windows":
try:
import ctypes
ctypes.windll.shcore.SetProcessDpiAwareness(1)
except Exception:
pass
# IMPORT VERSI TERBARU SDK GEMINI
from google import genai
# Konfigurasi Tema Aplikasi
ctk.set_appearance_mode("Dark")
ctk.set_default_color_theme("blue")
# KONFIGURASI API KEY GEMINI
API_KEY = "AIzaSyAmPU9OdJxhe65Fl9ZCO3CvgbQOcgpgp6M"
client = genai.Client(api_key=API_KEY)
class AIFikihApp(ctk.CTk):
def __init__(self):
super().__init__()
self.title("PyAI Gemini - Pro Version")
# Membuat layar selebar layar
try:
self.after(0, lambda: self.state('zoomed'))
except Exception:
w, h = self.winfo_screenwidth(), self.winfo_screenheight()
self.geometry(f"{w}x{h}")
self.minsize(600, 500)
self.grid_columnconfigure(0, weight=1)
self.grid_rowconfigure(1, weight=1)
# HEADER
self.header_frame = ctk.CTkFrame(self, fg_color="transparent")
self.header_frame.grid(row=0, column=0, padx=20, pady=(20, 10), sticky="ew")
self.title_label = ctk.CTkLabel(self.header_frame, text="🤖 PYTHON AI", font=("Segoe UI", 24, "bold"), text_color="#FFFFFF")
self.title_label.pack(side="left")
# RUANG OUTPUT
self.output_box = ctk.CTkTextbox(
self,
wrap="word",
fg_color="#121212",
scrollbar_button_color="#555555",
scrollbar_button_hover_color="#5DADE2"
)
self.output_box.grid(row=1, column=0, padx=20, pady=10, sticky="nsew")
self.setup_text_tags()
self.tampilkan_teks_awal()
# RUANG INPUT & TOMBOL
self.input_frame = ctk.CTkFrame(self, fg_color="transparent")
self.input_frame.grid(row=2, column=0, padx=20, pady=(10, 20), sticky="ew")
self.input_frame.grid_columnconfigure(0, weight=1)
self.input_entry = ctk.CTkEntry(
self.input_frame,
placeholder_text="Ketik pertanyaan Anda di sini...",
font=("Segoe UI", 14),
height=45,
fg_color="#1E1E1E",
border_color="#333333",
text_color="#FFFFFF"
)
self.input_entry.grid(row=0, column=0, padx=(0, 10), sticky="ew")
self.input_entry.bind("<Return>", lambda event: self.proses_pertanyaan())
self.btn_kirim = ctk.CTkButton(self.input_frame, text="Kirim", font=("Segoe UI", 14, "bold"), height=45, width=110, command=self.proses_pertanyaan)
self.btn_kirim.grid(row=0, column=1)
def setup_text_tags(self):
tk_textbox = self.output_box._textbox
tk_textbox.tag_config("anda", font=("Segoe UI", 15, "bold"), foreground="#F5B041", spacing1=10)
tk_textbox.tag_config("bot_header", font=("Segoe UI", 15, "bold"), foreground="#58D68D", spacing1=10)
# Teks normal tanpa indentasi (lmargin1 dihapus), namun spasi antar paragraf tetap dipertahankan
tk_textbox.tag_config("normal", font=("Segoe UI", 14), foreground="#EAEAEA", spacing1=8, spacing3=8)
tk_textbox.tag_config("italic", font=("Segoe UI", 13, "italic"), foreground="#A6ACAF", spacing1=5, spacing3=5)
# Tag khusus untuk memformat Judul, Sub-judul, dan tulisan tebal (Bold) dari AI
tk_textbox.tag_config("h1", font=("Segoe UI", 18, "bold"), foreground="#FFFFFF", spacing1=12, spacing3=8)
tk_textbox.tag_config("h2", font=("Segoe UI", 16, "bold"), foreground="#FFFFFF", spacing1=10, spacing3=5)
tk_textbox.tag_config("h3", font=("Segoe UI", 15, "bold"), foreground="#FFFFFF", spacing1=10, spacing3=5)
tk_textbox.tag_config("bold", font=("Segoe UI", 14, "bold"), foreground="#FFFFFF", spacing1=8, spacing3=8)
def tampilkan_teks_awal(self):
self.output_box.configure(state="normal")
self.output_box.insert("end", "🛜 ", "normal")
self.output_box.insert("end", "Pastikan koneksi internet aktif.\n\n", "italic")
self.output_box.configure(state="disabled")
def tampilkan_teks(self, teks, pengirim="AI"):
self.output_box.configure(state="normal")
if pengirim == "Anda":
self.output_box.insert("end", "🗣️ ANDA:\n", "anda")
self.output_box.insert("end", f"{teks}\n", "normal")
self.sisipkan_tombol_aksi(teks) # Masukkan tombol di bawah pertanyaan
elif pengirim == "Sistem":
self.output_box.insert("end", f"⏳ {teks}\n\n", "italic")
else:
self.output_box.insert("end", "🤖 AI MENJAWAB:\n", "bot_header")
# Membaca teks AI baris demi baris lalu menerapkan Font Bold pada Judul
for line in teks.split('\n'):
# Membersihkan simbol backtick jika ada
line = line.replace("`", "")
# Cek jika baris adalah Judul Utama (#)
if line.startswith("# "):
self.output_box.insert("end", line[2:] + "\n", "h1")
# Cek jika baris adalah Sub Judul (##)
elif line.startswith("## "):
self.output_box.insert("end", line[3:] + "\n", "h2")
# Cek jika baris adalah Sub-sub Judul (###)
elif line.startswith("### "):
self.output_box.insert("end", line[4:] + "\n", "h3")
else:
# Deteksi teks tebal (**) di tengah kalimat
parts = line.split("**")
for i, part in enumerate(parts):
# Indeks ganjil berarti teks berada di dalam **...** (teks tebal)
tag = "bold" if i % 2 != 0 else "normal"
if part:
self.output_box.insert("end", part, tag)
self.output_box.insert("end", "\n", "normal")
self.sisipkan_tombol_aksi(teks) # Masukkan tombol di bawah jawaban AI
self.output_box.insert("end", "-"*60 + "\n\n", "italic")
self.output_box.see("end")
self.output_box.configure(state="disabled")
def sisipkan_tombol_aksi(self, teks_target):
""" Membuat frame kecil berisi tombol Salin, Simpan, Berbagi di dalam Textbox """
# Frame transparan sebagai wadah tombol
frame_aksi = ctk.CTkFrame(self.output_box, fg_color="transparent")
# Tombol Salin
btn_salin = ctk.CTkButton(
frame_aksi, text="📋 Salin", width=60, height=25, font=("Segoe UI", 12, "bold"),
fg_color="#28a745", hover_color="#218838",
command=lambda t=teks_target: self.aksi_salin(t)
)
btn_salin.pack(side="left", padx=(0, 5))
# Tombol Simpan
btn_simpan = ctk.CTkButton(
frame_aksi, text="💾 Simpan", width=60, height=25, font=("Segoe UI", 12, "bold"),
fg_color="#17a2b8", hover_color="#138496",
command=lambda t=teks_target: self.aksi_simpan(t)
)
btn_simpan.pack(side="left", padx=5)
# Tombol Berbagi
btn_berbagi = ctk.CTkButton(
frame_aksi, text="🔗 Berbagi (WA)", width=60, height=25, font=("Segoe UI", 12, "bold"),
fg_color="#ffc107", hover_color="#e0a800", text_color="#000000",
command=lambda t=teks_target: self.aksi_berbagi(t)
)
btn_berbagi.pack(side="left", padx=5)
# Menanamkan frame tombol ke dalam Textbox Tkinter
self.output_box._textbox.window_create("end", window=frame_aksi)
self.output_box.insert("end", "\n\n") # Jarak setelah tombol
# --- FUNGSI-FUNGSI AKSI ---
def aksi_salin(self, teks):
self.clipboard_clear()
self.clipboard_append(teks)
messagebox.showinfo("Berhasil", "Teks telah disalin ke clipboard!")
def aksi_simpan(self, teks):
# Membuka dialog penyimpanan file bawaan OS
file_path = filedialog.asksaveasfilename(
defaultextension=".txt",
filetypes=[("Text Document", "*.txt"), ("All Files", "*.*")],
title="Simpan Percakapan"
)
if file_path: # Jika user tidak menekan 'Cancel'
try:
with open(file_path, 'w', encoding='utf-8') as file:
file.write(teks)
messagebox.showinfo("Berhasil", f"Teks berhasil disimpan di:\n{file_path}")
except Exception as e:
messagebox.showerror("Error", f"Gagal menyimpan file:\n{str(e)}")
def aksi_berbagi(self, teks):
# Menyalin teks ke clipboard sebagai cadangan
self.clipboard_clear()
self.clipboard_append(teks)
# PERBAIKAN: Mengarahkan langsung ke WhatsApp
try:
# Mengubah teks menjadi format URL yang aman (URL Encoding)
teks_encoded = urllib.parse.quote(teks)
# URL API WhatsApp
url_wa = f"https://wa.me/?text={teks_encoded}"
# Membuka URL di browser default (akan mengarahkan ke WA Web / Aplikasi WA Desktop)
webbrowser.open(url_wa)
except Exception as e:
messagebox.showerror("Error", f"Gagal membuka WhatsApp:\n{str(e)}")
# --- PROSES AI ---
def proses_pertanyaan(self):
pertanyaan = self.input_entry.get().strip()
if not pertanyaan:
return
self.input_entry.delete(0, "end")
self.tampilkan_teks(pertanyaan, pengirim="Anda")
self.tampilkan_teks("Sedang memproses... (Menghubungi AI)", pengirim="Sistem")
self.btn_kirim.configure(state="disabled")
threading.Thread(target=self.tanya_ai_thread, args=(pertanyaan,), daemon=True).start()
def tanya_ai_thread(self, teks_pertanyaan):
try:
prompt_khusus = f"Anda adalah pakar Fikih Islam. Jawablah pertanyaan berikut dengan bahasa yang mudah dipahami:\n\n{teks_pertanyaan}"
# FORMAT API BARU
response = client.models.generate_content(
model='gemini-2.5-flash',
contents=prompt_khusus
)
jawaban = response.text
except Exception as e:
jawaban = f"[ERROR] Gagal Terhubung ke AI API.\nPastikan API Key valid.\nDetail: {str(e)}"
self.after(0, self.selesai_memproses, jawaban)
def selesai_memproses(self, jawaban):
self.output_box.configure(state="normal")
pos_start = self.output_box.search("⏳ Sedang memproses... (Menghubungi AI)", "1.0", stopindex="end")
if pos_start:
self.output_box.delete(pos_start, f"{pos_start} + 2 lines")
self.output_box.configure(state="disabled")
self.tampilkan_teks(jawaban, pengirim="AI")
self.btn_kirim.configure(state="normal")
if __name__ == "__main__":
app = AIFikihApp()
app.mainloop()