<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Python GUIs - qtablemodel</title><link href="https://www.pythonguis.com/" rel="alternate"/><link href="https://www.pythonguis.com/feeds/qtablemodel.tag.atom.xml" rel="self"/><id>https://www.pythonguis.com/</id><updated>2021-03-29T09:00:00+00:00</updated><subtitle>Create GUI applications with Python and Qt</subtitle><entry><title>How to debug: QAbstractTableModel subclass does not call flags() — Understanding why flags() isn't called when using QDataWidgetMapper with standard widgets</title><link href="https://www.pythonguis.com/faq/how-to-debug-qabstracttablemodel-subclass-does-not-call-flags/" rel="alternate"/><published>2021-03-29T09:00:00+00:00</published><updated>2021-03-29T09:00:00+00:00</updated><author><name>Martin Fitzpatrick</name></author><id>tag:www.pythonguis.com,2021-03-29:/faq/how-to-debug-qabstracttablemodel-subclass-does-not-call-flags/</id><summary type="html">If you've subclassed &lt;code&gt;QAbstractTableModel&lt;/code&gt; and noticed that your &lt;code&gt;data()&lt;/code&gt; and &lt;code&gt;setData()&lt;/code&gt; methods are being called correctly, but &lt;code&gt;flags()&lt;/code&gt; never seems to fire, you're not alone. This is a common source of confusion, especially when you're using &lt;code&gt;QDataWidgetMapper&lt;/code&gt; to connect your model to standard form widgets like &lt;code&gt;QLineEdit&lt;/code&gt;.</summary><content type="html">
            &lt;p&gt;If you've subclassed &lt;code&gt;QAbstractTableModel&lt;/code&gt; and noticed that your &lt;code&gt;data()&lt;/code&gt; and &lt;code&gt;setData()&lt;/code&gt; methods are being called correctly, but &lt;code&gt;flags()&lt;/code&gt; never seems to fire, you're not alone. This is a common source of confusion, especially when you're using &lt;code&gt;QDataWidgetMapper&lt;/code&gt; to connect your model to standard form widgets like &lt;code&gt;QLineEdit&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Let's walk through why this happens and what you can do about it.&lt;/p&gt;
&lt;h2 id="how-flags-works-in-qts-modelview-architecture"&gt;How flags() works in Qt's Model/View architecture&lt;/h2&gt;
&lt;p&gt;In Qt's &lt;a href="https://www.pythonguis.com/tutorials/modelview-architecture/"&gt;model/view framework&lt;/a&gt;, the &lt;code&gt;flags()&lt;/code&gt; method on a model tells the &lt;em&gt;view&lt;/em&gt; what a user is allowed to do with each item &amp;mdash; whether it's selectable, editable, checkable, drag-enabled, and so on. Here's a typical implementation:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from PyQt5.QtCore import Qt

def flags(self, index):
    return Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemIsEditable
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;When you use a model/view widget like &lt;code&gt;QTableView&lt;/code&gt;, the view queries &lt;code&gt;flags()&lt;/code&gt; for every visible cell. It uses the returned flags to decide things like: should the cell be grayed out? Should double-clicking open an editor? Should a checkbox appear? That's why, with a &lt;code&gt;QTableView&lt;/code&gt;, you'll see &lt;code&gt;flags()&lt;/code&gt; called constantly &amp;mdash; even as you move the mouse around.&lt;/p&gt;
&lt;h2 id="why-flags-isnt-called-with-qdatawidgetmapper"&gt;Why flags() isn't called with QDataWidgetMapper&lt;/h2&gt;
&lt;p&gt;When you use &lt;code&gt;QDataWidgetMapper&lt;/code&gt; to map model columns to standard widgets like &lt;code&gt;QLineEdit&lt;/code&gt;, &lt;code&gt;QComboBox&lt;/code&gt;, or &lt;code&gt;QSpinBox&lt;/code&gt;, the situation is different. These standard widgets don't know anything about the model/view flags system. A &lt;code&gt;QLineEdit&lt;/code&gt; has its own &lt;code&gt;setReadOnly()&lt;/code&gt; method and its own properties &amp;mdash; it doesn't ask a model whether it should be editable.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;QDataWidgetMapper&lt;/code&gt; acts as a bridge between your model and these form widgets. It reads data from the model (calling &lt;code&gt;data()&lt;/code&gt;) and writes data back (calling &lt;code&gt;setData()&lt;/code&gt;), but it doesn't query &lt;code&gt;flags()&lt;/code&gt; and apply them to the widgets. The mapper simply doesn't have a built-in mechanism to translate model flags into widget properties.&lt;/p&gt;
&lt;p&gt;So if your model's &lt;code&gt;flags()&lt;/code&gt; method is never called, and you're using &lt;code&gt;QDataWidgetMapper&lt;/code&gt; with standard widgets, that's expected behavior. There's nothing wrong with your model or your initialization.&lt;/p&gt;
&lt;h2 id="applying-flags-like-behavior-to-standard-widgets"&gt;Applying flags-like behavior to standard widgets&lt;/h2&gt;
&lt;p&gt;If you were hoping to use &lt;code&gt;flags()&lt;/code&gt; to control widget behavior &amp;mdash; for example, making a &lt;code&gt;QLineEdit&lt;/code&gt; read-only based on some model state &amp;mdash; you'll need to handle that manually. Here's a practical approach:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from PyQt5.QtWidgets import (
    QApplication, QWidget, QVBoxLayout, QLineEdit,
    QPushButton, QLabel, QFormLayout
)
from PyQt5.QtCore import (
    QAbstractTableModel, Qt, QModelIndex
)
from PyQt5.QtWidgets import QDataWidgetMapper
import sys


class MyTableModel(QAbstractTableModel):
    def __init__(self, data):
        super().__init__()
        self._data = data
        # Track which columns are editable
        self._editable_columns = {0, 1}  # columns 0 and 1 are editable

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

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

    def data(self, index, role=Qt.DisplayRole):
        if not index.isValid():
            return None
        if role in (Qt.DisplayRole, Qt.EditRole):
            return self._data[index.row()][index.column()]
        return None

    def setData(self, index, value, role=Qt.EditRole):
        if not index.isValid() or role != Qt.EditRole:
            return False
        self._data[index.row()][index.column()] = value
        self.dataChanged.emit(index, index, [role])
        return True

    def flags(self, index):
        base_flags = Qt.ItemIsSelectable | Qt.ItemIsEnabled
        if index.column() in self._editable_columns:
            base_flags |= Qt.ItemIsEditable
        return base_flags

    def is_column_editable(self, column):
        """Helper method to check if a column is editable."""
        return column in self._editable_columns


class MyForm(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("QDataWidgetMapper with Manual Flags")

        # Sample data: [name, email, id]
        data = [
            ["Alice", "alice@example.com", "1001"],
            ["Bob", "bob@example.com", "1002"],
            ["Charlie", "charlie@example.com", "1003"],
        ]

        self.model = MyTableModel(data)

        # Create form widgets
        self.name_edit = QLineEdit()
        self.email_edit = QLineEdit()
        self.id_edit = QLineEdit()

        # Set up the mapper
        self.mapper = QDataWidgetMapper()
        self.mapper.setModel(self.model)
        self.mapper.addMapping(self.name_edit, 0)    # column 0: name
        self.mapper.addMapping(self.email_edit, 1)    # column 1: email
        self.mapper.addMapping(self.id_edit, 2)       # column 2: id

        # Apply flags-like behavior to widgets manually
        self.apply_widget_flags()

        # Navigate to the first row
        self.mapper.toFirst()

        # Navigation buttons
        prev_button = QPushButton("Previous")
        next_button = QPushButton("Next")
        prev_button.clicked.connect(self.mapper.toPrevious)
        next_button.clicked.connect(self.mapper.toNext)

        # Layout
        form_layout = QFormLayout()
        form_layout.addRow("Name:", self.name_edit)
        form_layout.addRow("Email:", self.email_edit)
        form_layout.addRow("ID:", self.id_edit)

        layout = QVBoxLayout()
        layout.addLayout(form_layout)
        layout.addWidget(QLabel("(ID field is read-only, based on model flags)"))
        layout.addWidget(prev_button)
        layout.addWidget(next_button)
        self.setLayout(layout)

    def apply_widget_flags(self):
        """
        Manually apply model flags to mapped widgets.
        The QDataWidgetMapper doesn't do this for us, so we
        check the model and configure each widget accordingly.
        """
        widget_column_map = {
            self.name_edit: 0,
            self.email_edit: 1,
            self.id_edit: 2,
        }

        for widget, column in widget_column_map.items():
            if isinstance(widget, QLineEdit):
                editable = self.model.is_column_editable(column)
                widget.setReadOnly(not editable)
                if not editable:
                    # Visual cue that the field isn't editable
                    widget.setStyleSheet(
                        "QLineEdit { background-color: #f0f0f0; }"
                    )


app = QApplication(sys.argv)
window = MyForm()
window.show()
sys.exit(app.exec_())
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In this example, column 2 (the ID field) is not in the &lt;code&gt;_editable_columns&lt;/code&gt; set, so &lt;code&gt;flags()&lt;/code&gt; would return flags &lt;em&gt;without&lt;/em&gt; &lt;code&gt;Qt.ItemIsEditable&lt;/code&gt; for that column. The &lt;code&gt;apply_widget_flags()&lt;/code&gt; method reads this information from the model and sets the &lt;code&gt;QLineEdit&lt;/code&gt; to read-only, giving it a gray background as a visual hint.&lt;/p&gt;
&lt;p&gt;This manual step bridges the gap between the model's &lt;code&gt;flags()&lt;/code&gt; and the form widgets.&lt;/p&gt;
&lt;h2 id="recap"&gt;Recap&lt;/h2&gt;
&lt;p&gt;When you use &lt;code&gt;QDataWidgetMapper&lt;/code&gt; with standard widgets like &lt;code&gt;QLineEdit&lt;/code&gt;, the mapper calls &lt;code&gt;data()&lt;/code&gt; and &lt;code&gt;setData()&lt;/code&gt; on your model but does &lt;strong&gt;not&lt;/strong&gt; call &lt;code&gt;flags()&lt;/code&gt;. This is because standard widgets have their own properties for controlling editability, visibility, and so on &amp;mdash; they don't participate in Qt's model/view flags system.&lt;/p&gt;
&lt;p&gt;If you need &lt;code&gt;flags()&lt;/code&gt;-like behavior on your form widgets, you can add a small helper method that reads from your model and configures each widget appropriately. This way, your model remains the single source of truth for what's editable, and your form stays in sync.&lt;/p&gt;
&lt;p&gt;If you want to see &lt;code&gt;flags()&lt;/code&gt; in action, try displaying your model in a &lt;a href="https://www.pythonguis.com/tutorials/qtableview-modelviews-numpy-pandas/"&gt;QTableView with numpy and pandas&lt;/a&gt; or explore &lt;a href="https://www.pythonguis.com/faq/editing-pyqt-tableview/"&gt;editing data in a QTableView&lt;/a&gt; &amp;mdash; in those contexts, the view actively queries &lt;code&gt;flags()&lt;/code&gt; for every visible cell.&lt;/p&gt;
            &lt;p&gt;For an in-depth guide to building Python GUIs with PyQt5 see my book, &lt;a href="https://www.martinfitzpatrick.com/pyqt5-book/"&gt;Create GUI Applications with Python &amp; Qt5.&lt;/a&gt;&lt;/p&gt;
            </content><category term="pyqt5"/><category term="pyqt"/><category term="pyside2"/><category term="pyside"/><category term="model-view"/><category term="qtablemodel"/><category term="python"/><category term="qt"/><category term="qt5"/></entry></feed>