No momento, minha tarefa é desenvolver um aplicativo para desenho. Para resolver esse problema, foi decidido usar QT/C++ . Como esta é a primeira vez na minha carreira que estou desenvolvendo um aplicativo GUI que consiste em um grande número de módulos, plug-ins e lógica, foi decidido usar um EventManager para comunicar todos os módulos entre si.
Quase todos os módulos (plugins) não sabem sobre a existência de outros módulos e se comunicam com o aplicativo principal usando o mesmo gerenciador de eventos enviando eventos para ele ou esperando por eventos. Mas há plugins que enviam eventos com muita frequência, o que afeta o desempenho. Como otimizar o gerenciador de eventos para melhor desempenho. Como exemplo, apresento abaixo um instantâneo de como os módulos existem isolados uns dos outros e se comunicam com a "Página de desenho" apenas enviando mensagens.
O Gerenciador de Eventos em si é uma classe estática simples que recebe uma string como o tipo de evento e QVariant como o parâmetro deste evento!
// event-manager.h
#include <QObject>
#include <functional>
class EventManager final :
public QObject
{
Q_OBJECT
public:
using EventCallback = std::function<void(const QString&, const QVariant&)>;
void triggerEvent(const QString &eventName, const QVariant& value) noexcept;
void registerEvent(const QString &eventName, QObject *receiver, EventCallback) noexcept;
void unregisterEvent(const QString &eventName, QObject *receiver) noexcept;
static EventManager& instance() noexcept;
signals:
void eventTriggered(const QString &eventName, const QVariant& value);
private:
explicit EventManager(QObject *parent = nullptr);
virtual ~EventManager() override;
private:
struct Private;
Private *d_ptr;
};
// event-manager.cpp
#include "event-manager.cpp"
#include <QHash>
#include <QMap>
#include <QMutex>
#include <QMutexLocker>
#include <QPointer>
#include <QVariant>
struct EventManager::Private {
struct EventSlot {
QPointer<QObject> receiver;
EventCallback callback;
};
QMutex mutex;
QMap<QString, QList<EventSlot>> events;
};
EventManager::EventManager(QObject *aParent) :
QObject(aParent),
d_ptr(new EventManager::Private)
{}
EventManager::~EventManager()
{
delete d_ptr;
}
void EventManager::triggerEvent(const QString& aEventName, const QVariant& aValue) noexcept
{
if (!aEventName.isEmpty()) {
QMutexLocker locker(&d_ptr->mutex);
const bool found = d_ptr->events.contains(aEventName);
locker.unlock();
if (!found) { return; }
locker.relock();
QList<Private::EventSlot> eventSlots;
eventSlots = d_ptr->events[aEventName];
locker.unlock();
for (const auto& eventSlot : std::as_const(eventSlots)) {
if (!eventSlot.receiver.isNull()) {
QMetaObject::invokeMethod(eventSlot.receiver,
[callback = eventSlot.callback, aEventName, aValue]() {
callback(aEventName, aValue);
}, Qt::QueuedConnection);
}
}
emit eventTriggered(aEventName, aValue);
}
}
void EventManager::registerEvent(const QString& aEventName, QObject* aReceiver, EventCallback aCallback) noexcept
{
if (!aEventName.isEmpty() && aReceiver) {
QMutexLocker locker(&d_ptr->mutex);
Private::EventSlot eventSlot;
eventSlot.callback = std::move(aCallback);
eventSlot.receiver = aReceiver;
d_ptr->events[aEventName].append(eventSlot);
}
}
void EventManager::unregisterEvent(const QString &aEventName, QObject *aReceiver) noexcept
{
if (!aEventName.isEmpty() && aReceiver) {
QMutexLocker locker(&d_ptr->mutex);
if (d_ptr->events.contains(aEventName)) {
auto &eventSlots = d_ptr->events[aEventName];
eventSlots.erase(std::remove_if(eventSlots.begin(), eventSlots.end(),
[aReceiver](const Private::EventSlot &slot) {
return slot.receiver == aReceiver;
}), eventSlots.end());
}
}
}
EventManager& EventManager::instance() noexcept
{
static EventManager EventManager;
return EventManager;
}
Na imagem acima, "ToolBarWidget1" envia um evento para "ZoomIn", e "Drawing Page" escuta esse evento e, ao receber tal evento, reage a ele. Não há problemas, mas assim que preciso, por exemplo, enviar um evento de movimento do mouse da "Drawing page", aparecem travamentos e o programa cai de desempenho. Não é recomendado conectar diretamente a "Drawing page" com módulos, porque dessa forma evitamos a vinculação rígida de módulos. Como isso pode ser otimizado?
PS : 1. Exemplo de envio de um evento:
EventManager::instance().triggerEvent("toolBarWidget1", "zoomIn");
PS : 2. Exemplo de tratamento de eventos:
EventManager::instance().registerEvent("toolBarWidget1",
this, [this](const QString& eventType, const QVariant& eventValue) {
(void) eventType;
if (eventValue.toString() == "zoomIn") {
drawingPage->zoomIn();
}
});