From cb76fedcbc8e419e2b945baa56ac3f986a9e79a3 Mon Sep 17 00:00:00 2001 From: Timotej Lazar Date: Wed, 1 Sep 2021 17:13:51 +0200 Subject: Implement event model in C++ Filtering events in JS is too slow with >20,000 events. This moves the event data model into C++. --- event_list.cpp | 141 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 event_list.cpp (limited to 'event_list.cpp') diff --git a/event_list.cpp b/event_list.cpp new file mode 100644 index 0000000..2654bef --- /dev/null +++ b/event_list.cpp @@ -0,0 +1,141 @@ +#include "event_list.h" + +#include +#include + +Qt::ItemFlags EventList::flags(const QModelIndex&) const +{ + return Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemNeverHasChildren; +} + +QHash EventList::roleNames() const +{ + static const QHash roles{ + {Role::Time, "time"}, + {Role::Tag, "tag"}, + {Role::Values, "values"}, + }; + return roles; +} + +int EventList::rowCount(const QModelIndex&) const +{ + return events.size(); +} + +QVariant EventList::data(const QModelIndex& index, int role) const +{ + const auto& event = events[index.row()]; + switch (role) { + case Role::Time: + return event.time; + case Role::Tag: + return event.tag; + case Role::Values: + return event.values; + default: + return {}; + } +} + +bool EventList::setData(const QModelIndex& index, const QVariant& value, int role) +{ + auto& event = events[index.row()]; + switch (role) { + case Role::Time: + event.time = value.toLongLong(); + break; + case Role::Tag: + event.tag = value.toString(); + break; + case Role::Values: + event.values = value.value().toVariant().toMap(); + break; + default: + return false; + } + emit dataChanged(index, index, {role}); + return true; +} + +int EventList::insert(const int time) +{ + int row = time == -1 ? rowCount() : find(time); + beginInsertRows(QModelIndex{}, row, row); + events.insert(row, {time}); + endInsertRows(); + return row; +} + +bool EventList::removeRows(int row, int count, const QModelIndex&) +{ + beginRemoveRows({}, row, row + count - 1); + while (row < events.size() && count-- > 0) + events.removeAt(row); + endRemoveRows(); + return count == -1; +} + +void EventList::load(const QJsonObject& json) +{ + const auto& jsonTags = json["tags"].toArray(); + if (!jsonTags.isEmpty()) { + tags = {}; + tagsOrder.clear(); + for (int i = 0; i < jsonTags.size(); i++) { + const auto name = jsonTags[i]["tag"].toString(); + tags[name] = jsonTags[i].toObject(); + tagsOrder.append(name); + } + emit tagsChanged(); + } + + const auto& jsonEvents = json["events"].toArray(); + if (!jsonEvents.isEmpty()) { + beginResetModel(); + events.clear(); + for (int i = 0; i < jsonEvents.size(); i++) { + auto event = jsonEvents[i].toObject().toVariantMap(); + events.append({ + event["time"].toLongLong(), + event["tag"].toString(), + event[event.contains("values") ? "values" : "fields"].toMap(), + }); + } + endResetModel(); + } +} + +QJsonObject EventList::save() const +{ + QJsonArray jsonEvents; + for (const auto& event : events) { + jsonEvents.append(QJsonObject{ + {"time", event.time}, + {"tag", event.tag}, + {"values", QJsonObject::fromVariantMap(event.values)} + }); + } + + QJsonArray jsonTags; + for (int i = 0; i < tagsOrder.size(); i++) + jsonTags.append(tags[tagsOrder[i]].toObject()); + + return {{"tags", jsonTags}, {"events", jsonEvents}}; +} + +// Return the index of the last event not later than given time. +// Assumes events are sorted by time. +int EventList::find(long long time) const +{ + int low = 0; + int high = events.size() - 1; + while (low <= high) { + int mid = (low + high) / 2; + if (events[mid].time <= time) + low = mid + 1; + else + high = mid - 1; + } + return low; +} -- cgit v1.3