Search code examples
pythonsqlitedatasetqtableviewpyside2

Live Update QTableView based on Signal from DB (SQLITE)


I have a very basic PySide2 program that uses the dataset library for sqlite.

My very simple layout file is

# -*- coding: utf-8 -*-

################################################################################
## Form generated from reading UI file 'basic.ui'
##
## Created by: Qt User Interface Compiler version 5.14.2
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################

from PySide2.QtCore import (QCoreApplication, QDate, QDateTime, QMetaObject,
    QObject, QPoint, QRect, QSize, QTime, QUrl, Qt)
from PySide2.QtGui import (QBrush, QColor, QConicalGradient, QCursor, QFont,
    QFontDatabase, QIcon, QKeySequence, QLinearGradient, QPalette, QPainter,
    QPixmap, QRadialGradient)
from PySide2.QtWidgets import *


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        if not MainWindow.objectName():
            MainWindow.setObjectName(u"MainWindow")
        MainWindow.resize(1034, 803)
        self.centralwidget = QWidget(MainWindow)
        self.centralwidget.setObjectName(u"centralwidget")
        self.tableWidget = QTableWidget(self.centralwidget)
        if (self.tableWidget.columnCount() < 2):
            self.tableWidget.setColumnCount(2)
        __qtablewidgetitem = QTableWidgetItem()
        self.tableWidget.setHorizontalHeaderItem(0, __qtablewidgetitem)
        __qtablewidgetitem1 = QTableWidgetItem()
        self.tableWidget.setHorizontalHeaderItem(1, __qtablewidgetitem1)
        self.tableWidget.setObjectName(u"tableWidget")
        self.tableWidget.setGeometry(QRect(40, 20, 981, 401))
        self.pushButton = QPushButton(self.centralwidget)
        self.pushButton.setObjectName(u"pushButton")
        self.pushButton.setGeometry(QRect(930, 430, 89, 25))
        MainWindow.setCentralWidget(self.centralwidget)
        self.statusbar = QStatusBar(MainWindow)
        self.statusbar.setObjectName(u"statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)

        QMetaObject.connectSlotsByName(MainWindow)
    # setupUi

    def retranslateUi(self, MainWindow):
        MainWindow.setWindowTitle(QCoreApplication.translate("MainWindow", u"MainWindow", None))
        ___qtablewidgetitem = self.tableWidget.horizontalHeaderItem(0)
        ___qtablewidgetitem.setText(QCoreApplication.translate("MainWindow", u"Name", None));
        ___qtablewidgetitem1 = self.tableWidget.horizontalHeaderItem(1)
        ___qtablewidgetitem1.setText(QCoreApplication.translate("MainWindow", u"Gender", None));
        self.pushButton.setText(QCoreApplication.translate("MainWindow", u"PushButton", None))
    # retranslateUi


My main file is

import os

import dataset
from PySide2 import QtWidgets
from PySide2.QtWidgets import QTableWidgetItem

from layouts.basic import Ui_MainWindow

# delete DB to test
try:
    os.remove("data.db")
except:
    pass


class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow) :
    def __init__(self, parent=None) :
        super(MainWindow, self).__init__()

        self.setupUi(self)
        self.showMaximized()

        self.pushButton.pressed.connect(self.upsert)


    def upsert(self):
        db = dataset.connect('sqlite:///data.db')
        table = db['abc']
        for i in range(10):
            table.insert(dict(name='Jane', age=34, gender='female'))

        self.results()

    def results(self) :
        db = dataset.connect('sqlite:///data.db')
        self.tableWidget.setRowCount(0)
        for value in db["abc"] :
            name = value['name']
            gender = value['gender']
            print(gender)

            rowPosition = self.tableWidget.rowCount()
            self.tableWidget.insertRow(rowPosition)
            # self.tableWidget.setSizeAdjustPolicy(
            #     QtWidgets.QAbstractScrollArea.AdjustToContents)
            # self.tableWidget.resizeColumnsToContents()

            self.tableWidget.setItem(rowPosition, 0, QTableWidgetItem(name))
            self.tableWidget.setItem(rowPosition, 1, QTableWidgetItem(gender))


if __name__ == "__main__" :
    app = QtWidgets.QApplication([])
    main_window = MainWindow()
    main_window.show()
    app.exec_()

As you can see, the main file on pushbutton, creates a db, writes a table called abc and inserts some values until 10 rows. Once this completes it calls the results functions and updates the table.

Now assume that data was written to this db live and it was constantly updating every second with rows.

I would like to

  1. Notify the results function when the first row data becomes available
  2. Update the QtableView as the data becomes available and not after the data loading finishes.

How would I do this ?


Solution

  • So here is how I solved it.

    I implemented a very simple QFileSystemWatcher as

        w = MainWindow()
        paths = ['data.db']
        fs_watcher = QFileSystemWatcher(paths)
        fs_watcher.fileChanged.connect(w.file_changed)
    

    where w is my MainWindow class.

    Now as soon as the file was updated live the QfilesystemWatcher notified the file_changed class so

        def file_changed(self) :
            ---Your Logic Here
    

    That solved it for me.