Cabana: dynamic sorting message list (#26150)

pull/26172/head^2
Dean Lee 3 years ago committed by GitHub
parent 4bf86742e6
commit 85d39ec34e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 106
      tools/cabana/messageswidget.cc
  2. 16
      tools/cabana/messageswidget.h

@ -6,7 +6,6 @@
#include <QHeaderView>
#include <QLineEdit>
#include <QPushButton>
#include <QSortFilterProxyModel>
#include <QTextEdit>
#include <QVBoxLayout>
@ -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);
}
}

@ -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<std::unique_ptr<Message>> msgs;
QString filter_str;
int sort_column = 0;
Qt::SortOrder sort_order = Qt::AscendingOrder;
};
class MessagesWidget : public QWidget {

Loading…
Cancel
Save