PyQt Quickie: Que no te lleve el basurero
Qt tiene sus mecanismos para crear y eliminar objetos (el árbol de QObjects, smart pointers, etc.) y PyQt usa Python, así que tiene garbage collection.
Consideremos un ejemplo simple:
from PyQt4 import QtCore def finished(): print "El proceso termino!" # Salir de la aplicación QtCore.QCoreApplication.instance().quit() def launch_process(): # Hacer algo asincrono proc = QtCore.QProcess() proc.start("/bin/sleep 3") # Cuando termine, llamar a finished proc.finished.connect(finished) def main(): app = QtCore.QCoreApplication([]) # Lanzar el proceso launch_process() app.exec_() main()
Si ejecutás eso, te va a pasar esto:
QProcess: Destroyed while process is still running. El proceso termino!
Encima el script no termina nunca. ¡Diversión! El problema es que proc
está siendo borrado al final de launch_process
porque no hay más referencias a él.
Ésta es una mejor manera de hacerlo:
from PyQt4 import QtCore processes = set([]) def finished(): print "El proceso termino!" # Salir de la aplicación QtCore.QCoreApplication.instance().quit() def launch_process(): # Hacer algo asincrono proc = QtCore.QProcess() processes.add(proc) proc.start("/bin/sleep 3") # Cuando termine, llamar a finished proc.finished.connect(finished) def main(): app = QtCore.QCoreApplication([]) # Lanzar el proceso launch_process() app.exec_() main()
Al agregar un processes
global y meter ahí proc
, mantenemos siempre una referencia, y el programa funciona. Sin embargo, sigue teniendo un problema: nunca eliminamos los objetos QProcess
.
Si bien en este caso la pérdida de memoria es muy breve porque el programa termina enseguida, en un programa de verdad esto no es buena idea.
Así que necesitamos agregar una manera de sacar proc
de processes
cuando no lo necesitemo. Esto no es tan fácil como parece. Por ejemplo, esto no funciona bien:
def launch_process(): # Hacer algo asincrono proc = QtCore.QProcess() processes.add(proc) proc.start("/bin/sleep 3") # Sacamos el proceso del global cuando no lo necesitamos proc.finished.connect(lambda: processes.remove(proc)) # Cuando termine, llamar a finished proc.finished.connect(finished)
¡En esta versión, todavía tenemos un memory leak de proc
, aunque processes
esté vacío! Lo que pasa es que el lambda
contiene una referencia a proc
.
No tengo una my buena respuesta para este problema que no involucre convertir todo en miembros de un Qbject
y usar sender
para saber cuál proceso es el que termina, o usar QSignalMapper
. Esa versión la dejo como ejercicio para el lector ;-)