Lompat ke konten Lompat ke sidebar Lompat ke footer

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.  


simple_stock_table

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

1  # Class - Main Window
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

24     # Method - Create Buttons
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

45     # Method - Create Table
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

55     # Method Fill - Table
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

79     # Method - Add Data
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

107     # 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

127     # Method - Delete Data
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"