Search code examples
pythonpyqtqtableviewqabstracttablemodel

Insert and remove row in pyqts QTableView using the QAbstractTableModel


How can i insert and delete single rows in a QTableView using the QAbstractTableModel.

A new row will be given in a seperate thread which is connected via a pyqtsignal to the QAbstractTableModel. The goal is to add a row every time a new emit is given. If the Table hit a max number of 10 rows, the new emitted row should be added and the first row should be removed. So i have a running table with a maxlen of 10 entries.

At the moment everything works except removing and adding a row

import sys
import time
from PyQt5.QtWidgets import QApplication, QMainWindow, QToolBar, QAction, QTableView
from PyQt5.QtCore import QSize, Qt,QAbstractTableModel, pyqtSlot, pyqtSignal, QThread

import sys
import time
from PyQt5.QtWidgets import QApplication, QMainWindow, QToolBar, QAction, QTableView
from PyQt5.QtCore import QSize, Qt,QAbstractTableModel, pyqtSlot, pyqtSignal, QThread

class Table(QMainWindow):
    def __init__(self):
        QMainWindow.__init__(self)
        self.resize(800, 600)
        self.setMinimumSize(QSize(800, 600))

        self.table = QTableView()
        self.setCentralWidget(self.table)

        toolbar = QToolBar("Toolbar")
        con = QAction("Connect", self)
        con.triggered.connect(self.updateTable)
        discon = QAction("Disconnect", self)
        discon.triggered.connect(self.terminateThread)

        toolbar.addAction(con)
        toolbar.addAction(discon)
        self.addToolBar(toolbar)

    def updateTable(self):
        print("Connect")
        data = [
            ["This", "is", "test", 1],
            ["This", "is", "test", 2],
            ["This", "is", "test", 3],
        ]

        self.model = TableModel(data)
        self.table.setModel(self.model)

        self.thread = QTableThread()
        self.thread.sig1.connect(self.model.addRow)
        self.thread.start()

    def terminateThread(self):
        self.thread1.terminate()

class QTableThread(QThread):
    sig1 = pyqtSignal(list)

    def __init__(self):
        super(QTableThread, self).__init__()
        self.count = 4

    def run(self):
        for i in range(20):
            self.sig1.emit(["This", "is", "test", self.count])
            self.count += 1
            time.sleep(0.5)

class TableModel(QAbstractTableModel):
    def __init__(self, data):
        super(TableModel, self).__init__()
        self._data = data

    def data(self, index, role):
        if role == Qt.DisplayRole:
            return self._data[index.row()][index.column()]

    def rowCount(self, index):
        return len(self._data)

    def columnCount(self, index):
        return len(self._data[0])

    @pyqtSlot(list)
    def addRow(self,status):
        print(status)
        # What to implement here?


if __name__ == "__main__":
    app = QApplication(sys.argv)
    win = Table()
    win.show()
    sys.exit( app.exec_() )

Solution

  • I marked for you the lines I made the changes. Try it:

    import sys
    #import time
    from PyQt5.QtWidgets import QApplication, QMainWindow, QToolBar, QAction, QTableView
    from PyQt5.QtCore import (QSize, Qt, QAbstractTableModel, pyqtSlot, pyqtSignal, 
        QThread, QModelIndex)
    
    
    class QTableThread(QThread):
        sig1 = pyqtSignal(list)
    
        def __init__(self):
            super(QTableThread, self).__init__()
            self.count = 4
    
        def run(self):
            for i in range(20):
                self.sig1.emit(["This", "is", "test", self.count])
                self.count += 1
                self.msleep(500)                                            # !!!
    
    
    class TableModel(QAbstractTableModel):
        def __init__(self, _data):
            super(TableModel, self).__init__()
            self._data = _data
    
        def data(self, index, role):
            if role == Qt.DisplayRole:
                return self._data[index.row()][index.column()]
    
        def rowCount(self, index=QModelIndex()):                     # +
            return  0 if index.isValid() else len(self._data)        # +
    
        def columnCount(self, index=QModelIndex()):                  
            return len(self._data[0])
    
    # +++ vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
        def insertRows(self, new_row, position, rows, parent=QModelIndex()):
            position = (position + self.rowCount()) if position < 0 else position
            start = position
            end = position + rows - 1
    
            if end <= 8:
                self.beginInsertRows(parent, start, end)
                self._data.append(new_row) 
                self.endInsertRows()
                return True
            else:
                self.beginInsertRows(parent, start, end)
                self._data.append(new_row) 
                self.endInsertRows()
                self.removeRows(0, 0)
                return True
    
        def removeRows(self, position, rows, parent=QModelIndex()):
            start, end = position, rows 
            self.beginRemoveRows(parent, start, end)
            del self._data[start:end + 1]
            self.endRemoveRows()
            return True
    # +++ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    
    
    class Table(QMainWindow):
        def __init__(self):
            QMainWindow.__init__(self)
            self.resize(800, 600)
            self.setMinimumSize(QSize(800, 600))
    
            self.table = QTableView()
            self.setCentralWidget(self.table)
    
            toolbar = QToolBar("Toolbar")
            con = QAction("Connect", self)
            con.triggered.connect(self.updateTable)
            discon = QAction("Disconnect", self)
            discon.triggered.connect(self.terminateThread)
    
            toolbar.addAction(con)
            toolbar.addAction(discon)
            self.addToolBar(toolbar)
            
            self.thread = None                                                             # +++
    
        def updateTable(self):
            print("Connect")
            data = [
                ["This", "is", "test", 1],
                ["This", "is", "test", 2],
                ["This", "is", "test", 3],
            ]
            self.model = TableModel(data)
            self.table.setModel(self.model)
    
            self.thread = QTableThread()
    #        self.thread.sig1.connect(self.model.addRow)                                    # ---
            self.thread.sig1.connect(lambda new_row: self.model.insertRows(new_row, -1, 1)) # +++
            self.thread.start()
    
        def terminateThread(self):
            if self.thread and self.thread.isRunning():                                     # +++
                self.thread.terminate()
            
    
    if __name__ == "__main__":
        app = QApplication(sys.argv)
        win = Table()
        win.show()
        sys.exit( app.exec_() )
    

    enter image description here