En mi aplicación, tengo que reemplazar todos los elementos de QLineEdit con QLineEdit personalizado. Para ello hay diferentes soluciones:
Hasta ahora, podría escribir un módulo (WidgetReplacer) que busque recursivamente los elementos de QGridLayout y busque si hay hijos de QLineEdit. Si es así, entonces reemplázalo con el mío.
El problema es que cuando bash acceder a uno de mis objetos LineEdit, aparece el siguiente error:
RuntimeError: wrapped C/C++ object of type QLineEdit has been deleted
Y si miro mi salida, me doy cuenta de que el objeto QLineEdit modificado tiene la ID antigua:
old qt_obj Replaced txt_line_1 with LineEdit new qt_obj ---------------------------------- ... ----------------------------------
¿Por qué sucede esto? ¿Cómo puedo reemplazar QWidgets en tiempo de ejecución?
Aquí hay algunos detalles adicionales con respecto a la envoltura de objetos: Dentro del módulo WidgetReplace si vuelco los objetos QEditLine y LineEdit que obtengo:
Reference count: 7 Address of wrapped object: 051FAC40 Created by: Python To be destroyed by: C/C++ Parent wrapper: Next sibling wrapper: NULL Previous sibling wrapper: First child wrapper: NULL
y
Reference count: 3 Address of wrapped object: 051FAC40 Created by: Python To be destroyed by: C/C++ Parent wrapper: Next sibling wrapper: Previous sibling wrapper: NULL First child wrapper: NULL
En su lugar, si vuelco mi edición de línea de la línea principal, obtengo el siguiente, que representa el objeto QLineEdit eliminado:
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
Diálogo El archivo ui y el archivo generado pueden descargarse desde DropBox dialog.ui y dialog.py
principal
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_())
LineEdit
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)
WidgetReplacer
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 "----------------------------------"
No reemplace los widgets en tiempo de ejecución: promocione los widgets en Qt Designer para que las ediciones de línea sean reemplazadas automáticamente por su clase personalizada cuando se genere el módulo GUI.
Aquí se explica cómo promocionar un widget para usar una clase personalizada:
En Qt Designer, seleccione todas las ediciones de línea que desea reemplazar, luego haga clic con el botón derecho sobre ellas y seleccione “Promocionar a …”. En el cuadro de diálogo, establezca “Nombre de clase promocionado” en “LineEdit” y configure “Archivo de encabezado” en la ruta de importación de Python para el módulo que contiene esta clase (por ejemplo, myapp.LineEdit
). Luego haga clic en “Agregar” y “Promocionar”, y verá el cambio de clase de “QLineEdit” a “LineEdit” en el panel del Inspector de objetos.
Ahora, cuando vuelva a generar su módulo ui con pyuic, debería ver que usa su clase personalizada de LineEdit y habrá una línea adicional en la parte inferior del archivo como esta:
from myapp.LineEdit import LineEdit
No revisé todo tu código …
Sin embargo, creo que aunque haya reemplazado el widget en el diseño, window.txt_line_1
todavía apunta al objeto eliminado. Por lo tanto, en su procedimiento de reemplazo también deberá actualizar este atributo.
Entonces, añadiendo
setattr(parent, obj_name, qt_obj);
a _replace_qlineedit_from_gridlayout
podría hacer el truco.