Tabel Database Sederhana Menggunakan PyQt5 dan SQLite3 Python
Pada post kali ini, akan dicoba membuat sebuah program GUI Python sederhana, yang mampu menampilkan dan mengedit tabel database SQLite.
Program python ini menggunakan sebuah file database yang sudah terdapat tabel di dalamnya. Sehingga fokusnya hanya untuk menambah dan menghapus data. Karena itu, file database.db harus ada pada direktori yang sama dengan file pythonnya saat program dijalankan.
Script python dan databasenya dapat di-download pada link GITHUB berikut ini. Sedangkan code nya akan dibahas pada tulisan ini.
https://github.com/WahyuNoorIchwan/simple-stock-table-sqlite3-pyqt5
Karena masih sederhana, program di atas masih belum mendukung fitur lain seperti verifikasi data. Misalkan cell dari tabel yang harus diisi dengan angka. Jadi mungkin akan muncul beberapa error ketika program dijalankan. Untuk fitur-fitur tambahan lain silahkan bisa dikembangkan sendiri ya.
Buat yang mau lebih paham seluk-beluk coding-nya, berikut ini penjelasan per bagian dari program python di atas.
Inisiasi Class Main Window
Bagian ini adalah awal yang selalu ada saat pembuatan GUI python menggunakan PyQt5. Dimana main window menampung semua widget yang ada, seperti buttons dan table.
Karena masih sederhana, program python ini hanya menggunakan 1 class yaitu main window. Semua proses yang ada dilakukan menggunakan method, seperti self.createButtons() untuk membuat 3 tombol di atas, dan self.createTable() untuk membuat tabel yang menampilkan data
2 class mainWindow(QtWidgets.QMainWindow):
3 def __init__(self):
4 super(mainWindow, self).__init__()
5
6 # Setup Main Window
7 self.setWindowTitle("Simple Database")
8 self.setFixedSize(600, 800)
9
10 # Main Window Frame Layout
11 self.mainFrame = QtWidgets.QFrame()
12 self.mainLayout = QtWidgets.QVBoxLayout()
13
14 # Create Window Layout - Buttons and Table
15 self.createButtons()
16 self.createTable()
17
18 # Fill Table when app is starting
19 self.fillTable()
20
21 # Setup Main Frame to Main Window
22 self.mainFrame.setLayout(self.mainLayout)
23 self.setCentralWidget(self.mainFrame)
- # Setup Main Window: Mengatur property dari main window. Disini yang diatur adalah judul dan ukuran main window.
- # Main Windo Frame Layout: Membuat widget dan layoutnya yang akan dipasang pada main window. Main Window pada PyQt5 tidak bisa langsung dipasangi widgets. Widget dipasang pada widget lain seperti QFrame, yang kemudian Frame ini dipasang pada Main Window.
- Frame widget menggunakan QFrame dan layoutnya menggunakan QVBoxLayout.
- # Create Window Layout - Button and Table: Membuat widgets dan memasangnya pada frame main window. Pembuatan buttons menggunakan self.createButtons() dan pembuatan tabel menggunakan self.createTable(). Pembedaan ini dilakukan agar proses pembuatan widget lebih rapih dan terkontrol.
- Table yang dibuat masih kosong. Untuk mengisinya digunakan self.fillTable(). Penggunakan method tersebut agar isi tabel bisa diubah/di-refresh ketika terjadi perubahan data pada SQL.
- # Setup Main Frame to Main Window: Setelah main frame terisi dengan buttons dan table, pasang frame pada main window menggunakan method .setCentralWidget().
Method: createButtons
25 def createButtons(self):
26 # Layout is consisted of buttons and table
27
28 # Buttons are grouped horizontaly
29 group = QtWidgets.QWidget()
30 group_ly = QtWidgets.QHBoxLayout()
31 group_ly.setContentsMargins(0, 0, 0, 0)
32
33 add_button = QtWidgets.QPushButton("Add Data")
34 add_button.clicked.connect(self.addDataDialog)
35 group_ly.addWidget(add_button)
36
37 del_button = QtWidgets.QPushButton("Delete Data")
38 del_button.clicked.connect(self.deleteData)
39 group_ly.addWidget(del_button)
40
41 # Setup buttons group & add to main layout
42 group.setLayout(group_ly)
43 self.mainLayout.addWidget(group)
- Terdapat 2 tombol add_button dan del_button yang tersusun secara horizontal. Dibutuhkan sebuah widget untuk menampung kedua button ini, baru ditambahkan ke self.mainLayout, karena self.mainLayout memiliki layout vertikal
- group berisi QWidget dan group_ly layout group menggunakan QHBoxLayout, untuk membuat layout horizontal
- add_button dan del_button dibuat menggunakan QPushButton. Untuk menjalankan suatu method ketika button diklik, gunakna method .clicked.connect(method/function). Ingat, jangan gunakan () pada function, karena akan menjalankannya.
- add_button terhubung dengan method self.addDataDialog. Yang dipanggil adalah window/dialog untuk memasukkan data yang akan ditambahkan ke SQL.
- del_button terhubung dengan self.delData, untuk menghapus data pada SQL dan me-refresh data pada tabel di GUI.
- add_button dan del_button ditambahkan ke group_ly menggunakan method .addWidget()
- Setelah buttons ditambahkan, setup group_ly ke group widget. Baru tambahkan group widget ke self.main.Layout, sama menggunakan method .addWidget()
Method: createTable
46 def createTable(self):
47 # Create Table Widget - Setup Number of Row, Columns, Headers
48 self.table = QtWidgets.QTableWidget()
49
50 self.table.setColumnCount(4)
51 self.table.setHorizontalHeaderLabels(["", "Name", "Phone", "Address"])
52
53 # Add Table to Main Layout
54 self.mainLayout.addWidget(self.table)
- createTable digunakan untuk membuat table. Table dibuat menggunakan QTableWidget dan disimpan pada self.table.
- Berbeda dengan button sebelumnya, table dijadikan attribute class agar bisa diakses dari method lain, seperti self.fillTable().
- Jumlah baris dan kolom dari QTableWidget harus ditentukan, jika tidak maka data tidak akan tampil. Atur jumlah kolom .setColumnCount(4). Karena ada 4 kolom pada tabel.
- Beri label dengan .setHorizontalHeaderLabels(). Kolom pertama adalah checkbox sehingga labelnya diisi dengan string kosong.
- Row tidak diatur dulu. Diatur pada method self.fillTable() sesuai dengan jumlah baris data dari SQL.
- Setelah jadi tambahkan self.table ke self.mainLayout().
Method: fillTable
56 def fillTable(self):
57 # First Get Data from Database then Insert to Table Widget
58
59 # Get Data from SQLite Table
60 conn = sqlite3.connect("database.db")
61 cursor = conn.cursor()
62
63 data = cursor.execute("""SELECT * FROM profile""").fetchall()
64 conn.close()
65
66 # Adjust row number to data number
67 self.table.setRowCount(len(data))
68
69 # Fill Table Cells using data from Database
70 for i in range(len(data)):
71 # Add Check Box
72 self.table.setCellWidget(i, 0, QtWidgets.QCheckBox())
73
74 for j in range(3):
75 self.table.setItem(i, j+1,
76 QtWidgets.QTableWidgetItem(str(data[i][j])))
77 # Setup colum width to fit contents
78 self.table.resizeColumnsToContents()
- method fillTable() terdapat 2 proses, mengambil data dari SQL, kemudian menampilkannya pada table yang sudah dibuat pada self.table
- Pertama, buat koneksi dan kursor untuk akses SQL (conn & cursor).
- Ambil data pada SQL dan simpan pada variabel data, menggunakan query pada baris ke 63. Hasil dari .fetchall() berformat nested list berisi data row yang berbentuk list.
- Setelah selesai, .close() connection SQL untuk menghindari masalah "database busy".
- Atur jumlah baris menggunakan .setRowCount(len(data)) agar self.table mempunyai jumlah baris sesuai jumlah data. Jika ini tidak dilakukan, data tidak akan tampil pada tabel walaupun sudah ditambahkan.
- Isi tabel dengan data. Gunakan for loop sejumlah baris data terlebih dahulu. Hal ini dilakukan untuk menambahkan checkbox pada kolom pertama.
- Pada for loop terhadap baris, tambahkan widget QCheckBox pada kolom pertama menggunakan self.table.setCellWidget(). i adalah baris ke-i, dan 0 adalah kolom ke 0.
- Masih ada 3 kolom yang harus ditambahkan. Buat foor loop lagi sebanyak tiga kali. Tambahkan data ke self.table menggunakan self.table.setItem().
- Sama seperti sebelumnya, i adalah baris ke-i dan j adalah data kolom ke j, masing-masing cell diisi data[i][j]. Pada table digunakan index j+1 karena pengisian data dimulai dari kolom ke 1, kolom pertama sudah diisi menggunakan QCheckBox.
- Gunakan method .resizeColumnsToContents() agar ukuran kolom menyesuaikan isi dari cell QTableWidget.
Method addDataDialog
80 def addDataDialog(self):
81 self.addDialog = QtWidgets.QDialog()
82 self.addDialog.setWindowTitle("Add Data")
83 dialog_ly = QtWidgets.QGridLayout()
84
85 dialog_ly.addWidget(QtWidgets.QLabel("Name"), 0, 0)
86 self.name_form = QtWidgets.QLineEdit()
87 dialog_ly.addWidget(self.name_form, 0, 1)
88
89 dialog_ly.addWidget(QtWidgets.QLabel("Phone"), 1, 0)
90 self.phone_form = QtWidgets.QSpinBox()
91 self.phone_form.setMinimum(0)
92 self.phone_form.setMaximum(999999999)
93 dialog_ly.addWidget(self.phone_form, 1, 1)
94
95 dialog_ly.addWidget(QtWidgets.QLabel("Address"), 2, 0)
96 self.address_form = QtWidgets.QLineEdit()
97 dialog_ly.addWidget(self.address_form, 2, 1)
98
99 # Add Button
100 addButton = QtWidgets.QPushButton("Add Data")
101 addButton.clicked.connect(self.addData)
102 dialog_ly.addWidget(addButton, 3, 1)
103
104 # Setup Dialog
105 self.addDialog.setLayout(dialog_ly)
106 self.addDialog.exec_()
- Method selanjutnya adalah dialog yang berisi form untuk menambahkan data. Terdapat 3 buah kolom data, yaitu nama, "Name", "Phone", "Address". Karena itu terdapat 3 form yang disimpan pada self.name_form, self.phone_form, self.address_form.
- self.name_form dan self.address_form berisi text, sehingga digunakan widget QLineEdit.
- self.phone_form menggunakan angka sebagai input. Karena itu digunakan QSpinBox agar hanya bisa menerima input angka. self.phone_form digunakan method .setMaximum(999999999), maksudnya agar bisa menerima input hingga 9 digit.
- Layout dialog menggunakan QGridLayout agar widget bisa disusun dalam bentuk grid dengan format "label: Form".
- Button "add data" dibuat dan dihubungkan denga method self.addData untuk proses penambahan data yang telah dimasukkan pada form di atas.
- Jalankan dialog yang dibuat dengan method .exec_().
Method add Data
108 def addData(self):
109 # Get Data to Tuple for SQLite Insert
110 batch = (self.name_form.text(), self.phone_form.value(),
111 self.address_form.text())
112
113 # Insert Data to SQL
114 conn = sqlite3.connect("database.db")
115 cursor = conn.cursor()
116
117 cursor.execute("""INSERT INTO profile VALUES (?, ?, ?)""",
118 batch)
119 conn.commit()
120 conn.close()
121
122 # Update Table
123 self.fillTable()
124
125 # Close Forms Dialog
126 self.addDialog.close()
- Method di atas adalah self.addData yang digunakan untuk memasukkan data dari self.addDataDialog ke table SQLite.
- Pertama ambil value dari 3 form yang ada, menggunakan method masing-masing self.name_form.text(), self.phone_form.value(), dan self.address_form.text().
- Ingat, self.phone_form menggunakan QSpinBox sendiri sehingga method untuk mengambil value nya beda.
- Susun ketiga value di atas menjadi sebari tuple dan disimpan pada variable batch. Sebaris tuple diperlukan untuk memasukkan satu baris data ke kolom tabel SQLite.
- Buat koneksi dan kursor ke SQLite. Proses ini akan selalu terulang setiap transaksi SQLite untuk menghindari error "database is locked".
- execute SQLite query pada baris ke 117.
- Commit insert dan tutup koneksi setelah query dijalankan.
- Setelah data masuk ke table SQL, update table / data yang ditampilkan dengan method self.fillTable() yang sudah dibuat sebelumnya.
- Tutup dialog penambahan data dengan self.addDialog.close()
Method deleteData
128 def deleteData(self):
129 # Delete data based on check box
130
131 # Connection and Cursor to delete SQL data and fill table
132 conn = sqlite3.connect("database.db")
133 cursor = conn.cursor()
134
135 # First get name of checked (want to delete)
136 # name will be used to delete record on database
137 deletedName = []
138 for i in range(self.table.rowCount()):
139 # Add Name to deleted if checked
140 if self.table.cellWidget(i, 0).isChecked():
150 deletedName.append(self.table.item(i, 1).text())
160
161 # Delete Data From SQL
162 for name in deletedName:
163 cursor.execute("""DELETE FROM profile WHERE name='{}'""".format(
164 name))
165
166 # Commit Change
167 conn.commit()
168
169 # Refresh table contents
170 self.fillTable()
- Method yang terakhir, untuk menghapus data dari table SQLite, kemudian me-refresh tabel yang tampil
- Idenya adalah, menghapus data yang di-check menggunakan checkbox di kolom pertama tabel.
- Penghapusan data baris tertentu pada tabel SQL menggunakan condition "WHERE column="value". Semua data yang memiliki nilai "value" pada kolom column akan dihapus.
- Tabel yang dibuat memiliki kolom "name" sebagai Primary Key, artinya semua "value" di kolom name unik tidak ada yang sama. Sehingga jika dimasukkan sebuah value, maka hanya 1 baris data yang dihapus, karena tidak ada data kembar.
- Buat list deletedName pada baris ke 137 untuk menampung "name" yang akan dihapus dari tabel.
- Mulai for loop pada baris 138, sebanyak jumlah baris tabel (self.table.rowCount()).
- Cek kolom pertama tabel, apakah check box nya di-check atau tidak menggunakan self.table.cellWidget(i, 0). Method .cellWidget() digunakan untuk mengambil widget yang terpasang pada cell QTableWidget.
- Jika di-check, ambil value dari kolom "name" menggunakan self.table.item(i, 1).text(). Artinya diambil text pada baris ke-i kolom kedua, ingat "name" berada di kolom kedua tabel.
- Tambahkan ke deletedName dengan method deletedName.append().
- Setelah terkumpul mulai hapus satu persatu data yang di-check. Gunakan for loop pada baris 162 terhadap seluruh name di dalam list deletedName.
- execute query untuk delete data pada baris 163. Seperti yang sudah dijelaskan, digunakan condition "WHERE name='{}'", yang artinya data yang dihapus yang kolom "name" memiliki value tertentu.
- Commit query dan close connection SQLite.
- Refresh data pada tabel menggunakan self.fillTable() seperti sebelumnya.
Posting Komentar untuk "Tabel Database Sederhana Menggunakan PyQt5 dan SQLite3 Python"