IIC2233 / Syllabus-2022-2

Repositorio oficial del curso IIC2233 Programación Avanzada 🚀✨
62 stars 63 forks source link

¿Por qué se define como atributo de clase y no de instancia? #216

Open 3rdPix opened 1 year ago

3rdPix commented 1 year ago

Prerrequisitos

(Marcar colocando una X entre los corchetes los ítems que ya hiciste, así: "[X]")

Duda

Contexto: contenidos semana-07, notebook 1: Threads y PyQt. En el ejemplo 3 (esto es, la 3ra celda de código en el documento), creamos múltiples instancias de QThread para llevar varios contadores en la misma ventana. Se tiene la siguiente definición de thread personalizado:

class MiThread(QThread):

    # Se define para la clase MiThread,
    # para que cada instancia tenga una propia
    senal_actualizar = pyqtSignal(int, str)

    def __init__(self, i, tiempo, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.indice = i
        self.tiempo = tiempo

    def run(self):
        for i in range(10):
            sleep(self.tiempo)
            self.senal_actualizar.emit(self.indice, str(i))

        sleep(self.tiempo)
        self.senal_actualizar.emit(self.indice, 'Status: thread terminado')

El objetivo completo de incluir la señal en la clase de QThread en lugar de la ventana (MiVentana), es para que cada instancia tenga su propia señal. Aquí entra la pregunta, si nos interesa que cada instancia tenga su propia señal, ¿Por qué se define como atributo de clase y no de instancia? ¿Podríamos cambiar la definición anterior con algo como lo siguiente?

class MiThread(QThread):

    # Se define para la clase MiThread,
    # para que cada instancia tenga una propia
    #senal_actualizar = pyqtSignal(int, str) DESCARTAMOS EL ATRIBUTO DE CLASE

    def __init__(self, i, tiempo, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.indice = i
        self.tiempo = tiempo
        self.senal_actualizar = pyqtSignal(int, str) # LO CREAMOS COMO ATRIBUTO DE INSTANCIA

    def run(self):
        for i in range(10):
            sleep(self.tiempo)
            self.senal_actualizar.emit(self.indice, str(i))

        sleep(self.tiempo)
        self.senal_actualizar.emit(self.indice, 'Status: thread terminado')

Y de ser posible, cuáles serían las diferencias?

cataconi commented 1 year ago

¡Hola!

Las señales siempre deben ser definidas por fuera, no como atributos. Puedes probarlo igualmente, pero es muy probable que utilizando el segundo código te lance el siguiente error:

'PyQt5.QtCore.pyqtSignal' object has no attribute 'connect'

Tal y como dice la documentación de 'PyQt5.QtCore.pyqtSignal', "las nuevas señales solo deben definirse en subclases de QObject. Deben ser parte de la definición de clase y no se pueden agregar dinámicamente como atributos de clase después de que se haya definido la clase".

Fuente: https://www.riverbankcomputing.com/static/Docs/PyQt5/signals_slots.html#PyQt5.QtCore.pyqtSlot