title: Replace QWidget objects at runtime
body: <p>In my application I have to replace all QLineEdit elements with customized QLineEdit. To do that there are different solutions:</p>
  
  <ol>
  <li>Modify the generated py file from pyuic4 and replace there all QLineEdit objects with my one LineEdit. This solution is not really the best, because every time I run pyuic4 I lose the modification I did into the generated output file.</li>
  <li>Write a new class that search recursively inside my window or dialog for QLineEdit widget types and replace them with my one LineEdit. This solution is much better. It allows to me to do the same also with other objects or extend it as I want without to care about the dialog or window.</li>
  </ol>
  
  <p>So far, I could write a module (WidgetReplacer) which search recursively for QGridLayout items and search if there are QLineEdit children. If yes, then replace it with my one.</p>
  
  <p>The problem is that when I try to access one of my LineEdit objects I get the following error:</p>
  
  <pre><code>RuntimeError: wrapped C/C++ object of type QLineEdit has been deleted
  </code></pre>
  
  <p>And if I look at my output I notice that the modified QLineEdit object has sitll the old id:</p>
  
  <pre><code>old qt_obj &lt;PyQt4.QtGui.QLineEdit object at 0x0543C930&gt;
  Replaced txt_line_1 with LineEdit
  new qt_obj &lt;LineEdit.LineEdit object at 0x0545B4B0&gt;
  ----------------------------------
  ...
  ----------------------------------
  &lt;PyQt4.QtGui.QLineEdit object at 0x0543C930&gt;
  </code></pre>
  
  <h2>Question</h2>
  
  <p>Why happen this? How can I replace QWidgets at runtime?</p>
  
  <hr>
  
  <h2>Update</h2>
  
  <p>Here some additional details regarding the ojects wrapping:
  Inside the module WidgetReplace if I dump the QEditLine and LineEdit objects I get:</p>
  
  <pre><code>&lt;PyQt4.QtGui.QLineEdit object at 0x05378618&gt;
      Reference count: 7
      Address of wrapped object: 051FAC40
      Created by: Python
      To be destroyed by: C/C++
      Parent wrapper: &lt;PyQt4.QtGui.QGridLayout object at 0x05378588&gt;
      Next sibling wrapper: NULL
      Previous sibling wrapper: &lt;PyQt4.QtGui.QLabel object at 0x05378930&gt;
      First child wrapper: NULL
  </code></pre>
  
  <p>and</p>
  
  <pre><code>&lt;LineEdit.LineEdit object at 0x05378978&gt;
      Reference count: 3
      Address of wrapped object: 051FAC40
      Created by: Python
      To be destroyed by: C/C++
      Parent wrapper: &lt;__main__.Dialog object at 0x0535FBB8&gt;
      Next sibling wrapper: &lt;PyQt4.QtGui.QGridLayout object at 0x05378780&gt;
      Previous sibling wrapper: NULL
      First child wrapper: NULL
  </code></pre>
  
  <p>Instead if I dump my line edit from the main, I get follow, which represent the deleted QLineEdit object:</p>
  
  <pre><code>&lt;PyQt4.QtGui.QLineEdit object at 0x05378618&gt;
      Reference count: 2
      Address of wrapped object: 00000000
      Created by: Python
      To be destroyed by: C/C++
      Parent wrapper: NULL
      Next sibling wrapper: NULL
      Previous sibling wrapper: NULL
      First child wrapper: NULL
  </code></pre>
  
  <hr>
  
  <h2>Here my code</h2>
  
  <p><strong>Dialog</strong>
  The ui and generate file can be dowloaded from DropBox <a href="https://www.dropbox.com/sh/odxa2cq7913zk50/plycO-039B" rel="nofollow">dialog.ui and dialog.py</a></p>
  
  <p><strong>main</strong></p>
  
  <pre><code>import sys
  from PyQt4.QtGui import QDialog, QApplication
  from dialog import Ui_Form
  from WidgetReplacer import WidgetReplacer
  
  class Dialog(QDialog, Ui_Form):
  
      def __init__(self, parent = None):
          super(Dialog, self).__init__(parent)
  
          # Set up the user interface from Designer.
          self.setupUi(self)
          WidgetReplacer().replace_all_qlineedit(self)
  
  
  if __name__ == "__main__":
      app = QApplication(sys.argv)
      window = Dialog()
  
      window.show()
  
      print window.txt_line_1
      window.txt_line_1.setText("Hello Text 1")
  
      sys.exit(app.exec_())
  </code></pre>
  
  <p><strong>LineEdit</strong></p>
  
  <pre><code>from PyQt4.QtGui import QLineEdit, QValidator, QPalette
  from PyQt4 import QtCore
  
  class LineEdit(QLineEdit):
  
      def __init__(self, parent=None):
          super(LineEdit, self).__init__(parent)
  
          self.color_red = QPalette()
          self.color_red.setColor(QPalette.Text, QtCore.Qt.red)
  
          self.color_black = QPalette()
          self.color_black.setColor(QPalette.Text, QtCore.Qt.red)
  
          # Make connections
          self.textChanged.connect(self.verify_text)
  
      def verify_text(self, text):
          validator = self.validator()
          is_valid = QValidator.Acceptable
  
          if validator is not None:
              is_valid = validator.validate(text, 0)
  
          if is_valid == QValidator.Acceptable:
              self.setPalette(self.color_black)
          elif is_valid in [QValidator.Invalid, QValidator.Intermediate]:
              self.setPalette(self.color_red)
  </code></pre>
  
  <p><strong>WidgetReplacer</strong></p>
  
  <pre><code>import sip
  from LineEdit import LineEdit
  from PyQt4.QtCore import QRegExp
  from PyQt4 import QtGui
  
  class WidgetReplacer():
  
      def __init__(self):
          pass
  
      def replace_all_qlineedit(self, qt_dlg):
          children = self._get_all_gridlayout(qt_dlg)
  
          items = []
          for child in children:
              new_items = self._find_all_obj_in_layout(child, QtGui.QLineEdit)
              items.extend(new_items)
  
          for item in items:
              layout = item[0]
              row = item[1]
              column = item[2]
              qt_obj = item[3]
              self._replace_qlineedit_from_gridlayout(qt_dlg, qt_obj,
                                                      layout, row, column)
  
      def _get_all_gridlayout(self, qt_dlg):
          return qt_dlg.findChildren(QtGui.QGridLayout, QRegExp("gridLayout_[0-9]"))
  
      def _find_all_obj_in_layout(self, layout, qt_type):
          # Output list format:
          # layout, row, col, qt_obj,
          objects = []
          if type(layout) == QtGui.QGridLayout:
              layout_cols = layout.columnCount()
              layout_rows = layout.rowCount()
              for i in range(layout_rows):
                  for j in range(layout_cols):
                      item = layout.itemAtPosition(i, j)
  #                    print "item(",i, j, "):", item
  
                      if type(item) == QtGui.QWidgetItem:
                          if type(item.widget()) == qt_type:
                              new_obj = [layout, i, j, item.widget()]
                              objects.append(new_obj)
  
          elif type(layout) in [QtGui.QHBoxLayout, QtGui.QVBoxLayout]:
              raise SyntaxError("ERROR: Find of Qt objects in QHBoxLayout or QVBoxLayout still not supported")
  #            for i in range(layout.count()):
  #                item = layout.itemAt(i)
  
          return objects
  
      def _replace_qlineedit_from_gridlayout(self, parent, qt_obj, layout, row, column):
          print "old qt_obj", qt_obj
          obj_name = qt_obj.objectName()
          layout.removeWidget(qt_obj)
          sip.delete(qt_obj)
          qt_obj = LineEdit(parent)
          qt_obj.setObjectName(obj_name)
  
          layout.addWidget(qt_obj, row, column)
          print "Replaced", obj_name, "with LineEdit"
  
          print "new qt_obj", qt_obj
          print "----------------------------------"
  </code></pre>