Respuestas:
La documentación de Qt probablemente lo explica mejor:
En Qt, los eventos son objetos, derivados de la
QEvent
clase 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 unaQObject
subclase, 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 QObject
subclases 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.
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:
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:
keyPressEvent
una señal, tendríamos que decidir si heredar QPushButton
o 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 keyPressEvent
un evento, transmiten su intención, que keyPressEvent
es 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.keyPressEvent
fuera 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:
QPushButton
cada 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" messagebox
cuando 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.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 QPushButton
emite 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).
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.
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 .
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
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.
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.
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::event
mé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 event
mé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.
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.
Encontré esta pregunta mientras leía 'Procesamiento de eventos' de Leow Wee Kheng. También dice:
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.
Otra consideración pragmática menor: emitir o recibir señales requiere heredar, QObject
mientras 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 QObject
ser la primera superclase, y es posible que no desee reorganizar su orden de herencia solo para poder enviar señales).
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 mouseMove
señ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).
QObject
jerarquí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.
accepted
pará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 accepted
campo. Pero Qt tomó las decisiones de diseño que tomó y ahora están grabadas en piedra.