cabana: paint sparkline in threads (#27925)

* multiple threading realtime sparkline

* delay visible

* remove msg_id

* cleanup
old-commit-hash: ed1f03dc41
beeps
Dean Lee 2 years ago committed by GitHub
parent b8fcd32542
commit 40caca7e24
  1. 2
      tools/cabana/SConscript
  2. 7
      tools/cabana/chart/chartswidget.cc
  3. 68
      tools/cabana/chart/sparkline.cc
  4. 24
      tools/cabana/chart/sparkline.h
  5. 251
      tools/cabana/signalview.cc
  6. 22
      tools/cabana/signalview.h

@ -31,7 +31,7 @@ prev_moc_path = cabana_env['QT_MOCHPREFIX']
cabana_env['QT_MOCHPREFIX'] = os.path.dirname(prev_moc_path) + '/cabana/moc_'
cabana_lib = cabana_env.Library("cabana_lib", ['mainwin.cc', 'streams/livestream.cc', 'streams/abstractstream.cc', 'streams/replaystream.cc', 'binaryview.cc', 'historylog.cc', 'videowidget.cc', 'signalview.cc',
'dbc/dbc.cc', 'dbc/dbcfile.cc', 'dbc/dbcmanager.cc',
'chart/chartswidget.cc', 'chart/chart.cc', 'chart/signalselector.cc', 'chart/tiplabel.cc',
'chart/chartswidget.cc', 'chart/chart.cc', 'chart/signalselector.cc', 'chart/tiplabel.cc', 'chart/sparkline.cc',
'commands.cc', 'messageswidget.cc', 'route.cc', 'settings.cc', 'util.cc', 'detailwidget.cc', 'tools/findsimilarbits.cc'], LIBS=cabana_libs, FRAMEWORKS=base_frameworks)
cabana_env.Program('cabana', ['cabana.cc', cabana_lib, assets], LIBS=cabana_libs, FRAMEWORKS=base_frameworks)

@ -314,7 +314,12 @@ void ChartsWidget::updateLayout(bool force) {
}
for (int i = 0; i < current_charts.size(); ++i) {
charts_layout->addWidget(current_charts[i], i / n, i % n);
current_charts[i]->setVisible(true);
if (current_charts[i]->sigs.isEmpty()) {
// the chart will be resized after add signal. delay setVisible to reduce flicker.
QTimer::singleShot(0, [c = current_charts[i]]() { c->setVisible(true); });
} else {
current_charts[i]->setVisible(true);
}
}
charts_container->setUpdatesEnabled(true);
}

@ -0,0 +1,68 @@
#include "tools/cabana/chart/sparkline.h"
#include <QPainter>
#include "tools/cabana/streams/abstractstream.h"
void Sparkline::update(const MessageId &msg_id, const cabana::Signal *sig, double last_msg_ts, int range, QSize size) {
const auto &msgs = can->events().at(msg_id);
uint64_t ts = (last_msg_ts + can->routeStartTime()) * 1e9;
auto first = std::lower_bound(msgs.cbegin(), msgs.cend(), CanEvent{.mono_time = (uint64_t)std::max<int64_t>(ts - range * 1e9, 0)});
auto last = std::upper_bound(first, msgs.cend(), CanEvent{.mono_time = ts});
bool update_values = last_ts != last_msg_ts || time_range != range;
last_ts = last_msg_ts;
time_range = range;
if (first != last) {
if (update_values) {
values.clear();
values.reserve(std::distance(first, last));
min_val = std::numeric_limits<double>::max();
max_val = std::numeric_limits<double>::lowest();
for (auto it = first; it != last; ++it) {
double value = get_raw_value(it->dat, it->size, *sig);
values.emplace_back((it->mono_time - first->mono_time) / 1e9, value);
if (min_val > value) min_val = value;
if (max_val < value) max_val = value;
}
if (min_val == max_val) {
min_val -= 1;
max_val += 1;
}
}
render(getColor(sig), size);
} else {
pixmap = QPixmap();
min_val = -1;
max_val = 1;
}
}
void Sparkline::render(const QColor &color, QSize size) {
const double xscale = (size.width() - 1) / (double)time_range;
const double yscale = (size.height() - 3) / (max_val - min_val);
points.clear();
points.reserve(values.size());
for (auto &v : values) {
points.emplace_back(v.x() * xscale, 1 + std::abs(v.y() - max_val) * yscale);
}
qreal dpr = qApp->devicePixelRatio();
size *= dpr;
if (size != pixmap.size()) {
pixmap = QPixmap(size);
}
pixmap.setDevicePixelRatio(dpr);
pixmap.fill(Qt::transparent);
QPainter painter(&pixmap);
painter.setRenderHint(QPainter::Antialiasing, points.size() < 500);
painter.setPen(color);
painter.drawPolyline(points.data(), points.size());
painter.setPen(QPen(color, 3));
if ((points.back().x() - points.front().x()) / points.size() > 8) {
painter.drawPoints(points.data(), points.size());
} else {
painter.drawPoint(points.back());
}
}

@ -0,0 +1,24 @@
#pragma once
#include <QPixmap>
#include <QPointF>
#include <vector>
#include "tools/cabana/dbc/dbcmanager.h"
class Sparkline {
public:
void update(const MessageId &msg_id, const cabana::Signal *sig, double last_msg_ts, int range, QSize size);
const QSize size() const { return pixmap.size() / pixmap.devicePixelRatio(); }
QPixmap pixmap;
double min_val = 0;
double max_val = 0;
double last_ts = 0;
int time_range = 0;
private:
void render(const QColor &color, QSize size);
std::vector<QPointF> values;
std::vector<QPointF> points;
};

@ -9,6 +9,8 @@
#include <QMessageBox>
#include <QPainter>
#include <QPushButton>
#include <QScrollBar>
#include <QtConcurrent>
#include <QVBoxLayout>
#include "tools/cabana/commands.h"
@ -22,7 +24,6 @@ SignalModel::SignalModel(QObject *parent) : root(new Item), QAbstractItemModel(p
QObject::connect(dbc(), &DBCManager::signalAdded, this, &SignalModel::handleSignalAdded);
QObject::connect(dbc(), &DBCManager::signalUpdated, this, &SignalModel::handleSignalUpdated);
QObject::connect(dbc(), &DBCManager::signalRemoved, this, &SignalModel::handleSignalRemoved);
QObject::connect(can, &AbstractStream::msgsReceived, this, &SignalModel::updateState);
}
void SignalModel::insertItem(SignalModel::Item *parent_item, int pos, const cabana::Signal *sig) {
@ -37,9 +38,7 @@ void SignalModel::insertItem(SignalModel::Item *parent_item, int pos, const caba
void SignalModel::setMessage(const MessageId &id) {
msg_id = id;
filter_str = "";
value_width = 0;
refresh();
updateState(nullptr);
}
void SignalModel::setFilter(const QString &txt) {
@ -60,33 +59,6 @@ void SignalModel::refresh() {
endResetModel();
}
void SignalModel::updateState(const QHash<MessageId, CanData> *msgs) {
if (!msgs || msgs->contains(msg_id)) {
auto &dat = can->lastMessage(msg_id).dat;
for (auto item : root->children) {
double value = get_raw_value((uint8_t *)dat.constData(), dat.size(), *item->sig);
item->sig_val = QString::number(value, 'f', item->sig->precision);
// Show unit
if (!item->sig->unit.isEmpty()) {
item->sig_val += " " + item->sig->unit;
}
// Show enum string
for (auto &[val, desc] : item->sig->val_desc) {
if (std::abs(value - val.toInt()) < 1e-6) {
item->sig_val = desc;
}
}
value_width = std::max(value_width, QFontMetrics(QFont()).width(item->sig_val));
}
for (int i = 0; i < root->children.size(); ++i) {
emit dataChanged(index(i, 1), index(i, 1), {Qt::DisplayRole});
}
}
}
SignalModel::Item *SignalModel::getItem(const QModelIndex &index) const {
SignalModel::Item *item = nullptr;
if (index.isValid()) {
@ -293,7 +265,6 @@ void SignalModel::handleSignalAdded(MessageId id, const cabana::Signal *sig) {
beginInsertRows({}, i, i);
insertItem(root.get(), i, sig);
endInsertRows();
updateState(nullptr);
}
}
@ -355,120 +326,68 @@ void SignalItemDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptio
QRect geom = option.rect;
geom.setLeft(geom.right() - editor->sizeHint().width());
editor->setGeometry(geom);
button_size = geom.size();
return;
}
QStyledItemDelegate::updateEditorGeometry(editor, option, index);
}
void SignalItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const {
int h_margin = option.widget->style()->pixelMetric(QStyle::PM_FocusFrameHMargin) + 1;
int v_margin = option.widget->style()->pixelMetric(QStyle::PM_FocusFrameVMargin);
auto item = (SignalModel::Item *)index.internalPointer();
if (index.column() == 0 && item && item->type == SignalModel::Item::Sig) {
painter->save();
if (item && item->type == SignalModel::Item::Sig) {
painter->setRenderHint(QPainter::Antialiasing);
if (option.state & QStyle::State_Selected) {
painter->fillRect(option.rect, option.palette.highlight());
}
// color label
auto bg_color = getColor(item->sig);
QRect rc{option.rect.left() + h_margin, option.rect.top(), color_label_width, option.rect.height()};
painter->setPen(Qt::NoPen);
painter->setBrush(item->highlight ? bg_color.darker(125) : bg_color);
painter->drawRoundedRect(rc.adjusted(0, v_margin, 0, -v_margin), 3, 3);
painter->setPen(item->highlight ? Qt::white : Qt::black);
painter->setFont(label_font);
painter->drawText(rc, Qt::AlignCenter, QString::number(item->row() + 1));
// signal name
painter->setFont(option.font);
painter->setPen(option.palette.color(option.state & QStyle::State_Selected ? QPalette::HighlightedText : QPalette::Text));
QString text = index.data(Qt::DisplayRole).toString();
QRect text_rect = option.rect;
text_rect.setLeft(rc.right() + h_margin * 2);
text = painter->fontMetrics().elidedText(text, Qt::ElideRight, text_rect.width());
painter->drawText(text_rect, option.displayAlignment, text);
painter->restore();
} else if (index.column() == 1 && item && item->type == SignalModel::Item::Sig) {
painter->save();
if (option.state & QStyle::State_Selected) {
painter->fillRect(option.rect, option.palette.highlight());
int h_margin = option.widget->style()->pixelMetric(QStyle::PM_FocusFrameHMargin) + 1;
int v_margin = option.widget->style()->pixelMetric(QStyle::PM_FocusFrameVMargin);
QRect r = option.rect.adjusted(h_margin, v_margin, -h_margin, -v_margin);
if (index.column() == 0) {
// color label
QPainterPath path;
QRect icon_rect{r.x(), r.y(), color_label_width, r.height()};
path.addRoundedRect(icon_rect, 3, 3);
painter->setPen(item->highlight ? Qt::white : Qt::black);
painter->setFont(label_font);
painter->fillPath(path, getColor(item->sig).darker(item->highlight ? 125 : 0));
painter->drawText(icon_rect, Qt::AlignCenter, QString::number(item->row() + 1));
r.setLeft(icon_rect.right() + h_margin * 2);
auto text = option.fontMetrics.elidedText(index.data(Qt::DisplayRole).toString(), Qt::ElideRight, r.width());
painter->setPen(option.palette.color(option.state & QStyle::State_Selected ? QPalette::HighlightedText : QPalette::Text));
painter->setFont(option.font);
painter->drawText(r, option.displayAlignment, text);
} else if (index.column() == 1) {
// sparkline
QSize sparkline_size = item->sparkline.pixmap.size() / item->sparkline.pixmap.devicePixelRatio();
painter->drawPixmap(QRect(r.topLeft(), sparkline_size), item->sparkline.pixmap);
// min-max value
painter->setPen(option.palette.color(option.state & QStyle::State_Selected ? QPalette::HighlightedText : QPalette::Text));
QRect rect = r.adjusted(sparkline_size.width() + 1, 0, 0, 0);
int value_adjust = 10;
if (item->highlight || option.state & QStyle::State_Selected) {
painter->drawLine(rect.topLeft(), rect.bottomLeft());
rect.adjust(5, -v_margin, 0, v_margin);
painter->setFont(minmax_font);
QString min = QString::number(item->sparkline.min_val);
QString max = QString::number(item->sparkline.max_val);
painter->drawText(rect, Qt::AlignLeft | Qt::AlignTop, max);
painter->drawText(rect, Qt::AlignLeft | Qt::AlignBottom, min);
QFontMetrics fm(minmax_font);
value_adjust = std::max(fm.width(min), fm.width(max)) + 5;
}
// value
painter->setFont(option.font);
rect.adjust(value_adjust, 0, -button_size.width(), 0);
auto text = option.fontMetrics.elidedText(index.data(Qt::DisplayRole).toString(), Qt::ElideRight, rect.width());
painter->drawText(rect, Qt::AlignRight | Qt::AlignVCenter, text);
}
SignalModel *model = (SignalModel*)index.model();
int adjust_right = ((SignalView *)parent())->tree->indexWidget(index)->sizeHint().width() + 2 * h_margin;
QRect r = option.rect.adjusted(h_margin, v_margin, -adjust_right, -v_margin);
int value_width = std::min<int>(model->value_width, r.width() * 0.4);
QRect value_rect = r.adjusted(r.width() - value_width - h_margin, 0, 0, 0);
auto text = painter->fontMetrics().elidedText(index.data(Qt::DisplayRole).toString(), Qt::ElideRight, value_rect.width());
painter->setPen(option.palette.color(option.state & QStyle::State_Selected ? QPalette::HighlightedText : QPalette::Text));
painter->drawText(value_rect, Qt::AlignRight | Qt::AlignVCenter, text);
drawSparkline(painter, r.adjusted(0, 0, -value_width, 0), option, index);
painter->restore();
} else {
QStyledItemDelegate::paint(painter, option, index);
}
}
void SignalItemDelegate::drawSparkline(QPainter *painter, const QRect &rect, const QStyleOptionViewItem &option, const QModelIndex &index) const {
static std::vector<QPointF> points;
const auto &msg_id = ((SignalView *)parent())->msg_id;
const auto &msgs = can->events().at(msg_id);
uint64_t ts = (can->lastMessage(msg_id).ts + can->routeStartTime()) * 1e9;
auto first = std::lower_bound(msgs.cbegin(), msgs.cend(), CanEvent{.mono_time = (uint64_t)std::max<int64_t>(ts - settings.sparkline_range * 1e9, 0)});
auto last = std::upper_bound(first, msgs.cend(), CanEvent{.mono_time = ts});
if (first != last) {
double min = std::numeric_limits<double>::max();
double max = std::numeric_limits<double>::lowest();
const auto item = (const SignalModel::Item *)index.internalPointer();
const auto sig = item->sig;
points.clear();
for (auto it = first; it != last; ++it) {
double value = get_raw_value(it->dat, it->size, *sig);
points.emplace_back((it->mono_time - first->mono_time) / 1e9, value);
min = std::min(min, value);
max = std::max(max, value);
}
if (min == max) {
min -= 1;
max += 1;
}
const double min_max_width = std::min(rect.width() - 10, QFontMetrics(minmax_font).width("000.00") + 5);
const QRect r = rect.adjusted(0, 0, -min_max_width, 0);
const double xscale = r.width() / (double)settings.sparkline_range;
const double yscale = r.height() / (max - min);
for (auto &pt : points) {
pt.rx() = r.left() + pt.x() * xscale;
pt.ry() = r.top() + std::abs(pt.y() - max) * yscale;
}
auto color = item->highlight ? getColor(sig).darker(125) : getColor(sig);
painter->setPen(color);
painter->drawPolyline(points.data(), points.size());
if ((points.back().x() - points.front().x()) / points.size() > 10) {
painter->setPen(Qt::NoPen);
painter->setBrush(color);
for (const auto &pt : points) {
painter->drawEllipse(pt, 2, 2);
}
}
if (item->highlight || option.state & QStyle::State_Selected) {
painter->setFont(minmax_font);
painter->setPen(option.state & QStyle::State_Selected ? option.palette.color(QPalette::HighlightedText) : Qt::darkGray);
painter->drawLine(r.topRight(), r.bottomRight());
QRect minmax_rect{r.right() + 5, r.top(), 1000, r.height()};
painter->drawText(minmax_rect, Qt::AlignLeft | Qt::AlignTop, QString::number(max));
painter->drawText(minmax_rect, Qt::AlignLeft | Qt::AlignBottom, QString::number(min));
}
}
}
QWidget *SignalItemDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const {
auto item = (SignalModel::Item *)index.internalPointer();
if (item->type == SignalModel::Item::Name || item->type == SignalModel::Item::Offset ||
@ -534,7 +453,7 @@ SignalView::SignalView(ChartsWidget *charts, QWidget *parent) : charts(charts),
// tree view
tree = new TreeView(this);
tree->setModel(model = new SignalModel(this));
tree->setItemDelegate(new SignalItemDelegate(this));
tree->setItemDelegate(delegate = new SignalItemDelegate(this));
tree->setFrameShape(QFrame::NoFrame);
tree->setHeaderHidden(true);
tree->setMouseTracking(true);
@ -560,6 +479,10 @@ SignalView::SignalView(ChartsWidget *charts, QWidget *parent) : charts(charts),
QObject::connect(model, &QAbstractItemModel::modelReset, this, &SignalView::rowsChanged);
QObject::connect(model, &QAbstractItemModel::rowsRemoved, this, &SignalView::rowsChanged);
QObject::connect(dbc(), &DBCManager::signalAdded, [this](MessageId id, const cabana::Signal *sig) { selectSignal(sig); });
QObject::connect(can, &AbstractStream::msgsReceived, this, &SignalView::updateState);
QObject::connect(dbc(), &DBCManager::signalUpdated, this, &SignalView::handleSignalUpdated);
QObject::connect(tree->verticalScrollBar(), &QScrollBar::valueChanged, [this]() { updateState(); });
QObject::connect(tree->verticalScrollBar(), &QScrollBar::rangeChanged, [this]() { updateState(); });
setWhatsThis(tr(R"(
<b>Signal view</b><br />
@ -568,7 +491,7 @@ SignalView::SignalView(ChartsWidget *charts, QWidget *parent) : charts(charts),
}
void SignalView::setMessage(const MessageId &id) {
msg_id = id;
max_value_width = 0;
filter_edit->clear();
model->setMessage(id);
}
@ -594,12 +517,13 @@ void SignalView::rowsChanged() {
auto sig = model->getItem(index)->sig;
QObject::connect(remove_btn, &QToolButton::clicked, [=]() { model->removeSignal(sig); });
QObject::connect(plot_btn, &QToolButton::clicked, [=](bool checked) {
emit showChart(msg_id, sig, checked, QGuiApplication::keyboardModifiers() & Qt::ShiftModifier);
emit showChart(model->msg_id, sig, checked, QGuiApplication::keyboardModifiers() & Qt::ShiftModifier);
});
}
}
updateToolBar();
updateChartState();
updateState();
}
void SignalView::rowClicked(const QModelIndex &index) {
@ -626,7 +550,7 @@ void SignalView::selectSignal(const cabana::Signal *sig, bool expand) {
void SignalView::updateChartState() {
int i = 0;
for (auto item : model->root->children) {
bool chart_opened = charts->hasSignal(msg_id, item->sig);
bool chart_opened = charts->hasSignal(model->msg_id, item->sig);
auto buttons = tree->indexWidget(model->index(i, 1))->findChildren<QToolButton *>();
if (buttons.size() > 0) {
buttons[0]->setChecked(chart_opened);
@ -655,7 +579,70 @@ void SignalView::updateToolBar() {
void SignalView::setSparklineRange(int value) {
settings.sparkline_range = value;
updateToolBar();
model->updateState(nullptr);
updateState();
}
void SignalView::handleSignalUpdated(const cabana::Signal *sig) {
if (int row = model->signalRow(sig); row != -1) {
auto item = model->getItem(model->index(row, 1));
// invalidate the sparkline
item->sparkline.last_ts = 0;
updateState();
}
}
void SignalView::updateState(const QHash<MessageId, CanData> *msgs) {
if (model->rowCount() == 0 || (msgs && !msgs->contains(model->msg_id))) return;
const auto &last_msg = can->lastMessage(model->msg_id);
for (auto item : model->root->children) {
double value = get_raw_value((uint8_t *)last_msg.dat.constData(), last_msg.dat.size(), *item->sig);
item->sig_val = QString::number(value, 'f', item->sig->precision);
// Show unit
if (!item->sig->unit.isEmpty()) {
item->sig_val += " " + item->sig->unit;
}
// Show enum string
for (auto &[val, desc] : item->sig->val_desc) {
if (std::abs(value - val.toInt()) < 1e-6) {
item->sig_val = desc;
}
}
max_value_width = std::max(max_value_width, fontMetrics().width(item->sig_val));
}
// update visible sparkline
QSize size(tree->columnWidth(1) - delegate->button_size.width(), delegate->button_size.height());
int min_max_width = std::min(size.width() - 10, QFontMetrics(delegate->minmax_font).width("-000.00") + 5);
int value_width = std::min<int>(max_value_width, size.width() * 0.35);
size -= {value_width + min_max_width, style()->pixelMetric(QStyle::PM_FocusFrameVMargin) * 2};
QModelIndex top = tree->indexAt(QPoint(0, 0));
QModelIndex bottom = tree->indexAt(tree->viewport()->rect().bottomLeft());
int start_row = top.parent().isValid() ? top.parent().row() + 1 : top.row();
int end_row = model->rowCount() - 1;
if (bottom.isValid()) {
end_row = bottom.parent().isValid() ? bottom.parent().row() : bottom.row();
}
QFutureSynchronizer<void> synchronizer;
for (int i = start_row; i <= end_row; ++i) {
auto item = model->getItem(model->index(i, 1));
auto &s = item->sparkline;
if (s.last_ts != last_msg.ts || s.size() != size || s.time_range != settings.sparkline_range) {
synchronizer.addFuture(QtConcurrent::run(
&s, &Sparkline::update, model->msg_id, item->sig, last_msg.ts, settings.sparkline_range, size));
}
}
synchronizer.waitForFinished();
for (int i = 0; i < model->rowCount(); ++i) {
emit model->dataChanged(model->index(i, 1), model->index(i, 1), {Qt::DisplayRole});
}
}
void SignalView::resizeEvent(QResizeEvent* event) {
updateState();
QFrame::resizeEvent(event);
}
void SignalView::leaveEvent(QEvent *event) {

@ -9,6 +9,7 @@
#include <QTreeView>
#include "tools/cabana/chart/chartswidget.h"
#include "tools/cabana/chart/sparkline.h"
class SignalModel : public QAbstractItemModel {
Q_OBJECT
@ -27,6 +28,7 @@ public:
bool highlight = false;
bool extra_expanded = false;
QString sig_val = "-";
Sparkline sparkline;
};
SignalModel(QObject *parent);
@ -54,12 +56,10 @@ private:
void handleSignalRemoved(const cabana::Signal *sig);
void handleMsgChanged(MessageId id);
void refresh();
void updateState(const QHash<MessageId, CanData> *msgs);
MessageId msg_id;
QString filter_str;
std::unique_ptr<Item> root;
int value_width = 0;
friend class SignalView;
friend class SignalItemDelegate;
};
@ -86,11 +86,12 @@ public:
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const;
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
bool helpEvent(QHelpEvent *event, QAbstractItemView *view, const QStyleOptionViewItem &option, const QModelIndex &index) override;
void drawSparkline(QPainter *painter, const QRect &rect, const QStyleOptionViewItem &option, const QModelIndex &index) const;
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
QValidator *name_validator, *double_validator;
QFont label_font, minmax_font;
const int color_label_width = 18;
mutable QSize button_size;
mutable QHash<QString, int> width_cache;
};
@ -105,7 +106,6 @@ public:
void selectSignal(const cabana::Signal *sig, bool expand = false);
void rowClicked(const QModelIndex &index);
SignalModel *model = nullptr;
MessageId msg_id;
signals:
void highlight(const cabana::Signal *sig);
@ -113,9 +113,12 @@ signals:
private:
void rowsChanged();
void leaveEvent(QEvent *event);
void leaveEvent(QEvent *event) override;
void resizeEvent(QResizeEvent* event) override;
void updateToolBar();
void setSparklineRange(int value);
void handleSignalUpdated(const cabana::Signal *sig);
void updateState(const QHash<MessageId, CanData> *msgs = nullptr);
struct TreeView : public QTreeView {
TreeView(QWidget *parent) : QTreeView(parent) {}
@ -124,13 +127,18 @@ private:
// update widget geometries in QTreeView::rowsInserted
QTreeView::rowsInserted(parent, start, end);
}
void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles = QVector<int>()) override {
// Bypass the slow call to QTreeView::dataChanged.
QAbstractItemView::dataChanged(topLeft, bottomRight, roles);
}
};
int max_value_width = 0;
TreeView *tree;
QLabel *sparkline_label;
QSlider *sparkline_range_slider;
QLineEdit *filter_edit;
ChartsWidget *charts;
QLabel *signal_count_lb;
SignalItemDelegate *delegate;
friend SignalItemDelegate;
};

Loading…
Cancel
Save