# Minimal SQLite table view in PyQt5

I have just started digging into PyQt5 in order to write a small GUI for editing a lexical database. Before I dig deeper into this, I would like to have feedback on the progress so far. Some particular points of note are listed below.

• I envision use cases where two windows of this type and a window of different type each show different portions of the underlying table, which is why I used the QSortFilterProxyModel instead of filtering directly in the database. For my production data, I have about 50000 rows × 10 columns, does that mean I should change that? See this Q
• The implementation details of what FilterDialog and FilterModel look like will change, but is the communication reasonable? I get data from a user input (here, the QLineEdit in FilterDialog) into the filter by reading the content of the input widget and writing an attribute of the FilterModel before telling everything that that model has changed, is this what I should do or should I do something else?
• I have never really done GUI programming before, so I don't know best practices on how to structure projects like this – and I'm sure I don't know many other things. How do I ensure that this project can grow into something maintainable?

The code is the following, it takes one command line argument which is the path to an SQLite 3 database with a table called “FormTable”.

import sys

from PyQt5.QtGui import *
from PyQt5.QtSql import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *

class FilterDialog(QDialog):
def __init__(self, parent):
super().__init__(parent)
self.init_elements()

def init_elements(self):
cancelButton = QPushButton("Cancel")
cancelButton.clicked.connect(self.reject)

okButton = QPushButton("OK")
okButton.setDefault(True)
okButton.clicked.connect(self.accept)

hbox = QHBoxLayout()

self.regex = QLineEdit()

vbox = QVBoxLayout()

self.setLayout(vbox)

self.setWindowTitle('Filter Column')

class FilterModel(QSortFilterProxyModel):
def __init__(self, filters):
super().__init__()
self.filters = filters

def filterAcceptsRow(self, row, modelindex):
for i, filter in enumerate(self.filters):
if filter not in str(self.sourceModel().index(row, i).data()):
return False
return True

class WordListViewer(QMainWindow):
def __init__(self, path):
super().__init__()
self.dbpath = path
self.init_elements()

def init_elements(self):
exitAct = QAction(QIcon.fromTheme('exit'), 'Exit', self)
exitAct.setShortcut('Ctrl+Q')
exitAct.triggered.connect(qApp.quit)

self.statusBar().showMessage(self.dbpath)

self.init_table()

self.setCentralWidget(self.table)

self.setGeometry(200, 100, 800, 600)
self.setWindowTitle('FormTable viewer')

self.show()

def init_table(self):
db.setDatabaseName(self.dbpath)
if not db.open():
raise Error("Could not open the database")

self.model = QSqlTableModel(self, db)
self.model.setTable("FormTable")
self.model.EditStrategy(QSqlTableModel.OnManualSubmit)
self.model.select()

self.table = QTableView()
self.table.setSortingEnabled(True)
self.table.show()

def show_filter(logical_index):
dialog = FilterDialog(self)
if not dialog.exec_():
return
filter = dialog.regex.text()
self.filters.filters[logical_index] = filter
print(self.filters.filters)
self.filters.modelReset.emit()

self.filters = FilterModel([""] * self.model.columnCount())
self.filters.setSourceModel(self.model)
self.table.setModel(self.filters)
self.filters.modelReset.emit()