0

I am trying to implement a custom QTableView. For that I am subclassing QAbstractTableModel such that it takes a list of strings. Here the implementation.


class ListTableModel(qtc.QAbstractTableModel):
    def __init__(self, data: List[List[str]], headers: List[str] = None, edit: List[Union[int, Tuple[int, int]]] = None,
                 checks: List[Union[int, Tuple[int, int]]] = None, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._data = data
        self._headers = headers
        self._edit = edit
        self._checks = checks
        self._check_state = {}

        self.name = None

    def get_name(self):
        return self.name

    def set_name(self, name: str):
        self.name = name

    def check_state(self, value, index):
        if index in self._check_state:
            return self._check_state[index]
        else:
            if value == 0:
                return qtc.Qt.Unchecked
            else:
                return qtc.Qt.Checked

    def data(self, index, role):
        if index.isValid() or (0 <= index.row() < self.rowCount() and 0 <= index.column() < self.columnCount()):
            val = self._data[index.row()][index.column()]
            val = fast_real(val, coerce=False)
            if (
                    role == qtc.Qt.DisplayRole or role == qtc.Qt.EditRole) and not index.flags() & qtc.Qt.ItemIsUserCheckable:
                if isinstance(val, float):
                    return f"{val:.4f}"
                return str(val)

            if role == qtc.Qt.CheckStateRole and index.flags() & qtc.Qt.ItemIsUserCheckable:
                return self.check_state(val, index)

            return None
        return None

    def setData(self, index, value, role):
        if role == qtc.Qt.EditRole:
            val = fast_real(value, coerce=False)
            self._data[index.row()][index.column()] = val
            return True

        if role == qtc.Qt.CheckStateRole:
            self._check_state[index] = value
            return True

        return False

    def flags(self, index):
        if self._edit is not None and index.column() in self._edit:
            return qtc.Qt.ItemIsSelectable | qtc.Qt.ItemIsEnabled

        if self._edit is not None and (index.row(), index.column()) in self._edit:
            return qtc.Qt.ItemIsSelectable | qtc.Qt.ItemIsEnabled

        if self._checks is not None and index.column() in self._checks:
            return qtc.Qt.ItemIsSelectable | qtc.Qt.ItemIsEnabled | qtc.Qt.ItemIsUserCheckable

        if self._checks is not None and (index.row(), index.column()) in self._checks:
            return qtc.Qt.ItemIsSelectable | qtc.Qt.ItemIsEnabled | qtc.Qt.ItemIsUserCheckable

        return qtc.Qt.ItemIsSelectable | qtc.Qt.ItemIsEnabled | qtc.Qt.ItemIsEditable

    def rowCount(self, parent=None):
        return len(self._data)

    def columnCount(self, parent=None):
        return len(self._data[0])

    def headerData(self, section, orientation, role):
        if role == qtc.Qt.DisplayRole:
            if orientation == qtc.Qt.Horizontal and self._headers is not None:
                return str(self._headers[section])

The class takes a list of list containing strings (representing the table). A list of strings representing the headers of the columns. The optional arguments edit and checks define which columns (if given as int) or cells (if given as tuple) are editable and checkable. The underlying data structure data takes values of 0 if the corresponding checkbox is unchecked, or any other value if the corresponding checkbox is checked.

My first question is that the cells that are checkable actually show some extra space, apart from drawing the checkbox. A separating line in the middle of the cell is actually visible when selecting the cell. How can I eliminate this? In the data method I control for cells that are checkable, and skip the display of the underlying data if it is. So I do not know how else to proceed here.

EXTRA: I would like to capture the signal that the model emits when any of the checkboxes are checked/unchecked. Any hint on this would be much appreciated as well.

ניר
  • 1,204
  • 1
  • 8
  • 28
Oier Arcelus
  • 107
  • 1
  • 8
  • 1
    The extra space is because by default items are expected to have some text to show, and that's more visible when the items are checkable because there is always some margin *in case* text eventually is set; if you're sure that those cells won't ever have any text, you need a custom delegate. The signal you're referring is [`dataChanged`](https://doc.qt.io/qt-5/qabstractitemmodel.html#dataChanged) and, as explained in the documentation, you have to emit it in the `setData()` implementation. – musicamante Feb 16 '23 at 17:36

0 Answers0