Respostas:
A documentação do Qt provavelmente explica melhor:
No Qt, eventos são objetos, derivados da
QEvent
classe abstrata , que representam coisas que aconteceram dentro de um aplicativo ou como resultado de atividade externa que o aplicativo precisa saber. Os eventos podem ser recebidos e tratados por qualquer instância de umaQObject
subclasse, mas são especialmente relevantes para widgets. Este documento descreve como os eventos são entregues e tratados em um aplicativo típico.
Portanto, eventos e sinais / slots são dois mecanismos paralelos que realizam as mesmas coisas. Em geral, um evento será gerado por uma entidade externa (por exemplo, teclado ou roda do mouse) e será entregue por meio do loop de eventos QApplication
. Em geral, a menos que você configure o código, você não estará gerando eventos. Você pode filtrá-los QObject::installEventFilter()
ou manipular eventos em um objeto de subclasse, substituindo as funções apropriadas.
Sinais e slots são muito mais fáceis de gerar e receber e você pode conectar quaisquer duas QObject
subclasses. Eles são controlados por meio da Metaclasse (dê uma olhada em seu arquivo moc_classname.cpp para mais informações), mas a maior parte da comunicação entre classes que você produzirá provavelmente usará sinais e slots. Os sinais podem ser entregues imediatamente ou adiados por meio de uma fila (se você estiver usando threads).
Um sinal pode ser gerado.
No Qt, sinais e eventos são implementações do padrão Observer . Eles são usados em diferentes situações porque têm diferentes pontos fortes e fracos.
Em primeiro lugar, vamos definir o que queremos dizer com 'evento Qt' exatamente: uma função virtual em uma classe Qt, que você deve reimplementar em uma classe base sua se quiser lidar com o evento. Está relacionado ao padrão Template Method .
Observe como usei a palavra " identificador ". Na verdade, aqui está uma diferença básica entre a intenção de sinais e eventos:
A diferença é que, ao "lidar" com o evento, você assume a responsabilidade de "responder" com um comportamento útil fora da classe. Por exemplo, considere um aplicativo que tem um botão com um número. O aplicativo precisa permitir que o usuário focalize o botão e altere o número pressionando as teclas "para cima" e "para baixo" do teclado. Caso contrário, o botão deve funcionar normalmente QPushButton
(pode ser clicado, etc). No Qt, isso é feito criando seu próprio pequeno "componente" reutilizável (subclasse de QPushButton
), que é reimplementado QWidget::keyPressEvent
. Pseudo-có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)
Vejo? Este código apresenta uma nova abstração: um widget que atua como um botão, mas com alguma funcionalidade extra. Adicionamos esta funcionalidade de forma muito conveniente:
keyPressEvent
um sinal, precisaríamos decidir se herdaríamos QPushButton
ou apenas se conectaríamos externamente ao sinal. Mas isso seria estúpido, já que no Qt sempre se espera que você herde ao escrever um widget com um comportamento personalizado (por uma boa razão - capacidade de reutilização / modularidade). Assim, ao criar keyPressEvent
um evento, eles transmitem sua intenção que keyPressEvent
é apenas um bloco de construção básico da funcionalidade. Se fosse um sinal, pareceria algo voltado para o usuário, quando não era para ser.keyPressEvent
fosse um sinal.O design do Qt é bem pensado - eles nos fizeram cair no poço do sucesso , tornando mais fácil fazer a coisa certa e difícil fazer a coisa errada (tornando keyPressEvent um evento).
Por outro lado, considere o uso mais simples de QPushButton
- apenas instanciá-lo e ser notificado quando for clicado :
button = new QPushButton(this)
connect(button, SIGNAL(clicked()), SLOT(sayHello())
Isso claramente deve ser feito pelo usuário da classe:
QPushButton
toda vez que quisermos que algum botão nos notifique de um clique, isso exigiria muitas subclasses sem um bom motivo! Um widget que sempre mostra um "Hello world" messagebox
quando clicado é útil apenas em um único caso - portanto, não é totalmente reutilizável. Novamente, não temos escolha a não ser fazer a coisa certa - conectando-nos a ela externamente.clicked()
- ou conectar vários sinais para sayHello()
. Com sinais, não há confusão. Com a criação de subclasses, você teria que se sentar e refletir sobre alguns diagramas de classes até decidir sobre um design apropriado.Observe que um dos locais QPushButton
emite clicked()
está em sua mousePressEvent()
implementação. Isso não significa clicked()
e mousePressEvent()
são intercambiáveis - apenas que estão relacionados.
Portanto, sinais e eventos têm finalidades diferentes (mas estão relacionados porque ambos permitem que você "assine" uma notificação de que algo está acontecendo).
Eu não gosto das respostas até agora. - Deixe-me concentrar nesta parte da questão:
Os eventos são uma abstração de sinal / slots?
Resposta curta: não. A resposta longa levanta uma questão “melhor”: como os sinais e eventos estão relacionados?
Um loop principal ocioso (Qt's, por exemplo) está geralmente “preso” em uma chamada select () do sistema operacional. Essa chamada faz o aplicativo “dormir”, enquanto ele passa um monte de sockets ou arquivos ou qualquer coisa para o kernel pedindo: se algo mudar neles, deixe a chamada select () retornar. - E o kernel, como dono do mundo, sabe quando isso acontece.
O resultado dessa chamada select () poderia ser: novos dados no soquete conectar ao X11, um pacote para uma porta UDP que ouvimos entrou, etc. - Esse material não é um sinal Qt, nem um evento Qt, e o O loop principal do Qt decide se transforma os dados novos em um, o outro ou os ignora.
Qt poderia chamar um método (ou vários) como keyPressEvent (), efetivamente transformando-o em um evento Qt. Ou Qt emite um sinal, que de fato procura todas as funções registradas para aquele sinal e as chama uma após a outra.
Uma diferença entre esses dois conceitos é visível aqui: um slot não tem voto sobre se outros slots registrados para aquele sinal serão chamados ou não. - Os eventos são mais como uma cadeia, e o manipulador de eventos decide se interrompe essa cadeia ou não. Os sinais se parecem com uma estrela ou árvore nesse aspecto.
Um evento pode disparar ou ser totalmente transformado em um sinal (apenas emita um, e não chame “super ()”). Um sinal pode ser transformado em um evento (chame um manipulador de eventos).
O que abstrai o que depende do caso: o sinal clicked () - abstrai os eventos do mouse (um botão desce e sobe novamente sem se mover muito). Os eventos de teclado são abstrações de níveis inferiores (coisas como 果 ou é são vários toques de tecla em meu sistema).
Talvez o focusInEvent () seja um exemplo do oposto: ele poderia usar (e, portanto, abstrato) o sinal clicked (), mas não sei se realmente usa.
Os eventos são despachados pelo loop de eventos. Cada programa GUI precisa de um loop de eventos, seja o que for que você escreva no Windows ou Linux, usando Qt, Win32 ou qualquer outra biblioteca GUI. Além disso, cada thread tem seu próprio loop de eventos. No Qt, "GUI Event Loop" (que é o loop principal de todos os aplicativos Qt) está oculto, mas você o inicia chamando:
QApplication a(argc, argv);
return a.exec();
As mensagens que o SO e outros aplicativos enviam ao seu programa são despachadas como eventos.
Sinais e slots são mecanismos Qt. No processo de compilações usando moc (compilador de meta-objeto), eles são alterados para funções de retorno de chamada.
O evento deve ter um receptor, que deve despachá-lo. Ninguém mais deve obter esse evento.
Todos os slots conectados ao sinal emitido serão executados.
Você não deve pensar em sinais como eventos, porque como você pode ler na documentação do Qt:
Quando um sinal é emitido, os slots conectados a ele geralmente são executados imediatamente, como uma chamada de função normal. Quando isso acontece, o mecanismo de sinais e slots é totalmente independente de qualquer loop de evento da GUI.
Quando você envia um evento, ele deve aguardar algum tempo até que o loop de eventos despache todos os eventos anteriores. Por isso, a execução do código após o envio do evento ou sinal é diferente. O código após o envio de um evento será executado imediatamente. Com os sinais e mecanismos de slots, depende do tipo de conexão. Normalmente será executado após todos os slots. Usando Qt :: QueuedConnection, ele será executado imediatamente, assim como os eventos. Verifique todos os tipos de conexão na documentação do 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
Há um artigo que discute o processamento de eventos com alguns detalhes: http://www.packtpub.com/article/events-and-signals
Ele discute a diferença entre eventos e sinais aqui:
Eventos e sinais são dois mecanismos paralelos usados para realizar a mesma coisa. Como uma diferença geral, os sinais são úteis ao usar um widget, enquanto os eventos são úteis ao implementar o widget. Por exemplo, quando estamos usando um widget como QPushButton, estamos mais interessados em seu sinal clicked () do que no pressionamento do mouse de baixo nível ou eventos de pressionamento de tecla que causaram a emissão do sinal. Mas se estivermos implementando a classe QPushButton, estaremos mais interessados na implementação de código para eventos de mouse e tecla. Além disso, normalmente lidamos com eventos, mas somos notificados por emissões de sinal.
Esta parece ser uma forma comum de falar sobre isso, já que a resposta aceita usa algumas das mesmas frases.
Observação, por favor, veja os comentários úteis abaixo sobre esta resposta de Kuba Ober, que me fazem pensar se ela pode ser um pouco simplista.
event
. Os sinais e slots são, concretamente , métodos, enquanto o mecanismo de conexão é uma estrutura de dados que permite ao sinal invocar um ou mais slots listados como conectados a ele. Espero que você veja que falar de sinal / slots como algum "subconjunto" ou "variante" de eventos é um absurdo e vice-versa. Eles realmente são coisas diferentes que acontecem a ser usado para fins semelhantes no contexto de alguns widgets. É isso aí. Quanto mais você generaliza, menos útil você se torna IMHO.
TL; DR: Sinais e slots são chamadas de método indireto. Os eventos são estruturas de dados. Então, eles são animais bem diferentes.
O único momento em que eles se unem é quando as chamadas de slot são feitas através dos limites do thread. Os argumentos de chamada de slot são empacotados em uma estrutura de dados e enviados como um evento para a fila de eventos do segmento de recebimento. Na thread de recebimento, o QObject::event
método descompacta os argumentos, executa a chamada e, possivelmente, retorna o resultado se for uma conexão de bloqueio.
Se estivermos dispostos a generalizar para o esquecimento, podemos pensar nos eventos como uma forma de invocar o event
método do objeto-alvo . Esta é uma chamada de método indireta, de certa forma - mas não acho que seja uma maneira útil de pensar sobre isso, mesmo que seja uma afirmação verdadeira.
Os eventos (em um sentido geral de interação usuário / rede) são normalmente tratados no Qt com sinais / slots, mas os sinais / slots podem fazer muitas outras coisas.
QEvent e suas subclasses são basicamente apenas pequenos pacotes de dados padronizados para a estrutura se comunicar com seu código. Se você quiser prestar atenção ao mouse de alguma forma, você só precisa olhar para a API QMouseEvent, e os designers da biblioteca não precisam reinventar a roda toda vez que você precisa descobrir o que o mouse fez em algum canto do a API Qt.
É verdade que se você estiver esperando por eventos (novamente no caso geral) de algum tipo, seu slot quase certamente aceitará uma subclasse QEvent como um argumento.
Com isso dito, sinais e slots podem certamente ser usados sem QEvents, embora você descubra que o ímpeto original para ativar um sinal frequentemente será algum tipo de interação do usuário ou outra atividade assíncrona. Às vezes, entretanto, seu código chegará a um ponto em que disparar um determinado sinal será a coisa certa a fazer. Por exemplo, disparar um sinal conectado a uma barra de progresso durante um longo processo não envolve um QEvent até aquele ponto.
Eu encontrei essa pergunta ao ler 'Processamento de eventos' por Leow Wee Kheng. Também diz:
Jasmine Blanchette diz:
A principal razão pela qual você usaria eventos em vez de chamadas de função padrão, ou sinais e slots, é que os eventos podem ser usados de forma síncrona e assíncrona (dependendo se você chama sendEvent () ou postEvents ()), enquanto chama uma função ou invoca um slot é sempre síncrono. Outra vantagem dos eventos é que eles podem ser filtrados.
Outra consideração pragmática menor: emitir ou receber sinais requer herança, QObject
enquanto um objeto de qualquer herança pode postar ou enviar um evento (desde que você invoque QCoreApplication.sendEvent()
ou postEvent()
). Isso geralmente não é um problema, mas: para usar sinais PyQt estranhamente requer QObject
para ser a primeira superclasse, e você pode não querer reorganizar sua ordem de herança apenas para poder enviar sinais.)
Na minha opinião, os eventos são completamente redundantes e podem ser descartados. Não há razão para que os sinais não possam ser substituídos por eventos ou eventos por sinais, exceto que o Qt já está configurado como está. Sinais enfileirados são envolvidos por eventos e eventos podem ser concebidos por sinais, por exemplo:
connect(this, &MyItem::mouseMove, [this](QMouseEvent*){});
Substituiria a mouseMoveEvent()
função de conveniência encontrada em QWidget
(mas não QQuickItem
mais) e lidaria com os mouseMove
sinais que um gerenciador de cena emitiria para o item. O fato de o sinal ser emitido em nome do item por alguma entidade externa não é importante e acontece com bastante frequência no mundo dos componentes Qt, embora supostamente não seja permitido (componentes Qt freqüentemente contornam essa regra). Mas Qt é um conglomerado de muitas decisões de design diferentes e praticamente definido por medo de quebrar o código antigo (o que acontece com bastante frequência de qualquer maneira).
QObject
hierarquia pai-filho, quando necessário. As conexões de sinal / slot são apenas uma promessa de chamar uma função, direta ou indiretamente, quando uma determinada condição for atendida. Não há uma hierarquia de manuseio associada com sinais e slots.
accepted
parâmetro de referência a um sinal e os slots que o manipulam ou pode ter uma estrutura como parâmetro de referência com um accepted
campo. Mas o Qt fez as escolhas de design que fez e agora elas estão imutáveis.