Несколько QcheckBox в одном QTableView. Выбрать только один QcheckBox

Столкнулся с проблемы. Есть таблица, в которой на одной строчке расположено несколько QcheckBox. Пользователь может изменить значение QcheckBox при помощи клика мыши.

Необходимо сделать так что в приделах одной строчки была, только одна галочка. То есть, при нажатии на Иванов status_2 галочка status_1 для Иванова уходила и вставала на status_2, соответственно должно работать и наоборот.
Также необходимо иметь актуальные данные, что реализовано в методе setData.

Подскажите как это сделать, не обновляя модель. Ниже указана часть кода:

import sys
from PyQt5 import QtWidgets, QtCore


class CheckBoxDelegate(QtWidgets.QItemDelegate):
    def __init__(self, parent):
        super().__init__(parent)

    def createEditor(self, parent, option, index):
        return None

    def paint(self, painter, option, index):
        checked = index.model().data(index, QtCore.Qt.DisplayRole) is True
        check_box_style_option = QtWidgets.QStyleOptionButton()

        if checked:
            check_box_style_option.state |= QtWidgets.QStyle.State_On
        else:
            check_box_style_option.state |= QtWidgets.QStyle.State_Off

        check_box_style_option.rect = self.getCheckBoxRect(option)
        check_box_style_option.state |= QtWidgets.QStyle.State_Enabled
        QtWidgets.QApplication.style().drawControl(QtWidgets.QStyle.CE_CheckBox, check_box_style_option, painter)

    def editorEvent(self, event, model, option, index):

        if event.type() == QtCore.QEvent.MouseButtonRelease:
            if event.button() == QtCore.Qt.LeftButton:
                model.setData(index, not index.data(), QtCore.Qt.DisplayRole)
                return True
        return False

    def getCheckBoxRect(self, option):
        check_box_style_option = QtWidgets.QStyleOptionButton()
        check_box_rect = QtWidgets.QApplication.style().subElementRect(QtWidgets.QStyle.SE_CheckBoxIndicator,
                                                                       check_box_style_option, None)
        check_box_point = QtCore.QPoint(option.rect.x() +
                            option.rect.width() / 2 -
                            check_box_rect.width() / 2,
                            option.rect.y() +
                            option.rect.height() / 2 -
                            check_box_rect.height() / 2)

        return QtCore.QRect(check_box_point, check_box_rect.size())


class TmodelCustom(QtCore.QAbstractTableModel):

    def __init__(self, data=None, header=None):
        QtCore.QAbstractTableModel.__init__(self)
        self.arraydata = data
        self.header = header

    def rowCount(self, parent=QtCore.QModelIndex()):
        return len(self.arraydata)

    def columnCount(self, parent=QtCore.QModelIndex()):
        return len(self.arraydata[0])

    def headerData(self, section: int, orientation: QtCore.Qt.Orientation, role: int = QtCore.Qt.DisplayRole):
        if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole:
            return QtCore.QVariant(self.header[section])
        else:
            return QtCore.QVariant()

    def data(self, index, role=QtCore.Qt.DisplayRole):
        if not index.isValid():
            return QtCore.QVariant()
        column = index.column()
        row = index.row()
        val = self.arraydata[row][column]

        if role == QtCore.Qt.DisplayRole:
            if val is None:
                return ""
            else:
                return val
        return QtCore.QVariant()

    def setData(self, index, value, role=QtCore.Qt.ItemDataRole):
        self.arraydata[index.row()][index.column()] = value
        self.dataChanged.emit(index, index, [QtCore.Qt.EditRole])
        return True

    def getData(self):
        return self.arraydata


class Window(QtWidgets.QMainWindow):

    def __init__(self, parent=None):
        super(Window, self).__init__(parent)
        self.setWindowFlags(QtCore.Qt.Window)
        QtWidgets.QMainWindow.__init__(self)
        central_widget = QtWidgets.QWidget()
        self.setCentralWidget(central_widget)
        grid_layout = QtWidgets.QGridLayout()
        central_widget.setLayout(grid_layout)
        self.table = QtWidgets.QTableView(self)
        horisontal_layout = QtWidgets.QHBoxLayout()
        horisontal_layout.addStretch(1)
        horisontal_layout.setAlignment(QtCore.Qt.AlignRight)
        grid_layout.addLayout(horisontal_layout, 0, 0)
        grid_layout.addWidget(self.table, 0, 0)
        self.model = None
        self.initTable()

    def initTable(self):
        header = ["Name", 'status_1', 'status_2']
        df = [['Иванов', True, False],
              ['Сидорово', True, False],
              ['Петров', False, True]]
        self.model = TmodelCustom(data=df, header=header)
        self.table.setModel(self.model)
        delegate = CheckBoxDelegate(self)
        # self.table.setItemDelegateForColumn(0, delegate)
        self.table.setItemDelegateForColumn(1, delegate)
        self.table.setItemDelegateForColumn(2, delegate)
        self.model.dataChanged.connect(self.test)

    def test(self):
        pass


if __name__ == "__main__":
    application = QtWidgets.QApplication([])
    window = Window()
    window.setWindowTitle("Counter")
    window.setMinimumSize(480, 380)
    window.show()
    sys.exit(application.exec_())

Заранее благодарю Вас!


Ответы (1 шт):

Автор решения: S. Nick

Попробуйте так:

import sys
from PyQt5 import QtWidgets, QtCore


class CheckBoxDelegate(QtWidgets.QItemDelegate):
    def __init__(self, parent):
        super().__init__(parent)

    def createEditor(self, parent, option, index):
        return None

    def paint(self, painter, option, index):
        checked = index.model().data(index, QtCore.Qt.DisplayRole) is True
        check_box_style_option = QtWidgets.QStyleOptionButton()

        if checked:
            check_box_style_option.state |= QtWidgets.QStyle.State_On
        else:
            check_box_style_option.state |= QtWidgets.QStyle.State_Off

        check_box_style_option.rect = self.getCheckBoxRect(option)
        check_box_style_option.state |= QtWidgets.QStyle.State_Enabled
        QtWidgets.QApplication.style().drawControl(QtWidgets.QStyle.CE_CheckBox, check_box_style_option, painter)

    def editorEvent(self, event, model, option, index):

        if event.type() == QtCore.QEvent.MouseButtonRelease:
            if event.button() == QtCore.Qt.LeftButton:
# !!!           
# +++ vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv              
                fl = not index.data()
            
#                model.setData(index, not index.data(), QtCore.Qt.DisplayRole)
                model.setData(index, fl, QtCore.Qt.DisplayRole)
                
                if fl:
                    row = index.row()
                    column = index.column()
                    column = 1 if column == 2 else 2
                    _index = model.index(row, column)
                    model.setData(_index, False, QtCore.Qt.DisplayRole)
# +++ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                
                return True
        return False

    def getCheckBoxRect(self, option):
        check_box_style_option = QtWidgets.QStyleOptionButton()
        check_box_rect = QtWidgets.QApplication.style().subElementRect(QtWidgets.QStyle.SE_CheckBoxIndicator,
                                                                       check_box_style_option, None)
        check_box_point = QtCore.QPoint(option.rect.x() +
                            option.rect.width() / 2 -
                            check_box_rect.width() / 2,
                            option.rect.y() +
                            option.rect.height() / 2 -
                            check_box_rect.height() / 2)

        return QtCore.QRect(check_box_point, check_box_rect.size())


class TmodelCustom(QtCore.QAbstractTableModel):

    def __init__(self, data=None, header=None):
        QtCore.QAbstractTableModel.__init__(self)
        self.arraydata = data
        self.header = header

    def rowCount(self, parent=QtCore.QModelIndex()):
        return len(self.arraydata)

    def columnCount(self, parent=QtCore.QModelIndex()):
        return len(self.arraydata[0])

    def headerData(self, section: int, orientation: QtCore.Qt.Orientation, role: int = QtCore.Qt.DisplayRole):
        if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole:
            return QtCore.QVariant(self.header[section])
        else:
            return QtCore.QVariant()

    def data(self, index, role=QtCore.Qt.DisplayRole):
        if not index.isValid():
            return QtCore.QVariant()
        column = index.column()
        row = index.row()
        val = self.arraydata[row][column]

        if role == QtCore.Qt.DisplayRole:
            if val is None:
                return ""
            else:
                return val
        return QtCore.QVariant()

    def setData(self, index, value, role=QtCore.Qt.ItemDataRole):
        self.arraydata[index.row()][index.column()] = value
        self.dataChanged.emit(index, index, [QtCore.Qt.EditRole])
        return True

    def getData(self):
        return self.arraydata


class Window(QtWidgets.QMainWindow):

    def __init__(self, parent=None):
        super(Window, self).__init__(parent)
        self.setWindowFlags(QtCore.Qt.Window)
        QtWidgets.QMainWindow.__init__(self)
        central_widget = QtWidgets.QWidget()
        self.setCentralWidget(central_widget)
        grid_layout = QtWidgets.QGridLayout()
        central_widget.setLayout(grid_layout)
        self.table = QtWidgets.QTableView(self)
        horisontal_layout = QtWidgets.QHBoxLayout()
        horisontal_layout.addStretch(1)
        horisontal_layout.setAlignment(QtCore.Qt.AlignRight)
        grid_layout.addLayout(horisontal_layout, 0, 0)
        grid_layout.addWidget(self.table, 0, 0)
        self.model = None
        self.initTable()

    def initTable(self):
        header = ["Name", 'status_1', 'status_2']
        df = [['Иванов', True, False],
              ['Сидорово', True, False],
              ['Петров', False, True]]
        self.model = TmodelCustom(data=df, header=header)
        self.table.setModel(self.model)
        
        delegate = CheckBoxDelegate(self)
        # self.table.setItemDelegateForColumn(0, delegate)
        self.table.setItemDelegateForColumn(1, delegate)
        self.table.setItemDelegateForColumn(2, delegate)
        self.model.dataChanged.connect(self.test)

    def test(self):
        pass


if __name__ == "__main__":
    application = QtWidgets.QApplication([])
    window = Window()
    window.setWindowTitle("Counter")
    window.setMinimumSize(480, 380)
    window.show()
    sys.exit(application.exec_())

введите сюда описание изображения

→ Ссылка