Mecanismos de Distribuição e Processamento de Eventos no Qt

Fluxo de Processamento de Eventos

No Qt, o ciclo de vida de um evento segue um caminho rigoroso desde a sua geração até o consumo final. O processo pode ser resumido nas seguintes etapas:

  1. Origem do Evento: Pode ser gerado pelo sistema (mouse, teclado), por timers, notificações de rede/IO ou via código do desenvolvedor.
  2. Modo de Envio:
    • Síncrono (sendEvent): O evento é enviado diretamente ao reecptor. A execução é bloqueada até que o processamento termine. O programador deve gerenciar a memória do objeto de evento.
    • Assíncrono (postEvent): O evento é postado em uma fila (QEventQueue). O controle retorna imediatamente ao chamador. O QEventLoop retira o evento da fila posteriormente e o envia ao notify(). O Qt gerencia a desalocação da memória automaticamente.
  3. Distribuição (QCoreApplication::notify): O ponto central de entrada da aplicação. Antes de entregar o evento ao objeto, ele verifica os filtros de eventos instalados.
  4. Filtros de Eventos: Se um filtro retornar true, o evento é interrompido. Caso contrário, ele prossegue para o método event() do objeto.
  5. Distribuição de Nível de Objeto (QObject::event): Atua como um distribuidor interno que encaminha o evento para handlers específicos (como mousePressEvent ou keyPressEvent) com base no tipo do evento.

Envio Síncrono vs. Assíncrono

O envio imediato utiliza sendEvent. É recomendável criar o evento na stack para evitar vazamentos de memória, já que o objeto não é deletado automaticamente pelo framework.

// Envio síncrono (bloqueante)
QKeyEvent pressionarTecla(QEvent::KeyPress, Qt::Key_Escape, Qt::NoModifier);
bool foiProcessado = QCoreApplication::sendEvent(meuWidget, &pressionarTecla);

Para envio assíncrono, utiliza-se postEvent. O evento deve obrigatoriamente ser alocado no heap (usando new), pois o Qt assumirá a propriedade do ponteiro e o excluirá após o processamento.

// Envio assíncrono (não bloqueante)
QWheelEvent *eventoRoda = new QWheelEvent(QPointF(10, 10), QPointF(10, 10), QPoint(0, 120), QPoint(0, 120), Qt::NoButton, Qt::NoModifier, Qt::NoPhase, false);
QCoreApplication::postEvent(areaScroll, eventoRoda);
// O objeto 'eventoRoda' será deletado pelo Qt automaticamente

Filtros de Eventos (Event Filters)

Os filtros de eventos funcionam como interceptadores. Qualquer classe derivada de QObject pode implementar o método virtual eventFilter(). Isso permite que um objeto monitore e manipule eventos destinados a outro objeto antes que este último os receba.

Características Principais:

  • Retornar true interrompe a propagação (o evento é "comido").
  • Retornar false permite que o evento continue para o próximo filtro ou para o destinatário original.
  • Múltiplos filtros podem ser instalados em um único objeto; o último a ser instalado é o primeiro a ser executado.
// Implementação de um interceptador de teclado
bool MonitorTeclado::eventFilter(QObject *alvo, QEvent *evento)
{
    if (evento->type() == QEvent::KeyPress) {
        QKeyEvent *keyEv = static_cast<QKeyEvent *>(evento);
        if (keyEv->key() == Qt::Key_Return) {
            qDebug() << "Tecla Enter interceptada no objeto:" << alvo->objectName();
            return true; // Bloqueia o processamento padrão
        }
    }
    // Propaga para o comportamento padrão
    return QObject::eventFilter(alvo, evento);
}

Filtros Globais

Para capturar eventos em toda a aplicação, pode-se instalar um filtro no objeto global qApp. Isso é útil para implementar logs de atividade, gerenciamento de inatividade ou atalhos globais personalizados.

// No construtor da MainWindow ou no main.cpp
qApp->installEventFilter(this);

// Processando eventos de todos os widgets da app
bool MainWindow::eventFilter(QObject *obj, QEvent *ev)
{
    if (ev->type() == QEvent::MouseButtonPress) {
        qDebug() << "Clique detectado em algum lugar da aplicação.";
    }
    return QMainWindow::eventFilter(obj, ev);
}

Sobrescrita do Método event()

Embora seja possível processar eventos sobrescrevendo handlers específicos (como paintEvent), o método event() oferece um nível de controle intermediário. Ele é útil para lidar com eventos personalizados ou para agrupar lógica de diferentes tipos de eventos em um único local antes da distribuição para as funções de conveniência.

bool MeuComponenteCustomizado::event(QEvent *e)
{
    if (e->type() == QEvent::HoverEnter) {
        atualizarEstiloDestaque(true);
        return true; 
    }
    
    // Tratamento de eventos de toque específicos
    if (e->type() == QEvent::TouchBegin) {
        e->accept();
        return true;
    }

    // Encaminha eventos não tratados para a classe base
    return QWidget::event(e);
}

Observação de Segurança: Se um objeto for deletado dentro de um eventFilter, é obrigatório retornar true. Caso contrário, o sistema de eventos tentará entregar o evento a um objeto que não existe mais, resultando em falha de segmentação.

Tags: Qt C++ QEvent EventHandling GUI-Development

Publicado em 7-25 03:09