cabana: gray out inactive messages (#32121)

* improve message list

remove TODO

* improve sort

* remove translate

* fix seeking issue
pull/32149/head
Dean Lee 1 year ago committed by GitHub
parent a623552127
commit bf61e92518
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 1
      tools/cabana/mainwin.cc
  2. 160
      tools/cabana/messageswidget.cc
  3. 11
      tools/cabana/messageswidget.h
  4. 12
      tools/cabana/utils/util.cc
  5. 38
      tools/replay/replay.cc
  6. 1
      tools/replay/replay.h

@ -179,6 +179,7 @@ void MainWindow::createDockWindows() {
void MainWindow::createDockWidgets() { void MainWindow::createDockWidgets() {
messages_widget = new MessagesWidget(this); messages_widget = new MessagesWidget(this);
messages_dock->setWidget(messages_widget); messages_dock->setWidget(messages_widget);
QObject::connect(messages_widget, &MessagesWidget::titleChanged, messages_dock, &QDockWidget::setWindowTitle);
// right panel // right panel
charts_widget = new ChartsWidget(this); charts_widget = new ChartsWidget(this);

@ -6,16 +6,29 @@
#include <QCheckBox> #include <QCheckBox>
#include <QHBoxLayout> #include <QHBoxLayout>
#include <QPainter> #include <QPainter>
#include <QPalette>
#include <QPushButton> #include <QPushButton>
#include <QScrollBar> #include <QScrollBar>
#include <QVBoxLayout> #include <QVBoxLayout>
#include "tools/cabana/commands.h" #include "tools/cabana/commands.h"
static bool isMessageActive(const MessageId &id) {
if (auto dummy_stream = dynamic_cast<DummyStream *>(can)) {
return true;
}
if (id.source == INVALID_SOURCE) {
return false;
}
// Check if the message is active based on time difference and frequency
const auto &m = can->lastMessage(id);
float delta = can->currentSec() - m.ts;
return (m.freq == 0 && delta < 1.5) || (m.freq > 0 && ((delta - 1.0 / settings.fps) < (5.0 / m.freq)));
}
MessagesWidget::MessagesWidget(QWidget *parent) : menu(new QMenu(this)), QWidget(parent) { MessagesWidget::MessagesWidget(QWidget *parent) : menu(new QMenu(this)), QWidget(parent) {
QVBoxLayout *main_layout = new QVBoxLayout(this); QVBoxLayout *main_layout = new QVBoxLayout(this);
main_layout->setContentsMargins(0, 0, 0, 0); main_layout->setContentsMargins(0, 0, 0, 0);
main_layout->setSpacing(0);
// toolbar // toolbar
main_layout->addWidget(createToolBar()); main_layout->addWidget(createToolBar());
// message table // message table
@ -39,23 +52,10 @@ MessagesWidget::MessagesWidget(QWidget *parent) : menu(new QMenu(this)), QWidget
header->setStretchLastSection(true); header->setStretchLastSection(true);
header->setContextMenuPolicy(Qt::CustomContextMenu); header->setContextMenuPolicy(Qt::CustomContextMenu);
// suppress
QHBoxLayout *suppress_layout = new QHBoxLayout();
suppress_layout->addWidget(suppress_add = new QPushButton("Suppress Highlighted"));
suppress_layout->addWidget(suppress_clear = new QPushButton());
suppress_clear->setToolTip(tr("Clear suppressed"));
suppress_layout->addStretch(1);
QCheckBox *suppress_defined_signals = new QCheckBox(tr("Suppress Signals"), this);
suppress_defined_signals->setToolTip(tr("Suppress defined signals"));
suppress_defined_signals->setChecked(settings.suppress_defined_signals);
suppress_layout->addWidget(suppress_defined_signals);
main_layout->addLayout(suppress_layout);
// signals/slots // signals/slots
QObject::connect(menu, &QMenu::aboutToShow, this, &MessagesWidget::menuAboutToShow); QObject::connect(menu, &QMenu::aboutToShow, this, &MessagesWidget::menuAboutToShow);
QObject::connect(header, &MessageViewHeader::customContextMenuRequested, this, &MessagesWidget::headerContextMenuEvent); QObject::connect(header, &MessageViewHeader::customContextMenuRequested, this, &MessagesWidget::headerContextMenuEvent);
QObject::connect(view->horizontalScrollBar(), &QScrollBar::valueChanged, header, &MessageViewHeader::updateHeaderPositions); QObject::connect(view->horizontalScrollBar(), &QScrollBar::valueChanged, header, &MessageViewHeader::updateHeaderPositions);
QObject::connect(suppress_defined_signals, &QCheckBox::stateChanged, can, &AbstractStream::suppressDefinedSignals);
QObject::connect(can, &AbstractStream::msgsReceived, model, &MessageListModel::msgsReceived); QObject::connect(can, &AbstractStream::msgsReceived, model, &MessageListModel::msgsReceived);
QObject::connect(dbc(), &DBCManager::DBCFileChanged, model, &MessageListModel::dbcModified); QObject::connect(dbc(), &DBCManager::DBCFileChanged, model, &MessageListModel::dbcModified);
QObject::connect(UndoStack::instance(), &QUndoStack::indexChanged, model, &MessageListModel::dbcModified); QObject::connect(UndoStack::instance(), &QUndoStack::indexChanged, model, &MessageListModel::dbcModified);
@ -75,9 +75,6 @@ MessagesWidget::MessagesWidget(QWidget *parent) : menu(new QMenu(this)), QWidget
} }
} }
}); });
QObject::connect(suppress_add, &QPushButton::clicked, this, &MessagesWidget::suppressHighlighted);
QObject::connect(suppress_clear, &QPushButton::clicked, this, &MessagesWidget::suppressHighlighted);
suppressHighlighted();
setWhatsThis(tr(R"( setWhatsThis(tr(R"(
<b>Message View</b><br/> <b>Message View</b><br/>
@ -91,18 +88,30 @@ MessagesWidget::MessagesWidget(QWidget *parent) : menu(new QMenu(this)), QWidget
)")); )"));
} }
QToolBar *MessagesWidget::createToolBar() { QWidget *MessagesWidget::createToolBar() {
QToolBar *toolbar = new QToolBar(this); QWidget *toolbar = new QWidget(this);
toolbar->setIconSize({12, 12}); QHBoxLayout *layout = new QHBoxLayout(toolbar);
toolbar->addWidget(num_msg_label = new QLabel(this)); layout->setContentsMargins(0, 9, 0, 0);
num_msg_label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); layout->addWidget(suppress_add = new QPushButton("Suppress Highlighted"));
layout->addWidget(suppress_clear = new QPushButton());
suppress_clear->setToolTip(tr("Clear suppressed"));
layout->addStretch(1);
QCheckBox *suppress_defined_signals = new QCheckBox(tr("Suppress Signals"), this);
suppress_defined_signals->setToolTip(tr("Suppress defined signals"));
suppress_defined_signals->setChecked(settings.suppress_defined_signals);
layout->addWidget(suppress_defined_signals);
auto views_btn = toolbar->addAction(utils::icon("three-dots"), tr("View...")); auto view_button = new ToolButton("three-dots", tr("View..."));
views_btn->setMenu(menu); view_button->setMenu(menu);
auto view_button = qobject_cast<QToolButton *>(toolbar->widgetForAction(views_btn));
view_button->setPopupMode(QToolButton::InstantPopup); view_button->setPopupMode(QToolButton::InstantPopup);
view_button->setToolButtonStyle(Qt::ToolButtonIconOnly);
view_button->setStyleSheet("QToolButton::menu-indicator { image: none; }"); view_button->setStyleSheet("QToolButton::menu-indicator { image: none; }");
layout->addWidget(view_button);
QObject::connect(suppress_add, &QPushButton::clicked, this, &MessagesWidget::suppressHighlighted);
QObject::connect(suppress_clear, &QPushButton::clicked, this, &MessagesWidget::suppressHighlighted);
QObject::connect(suppress_defined_signals, &QCheckBox::stateChanged, can, &AbstractStream::suppressDefinedSignals);
suppressHighlighted();
return toolbar; return toolbar;
} }
@ -113,7 +122,7 @@ void MessagesWidget::updateTitle() {
auto m = dbc()->msg(item.id); auto m = dbc()->msg(item.id);
return m ? std::make_pair(pair.first + 1, pair.second + m->sigs.size()) : pair; return m ? std::make_pair(pair.first + 1, pair.second + m->sigs.size()) : pair;
}); });
num_msg_label->setText(tr("%1 Messages (%2 DBC Messages, %3 Signals)") emit titleChanged(tr("%1 Messages (%2 DBC Messages, %3 Signals)")
.arg(model->items_.size()).arg(stats.first).arg(stats.second)); .arg(model->items_.size()).arg(stats.first).arg(stats.second));
} }
@ -156,6 +165,10 @@ void MessagesWidget::menuAboutToShow() {
auto action = menu->addAction(tr("Multi-Line bytes"), this, &MessagesWidget::setMultiLineBytes); auto action = menu->addAction(tr("Multi-Line bytes"), this, &MessagesWidget::setMultiLineBytes);
action->setCheckable(true); action->setCheckable(true);
action->setChecked(settings.multiple_lines_hex); action->setChecked(settings.multiple_lines_hex);
action = menu->addAction(tr("Show inactive Messages"), model, &MessageListModel::showInactivemessages);
action->setCheckable(true);
action->setChecked(model->show_inactive_messages);
} }
void MessagesWidget::setMultiLineBytes(bool multi) { void MessagesWidget::setMultiLineBytes(bool multi) {
@ -186,9 +199,9 @@ QVariant MessageListModel::headerData(int section, Qt::Orientation orientation,
QVariant MessageListModel::data(const QModelIndex &index, int role) const { QVariant MessageListModel::data(const QModelIndex &index, int role) const {
if (!index.isValid() || index.row() >= items_.size()) return {}; if (!index.isValid() || index.row() >= items_.size()) return {};
auto getFreq = [](const CanData &d) { auto getFreq = [](float freq) {
if (d.freq > 0 && (can->currentSec() - d.ts - 1.0 / settings.fps) < (5.0 / d.freq)) { if (freq > 0) {
return d.freq >= 0.95 ? QString::number(std::nearbyint(d.freq)) : QString::number(d.freq, 'f', 2); return freq >= 0.95 ? QString::number(std::nearbyint(freq)) : QString::number(freq, 'f', 2);
} else { } else {
return QStringLiteral("--"); return QStringLiteral("--");
} }
@ -202,7 +215,7 @@ QVariant MessageListModel::data(const QModelIndex &index, int role) const {
case Column::SOURCE: return item.id.source != INVALID_SOURCE ? QString::number(item.id.source) : "N/A"; case Column::SOURCE: return item.id.source != INVALID_SOURCE ? QString::number(item.id.source) : "N/A";
case Column::ADDRESS: return QString::number(item.id.address, 16); case Column::ADDRESS: return QString::number(item.id.address, 16);
case Column::NODE: return item.node; case Column::NODE: return item.node;
case Column::FREQ: return item.id.source != INVALID_SOURCE ? getFreq(data) : "N/A"; case Column::FREQ: return item.id.source != INVALID_SOURCE ? getFreq(data.freq) : "N/A";
case Column::COUNT: return item.id.source != INVALID_SOURCE ? QString::number(data.count) : "N/A"; case Column::COUNT: return item.id.source != INVALID_SOURCE ? QString::number(data.count) : "N/A";
case Column::DATA: return item.id.source != INVALID_SOURCE ? "" : "N/A"; case Column::DATA: return item.id.source != INVALID_SOURCE ? "" : "N/A";
} }
@ -210,6 +223,8 @@ QVariant MessageListModel::data(const QModelIndex &index, int role) const {
return QVariant::fromValue((void*)(&data.colors)); return QVariant::fromValue((void*)(&data.colors));
} else if (role == BytesRole && index.column() == Column::DATA && item.id.source != INVALID_SOURCE) { } else if (role == BytesRole && index.column() == Column::DATA && item.id.source != INVALID_SOURCE) {
return QVariant::fromValue((void*)(&data.dat)); return QVariant::fromValue((void*)(&data.dat));
} else if (role == Qt::ForegroundRole && !item.active) {
return settings.theme == DARK_THEME ? QApplication::palette().color(QPalette::Text).darker(150) : QColor(Qt::gray);
} else if (role == Qt::ToolTipRole && index.column() == Column::NAME) { } else if (role == Qt::ToolTipRole && index.column() == Column::NAME) {
auto msg = dbc()->msg(item.id); auto msg = dbc()->msg(item.id);
auto tooltip = item.name; auto tooltip = item.name;
@ -224,6 +239,11 @@ void MessageListModel::setFilterStrings(const QMap<int, QString> &filters) {
filterAndSort(); filterAndSort();
} }
void MessageListModel::showInactivemessages(bool show) {
show_inactive_messages = show;
filterAndSort();
}
void MessageListModel::dbcModified() { void MessageListModel::dbcModified() {
dbc_messages_.clear(); dbc_messages_.clear();
for (const auto &[_, m] : dbc()->getMessages(-1)) { for (const auto &[_, m] : dbc()->getMessages(-1)) {
@ -233,19 +253,22 @@ void MessageListModel::dbcModified() {
} }
void MessageListModel::sortItems(std::vector<MessageListModel::Item> &items) { void MessageListModel::sortItems(std::vector<MessageListModel::Item> &items) {
auto do_sort = [order = sort_order](std::vector<MessageListModel::Item> &m, auto proj) { auto compare = [this](const auto &l, const auto &r) {
std::stable_sort(m.begin(), m.end(), [order, proj = std::move(proj)](auto &l, auto &r) { switch (sort_column) {
return order == Qt::AscendingOrder ? proj(l) < proj(r) : proj(l) > proj(r); case Column::NAME: return l.name < r.name;
}); case Column::SOURCE: return l.id.source < r.id.source;
case Column::ADDRESS: return l.id.address < r.id.address;
case Column::NODE: return l.node < r.node;
case Column::FREQ: return can->lastMessage(l.id).freq < can->lastMessage(r.id).freq;
case Column::COUNT: return can->lastMessage(l.id).count < can->lastMessage(r.id).count;
default: return false; // Default case to suppress compiler warning
}
}; };
switch (sort_column) {
case Column::NAME: do_sort(items, [](auto &item) { return std::tie(item.name, item.id); }); break; if (sort_order == Qt::DescendingOrder)
case Column::SOURCE: do_sort(items, [](auto &item) { return std::tie(item.id.source, item.id); }); break; std::stable_sort(items.rbegin(), items.rend(), compare);
case Column::ADDRESS: do_sort(items, [](auto &item) { return std::tie(item.id.address, item.id);}); break; else
case Column::NODE: do_sort(items, [](auto &item) { return std::tie(item.node, item.id);}); break; std::stable_sort(items.begin(), items.end(), compare);
case Column::FREQ: do_sort(items, [](auto &item) { return std::make_pair(can->lastMessage(item.id).freq, item.id); }); break;
case Column::COUNT: do_sort(items, [](auto &item) { return std::make_pair(can->lastMessage(item.id).count, item.id); }); break;
}
} }
static bool parseRange(const QString &filter, uint32_t value, int base = 10) { static bool parseRange(const QString &filter, uint32_t value, int base = 10) {
@ -292,7 +315,6 @@ bool MessageListModel::match(const MessageListModel::Item &item) {
match = item.node.contains(txt, Qt::CaseInsensitive); match = item.node.contains(txt, Qt::CaseInsensitive);
break; break;
case Column::FREQ: case Column::FREQ:
// TODO: Hide stale messages?
match = parseRange(txt, data.freq); match = parseRange(txt, data.freq);
break; break;
case Column::COUNT: case Column::COUNT:
@ -306,7 +328,7 @@ bool MessageListModel::match(const MessageListModel::Item &item) {
return match; return match;
} }
void MessageListModel::filterAndSort() { bool MessageListModel::filterAndSort() {
// merge CAN and DBC messages // merge CAN and DBC messages
std::vector<MessageId> all_messages; std::vector<MessageId> all_messages;
all_messages.reserve(can->lastMessages().size() + dbc_messages_.size()); all_messages.reserve(can->lastMessages().size() + dbc_messages_.size());
@ -319,13 +341,18 @@ void MessageListModel::filterAndSort() {
// filter and sort // filter and sort
std::vector<Item> items; std::vector<Item> items;
items.reserve(all_messages.size());
for (const auto &id : all_messages) { for (const auto &id : all_messages) {
auto msg = dbc()->msg(id); bool active = isMessageActive(id);
Item item = {.id = id, if (active || show_inactive_messages) {
.name = msg ? msg->name : UNTITLED, auto msg = dbc()->msg(id);
.node = msg ? msg->transmitter : QString()}; Item item = {.id = id,
if (match(item)) .active = active,
items.emplace_back(item); .name = msg ? msg->name : UNTITLED,
.node = msg ? msg->transmitter : QString()};
if (match(item))
items.emplace_back(item);
}
} }
sortItems(items); sortItems(items);
@ -333,16 +360,21 @@ void MessageListModel::filterAndSort() {
beginResetModel(); beginResetModel();
items_ = std::move(items); items_ = std::move(items);
endResetModel(); endResetModel();
return true;
} }
return false;
} }
void MessageListModel::msgsReceived(const std::set<MessageId> *new_msgs, bool has_new_ids) { void MessageListModel::msgsReceived(const std::set<MessageId> *new_msgs, bool has_new_ids) {
if (has_new_ids || filters_.contains(Column::FREQ) || filters_.contains(Column::COUNT) || filters_.contains(Column::DATA)) { if (has_new_ids || filters_.contains(Column::FREQ) || filters_.contains(Column::COUNT) || filters_.contains(Column::DATA)) {
filterAndSort(); if (filterAndSort()) return;
} }
for (int i = 0; i < items_.size(); ++i) { for (int i = 0; i < items_.size(); ++i) {
if (!new_msgs || new_msgs->count(items_[i].id)) { auto &item = items_[i];
for (int col = Column::FREQ; col < columnCount(); ++col) bool prev_active = item.active;
item.active = isMessageActive(item.id);
if (item.active != prev_active || !new_msgs || new_msgs->count(item.id)) {
for (int col = 0; col < columnCount(); ++col)
emit dataChanged(index(i, col), index(i, col), {Qt::DisplayRole}); emit dataChanged(index(i, col), index(i, col), {Qt::DisplayRole});
} }
} }
@ -360,20 +392,18 @@ void MessageListModel::sort(int column, Qt::SortOrder order) {
void MessageView::drawRow(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { void MessageView::drawRow(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const {
QTreeView::drawRow(painter, option, index); QTreeView::drawRow(painter, option, index);
const int gridHint = style()->styleHint(QStyle::SH_Table_GridLineColor, &option, this);
const QColor gridColor = QColor::fromRgba(static_cast<QRgb>(gridHint));
QPen old_pen = painter->pen();
painter->setPen(gridColor);
painter->drawLine(option.rect.left(), option.rect.bottom(), option.rect.right(), option.rect.bottom());
auto y = option.rect.y(); QPen oldPen = painter->pen();
painter->translate(visualRect(model()->index(0, 0)).x() - indentation() - .5, -.5); const int gridHint = style()->styleHint(QStyle::SH_Table_GridLineColor, &option, this);
painter->setPen(QColor::fromRgba(static_cast<QRgb>(gridHint)));
// Draw bottom border for the row
painter->drawLine(option.rect.bottomLeft(), option.rect.bottomRight());
// Draw vertical borders for each column
for (int i = 0; i < header()->count(); ++i) { for (int i = 0; i < header()->count(); ++i) {
painter->translate(header()->sectionSize(header()->logicalIndex(i)), 0); int sectionX = header()->sectionViewportPosition(i);
painter->drawLine(0, y, 0, y + option.rect.height()); painter->drawLine(sectionX, option.rect.top(), sectionX, option.rect.bottom());
} }
painter->setPen(old_pen); painter->setPen(oldPen);
painter->resetTransform();
} }
void MessageView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles) { void MessageView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles) {

@ -7,10 +7,8 @@
#include <QAbstractTableModel> #include <QAbstractTableModel>
#include <QHeaderView> #include <QHeaderView>
#include <QLabel>
#include <QLineEdit> #include <QLineEdit>
#include <QMenu> #include <QMenu>
#include <QToolBar>
#include <QTreeView> #include <QTreeView>
#include <QWheelEvent> #include <QWheelEvent>
@ -38,19 +36,22 @@ public:
int rowCount(const QModelIndex &parent = QModelIndex()) const override { return items_.size(); } int rowCount(const QModelIndex &parent = QModelIndex()) const override { return items_.size(); }
void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override; void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override;
void setFilterStrings(const QMap<int, QString> &filters); void setFilterStrings(const QMap<int, QString> &filters);
void showInactivemessages(bool show);
void msgsReceived(const std::set<MessageId> *new_msgs, bool has_new_ids); void msgsReceived(const std::set<MessageId> *new_msgs, bool has_new_ids);
void filterAndSort(); bool filterAndSort();
void dbcModified(); void dbcModified();
struct Item { struct Item {
MessageId id; MessageId id;
QString name; QString name;
QString node; QString node;
bool active;
bool operator==(const Item &other) const { bool operator==(const Item &other) const {
return id == other.id && name == other.name && node == other.node; return id == other.id && name == other.name && node == other.node;
} }
}; };
std::vector<Item> items_; std::vector<Item> items_;
bool show_inactive_messages = true;
private: private:
void sortItems(std::vector<MessageListModel::Item> &items); void sortItems(std::vector<MessageListModel::Item> &items);
@ -100,9 +101,10 @@ public:
signals: signals:
void msgSelectionChanged(const MessageId &message_id); void msgSelectionChanged(const MessageId &message_id);
void titleChanged(const QString &title);
protected: protected:
QToolBar *createToolBar(); QWidget *createToolBar();
void headerContextMenuEvent(const QPoint &pos); void headerContextMenuEvent(const QPoint &pos);
void menuAboutToShow(); void menuAboutToShow();
void setMultiLineBytes(bool multi); void setMultiLineBytes(bool multi);
@ -115,6 +117,5 @@ protected:
MessageListModel *model; MessageListModel *model;
QPushButton *suppress_add; QPushButton *suppress_add;
QPushButton *suppress_clear; QPushButton *suppress_clear;
QLabel *num_msg_label;
QMenu *menu; QMenu *menu;
}; };

@ -88,18 +88,24 @@ void MessageBytesDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
const auto &bytes = *static_cast<std::vector<uint8_t>*>(data.value<void*>()); const auto &bytes = *static_cast<std::vector<uint8_t>*>(data.value<void*>());
const auto &colors = *static_cast<std::vector<QColor>*>(index.data(ColorsRole).value<void*>()); const auto &colors = *static_cast<std::vector<QColor>*>(index.data(ColorsRole).value<void*>());
auto text_color = index.data(Qt::ForegroundRole).value<QColor>();
bool inactive = text_color.isValid();
if (!inactive) {
text_color = option.palette.color(QPalette::Text);
}
for (int i = 0; i < bytes.size(); ++i) { for (int i = 0; i < bytes.size(); ++i) {
int row = !multiple_lines ? 0 : i / 8; int row = !multiple_lines ? 0 : i / 8;
int column = !multiple_lines ? i : i % 8; int column = !multiple_lines ? i : i % 8;
QRect r = QRect({pt.x() + column * byte_size.width(), pt.y() + row * byte_size.height()}, byte_size); QRect r = QRect({pt.x() + column * byte_size.width(), pt.y() + row * byte_size.height()}, byte_size);
if (i < colors.size() && colors[i].alpha() > 0) { if (!inactive && i < colors.size() && colors[i].alpha() > 0) {
if (option.state & QStyle::State_Selected) { if (option.state & QStyle::State_Selected) {
painter->setPen(option.palette.color(QPalette::Text)); painter->setPen(option.palette.color(QPalette::Text));
painter->fillRect(r, option.palette.color(QPalette::Window)); painter->fillRect(r, option.palette.color(QPalette::Window));
} }
painter->fillRect(r, colors[i]); painter->fillRect(r, colors[i]);
} else if (option.state & QStyle::State_Selected) { } else {
painter->setPen(option.palette.color(QPalette::HighlightedText)); painter->setPen(option.state & QStyle::State_Selected ? option.palette.color(QPalette::HighlightedText) : text_color);
} }
utils::drawStaticText(painter, r, hex_text_table[bytes[i]]); utils::drawStaticText(painter, r, hex_text_table[bytes[i]]);
} }

@ -96,20 +96,26 @@ void Replay::updateEvents(const std::function<bool()> &lambda) {
} }
void Replay::seekTo(double seconds, bool relative) { void Replay::seekTo(double seconds, bool relative) {
seconds = relative ? seconds + currentSeconds() : seconds; seeking_to_seconds_ = relative ? seconds + currentSeconds() : seconds;
seeking_to_seconds_ = std::max(double(0.0), seeking_to_seconds_);
updateEvents([&]() { updateEvents([&]() {
seconds = std::max(double(0.0), seconds); int target_segment = (int)seeking_to_seconds_ / 60;
int seg = (int)seconds / 60; if (segments_.count(target_segment) == 0) {
if (segments_.find(seg) == segments_.end()) { rWarning("can't seek to %d s segment %d is invalid", (int)seeking_to_seconds_, target_segment);
rWarning("can't seek to %d s segment %d is invalid", seconds, seg);
return true; return true;
} }
rInfo("seeking to %d s, segment %d", (int)seconds, seg); rInfo("seeking to %d s, segment %d", (int)seeking_to_seconds_, target_segment);
current_segment_ = seg; current_segment_ = target_segment;
cur_mono_time_ = route_start_ts_ + seconds * 1e9; cur_mono_time_ = route_start_ts_ + seeking_to_seconds_ * 1e9;
emit seekedTo(seconds); bool segment_merged = isSegmentMerged(target_segment);
return isSegmentMerged(seg); if (segment_merged) {
emit seekedTo(seeking_to_seconds_);
// Reset seeking_to_seconds_ to indicate completion of seek
seeking_to_seconds_ = -1;
}
return segment_merged;
}); });
queueSegment(); queueSegment();
} }
@ -277,6 +283,18 @@ void Replay::mergeSegments(const SegmentMap::iterator &begin, const SegmentMap::
if (stream_thread_) { if (stream_thread_) {
emit segmentsMerged(); emit segmentsMerged();
// Check if seeking is in progress
if (seeking_to_seconds_ >= 0) {
int target_segment = int(seeking_to_seconds_ / 60);
auto segment_found = std::find(segments_need_merge.begin(), segments_need_merge.end(), target_segment);
// If the target segment is found, emit seekedTo signal and reset seeking_to_seconds_
if (segment_found != segments_need_merge.end()) {
emit seekedTo(seeking_to_seconds_);
seeking_to_seconds_ = -1; // Reset seeking_to_seconds_ to indicate completion of seek
}
}
} }
updateEvents([&]() { updateEvents([&]() {
events_.swap(new_events_); events_.swap(new_events_);

@ -117,6 +117,7 @@ protected:
std::condition_variable stream_cv_; std::condition_variable stream_cv_;
std::atomic<bool> updating_events_ = false; std::atomic<bool> updating_events_ = false;
std::atomic<int> current_segment_ = 0; std::atomic<int> current_segment_ = 0;
double seeking_to_seconds_ = -1;
SegmentMap segments_; SegmentMap segments_;
// the following variables must be protected with stream_lock_ // the following variables must be protected with stream_lock_
std::atomic<bool> exit_ = false; std::atomic<bool> exit_ = false;

Loading…
Cancel
Save