Qt eventos y señal / ranuras

97

En el mundo Qt, ¿cuál es la diferencia de eventos y señal / ranuras?

¿Reemplaza uno al otro? ¿Son los eventos una abstracción de señales / ranuras?

Raphael
fuente

Respuestas:

30

La documentación de Qt probablemente lo explica mejor:

En Qt, los eventos son objetos, derivados de la QEventclase abstracta , que representan cosas que han sucedido dentro de una aplicación o como resultado de una actividad externa que la aplicación necesita conocer. Los eventos pueden ser recibidos y manejados por cualquier instancia de una QObjectsubclase, pero son especialmente relevantes para los widgets. Este documento describe cómo se envían y gestionan los eventos en una aplicación típica.

Entonces, los eventos y las señales / ranuras son dos mecanismos paralelos que logran las mismas cosas. En general, un evento será generado por una entidad externa (por ejemplo, el teclado o la rueda del mouse) y se entregará a través del bucle de eventos en QApplication. En general, a menos que configure el código, no generará eventos. Puede filtrarlos QObject::installEventFilter()o manejar eventos en un objeto subclasificado anulando las funciones apropiadas.

Las señales y ranuras son mucho más fáciles de generar y recibir y puede conectar dos QObjectsubclases cualesquiera . Se manejan a través de la Metaclase (eche un vistazo a su archivo moc_classname.cpp para obtener más información), pero la mayor parte de la comunicación entre clases que producirá probablemente utilizará señales y ranuras. Las señales se pueden entregar inmediatamente o diferir a través de una cola (si está utilizando subprocesos).

Se puede generar una señal.

Harald Scheirich
fuente
¿La cola diferida es la misma que el bucle de eventos de cola o existen dos colas? uno para señales diferidas y encendido para evento?
Guillaume
29
@Raphael, la vergüenza de usted por aceptar esta respuesta. - ¡El párrafo citado ni siquiera contiene las palabras "señal" o "ranura"!
Robert Siemer
1
@neuronet: el párrafo se cita con “[lo] explica mejor”, que no es así. De ningún modo. Ni siquiera un poco.
Robert Siemer
@Robert, así que si esa pequeña línea de introducción se cambiara a "Primero, consideremos cómo los documentos explican los eventos, antes de pasar a considerar cómo se relacionan con las señales", ¿estaría de acuerdo con la respuesta? Si es así, podemos editar la respuesta para mejorarla, ya que es un punto menor y estoy de acuerdo en que podría redactarse mejor. [Editar, esa es una pregunta genuina: como no estoy seguro de que el resto sea mucho mejor ... pero solo porque la parte citada no menciona señales parece una preocupación superficial si el resto es realmente bueno, lo cual soy no asumiendo .]
Eric
4
@neuronet, si eliminas la cita por completo, mejorará la respuesta en mi opinión. - Si elimina la respuesta completa, mejorará todo este control de calidad.
Robert Siemer
152

En Qt, las señales y los eventos son implementaciones del patrón Observer . Se utilizan en diferentes situaciones porque tienen diferentes fortalezas y debilidades.

En primer lugar, definamos lo que queremos decir con 'evento Qt' exactamente: una función virtual en una clase Qt, que se espera que vuelva a implementar en una clase base suya si desea manejar el evento. Está relacionado con el patrón del método de plantilla .

Observe cómo usé la palabra " manejar ". De hecho, aquí hay una diferencia básica entre la intención de las señales y los eventos:

  • Tú " manejas " los eventos
  • " Recibe notificaciones de " emisiones de señales

La diferencia es que cuando "maneja" el evento, asume la responsabilidad de "responder" con un comportamiento que es útil fuera de la clase. Por ejemplo, considere una aplicación que tiene un botón con un número. La aplicación debe permitir que el usuario enfoque el botón y cambie el número presionando las teclas "arriba" y "abajo" del teclado. De lo contrario, el botón debería funcionar como normal QPushButton(se puede hacer clic en él, etc.). En Qt, esto se hace creando su propio "componente" reutilizable (subclase de QPushButton), que se vuelve a implementar QWidget::keyPressEvent. Pseudocódigo:

class NumericButton extends QPushButton
    private void addToNumber(int value):
        // ...

    reimplement base.keyPressEvent(QKeyEvent event):
        if(event.key == up)
            this.addToNumber(1)
        else if(event.key == down)
            this.addToNumber(-1)
        else
            base.keyPressEvent(event)

¿Ver? Este código presenta una nueva abstracción: un widget que actúa como un botón, pero con alguna funcionalidad extra. Agregamos esta funcionalidad de manera muy conveniente:

  • Desde que reimplementamos un virtual, nuestra implementación se encapsuló automáticamente en nuestra clase. Si los diseñadores de Qt hubieran hecho keyPressEventuna señal, tendríamos que decidir si heredar QPushButtono simplemente conectarnos externamente a la señal. Pero eso sería estúpido, ya que en Qt siempre se espera que heredes cuando escribes un widget con un comportamiento personalizado (por una buena razón: reutilización / modularidad). Entonces, al hacer keyPressEventun evento, transmiten su intención, que keyPressEventes solo un componente básico de funcionalidad. Si fuera una señal, se vería como algo que enfrenta el usuario, cuando no está destinado a serlo.
  • Dado que la implementación de la clase base de la función está disponible, implementamos fácilmente el patrón de Cadena de responsabilidad manejando nuestros casos especiales (teclas arriba y abajo) y dejando el resto a la clase base. Puede ver que esto sería casi imposible si keyPressEventfuera una señal.

El diseño de Qt está bien pensado: nos hicieron caer en el pozo del éxito al hacer que sea fácil hacer lo correcto y difícil hacer lo incorrecto (al convertir keyPressEvent en un evento).

Por otro lado, considere el uso más simple de QPushButton, simplemente instanciarlo y recibir una notificación cuando se haga clic en él :

button = new QPushButton(this)
connect(button, SIGNAL(clicked()), SLOT(sayHello())

Esto está claramente destinado a ser realizado por el usuario de la clase:

  • si tuviéramos que crear una subclase QPushButtoncada vez que queramos que algún botón nos notifique de un clic, eso requeriría muchas subclases sin una buena razón. Un widget que siempre muestra un "Hola mundo" messageboxcuando se hace clic en él es útil solo en un caso, por lo que no es totalmente reutilizable. Una vez más, no tenemos más remedio que hacer lo correcto, conectándonos externamente.
  • es posible que deseemos conectar varias ranuras clicked()o conectar varias señales sayHello(). Con las señales no hay problemas. Con la subclasificación, tendría que sentarse y reflexionar sobre algunos diagramas de clases hasta que decida un diseño apropiado.

Tenga en cuenta que uno de los lugares que QPushButtonemite clicked()está en su mousePressEvent()implementación. Eso no significa clicked()y mousePressEvent()son intercambiables, solo que están relacionados.

Entonces, las señales y los eventos tienen diferentes propósitos (pero están relacionados en el sentido de que ambos le permiten "suscribirse" a una notificación de algo que está sucediendo).

Stefan Monov
fuente
Entiendo la idea. El evento es por clase, todas las instancias de una clase reaccionarán igual, todas llamarán al mismo evento QClassName ::. Pero la señal es por objeto, cada objeto puede tener su conexión de ranura de señal única.
炸鱼 薯条 德里克
39

No me gustan las respuestas hasta ahora. - Permítanme concentrarme en esta parte de la pregunta:

¿Son los eventos una abstracción de señales / ranuras?

Respuesta corta: no. La respuesta larga plantea una pregunta "mejor": ¿Cómo se relacionan las señales y los eventos?

Un bucle principal inactivo (Qt, por ejemplo) suele estar "atascado" en una llamada select () del sistema operativo. Esa llamada hace que la aplicación "duerma", mientras pasa un montón de sockets o archivos o lo que sea al kernel solicitando: si algo cambia en estos, deje que la llamada select () regrese. - Y el núcleo, como amo del mundo, sabe cuándo sucede eso.

El resultado de esa llamada select () podría ser: nuevos datos en el socket se conectan a X11, un paquete a un puerto UDP que escuchamos ingresó, etc. - Eso no es una señal Qt, ni un evento Qt, y el El bucle principal de Qt decide por sí mismo si convierte los datos nuevos en uno, el otro o lo ignora.

Qt podría llamar a un método (o varios) como keyPressEvent (), convirtiéndolo efectivamente en un evento Qt. O Qt emite una señal, que de hecho busca todas las funciones registradas para esa señal y las llama una tras otra.

Una diferencia de esos dos conceptos es visible aquí: una ranura no tiene voto sobre si otras ranuras registradas en esa señal serán llamadas o no. - Los eventos son más como una cadena, y el controlador de eventos decide si interrumpe esa cadena o no. Las señales se parecen a una estrella o un árbol a este respecto.

Un evento puede dispararse o convertirse por completo en una señal (solo emita una y no llame a "super ()"). Una señal se puede convertir en un evento (llamar a un controlador de eventos).

Lo que abstrae lo que depende del caso: la señal clicked () - abstrae los eventos del mouse (un botón sube y baja sin moverse demasiado). Los eventos de teclado son abstracciones de niveles inferiores (cosas como 果 o é son varias pulsaciones de teclas en mi sistema).

Quizás el focusInEvent () es un ejemplo de lo contrario: podría usar (y por lo tanto abstraer) la señal clicked (), pero no sé si realmente lo hace.

Robert Siemer
fuente
4
Encuentro esta parte: "una ranura no tiene voto sobre si otras ranuras registradas para esa señal serán llamadas o no" muy esclarecedora para la diferencia entre señales y eventos. Sin embargo, tengo una pregunta relacionada: ¿qué son los mensajes y cómo se relacionan los mensajes con las señales y los eventos? (si están relacionados). ¿Son los mensajes una abstracción de señales y eventos? ¿Pueden usarse para describir tales interacciones entre QObjects (u otros objetos?)
user1284631
@axeoth: En Qt, no existen los "mensajes". Bueno, hay un cuadro de mensaje, pero eso es todo.
Reincorporación a Monica el
@KubaOber: Gracias. Me refería a "eventos".
user1284631
3
@axeoth: Entonces tu pregunta es una tontería. Dice: "qué son los eventos y cómo se relacionan los eventos con las señales y los eventos".
Reincorporación a Monica el
@KubaOber: No recuerdo el contexto un año después. De todos modos, parece que estaba preguntando casi lo mismo que el OP: cuál es la diferencia entre eventos y cómo se relacionan los eventos con las señales y las ranuras. Cuál era realmente el caso en ese entonces, IIRC, estaba buscando una manera de envolver las clases controladas por señales / ranuras de Qt en algún tipo de clases que no sean de Qt, así que estaba buscando alguna forma de ordenar la primera desde dentro de la última. Me imagino que estaba considerando enviarles eventos, ya que eso significaba que solo tenía que incluir encabezados Qt y no forzar a mis clases a implementar la señal / ranuras.
user1284631
16

Los eventos son enviados por el bucle de eventos. Cada programa de GUI necesita un bucle de eventos, independientemente de lo que escriba en Windows o Linux, utilizando Qt, Win32 o cualquier otra biblioteca de GUI. Además, cada hilo tiene su propio bucle de eventos. En Qt, "GUI Event Loop" (que es el bucle principal de todas las aplicaciones de Qt) está oculto, pero lo inicias llamando:

QApplication a(argc, argv);
return a.exec();

Los mensajes que el sistema operativo y otras aplicaciones envían a su programa se envían como eventos.

Las señales y las ranuras son mecanismos Qt. En el proceso de compilaciones que usan moc (compilador de metaobjetos), se cambian a funciones de devolución de llamada.

El evento debe tener un receptor, que debe enviarlo. Nadie más debería recibir ese evento.

Se ejecutarán todas las ranuras conectadas a la señal emitida.

No debe pensar en las señales como eventos, porque como puede leer en la documentación de Qt:

Cuando se emite una señal, las ranuras conectadas a ella generalmente se ejecutan inmediatamente, al igual que una llamada de función normal. Cuando esto sucede, el mecanismo de señales y ranuras es totalmente independiente de cualquier bucle de eventos de la GUI.

Cuando envía un evento, debe esperar un tiempo hasta que el bucle de eventos distribuya todos los eventos anteriores. Debido a esto, la ejecución del código después de enviar el evento o la señal es diferente. El código siguiente al envío de un evento se ejecutará inmediatamente. Con los mecanismos de señales y slots depende del tipo de conexión. Normalmente se ejecutará después de todas las ranuras. Usando Qt :: QueuedConnection, se ejecutará inmediatamente, al igual que los eventos. Verifique todos los tipos de conexión en la documentación de Qt .

firescreamer
fuente
Esta frase me da una concisa la comprensión de la diferencia entre la señal de Qt-ranura y eventos:When you send an event, it must wait for time when event loop dispatch all events that came earlier. Because of this, execution of the cod after sending event or signal is different
swdev
7

Hay un artículo que analiza el procesamiento de eventos con cierto detalle: http://www.packtpub.com/article/events-and-signals

Aquí se analiza la diferencia entre eventos y señales:

Los eventos y las señales son dos mecanismos paralelos que se utilizan para lograr lo mismo. Como diferencia general, las señales son útiles cuando se usa un widget, mientras que los eventos son útiles cuando se implementa el widget. Por ejemplo, cuando usamos un widget como QPushButton, estamos más interesados ​​en su señal clicked () que en los eventos de pulsación de ratón o de tecla de bajo nivel que provocaron la emisión de la señal. Pero si estamos implementando la clase QPushButton, estamos más interesados ​​en la implementación de código para eventos de teclas y mouse. Además, generalmente manejamos eventos pero somos notificados por emisiones de señales.

Esta parece ser una forma común de hablar de ello, ya que la respuesta aceptada utiliza algunas de las mismas frases.


Tenga en cuenta que consulte los comentarios útiles a continuación sobre esta respuesta de Kuba Ober, que me hacen preguntarme si podría ser un poco simplista.

Eric
fuente
No veo cómo se usarían dos mecanismos para lograr lo mismo; creo que es una generalización inútil que no ayuda a nadie.
Reincorporación a Monica el
@KubaOber no, ayuda a evitar los detalles de nivel inferior. ¿Diría que Python no es útil porque puede hacer lo mismo escribiendo en código máquina? :)
Eric
2
Estoy diciendo que la primera oración de la cita se está generalizando hasta el punto de ser inútil. Técnicamente, no es incorrecto, pero solo porque podría, si así lo desea, usar el paso de eventos para lograr lo que hace el mecanismo de ranura de señal, y viceversa. Sería muy engorroso. Entonces, no, las ranuras de señal no son lo mismo que los eventos, no logran lo mismo, y el hecho de que ambos existan es fundamental para el éxito de Qt. El ejemplo de botón citado ignora por completo la funcionalidad general del sistema de ranura de señal.
Reincorporación a Monica el
1
"Los eventos y las señales son dos mecanismos paralelos que se utilizan para lograr lo mismo dentro del ámbito limitado de algunos controles / widgets de IU ". La parte en negrita falta de manera crítica y hace que la cita sea inútil. Incluso entonces, uno puede preguntarse cuánto de lo "mismo" se logra realmente: las señales le permiten reaccionar a los eventos de clic sin instalar un filtro de eventos en el widget (o derivar del widget). Hacerlo sin las señales es mucho más engorroso y acopla el código del cliente al control. Eso es mal diseño.
Reintegro a Monica el
1
Los eventos son un concepto ubicuo . La implementación particular en Qt los establece concretamente como estructuras de datos a las que se pasan event. Las señales y ranuras son, concretamente , métodos, mientras que el mecanismo de conexión es una estructura de datos que permite que la señal invoque una o más ranuras listadas como conectadas a ella. Espero que vean que hablar de señales / ranuras como algún "subconjunto" o "variante" de eventos es una tontería, y viceversa. Que en realidad son cosas diferentes que pasan a utilizarse para fines similares en el contexto de algunos widgets. Eso es. Cuanto más generalizas, menos útil te vuelves en mi humilde opinión.
Reincorpora a Monica el
5

TL; DR: las señales y las ranuras son llamadas a métodos indirectos. Los eventos son estructuras de datos. Entonces son animales bastante diferentes.

El único momento en que se unen es cuando se realizan llamadas de tragamonedas a través de los límites del hilo. Los argumentos de la llamada de ranura se empaquetan en una estructura de datos y se envían como un evento a la cola de eventos del hilo receptor. En el hilo de recepción, el QObject::eventmétodo descomprime los argumentos, ejecuta la llamada y posiblemente devuelve el resultado si era una conexión de bloqueo.

Si estamos dispuestos a generalizar hasta el olvido, se podría pensar en los eventos como una forma de invocar el eventmétodo del objeto objetivo . Esta es una llamada a un método indirecto, en cierto modo, pero no creo que sea una forma útil de pensar en ello, incluso si es una afirmación verdadera.

Reincorporar a Monica
fuente
2

Los eventos (en un sentido general de interacción usuario / red) generalmente se manejan en Qt con señales / ranuras, pero las señales / ranuras pueden hacer muchas otras cosas.

QEvent y sus subclases son básicamente pequeños paquetes de datos estandarizados para que el marco se comunique con su código. Si desea prestar atención al mouse de alguna manera, solo tiene que mirar la API QMouseEvent, y los diseñadores de la biblioteca no tienen que reinventar la rueda cada vez que necesite averiguar qué hizo el mouse en algún rincón de la API de Qt.

Es cierto que si está esperando eventos (nuevamente en el caso general) de algún tipo, es casi seguro que su ranura aceptará una subclase QEvent como argumento.

Dicho esto, las señales y las ranuras ciertamente se pueden usar sin QEvents, aunque encontrará que el ímpetu original para activar una señal a menudo será algún tipo de interacción del usuario u otra actividad asincrónica. A veces, sin embargo, su código simplemente llegará a un punto en el que disparar una determinada señal será lo correcto. Por ejemplo, disparar una señal conectada a una barra de progreso durante un proceso largo no implica un QEvent hasta ese momento.

jkerian
fuente
2
"Los eventos generalmente se manejan en Qt con señales / ranuras" - en realidad no ... ¡Los objetos QEvent siempre se pasan a través de virtuales sobrecargados! Por ejemplo, no hay señal "keyDown" en QWidget; en cambio, keyDown es una función virtual.
Stefan Monov
De acuerdo con stefan, en realidad una pieza bastante confusa de Qt
Harald Scheirich
1
@Stefan: Yo diría que pocas aplicaciones Qt anulan keyDown (y en su lugar usan principalmente señales como QAbstractButton :: clicked y QLineEdit :: editFinished) para justificar "típicamente". Ciertamente me he enganchado antes con la entrada del teclado, pero no es la forma habitual de manejar eventos.
jkerian
después de su edición (aclarando lo que quiere decir con "evento"), su publicación ahora es correcta. Sin embargo, me abstengo de reutilizar palabras como esa. Las cosas están lo suficientemente turbias sin llamar señales "un tipo de eventos".
Stefan Monov
2

Encontré esta pregunta mientras leía 'Procesamiento de eventos' de Leow Wee Kheng. También dice:

ingrese la descripción de la imagen aquí

Jasmine Blanchette dice:

La razón principal por la que usarías eventos en lugar de llamadas a funciones estándar, o señales y ranuras, es que los eventos se pueden usar tanto de forma sincrónica como asincrónica (dependiendo de si llamas a sendEvent () o postEvents ()), mientras llamas a una función o invocas una ranura siempre es sincrónica. Otra ventaja de los eventos es que se pueden filtrar.

Francek
fuente
1

Otra consideración pragmática menor: emitir o recibir señales requiere heredar, QObjectmientras que un objeto de cualquier herencia puede publicar o enviar un evento (ya que invocas QCoreApplication.sendEvent()o postEvent()) Esto generalmente no es un problema, pero: para usar señales, PyQt extrañamente requiere QObjectser la primera superclase, y es posible que no desee reorganizar su orden de herencia solo para poder enviar señales).

bootchk
fuente
-1

En mi opinión, los eventos son completamente redundantes y podrían descartarse. No hay ninguna razón por la que las señales no puedan ser reemplazadas por eventos o eventos por señales, excepto que Qt ya está configurado como está. Las señales en cola están envueltas por eventos y los eventos posiblemente podrían estar envueltos por señales, por ejemplo:

connect(this, &MyItem::mouseMove, [this](QMouseEvent*){});

Reemplazaría la mouseMoveEvent()función de conveniencia que se encuentra en QWidget(pero ya no en QQuickItem) y manejaría las mouseMoveseñales que un administrador de escenas emitiría para el elemento. El hecho de que la señal sea emitida en nombre del elemento por alguna entidad externa no es importante y ocurre con bastante frecuencia en el mundo de los componentes Qt, aunque supuestamente no está permitido (los componentes Qt a menudo eluden esta regla). Pero Qt es un conglomerado de muchas decisiones de diseño diferentes y prácticamente grabado en piedra por temor a romper el código antiguo (lo que sucede con bastante frecuencia de todos modos).

usuario1095108
fuente
Los eventos tienen la ventaja de propagarse hacia arriba en la QObjectjerarquía de padres e hijos, cuando es necesario. Las conexiones de señal / ranura son solo una promesa de llamar a una función, directa o indirectamente, cuando se cumple una determinada condición. No hay una jerarquía de manejo asociada con señales y ranuras.
anónimo
Podría implementar reglas de propagación similares con señales. No hay ninguna regla. que una determinada señal no se puede volver a emitir a otro objeto (niño). Puede agregar un acceptedparámetro de referencia a una señal y las ranuras que la manejan o puede tener una estructura como parámetro de referencia con un acceptedcampo. Pero Qt tomó las decisiones de diseño que tomó y ahora están grabadas en piedra.
user1095108
1
Básicamente, solo está reimplementando eventos si lo hace de la manera que sugiere.
Fabio A.
@FabioA. Ese es el punto de la pregunta del OP. Los eventos y las señales pueden / podrían reemplazarse entre sí.
user1095108
Si sus eventos de reimplementación, entonces no está reemplazando eventos. Más aún: las señales se implementan además de los eventos. Necesita una especie de "sistema de eventos" para decirle a otros bucles de eventos, posiblemente en un hilo que no es suyo, que en algún momento en el futuro tiene que ejecutar una función determinada en algún lugar.
Fabio A.