How to implement setData on PyQt5 QAbstractTableModel with QSqlRelationalTableModel

Question:

I am newbie with Pyqt and need help with setting setData on QAbstractTableModel with QSqlRelationalTableModel table model

I have this simple code and I dont now how to implement setData function.
I can’t set value to field.
I’m getting TypeError: ‘QSqlRelationalTableModel’ object is not subscriptable error.

Also, how to perform calculatation on column (eg. Total column)?

    from pyexpat import model
    import sys
    from PyQt5 import QtCore, QtGui, QtWidgets
    from PyQt5.QtCore import Qt
    from PyQt5.QtWidgets import (QApplication, QGridLayout, QHeaderView, QMessageBox,
        QTableView, QMainWindow, QWidget)
    from PyQt5.QtSql import QSqlDatabase, QSqlRelationalTableModel, QSqlTableModel
    from PyQt5.QtSql import QSqlRelation, QSqlRelationalDelegate
    from PyQt5 import QtCore
    
    from PyQt5.QtCore import QAbstractTableModel, QModelIndex, QRect, Qt
    from datetime import date
    from time import strftime
    
    from numpy import record
    
    class MyTableModel(QtCore.QAbstractTableModel):
        def __init__(self, model):
            super().__init__()
            self._model = model
    
        # Create the data method
        def data(self, index, role):
            value = self._model.record(index.row()).value(index.column())
    
            if role == Qt.ItemDataRole.DisplayRole:
                if isinstance(value, int) and index.column() == 0:
                    return f'# {value}'
                if isinstance(value, int) and index.column() == 1:
                    # Format the currency value
                    return "${: ,.2f}".format(value)
     
                if role == Qt.EditRole:
                    return self._model[index.row()][index.column()]
                
                return value
     
     
            if role == Qt.ItemDataRole.DecorationRole:
                    if isinstance(value, int) and index.column() == 0:
                        return QtGui.QIcon('data/icons/hashtag_icon.png')
                    if isinstance(value, str) and index.column() == 9:
                        return QtGui.QIcon('data/icons/calendar.png')
    
        # Create the headerData method
        def headerData(self, section: int, orientation: Qt.Orientation, role: int):
            if role == Qt.ItemDataRole.DisplayRole and orientation == Qt.Orientation.Horizontal:
                return self._model.headerData(section, orientation, role=role)
        
        # Create the rowCount method
        def rowCount(self, parent: QModelIndex) -> int:
            return self._model.rowCount()
            
        # Create the columnCount method
        def columnCount(self, parent: QModelIndex) -> int:
            return self._model.columnCount()
        
        def setData(self, index, value, role):
            if not index.isValid():
                return False
            if role == Qt.EditRole: 
                self._model[index.row()][index.column()]=value
                self.dataChanged.emit(index, index,)
                return True
            return False
           
        def flags(self, index):
            return Qt.ItemFlag.ItemIsSelectable | Qt.ItemFlag.ItemIsEnabled | Qt.ItemFlag.ItemIsEditable
    
    
    # Inherit from QMainWindow
    class MainWindow(QMainWindow):
        def __init__(self, parent=None):
            super().__init__(parent)
            # Set the window title
         
            self.setWindowTitle('QTable Example')
            self.window_width, self.window_height = 1000, 700
            self.setMinimumSize(self.window_width, self.window_height)
            # Create the model
            model = QSqlRelationalTableModel(self)
            
            # Set the table to display
    
            model.setTable('obracundetails')
            model.setEditStrategy(QSqlRelationalTableModel.EditStrategy.OnFieldChange)
            # Set relations for related columns to be displayed
            #model.setRelation(1, QSqlRelation('products', 'ProductID', 'Price'))  
            model.setRelation(8, QSqlRelation('asortiman', 'asortiman_id', 'naziv'))
            model.setRelation(9, QSqlRelation('obracunmain', 'obracunmain_id', 'datum'))
            #model.setHeaderData(0, Qt.Horizontal, "ID")
            model.select()
            # Setup the view
    
            # Create the view = a table widget
            presentation_model = MyTableModel(model)
            view = QTableView(self)
    
            # Set the data model for table widget
            
            #view.setModel(model)
            view.setModel(presentation_model)
    
            # Adjust column widths to their content
            view.resizeColumnsToContents()
            # Add the widget to main window
            self.setCentralWidget(view)
    
     
    # Type hint for return value
    def createConnection() -> bool:
        # SQLite type database connection instance
        con = QSqlDatabase.addDatabase('QSQLITE')
        # Connect to the database file
        con.setDatabaseName('havana.sqlite3')
        # Show message box when there is a connection issue
        if not con.open():
            QMessageBox.critical(
                None,
                'QTableView Example - Error!',
                'Database Error: %s' % con.lastError().databaseText(),
            )
            return False
        return True
    
    
    if __name__ == "__main__":
        app = QApplication(sys.argv)
        if not createConnection():
            sys.exit(1)
        form = MainWindow()
        form.show()
        app.exec()

Thanks in advance

Asked By: Saša Šoltić

||

Answers:

The "object is not subscriptable" error means that you’re trying to access an item or index for an object that doesn’t provide such interface.

>>> obj = 100
>>> print(obj[2])

TypeError: 'int' object is not subscriptable

self._model[index.row()][index.column()] cannot work because Qt models are not lists or arrays, you cannot get their contents using square brackets.

In the same way, you cannot write values as you would do with a list with the same notation. You must use setData() in the same way you’re seeing in your implementation: by providing an index for that model and a value.

        def setData(self, index, value, role):
            if not index.isValid():
                return False
            if role == Qt.EditRole:
                targetIndex = self._model.index(index.row(), index.column())
                if self._model.setData(targetIndex, value, role):
                    self.dataChanged.emit(index, index)
                    return True
            return False
Answered By: musicamante

I find solution but I’m not sure is completely right, but it’s working fine.

self._model.setData(index, value, role)

For calculated column I have this working code for column3

row = index.row()

# getting value from column4 
value_col4 = self._model.index(row, 4).data() 

#getting index of column7
index_total=self._model.index(row, 7)

# perform some calculation
total_col7 = value_col4 * price

#saving to column7
self._model.setData(index_total, float(total_col7))
Answered By: Saša Šoltić