Membangun Aplikasi Manajemen Kontak dengan PyQt5: Panduan Lengkap untuk Pemula



**Pendahuluan**  
Aplikasi manajemen kontak adalah proyek ideal untuk mempelajari konsep CRUD (Create, Read, Update, Delete) dalam GUI. Artikel ini akan membimbing Anda membuat aplikasi phonebook sederhana menggunakan PyQt5 dengan fitur tambah, edit, dan hapus kontak. Kita akan menggunakan kombinasi QListWidget dan QDialog untuk antarmuka yang interaktif.

---

### **Struktur Proyek**  
1. **`main.py`** - File utama aplikasi  
2. **`MainForm.py`** - Form utama manajemen kontak  
3. **`EntryForm.py`** - Dialog form input data  

---

### **1. File main.py**  
```python
#!/usr/bin/python3

import sys
from PyQt5.QtWidgets import QApplication
from MainForm import MainForm

if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = MainForm()
    window.show()
    app.exec_()
```

**Penjelasan:**  
- Inisialisasi aplikasi Qt  
- Membuat dan menampilkan form utama  
- Memulai event loop  

---

### **2. File MainForm.py**  
#### **Komponen Utama**  
```python
class MainForm(QWidget):
    def __init__(self):
        super().__init__()
        # Inisialisasi UI
        self.setupUi()
        
        # Data storage
        self.contacts = []  # Alternatif: gunakan model data terpisah
```

**Struktur UI:**  
```python
def setupUi(self):
    # Konfigurasi window
    self.setGeometry(300, 300, 450, 350)
    self.setWindowTitle('Phonebook Manager')
    
    # Tombol aksi
    self.buttonLayout = QHBoxLayout()
    buttons = [
        ('Tambah', self.addButtonClick),
        ('Ubah', self.editButtonClick),
        ('Hapus', self.deleteButtonClick),
        ('Kosongkan', self.contactList.clear),
        ('Keluar', self.close)
    ]
    
    for text, handler in buttons:
        btn = QPushButton(text)
        btn.clicked.connect(handler)
        self.buttonLayout.addWidget(btn)
    
    # List kontak
    self.contactList = QListWidget()
    
    # Layout utama
    mainLayout = QVBoxLayout()
    mainLayout.addWidget(self.contactList)
    mainLayout.addLayout(self.buttonLayout)
    self.setLayout(mainLayout)
```

**Fitur CRUD:**  
1. **Tambah Kontak**  
```python
def addButtonClick(self):
    dialog = EntryForm()
    if dialog.exec_() == QDialog.Accepted:
        name = dialog.nameLineEdit.text().strip()
        phone = dialog.phoneLineEdit.text().strip()
        if name and phone:
            self.contactList.addItem(f"{name} - {phone}")
```

2. **Edit Kontak**  
```python
def editButtonClick(self):
    selected = self.contactList.currentItem()
    if not selected: return
    
    dialog = EntryForm()
    name, phone = selected.text().split(" - ")
    dialog.nameLineEdit.setText(name)
    dialog.phoneLineEdit.setText(phone)
    
    if dialog.exec_() == QDialog.Accepted:
        selected.setText(f"{dialog.nameLineEdit.text()} - {dialog.phoneLineEdit.text()}")
```

3. **Hapus Kontak**  
```python
def deleteButtonClick(self):
    row = self.contactList.currentRow()
    if row >= 0:
        self.contactList.takeItem(row)
```

---

### **3. File EntryForm.py**  
```python
class EntryForm(QDialog):
    def __init__(self):
        super().__init__()
        self.setupUi()
        
    def setupUi(self):
        # Konfigurasi dialog
        self.setWindowTitle('Form Kontak')
        self.setFixedSize(300, 150)
        
        # Komponen input
        self.nameLineEdit = QLineEdit()
        self.phoneLineEdit = QLineEdit()
        
        # Validasi input
        self.phoneLineEdit.setValidator(QIntValidator())
        
        # Tombol aksi
        self.buttonBox = QDialogButtonBox(
            QDialogButtonBox.Ok | QDialogButtonBox.Cancel
        )
        self.buttonBox.accepted.connect(self.validate)
        self.buttonBox.rejected.connect(self.reject)
        
        # Layout
        formLayout = QFormLayout()
        formLayout.addRow('Nama Lengkap:', self.nameLineEdit)
        formLayout.addRow('Nomor HP:', self.phoneLineEdit)
        formLayout.addRow(self.buttonBox)
        
        self.setLayout(formLayout)
    
    def validate(self):
        """Validasi input sebelum accept"""
        if self.nameLineEdit.text() and self.phoneLineEdit.text():
            self.accept()
        else:
            QMessageBox.warning(self, 'Error', 'Semua field harus diisi!')
```

---

### **Arsitektur Aplikasi**  
```
+-----------------------+
|      Main Window      |
| +-------------------+ |
| |  Daftar Kontak    | |
| +-------------------+ |
| [Tambah][Edit][Hapus] |
+-----------------------+
          |
          v
+-----------------------+
|    EntryForm Dialog   |
| Nama: _______________ |
| No HP: ______________ |
|      [OK] [Batal]     |
+-----------------------+
```

---

### **Best Practices**  
1. **Pemisahan Logika**  
   - Pisahkan logika bisnis ke class terpisah  
   - Gunakan model-view-controller pattern  

2. **Validasi Data**  
   ```python
   # Di EntryForm.py
   self.phoneLineEdit.setValidator(QRegExpValidator(
       QRegExp("[0-9]{8,15}"), self))
   ```

3. **Persistensi Data**  
   Simpan data ke file JSON:  
   ```python
   def save_data(self):
       with open('contacts.json', 'w') as f:
           json.dump([self.contactList.item(i).text() 
                     for i in range(self.contactList.count())], f)
   ```

---

### **Penyempurnaan**  
1. **Pencarian Kontak**  
   ```python
   self.searchField = QLineEdit()
   self.searchField.textChanged.connect(self.filter_contacts)
   
   def filter_contacts(self, text):
       for i in range(self.contactList.count()):
           item = self.contactList.item(i)
           item.setHidden(text.lower() not in item.text().lower())
   ```

2. **Import/Export CSV**  
   ```python
   def export_csv(self):
       with open('contacts.csv', 'w') as f:
           writer = csv.writer(f)
           writer.writerow(['Nama', 'Telepon'])
           for i in range(self.contactList.count()):
               name, phone = self.contactList.item(i).text().split(' - ')
               writer.writerow([name, phone])
   ```

---

### **Kesimpulan**  
Aplikasi ini menunjukkan:  
✅ Implementasi CRUD dasar  
✅ Manajemen dialog modal  
✅ Penggunaan layout manager  

**Challenge:** Tambahkan fitur grup kontak dan foto profil!

---

**Referensi Tambahan:**  
- [Dokumentasi QListWidget](https://doc.qt.io/qt-5/qlistwidget.html)  
- [PyQt Form Validation](https://www.pythonguis.com/tutorials/pyqt6-form-validation/)  
- [Model-View Programming](https://doc.qt.io/qt-6/model-view-programming.html)

Komentar