Обновить QTableView при изменении данных в базе postgres, PyQt5

Есть база, в которой хранятся данные для таблицы триггер и функция на добавление, удаление и редактирование записей:

CREATE TRIGGER trigger1 AFTER INSERT OR DELETE OR UPDATE ON public.main FOR EACH ROW EXECUTE PROCEDURE notify_id_trigger()

CREATE OR REPLACE FUNCTION public.notify_id_trigger()
 RETURNS TRIGGER
 LANGUAGE plpgsql
AS $function$
BEGIN 
    perform pg_notify('new_id'::text, NEW."id"::text); 
    RETURN NEW; 
END; 
$function$

При изменении данных в таблице должна обновляться QTableView

main.py:

from PyQt5 import QtCore, QtGui, QtWidgets


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(800, 600)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.horizontalLayout = QtWidgets.QHBoxLayout(self.centralwidget)
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.tableView = QtWidgets.QTableView(self.centralwidget)
        self.tableView.setObjectName("tableView")
        self.horizontalLayout.addWidget(self.tableView)
        MainWindow.setCentralWidget(self.centralwidget)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))

project.py:

from PyQt5.QtCore import Qt, QThread
from PyQt5.QtWidgets import QMainWindow, QApplication
from PyQt5.QtSql import QSqlDatabase, QSqlQueryModel
import main
import psycopg2
from psycopg2.extensions import ISOLATION_LEVEL_AUTOCOMMIT
import select
 
class Mywindow(QMainWindow, main.Ui_MainWindow):
    def __init__(self, parent=None):
        super(Mywindow, self).__init__(parent)
        self.setupUi(self)
        self.openDB()
        self.uploadData()
        self.thread = QThread()
        self.thread.started.connect(self.dblistener())
        self.thread.start()
 
    def openDB(self):
        db = QSqlDatabase.addDatabase('QPSQL')
        db.setHostName('127.0.0.1')
        db.setDatabaseName('delo')
        db.setUserName('postgres')
        db.setPassword('postgres')
        db.setPort(5432)
        db.open()
 
    def uploadData(self):
        tabmodel = QSqlQueryModel()
        tabmodel.setQuery(f"SELECT * FROM main")
        tabmodel.setHeaderData(0, Qt.Horizontal, "ID")
        tabmodel.setHeaderData(1, Qt.Horizontal, "ФАМИЛИЯ")
        tabmodel.setHeaderData(2, Qt.Horizontal, "ИМЯ")
        tabmodel.setHeaderData(3, Qt.Horizontal, "ОТЧЕСТВО")
        self.tableView.setModel(tabmodel)
 
    def dblistener(self):
        connection = psycopg2.connect(
                host="127.0.0.1",
                database="delo",
                user="postgres",
                password="postgres")
 
        connection.set_isolation_level(ISOLATION_LEVEL_AUTOCOMMIT)
        cur = connection.cursor()
        cur.execute("LISTEN new_id;")
        while True:
            select.select([connection], [], [])
            connection.poll()
            while connection.notifies:
                notify = connection.notifies.pop()
                self.uploadData()
 
if __name__ == '__main__':
    app = QApplication([])
    w = Mywindow()
    w.show()
    app.exec_()

dblistener слушает базу и получает уведомления о изменениях, но почему-то ничего не работает.
Не могу понять где ошибка, приложение вообще не запускается.


Ответы (1 шт):

Автор решения: S. Nick

1. Вы не правильно организовали работу с дополнительным потоком.
Вы можете просмотреть множество примеров приложений, которые используют класс QThread для управления потоками.

Рекомендую проработать пост 3 разных и простых способа работы с потоками.

2. Я не могу проверить работу вашего приложения с использованием БД PostgreSQL.
Я не знаю как вы добавляете, удаляете и редактируете записи в БД и предполагаю что QTableView только визуализирует актуальные данные, содержащиеся в БД.

И если это так, то предложу вам попробовать просто по таймеру с каким-то интервалом (например, каждые 5 секунд) обновлять QTableView.
Если такой простой вариант вам не подходит, вернитесь к пункту 1.

3. Подставьте свою базу и попробуйте.

Обратите внимание, что я выполняю добавление, удаление и редактирование записи прямо в БД.

import sys
from PyQt5 import QtWidgets, QtGui, QtCore
from PyQt5.Qt import *
from PyQt5.QtSql import QSqlDatabase, QSqlQuery, QSqlTableModel

import sqlite3                                                       # установите свое

#import psycopg2
#from psycopg2.extensions import ISOLATION_LEVEL_AUTOCOMMIT
#import select


#import main
class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(800, 600)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        
        self.horizontalLayout = QtWidgets.QVBoxLayout(self.centralwidget)
        self.horizontalLayout.setObjectName("horizontalLayout")
        
        self.tableView = QtWidgets.QTableView(self.centralwidget)
        self.tableView.setObjectName("tableView")
        self.horizontalLayout.addWidget(self.tableView)
        MainWindow.setCentralWidget(self.centralwidget)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        
        
class Mywindow(QMainWindow, Ui_MainWindow):
    def __init__(self, parent=None):
        super(Mywindow, self).__init__(parent)
        self.setupUi(self)
        
        self.pushButton = QPushButton('updateDB')
        self.pushButton.clicked.connect(self.updateDB)
        self.horizontalLayout.addWidget(self.pushButton)
        
        self.openDB()
        self.uploadData()
        
        self._timer = QTimer(self, timeout=self.updateDB)                      # +++
        self._timer.start(5000)                                                # +++ 5 секунд

    def updateDB(self):                                                        # +++ 
        self.model.select()                                                    # !!!
       
    def openDB(self):
        '''
        db = QSqlDatabase.addDatabase('QPSQL')
        db.setHostName('127.0.0.1')
        db.setDatabaseName('delo')
        db.setUserName('postgres')
        db.setPassword('postgres')
        db.setPort(5432)
        db.open()
        '''
        db = QSqlDatabase.addDatabase('QSQLITE')                               # установите свое
        db.setDatabaseName('book.db')                                          # установите свое
        db.open()
 
    def uploadData(self):
        '''
        tabmodel = QSqlQueryModel()
        tabmodel.setQuery(f"SELECT * FROM main")
        tabmodel.setHeaderData(0, Qt.Horizontal, "ID")
        tabmodel.setHeaderData(1, Qt.Horizontal, "ФАМИЛИЯ")
        tabmodel.setHeaderData(2, Qt.Horizontal, "ИМЯ")
        tabmodel.setHeaderData(3, Qt.Horizontal, "ОТЧЕСТВО")
        self.tableView.setModel(tabmodel)
        '''
        self.model = QSqlTableModel(self)                                      # !!!
        self.model.setTable("card")                                            # установите свое
        self.model.setHeaderData(0, Qt.Horizontal, "ID")
        self.model.setHeaderData(1, Qt.Horizontal, "ИМЯ")
        self.model.select()
        self.tableView.setModel(self.model)        

 
if __name__ == '__main__':
    app = QApplication([])
    w = Mywindow()
    w.resize(500, 300)
    w.show()
    app.exec_()

введите сюда описание изображения

введите сюда описание изображения

→ Ссылка