PySide: QAbstractItemModel not talking to simple controls
Question: Using PySide, I am trying to connect several data controls to data from a file
that I read from disc. So I made a data model, derived from
QAbstractItemModel. Should be trivial, right? But one problem that has me
beaten is connecting QLineEdit and QTextEdit controls to show and allow
editing of the data in the model.

From examples such as the Simple Widget Mapper and the Combo Widget Mapper in
the QT documentation, I believe I have to have a data model with one row, and
a QWidgetMapper to connect the cells in that row to the edit controls.

Here is a very cut-down program that shows the problem. The data model in this
example returns the three strings x1, x2 and x3. The strings are changed from
e.g. "First (1)" to "First (2)" by calling Update, i.e. by clicking the "Next"
button. That is to simulate new values being read from a file or wherever. The
main window has a couple of QLineEdits and a QTextEdit, which the mapper
should link to the model data. But the data don't show up in the edit
controls.

To check the model, I added a QTableView. The data show up there alright, and
update when "Next" is clicked, so it's not the data model. It's something
between there and the edit controls. But I cannot see what I am not doing,
that the widget mapper examples are doing.

What am I doing wrong?

Incidentally, going the other way doesn't work well either. If I add a
setData() method to the model, and emit dataChanged, a change typed into the
line edit gets to the Table. But even when that happens, the item disappears
from the line edit. And after "Next" is clicked, this stops working for the
QLineEdit and QTextEdit -- setData() no longer gets called. But if I edit in
the table view, setData is still called.

Here is the example code:

    
    
    #!/usr/bin/python
    
    import sys
    from PySide.QtCore import ( Qt, QAbstractItemModel,QModelIndex
                              )
    from PySide.QtGui import ( QApplication, QMainWindow, QPushButton, QWidget,
                               QTextEdit, QLineEdit, QFormLayout, QTableView,
                               QDataWidgetMapper, 
                             )
    
    ##############################################################################
    class TModel(QAbstractItemModel):
      """
      This model will have 1 row of 3 items
    
      There will be a slot that will change the items. If they are displayed
      in widgets, I want to see them update.
      """
    
      def __init__(self, parent=None):
        super(TModel, self).__init__(parent)
    
        self.counter = 0
        self.x1 = ""
        self.x2 = ""
        self.x3 = ""
        self.Update()
    
      def columnCount(self, index=QModelIndex()):
        return 3
    
      def rowCount(self, index=QModelIndex()):
        return 1
    
      def index(self, row, column, index=QModelIndex()):
        if not self.hasIndex(row, column, index):
          return QModelIndex()
        return self.createIndex(row, column)
    
      def parent(self, index):
        return QModelIndex()
    
      def hasChildren(self, index):
        return False
    
      def data(self, index, role=Qt.DisplayRole):
        if index.isValid():
          if role == Qt.DisplayRole:
            if index.column() == 0:
              return self.x1
            elif index.column() == 1:
              return self.x2
            elif index.column() == 2:
              return self.x3
    
        return None
    
      def headerData(self, section, orientation, role):
        if role != Qt.DisplayRole:
          return None
    
        if orientation == Qt.Horizontal:
          if section == 0:
            return "col 1"
          elif section == 1:
            return "col 2"
          elif section == 2:
            return "col 3"
    
      def Update(self):
        self.beginResetModel()
        self.x1 = "First (%d)"%self.counter
        self.x2 = "Second (%d)"%self.counter
        self.x3 = "Third (%d)"%self.counter
        self.counter += 1
        self.endResetModel()
    
    
    
    ##############################################################################
    class TMainWindow(QMainWindow):
      """Main GUI object"""
    
      def __init__(self, parent=None):
        super(TMainWindow, self).__init__(parent)
    
        self.DataModel = TModel()
    
        self.Mapper = QDataWidgetMapper()
        self.Mapper.setSubmitPolicy(self.Mapper.AutoSubmit)
        self.Mapper.setModel(self.DataModel)
    
        self.FirstFieldEdit = QLineEdit()
        self.SecondFieldEdit = QLineEdit()
        self.ThirdFieldEdit = QTextEdit()
    
        self.Mapper.addMapping(self.FirstFieldEdit, 0)
        self.Mapper.addMapping(self.SecondFieldEdit, 1)
        self.Mapper.addMapping(self.ThirdFieldEdit, 2)
        self.Mapper.toFirst()
    
        self.UpdateButton = QPushButton("Next")
        self.UpdateButton.clicked.connect(self.DataModel.Update)
    
        formLayout = QFormLayout()
        formLayout.addRow("&First:",  self.FirstFieldEdit)
        formLayout.addRow("&Second:", self.SecondFieldEdit)
        formLayout.addRow("&Third:",  self.ThirdFieldEdit)
        formLayout.addRow("",         self.UpdateButton)
    
        self.testTable = QTableView()
        self.testTable.setModel(self.DataModel)
        formLayout.addRow("Table:", self.testTable)
    
        W = QWidget()
        W.setLayout(formLayout)
        self.setCentralWidget(W)
    
    
    
    ##############################################################################
    
    if __name__ == "__main__":
      app = QApplication(sys.argv)
      MainWindow = TMainWindow()
      MainWindow.show()
      sys.exit(app.exec_())
    


Answer: The `QDataWidgetMapper` class is intended to allow displaying and editing
records in a model. But your model doesn't return anything when data is
requested for editing. So a simple fix would be:

    
    
        def data(self, index, role=Qt.DisplayRole):
            if index.isValid():
                if role == Qt.DisplayRole or role == Qt.EditRole:
                    ...
    

**EDIT** :

From the Qt Docs:

> QDataWidgetMapper can be used to create data-aware widgets by mapping them
> to **sections** of an item model. A section is a **column** of a model if
> the orientation is horizontal (the default), otherwise a **row**. [emphasis
> added]

I would suggest you avoid trying to write a custom model (which is very far
from trivial, except for the simplest of cases), and start by using a
[QStandardItemModel](http://doc.qt.io/qt-5/qstandarditemmodel.html).

