Modul Praktikum

Modul Praktikum

 

 

 

KURIKULUM OBE • KOMP1106 - 07

MODUL PRAKTIKUM PEMROGRAMAN DASAR DAN PRAKTIKUM

10 Pertemuan Lengkap • Mendukung Praktikum GUI Laptop/PC (Tkinter) & Smartphone/Google Colab (ipywidgets)

PERTEMUAN 1: Pengenalan Pemrograman, Algoritma, dan Python

Sub-CPMK 1
🎯 Fokus Praktikum: Algoritma sederhana (pseudocode/flowchart) dan program print()
📌 Capaian Sub-CPMK 1: Mampu menjelaskan konsep dasar algoritma, pemrograman, serta menjalankan program Python sederhana (print()).
📖 Penjelasan Konsep & Cara Kerja Program (Klik untuk Buka/Tutup)

Pada praktikum pertama ini, mahasiswa mempelajari bagaimana komputer mengeksekusi instruksi secara berurutan dari atas ke bawah (Algoritma Sekuensial):

  • Fungsi print(): Digunakan untuk mengirimkan teks keluaran ke konsol/terminal sekaligus ditampilkan ke komponen Label pada jendela GUI.
  • Simulasi Urutan Algoritma: Saat tombol ditekan, fungsi memanggil langkah-langkah pembuatan program Python secara berurutan dari MULAI (Start) hingga SELESAI (End) ke dalam area teks.
💻 A. Source Code GUI Desktop / Laptop (Tkinter) — pertemuan_1_algoritma_print.py
import tkinter as tk

def jalankan_print():
    pesan = "Halo Mahasiswa! Selamat datang di Pemrograman Dasar Python."
    print(pesan)
    lbl_output_print.config(text=f"Output print():\n{pesan}", fg="#1B365D")

def tampilkan_algoritma():
    algoritma = (
        "ALGORITMA MEMBUAT PROGRAM PYTHON:\n"
        "1. MULAI (Start)\n"
        "2. Buka teks editor / IDE Python\n"
        "3. Ketik kode instruksi print('Halo Dunia')\n"
        "4. Simpan file dengan ekstensi .py\n"
        "5. Jalankan (Run) program dan lihat hasilnya\n"
        "6. SELESAI (End)"
    )
    print("\n--- Algoritma Dieksekusi ---")
    print(algoritma)
    txt_algoritma.delete("1.0", tk.END)
    txt_algoritma.insert(tk.END, algoritma)

root = tk.Tk()
root.title("Pertemuan 1 - Algoritma & Program print()")
root.geometry("480x400")
root.config(padx=20, pady=20)

tk.Label(root, text="Praktikum 1: Algoritma & Fungsi print()", font=("Arial", 13, "bold")).pack(pady=(0, 15))
tk.Button(root, text="1. Jalankan Fungsi print()", command=jalankan_print, bg="#1B365D", fg="white", width=30).pack(pady=5)
lbl_output_print = tk.Label(root, text="Output akan tampil di sini dan di Terminal.", font=("Arial", 10, "italic"))
lbl_output_print.pack(pady=10)
tk.Button(root, text="2. Tampilkan Urutan Algoritma", command=tampilkan_algoritma, bg="#4682B4", fg="white", width=30).pack(pady=5)
txt_algoritma = tk.Text(root, height=8, width=50, font=("Consolas", 10))
txt_algoritma.pack(pady=10)

root.mainloop()
📱 B. Source Code GUI Google Colab / Smartphone (ipywidgets)
import ipywidgets as widgets
from IPython.display import display, clear_output

judul = widgets.HTML("<h3>Praktikum 1: Algoritma & Fungsi print()</h3>")
in_pesan = widgets.Text(value="Halo Dunia! Saya belajar algoritma Python.", description="Pesan:")
btn_print = widgets.Button(description="1. Jalankan print()", button_style="primary")
btn_algo = widgets.Button(description="2. Tampilkan Algoritma", button_style="info")
out = widgets.Output()

def aksi_print(b):
    with out:
        clear_output()
        print("=== OUTPUT FUNGSI print() ===")
        print(in_pesan.value)

def aksi_algo(b):
    with out:
        clear_output()
        print("ALGORITMA MEMBUAT PROGRAM PYTHON:")
        langkah = [
            "1. MULAI (Start)",
            "2. Buka Google Colab di Browser HP/Laptop",
            f"3. Ketik instruksi print('{in_pesan.value}')",
            "4. Tekan tombol Run / Play",
            "5. SELESAI (End)"
        ]
        for l in langkah:
            print(l)

btn_print.on_click(aksi_print)
btn_algo.on_click(aksi_algo)
display(judul, in_pesan, widgets.HBox([btn_print, btn_algo]), out)

PERTEMUAN 2: Variabel, Tipe Data, Input/Output, dan Operator

Sub-CPMK 2
🎯 Fokus Praktikum: Program biodata dan kalkulator
📌 Capaian Sub-CPMK 2: Mampu menerapkan variabel, tipe data, operator, serta input/output dalam membuat program kalkulator dan biodata.
📖 Penjelasan Konsep & Cara Kerja Program
  • Konversi Tipe Data (Casting): Input dari kotak teks GUI awalnya bertipe str (teks). Untuk menghitung tahun lahir pada biodata, input umur dikonversi menjadi bilangan bulat dengan int(umur).
  • Kalkulator Operator Aritmatika: Menggunakan tipe data float() agar mendukung angka desimal pada operasi penjumlahan (+), pengurangan (-), perkalian (*), dan pembagian (/) disertai pencegahan pembagian dengan nol.
💻 A. Source Code GUI Desktop / Laptop (Tkinter) — pertemuan_2_biodata_kalkulator.py
import tkinter as tk
from tkinter import messagebox

def proses_biodata():
    nama = str(ent_nama.get())
    nim = str(ent_nim.get())
    umur = ent_umur.get()
    if not nama or not nim or not umur.isdigit():
        messagebox.showwarning("Peringatan", "Pastikan semua kolom terisi dan Umur berupa angka!")
        return
    umur_int = int(umur)
    tahun_lahir = 2026 - umur_int
    hasil_bio = f"Nama: {nama} (str)\nNIM: {nim} (str)\nUmur: {umur_int} Tahun (int)\nEstimasi Tahun Lahir: {tahun_lahir}"
    lbl_hasil_bio.config(text=hasil_bio)

def hitung(operator):
    try:
        angka1 = float(ent_angka1.get())
        angka2 = float(ent_angka2.get())
        if operator == "+": hasil = angka1 + angka2
        elif operator == "-": hasil = angka1 - angka2
        elif operator == "*": hasil = angka1 * angka2
        elif operator == "/": hasil = angka1 / angka2 if angka2 != 0 else "Error (Bagi 0)"
        lbl_hasil_kalkulator.config(text=f"Hasil: {hasil}")
    except ValueError:
        messagebox.showerror("Error", "Masukkan angka yang valid pada kalkulator!")

root = tk.Tk()
root.title("Pertemuan 2 - Biodata & Kalkulator")
root.geometry("460x480")
root.config(padx=20, pady=15)

frame_bio = tk.LabelFrame(root, text="1. Form Biodata Mahasiswa", padx=10, pady=10, font=("Arial", 10, "bold"))
frame_bio.pack(fill="x", pady=5)
tk.Label(frame_bio, text="Nama Lengkap:").grid(row=0, column=0, sticky="w", pady=2)
ent_nama = tk.Entry(frame_bio, width=30)
ent_nama.grid(row=0, column=1, pady=2)
tk.Label(frame_bio, text="NIM:").grid(row=1, column=0, sticky="w", pady=2)
ent_nim = tk.Entry(frame_bio, width=30)
ent_nim.grid(row=1, column=1, pady=2)
tk.Label(frame_bio, text="Umur (Tahun):").grid(row=2, column=0, sticky="w", pady=2)
ent_umur = tk.Entry(frame_bio, width=30)
ent_umur.grid(row=2, column=1, pady=2)
tk.Button(frame_bio, text="Tampilkan Biodata", command=proses_biodata, bg="#1B365D", fg="white").grid(row=3, column=0, columnspan=2, pady=8)
lbl_hasil_bio = tk.Label(frame_bio, text="Hasil biodata akan tampil di sini.", justify="left")
lbl_hasil_bio.grid(row=4, column=0, columnspan=2, sticky="w")

frame_calc = tk.LabelFrame(root, text="2. Kalkulator Operator Aritmatika", padx=10, pady=10, font=("Arial", 10, "bold"))
frame_calc.pack(fill="x", pady=10)
tk.Label(frame_calc, text="Angka Pertama:").grid(row=0, column=0, sticky="w", pady=2)
ent_angka1 = tk.Entry(frame_calc, width=20)
ent_angka1.grid(row=0, column=1, columnspan=3, sticky="w", pady=2)
tk.Label(frame_calc, text="Angka Kedua:").grid(row=1, column=0, sticky="w", pady=2)
ent_angka2 = tk.Entry(frame_calc, width=20)
ent_angka2.grid(row=1, column=1, columnspan=3, sticky="w", pady=2)

for i, op in enumerate(["+", "-", "*", "/"]):
    tk.Button(frame_calc, text=op, width=5, command=lambda o=op: hitung(o), bg="#4682B4", fg="white").grid(row=2, column=i, pady=8, padx=3)

lbl_hasil_kalkulator = tk.Label(frame_calc, text="Hasil: 0", font=("Arial", 11, "bold"), fg="#1B365D")
lbl_hasil_kalkulator.grid(row=3, column=0, columnspan=4, pady=5)

root.mainloop()
📱 B. Source Code GUI Google Colab / Smartphone (ipywidgets)
import ipywidgets as widgets
from IPython.display import display, clear_output

judul_bio = widgets.HTML("<h3>1. Form Biodata Mahasiswa</h3>")
in_nama = widgets.Text(description="Nama:", placeholder="Nama lengkap")
in_nim = widgets.Text(description="NIM:", placeholder="Nomor Induk")
in_umur = widgets.IntText(description="Umur:", value=19)
btn_bio = widgets.Button(description="Tampilkan Biodata", button_style="primary")
out_bio = widgets.Output()

def proses_biodata(b):
    with out_bio:
        clear_output()
        tahun_lahir = 2026 - in_umur.value
        print(f"Nama Mahasiswa : {in_nama.value} (str)")
        print(f"NIM            : {in_nim.value} (str)")
        print(f"Umur           : {in_umur.value} Tahun (int) -> Lahir ~{tahun_lahir}")

btn_bio.on_click(proses_biodata)

judul_calc = widgets.HTML("<h3>2. Kalkulator Aritmatika</h3>")
in_a = widgets.FloatText(description="Angka 1:", value=10.0)
in_b = widgets.FloatText(description="Angka 2:", value=5.0)
in_op = widgets.Dropdown(options=["+", "-", "*", "/"], description="Operator:")
btn_calc = widgets.Button(description="Hitung", button_style="info")
out_calc = widgets.Output()

def proses_hitung(b):
    with out_calc:
        clear_output()
        x, y, op = in_a.value, in_b.value, in_op.value
        if op == "+": res = x + y
        elif op == "-": res = x - y
        elif op == "*": res = x * y
        elif op == "/": res = x / y if y != 0 else "Error (Bagi 0)"
        print(f"Hasil: {x} {op} {y} = {res}")

btn_calc.on_click(proses_hitung)
display(judul_bio, in_nama, in_nim, in_umur, btn_bio, out_bio, judul_calc, in_a, in_b, in_op, btn_calc, out_calc)

PERTEMUAN 3: Percabangan (if, elif, else)

Sub-CPMK 3
🎯 Fokus Praktikum: Program penentuan nilai akademis dan kelulusan
📌 Capaian Sub-CPMK 3: Mampu menggunakan struktur logika percabangan (if, elif, else) untuk menentukan nilai dan kelulusan.
📖 Penjelasan Konsep & Cara Kerja Program
  • Perhitungan Nilai Akhir Berbobot: Menghitung skor gabungan dengan rumus (0.3 * tugas) + (0.3 * uts) + (0.4 * uas).
  • Seleksi Kondisi Bertingkat (if-elif-else): Mengevaluasi nilai akhir dari syarat tertinggi (>= 81 mendapat Grade A, >= 71 mendapat Grade B, >= 60 mendapat Grade C, >= 50 mendapat Grade D, dan di bawah itu masuk ke blok else yaitu Grade E).
💻 A. Source Code GUI Desktop / Laptop (Tkinter) — pertemuan_3_percabangan_nilai.py
import tkinter as tk
from tkinter import messagebox

def cek_kelulusan():
    try:
        nama = ent_nama.get()
        tugas = float(ent_tugas.get())
        uts = float(ent_uts.get())
        uas = float(ent_uas.get())
        if not (0 <= tugas <= 100 and 0 <= uts <= 100 and 0 <= uas <= 100):
            messagebox.showwarning("Validasi", "Nilai harus berada di rentang 0 - 100!")
            return
        nilai_akhir = (0.3 * tugas) + (0.3 * uts) + (0.4 * uas)
        if nilai_akhir >= 81:
            grade, status, warna = "A", "LULUS (Sangat Memuaskan)", "green"
        elif nilai_akhir >= 71:
            grade, status, warna = "B", "LULUS (Baik)", "green"
        elif nilai_akhir >= 60:
            grade, status, warna = "C", "LULUS (Cukup)", "#D97706"
        elif nilai_akhir >= 50:
            grade, status, warna = "D", "TIDAK LULUS (Perbaikan)", "red"
        else:
            grade, status, warna = "E", "TIDAK LULUS (Mengulang)", "red"
        lbl_hasil.config(text=f"Mahasiswa: {nama}\nNilai Akhir: {nilai_akhir:.2f}\nGrade: {grade}\nStatus: {status}", fg=warna)
    except ValueError:
        messagebox.showerror("Error", "Pastikan nilai Tugas, UTS, dan UAS diisi angka!")

root = tk.Tk()
root.title("Pertemuan 3 - Penentuan Nilai & Kelulusan")
root.geometry("420x380")
root.config(padx=20, pady=20)
tk.Label(root, text="Program Penentuan Nilai & Kelulusan", font=("Arial", 12, "bold")).pack(pady=(0, 15))
frame_input = tk.Frame(root)
frame_input.pack()

entries = []
for i, text in enumerate(["Nama Mahasiswa:", "Nilai Tugas (30%):", "Nilai UTS (30%):", "Nilai UAS (40%):"]):
    tk.Label(frame_input, text=text).grid(row=i, column=0, sticky="w", pady=4)
    ent = tk.Entry(frame_input, width=25)
    ent.grid(row=i, column=1, pady=4, padx=5)
    entries.append(ent)
ent_nama, ent_tugas, ent_uts, ent_uas = entries

tk.Button(root, text="Proses Nilai & Kelulusan", command=cek_kelulusan, bg="#1B365D", fg="white", width=25).pack(pady=15)
lbl_hasil = tk.Label(root, text="Silakan masukkan nilai di atas.", font=("Arial", 10, "bold"), justify="left")
lbl_hasil.pack(pady=5)
root.mainloop()
📱 B. Source Code GUI Google Colab / Smartphone (ipywidgets)
import ipywidgets as widgets
from IPython.display import display, clear_output

judul = widgets.HTML("<h3>Praktikum 3: Penentuan Nilai & Kelulusan (if-elif-else)</h3>")
in_nama = widgets.Text(description="Nama:", value="Mahasiswa A")
in_tugas = widgets.FloatSlider(description="Tugas (30%):", min=0, max=100, step=1, value=80)
in_uts = widgets.FloatSlider(description="UTS (30%):", min=0, max=100, step=1, value=75)
in_uas = widgets.FloatSlider(description="UAS (40%):", min=0, max=100, step=1, value=85)
btn_cek = widgets.Button(description="Evaluasi Kelulusan", button_style="success")
out = widgets.Output()

def evaluasi(b):
    with out:
        clear_output()
        akhir = (0.3 * in_tugas.value) + (0.3 * in_uts.value) + (0.4 * in_uas.value)
        if akhir >= 81: grade, status = "A", "LULUS (Sangat Memuaskan)"
        elif akhir >= 71: grade, status = "B", "LULUS (Baik)"
        elif akhir >= 60: grade, status = "C", "LULUS (Cukup)"
        elif akhir >= 50: grade, status = "D", "TIDAK LULUS (Wajib Perbaikan)"
        else: grade, status = "E", "TIDAK LULUS (Mengulang)"
        print(f"Nama Mahasiswa : {in_nama.value}")
        print(f"Nilai Akhir    : {akhir:.2f}")
        print(f"Grade Huruf    : {grade}")
        print(f"Status         : {status}")

btn_cek.on_click(evaluasi)
display(judul, in_nama, in_tugas, in_uts, in_uas, btn_cek, out)

PERTEMUAN 4: Perulangan (for, while)

Sub-CPMK 4
🎯 Fokus Praktikum: Program tabel perkalian dan perhitungan berulang
📌 Capaian Sub-CPMK 4: Mampu menerapkan struktur perulangan (for, while) pada perhitungan berulang dan tabel perkalian.
📖 Penjelasan Konsep & Cara Kerja Program
  • Perulangan for dengan range(1, batas + 1): Digunakan untuk membangkitkan deret tabel perkalian dengan jumlah iterasi yang sudah pasti.
  • Perulangan while: Mengeksekusi penjumlahan akumulasi berulang selama kondisi iterasi <= batas bernilai True, dilengkapi penambahan iterasi += 1 agar loop berhenti tepat waktu.
💻 A. Source Code GUI Desktop / Laptop (Tkinter) — pertemuan_4_perulangan.py
import tkinter as tk
from tkinter import messagebox

def buat_tabel_perkalian():
    try:
        angka = int(ent_angka.get())
        batas = int(ent_batas.get())
        txt_output.delete("1.0", tk.END)
        txt_output.insert(tk.END, f"=== TABEL PERKALIAN {angka} (FOR LOOP) ===\n")
        for i in range(1, batas + 1):
            txt_output.insert(tk.END, f"{angka} x {i} = {angka * i}\n")
    except ValueError:
        messagebox.showerror("Error", "Masukkan bilangan bulat!")

def hitung_berulang_while():
    try:
        angka_awal = int(ent_angka.get())
        batas = int(ent_batas.get())
        txt_output.delete("1.0", tk.END)
        txt_output.insert(tk.END, "=== PERHITUNGAN BERULANG (WHILE LOOP) ===\n")
        iterasi, total = 1, 0
        while iterasi <= batas:
            nilai_tambah = angka_awal * iterasi
            total += nilai_tambah
            txt_output.insert(tk.END, f"Langkah {iterasi}: +{nilai_tambah} -> Akumulasi = {total}\n")
            iterasi += 1
        txt_output.insert(tk.END, f"\nTotal Akhir: {total}")
    except ValueError:
        messagebox.showerror("Error", "Masukkan bilangan bulat!")

root = tk.Tk()
root.title("Pertemuan 4 - Perulangan (for & while)")
root.geometry("460x440")
root.config(padx=20, pady=20)
tk.Label(root, text="Praktikum Perulangan (for & while)", font=("Arial", 12, "bold")).pack(pady=(0, 10))
frame_in = tk.Frame(root)
frame_in.pack(pady=5)
tk.Label(frame_in, text="Angka Dasar:").grid(row=0, column=0, sticky="w", pady=3)
ent_angka = tk.Entry(frame_in, width=15)
ent_angka.grid(row=0, column=1, pady=3, padx=5)
tk.Label(frame_in, text="Jumlah Perulangan:").grid(row=1, column=0, sticky="w", pady=3)
ent_batas = tk.Entry(frame_in, width=15)
ent_batas.grid(row=1, column=1, pady=3, padx=5)

frame_btn = tk.Frame(root)
frame_btn.pack(pady=10)
tk.Button(frame_btn, text="Tabel Perkalian (for)", command=buat_tabel_perkalian, bg="#1B365D", fg="white", width=20).grid(row=0, column=0, padx=5)
tk.Button(frame_btn, text="Hitung Berulang (while)", command=hitung_berulang_while, bg="#4682B4", fg="white", width=20).grid(row=0, column=1, padx=5)
txt_output = tk.Text(root, height=13, width=50, font=("Consolas", 10))
txt_output.pack(pady=10)
root.mainloop()
📱 B. Source Code GUI Google Colab / Smartphone (ipywidgets)
import ipywidgets as widgets
from IPython.display import display, clear_output

judul = widgets.HTML("<h3>Praktikum 4: Perulangan (for & while)</h3>")
in_angka = widgets.IntText(description="Angka Dasar:", value=7)
in_batas = widgets.IntSlider(description="Batas Loop:", min=1, max=20, value=10)
btn_for = widgets.Button(description="Tabel Perkalian (for)", button_style="primary")
btn_while = widgets.Button(description="Akumulasi (while)", button_style="info")
out = widgets.Output()

def jalan_for(b):
    with out:
        clear_output()
        print(f"=== TABEL PERKALIAN {in_angka.value} (FOR LOOP) ===")
        for i in range(1, in_batas.value + 1):
            print(f"{in_angka.value} x {i} = {in_angka.value * i}")

def jalan_while(b):
    with out:
        clear_output()
        print("=== PERHITUNGAN BERULANG (WHILE LOOP) ===")
        i, total = 1, 0
        while i <= in_batas.value:
            tambah = in_angka.value * i
            total += tambah
            print(f"Iterasi {i}: +{tambah} -> Akumulasi = {total}")
            i += 1
        print(f"Total Akhir: {total}")

btn_for.on_click(jalan_for)
btn_while.on_click(jalan_while)
display(judul, in_angka, in_batas, widgets.HBox([btn_for, btn_while]), out)

PERTEMUAN 5: Ujian Tengah Semester (UTS — Integrasi Materi 1–4)

Sub-CPMK 5 • UTS
🎯 Fokus Praktikum: Ujian teori dan praktikum materi pertemuan 1–4
📌 Capaian Sub-CPMK 5: Mampu mengukur ketercapaian pemahaman teori dan keterampilan praktikum materi pertemuan 1–4 melalui UTS.
📖 Penjelasan Studi Kasus UTS (Kasir Toko Buku Akademik)

Proyek UTS ini menguji integrasi 4 pertemuan pertama dalam satu aplikasi Kasir Toko Buku:

  • Materi 1 & 2 (I/O & Operator): Menerima input nama pembeli, harga satuan, dan jumlah buku, lalu menghitung subtotal = harga_satuan * jumlah_beli serta mencetak log ke terminal dengan print().
  • Materi 3 (Percabangan Diskon): Menentukan diskon bertingkat menggunakan if-elif-else (Subtotal >= 500.000 diskon 15%, >= 250.000 diskon 10%, >= 100.000 diskon 5%).
  • Materi 4 (Perulangan Struk): Mencetak rincian setiap item buku (Item ke-1 s/d ke-N) secara otomatis pada struk menggunakan perulangan for.
💻 A. Source Code GUI Desktop / Laptop (Tkinter) — pertemuan_5_uts_integrasi_1_4.py
import tkinter as tk
from tkinter import messagebox

def proses_transaksi():
    try:
        pembeli = ent_nama.get()
        harga_satuan = float(ent_harga.get())
        jumlah_beli = int(ent_jumlah.get())
        if not pembeli or harga_satuan <= 0 or jumlah_beli <= 0:
            messagebox.showwarning("Peringatan", "Lengkapi data transaksi dengan benar!")
            return
        subtotal = harga_satuan * jumlah_beli
        if subtotal >= 500000: persen_diskon = 15
        elif subtotal >= 250000: persen_diskon = 10
        elif subtotal >= 100000: persen_diskon = 5
        else: persen_diskon = 0
        nominal_diskon = subtotal * (persen_diskon / 100)
        total_bayar = subtotal - nominal_diskon

        txt_struk.delete("1.0", tk.END)
        txt_struk.insert(tk.END, "=== STRUK PEMBELIAN BUKU (UTS) ===\n")
        txt_struk.insert(tk.END, f"Nama Pembeli : {pembeli}\n" + "-"*36 + "\n")
        for i in range(1, jumlah_beli + 1):
            txt_struk.insert(tk.END, f"Item ke-{i} : Rp {harga_satuan:,.0f}\n")
        txt_struk.insert(tk.END, "-"*36 + "\n")
        txt_struk.insert(tk.END, f"Subtotal     : Rp {subtotal:,.0f}\n")
        txt_struk.insert(tk.END, f"Diskon ({persen_diskon}%) : Rp {nominal_diskon:,.0f}\n")
        txt_struk.insert(tk.END, f"TOTAL BAYAR  : Rp {total_bayar:,.0f}\n")
        print(f"[LOG UTS] Transaksi {pembeli} berhasil. Total: Rp {total_bayar:,.0f}")
    except ValueError:
        messagebox.showerror("Error", "Harga dan Jumlah harus berupa angka!")

root = tk.Tk()
root.title("Pertemuan 5 - Proyek Praktikum UTS (Materi 1-4)")
root.geometry("460x470")
root.config(padx=20, pady=15)
tk.Label(root, text="Studi Kasus UTS: Kasir Toko Buku", font=("Arial", 12, "bold")).pack(pady=(0, 10))
frame_form = tk.Frame(root)
frame_form.pack()
tk.Label(frame_form, text="Nama Pembeli:").grid(row=0, column=0, sticky="w", pady=3)
ent_nama = tk.Entry(frame_form, width=25)
ent_nama.grid(row=0, column=1, pady=3)
tk.Label(frame_form, text="Harga Buku Satuan (Rp):").grid(row=1, column=0, sticky="w", pady=3)
ent_harga = tk.Entry(frame_form, width=25)
ent_harga.grid(row=1, column=1, pady=3)
tk.Label(frame_form, text="Jumlah Buku Dibeli:").grid(row=2, column=0, sticky="w", pady=3)
ent_jumlah = tk.Entry(frame_form, width=25)
ent_jumlah.grid(row=2, column=1, pady=3)
tk.Button(root, text="Hitung & Cetak Struk", command=proses_transaksi, bg="#1B365D", fg="white", width=25).pack(pady=12)
txt_struk = tk.Text(root, height=14, width=48, font=("Consolas", 10))
txt_struk.pack()
root.mainloop()
📱 B. Source Code GUI Google Colab / Smartphone (ipywidgets)
import ipywidgets as widgets
from IPython.display import display, clear_output

judul = widgets.HTML("<h3>Praktikum 5 (UTS): Sistem Kasir Toko Buku Akademik</h3>")
in_pembeli = widgets.Text(description="Pembeli:", value="Budi")
in_harga = widgets.FloatText(description="Harga (Rp):", value=75000)
in_jumlah = widgets.IntSlider(description="Jumlah:", min=1, max=15, value=4)
btn_proses = widgets.Button(description="Cetak Struk Transaksi", button_style="primary")
out = widgets.Output()

def cetak_struk(b):
    with out:
        clear_output()
        sub = in_harga.value * in_jumlah.value
        if sub >= 500000: disc = 15
        elif sub >= 250000: disc = 10
        elif sub >= 100000: disc = 5
        else: disc = 0
        potongan = sub * (disc / 100)
        total = sub - potongan
        print("=== STRUK PEMBELIAN BUKU (UTS) ===")
        print(f"Nama Pembeli : {in_pembeli.value}")
        print("-" * 35)
        for i in range(1, in_jumlah.value + 1):
            print(f"Buku ke-{i}    : Rp {in_harga.value:,.0f}")
        print("-" * 35)
        print(f"Subtotal     : Rp {sub:,.0f}")
        print(f"Diskon ({disc}%) : Rp {potongan:,.0f}")
        print(f"TOTAL BAYAR  : Rp {total:,.0f}")

btn_proses.on_click(cetak_struk)
display(judul, in_pembeli, in_harga, in_jumlah, btn_proses, out)

PERTEMUAN 6: Struktur Data Dasar (List dan Tuple)

Sub-CPMK 6
🎯 Fokus Praktikum: Pengolahan nilai mahasiswa (rata-rata, max/min)
📌 Capaian Sub-CPMK 6: Mampu mengorganisasikan data menggunakan struktur data dasar (List dan Tuple) pada pengolahan data nilai.
📖 Penjelasan Konsep & Cara Kerja Program
  • Tuple (KOMPONEN_TETAP): Menyimpan daftar nama komponen mata kuliah yang bersifat konstan/tidak berubah (immutable).
  • List (daftar_nilai): Menampung nilai mahasiswa secara dinamis (mutable) menggunakan metode .append() dan mengolah statistik kelas menggunakan fungsi bawaan sum(), len(), max(), dan min().
💻 A. Source Code GUI Desktop / Laptop (Tkinter) — pertemuan_6_list_tuple_nilai.py
import tkinter as tk
from tkinter import messagebox

KOMPONEN_TETAP = ("Praktikum Python", "Algoritma", "Struktur Data")
daftar_nilai = []

def tambah_nilai():
    try:
        nilai = float(ent_nilai.get())
        if not (0 <= nilai <= 100):
            messagebox.showwarning("Validasi", "Nilai harus antara 0 hingga 100!")
            return
        daftar_nilai.append(nilai)
        ent_nilai.delete(0, tk.END)
        perbarui_statistik()
    except ValueError:
        messagebox.showerror("Error", "Masukkan angka nilai yang valid!")

def hapus_semua():
    daftar_nilai.clear()
    perbarui_statistik()

def perbarui_statistik():
    listbox_nilai.delete(0, tk.END)
    for idx, n in enumerate(daftar_nilai, start=1):
        listbox_nilai.insert(tk.END, f"Mahasiswa ke-{idx}: {n}")
    if daftar_nilai:
        rata = sum(daftar_nilai) / len(daftar_nilai)
        info = f"Jumlah Data: {len(daftar_nilai)} | Rata-rata: {rata:.2f}\nTertinggi (Max): {max(daftar_nilai)} | Terendah (Min): {min(daftar_nilai)}"
    else:
        info = "Belum ada data nilai di dalam List."
    lbl_statistik.config(text=info)

root = tk.Tk()
root.title("Pertemuan 6 - Struktur Data (List & Tuple)")
root.geometry("450x470")
root.config(padx=20, pady=15)
tk.Label(root, text="Pengolahan Nilai Mahasiswa (List & Tuple)", font=("Arial", 12, "bold")).pack()
tk.Label(root, text=f"Komponen (Tuple): {', '.join(KOMPONEN_TETAP)}", font=("Arial", 9, "italic"), fg="#4682B4").pack(pady=(0, 10))
frame_input = tk.Frame(root)
frame_input.pack(pady=5)
tk.Label(frame_input, text="Input Nilai (0-100):").grid(row=0, column=0, padx=5)
ent_nilai = tk.Entry(frame_input, width=15)
ent_nilai.grid(row=0, column=1, padx=5)
tk.Button(frame_input, text="Tambah ke List", command=tambah_nilai, bg="#1B365D", fg="white").grid(row=0, column=2, padx=5)
listbox_nilai = tk.Listbox(root, width=45, height=8, font=("Consolas", 10))
listbox_nilai.pack(pady=10)
lbl_statistik = tk.Label(root, text="Belum ada data nilai di dalam List.", font=("Arial", 10, "bold"))
lbl_statistik.pack(pady=5)
tk.Button(root, text="Kosongkan List", command=hapus_semua, bg="#DC2626", fg="white").pack(pady=5)
root.mainloop()
📱 B. Source Code GUI Google Colab / Smartphone (ipywidgets)
import ipywidgets as widgets
from IPython.display import display, clear_output

KOMPONEN_TETAP = ("Praktikum Python", "Algoritma", "Struktur Data")
daftar_nilai = []

judul = widgets.HTML(f"<h3>Praktikum 6: List & Tuple</h3><p><b>Tuple Mata Kuliah:</b> {KOMPONEN_TETAP}</p>")
in_nilai = widgets.FloatSlider(description="Nilai:", min=0, max=100, step=0.5, value=80)
btn_tambah = widgets.Button(description="Tambah ke List", button_style="primary")
btn_reset = widgets.Button(description="Reset List", button_style="danger")
out = widgets.Output()

def tampilkan():
    with out:
        clear_output()
        print("Isi List Nilai:", daftar_nilai)
        if daftar_nilai:
            print(f"Jumlah Data : {len(daftar_nilai)} Mahasiswa")
            print(f"Rata-rata   : {sum(daftar_nilai)/len(daftar_nilai):.2f}")
            print(f"Nilai Max   : {max(daftar_nilai)}")
            print(f"Nilai Min   : {min(daftar_nilai)}")
        else:
            print("List masih kosong.")

def aksi_tambah(b):
    daftar_nilai.append(in_nilai.value)
    tampilkan()

def aksi_reset(b):
    daftar_nilai.clear()
    tampilkan()

btn_tambah.on_click(aksi_tambah)
btn_reset.on_click(aksi_reset)
display(judul, in_nilai, widgets.HBox([btn_tambah, btn_reset]), out)

PERTEMUAN 7: Fungsi dan Modularisasi Program

Sub-CPMK 7
🎯 Fokus Praktikum: Membuat program dengan fungsi (parameter & return value)
📌 Capaian Sub-CPMK 7: Mampu merancang dan menerapkan fungsi serta pemanggilan variabel secara modular.
📖 Penjelasan Konsep & Cara Kerja Program
  • Deklarasi Fungsi (def): Memisahkan rumus perhitungan (luas persegi panjang, keliling, dan konversi suhu Celcius ke Fahrenheit) ke dalam fungsi-fungsi independen.
  • Parameter & return: Setiap fungsi menerima input parameter dari pengguna, mengolahnya, dan mengembalikan nilai hasil olahan melalui perintah return untuk ditampilkan ke GUI.
💻 A. Source Code GUI Desktop / Laptop (Tkinter) — pertemuan_7_fungsi_modular.py
import tkinter as tk
from tkinter import messagebox

def hitung_luas_persegi_panjang(panjang, lebar):
    return panjang * lebar

def hitung_keliling_persegi_panjang(panjang, lebar):
    return 2 * (panjang + lebar)

def konversi_celcius_ke_fahrenheit(celcius):
    return (celcius * 9/5) + 32

def eksekusi_program():
    try:
        p = float(ent_panjang.get())
        l = float(ent_lebar.get())
        c = float(ent_suhu.get())
        luas = hitung_luas_persegi_panjang(p, l)
        keliling = hitung_keliling_persegi_panjang(p, l)
        fahrenheit = konversi_celcius_ke_fahrenheit(c)
        lbl_output.config(text=f"1. Luas Persegi Panjang     : {luas:.2f}\n2. Keliling Persegi Panjang : {keliling:.2f}\n3. Suhu {c}°C ke Fahrenheit : {fahrenheit:.2f}°F")
    except ValueError:
        messagebox.showerror("Error", "Pastikan seluruh parameter diisi dengan angka!")

root = tk.Tk()
root.title("Pertemuan 7 - Fungsi & Modularisasi Program")
root.geometry("440x360")
root.config(padx=20, pady=20)
tk.Label(root, text="Program Modular dengan Fungsi", font=("Arial", 12, "bold")).pack(pady=(0, 15))
frame_param = tk.Frame(root)
frame_param.pack()
tk.Label(frame_param, text="Parameter Panjang:").grid(row=0, column=0, sticky="w", pady=4)
ent_panjang = tk.Entry(frame_param, width=20)
ent_panjang.grid(row=0, column=1, pady=4, padx=5)
tk.Label(frame_param, text="Parameter Lebar:").grid(row=1, column=0, sticky="w", pady=4)
ent_lebar = tk.Entry(frame_param, width=20)
ent_lebar.grid(row=1, column=1, pady=4, padx=5)
tk.Label(frame_param, text="Parameter Suhu (°C):").grid(row=2, column=0, sticky="w", pady=4)
ent_suhu = tk.Entry(frame_param, width=20)
ent_suhu.grid(row=2, column=1, pady=4, padx=5)
tk.Button(root, text="Panggil Fungsi & Tampilkan Return Value", command=eksekusi_program, bg="#1B365D", fg="white").pack(pady=15)
lbl_output = tk.Label(root, text="Hasil return value akan tampil di sini.", font=("Consolas", 10), justify="left")
lbl_output.pack(pady=5)
root.mainloop()
📱 B. Source Code GUI Google Colab / Smartphone (ipywidgets)
import ipywidgets as widgets
from IPython.display import display, clear_output

def hitung_luas(p, l): return p * l
def hitung_keliling(p, l): return 2 * (p + l)
def c_ke_f(c): return (c * 9/5) + 32

judul = widgets.HTML("<h3>Praktikum 7: Fungsi & Modularisasi Program</h3>")
in_p = widgets.FloatText(description="Panjang:", value=12.0)
in_l = widgets.FloatText(description="Lebar:", value=5.0)
in_c = widgets.FloatText(description="Suhu (°C):", value=30.0)
btn = widgets.Button(description="Panggil Fungsi Modular", button_style="primary")
out = widgets.Output()

def panggil(b):
    with out:
        clear_output()
        print(f"Return Luas Persegi Panjang     : {hitung_luas(in_p.value, in_l.value):.2f}")
        print(f"Return Keliling Persegi Panjang : {hitung_keliling(in_p.value, in_l.value):.2f}")
        print(f"Return Konversi {in_c.value}°C -> °F    : {c_ke_f(in_c.value):.2f}°F")

btn.on_click(panggil)
display(judul, in_p, in_l, in_c, btn, out)

PERTEMUAN 8: Pengolahan File, Validasi, dan Debugging

Sub-CPMK 8
🎯 Fokus Praktikum: Menyimpan data ke file .txt/.csv dan memperbaiki kesalahan program
📌 Capaian Sub-CPMK 8: Mampu menerapkan operasi file handling (simpan/baca data) serta penanganan kesalahan (debugging & validasi).
📖 Penjelasan Konsep & Cara Kerja Program
  • Validasi & Exception Handling (try-except): Memeriksa apakah ada kolom kosong atau nilai di luar rentang 0–100 menggunakan raise ValueError(...) serta menangkap pesan kesalahannya ke konsol log tanpa membuat aplikasi crash.
  • Operasi File (with open): Menyimpan data secara permanen ke dalam file data_mahasiswa.csv menggunakan modul csv.writer dan mencatat log teks ke log_mahasiswa.txt.
💻 A. Source Code GUI Desktop / Laptop (Tkinter) — pertemuan_8_file_validasi_debugging.py
import tkinter as tk
from tkinter import messagebox
import csv, os

FILE_CSV = "data_mahasiswa.csv"
FILE_TXT = "log_mahasiswa.txt"

def catat_log_debug(pesan):
    txt_debug.insert(tk.END, pesan + "\n")
    txt_debug.see(tk.END)

def validasi_dan_simpan():
    try:
        nama = ent_nama.get().strip()
        nim = ent_nim.get().strip()
        nilai_str = ent_nilai.get().strip()
        if not nama or not nim or not nilai_str:
            raise ValueError("Validasi Gagal: Semua kolom wajib diisi!")
        nilai = float(nilai_str)
        if not (0 <= nilai <= 100):
            raise ValueError("Validasi Gagal: Nilai harus berada di rentang 0 - 100!")
        with open(FILE_CSV, mode="a", newline="", encoding="utf-8") as f_csv:
            csv.writer(f_csv).writerow([nim, nama, nilai])
        with open(FILE_TXT, mode="a", encoding="utf-8") as f_txt:
            f_txt.write(f"NIM: {nim} | Nama: {nama} | Nilai: {nilai}\n")
        catat_log_debug(f"[SUKSES] Data {nama} disimpan ke CSV & TXT.")
    except ValueError as err:
        catat_log_debug(f"[DEBUG ERROR] {err}")
        messagebox.showwarning("Input Tidak Valid", str(err))

def baca_file_txt():
    try:
        if not os.path.exists(FILE_TXT):
            raise FileNotFoundError("File log_mahasiswa.txt belum ditemukan!")
        with open(FILE_TXT, mode="r", encoding="utf-8") as f_txt:
            catat_log_debug("=== ISI FILE TXT ===\n" + f_txt.read().strip())
    except FileNotFoundError as fnf_err:
        catat_log_debug(f"[DEBUG WARNING] {fnf_err}")

root = tk.Tk()
root.title("Pertemuan 8 - File Handling, Validasi & Debugging")
root.geometry("500x460")
root.config(padx=20, pady=15)
tk.Label(root, text="Pengolahan File (.CSV/.TXT) & Debugging", font=("Arial", 12, "bold")).pack(pady=(0, 10))
frame_form = tk.Frame(root)
frame_form.pack()
tk.Label(frame_form, text="NIM:").grid(row=0, column=0, sticky="w", pady=3)
ent_nim = tk.Entry(frame_form, width=28)
ent_nim.grid(row=0, column=1, pady=3)
tk.Label(frame_form, text="Nama Mahasiswa:").grid(row=1, column=0, sticky="w", pady=3)
ent_nama = tk.Entry(frame_form, width=28)
ent_nama.grid(row=1, column=1, pady=3)
tk.Label(frame_form, text="Nilai Akhir (0-100):").grid(row=2, column=0, sticky="w", pady=3)
ent_nilai = tk.Entry(frame_form, width=28)
ent_nilai.grid(row=2, column=1, pady=3)
frame_btn = tk.Frame(root)
frame_btn.pack(pady=10)
tk.Button(frame_btn, text="Validasi & Simpan", command=validasi_dan_simpan, bg="#1B365D", fg="white").grid(row=0, column=0, padx=5)
tk.Button(frame_btn, text="Baca File .TXT", command=baca_file_txt, bg="#4682B4", fg="white").grid(row=0, column=1, padx=5)
txt_debug = tk.Text(root, height=11, width=56, font=("Consolas", 9))
txt_debug.pack(pady=5)
root.mainloop()
📱 B. Source Code GUI Google Colab / Smartphone (ipywidgets)
import ipywidgets as widgets
from IPython.display import display, clear_output
import csv, os

FILE_CSV = "data_mahasiswa.csv"
FILE_TXT = "log_mahasiswa.txt"

judul = widgets.HTML("<h3>Praktikum 8: File Handling (.TXT/.CSV), Validasi & Debugging</h3>")
in_nim = widgets.Text(description="NIM:")
in_nama = widgets.Text(description="Nama:")
in_nilai = widgets.Text(description="Nilai (0-100):")
btn_simpan = widgets.Button(description="Validasi & Simpan", button_style="primary")
btn_baca = widgets.Button(description="Baca File .TXT", button_style="info")
out = widgets.Output()

def simpan(b):
    with out:
        clear_output()
        try:
            nim, nama, n_str = in_nim.value.strip(), in_nama.value.strip(), in_nilai.value.strip()
            if not nim or not nama or not n_str:
                raise ValueError("Semua kolom wajib diisi!")
            nilai = float(n_str)
            if not (0 <= nilai <= 100):
                raise ValueError("Nilai harus berada di rentang 0 - 100!")
            with open(FILE_CSV, "a", newline="", encoding="utf-8") as f:
                csv.writer(f).writerow([nim, nama, nilai])
            with open(FILE_TXT, "a", encoding="utf-8") as f:
                f.write(f"NIM: {nim} | Nama: {nama} | Nilai: {nilai}\n")
            print(f"[SUKSES] Data {nama} tersimpan ke {FILE_CSV} & {FILE_TXT}")
        except ValueError as err:
            print(f"[DEBUG ERROR - Validasi Gagal] {err}")

def baca(b):
    with out:
        clear_output()
        if not os.path.exists(FILE_TXT):
            print("[DEBUG WARNING] File belum ditemukan. Simpan data terlebih dahulu!")
            return
        with open(FILE_TXT, "r", encoding="utf-8") as f:
            print("=== ISI FILE TXT ===\n" + f.read())

btn_simpan.on_click(simpan)
btn_baca.on_click(baca)
display(judul, in_nim, in_nama, in_nilai, widgets.HBox([btn_simpan, btn_baca]), out)

PERTEMUAN 9: Proyek Mini Pemrograman (PjBL)

Sub-CPMK 9 • Proyek Mini
🎯 Fokus Praktikum: Pembuatan aplikasi sederhana terintegrasi
📌 Capaian Sub-CPMK 9: Mampu merancang dan menyelesaikan proyek mini aplikasi sederhana terintegrasi secara tim/kelompok.
📖 Penjelasan Integrasi Proyek Mini (Sistem Inventaris Laboratorium)

Proyek Mini ini menggabungkan seluruh komponen materi utama ke dalam aplikasi manajemen inventaris laboratorium:

  • Struktur Data: Menggunakan Tuple untuk pilihan kategori tetap (Perangkat Keras, Perangkat Lunak, Jaringan, Aksesoris) dan List untuk daftar inventaris aktif.
  • Fungsi & Percabangan: Fungsi tentukan_status_stok(jumlah) otomatis menentukan apakah stok Tersedia Aman (>=10), Stok Menipis (>0), atau Habis.
  • Tabel Treeview & Penyimpanan CSV: Menampilkan data dalam tabel GUI dan menyimpannya ke file inventaris_lab.csv.
💻 A. Source Code GUI Desktop / Laptop (Tkinter) — pertemuan_9_proyek_mini_inventaris.py
import tkinter as tk
from tkinter import ttk, messagebox
import csv, os

FILE_INVENTARIS = "inventaris_lab.csv"
KATEGORI_BARANG = ("Perangkat Keras", "Perangkat Lunak", "Jaringan", "Aksesoris")
data_inventaris = []

def tentukan_status_stok(jumlah):
    if jumlah >= 10: return "Tersedia Aman"
    elif jumlah > 0: return "Stok Menipis"
    else: return "Habis"

def simpan_ke_csv():
    with open(FILE_INVENTARIS, mode="w", newline="", encoding="utf-8") as f:
        writer = csv.writer(f)
        writer.writerow(["Kode", "Nama Alat", "Kategori", "Jumlah", "Status"])
        writer.writerows(data_inventaris)

def perbarui_tabel():
    for row in tree.get_children(): tree.delete(row)
    for item in data_inventaris: tree.insert("", tk.END, values=item)

def tambah_barang():
    try:
        kode, nama, kategori = ent_kode.get().strip(), ent_nama.get().strip(), cmb_kategori.get()
        jumlah = int(ent_jumlah.get().strip())
        if not kode or not nama or jumlah < 0: raise ValueError("Data tidak valid!")
        data_inventaris.append([kode, nama, kategori, jumlah, tentukan_status_stok(jumlah)])
        simpan_ke_csv()
        perbarui_tabel()
    except ValueError as e:
        messagebox.showwarning("Validasi Input", str(e))

root = tk.Tk()
root.title("Pertemuan 9 - Proyek Mini: Sistem Inventaris Lab")
root.geometry("640x440")
root.config(padx=15, pady=15)
tk.Label(root, text="PROYEK MINI: SISTEM INVENTARIS LABORATORIUM", font=("Arial", 12, "bold"), fg="#1B365D").pack(pady=(0, 10))
frame_in = tk.Frame(root)
frame_in.pack(fill="x", pady=5)
tk.Label(frame_in, text="Kode:").grid(row=0, column=0)
ent_kode = tk.Entry(frame_in, width=15)
ent_kode.grid(row=0, column=1, padx=5)
tk.Label(frame_in, text="Nama Alat:").grid(row=0, column=2)
ent_nama = tk.Entry(frame_in, width=20)
ent_nama.grid(row=0, column=3, padx=5)
tk.Label(frame_in, text="Kategori:").grid(row=1, column=0)
cmb_kategori = ttk.Combobox(frame_in, values=KATEGORI_BARANG, state="readonly", width=13)
cmb_kategori.current(0)
cmb_kategori.grid(row=1, column=1, padx=5)
tk.Label(frame_in, text="Jumlah:").grid(row=1, column=2)
ent_jumlah = tk.Entry(frame_in, width=20)
ent_jumlah.grid(row=1, column=3, padx=5)
tk.Button(root, text="Tambah & Simpan CSV", command=tambah_barang, bg="#1B365D", fg="white").pack(pady=8)
kolom = ("Kode", "Nama Alat", "Kategori", "Jumlah", "Status")
tree = ttk.Treeview(root, columns=kolom, show="headings", height=9)
for kol in kolom:
    tree.heading(kol, text=kol)
    tree.column(kol, width=115, anchor="center")
tree.pack(fill="both", expand=True)
root.mainloop()
📱 B. Source Code GUI Google Colab / Smartphone (ipywidgets)
import ipywidgets as widgets
from IPython.display import display, clear_output
import csv

KATEGORI = ("Perangkat Keras", "Perangkat Lunak", "Jaringan", "Aksesoris")
inventaris = []

def cek_status(jml):
    if jml >= 10: return "Tersedia Aman"
    elif jml > 0: return "Stok Menipis"
    return "Habis"

judul = widgets.HTML("<h3>Praktikum 9 (Proyek Mini): Sistem Inventaris Lab Terintegrasi</h3>")
in_kode = widgets.Text(description="Kode:", value="LAB-01")
in_nama = widgets.Text(description="Nama Alat:", value="Router Mikrotik")
in_kat = widgets.Dropdown(options=KATEGORI, description="Kategori:")
in_jml = widgets.IntText(description="Stok:", value=12)
btn_add = widgets.Button(description="Tambah & Simpan CSV", button_style="primary")
out = widgets.Output()

def tambah(b):
    with out:
        clear_output()
        status = cek_status(in_jml.value)
        inventaris.append([in_kode.value, in_nama.value, in_kat.value, in_jml.value, status])
        with open("inventaris_lab.csv", "w", newline="", encoding="utf-8") as f:
            w = csv.writer(f)
            w.writerow(["Kode", "Nama Alat", "Kategori", "Stok", "Status"])
            w.writerows(inventaris)
        print("=== DAFTAR INVENTARIS LAB (Tersimpan di inventaris_lab.csv) ===")
        for idx, item in enumerate(inventaris, 1):
            print(f"{idx}. [{item[0]}] {item[1]} ({item[2]}) - Stok: {item[3]} -> {item[4]}")

btn_add.on_click(tambah)
display(judul, in_kode, in_nama, in_kat, in_jml, btn_add, out)

PERTEMUAN 10: Ujian Akhir Semester (UAS — Studi Kasus Komprehensif)

Sub-CPMK 10 • UAS
🎯 Fokus Praktikum: Praktikum individu berbasis studi kasus
📌 Capaian Sub-CPMK 10: Mampu mendemonstrasikan penguasaan seluruh materi melalui Ujian Akhir Semester berbasis studi kasus mandiri.
📖 Penjelasan Studi Kasus UAS (Sistem Evaluasi Ketercapaian CPMK & OBE)

Studi kasus puncak pada UAS ini mengintegrasikan seluruh capaian pembelajaran untuk menghitung evaluasi akhir mata kuliah berbasis OBE:

  • Fungsi Perhitungan Bobot OBE: Menghitung skor akhir dengan bobot (0.20 * Tugas) + (0.25 * UTS) + (0.25 * Proyek) + (0.30 * UAS).
  • Konversi Grade & Standar Ketercapaian: Menentukan Grade huruf serta mengecek apakah skor mahasiswa mencapai ambang batas kelulusan CPMK (>= 68).
  • Evaluasi Indikator Kelas & Ekspor CSV: Menghitung persentase mahasiswa yang mencapai skor >= 68 (target minimal 75% mahasiswa) dan menyimpan rekapitulasinya ke dalam file CSV.
💻 A. Source Code GUI Desktop / Laptop (Tkinter) — pertemuan_10_uas_studi_kasus_obe.py
import tkinter as tk
from tkinter import ttk, messagebox
import csv

data_mahasiswa = []

def hitung_nilai_akhir(tugas, uts, proyek, uas):
    return (0.20 * tugas) + (0.25 * uts) + (0.25 * proyek) + (0.30 * uas)

def konversi_grade_dan_obe(skor):
    if skor >= 81: grade = "A"
    elif skor >= 71: grade = "B"
    elif skor >= 68: grade = "B-"
    elif skor >= 60: grade = "C"
    else: grade = "D/E"
    return grade, ("Tercapai (>=68)" if skor >= 68 else "Belum Tercapai")

def proses_tambah_mahasiswa():
    try:
        nim, nama = ent_nim.get().strip(), ent_nama.get().strip()
        n_t, n_u, n_p, n_a = float(ent_tugas.get()), float(ent_uts.get()), float(ent_proyek.get()), float(ent_uas.get())
        akhir = hitung_nilai_akhir(n_t, n_u, n_p, n_a)
        grade, obe = konversi_grade_dan_obe(akhir)
        data_mahasiswa.append([nim, nama, n_t, n_u, n_p, n_a, round(akhir, 2), grade, obe])
        for item in tree.get_children(): tree.delete(item)
        for m in data_mahasiswa: tree.insert("", tk.END, values=m)
        skor_list = [m[6] for m in data_mahasiswa]
        persen_obe = (sum(1 for s in skor_list if s >= 68) / len(skor_list)) * 100
        lbl_stat.config(text=f"Rata-rata: {sum(skor_list)/len(skor_list):.2f} | Ketercapaian OBE (>=68): {persen_obe:.1f}%")
        with open("rekap_uas_obe.csv", "w", newline="", encoding="utf-8") as f:
            csv.writer(f).writerows(data_mahasiswa)
    except ValueError:
        messagebox.showerror("Error", "Pastikan seluruh input nilai valid!")

root = tk.Tk()
root.title("Pertemuan 10 - Studi Kasus UAS: Sistem Evaluasi Nilai OBE")
root.geometry("750x480")
root.config(padx=15, pady=15)
tk.Label(root, text="STUDI KASUS UAS: REKAPITULASI NILAI & EVALUASI OBE", font=("Arial", 12, "bold"), fg="#1B365D").pack(pady=(0, 10))
frame_form = tk.Frame(root)
frame_form.pack()
tk.Label(frame_form, text="NIM:").grid(row=0, column=0)
ent_nim = tk.Entry(frame_form, width=12); ent_nim.grid(row=0, column=1, padx=3)
tk.Label(frame_form, text="Nama:").grid(row=0, column=2)
ent_nama = tk.Entry(frame_form, width=18); ent_nama.grid(row=0, column=3, padx=3)
tk.Label(frame_form, text="Tugas(20%):").grid(row=0, column=4)
ent_tugas = tk.Entry(frame_form, width=8); ent_tugas.grid(row=0, column=5, padx=3)
tk.Label(frame_form, text="UTS(25%):").grid(row=1, column=0)
ent_uts = tk.Entry(frame_form, width=12); ent_uts.grid(row=1, column=1, padx=3)
tk.Label(frame_form, text="Proyek(25%):").grid(row=1, column=2)
ent_proyek = tk.Entry(frame_form, width=18); ent_proyek.grid(row=1, column=3, padx=3)
tk.Label(frame_form, text="UAS(30%):").grid(row=1, column=4)
ent_uas = tk.Entry(frame_form, width=8); ent_uas.grid(row=1, column=5, padx=3)
tk.Button(root, text="Hitung & Simpan Evaluasi OBE", command=proses_tambah_mahasiswa, bg="#1B365D", fg="white").pack(pady=8)
kolom = ("NIM", "Nama", "Tugas", "UTS", "Proyek", "UAS", "Akhir", "Grade", "Status OBE")
tree = ttk.Treeview(root, columns=kolom, show="headings", height=9)
for kol in kolom:
    tree.heading(kol, text=kol)
    tree.column(kol, width=78, anchor="center")
tree.pack(fill="both", expand=True)
lbl_stat = tk.Label(root, text="Belum ada data mahasiswa.", font=("Arial", 10, "bold"), fg="#1B365D")
lbl_stat.pack(pady=5)
root.mainloop()
📱 B. Source Code GUI Google Colab / Smartphone (ipywidgets)
import ipywidgets as widgets
from IPython.display import display, clear_output
import csv

rekap_obe = []

def hitung_obe(t, uts, p, uas):
    akhir = (0.20 * t) + (0.25 * uts) + (0.25 * p) + (0.30 * uas)
    if akhir >= 81: g = "A"
    elif akhir >= 71: g = "B"
    elif akhir >= 68: g = "B-"
    elif akhir >= 60: g = "C"
    else: g = "D/E"
    status = "Tercapai (>=68)" if akhir >= 68 else "Belum Tercapai"
    return round(akhir, 2), g, status

judul = widgets.HTML("<h3>Praktikum 10 (UAS): Evaluasi Ketercapaian CPMK & OBE</h3>")
in_nim = widgets.Text(description="NIM:", value="26001")
in_nama = widgets.Text(description="Nama:", value="Aisyah")
in_t = widgets.FloatSlider(description="Tugas(20%):", min=0, max=100, value=80)
in_u = widgets.FloatSlider(description="UTS(25%):", min=0, max=100, value=75)
in_p = widgets.FloatSlider(description="Proyek(25%):", min=0, max=100, value=85)
in_a = widgets.FloatSlider(description="UAS(30%):", min=0, max=100, value=80)
btn = widgets.Button(description="Simpan & Analisis OBE", button_style="success")
out = widgets.Output()

def proses_uas(b):
    with out:
        clear_output()
        akhir, grade, st = hitung_obe(in_t.value, in_u.value, in_p.value, in_a.value)
        rekap_obe.append([in_nim.value, in_nama.value, akhir, grade, st])
        with open("rekap_nilai_uas_obe.csv", "w", newline="", encoding="utf-8") as f:
            w = csv.writer(f)
            w.writerow(["NIM", "Nama", "Nilai Akhir", "Grade", "Status OBE"])
            w.writerows(rekap_obe)
        skor = [r[2] for r in rekap_obe]
        lulus_obe = sum(1 for s in skor if s >= 68)
        persen = (lulus_obe / len(skor)) * 100
        indikator = "MEMENUHI STANDAR OBE (>=75%)" if persen >= 75 else "BELUM MEMENUHI (<75%)"
        print(f"Total Mahasiswa: {len(skor)} | Rata-rata Kelas: {sum(skor)/len(skor):.2f}")
        print(f"Ketercapaian CPMK (>=68): {persen:.1f}% -> {indikator}")
        print("-" * 55)
        for r in rekap_obe:
            print(f"{r[0]} - {r[1]} | Akhir: {r[2]} ({r[3]}) -> {r[4]}")

btn.on_click(proses_uas)
display(judul, in_nim, in_nama, in_t, in_u, in_p, in_a, btn, out)