From 85d39ec34ef80ab292eae0ac18600037f7903e44 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Fri, 21 Oct 2022 02:23:27 +0800 Subject: [PATCH] Cabana: dynamic sorting message list (#26150) --- tools/cabana/messageswidget.cc | 106 +++++++++++++++++++++++---------- tools/cabana/messageswidget.h | 16 ++++- 2 files changed, 89 insertions(+), 33 deletions(-) diff --git a/tools/cabana/messageswidget.cc b/tools/cabana/messageswidget.cc index dfe665d750..c34d883b3f 100644 --- a/tools/cabana/messageswidget.cc +++ b/tools/cabana/messageswidget.cc @@ -6,7 +6,6 @@ #include #include #include -#include #include #include @@ -49,27 +48,23 @@ MessagesWidget::MessagesWidget(QWidget *parent) : QWidget(parent) { // message table table_widget = new QTableView(this); model = new MessageListModel(this); - QSortFilterProxyModel *proxy_model = new QSortFilterProxyModel(this); - proxy_model->setSourceModel(model); - proxy_model->setFilterCaseSensitivity(Qt::CaseInsensitive); - proxy_model->setDynamicSortFilter(false); - table_widget->setModel(proxy_model); + table_widget->setModel(model); table_widget->setSelectionBehavior(QAbstractItemView::SelectRows); table_widget->setSelectionMode(QAbstractItemView::SingleSelection); table_widget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); table_widget->setSortingEnabled(true); + table_widget->sortByColumn(0, Qt::AscendingOrder); table_widget->setColumnWidth(0, 250); table_widget->setColumnWidth(1, 80); table_widget->setColumnWidth(2, 80); table_widget->horizontalHeader()->setStretchLastSection(true); table_widget->verticalHeader()->hide(); - table_widget->sortByColumn(0, Qt::AscendingOrder); main_layout->addWidget(table_widget); // signals/slots - QObject::connect(filter, &QLineEdit::textChanged, proxy_model, &QSortFilterProxyModel::setFilterFixedString); + QObject::connect(filter, &QLineEdit::textChanged, model, &MessageListModel::setFilterString); QObject::connect(can, &CANMessages::eventsMerged, this, &MessagesWidget::loadDBCFromFingerprint); - QObject::connect(can, &CANMessages::updated, model, &MessageListModel::updateState); + QObject::connect(can, &CANMessages::updated, [this]() { model->updateState(); }); QObject::connect(dbc_combo, SIGNAL(activated(const QString &)), SLOT(loadDBCFromName(const QString &))); QObject::connect(load_from_paste, &QPushButton::clicked, this, &MessagesWidget::loadDBCFromPaste); QObject::connect(save_btn, &QPushButton::clicked, [=]() { @@ -122,21 +117,15 @@ QVariant MessageListModel::headerData(int section, Qt::Orientation orientation, QVariant MessageListModel::data(const QModelIndex &index, int role) const { if (role == Qt::DisplayRole) { - auto it = std::next(can->can_msgs.begin(), index.row()); - if (it != can->can_msgs.end() && !it.value().empty()) { - const QString &msg_id = it.key(); - switch (index.column()) { - case 0: { - auto msg = dbc()->msg(msg_id); - return msg ? msg->name.c_str() : "untitled"; - } - case 1: return msg_id; - case 2: return can->counters[msg_id]; - case 3: return toHex(it.value().front().dat); - } + const auto &m = msgs[index.row()]; + switch (index.column()) { + case 0: return m->name; + case 1: return m->id; + case 2: return can->counters[m->id]; + case 3: return toHex(can->lastMessage(m->id).dat); } } else if (role == Qt::UserRole) { - return std::next(can->can_msgs.begin(), index.row()).key(); + return msgs[index.row()]->id; } else if (role == Qt::FontRole) { if (index.column() == 3) { return QFontDatabase::systemFont(QFontDatabase::FixedFont); @@ -145,20 +134,77 @@ QVariant MessageListModel::data(const QModelIndex &index, int role) const { return {}; } -void MessageListModel::updateState() { - int prev_row_count = row_count; - row_count = can->can_msgs.size(); - int delta = row_count - prev_row_count; +bool MessageListModel::updateMessages(bool sort) { + if (msgs.size() == can->can_msgs.size() && filter_str.isEmpty() && !sort) + return false; + + // update message list + int i = 0; + bool search_id = filter_str.contains(':'); + for (auto it = can->can_msgs.begin(); it != can->can_msgs.end(); ++it) { + const Msg *msg = dbc()->msg(it.key()); + QString msg_name = msg ? msg->name.c_str() : "untitled"; + if (!filter_str.isEmpty() && !(search_id ? it.key() : msg_name).contains(filter_str, Qt::CaseInsensitive)) + continue; + auto &m = i < msgs.size() ? msgs[i] : msgs.emplace_back(new Message); + m->id = it.key(); + m->name = msg_name; + ++i; + } + msgs.resize(i); + + if (sort_column == 0) { + std::sort(msgs.begin(), msgs.end(), [this](auto &l, auto &r) { + bool ret = l->name < r->name || (l->name == r->name && l->id < r->id); + return sort_order == Qt::AscendingOrder ? ret : !ret; + }); + } else if (sort_column == 1) { + std::sort(msgs.begin(), msgs.end(), [this](auto &l, auto &r) { + return sort_order == Qt::AscendingOrder ? l->id < r->id : l->id > r->id; + }); + } else if (sort_column == 2) { + std::sort(msgs.begin(), msgs.end(), [this](auto &l, auto &r) { + uint32_t lcount = can->counters[l->id], rcount = can->counters[r->id]; + bool ret = lcount < rcount || (lcount == rcount && l->id < r->id); + return sort_order == Qt::AscendingOrder ? ret : !ret; + }); + } + return true; +} + +void MessageListModel::updateState(bool sort) { + int prev_row_count = msgs.size(); + auto prev_idx = persistentIndexList(); + QString selected_msg_id = prev_idx.empty() ? "" : prev_idx[0].data(Qt::UserRole).toString(); + + bool msg_updated = updateMessages(sort); + int delta = msgs.size() - prev_row_count; if (delta > 0) { - beginInsertRows({}, prev_row_count, row_count - 1); + beginInsertRows({}, prev_row_count, msgs.size() - 1); endInsertRows(); } else if (delta < 0) { - beginRemoveRows({}, row_count, prev_row_count - 1); + beginRemoveRows({}, msgs.size(), prev_row_count - 1); endRemoveRows(); } - if (row_count > 0) { - emit dataChanged(index(0, 0), index(row_count - 1, 3), {Qt::DisplayRole}); + if (!msgs.empty()) { + if (msg_updated && !prev_idx.isEmpty()) { + // keep selection + auto it = std::find_if(msgs.begin(), msgs.end(), [&](auto &m) { return m->id == selected_msg_id; }); + if (it != msgs.end()) { + for (auto &idx : prev_idx) + changePersistentIndex(idx, index(std::distance(msgs.begin(), it), idx.column())); + } + } + emit dataChanged(index(0, 0), index(msgs.size() - 1, 3), {Qt::DisplayRole}); + } +} + +void MessageListModel::sort(int column, Qt::SortOrder order) { + if (column != columnCount() - 1) { + sort_column = column; + sort_order = order; + updateState(true); } } diff --git a/tools/cabana/messageswidget.h b/tools/cabana/messageswidget.h index bef15c2cf7..7b6c8e53cf 100644 --- a/tools/cabana/messageswidget.h +++ b/tools/cabana/messageswidget.h @@ -25,11 +25,21 @@ public: QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override { return 4; } QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; - int rowCount(const QModelIndex &parent = QModelIndex()) const override { return row_count; } - void updateState(); + int rowCount(const QModelIndex &parent = QModelIndex()) const override { return msgs.size(); } + void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override; + void updateState(bool sort = false); + void setFilterString(const QString &string) { filter_str = string; } private: - int row_count = 0; + bool updateMessages(bool sort); + + struct Message { + QString id, name; + }; + std::vector> msgs; + QString filter_str; + int sort_column = 0; + Qt::SortOrder sort_order = Qt::AscendingOrder; }; class MessagesWidget : public QWidget {