pythonpyqtpyqt5qtableviewqsqltablemodel

How to retrieve more than one row with the checked checkbox in QTableView


I am developing an app, where user can select multiple (<=5) rows to preview. I have used QSqlTableModel with QTableView.

In order to retrieve data I am using following line of code:

out = (self.model.record(self.view.selectionModel().selectedIndexes()  [0].row()).value("NAME"))
    print(out)

It is giving me row value. But there are some major issues:

  1. If I select checkbox and unselect. Still, it is considering that as selected.
  2. If I select n checkbox, it is giving only the n th selection. In a nutshell, multiple selection is not working.

I am assuming I have to create a loop. But I am unable to construct the solution.

The complete code is hereby:

import sys

from PyQt5 import QtCore, QtWidgets
from PyQt5.QtWidgets import QVBoxLayout, QPushButton
from PyQt5 import QtGui
from PyQt5.QtCore import Qt, QModelIndex
from PyQt5.QtSql import QSqlDatabase, QSqlTableModel, QSqlQuery
from PyQt5.QtWidgets import (QApplication, QMainWindow, QMessageBox, QTableView,  QPushButton, QGridLayout, QGroupBox, QVBoxLayout)
from PyQt5 import QtCore
from PyQt5.QtCore import pyqtSignal


class RealMainWindow(QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)

        self.setWindowTitle("QTableView Example")
        self.resize(415, 200)
        # Set up the model
        self.button = QPushButton("check", self)
        self.button.clicked.connect(self.selected)
        self.button.move(1000, 1110)

        self.model = TableModel(self)
        self.model.setTable("Table2")
        self.model.setEditStrategy(QSqlTableModel.OnFieldChange)
        #self.model.setHeaderData(0, Qt.Horizontal, "ID")


        self.model.select()
        # Set up the view
        self.view = QTableView()
        self.view.setModel(self.model)
        #self.view.resizeColumnsToContents()

        central_widget = QtWidgets.QWidget()
        self.setCentralWidget(central_widget)
        lay = QVBoxLayout(central_widget)
        lay.addWidget(self.button)
        lay.addWidget(self.view)


     def selected(self):
        out = (self.model.record(self.view.selectionModel().selectedIndexes()[0].row()).value("NAME"))
        print(out)
        


class TableModel(QSqlTableModel):
    def __init__(self, *args, **kwargs):
        QSqlTableModel.__init__(self, *args, **kwargs)
        self.checkeable_data = {}

    def flags(self, index):
        fl = QSqlTableModel.flags(self, index)
        if index.column() == 0:
            fl |= Qt.ItemIsUserCheckable
        return fl

    def data(self, index, role=Qt.DisplayRole):
        if role == Qt.CheckStateRole and (
            self.flags(index) & Qt.ItemIsUserCheckable != Qt.NoItemFlags
        ):
            if index.row() not in self.checkeable_data.keys():
                self.setData(index, Qt.Unchecked, Qt.CheckStateRole)
            return self.checkeable_data[index.row()]
        else:
            return QSqlTableModel.data(self, index, role)

    def setData(self, index, value, role=Qt.EditRole):
        if role == Qt.CheckStateRole and (
            self.flags(index) & Qt.ItemIsUserCheckable != Qt.NoItemFlags
         ):
            self.checkeable_data[index.row()] = value
            self.dataChanged.emit(index, index, (role,))
            return True
        return QSqlTableModel.setData(self, index, value, role)


def createConnection():

    con = QSqlDatabase.addDatabase("QSQLITE")
    con.setDatabaseName("ppcp_database.db")
    if not con.open():
        QMessageBox.critical(
            None,
            "QTableView Example - Error!",
            "Database Error: %s" % con.lastError().databaseText(),
        )
        return False
    return True

app = QApplication(sys.argv)
if not createConnection():
sys.exit(1)
win = RealMainWindow()
win.show()
sys.exit(app.exec_())

Solution

  • The state of the checkbox is being confused with the selection. In your case you must iterate over checkeable_data and verify if it is checked or not:

    def selected(self):
        names = []
        for row, is_checked in self.model.checkeable_data.items():
            if is_checked:
                record = self.model.record(row)
                name = record.value("NAME")
                names.append(name)
        print(names)