pythonqsortfilterproxymodelpyqt6

PyQt6 - How to check a checkbox within table view when selecting an item


I have a single column TableView with a checkbox. I want to check the checkbox when an item is selected and the same opposite when a check box is checked, item should be in selection as well. I have this working with one issue, since I have set the table in QSortFilterProxyModel, when an item is filtered and selected the the row number is changing as per the filter as well. So I can't get the actual item in my QStandardItemModel. Like, if I filtered and checked the 3rd item, the selected item will be the one in initial state.

My Code is as follows:

class WindowHomeScreen(QtWidgets.QMainWindow):
    """Main Screen of the application"""

    def __init__(self):
        super().__init__()
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)
        self.menu_items = {}
        self.is_all_selected = False  # Table items
        self.filter_proxy_model = QtCore.QSortFilterProxyModel()

        articles = sql_db.query_fetch_articles_list()
        self.fixed_rates = sql_db.query_fetch_fixed_rates()
        self.model = QtGui.QStandardItemModel(len(articles), 1)
        self.model.setHorizontalHeaderLabels(["Articles"])
        self.articles_dict: dict[str, tuple[Article, PriceStructure, OSCharges]] = {}
        for row, article in enumerate(articles):
            if article[0].mrp != 0:
                key = f"{article[0].article}  - ₹{article[0].mrp}"
            else:
                key = f"{article[0].article}  - ###"
            item = QtGui.QStandardItem(key)
            self.articles_dict[key] = article
            item.setFlags(
                Qt.ItemFlag.ItemIsUserCheckable
                | Qt.ItemFlag.ItemIsEnabled
                | Qt.ItemFlag.ItemIsSelectable
            )
            item.setCheckState(Qt.CheckState.Unchecked)
            self.model.setItem(row, 0, item)
        self.filter_proxy_model.setSourceModel(self.model)
        self.filter_proxy_model.setFilterCaseSensitivity(
            Qt.CaseSensitivity.CaseInsensitive
        )
        self.filter_proxy_model.setFilterKeyColumn(0)

        # Connections with filter
        self.ui.lineEdit.textChanged.connect(
            self.filter_proxy_model.setFilterRegularExpression
        )
        self.ui.tableView.horizontalHeader().setSectionResizeMode(
            QtWidgets.QHeaderView.ResizeMode.Stretch
        )
        self.ui.tableView.setEditTriggers(
            QtWidgets.QTableView.EditTrigger.NoEditTriggers
        )
        self.ui.tableView.setModel(self.filter_proxy_model)
        self.ui.tableView.doubleClicked.connect(self.tableDoubleClicked)
        self.ui.tableView.clicked.connect(self.tableSingleClicked)
        self.ui.tableView.selectionModel().selectionChanged.connect(
            self.tableSelectionChanged
        )
        self.ui.tableView.findChild(QtWidgets.QAbstractButton).clicked.connect(
            self.tableSelectAll
        )

        # Connections to buttons
        self.ui.button_show_stats.clicked.connect(self.buttonShowStats)
        self.ui.button_export_xl.clicked.connect(self.buttonExport)
        self.ui.button_export_summary.clicked.connect(self.buttonExportSummaryReport)
        # TODO: Create new method to export only currently displaying item
        self.ui.button_export_xl_sub.clicked.connect(self.buttonExport)

        # Connections to menu items
        self.ui.actionClose.triggered.connect(self.menu_close_app)
        self.ui.actionUpgradeBom.triggered.connect(self.menu_create_bom)
        self.ui.actionUpgradeOS_Charge.triggered.connect(self.menu_create_osc)
        self.ui.actionUpgradePrice_Structure.triggered.connect(self.menu_create_ps)
        self.ui.actionUpdateOS_Charges.triggered.connect(self.menu_manage_osc)
        self.ui.actionUpdatePrice_Structure.triggered.connect(self.menu_manage_ps)
        self.ui.actionUpdateOther_Expenses.triggered.connect(self.menu_manage_expenses)
        self.ui.actionUpdateFixed_Charges.triggered.connect(
            self.menu_manage_fixed_charges
        )

    def tableSelectAll(self):
        """Select or Unselect all check boxes in the table

        Table View corner button function.
        """
        if self.is_all_selected:
            self.ui.tableView.clearSelection()
        self.is_all_selected = not self.is_all_selected

    def tableSelectionChanged(
        self, selected: QtCore.QItemSelection, deselected: QtCore.QItemSelection
    ):
        """Catch Selection changed behaviour"""

        for item in selected.indexes():
            self.model.item(item.row(), 0).setCheckState(Qt.CheckState.Checked)

        for item in deselected.indexes():
            self.model.item(item.row(), 0).setCheckState(Qt.CheckState.Unchecked)

    def tableSingleClicked(self, modelIndex: QtCore.QModelIndex):
        """Single clicked item in the tableview

        Select or Unselect item.
        """
        if (
            self.model.item(modelIndex.row(), 0).checkState() == Qt.CheckState.Checked
            and modelIndex not in self.ui.tableView.selectedIndexes()
        ) or (
            self.model.item(modelIndex.row(), 0).checkState() == Qt.CheckState.Unchecked
            and modelIndex in self.ui.tableView.selectedIndexes()
        ):

            self.ui.tableView.selectionModel().select(
                modelIndex, QtCore.QItemSelectionModel.SelectionFlag.Toggle
            )

# rest of the code...

Solution

  • Thanks to: @musicamante ;

    I was able to solve the issue by modifying my selection changed method and singleitem clicked method as follows;

         def tableSelectionChanged(
             self, selected: QtCore.QItemSelection, deselected: QtCore.QItemSelection
         ):
             """Catch Selection changed behaviour"""
     
             for index in selected.indexes():
                 self.filter_proxy_model.setData(index, Qt.CheckState.Checked, Qt.ItemDataRole.CheckStateRole) 
             for index in deselected.indexes():
                 self.filter_proxy_model.setData(index, Qt.CheckState.Unchecked, Qt.ItemDataRole.CheckStateRole) 
     
         def tableSingleClicked(self, modelIndex: QtCore.QModelIndex):
             """Single item clicked/checked in the tableview
     
             Select or Unselect item when checkbox is checked or unchecked.
             
             """
             # CheckState key is 10 in itemData - Not read doc for info
             # check_state will be Qt.CheckState "enum value" when checkbox item is checked.
             # check_state will be Qt.CheckState "enum" when item selected.
             # only checkbox item selection is needed, so integer value will consider in here and other ignored which isn't needed.
             check_state = self.filter_proxy_model.itemData(modelIndex).get(10)
             if (
                 check_state == 2
                 and modelIndex not in self.ui.tableView.selectedIndexes()
             ) or (
                 check_state == 0
                 and modelIndex in self.ui.tableView.selectedIndexes()
             ):
                 self.ui.tableView.selectionModel().select(
                     modelIndex, QtCore.QItemSelectionModel.SelectionFlag.Toggle
                 )