Proyek Akhir Tkinter: Membangun Kalkulator Geometri
Setelah mempelajari berbagai konsep Tkinter secara terpisah—mulai dari widget dasar, layout manager, penanganan event, hingga dialog—kini saatnya kita menyatukan semua kepingan puzzle tersebut. Dalam artikel ini, kita akan membangun satu proyek nyata dari awal hingga akhir: sebuah Kalkulator Geometri.
Aplikasi ini akan menerapkan hampir semua yang telah kita pelajari dan disusun menggunakan struktur Pemrograman Berorientasi Objek (OOP) yang rapi dan profesional. Tujuannya adalah untuk menunjukkan bagaimana komponen-komponen individual saling bekerja sama untuk menciptakan sebuah aplikasi yang utuh dan fungsional.
Perencanaan Aplikasi 🏗️
Aplikasi kita akan memiliki fitur-fitur berikut:
- Sebuah jendela utama yang terstruktur dalam sebuah Class.
- Pilihan bangun datar menggunakan grup Radiobutton yang terlihat jelas.
- Sebuah judul dinamis yang berubah untuk menunjukkan bentuk apa yang sedang aktif.
- Tata letak dua kolom: area kontrol di kiri (pilihan, input, hasil) dan area visualisasi di kanan (canvas).
- Sebuah Canvas untuk menampilkan visualisasi sederhana dari bangun datar yang dipilih.
- Tombol "Hitung" untuk memicu kalkulasi (Event Handling).
- Ukuran font dan jarak yang nyaman untuk dibaca.
- Kotak pesan Messagebox untuk menampilkan error jika input tidak valid.
Kode Program Lengkap 📐
Berikut adalah kode lengkap untuk aplikasi Kalkulator Geometri kita, yang menerapkan semua fitur yang telah direncanakan.
import tkinter as tk
from tkinter import messagebox
import math
class AplikasiGeometri(tk.Tk):
def __init__(self):
super().__init__()
self.title("Kalkulator Geometri")
# Definisikan font
self.font_reguler = ("Arial", 12)
self.font_judul = ("Arial", 14, "bold")
self.font_hasil = ("Arial", 14, "bold")
self.frame_aktif = None
# --- Buat Frame Utama ---
frame_kiri = tk.Frame(self, padx=10, pady=10)
frame_kiri.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
frame_kanan = tk.Frame(self, padx=10, pady=10)
frame_kanan.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True)
# --- Isi Frame Kanan (Canvas) ---
tk.Label(frame_kanan, text="Visualisasi", font=self.font_judul).pack(pady=5)
self.canvas = tk.Canvas(frame_kanan, width=250, height=250, bg="white", relief="sunken", bd=2)
self.canvas.pack(pady=10)
# --- Isi Frame Kiri (Kontrol) ---
# 1. Pilihan Bentuk (Radiobutton)
frame_pilihan = tk.Frame(frame_kiri)
frame_pilihan.pack(pady=10, anchor="w")
tk.Label(frame_pilihan, text="Pilih Bentuk:", font=self.font_judul).pack(anchor="w")
self.pilihan_bentuk = tk.StringVar(value="Lingkaran")
opsi = ["Lingkaran", "Persegi", "Persegi Panjang"]
for pilihan in opsi:
rb = tk.Radiobutton(frame_pilihan, text=pilihan, variable=self.pilihan_bentuk, value=pilihan, command=self.ganti_frame, font=self.font_reguler)
rb.pack(anchor="w")
# 2. Judul Dinamis dan Kontainer Input
self.label_judul_input = tk.Label(frame_kiri, font=self.font_judul)
self.label_judul_input.pack(pady=(20, 5))
self.container_input = tk.Frame(frame_kiri)
self.container_input.pack(pady=10)
# 3. Area Hasil
frame_hasil = tk.Frame(frame_kiri)
frame_hasil.pack(pady=20)
self.label_luas = tk.Label(frame_hasil, text="Luas: -", font=self.font_hasil)
self.label_luas.pack()
self.label_keliling = tk.Label(frame_hasil, text="Keliling: -", font=self.font_hasil)
self.label_keliling.pack()
# Setup frame-frame input
self.buat_frame_lingkaran()
self.buat_frame_persegi()
self.buat_frame_persegipanjang()
self.ganti_frame() # Tampilkan frame default saat start
def ganti_frame(self):
pilihan = self.pilihan_bentuk.get()
if pilihan == "Lingkaran":
self.tampilkan_frame(self.frame_lingkaran, "Input untuk Lingkaran")
elif pilihan == "Persegi":
self.tampilkan_frame(self.frame_persegi, "Input untuk Persegi")
elif pilihan == "Persegi Panjang":
self.tampilkan_frame(self.frame_persegipanjang, "Input untuk Persegi Panjang")
def tampilkan_frame(self, frame_target, judul):
if self.frame_aktif:
self.frame_aktif.pack_forget()
self.frame_aktif = frame_target
self.frame_aktif.pack(in_=self.container_input)
self.label_judul_input.config(text=judul)
self.canvas.delete("all")
self.label_luas.config(text="Luas: -")
self.label_keliling.config(text="Keliling: -")
# --- Definisi Frame dan Logika untuk setiap bangun... ---
def buat_frame_lingkaran(self):
self.frame_lingkaran = tk.Frame(self.container_input)
tk.Label(self.frame_lingkaran, text="Jari-jari:", font=self.font_reguler).grid(row=0, column=0, padx=5, pady=5)
self.entry_jarijari = tk.Entry(self.frame_lingkaran, font=self.font_reguler)
self.entry_jarijari.grid(row=0, column=1, padx=5, pady=5)
tk.Button(self.frame_lingkaran, text="Hitung", font=self.font_reguler, command=self.hitung_lingkaran).grid(row=1, columnspan=2, pady=10)
def hitung_lingkaran(self):
try:
r = float(self.entry_jarijari.get())
luas = math.pi * r**2
keliling = 2 * math.pi * r
self.label_luas.config(text=f"Luas: {luas:.2f}")
self.label_keliling.config(text=f"Keliling: {keliling:.2f}")
self.canvas.delete("all")
self.canvas.create_oval(55, 55, 195, 195, fill="lightblue", outline="blue")
except ValueError:
messagebox.showerror("Error Input", "Masukkan bilangan yang valid untuk jari-jari!")
def buat_frame_persegi(self):
self.frame_persegi = tk.Frame(self.container_input)
tk.Label(self.frame_persegi, text="Sisi:", font=self.font_reguler).grid(row=0, column=0, padx=5, pady=5)
self.entry_sisi_persegi = tk.Entry(self.frame_persegi, font=self.font_reguler)
self.entry_sisi_persegi.grid(row=0, column=1, padx=5, pady=5)
tk.Button(self.frame_persegi, text="Hitung", font=self.font_reguler, command=self.hitung_persegi).grid(row=1, columnspan=2, pady=10)
def hitung_persegi(self):
try:
s = float(self.entry_sisi_persegi.get())
luas = s * s
keliling = 4 * s
self.label_luas.config(text=f"Luas: {luas:.2f}")
self.label_keliling.config(text=f"Keliling: {keliling:.2f}")
self.canvas.delete("all")
self.canvas.create_rectangle(50, 50, 200, 200, fill="lightgreen", outline="green")
except ValueError:
messagebox.showerror("Error Input", "Masukkan bilangan yang valid untuk sisi!")
def buat_frame_persegipanjang(self):
self.frame_persegipanjang = tk.Frame(self.container_input)
tk.Label(self.frame_persegipanjang, text="Panjang:", font=self.font_reguler).grid(row=0, column=0, padx=5, pady=5)
self.entry_panjang = tk.Entry(self.frame_persegipanjang, font=self.font_reguler)
self.entry_panjang.grid(row=0, column=1, padx=5, pady=5)
tk.Label(self.frame_persegipanjang, text="Lebar:", font=self.font_reguler).grid(row=1, column=0, padx=5, pady=5)
self.entry_lebar = tk.Entry(self.frame_persegipanjang, font=self.font_reguler)
self.entry_lebar.grid(row=1, column=1, padx=5, pady=5)
tk.Button(self.frame_persegipanjang, text="Hitung", font=self.font_reguler, command=self.hitung_persegipanjang).grid(row=2, columnspan=2, pady=10)
def hitung_persegipanjang(self):
try:
p = float(self.entry_panjang.get())
l = float(self.entry_lebar.get())
luas = p * l
keliling = 2 * (p + l)
self.label_luas.config(text=f"Luas: {luas:.2f}")
self.label_keliling.config(text=f"Keliling: {keliling:.2f}")
self.canvas.delete("all")
self.canvas.create_rectangle(50, 75, 200, 175, fill="lightyellow", outline="orange")
except ValueError:
messagebox.showerror("Error Input", "Masukkan bilangan yang valid untuk panjang dan lebar!")
if __name__ == "__main__":
app = AplikasiGeometri()
app.mainloop()
Kesimpulan
Penggunaan grup Radiobutton untuk navigasi dan adanya judul dinamis membuat aplikasi ini intuitif dan nyaman digunakan. Proyek ini menunjukkan betapa pentingnya memikirkan pengalaman pengguna (UX) saat merancang antarmuka. Dengan kode yang terstruktur dalam Class (OOP), kita dapat membangun aplikasi yang kompleks namun tetap rapi dan mudah dikelola.

