HoYa
HoYa

Reputation: 342

Get state from QCheckBox inside QTableWidget cell

I want to get a state from QCheckBox inside QTableWidget cell. I've made example code for this issue.

import sys
from PyQt5.QtCore import Qt, QSettings
from PyQt5.QtWidgets import QApplication, QWidget, QHBoxLayout, QVBoxLayout
from PyQt5.QtWidgets import QPushButton, QTableWidget, QCheckBox, QTextEdit


class TestUI(QWidget):
    def __init__(self):
        super().__init__()

        self.btn = QPushButton("Get Data")
        self.tbl = QTableWidget()
        self.log = QTextEdit()

        self.init_ui()

    def init_ui(self):
        self.btn.clicked.connect(self.get_data)

        self.tbl.setFocusPolicy(Qt.NoFocus)
        self.tbl.setMinimumHeight(255)
        self.tbl.setMaximumHeight(255)
        self.tbl.setRowCount(20)
        self.tbl.setColumnCount(3)
        self.tbl.horizontalHeader().setStretchLastSection(True)
        self.tbl.setHorizontalHeaderLabels(["", "Option", "Value"])
        self.tbl.resizeRowsToContents()
        self.tbl.resizeColumnsToContents()
        for row in range(20):
            chk_box = QCheckBox()
            chk_box.setCheckState(Qt.Unchecked)
            cell = QWidget()
            hlayout = QHBoxLayout()
            hlayout.addWidget(chk_box)
            hlayout.setAlignment(Qt.AlignCenter | Qt.AlignVCenter)
            hlayout.setContentsMargins(0, 0, 0, 0)
            cell.setLayout(hlayout)
            self.tbl.setCellWidget(row, 0, cell)

        vlayout = QVBoxLayout()
        vlayout.addWidget(self.btn)
        vlayout.addWidget(self.tbl)
        vlayout.addWidget(self.log)

        self.setLayout(vlayout)
        self.show()

    def get_data(self):
        self.log.clear()
        self.log.append(self.tbl.cellWidget(0, 0).isChecked())
        self.log.append(self.tbl.cellWidget(0, 1).text())
        self.log.append(self.tbl.cellWidget(0, 2).text())


if __name__ == "__main__":
    APP = QApplication(sys.argv)
    ex = TestUI()
    sys.exit(APP.exec_())

How can I do this? I can't get the state through this code.

I think the code should be self.log.append(self.tbl.cellWidget(0, 0).????.isChecked()).

But I do not know exactly how to do it.

Please help me.

Upvotes: 1

Views: 497

Answers (1)

eyllanesc
eyllanesc

Reputation: 243887

If the code that places the widget in column 0 is analyzed:

chk_box = QCheckBox()
chk_box.setCheckState(Qt.Unchecked)
cell = QWidget()
hlayout = QHBoxLayout()
hlayout.addWidget(chk_box)
hlayout.setAlignment(Qt.AlignCenter | Qt.AlignVCenter)
hlayout.setContentsMargins(0, 0, 0, 0)
cell.setLayout(hlayout)
self.tbl.setCellWidget(row, 0, cell)

It is noted that the widget set is not the QCheckBox but a QWidget, and the QCheckBox is the son of the QWidget so that information can be used to obtain it using the findChild() method. On the other hand, the item method may return None, so you should check if it is not, since it could throw an exception:

def get_data(self):
    self.log.clear()
    widget = self.tbl.cellWidget(0, 0)
    if widget is not None:
        chk_box = widget.findChild(QCheckBox)
        if chk_box is not None:
            self.log.append(str(chk_box.isChecked()))
    it1 = self.tbl.item(0, 1)
    self.log.append(it1.text() if it1 is not None else "")
    it2 = self.tbl.item(0, 2)
    self.log.append(it2.text() if it2 is not None else "")

A more elegant version of the above is to make the "cell" a custom widget that exposes the isChecked() method of the QCheckBox:

class Widget(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.chk_box = QCheckBox()
        self.chk_box.setCheckState(Qt.Unchecked)
        hlayout = QHBoxLayout(self)
        hlayout.addWidget(self.chk_box)
        hlayout.setAlignment(Qt.AlignCenter)
        hlayout.setContentsMargins(0, 0, 0, 0)

    def isChecked(self):
        return self.chk_box.isChecked()


class TestUI(QWidget):
    def __init__(self):
        super().__init__()

        self.btn = QPushButton("Get Data")
        self.tbl = QTableWidget()
        self.log = QTextEdit()

        self.init_ui()

    def init_ui(self):
        self.btn.clicked.connect(self.get_data)

        self.tbl.setFocusPolicy(Qt.NoFocus)
        self.tbl.setMinimumHeight(255)
        self.tbl.setMaximumHeight(255)
        self.tbl.setRowCount(20)
        self.tbl.setColumnCount(3)
        self.tbl.horizontalHeader().setStretchLastSection(True)
        self.tbl.setHorizontalHeaderLabels(["", "Option", "Value"])
        self.tbl.resizeRowsToContents()
        self.tbl.resizeColumnsToContents()
        for row in range(20):
            cell = Widget()
            self.tbl.setCellWidget(row, 0, cell)
        vlayout = QVBoxLayout(self)
        vlayout.addWidget(self.btn)
        vlayout.addWidget(self.tbl)
        vlayout.addWidget(self.log)
        self.show()

    def get_data(self):
        self.log.clear()
        widget = self.tbl.cellWidget(0, 0)
        if widget is not None:
            self.log.append(str(widget.isChecked()))
        it1 = self.tbl.item(0, 1)
        self.log.append(it1.text() if it1 is not None else "")
        it2 = self.tbl.item(0, 2)
        self.log.append(it2.text() if it2 is not None else "")

It can be deduced that you use the QWidget to center the QCheckBox inside the cell but I think it can be optimized to avoid creating widgets using a QProxyStyle, and then access the information through the checkState() method of the QTableWidgetItem:

import sys
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (
    QApplication,
    QWidget,
    QHBoxLayout,
    QVBoxLayout,
    QPushButton,
    QTableWidget,
    QTextEdit,
    QTableWidgetItem,
    QProxyStyle,
    QStyle,
)


class ProxyStyle(QProxyStyle):
    def subElementRect(self, element, option, widget):
        r = super().subElementRect(element, option, widget)
        if element == QStyle.SE_ItemViewItemCheckIndicator:
            r.moveCenter(option.rect.center())
        return r


class TestUI(QWidget):
    def __init__(self):
        super().__init__()
        self.btn = QPushButton("Get Data")
        self.tbl = QTableWidget()
        self.log = QTextEdit()
        self.init_ui()

    def init_ui(self):
        self.btn.clicked.connect(self.get_data)

        proxy = ProxyStyle()
        self.tbl.setStyle(proxy)
        self.tbl.setFocusPolicy(Qt.NoFocus)
        self.tbl.setFixedHeight(255)
        self.tbl.setRowCount(20)
        self.tbl.setColumnCount(3)
        self.tbl.horizontalHeader().setStretchLastSection(True)
        self.tbl.setHorizontalHeaderLabels(["", "Option", "Value"])
        self.tbl.resizeRowsToContents()
        self.tbl.resizeColumnsToContents()
        for row in range(20):
            it = QTableWidgetItem()
            it.setCheckState(Qt.Checked)
            self.tbl.setItem(row, 0, it)
        vlayout = QVBoxLayout(self)
        vlayout.addWidget(self.btn)
        vlayout.addWidget(self.tbl)
        vlayout.addWidget(self.log)
        self.show()

    def get_data(self):
        self.log.clear()
        it0 = self.tbl.item(0, 0)
        self.log.append(str(it0.checkState() == Qt.Checked))
        it1 = self.tbl.item(0, 1)
        self.log.append(it1.text() if it1 is not None else "")
        it2 = self.tbl.item(0, 2)
        self.log.append(it2.text() if it2 is not None else "")


if __name__ == "__main__":
    APP = QApplication(sys.argv)
    ex = TestUI()
    sys.exit(APP.exec_())

Upvotes: 4

Related Questions