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...
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
)