2

How can I iterate one column in QTableWidget and check if a checkbox is checked?

enter image description here

When I clicked the button in the left, I want to iterate the column in the picture and check if a checkbox is checked.

As of now, I've tried this function but the function only return None:

def checkBoxcheck(self):
    print("checkBoxcheck started")
    for row in range(self.rowCount()):
        print(self.cellWidget(1, 1))
        if not self.cellWidget(1, 1) is None:
            if self.cellWidget(row, 1).isChecked():
                #others to do

This is the code to use in order to reproduce this problem (I already removed those unnecessary parts of the code):

from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QTableWidget, \
                        QTableWidgetItem, QVBoxLayout, QHBoxLayout
from PyQt5.QtCore import Qt
from PyQt5 import QtCore, QtWidgets
import sys

class TableWidget(QTableWidget):
    def __init__(self):
        super().__init__(10, 3)
        self.verticalHeader().setDefaultSectionSize(10)
        self.horizontalHeader().setDefaultSectionSize(200)

        for rows in range(self.rowCount()):
            item = QtWidgets.QTableWidgetItem()
            item.setCheckState(QtCore.Qt.Unchecked)
            self.setItem(rows, 1, item)

    def checkBoxcheck(self):
        print("checkBoxcheck started")
        for row in range(self.rowCount()):
            print(self.cellWidget(1, 1))
            if not self.cellWidget(1, 1) is None:
                if self.cellWidget(row, 1).isChecked():
                    #others to do
                    pass


class AppDemo(QWidget):
    def __init__(self):
        super().__init__()
        self.resize(700, 500)

        mainLayout = QHBoxLayout()
        table = TableWidget()
        mainLayout.addWidget(table)
        buttonLayout = QVBoxLayout()

        button_new = QPushButton('Check if checked')
        button_new.clicked.connect(table.checkBoxcheck)
        buttonLayout.addWidget(button_new)

        mainLayout.addLayout(buttonLayout)
        self.setLayout(mainLayout)

def app():
    app = QApplication(sys.argv)
    demo = AppDemo()
    demo.show()
    sys.exit(app.exec_())

app()

I hope you can help me with this problem

Tonechas
  • 13,398
  • 16
  • 46
  • 80
Eliazar
  • 301
  • 3
  • 13
  • Just for clarity, besides the explanation in my answer, in your loop you're always checking the item at `(1, 1)`, which is clearly pointless. – musicamante Sep 14 '21 at 13:36
  • Ok, I just noticed that it *was* a typo, that you repeated *again*, and that you already posted another question. So: 1. Please be more careful in using variables of for loops; 2. Follow the suggestions given to you more carefully; 3. Don't create new questions if you just need to add details or code, use the [edit] link and modify the post instead! – musicamante Sep 14 '21 at 13:54

1 Answers1

1

A "cell widget" is a QWidget that is added to an index of the view, as much as it's done using setIndexWidget() for any QAbstractItemView. It can be any widget, a checkbox, a combo, a custom widget, or even another item view.

If the requirement is to have checkable items, there's no need for that, since Qt models already provide the feature, which you're already using when doing item.setCheckState().

So, the solution is much simpler: look for the item(), and then verify the state:

for row in range(self.rowCount()):
    item = self.item(row, 1)
    if item is not None and item.checkState():
        # ...

Remember that:

  • QTableWidget can have empty cells, which are items for which no QTableWidgetItem has been set yet and no data has been inserted by the user (even if their index exist), that's why I had to check if the item is not None;
  • don't confuse item(row, col) with itemAt(x, y), which is based on pixel coordinates on the view;
  • if an item has no checkState explicitly set, its value is also None.
  • don't use booleans to set and verify the states, because Qt check states are tristate: Unchecked (0), PartiallyChecked (1) and Checked (2); by default their mouse behavior is boolean, but you have to consiser this before doing any "basic" action like you'd do with a basic bool (for QCheckBox, though, as for any button, you can use isChecked() and setChecked(), which are normal boolean based functions); this means that if you try to do setCheckState(True), the result will be a partially checked item, since for python True is the same as 1 (so, in Qt terms, PartiallyChecked): if you want to set a fully checked state, you should to do setCheckState(Qt.Checked);
musicamante
  • 41,230
  • 6
  • 33
  • 58
  • Thank you for the guidance, I'm really sorry about asking a duplicate of my other question but I will make sure that it will never happen again. – Eliazar Sep 14 '21 at 22:09
  • Couldn't they do an `is True` check to distinguish between boolean and integer? That's just asking for errors. – Patrick Parker Sep 16 '21 at 04:48
  • @PatrickParker 1. You have to remember that PyQt is a *binding*, a system that allows to use Qt (which is written in C++ and compiled) from Python; 2. The PyQt maintainer puts lots of efforts in keeping the "mid-level-API" as much consistent as possible with the original API; 3. The `Qt.CheckState` is tristate by design, providing an intermediate check would mean to create performance issues; 4. if you don't like the default behavior, just subclass and override `setData` by checking the `Qt.CheckStateRole`. – musicamante Sep 16 '21 at 06:26