title: QLineEdit displays no data
body: <p>I created a gui that supposedly should allow to write some notes in a QLineEdit. If I use no custom approach and just display the data via the basic QTableView I can see everything normally, but when I start using a custom approach (because of dates, combo boxes etc), every widget works correctly with the exception of QLineEdit, which does not display the model data and refuses to display anything I write into it (albeit during editing I can see what I'm typing).</p>
  
  <p>I've fought with this for over a week, trying many different approaches (and creating more bugs). My current suspicion is that for some reason I'm not interacting with the right QLineEdit widget, and that somehow my code creates an additional one right on top of the one that is connected to the model.
  Still, I created this by following the basic examples, and I'm not able to see where the issue is, nor I have been able to debug since python debugger skips over all the default implementations (because they are C++, I guess).</p>
  
  <p>The minimal example is still a bit fat, so I've put it into a file: <a href="http://www.filedropper.com/test_185" rel="nofollow noreferrer">download</a></p>
  
  <p>All that I expect is for the second column widgets to display text:</p>
  
  <ol>
  <li>At start, since they are initialised (or should be) with the model data.</li>
  <li>After an edit.</li>
  </ol>
  
  <p>In short, they should always display the text.</p>
  
  <p>Just run and observe the 2nd column (try to edit too). The TableView sets the Delegate responsible for the widget interaction and manages the model. I would expect the issue to be between TableView and Delegate.</p>
  
  <p>EDIT: someone requested I paste the minimalistic example instead of the file, so here it is.</p>
  
  <pre class="lang-py prettyprint-override"><code>#!/usr/bin/env python3
  from collections import OrderedDict
  from PyQt5 import QtCore, QtWidgets
  import pandas as pd
  
  
  def validate(value, expected_type):
      """ Returns an object of expected_type, with the passed value if possible, the default value otherwise. """
      if type(value) == QtCore.QVariant:
          value = None if value.isNull() or not value.isValid() else value.value()
      value = expected_type() if value is None else expected_type(str(value))
      return value
  
  
  class ComboBox(QtWidgets.QComboBox):
      def __init__(self, parent=None):
          super().__init__(parent)
  
      def data(self):
          return self.currentIndex()
  
      def set_data(self, value):
          print("ComboBox received {}".format(type(value)))
          print("value {}".format(value))
          value = int(validate(value, float))
          print("valid value {}".format(value))
          self.setCurrentIndex(value)
          self.update()
  
  
  class InfoTypeSelector(ComboBox):
      def __init__(self, parent=None):
          super().__init__(parent)
          self.addItems(["A1", "A2", "A3", "A4"])
  
  
  class DateEdit(QtWidgets.QDateEdit):
      def __init__(self, parent=None):
          super().__init__(parent)
          self.setCalendarPopup(True)
  
      def data(self):
          return self.date().toString()
  
      def set_data(self, value):
          print("DateEdit received {}".format(type(value)))
          print("value {}".format(value))
          value = validate(value, str)
          print("valid value {}".format(value))
          self.setDate(QtCore.QDate.fromString(value))
          self.update()
  
  
  class TimeEdit(QtWidgets.QTimeEdit):
      def data(self):
          return self.time().toString()
  
      def set_data(self, value):
          print("TimeEdit received {}".format(type(value)))
          print("value {}".format(value.value()))
          value = validate(value, str)
          print("valid value {}".format(value))
          self.setTime(QtCore.QTime.fromString(value))
          self.update()
  
  
  class LineEdit(QtWidgets.QLineEdit):
      def __init__(self, parent=None):
          super().__init__(parent)
          self.setAlignment(QtCore.Qt.AlignLeft)
  
      def data(self):
          return str(self.text())
  
      def set_data(self, value):
          print("LineEdit received {}".format(type(value)))
          print("value {}".format(value))
          value = validate(value, str)
          print("valid value {}".format(value))
          self.setText(value)
          self.update()
  
      def displayText(self):
          print("dysplaying: {}".format(super().displayText()))
          return super().displayText()
  
      def setText(self, value):
          print("updating: {}".format(value))
          super().setText(value)
  
  
  class CheckBox(QtWidgets.QCheckBox):
      def data(self):
          return self.checkState()
  
      def set_data(self, value):
          print("CheckBox received {}".format(type(value)))
          print("value {}".format(value))
          value = validate(value, bool)
          print("valid value {}".format(value))
          self.setCheckState(value)
          self.update()
  
  
  class Delegate(QtWidgets.QStyledItemDelegate):
      """ This handles widget creation, allowing to pass a dataframe and a 2d index on construction. """
      def __init__(self, factories, model):
          """ data is the dataframe used for initialisation of the widgets, as well as to store the widget values
              so that they can be serialized.
              factories is a list of widget generators (their type), indexed by the column index. """
          super().__init__()
          self.factories = factories
          self.model = model
  
      def createEditor(self, parent, option, index):
          if self.factories[index.column()] == str:
              return super().createEditor(parent, option, index)
          value = self.model.data(index)
          widget = self.factories[index.column()](parent)
          widget.set_data(value)
          return widget
  
      def setModelData(self, widget, model, index):
          if self.factories[index.column()] == str:
              super().setModelData(widget, model, index)
              return
          self.model.setData(index, widget.data())
  
  
  class TableView(QtWidgets.QTableView):
      def __init__(self, name, dataframe, gui_column_types, default_column_values, parent=None, editable=False):
          """ dataframe holds the serializable data that store the internal state of the widgets in guiframe.
              gui_column_types is a list of types, ordered as the columns of guiframe should be. """
          super().__init__(parent)
          self.setSortingEnabled(True)
          self.setObjectName(name)
  
          self.editable = editable
          self.gui_column_types = gui_column_types
          self.default_column_values = default_column_values
  
          self.model = SerializableModel(dataframe, default_column_values, self, editable=self.editable)
  
          # Delegate Setup
          delegate = Delegate(gui_column_types, self.model)
          self.setItemDelegate(delegate)
          # Show the edit widget as soon as the user clicks in the cell (needed for item delegate)
          self.setEditTriggers(self.CurrentChanged)
          self.setModel(self.model)
  
      def init(self):
          # self.model.init()
          for row in range(self.model.rowCount()):
              for column in range(self.model.columnCount()):
                  self.enable_cell(row, column)
  
      def row_add(self):
          # This cleans filters. If it didn't the added row might be filtered out and thus be invisible to the user.
          #0. clean filters
          #1. add to data
          self.model.row_append()
          return
  
      def row_delete(self):
          # This can be done while filtering.
          #0. remove from data
          #1. remove from filtered
          return
  
      def enable_cell(self, row, column):
          if self.gui_column_types[column] == str:
              return
          index = self.model.index(row, column)
          self.openPersistentEditor(index)
  
      def model_data(self):
          return self.model.dataframe
  
  
  class SerializableModel(QtCore.QAbstractTableModel):
      def __init__(self, dataframe, default_column_values, table_view, parent=None, editable=False):
          "empty_row is a list of types ordered according to the columns in the dataframe, which represents an empty row"
          QtCore.QAbstractTableModel.__init__(self, parent=parent)
          self.empty_row = pd.DataFrame(data=default_column_values, columns=list(dataframe.columns))
          self.dataframe = dataframe
          self.filtered = self.dataframe
          self.table_view = table_view
          self.editable = editable
  
      def flags(self, index):
          defaults = super().flags(index)
          if self.editable:
              return defaults | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsSelectable
          return defaults | QtCore.Qt.ItemIsEnabled
  
      def data(self, index, role=QtCore.Qt.DisplayRole):
          # print("reading data {} {}".format(index.row(), index.column()))
          if role == QtCore.Qt.TextAlignmentRole:
              return QtCore.Qt.AlignCenter
          if not index.isValid() or role != QtCore.Qt.DisplayRole or index.row() &gt;= self.filtered.shape[0]:
              return QtCore.QVariant()
          value = self.filtered.iat[index.row(), index.column()]
          return QtCore.QVariant(str(value))
  
      def setData(self, index, value, role=QtCore.Qt.EditRole):
          column = self.filtered.columns[index.column()]
          column_dtype = self.filtered[column].dtype
          if column_dtype != object:
              value = None if value == '' else column_dtype.type(value)
          row = self.filtered.index.values[index.row()]
          self.dataframe.iat[row, index.column()] = value
          self.dataChanged.emit(index, index)
          return True
  
      def rowCount(self, parent=QtCore.QModelIndex()):
          return self.filtered.shape[0] if not parent.isValid() else 0
  
      def columnCount(self, parent=QtCore.QModelIndex()):
          return self.filtered.shape[1] if not parent.isValid() else 0
  
      def insertRows(self, row, count, parent=QtCore.QModelIndex()):
          """ Since we only need append, we assume row == rowCount. """
          for _ in range(count):
              self.dataframe = self.dataframe.append(self.empty_row, ignore_index=True)
          return True
  
      def row_append(self, count=1, parent=QtCore.QModelIndex()):
          """ Reset filters, to ensure the new rows are not hidden by them, and appends rows with default values. """
          self.filtered = self.dataframe
          self.beginInsertRows()
          self.insertRows(self.rowCount(), count, parent)
          self.endInsertRows()
          self.layoutChanged.emit()
  
  
  DF_PROTOTYPE_GLOBAL = pd.DataFrame(data=OrderedDict((
      ("Type", [int()]),
      ("Details", [str()]),
  )))
  
  DF_PROTOTYPE_GLOBAL_SAVED = pd.DataFrame(data=OrderedDict((
      ("Type", [2, 0]),
      ("Details", ["item 1", "item 2"]),
  )))
  
  DF_PROTOTYPE_GLOBAL_GUIFRAME_TYPES = [InfoTypeSelector, LineEdit]
  
  class InfoManager:
      def __init__(self):
          self.window = QtWidgets.QMainWindow()
          self.ui = Ui_InfoWindow()
          self.ui.setupUi(self.window)
  
          # Overwrites
          self.ui.view_global.setParent(None)
          self.ui.view_global = TableView("view_global", DF_PROTOTYPE_GLOBAL_SAVED,
                                                DF_PROTOTYPE_GLOBAL_GUIFRAME_TYPES,
                                                DF_PROTOTYPE_GLOBAL,
                                                parent=self.ui.tab_global, editable=True)
          self.ui.verticalLayout_4.addWidget(self.ui.view_global)
          self.ui.view_global.init()
  
          # self.ui.view_notes.setParent(None)
          # self.ui.view_notes = iwl.DataFrameWidget(name="view_notes", parent=self.ui.tab_notes, df=data[1], editable=True)
          # self.ui.verticalLayout_2.addWidget(self.ui.view_notes)
          #
          # self.ui.view_events.setParent(None)
          # self.ui.view_events = iwl.DataFrameWidget(name="view_events", parent=self.ui.tab_events, df=data[2], editable=True)
          # self.ui.verticalLayout.addWidget(self.ui.view_events)
  
      def show(self):
          self.window.show()
  
  
  class Ui_InfoWindow(object):
      def setupUi(self, InfoWindow):
          InfoWindow.setObjectName("InfoWindow")
          InfoWindow.resize(1000, 748)
          self.centralwidget = QtWidgets.QWidget(InfoWindow)
          self.centralwidget.setObjectName("centralwidget")
          self.verticalLayout_5 = QtWidgets.QVBoxLayout(self.centralwidget)
          self.verticalLayout_5.setObjectName("verticalLayout_5")
          self.horizontalLayout = QtWidgets.QHBoxLayout()
          self.horizontalLayout.setObjectName("horizontalLayout")
          self.verticalLayout_5.addLayout(self.horizontalLayout)
          self.info_tabs = QtWidgets.QTabWidget(self.centralwidget)
          self.info_tabs.setObjectName("info_tabs")
          self.tab_global = QtWidgets.QWidget()
          self.tab_global.setObjectName("tab_global")
          self.verticalLayout_4 = QtWidgets.QVBoxLayout(self.tab_global)
          self.verticalLayout_4.setContentsMargins(0, 0, 0, 0)
          self.verticalLayout_4.setObjectName("verticalLayout_4")
          self.view_global = QtWidgets.QTableView(self.tab_global)
          self.view_global.setSortingEnabled(True)
          self.view_global.setObjectName("view_global")
          self.verticalLayout_4.addWidget(self.view_global)
          self.info_tabs.addTab(self.tab_global, "")
          self.verticalLayout_5.addWidget(self.info_tabs)
          InfoWindow.setCentralWidget(self.centralwidget)
          self.menubar = QtWidgets.QMenuBar(InfoWindow)
          self.menubar.setGeometry(QtCore.QRect(0, 0, 1000, 20))
          self.menubar.setObjectName("menubar")
          InfoWindow.setMenuBar(self.menubar)
          self.statusbar = QtWidgets.QStatusBar(InfoWindow)
          self.statusbar.setObjectName("statusbar")
          InfoWindow.setStatusBar(self.statusbar)
          self.action_email_tab = QtWidgets.QAction(InfoWindow)
          self.action_email_tab.setObjectName("action_email_tab")
          self.action_exit = QtWidgets.QAction(InfoWindow)
          self.action_exit.setObjectName("action_exit")
  
          self.retranslateUi(InfoWindow)
          self.info_tabs.setCurrentIndex(0)
          QtCore.QMetaObject.connectSlotsByName(InfoWindow)
  
      def retranslateUi(self, InfoWindow):
          _translate = QtCore.QCoreApplication.translate
          InfoWindow.setWindowTitle(_translate("InfoWindow", "Info Window"))
          self.info_tabs.setTabText(self.info_tabs.indexOf(self.tab_global), _translate("InfoWindow", "Global"))
          self.action_email_tab.setText(_translate("InfoWindow", "Email Current Tab"))
          self.action_exit.setText(_translate("InfoWindow", "Exit"))
  
  
  if __name__ == '__main__':
      import sys
      app = QtWidgets.QApplication(sys.argv)
      info_manager = InfoManager()
      info_manager.show()
      sys.exit(app.exec_())
  </code></pre>