mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-08-21 16:23:46 +08:00
cabana: improve UI & fix bugs (#27387)
* improve ui * keep splitter size after msg changed * no leading spaces allowed in msg filter and signal filter * draw color byte AlignCenter * always set as current index * reduce chart flickers while resizing * dispaly more info in tooltip for signal * narrow combobox * use * typo * private sigs,fix bugs * merge #27383 * no expanding after undo/redo * gray color in tooltip * clear current_msg_id before reset model * dont call setmeesage if id is the same * fix bugs * cleanup * dont fetch logs if invisible * add new CenterWidget, make sure msg_id is always valid * cache icons * cleanup paint byte color * merge #27385 implement sizeHint * cleanup code * fillrect if alpha>0 old-commit-hash: 4efd246bac004f3b6f1f7d733e6e285b3fdc8322
This commit is contained in:
+46
-44
@@ -32,7 +32,6 @@ BinaryView::BinaryView(QWidget *parent) : QTableView(parent) {
|
|||||||
verticalHeader()->setSectionResizeMode(QHeaderView::Fixed);
|
verticalHeader()->setSectionResizeMode(QHeaderView::Fixed);
|
||||||
verticalHeader()->setDefaultSectionSize(CELL_HEIGHT);
|
verticalHeader()->setDefaultSectionSize(CELL_HEIGHT);
|
||||||
horizontalHeader()->hide();
|
horizontalHeader()->hide();
|
||||||
setFrameShape(QFrame::NoFrame);
|
|
||||||
setShowGrid(false);
|
setShowGrid(false);
|
||||||
setMouseTracking(true);
|
setMouseTracking(true);
|
||||||
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||||
@@ -44,17 +43,17 @@ BinaryView::BinaryView(QWidget *parent) : QTableView(parent) {
|
|||||||
setWhatsThis(R"(
|
setWhatsThis(R"(
|
||||||
<b>Binary View</b><br/>
|
<b>Binary View</b><br/>
|
||||||
<!-- TODO: add descprition here -->
|
<!-- TODO: add descprition here -->
|
||||||
Shortcuts:<br />
|
<span style="color:gray">Shortcuts</span><br />
|
||||||
Delete Signal:
|
Delete Signal:
|
||||||
<span style="background-color:lightGray;color:gray"> x </span>,
|
<span style="background-color:lightGray;color:gray"> x </span>,
|
||||||
<span style="background-color:lightGray;color:gray"> Backspace </span>,
|
<span style="background-color:lightGray;color:gray"> Backspace </span>,
|
||||||
<span style="background-color:lightGray;color:gray"> Delete</span><br />
|
<span style="background-color:lightGray;color:gray"> Delete </span><br />
|
||||||
Change endianness: <span style="background-color:lightGray;color:gray"> e </span><br />
|
Change endianness: <span style="background-color:lightGray;color:gray"> e </span><br />
|
||||||
Change singedness: <span style="background-color:lightGray;color:gray"> s </span><br />
|
Change singedness: <span style="background-color:lightGray;color:gray"> s </span><br />
|
||||||
Open chart:
|
Open chart:
|
||||||
<span style="background-color:lightGray;color:gray"> c </span>,
|
<span style="background-color:lightGray;color:gray"> c </span>,
|
||||||
<span style="background-color:lightGray;color:gray"> p </span>,
|
<span style="background-color:lightGray;color:gray"> p </span>,
|
||||||
<span style="background-color:lightGray;color:gray"> g </span><br />
|
<span style="background-color:lightGray;color:gray"> g </span>
|
||||||
)");
|
)");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,14 +107,14 @@ void BinaryView::addShortcuts() {
|
|||||||
QObject::connect(shortcut_plot_c, &QShortcut::activated, shortcut_plot, &QShortcut::activated);
|
QObject::connect(shortcut_plot_c, &QShortcut::activated, shortcut_plot, &QShortcut::activated);
|
||||||
QObject::connect(shortcut_plot, &QShortcut::activated, [=]{
|
QObject::connect(shortcut_plot, &QShortcut::activated, [=]{
|
||||||
if (hovered_sig != nullptr) {
|
if (hovered_sig != nullptr) {
|
||||||
emit showChart(*model->msg_id, hovered_sig, true, false);
|
emit showChart(model->msg_id, hovered_sig, true, false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
QSize BinaryView::minimumSizeHint() const {
|
QSize BinaryView::minimumSizeHint() const {
|
||||||
return {(horizontalHeader()->minimumSectionSize() + 1) * 9 + VERTICAL_HEADER_WIDTH,
|
return {(horizontalHeader()->minimumSectionSize() + 1) * 9 + VERTICAL_HEADER_WIDTH + 2,
|
||||||
CELL_HEIGHT * std::min(model->rowCount(), 10)};
|
CELL_HEIGHT * std::min(model->rowCount(), 10) + 2};
|
||||||
}
|
}
|
||||||
|
|
||||||
void BinaryView::highlight(const Signal *sig) {
|
void BinaryView::highlight(const Signal *sig) {
|
||||||
@@ -127,6 +126,16 @@ void BinaryView::highlight(const Signal *sig) {
|
|||||||
emit model->dataChanged(index, index, {Qt::DisplayRole});
|
emit model->dataChanged(index, index, {Qt::DisplayRole});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (sig && underMouse()) {
|
||||||
|
QString tooltip = tr(R"(%1<br /><span style="color:gray;font-size:small">
|
||||||
|
Size:%2 LE:%3 SGD:%4</span>
|
||||||
|
)").arg(sig->name).arg(sig->size).arg(sig->is_little_endian ? "Y" : "N").arg(sig->is_signed ? "Y" : "N");
|
||||||
|
QToolTip::showText(QCursor::pos(), tooltip, this, rect());
|
||||||
|
} else {
|
||||||
|
QToolTip::showText(QCursor::pos(), "", this, rect());
|
||||||
|
}
|
||||||
|
|
||||||
hovered_sig = sig;
|
hovered_sig = sig;
|
||||||
emit signalHovered(hovered_sig);
|
emit signalHovered(hovered_sig);
|
||||||
}
|
}
|
||||||
@@ -169,7 +178,6 @@ void BinaryView::highlightPosition(const QPoint &pos) {
|
|||||||
auto item = (BinaryViewModel::Item *)index.internalPointer();
|
auto item = (BinaryViewModel::Item *)index.internalPointer();
|
||||||
const Signal *sig = item->sigs.isEmpty() ? nullptr : item->sigs.back();
|
const Signal *sig = item->sigs.isEmpty() ? nullptr : item->sigs.back();
|
||||||
highlight(sig);
|
highlight(sig);
|
||||||
QToolTip::showText(pos, sig ? sig->name : "", this, rect());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -210,8 +218,6 @@ void BinaryView::setMessage(const MessageId &message_id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void BinaryView::refresh() {
|
void BinaryView::refresh() {
|
||||||
if (!model->msg_id) return;
|
|
||||||
|
|
||||||
clearSelection();
|
clearSelection();
|
||||||
anchor_index = QModelIndex();
|
anchor_index = QModelIndex();
|
||||||
resize_sig = nullptr;
|
resize_sig = nullptr;
|
||||||
@@ -245,26 +251,26 @@ std::tuple<int, int, bool> BinaryView::getSelection(QModelIndex index) {
|
|||||||
void BinaryViewModel::refresh() {
|
void BinaryViewModel::refresh() {
|
||||||
beginResetModel();
|
beginResetModel();
|
||||||
items.clear();
|
items.clear();
|
||||||
if (auto dbc_msg = dbc()->msg(*msg_id)) {
|
if (auto dbc_msg = dbc()->msg(msg_id)) {
|
||||||
row_count = dbc_msg->size;
|
row_count = dbc_msg->size;
|
||||||
items.resize(row_count * column_count);
|
items.resize(row_count * column_count);
|
||||||
for (auto &sig : dbc_msg->sigs) {
|
for (auto sig : dbc_msg->getSignals()) {
|
||||||
auto [start, end] = getSignalRange(&sig);
|
auto [start, end] = getSignalRange(sig);
|
||||||
for (int j = start; j <= end; ++j) {
|
for (int j = start; j <= end; ++j) {
|
||||||
int bit_index = sig.is_little_endian ? bigEndianBitIndex(j) : j;
|
int bit_index = sig->is_little_endian ? bigEndianBitIndex(j) : j;
|
||||||
int idx = column_count * (bit_index / 8) + bit_index % 8;
|
int idx = column_count * (bit_index / 8) + bit_index % 8;
|
||||||
if (idx >= items.size()) {
|
if (idx >= items.size()) {
|
||||||
qWarning() << "signal " << sig.name << "out of bounds.start_bit:" << sig.start_bit << "size:" << sig.size;
|
qWarning() << "signal " << sig->name << "out of bounds.start_bit:" << sig->start_bit << "size:" << sig->size;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (j == start) sig.is_little_endian ? items[idx].is_lsb = true : items[idx].is_msb = true;
|
if (j == start) sig->is_little_endian ? items[idx].is_lsb = true : items[idx].is_msb = true;
|
||||||
if (j == end) sig.is_little_endian ? items[idx].is_msb = true : items[idx].is_lsb = true;
|
if (j == end) sig->is_little_endian ? items[idx].is_msb = true : items[idx].is_lsb = true;
|
||||||
items[idx].bg_color = getColor(&sig);
|
items[idx].bg_color = getColor(sig);
|
||||||
items[idx].sigs.push_back(&sig);
|
items[idx].sigs.push_back(sig);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
row_count = can->lastMessage(*msg_id).dat.size();
|
row_count = can->lastMessage(msg_id).dat.size();
|
||||||
items.resize(row_count * column_count);
|
items.resize(row_count * column_count);
|
||||||
}
|
}
|
||||||
endResetModel();
|
endResetModel();
|
||||||
@@ -273,7 +279,7 @@ void BinaryViewModel::refresh() {
|
|||||||
|
|
||||||
void BinaryViewModel::updateState() {
|
void BinaryViewModel::updateState() {
|
||||||
auto prev_items = items;
|
auto prev_items = items;
|
||||||
const auto &last_msg = can->lastMessage(*msg_id);
|
const auto &last_msg = can->lastMessage(msg_id);
|
||||||
const auto &binary = last_msg.dat;
|
const auto &binary = last_msg.dat;
|
||||||
|
|
||||||
// data size may changed.
|
// data size may changed.
|
||||||
@@ -283,33 +289,29 @@ void BinaryViewModel::updateState() {
|
|||||||
items.resize(row_count * column_count);
|
items.resize(row_count * column_count);
|
||||||
endInsertRows();
|
endInsertRows();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double max_f = 255.0;
|
||||||
|
double factor = 0.25;
|
||||||
|
double scaler = max_f / log2(1.0 + factor);
|
||||||
char hex[3] = {'\0'};
|
char hex[3] = {'\0'};
|
||||||
for (int i = 0; i < binary.size(); ++i) {
|
for (int i = 0; i < binary.size(); ++i) {
|
||||||
for (int j = 0; j < 8; ++j) {
|
for (int j = 0; j < 8; ++j) {
|
||||||
items[i * column_count + j].val = ((binary[i] >> (7 - j)) & 1) != 0 ? '1' : '0';
|
auto &item = items[i * column_count + j];
|
||||||
|
item.val = ((binary[i] >> (7 - j)) & 1) != 0 ? '1' : '0';
|
||||||
// Bit update frequency based highlighting
|
// Bit update frequency based highlighting
|
||||||
bool has_signal = items[i * column_count + j].sigs.size() > 0;
|
double offset = !item.sigs.empty() ? 50 : 0;
|
||||||
double offset = has_signal ? 50 : 0;
|
auto n = last_msg.bit_change_counts[i][7 - j];
|
||||||
|
double min_f = n == 0 ? offset : offset + 25;
|
||||||
double min_f = last_msg.bit_change_counts[i][7 - j] == 0 ? offset : offset + 25;
|
double alpha = std::clamp(offset + log2(1.0 + factor * (double)n / (double)last_msg.count) * scaler, min_f, max_f);
|
||||||
double max_f = 255.0;
|
item.bg_color.setAlpha(alpha);
|
||||||
|
|
||||||
double factor = 0.25;
|
|
||||||
double scaler = max_f / log2(1.0 + factor);
|
|
||||||
|
|
||||||
double alpha = std::clamp(offset + log2(1.0 + factor * (double)last_msg.bit_change_counts[i][7 - j] / (double)last_msg.count) * scaler, min_f, max_f);
|
|
||||||
items[i * column_count + j].bg_color.setAlpha(alpha);
|
|
||||||
}
|
}
|
||||||
hex[0] = toHex(binary[i] >> 4);
|
hex[0] = toHex(binary[i] >> 4);
|
||||||
hex[1] = toHex(binary[i] & 0xf);
|
hex[1] = toHex(binary[i] & 0xf);
|
||||||
items[i * column_count + 8].val = hex;
|
items[i * column_count + 8].val = hex;
|
||||||
items[i * column_count + 8].bg_color = last_msg.colors[i];
|
items[i * column_count + 8].bg_color = last_msg.colors[i];
|
||||||
}
|
}
|
||||||
for (int i = binary.size(); i < row_count; ++i) {
|
for (int i = binary.size() * column_count; i < items.size(); ++i) {
|
||||||
for (int j = 0; j < column_count; ++j) {
|
items[i].val = "-";
|
||||||
items[i * column_count + j].val = "-";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < items.size(); ++i) {
|
for (int i = 0; i < items.size(); ++i) {
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <optional>
|
|
||||||
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QList>
|
#include <QList>
|
||||||
#include <QSet>
|
#include <QSet>
|
||||||
@@ -50,7 +48,7 @@ public:
|
|||||||
};
|
};
|
||||||
std::vector<Item> items;
|
std::vector<Item> items;
|
||||||
|
|
||||||
std::optional<MessageId> msg_id;
|
MessageId msg_id;
|
||||||
int row_count = 0;
|
int row_count = 0;
|
||||||
const int column_count = 9;
|
const int column_count = 9;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -11,15 +11,16 @@
|
|||||||
#include <QRubberBand>
|
#include <QRubberBand>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
#include <QToolBar>
|
#include <QToolBar>
|
||||||
#include <QToolButton>
|
|
||||||
#include <QToolTip>
|
#include <QToolTip>
|
||||||
#include <QtConcurrent>
|
#include <QtConcurrent>
|
||||||
|
|
||||||
const int MAX_COLUMN_COUNT = 4;
|
const int MAX_COLUMN_COUNT = 4;
|
||||||
// ChartsWidget
|
// ChartsWidget
|
||||||
|
|
||||||
ChartsWidget::ChartsWidget(QWidget *parent) : QWidget(parent) {
|
ChartsWidget::ChartsWidget(QWidget *parent) : QFrame(parent) {
|
||||||
|
setFrameStyle(QFrame::StyledPanel | QFrame::Plain);
|
||||||
QVBoxLayout *main_layout = new QVBoxLayout(this);
|
QVBoxLayout *main_layout = new QVBoxLayout(this);
|
||||||
|
main_layout->setContentsMargins(0, 0, 0, 0);
|
||||||
|
|
||||||
// toolbar
|
// toolbar
|
||||||
QToolBar *toolbar = new QToolBar(tr("Charts"), this);
|
QToolBar *toolbar = new QToolBar(tr("Charts"), this);
|
||||||
@@ -43,6 +44,7 @@ ChartsWidget::ChartsWidget(QWidget *parent) : QWidget(parent) {
|
|||||||
|
|
||||||
range_lb_action = toolbar->addWidget(range_lb = new QLabel(this));
|
range_lb_action = toolbar->addWidget(range_lb = new QLabel(this));
|
||||||
range_slider = new QSlider(Qt::Horizontal, this);
|
range_slider = new QSlider(Qt::Horizontal, this);
|
||||||
|
range_slider->setMaximumWidth(200);
|
||||||
range_slider->setToolTip(tr("Set the chart range"));
|
range_slider->setToolTip(tr("Set the chart range"));
|
||||||
range_slider->setRange(1, settings.max_cached_minutes * 60);
|
range_slider->setRange(1, settings.max_cached_minutes * 60);
|
||||||
range_slider->setSingleStep(1);
|
range_slider->setSingleStep(1);
|
||||||
@@ -67,6 +69,7 @@ ChartsWidget::ChartsWidget(QWidget *parent) : QWidget(parent) {
|
|||||||
charts_main_layout->addStretch(0);
|
charts_main_layout->addStretch(0);
|
||||||
|
|
||||||
QScrollArea *charts_scroll = new QScrollArea(this);
|
QScrollArea *charts_scroll = new QScrollArea(this);
|
||||||
|
charts_scroll->setFrameStyle(QFrame::NoFrame);
|
||||||
charts_scroll->setWidgetResizable(true);
|
charts_scroll->setWidgetResizable(true);
|
||||||
charts_scroll->setWidget(charts_container);
|
charts_scroll->setWidget(charts_container);
|
||||||
charts_scroll->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
charts_scroll->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||||
@@ -329,18 +332,12 @@ ChartView::ChartView(QWidget *parent) : QChartView(nullptr, parent) {
|
|||||||
move_icon = new QGraphicsPixmapItem(utils::icon("grip-horizontal"), chart);
|
move_icon = new QGraphicsPixmapItem(utils::icon("grip-horizontal"), chart);
|
||||||
move_icon->setToolTip(tr("Drag and drop to combine charts"));
|
move_icon->setToolTip(tr("Drag and drop to combine charts"));
|
||||||
|
|
||||||
QToolButton *remove_btn = new QToolButton();
|
QToolButton *remove_btn = toolButton("x", tr("Remove Chart"));
|
||||||
remove_btn->setIcon(utils::icon("x"));
|
|
||||||
remove_btn->setAutoRaise(true);
|
|
||||||
remove_btn->setToolTip(tr("Remove Chart"));
|
|
||||||
close_btn_proxy = new QGraphicsProxyWidget(chart);
|
close_btn_proxy = new QGraphicsProxyWidget(chart);
|
||||||
close_btn_proxy->setWidget(remove_btn);
|
close_btn_proxy->setWidget(remove_btn);
|
||||||
close_btn_proxy->setZValue(chart->zValue() + 11);
|
close_btn_proxy->setZValue(chart->zValue() + 11);
|
||||||
|
|
||||||
QToolButton *manage_btn = new QToolButton();
|
QToolButton *manage_btn = toolButton("list", "");
|
||||||
manage_btn->setToolButtonStyle(Qt::ToolButtonIconOnly);
|
|
||||||
manage_btn->setIcon(utils::icon("list"));
|
|
||||||
manage_btn->setAutoRaise(true);
|
|
||||||
QMenu *menu = new QMenu(this);
|
QMenu *menu = new QMenu(this);
|
||||||
line_series_action = menu->addAction(tr("Line"), [this]() { setSeriesType(QAbstractSeries::SeriesTypeLine); });
|
line_series_action = menu->addAction(tr("Line"), [this]() { setSeriesType(QAbstractSeries::SeriesTypeLine); });
|
||||||
line_series_action->setCheckable(true);
|
line_series_action->setCheckable(true);
|
||||||
@@ -434,12 +431,12 @@ void ChartView::manageSeries() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ChartView::resizeEvent(QResizeEvent *event) {
|
void ChartView::resizeEvent(QResizeEvent *event) {
|
||||||
QChartView::resizeEvent(event);
|
|
||||||
updatePlotArea(align_to);
|
updatePlotArea(align_to);
|
||||||
int x = event->size().width() - close_btn_proxy->size().width() - 11;
|
int x = event->size().width() - close_btn_proxy->size().width() - 11;
|
||||||
close_btn_proxy->setPos(x, 8);
|
close_btn_proxy->setPos(x, 8);
|
||||||
manage_btn_proxy->setPos(x - manage_btn_proxy->size().width() - 5, 8);
|
manage_btn_proxy->setPos(x - manage_btn_proxy->size().width() - 5, 8);
|
||||||
move_icon->setPos(11, 8);
|
move_icon->setPos(11, 8);
|
||||||
|
QChartView::resizeEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChartView::updatePlotArea(int left) {
|
void ChartView::updatePlotArea(int left) {
|
||||||
@@ -448,7 +445,7 @@ void ChartView::updatePlotArea(int left) {
|
|||||||
align_to = left;
|
align_to = left;
|
||||||
background->setRect(r);
|
background->setRect(r);
|
||||||
chart()->legend()->setGeometry(QRect(r.left(), r.top(), r.width(), 45));
|
chart()->legend()->setGeometry(QRect(r.left(), r.top(), r.width(), 45));
|
||||||
chart()->setPlotArea(QRect(align_to, r.top() + 45, r.width() - align_to - 22, r.height() - 80));
|
chart()->setPlotArea(QRect(align_to, r.top() + 45, r.width() - align_to - 36, r.height() - 80));
|
||||||
chart()->layout()->invalidate();
|
chart()->layout()->invalidate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -570,7 +567,7 @@ void ChartView::updateAxisY() {
|
|||||||
|
|
||||||
QFontMetrics fm(axis_y->labelsFont());
|
QFontMetrics fm(axis_y->labelsFont());
|
||||||
int n = qMax(int(-qFloor(std::log10((max_y - min_y) / (tick_count - 1)))), 0) + 1;
|
int n = qMax(int(-qFloor(std::log10((max_y - min_y) / (tick_count - 1)))), 0) + 1;
|
||||||
y_label_width = qMax(fm.width(QString::number(min_y, 'f', n)), fm.width(QString::number(max_y, 'f', n))) + 20; // left margin 20
|
y_label_width = qMax(fm.width(QString::number(min_y, 'f', n)), fm.width(QString::number(max_y, 'f', n))) + 15; // left margin 15
|
||||||
emit axisYLabelWidthChanged(y_label_width);
|
emit axisYLabelWidthChanged(y_label_width);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -887,10 +884,10 @@ void SeriesSelector::updateAvailableList(int index) {
|
|||||||
available_list->clear();
|
available_list->clear();
|
||||||
MessageId msg_id = msgs_combo->itemData(index).value<MessageId>();
|
MessageId msg_id = msgs_combo->itemData(index).value<MessageId>();
|
||||||
auto selected_items = seletedItems();
|
auto selected_items = seletedItems();
|
||||||
for (auto &s : dbc()->msg(msg_id)->sigs) {
|
for (auto s : dbc()->msg(msg_id)->getSignals()) {
|
||||||
bool is_selected = std::any_of(selected_items.begin(), selected_items.end(), [=, sig=&s](auto it) { return it->msg_id == msg_id && it->sig == sig; });
|
bool is_selected = std::any_of(selected_items.begin(), selected_items.end(), [=, sig=s](auto it) { return it->msg_id == msg_id && it->sig == sig; });
|
||||||
if (!is_selected) {
|
if (!is_selected) {
|
||||||
addItemToList(available_list, msg_id, &s);
|
addItemToList(available_list, msg_id, s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ private:
|
|||||||
friend class ChartsWidget;
|
friend class ChartsWidget;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ChartsWidget : public QWidget {
|
class ChartsWidget : public QFrame {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -38,8 +38,8 @@ RemoveMsgCommand::RemoveMsgCommand(const MessageId &id, QUndoCommand *parent) :
|
|||||||
void RemoveMsgCommand::undo() {
|
void RemoveMsgCommand::undo() {
|
||||||
if (!message.name.isEmpty()) {
|
if (!message.name.isEmpty()) {
|
||||||
dbc()->updateMsg(id, message.name, message.size);
|
dbc()->updateMsg(id, message.name, message.size);
|
||||||
for (auto &s : message.sigs)
|
for (auto s : message.getSignals())
|
||||||
dbc()->addSignal(id, s);
|
dbc()->addSignal(id, *s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+28
-25
@@ -10,10 +10,6 @@
|
|||||||
|
|
||||||
namespace dbcmanager {
|
namespace dbcmanager {
|
||||||
|
|
||||||
void sortSignalsByAddress(QList<Signal> &sigs) {
|
|
||||||
std::sort(sigs.begin(), sigs.end(), [](auto &a, auto &b) { return a.start_bit < b.start_bit; });
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DBCManager::open(const QString &dbc_file_name, QString *error) {
|
bool DBCManager::open(const QString &dbc_file_name, QString *error) {
|
||||||
QString opendbc_file_path = QString("%1/%2.dbc").arg(OPENDBC_FILE_PATH, dbc_file_name);
|
QString opendbc_file_path = QString("%1/%2.dbc").arg(OPENDBC_FILE_PATH, dbc_file_name);
|
||||||
QFile file(opendbc_file_path);
|
QFile file(opendbc_file_path);
|
||||||
@@ -83,27 +79,27 @@ QString DBCManager::generateDBC() {
|
|||||||
QString dbc_string, signal_comment, val_desc;
|
QString dbc_string, signal_comment, val_desc;
|
||||||
for (auto &[address, m] : msgs) {
|
for (auto &[address, m] : msgs) {
|
||||||
dbc_string += QString("BO_ %1 %2: %3 XXX\n").arg(address).arg(m.name).arg(m.size);
|
dbc_string += QString("BO_ %1 %2: %3 XXX\n").arg(address).arg(m.name).arg(m.size);
|
||||||
for (auto &sig : m.sigs) {
|
for (auto sig : m.getSignals()) {
|
||||||
dbc_string += QString(" SG_ %1 : %2|%3@%4%5 (%6,%7) [%8|%9] \"%10\" XXX\n")
|
dbc_string += QString(" SG_ %1 : %2|%3@%4%5 (%6,%7) [%8|%9] \"%10\" XXX\n")
|
||||||
.arg(sig.name)
|
.arg(sig->name)
|
||||||
.arg(sig.start_bit)
|
.arg(sig->start_bit)
|
||||||
.arg(sig.size)
|
.arg(sig->size)
|
||||||
.arg(sig.is_little_endian ? '1' : '0')
|
.arg(sig->is_little_endian ? '1' : '0')
|
||||||
.arg(sig.is_signed ? '-' : '+')
|
.arg(sig->is_signed ? '-' : '+')
|
||||||
.arg(sig.factor, 0, 'g', std::numeric_limits<double>::digits10)
|
.arg(sig->factor, 0, 'g', std::numeric_limits<double>::digits10)
|
||||||
.arg(sig.offset, 0, 'g', std::numeric_limits<double>::digits10)
|
.arg(sig->offset, 0, 'g', std::numeric_limits<double>::digits10)
|
||||||
.arg(sig.min)
|
.arg(sig->min)
|
||||||
.arg(sig.max)
|
.arg(sig->max)
|
||||||
.arg(sig.unit);
|
.arg(sig->unit);
|
||||||
if (!sig.comment.isEmpty()) {
|
if (!sig->comment.isEmpty()) {
|
||||||
signal_comment += QString("CM_ SG_ %1 %2 \"%3\";\n").arg(address).arg(sig.name).arg(sig.comment);
|
signal_comment += QString("CM_ SG_ %1 %2 \"%3\";\n").arg(address).arg(sig->name).arg(sig->comment);
|
||||||
}
|
}
|
||||||
if (!sig.val_desc.isEmpty()) {
|
if (!sig->val_desc.isEmpty()) {
|
||||||
QString text;
|
QStringList text;
|
||||||
for (auto &[val, desc] : sig.val_desc) {
|
for (auto &[val, desc] : sig->val_desc) {
|
||||||
text += QString("%1 \"%2\"").arg(val, desc);
|
text << QString("%1 \"%2\"").arg(val, desc);
|
||||||
}
|
}
|
||||||
val_desc += QString("VAL_ %1 %2 %3;\n").arg(address).arg(sig.name).arg(text);
|
val_desc += QString("VAL_ %1 %2 %3;\n").arg(address).arg(sig->name).arg(text.join(" "));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dbc_string += "\n";
|
dbc_string += "\n";
|
||||||
@@ -127,7 +123,6 @@ void DBCManager::addSignal(const MessageId &id, const Signal &sig) {
|
|||||||
if (auto m = const_cast<Msg *>(msg(id.address))) {
|
if (auto m = const_cast<Msg *>(msg(id.address))) {
|
||||||
m->sigs.push_back(sig);
|
m->sigs.push_back(sig);
|
||||||
auto s = &m->sigs.last();
|
auto s = &m->sigs.last();
|
||||||
sortSignalsByAddress(m->sigs);
|
|
||||||
emit signalAdded(id.address, s);
|
emit signalAdded(id.address, s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -136,7 +131,6 @@ void DBCManager::updateSignal(const MessageId &id, const QString &sig_name, cons
|
|||||||
if (auto m = const_cast<Msg *>(msg(id))) {
|
if (auto m = const_cast<Msg *>(msg(id))) {
|
||||||
if (auto s = (Signal *)m->sig(sig_name)) {
|
if (auto s = (Signal *)m->sig(sig_name)) {
|
||||||
*s = sig;
|
*s = sig;
|
||||||
sortSignalsByAddress(m->sigs);
|
|
||||||
emit signalUpdated(s);
|
emit signalUpdated(s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -157,6 +151,16 @@ DBCManager *dbc() {
|
|||||||
return &dbc_manager;
|
return &dbc_manager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Msg
|
||||||
|
|
||||||
|
std::vector<const Signal*> Msg::getSignals() const {
|
||||||
|
std::vector<const Signal*> ret;
|
||||||
|
ret.reserve(sigs.size());
|
||||||
|
for (auto &sig : sigs) ret.push_back(&sig);
|
||||||
|
std::sort(ret.begin(), ret.end(), [](auto l, auto r) { return l->start_bit < r->start_bit; });
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
// helper functions
|
// helper functions
|
||||||
|
|
||||||
static QVector<int> BIG_ENDIAN_START_BITS = []() {
|
static QVector<int> BIG_ENDIAN_START_BITS = []() {
|
||||||
@@ -246,7 +250,6 @@ bool dbcmanager::DBCManager::open(const QString &name, const QString &content, Q
|
|||||||
sig.offset = s.offset;
|
sig.offset = s.offset;
|
||||||
sig.is_little_endian = s.is_little_endian;
|
sig.is_little_endian = s.is_little_endian;
|
||||||
}
|
}
|
||||||
sortSignalsByAddress(m.sigs);
|
|
||||||
}
|
}
|
||||||
parseExtraInfo(content);
|
parseExtraInfo(content);
|
||||||
name_ = name;
|
name_ = name;
|
||||||
|
|||||||
@@ -52,12 +52,16 @@ struct Signal {
|
|||||||
struct Msg {
|
struct Msg {
|
||||||
QString name;
|
QString name;
|
||||||
uint32_t size;
|
uint32_t size;
|
||||||
QList<Signal> sigs;
|
|
||||||
|
|
||||||
|
std::vector<const Signal*> getSignals() const;
|
||||||
const Signal *sig(const QString &sig_name) const {
|
const Signal *sig(const QString &sig_name) const {
|
||||||
auto it = std::find_if(sigs.begin(), sigs.end(), [&](auto &s) { return s.name == sig_name; });
|
auto it = std::find_if(sigs.begin(), sigs.end(), [&](auto &s) { return s.name == sig_name; });
|
||||||
return it != sigs.end() ? &(*it) : nullptr;
|
return it != sigs.end() ? &(*it) : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
QList<Signal> sigs;
|
||||||
|
friend class DBCManager;
|
||||||
};
|
};
|
||||||
|
|
||||||
class DBCManager : public QObject {
|
class DBCManager : public QObject {
|
||||||
|
|||||||
@@ -3,15 +3,13 @@
|
|||||||
#include <QFormLayout>
|
#include <QFormLayout>
|
||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <QToolButton>
|
|
||||||
|
|
||||||
#include "tools/cabana/commands.h"
|
#include "tools/cabana/commands.h"
|
||||||
|
|
||||||
// DetailWidget
|
// DetailWidget
|
||||||
|
|
||||||
DetailWidget::DetailWidget(ChartsWidget *charts, QWidget *parent) : charts(charts), QWidget(parent) {
|
DetailWidget::DetailWidget(ChartsWidget *charts, QWidget *parent) : charts(charts), QWidget(parent) {
|
||||||
QWidget *main_widget = new QWidget(this);
|
QVBoxLayout *main_layout = new QVBoxLayout(this);
|
||||||
QVBoxLayout *main_layout = new QVBoxLayout(main_widget);
|
|
||||||
main_layout->setContentsMargins(0, 0, 0, 0);
|
main_layout->setContentsMargins(0, 0, 0, 0);
|
||||||
|
|
||||||
// tabbar
|
// tabbar
|
||||||
@@ -23,22 +21,22 @@ DetailWidget::DetailWidget(ChartsWidget *charts, QWidget *parent) : charts(chart
|
|||||||
main_layout->addWidget(tabbar);
|
main_layout->addWidget(tabbar);
|
||||||
|
|
||||||
// message title
|
// message title
|
||||||
QToolBar *toolbar = new QToolBar(this);
|
QHBoxLayout *title_layout = new QHBoxLayout();
|
||||||
toolbar->setIconSize({16, 16});
|
title_layout->setContentsMargins(0, 6, 0, 0);
|
||||||
toolbar->addWidget(new QLabel("time:"));
|
|
||||||
time_label = new QLabel(this);
|
time_label = new QLabel(this);
|
||||||
time_label->setStyleSheet("font-weight:bold");
|
time_label->setToolTip(tr("Current time"));
|
||||||
toolbar->addWidget(time_label);
|
time_label->setStyleSheet("QLabel{font-weight:bold;}");
|
||||||
|
title_layout->addWidget(time_label);
|
||||||
name_label = new ElidedLabel(this);
|
name_label = new ElidedLabel(this);
|
||||||
name_label->setContentsMargins(5, 0, 5, 0);
|
name_label->setStyleSheet("QLabel{font-weight:bold;}");
|
||||||
name_label->setStyleSheet("font-weight:bold;");
|
|
||||||
name_label->setAlignment(Qt::AlignCenter);
|
name_label->setAlignment(Qt::AlignCenter);
|
||||||
name_label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
|
name_label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
|
||||||
toolbar->addWidget(name_label);
|
title_layout->addWidget(name_label);
|
||||||
toolbar->addAction(utils::icon("pencil"), "", this, &DetailWidget::editMsg)->setToolTip(tr("Edit Message"));
|
auto edit_btn = toolButton("pencil", tr("Edit Message"));
|
||||||
remove_msg_act = toolbar->addAction(utils::icon("x-lg"), "", this, &DetailWidget::removeMsg);
|
title_layout->addWidget(edit_btn);
|
||||||
remove_msg_act->setToolTip(tr("Remove Message"));
|
remove_btn = toolButton("x-lg", tr("Remove Message"));
|
||||||
main_layout->addWidget(toolbar);
|
title_layout->addWidget(remove_btn);
|
||||||
|
main_layout->addLayout(title_layout);
|
||||||
|
|
||||||
// warning
|
// warning
|
||||||
warning_widget = new QWidget(this);
|
warning_widget = new QWidget(this);
|
||||||
@@ -59,19 +57,18 @@ DetailWidget::DetailWidget(ChartsWidget *charts, QWidget *parent) : charts(chart
|
|||||||
splitter->setStretchFactor(1, 1);
|
splitter->setStretchFactor(1, 1);
|
||||||
|
|
||||||
tab_widget = new QTabWidget(this);
|
tab_widget = new QTabWidget(this);
|
||||||
|
tab_widget->setStyleSheet("QTabWidget::pane {border: none; margin-bottom: -2px;}");
|
||||||
tab_widget->setTabPosition(QTabWidget::South);
|
tab_widget->setTabPosition(QTabWidget::South);
|
||||||
tab_widget->addTab(splitter, utils::icon("file-earmark-ruled"), "&Msg");
|
tab_widget->addTab(splitter, utils::icon("file-earmark-ruled"), "&Msg");
|
||||||
tab_widget->addTab(history_log = new LogsWidget(this), utils::icon("stopwatch"), "&Logs");
|
tab_widget->addTab(history_log = new LogsWidget(this), utils::icon("stopwatch"), "&Logs");
|
||||||
main_layout->addWidget(tab_widget);
|
main_layout->addWidget(tab_widget);
|
||||||
|
|
||||||
stacked_layout = new QStackedLayout(this);
|
QObject::connect(edit_btn, &QToolButton::clicked, this, &DetailWidget::editMsg);
|
||||||
stacked_layout->addWidget(new WelcomeWidget(this));
|
QObject::connect(remove_btn, &QToolButton::clicked, this, &DetailWidget::removeMsg);
|
||||||
stacked_layout->addWidget(main_widget);
|
|
||||||
|
|
||||||
QObject::connect(binary_view, &BinaryView::resizeSignal, signal_view->model, &SignalModel::resizeSignal);
|
QObject::connect(binary_view, &BinaryView::resizeSignal, signal_view->model, &SignalModel::resizeSignal);
|
||||||
QObject::connect(binary_view, &BinaryView::addSignal, signal_view->model, &SignalModel::addSignal);
|
QObject::connect(binary_view, &BinaryView::addSignal, signal_view->model, &SignalModel::addSignal);
|
||||||
QObject::connect(binary_view, &BinaryView::signalHovered, signal_view, &SignalView::signalHovered);
|
QObject::connect(binary_view, &BinaryView::signalHovered, signal_view, &SignalView::signalHovered);
|
||||||
QObject::connect(binary_view, &BinaryView::signalClicked, signal_view, &SignalView::expandSignal);
|
QObject::connect(binary_view, &BinaryView::signalClicked, [this](const Signal *s) { signal_view->selectSignal(s, true); });
|
||||||
QObject::connect(binary_view, &BinaryView::editSignal, signal_view->model, &SignalModel::saveSignal);
|
QObject::connect(binary_view, &BinaryView::editSignal, signal_view->model, &SignalModel::saveSignal);
|
||||||
QObject::connect(binary_view, &BinaryView::removeSignal, signal_view->model, &SignalModel::removeSignal);
|
QObject::connect(binary_view, &BinaryView::removeSignal, signal_view->model, &SignalModel::removeSignal);
|
||||||
QObject::connect(binary_view, &BinaryView::showChart, charts, &ChartsWidget::showChart);
|
QObject::connect(binary_view, &BinaryView::showChart, charts, &ChartsWidget::showChart);
|
||||||
@@ -87,9 +84,7 @@ DetailWidget::DetailWidget(ChartsWidget *charts, QWidget *parent) : charts(chart
|
|||||||
setMessage(tabbar->tabData(index).value<MessageId>());
|
setMessage(tabbar->tabData(index).value<MessageId>());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
QObject::connect(tabbar, &QTabBar::tabCloseRequested, [this](int index) {
|
QObject::connect(tabbar, &QTabBar::tabCloseRequested, tabbar, &QTabBar::removeTab);
|
||||||
tabbar->removeTab(index);
|
|
||||||
});
|
|
||||||
QObject::connect(charts, &ChartsWidget::seriesChanged, signal_view, &SignalView::updateChartState);
|
QObject::connect(charts, &ChartsWidget::seriesChanged, signal_view, &SignalView::updateChartState);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,18 +103,8 @@ void DetailWidget::showTabBarContextMenu(const QPoint &pt) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DetailWidget::removeAll() {
|
|
||||||
msg_id = std::nullopt;
|
|
||||||
tabbar->blockSignals(true);
|
|
||||||
while (tabbar->count() > 0) {
|
|
||||||
tabbar->removeTab(0);
|
|
||||||
}
|
|
||||||
tabbar->blockSignals(false);
|
|
||||||
stacked_layout->setCurrentIndex(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DetailWidget::setMessage(const MessageId &message_id) {
|
void DetailWidget::setMessage(const MessageId &message_id) {
|
||||||
msg_id = message_id;
|
if (std::exchange(msg_id, message_id) == message_id) return;
|
||||||
|
|
||||||
tabbar->blockSignals(true);
|
tabbar->blockSignals(true);
|
||||||
int index = tabbar->count() - 1;
|
int index = tabbar->count() - 1;
|
||||||
@@ -135,25 +120,18 @@ void DetailWidget::setMessage(const MessageId &message_id) {
|
|||||||
tabbar->blockSignals(false);
|
tabbar->blockSignals(false);
|
||||||
|
|
||||||
setUpdatesEnabled(false);
|
setUpdatesEnabled(false);
|
||||||
|
signal_view->setMessage(msg_id);
|
||||||
signal_view->setMessage(*msg_id);
|
binary_view->setMessage(msg_id);
|
||||||
binary_view->setMessage(*msg_id);
|
history_log->setMessage(msg_id);
|
||||||
history_log->setMessage(*msg_id);
|
|
||||||
|
|
||||||
stacked_layout->setCurrentIndex(1);
|
|
||||||
refresh();
|
refresh();
|
||||||
splitter->setSizes({1, 2});
|
|
||||||
|
|
||||||
setUpdatesEnabled(true);
|
setUpdatesEnabled(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DetailWidget::refresh() {
|
void DetailWidget::refresh() {
|
||||||
if (!msg_id) return;
|
|
||||||
|
|
||||||
QStringList warnings;
|
QStringList warnings;
|
||||||
auto msg = dbc()->msg(*msg_id);
|
auto msg = dbc()->msg(msg_id);
|
||||||
if (msg) {
|
if (msg) {
|
||||||
if (msg->size != can->lastMessage(*msg_id).dat.size()) {
|
if (msg->size != can->lastMessage(msg_id).dat.size()) {
|
||||||
warnings.push_back(tr("Message size (%1) is incorrect.").arg(msg->size));
|
warnings.push_back(tr("Message size (%1) is incorrect.").arg(msg->size));
|
||||||
}
|
}
|
||||||
for (auto s : binary_view->getOverlappingSignals()) {
|
for (auto s : binary_view->getOverlappingSignals()) {
|
||||||
@@ -162,8 +140,8 @@ void DetailWidget::refresh() {
|
|||||||
} else {
|
} else {
|
||||||
warnings.push_back(tr("Drag-Select in binary view to create new signal."));
|
warnings.push_back(tr("Drag-Select in binary view to create new signal."));
|
||||||
}
|
}
|
||||||
remove_msg_act->setEnabled(msg != nullptr);
|
remove_btn->setEnabled(msg != nullptr);
|
||||||
name_label->setText(msgName(*msg_id));
|
name_label->setText(msgName(msg_id));
|
||||||
|
|
||||||
if (!warnings.isEmpty()) {
|
if (!warnings.isEmpty()) {
|
||||||
warning_label->setText(warnings.join('\n'));
|
warning_label->setText(warnings.join('\n'));
|
||||||
@@ -174,7 +152,7 @@ void DetailWidget::refresh() {
|
|||||||
|
|
||||||
void DetailWidget::updateState(const QHash<MessageId, CanData> *msgs) {
|
void DetailWidget::updateState(const QHash<MessageId, CanData> *msgs) {
|
||||||
time_label->setText(QString::number(can->currentSec(), 'f', 3));
|
time_label->setText(QString::number(can->currentSec(), 'f', 3));
|
||||||
if (!msg_id || (msgs && !msgs->contains(*msg_id)))
|
if ((msgs && !msgs->contains(msg_id)))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (tab_widget->currentIndex() == 0)
|
if (tab_widget->currentIndex() == 0)
|
||||||
@@ -184,17 +162,16 @@ void DetailWidget::updateState(const QHash<MessageId, CanData> *msgs) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void DetailWidget::editMsg() {
|
void DetailWidget::editMsg() {
|
||||||
MessageId id = *msg_id;
|
auto msg = dbc()->msg(msg_id);
|
||||||
auto msg = dbc()->msg(id);
|
int size = msg ? msg->size : can->lastMessage(msg_id).dat.size();
|
||||||
int size = msg ? msg->size : can->lastMessage(id).dat.size();
|
EditMessageDialog dlg(msg_id, msgName(msg_id), size, this);
|
||||||
EditMessageDialog dlg(id, msgName(id), size, this);
|
|
||||||
if (dlg.exec()) {
|
if (dlg.exec()) {
|
||||||
UndoStack::push(new EditMsgCommand(*msg_id, dlg.name_edit->text(), dlg.size_spin->value()));
|
UndoStack::push(new EditMsgCommand(msg_id, dlg.name_edit->text(), dlg.size_spin->value()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DetailWidget::removeMsg() {
|
void DetailWidget::removeMsg() {
|
||||||
UndoStack::push(new RemoveMsgCommand(*msg_id));
|
UndoStack::push(new RemoveMsgCommand(msg_id));
|
||||||
}
|
}
|
||||||
|
|
||||||
// EditMessageDialog
|
// EditMessageDialog
|
||||||
@@ -240,10 +217,34 @@ void EditMessageDialog::validateName(const QString &text) {
|
|||||||
btn_box->button(QDialogButtonBox::Ok)->setEnabled(valid);
|
btn_box->button(QDialogButtonBox::Ok)->setEnabled(valid);
|
||||||
}
|
}
|
||||||
|
|
||||||
// WelcomeWidget
|
// CenterWidget
|
||||||
|
|
||||||
WelcomeWidget::WelcomeWidget(QWidget *parent) : QWidget(parent) {
|
CenterWidget::CenterWidget(ChartsWidget *charts, QWidget *parent) : charts(charts), QWidget(parent) {
|
||||||
QVBoxLayout *main_layout = new QVBoxLayout(this);
|
QVBoxLayout *main_layout = new QVBoxLayout(this);
|
||||||
|
main_layout->setContentsMargins(0, 0, 0, 0);
|
||||||
|
main_layout->addWidget(welcome_widget = createWelcomeWidget());
|
||||||
|
}
|
||||||
|
|
||||||
|
void CenterWidget::setMessage(const MessageId &msg_id) {
|
||||||
|
if (!detail_widget) {
|
||||||
|
delete welcome_widget;
|
||||||
|
welcome_widget = nullptr;
|
||||||
|
layout()->addWidget(detail_widget = new DetailWidget(charts, this));
|
||||||
|
}
|
||||||
|
detail_widget->setMessage(msg_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CenterWidget::clear() {
|
||||||
|
delete detail_widget;
|
||||||
|
detail_widget = nullptr;
|
||||||
|
if (!welcome_widget) {
|
||||||
|
layout()->addWidget(welcome_widget = createWelcomeWidget());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QWidget *CenterWidget::createWelcomeWidget() {
|
||||||
|
QWidget *w = new QWidget(this);
|
||||||
|
QVBoxLayout *main_layout = new QVBoxLayout(w);
|
||||||
main_layout->addStretch(0);
|
main_layout->addStretch(0);
|
||||||
QLabel *logo = new QLabel("CABANA");
|
QLabel *logo = new QLabel("CABANA");
|
||||||
logo->setAlignment(Qt::AlignCenter);
|
logo->setAlignment(Qt::AlignCenter);
|
||||||
@@ -268,7 +269,8 @@ WelcomeWidget::WelcomeWidget(QWidget *parent) : QWidget(parent) {
|
|||||||
main_layout->addLayout(newShortcutRow("WhatsThis", "Shift+F1"));
|
main_layout->addLayout(newShortcutRow("WhatsThis", "Shift+F1"));
|
||||||
main_layout->addStretch(0);
|
main_layout->addStretch(0);
|
||||||
|
|
||||||
setStyleSheet("QLabel{color:darkGray;}");
|
w->setStyleSheet("QLabel{color:darkGray;}");
|
||||||
setBackgroundRole(QPalette::Base);
|
w->setBackgroundRole(QPalette::Base);
|
||||||
setAutoFillBackground(true);
|
w->setAutoFillBackground(true);
|
||||||
|
return w;
|
||||||
}
|
}
|
||||||
|
|||||||
+16
-11
@@ -2,9 +2,7 @@
|
|||||||
|
|
||||||
#include <QDialogButtonBox>
|
#include <QDialogButtonBox>
|
||||||
#include <QSplitter>
|
#include <QSplitter>
|
||||||
#include <QStackedLayout>
|
|
||||||
#include <QTabWidget>
|
#include <QTabWidget>
|
||||||
#include <QToolBar>
|
|
||||||
|
|
||||||
#include "selfdrive/ui/qt/widgets/controls.h"
|
#include "selfdrive/ui/qt/widgets/controls.h"
|
||||||
#include "tools/cabana/binaryview.h"
|
#include "tools/cabana/binaryview.h"
|
||||||
@@ -24,11 +22,6 @@ public:
|
|||||||
QSpinBox *size_spin;
|
QSpinBox *size_spin;
|
||||||
};
|
};
|
||||||
|
|
||||||
class WelcomeWidget : public QWidget {
|
|
||||||
public:
|
|
||||||
WelcomeWidget(QWidget *parent);
|
|
||||||
};
|
|
||||||
|
|
||||||
class DetailWidget : public QWidget {
|
class DetailWidget : public QWidget {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
@@ -36,7 +29,6 @@ public:
|
|||||||
DetailWidget(ChartsWidget *charts, QWidget *parent);
|
DetailWidget(ChartsWidget *charts, QWidget *parent);
|
||||||
void setMessage(const MessageId &message_id);
|
void setMessage(const MessageId &message_id);
|
||||||
void refresh();
|
void refresh();
|
||||||
void removeAll();
|
|
||||||
QSize minimumSizeHint() const override { return binary_view->minimumSizeHint(); }
|
QSize minimumSizeHint() const override { return binary_view->minimumSizeHint(); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -45,17 +37,30 @@ private:
|
|||||||
void removeMsg();
|
void removeMsg();
|
||||||
void updateState(const QHash<MessageId, CanData> * msgs = nullptr);
|
void updateState(const QHash<MessageId, CanData> * msgs = nullptr);
|
||||||
|
|
||||||
std::optional<MessageId> msg_id;
|
MessageId msg_id;
|
||||||
QLabel *time_label, *warning_icon, *warning_label;
|
QLabel *time_label, *warning_icon, *warning_label;
|
||||||
ElidedLabel *name_label;
|
ElidedLabel *name_label;
|
||||||
QWidget *warning_widget;
|
QWidget *warning_widget;
|
||||||
QTabBar *tabbar;
|
QTabBar *tabbar;
|
||||||
QTabWidget *tab_widget;
|
QTabWidget *tab_widget;
|
||||||
QAction *remove_msg_act;
|
QToolButton *remove_btn;
|
||||||
LogsWidget *history_log;
|
LogsWidget *history_log;
|
||||||
BinaryView *binary_view;
|
BinaryView *binary_view;
|
||||||
SignalView *signal_view;
|
SignalView *signal_view;
|
||||||
ChartsWidget *charts;
|
ChartsWidget *charts;
|
||||||
QSplitter *splitter;
|
QSplitter *splitter;
|
||||||
QStackedLayout *stacked_layout;
|
};
|
||||||
|
|
||||||
|
class CenterWidget : public QWidget {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
CenterWidget(ChartsWidget* charts, QWidget *parent);
|
||||||
|
void setMessage(const MessageId &msg_id);
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QWidget *createWelcomeWidget();
|
||||||
|
DetailWidget *detail_widget = nullptr;
|
||||||
|
QWidget *welcome_widget = nullptr;
|
||||||
|
ChartsWidget *charts;
|
||||||
};
|
};
|
||||||
|
|||||||
+45
-31
@@ -5,8 +5,6 @@
|
|||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
|
|
||||||
#include "tools/cabana/commands.h"
|
#include "tools/cabana/commands.h"
|
||||||
#include "tools/cabana/util.h"
|
|
||||||
|
|
||||||
// HistoryLogModel
|
// HistoryLogModel
|
||||||
|
|
||||||
QVariant HistoryLogModel::data(const QModelIndex &index, int role) const {
|
QVariant HistoryLogModel::data(const QModelIndex &index, int role) const {
|
||||||
@@ -27,17 +25,19 @@ void HistoryLogModel::setMessage(const MessageId &message_id) {
|
|||||||
msg_id = message_id;
|
msg_id = message_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryLogModel::refresh() {
|
void HistoryLogModel::refresh(bool fetch_message) {
|
||||||
beginResetModel();
|
beginResetModel();
|
||||||
sigs.clear();
|
sigs.clear();
|
||||||
if (auto dbc_msg = dbc()->msg(*msg_id)) {
|
if (auto dbc_msg = dbc()->msg(msg_id)) {
|
||||||
sigs = dbc_msg->sigs;
|
sigs = dbc_msg->getSignals();
|
||||||
}
|
}
|
||||||
last_fetch_time = 0;
|
last_fetch_time = 0;
|
||||||
has_more_data = true;
|
has_more_data = true;
|
||||||
messages.clear();
|
messages.clear();
|
||||||
hex_colors.clear();
|
hex_colors.clear();
|
||||||
updateState();
|
if (fetch_message) {
|
||||||
|
updateState();
|
||||||
|
}
|
||||||
endResetModel();
|
endResetModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,9 +48,9 @@ QVariant HistoryLogModel::headerData(int section, Qt::Orientation orientation, i
|
|||||||
if (section == 0) {
|
if (section == 0) {
|
||||||
return "Time";
|
return "Time";
|
||||||
}
|
}
|
||||||
return show_signals ? sigs[section - 1].name : "Data";
|
return show_signals ? sigs[section - 1]->name : "Data";
|
||||||
} else if (role == Qt::BackgroundRole && section > 0 && show_signals) {
|
} else if (role == Qt::BackgroundRole && section > 0 && show_signals) {
|
||||||
return QBrush(getColor(&sigs[section - 1]));
|
return QBrush(getColor(sigs[section - 1]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
@@ -79,17 +79,15 @@ void HistoryLogModel::setFilter(int sig_idx, const QString &value, std::function
|
|||||||
}
|
}
|
||||||
|
|
||||||
void HistoryLogModel::updateState() {
|
void HistoryLogModel::updateState() {
|
||||||
if (msg_id) {
|
uint64_t current_time = (can->lastMessage(msg_id).ts + can->routeStartTime()) * 1e9 + 1;
|
||||||
uint64_t current_time = (can->lastMessage(*msg_id).ts + can->routeStartTime()) * 1e9 + 1;
|
auto new_msgs = dynamic_mode ? fetchData(current_time, last_fetch_time) : fetchData(0);
|
||||||
auto new_msgs = dynamic_mode ? fetchData(current_time, last_fetch_time) : fetchData(0);
|
if (!new_msgs.empty()) {
|
||||||
if (!new_msgs.empty()) {
|
beginInsertRows({}, 0, new_msgs.size() - 1);
|
||||||
beginInsertRows({}, 0, new_msgs.size() - 1);
|
messages.insert(messages.begin(), std::move_iterator(new_msgs.begin()), std::move_iterator(new_msgs.end()));
|
||||||
messages.insert(messages.begin(), std::move_iterator(new_msgs.begin()), std::move_iterator(new_msgs.end()));
|
endInsertRows();
|
||||||
endInsertRows();
|
|
||||||
}
|
|
||||||
has_more_data = new_msgs.size() >= batch_size;
|
|
||||||
last_fetch_time = current_time;
|
|
||||||
}
|
}
|
||||||
|
has_more_data = new_msgs.size() >= batch_size;
|
||||||
|
last_fetch_time = current_time;
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryLogModel::fetchMore(const QModelIndex &parent) {
|
void HistoryLogModel::fetchMore(const QModelIndex &parent) {
|
||||||
@@ -111,10 +109,10 @@ std::deque<HistoryLogModel::Message> HistoryLogModel::fetchData(InputIt first, I
|
|||||||
for (auto it = first; it != last && (*it)->mono_time > min_time; ++it) {
|
for (auto it = first; it != last && (*it)->mono_time > min_time; ++it) {
|
||||||
if ((*it)->which == cereal::Event::Which::CAN) {
|
if ((*it)->which == cereal::Event::Which::CAN) {
|
||||||
for (const auto &c : (*it)->event.getCan()) {
|
for (const auto &c : (*it)->event.getCan()) {
|
||||||
if (msg_id->address == c.getAddress() && msg_id->source == c.getSrc()) {
|
if (msg_id.address == c.getAddress() && msg_id.source == c.getSrc()) {
|
||||||
const auto dat = c.getDat();
|
const auto dat = c.getDat();
|
||||||
for (int i = 0; i < sigs.size(); ++i) {
|
for (int i = 0; i < sigs.size(); ++i) {
|
||||||
values[i] = get_raw_value((uint8_t *)dat.begin(), dat.size(), sigs[i]);
|
values[i] = get_raw_value((uint8_t *)dat.begin(), dat.size(), *sigs[i]);
|
||||||
}
|
}
|
||||||
if (!filter_cmp || filter_cmp(values[filter_sig_idx], filter_value)) {
|
if (!filter_cmp || filter_cmp(values[filter_sig_idx], filter_value)) {
|
||||||
auto &m = msgs.emplace_back();
|
auto &m = msgs.emplace_back();
|
||||||
@@ -136,7 +134,7 @@ template std::deque<HistoryLogModel::Message> HistoryLogModel::fetchData<>(std::
|
|||||||
|
|
||||||
std::deque<HistoryLogModel::Message> HistoryLogModel::fetchData(uint64_t from_time, uint64_t min_time) {
|
std::deque<HistoryLogModel::Message> HistoryLogModel::fetchData(uint64_t from_time, uint64_t min_time) {
|
||||||
auto events = can->events();
|
auto events = can->events();
|
||||||
const auto freq = can->lastMessage(*msg_id).freq;
|
const auto freq = can->lastMessage(msg_id).freq;
|
||||||
const bool update_colors = !display_signals_mode || sigs.empty();
|
const bool update_colors = !display_signals_mode || sigs.empty();
|
||||||
|
|
||||||
if (dynamic_mode) {
|
if (dynamic_mode) {
|
||||||
@@ -189,7 +187,8 @@ void HeaderView::paintSection(QPainter *painter, const QRect &rect, int logicalI
|
|||||||
|
|
||||||
// LogsWidget
|
// LogsWidget
|
||||||
|
|
||||||
LogsWidget::LogsWidget(QWidget *parent) : QWidget(parent) {
|
LogsWidget::LogsWidget(QWidget *parent) : QFrame(parent) {
|
||||||
|
setFrameStyle(QFrame::StyledPanel | QFrame::Plain);
|
||||||
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);
|
main_layout->setSpacing(0);
|
||||||
@@ -209,7 +208,8 @@ LogsWidget::LogsWidget(QWidget *parent) : QWidget(parent) {
|
|||||||
h->addStretch(0);
|
h->addStretch(0);
|
||||||
h->addWidget(dynamic_mode = new QCheckBox(tr("Dynamic")), 0, Qt::AlignRight);
|
h->addWidget(dynamic_mode = new QCheckBox(tr("Dynamic")), 0, Qt::AlignRight);
|
||||||
|
|
||||||
display_type_cb->addItems({"Signal Value", "Hex Value"});
|
display_type_cb->addItems({"Signal", "Hex"});
|
||||||
|
display_type_cb->setToolTip(tr("Display signal value or raw hex value"));
|
||||||
comp_box->addItems({">", "=", "!=", "<"});
|
comp_box->addItems({">", "=", "!=", "<"});
|
||||||
value_edit->setClearButtonEnabled(true);
|
value_edit->setClearButtonEnabled(true);
|
||||||
value_edit->setValidator(new QDoubleValidator(-500000, 500000, 6, this));
|
value_edit->setValidator(new QDoubleValidator(-500000, 500000, 6, this));
|
||||||
@@ -219,10 +219,10 @@ LogsWidget::LogsWidget(QWidget *parent) : QWidget(parent) {
|
|||||||
main_layout->addWidget(toolbar);
|
main_layout->addWidget(toolbar);
|
||||||
QFrame *line = new QFrame(this);
|
QFrame *line = new QFrame(this);
|
||||||
line->setFrameStyle(QFrame::HLine | QFrame::Sunken);
|
line->setFrameStyle(QFrame::HLine | QFrame::Sunken);
|
||||||
main_layout->addWidget(line);;
|
main_layout->addWidget(line);
|
||||||
|
|
||||||
main_layout->addWidget(logs = new QTableView(this));
|
main_layout->addWidget(logs = new QTableView(this));
|
||||||
logs->setModel(model = new HistoryLogModel(this));
|
logs->setModel(model = new HistoryLogModel(this));
|
||||||
|
delegate = new MessageBytesDelegate(this);
|
||||||
logs->setItemDelegateForColumn(1, new MessageBytesDelegate(this));
|
logs->setItemDelegateForColumn(1, new MessageBytesDelegate(this));
|
||||||
logs->setHorizontalHeader(new HeaderView(Qt::Horizontal, this));
|
logs->setHorizontalHeader(new HeaderView(Qt::Horizontal, this));
|
||||||
logs->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft | (Qt::Alignment)Qt::TextWordWrap);
|
logs->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft | (Qt::Alignment)Qt::TextWordWrap);
|
||||||
@@ -230,7 +230,10 @@ LogsWidget::LogsWidget(QWidget *parent) : QWidget(parent) {
|
|||||||
logs->verticalHeader()->setVisible(false);
|
logs->verticalHeader()->setVisible(false);
|
||||||
logs->setFrameShape(QFrame::NoFrame);
|
logs->setFrameShape(QFrame::NoFrame);
|
||||||
|
|
||||||
QObject::connect(display_type_cb, SIGNAL(activated(int)), model, SLOT(setDisplayType(int)));
|
QObject::connect(display_type_cb, qOverload<int>(&QComboBox::activated), [this](int index) {
|
||||||
|
logs->setItemDelegateForColumn(1, index == 1 ? delegate : nullptr);
|
||||||
|
model->setDisplayType(index);
|
||||||
|
});
|
||||||
QObject::connect(dynamic_mode, &QCheckBox::stateChanged, model, &HistoryLogModel::setDynamicMode);
|
QObject::connect(dynamic_mode, &QCheckBox::stateChanged, model, &HistoryLogModel::setDynamicMode);
|
||||||
QObject::connect(signals_cb, SIGNAL(activated(int)), this, SLOT(setFilter()));
|
QObject::connect(signals_cb, SIGNAL(activated(int)), this, SLOT(setFilter()));
|
||||||
QObject::connect(comp_box, SIGNAL(activated(int)), this, SLOT(setFilter()));
|
QObject::connect(comp_box, SIGNAL(activated(int)), this, SLOT(setFilter()));
|
||||||
@@ -247,17 +250,16 @@ void LogsWidget::setMessage(const MessageId &message_id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void LogsWidget::refresh() {
|
void LogsWidget::refresh() {
|
||||||
if (!model->msg_id) return;
|
|
||||||
|
|
||||||
model->setFilter(0, "", nullptr);
|
model->setFilter(0, "", nullptr);
|
||||||
model->refresh();
|
model->refresh(isVisible());
|
||||||
bool has_signal = model->sigs.size();
|
bool has_signal = model->sigs.size();
|
||||||
if (has_signal) {
|
if (has_signal) {
|
||||||
signals_cb->clear();
|
signals_cb->clear();
|
||||||
for (auto &s : model->sigs) {
|
for (auto s : model->sigs) {
|
||||||
signals_cb->addItem(s.name);
|
signals_cb->addItem(s->name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
logs->setItemDelegateForColumn(1, !has_signal || display_type_cb->currentIndex() == 1 ? delegate : nullptr);
|
||||||
value_edit->clear();
|
value_edit->clear();
|
||||||
comp_box->setCurrentIndex(0);
|
comp_box->setCurrentIndex(0);
|
||||||
filters_widget->setVisible(has_signal);
|
filters_widget->setVisible(has_signal);
|
||||||
@@ -276,3 +278,15 @@ void LogsWidget::setFilter() {
|
|||||||
model->setFilter(signals_cb->currentIndex(), value_edit->text(), cmp);
|
model->setFilter(signals_cb->currentIndex(), value_edit->text(), cmp);
|
||||||
model->refresh();
|
model->refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LogsWidget::updateState() {
|
||||||
|
if (isVisible() && dynamic_mode->isChecked()) {
|
||||||
|
model->updateState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LogsWidget::showEvent(QShowEvent *event) {
|
||||||
|
if (dynamic_mode->isChecked() || model->canFetchMore({}) && model->rowCount() == 0) {
|
||||||
|
model->refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <deque>
|
#include <deque>
|
||||||
#include <optional>
|
|
||||||
|
|
||||||
#include <QCheckBox>
|
#include <QCheckBox>
|
||||||
#include <QComboBox>
|
#include <QComboBox>
|
||||||
#include <QHeaderView>
|
#include <QHeaderView>
|
||||||
@@ -11,6 +9,8 @@
|
|||||||
|
|
||||||
#include "tools/cabana/dbcmanager.h"
|
#include "tools/cabana/dbcmanager.h"
|
||||||
#include "tools/cabana/streams/abstractstream.h"
|
#include "tools/cabana/streams/abstractstream.h"
|
||||||
|
#include "tools/cabana/util.h"
|
||||||
|
|
||||||
using namespace dbcmanager;
|
using namespace dbcmanager;
|
||||||
|
|
||||||
class HeaderView : public QHeaderView {
|
class HeaderView : public QHeaderView {
|
||||||
@@ -36,7 +36,7 @@ public:
|
|||||||
int columnCount(const QModelIndex &parent = QModelIndex()) const override {
|
int columnCount(const QModelIndex &parent = QModelIndex()) const override {
|
||||||
return display_signals_mode && !sigs.empty() ? sigs.size() + 1 : 2;
|
return display_signals_mode && !sigs.empty() ? sigs.size() + 1 : 2;
|
||||||
}
|
}
|
||||||
void refresh();
|
void refresh(bool fetch_message = true);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void setDisplayType(int type);
|
void setDisplayType(int type);
|
||||||
@@ -55,7 +55,7 @@ public:
|
|||||||
std::deque<HistoryLogModel::Message> fetchData(InputIt first, InputIt last, uint64_t min_time);
|
std::deque<HistoryLogModel::Message> fetchData(InputIt first, InputIt last, uint64_t min_time);
|
||||||
std::deque<Message> fetchData(uint64_t from_time, uint64_t min_time = 0);
|
std::deque<Message> fetchData(uint64_t from_time, uint64_t min_time = 0);
|
||||||
|
|
||||||
std::optional<MessageId> msg_id;
|
MessageId msg_id;
|
||||||
ChangeTracker hex_colors;
|
ChangeTracker hex_colors;
|
||||||
bool has_more_data = true;
|
bool has_more_data = true;
|
||||||
const int batch_size = 50;
|
const int batch_size = 50;
|
||||||
@@ -64,19 +64,19 @@ public:
|
|||||||
uint64_t last_fetch_time = 0;
|
uint64_t last_fetch_time = 0;
|
||||||
std::function<bool(double, double)> filter_cmp = nullptr;
|
std::function<bool(double, double)> filter_cmp = nullptr;
|
||||||
std::deque<Message> messages;
|
std::deque<Message> messages;
|
||||||
QList<Signal> sigs;
|
std::vector<const Signal *> sigs;
|
||||||
bool dynamic_mode = true;
|
bool dynamic_mode = true;
|
||||||
bool display_signals_mode = true;
|
bool display_signals_mode = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
class LogsWidget : public QWidget {
|
class LogsWidget : public QFrame {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
LogsWidget(QWidget *parent);
|
LogsWidget(QWidget *parent);
|
||||||
void setMessage(const MessageId &message_id);
|
void setMessage(const MessageId &message_id);
|
||||||
void updateState() {if (dynamic_mode->isChecked()) model->updateState(); }
|
void updateState();
|
||||||
void showEvent(QShowEvent *event) override { if (dynamic_mode->isChecked()) model->refresh(); }
|
void showEvent(QShowEvent *event) override;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void setFilter();
|
void setFilter();
|
||||||
@@ -90,4 +90,5 @@ private:
|
|||||||
QComboBox *signals_cb, *comp_box, *display_type_cb;
|
QComboBox *signals_cb, *comp_box, *display_type_cb;
|
||||||
QLineEdit *value_edit;
|
QLineEdit *value_edit;
|
||||||
QWidget *filters_widget;
|
QWidget *filters_widget;
|
||||||
|
MessageBytesDelegate *delegate;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -27,8 +27,8 @@ void qLogMessageHandler(QtMsgType type, const QMessageLogContext &context, const
|
|||||||
|
|
||||||
MainWindow::MainWindow() : QMainWindow() {
|
MainWindow::MainWindow() : QMainWindow() {
|
||||||
createDockWindows();
|
createDockWindows();
|
||||||
detail_widget = new DetailWidget(charts_widget, this);
|
center_widget = new CenterWidget(charts_widget, this);
|
||||||
setCentralWidget(detail_widget);
|
setCentralWidget(center_widget);
|
||||||
createActions();
|
createActions();
|
||||||
createStatusBar();
|
createStatusBar();
|
||||||
createShortcuts();
|
createShortcuts();
|
||||||
@@ -60,7 +60,7 @@ MainWindow::MainWindow() : QMainWindow() {
|
|||||||
|
|
||||||
QObject::connect(this, &MainWindow::showMessage, statusBar(), &QStatusBar::showMessage);
|
QObject::connect(this, &MainWindow::showMessage, statusBar(), &QStatusBar::showMessage);
|
||||||
QObject::connect(this, &MainWindow::updateProgressBar, this, &MainWindow::updateDownloadProgress);
|
QObject::connect(this, &MainWindow::updateProgressBar, this, &MainWindow::updateDownloadProgress);
|
||||||
QObject::connect(messages_widget, &MessagesWidget::msgSelectionChanged, detail_widget, &DetailWidget::setMessage);
|
QObject::connect(messages_widget, &MessagesWidget::msgSelectionChanged, center_widget, &CenterWidget::setMessage);
|
||||||
QObject::connect(charts_widget, &ChartsWidget::dock, this, &MainWindow::dockCharts);
|
QObject::connect(charts_widget, &ChartsWidget::dock, this, &MainWindow::dockCharts);
|
||||||
QObject::connect(can, &AbstractStream::streamStarted, this, &MainWindow::loadDBCFromFingerprint);
|
QObject::connect(can, &AbstractStream::streamStarted, this, &MainWindow::loadDBCFromFingerprint);
|
||||||
QObject::connect(dbc(), &DBCManager::DBCFileChanged, this, &MainWindow::DBCFileChanged);
|
QObject::connect(dbc(), &DBCManager::DBCFileChanged, this, &MainWindow::DBCFileChanged);
|
||||||
@@ -175,7 +175,7 @@ void MainWindow::createStatusBar() {
|
|||||||
progress_bar = new QProgressBar();
|
progress_bar = new QProgressBar();
|
||||||
progress_bar->setRange(0, 100);
|
progress_bar->setRange(0, 100);
|
||||||
progress_bar->setTextVisible(true);
|
progress_bar->setTextVisible(true);
|
||||||
progress_bar->setFixedSize({230, 16});
|
progress_bar->setFixedSize({300, 16});
|
||||||
progress_bar->setVisible(false);
|
progress_bar->setVisible(false);
|
||||||
statusBar()->addWidget(new QLabel(tr("For Help, Press F1")));
|
statusBar()->addWidget(new QLabel(tr("For Help, Press F1")));
|
||||||
statusBar()->addPermanentWidget(progress_bar);
|
statusBar()->addPermanentWidget(progress_bar);
|
||||||
@@ -220,7 +220,7 @@ void MainWindow::DBCFileChanged() {
|
|||||||
void MainWindow::openRoute() {
|
void MainWindow::openRoute() {
|
||||||
OpenRouteDialog dlg(this);
|
OpenRouteDialog dlg(this);
|
||||||
if (dlg.exec()) {
|
if (dlg.exec()) {
|
||||||
detail_widget->removeAll();
|
center_widget->clear();
|
||||||
charts_widget->removeAll();
|
charts_widget->removeAll();
|
||||||
statusBar()->showMessage(tr("Route %1 loaded").arg(can->routeName()), 2000);
|
statusBar()->showMessage(tr("Route %1 loaded").arg(can->routeName()), 2000);
|
||||||
} else if (dlg.failedToLoad()) {
|
} else if (dlg.failedToLoad()) {
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ protected:
|
|||||||
VideoWidget *video_widget = nullptr;
|
VideoWidget *video_widget = nullptr;
|
||||||
QDockWidget *video_dock;
|
QDockWidget *video_dock;
|
||||||
MessagesWidget *messages_widget;
|
MessagesWidget *messages_widget;
|
||||||
DetailWidget *detail_widget;
|
CenterWidget *center_widget;
|
||||||
ChartsWidget *charts_widget;
|
ChartsWidget *charts_widget;
|
||||||
QWidget *floating_window = nullptr;
|
QWidget *floating_window = nullptr;
|
||||||
QVBoxLayout *charts_layout;
|
QVBoxLayout *charts_layout;
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
#include "tools/cabana/messageswidget.h"
|
#include "tools/cabana/messageswidget.h"
|
||||||
|
|
||||||
#include <QApplication>
|
|
||||||
#include <QFontDatabase>
|
|
||||||
#include <QHBoxLayout>
|
#include <QHBoxLayout>
|
||||||
#include <QPainter>
|
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
|
|
||||||
MessagesWidget::MessagesWidget(QWidget *parent) : QWidget(parent) {
|
MessagesWidget::MessagesWidget(QWidget *parent) : QWidget(parent) {
|
||||||
QVBoxLayout *main_layout = new QVBoxLayout(this);
|
QVBoxLayout *main_layout = new QVBoxLayout(this);
|
||||||
|
main_layout->setContentsMargins(0 ,0, 0, 0);
|
||||||
|
|
||||||
// message filter
|
// message filter
|
||||||
filter = new QLineEdit(this);
|
filter = new QLineEdit(this);
|
||||||
|
QRegularExpression re("\\S+");
|
||||||
|
filter->setValidator(new QRegularExpressionValidator(re, this));
|
||||||
filter->setClearButtonEnabled(true);
|
filter->setClearButtonEnabled(true);
|
||||||
filter->setPlaceholderText(tr("filter messages"));
|
filter->setPlaceholderText(tr("filter messages"));
|
||||||
main_layout->addWidget(filter);
|
main_layout->addWidget(filter);
|
||||||
@@ -44,11 +44,16 @@ MessagesWidget::MessagesWidget(QWidget *parent) : QWidget(parent) {
|
|||||||
QObject::connect(dbc(), &DBCManager::DBCFileChanged, model, &MessageListModel::sortMessages);
|
QObject::connect(dbc(), &DBCManager::DBCFileChanged, model, &MessageListModel::sortMessages);
|
||||||
QObject::connect(dbc(), &DBCManager::msgUpdated, model, &MessageListModel::sortMessages);
|
QObject::connect(dbc(), &DBCManager::msgUpdated, model, &MessageListModel::sortMessages);
|
||||||
QObject::connect(dbc(), &DBCManager::msgRemoved, model, &MessageListModel::sortMessages);
|
QObject::connect(dbc(), &DBCManager::msgRemoved, model, &MessageListModel::sortMessages);
|
||||||
QObject::connect(model, &MessageListModel::modelReset, [this]() { selectMessage(*current_msg_id); });
|
QObject::connect(model, &MessageListModel::modelReset, [this]() {
|
||||||
|
if (current_msg_id) {
|
||||||
|
selectMessage(*current_msg_id);
|
||||||
|
}
|
||||||
|
});
|
||||||
QObject::connect(table_widget->selectionModel(), &QItemSelectionModel::currentChanged, [=](const QModelIndex ¤t, const QModelIndex &previous) {
|
QObject::connect(table_widget->selectionModel(), &QItemSelectionModel::currentChanged, [=](const QModelIndex ¤t, const QModelIndex &previous) {
|
||||||
if (current.isValid() && current.row() < model->msgs.size()) {
|
if (current.isValid() && current.row() < model->msgs.size()) {
|
||||||
if (model->msgs[current.row()] != *current_msg_id) {
|
auto &id = model->msgs[current.row()];
|
||||||
current_msg_id = model->msgs[current.row()];
|
if (!current_msg_id || id != *current_msg_id) {
|
||||||
|
current_msg_id = id;
|
||||||
emit msgSelectionChanged(*current_msg_id);
|
emit msgSelectionChanged(*current_msg_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -67,10 +72,10 @@ MessagesWidget::MessagesWidget(QWidget *parent) : QWidget(parent) {
|
|||||||
setWhatsThis(tr(R"(
|
setWhatsThis(tr(R"(
|
||||||
<b>Message View</b><br/>
|
<b>Message View</b><br/>
|
||||||
<!-- TODO: add descprition here -->
|
<!-- TODO: add descprition here -->
|
||||||
Byte color: <br />
|
<span style="color:gray">Byte color</span><br />
|
||||||
<span style="color:gray;">■ </span> constant changing<br />
|
<span style="color:gray;">■ </span> constant changing<br />
|
||||||
<span style="color:blue;">■ </span> increasing<br />
|
<span style="color:blue;">■ </span> increasing<br />
|
||||||
<span style="color:red;">■ </span> decreasing <br />
|
<span style="color:red;">■ </span> decreasing
|
||||||
)"));
|
)"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,9 +96,10 @@ void MessagesWidget::updateSuppressedButtons() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MessagesWidget::reset() {
|
void MessagesWidget::reset() {
|
||||||
|
current_msg_id = std::nullopt;
|
||||||
|
table_widget->selectionModel()->clear();
|
||||||
model->reset();
|
model->reset();
|
||||||
filter->clear();
|
filter->clear();
|
||||||
current_msg_id = std::nullopt;
|
|
||||||
updateSuppressedButtons();
|
updateSuppressedButtons();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,8 +144,8 @@ void MessageListModel::setFilterString(const QString &string) {
|
|||||||
if (id.toString().contains(txt, cs) || msgName(id).contains(txt, cs)) return true;
|
if (id.toString().contains(txt, cs) || msgName(id).contains(txt, cs)) return true;
|
||||||
// Search by signal name
|
// Search by signal name
|
||||||
if (const auto msg = dbc()->msg(id)) {
|
if (const auto msg = dbc()->msg(id)) {
|
||||||
for (auto &signal : msg->sigs) {
|
for (auto s : msg->getSignals()) {
|
||||||
if (signal.name.contains(txt, cs)) return true;
|
if (s->name.contains(txt, cs)) return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <optional>
|
|
||||||
|
|
||||||
#include <QAbstractTableModel>
|
#include <QAbstractTableModel>
|
||||||
#include <QHeaderView>
|
#include <QHeaderView>
|
||||||
#include <QLineEdit>
|
#include <QLineEdit>
|
||||||
#include <QSet>
|
#include <QSet>
|
||||||
#include <QStyledItemDelegate>
|
|
||||||
#include <QTableView>
|
#include <QTableView>
|
||||||
|
|
||||||
#include "tools/cabana/dbcmanager.h"
|
#include "tools/cabana/dbcmanager.h"
|
||||||
|
|||||||
+42
-41
@@ -6,7 +6,6 @@
|
|||||||
#include <QHeaderView>
|
#include <QHeaderView>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
#include <QToolButton>
|
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
|
|
||||||
#include "tools/cabana/commands.h"
|
#include "tools/cabana/commands.h"
|
||||||
@@ -48,9 +47,9 @@ void SignalModel::refresh() {
|
|||||||
beginResetModel();
|
beginResetModel();
|
||||||
root.reset(new SignalModel::Item);
|
root.reset(new SignalModel::Item);
|
||||||
if (auto msg = dbc()->msg(msg_id)) {
|
if (auto msg = dbc()->msg(msg_id)) {
|
||||||
for (auto &s : msg->sigs) {
|
for (auto s : msg->getSignals()) {
|
||||||
if (filter_str.isEmpty() || s.name.contains(filter_str, Qt::CaseInsensitive)) {
|
if (filter_str.isEmpty() || s->name.contains(filter_str, Qt::CaseInsensitive)) {
|
||||||
insertItem(root.get(), root->children.size(), &s);
|
insertItem(root.get(), root->children.size(), s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -58,7 +57,7 @@ void SignalModel::refresh() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SignalModel::updateState(const QHash<MessageId, CanData> *msgs) {
|
void SignalModel::updateState(const QHash<MessageId, CanData> *msgs) {
|
||||||
if (!msgs || (msgs->contains(msg_id))) {
|
if (!msgs || msgs->contains(msg_id)) {
|
||||||
auto &dat = can->lastMessage(msg_id).dat;
|
auto &dat = can->lastMessage(msg_id).dat;
|
||||||
int row = 0;
|
int row = 0;
|
||||||
for (auto item : root->children) {
|
for (auto item : root->children) {
|
||||||
@@ -93,9 +92,8 @@ Qt::ItemFlags SignalModel::flags(const QModelIndex &index) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int SignalModel::signalRow(const Signal *sig) const {
|
int SignalModel::signalRow(const Signal *sig) const {
|
||||||
auto &children = root->children;
|
for (int i = 0; i < root->children.size(); ++i) {
|
||||||
for (int i = 0; i < children.size(); ++i) {
|
if (root->children[i]->sig == sig) return i;
|
||||||
if (children[i]->sig == sig) return i;
|
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -129,11 +127,11 @@ QVariant SignalModel::data(const QModelIndex &index, int role) const {
|
|||||||
case Item::Min: return item->sig->min;
|
case Item::Min: return item->sig->min;
|
||||||
case Item::Max: return item->sig->max;
|
case Item::Max: return item->sig->max;
|
||||||
case Item::Desc: {
|
case Item::Desc: {
|
||||||
QString val_desc;
|
QStringList val_desc;
|
||||||
for (auto &[val, desc] : item->sig->val_desc) {
|
for (auto &[val, desc] : item->sig->val_desc) {
|
||||||
val_desc += QString("%1 \"%2\"").arg(val, desc);
|
val_desc << QString("%1 \"%2\"").arg(val, desc);
|
||||||
}
|
}
|
||||||
return val_desc;
|
return val_desc.join(" ");
|
||||||
}
|
}
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
@@ -280,16 +278,24 @@ void SignalModel::handleSignalRemoved(const Signal *sig) {
|
|||||||
|
|
||||||
// SignalItemDelegate
|
// SignalItemDelegate
|
||||||
|
|
||||||
SignalItemDelegate::SignalItemDelegate(QObject *parent) {
|
SignalItemDelegate::SignalItemDelegate(QObject *parent) : QStyledItemDelegate(parent) {
|
||||||
name_validator = new NameValidator(this);
|
name_validator = new NameValidator(this);
|
||||||
double_validator = new QDoubleValidator(this);
|
double_validator = new QDoubleValidator(this);
|
||||||
double_validator->setLocale(QLocale::C); // Match locale of QString::toDouble() instead of system
|
double_validator->setLocale(QLocale::C); // Match locale of QString::toDouble() instead of system
|
||||||
small_font.setPointSize(8);
|
small_font.setPointSize(8);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QSize SignalItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const {
|
||||||
|
QSize size = QStyledItemDelegate::sizeHint(option, index);
|
||||||
|
if (!index.parent().isValid() && index.column() == 0) {
|
||||||
|
size.rwidth() = std::min(((QWidget*)parent())->size().width() / 2, size.width() + color_label_width + 8);
|
||||||
|
}
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
void SignalItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const {
|
void SignalItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const {
|
||||||
auto item = (SignalModel::Item *)index.internalPointer();
|
auto item = (SignalModel::Item *)index.internalPointer();
|
||||||
if (item && !index.parent().isValid() && index.column() == 0) {
|
if (index.column() == 0 && item && item->type == SignalModel::Item::Sig) {
|
||||||
painter->save();
|
painter->save();
|
||||||
painter->setRenderHint(QPainter::Antialiasing);
|
painter->setRenderHint(QPainter::Antialiasing);
|
||||||
if (option.state & QStyle::State_Selected) {
|
if (option.state & QStyle::State_Selected) {
|
||||||
@@ -298,7 +304,7 @@ void SignalItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &op
|
|||||||
|
|
||||||
// color label
|
// color label
|
||||||
auto bg_color = getColor(item->sig);
|
auto bg_color = getColor(item->sig);
|
||||||
QRect rc{option.rect.left(), option.rect.top(), 18, option.rect.height()};
|
QRect rc{option.rect.left(), option.rect.top(), color_label_width, option.rect.height()};
|
||||||
painter->setPen(Qt::NoPen);
|
painter->setPen(Qt::NoPen);
|
||||||
painter->setBrush(item->highlight ? bg_color.darker(125) : bg_color);
|
painter->setBrush(item->highlight ? bg_color.darker(125) : bg_color);
|
||||||
painter->drawRoundedRect(rc.adjusted(0, 2, 0, -2), 3, 3);
|
painter->drawRoundedRect(rc.adjusted(0, 2, 0, -2), 3, 3);
|
||||||
@@ -345,22 +351,22 @@ QWidget *SignalItemDelegate::createEditor(QWidget *parent, const QStyleOptionVie
|
|||||||
|
|
||||||
// SignalView
|
// SignalView
|
||||||
|
|
||||||
SignalView::SignalView(ChartsWidget *charts, QWidget *parent) : charts(charts), QWidget(parent) {
|
SignalView::SignalView(ChartsWidget *charts, QWidget *parent) : charts(charts), QFrame(parent) {
|
||||||
|
setFrameStyle(QFrame::StyledPanel | QFrame::Plain);
|
||||||
// title bar
|
// title bar
|
||||||
QWidget *title_bar = new QWidget(this);
|
QWidget *title_bar = new QWidget(this);
|
||||||
title_bar->setAutoFillBackground(true);
|
title_bar->setAutoFillBackground(true);
|
||||||
QHBoxLayout *hl = new QHBoxLayout(title_bar);
|
QHBoxLayout *hl = new QHBoxLayout(title_bar);
|
||||||
hl->addWidget(signal_count_lb = new QLabel());
|
hl->addWidget(signal_count_lb = new QLabel());
|
||||||
filter_edit = new QLineEdit(this);
|
filter_edit = new QLineEdit(this);
|
||||||
|
QRegularExpression re("\\S+");
|
||||||
|
filter_edit->setValidator(new QRegularExpressionValidator(re, this));
|
||||||
filter_edit->setClearButtonEnabled(true);
|
filter_edit->setClearButtonEnabled(true);
|
||||||
filter_edit->setPlaceholderText(tr("filter signals by name"));
|
filter_edit->setPlaceholderText(tr("filter signals"));
|
||||||
hl->addWidget(filter_edit);
|
hl->addWidget(filter_edit);
|
||||||
hl->addStretch(1);
|
hl->addStretch(1);
|
||||||
auto collapse_btn = new QToolButton();
|
auto collapse_btn = toolButton("dash-square", tr("Collapse All"));
|
||||||
collapse_btn->setIcon(utils::icon("dash-square"));
|
|
||||||
collapse_btn->setIconSize({12, 12});
|
collapse_btn->setIconSize({12, 12});
|
||||||
collapse_btn->setAutoRaise(true);
|
|
||||||
collapse_btn->setToolTip(tr("Collapse All"));
|
|
||||||
hl->addWidget(collapse_btn);
|
hl->addWidget(collapse_btn);
|
||||||
|
|
||||||
// tree view
|
// tree view
|
||||||
@@ -371,7 +377,8 @@ SignalView::SignalView(ChartsWidget *charts, QWidget *parent) : charts(charts),
|
|||||||
tree->setHeaderHidden(true);
|
tree->setHeaderHidden(true);
|
||||||
tree->setMouseTracking(true);
|
tree->setMouseTracking(true);
|
||||||
tree->setExpandsOnDoubleClick(false);
|
tree->setExpandsOnDoubleClick(false);
|
||||||
tree->header()->setSectionResizeMode(QHeaderView::Stretch);
|
tree->header()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
|
||||||
|
tree->header()->setStretchLastSection(true);
|
||||||
tree->setMinimumHeight(300);
|
tree->setMinimumHeight(300);
|
||||||
tree->setStyleSheet("QSpinBox{background-color:white;border:none;} QLineEdit{background-color:white;}");
|
tree->setStyleSheet("QSpinBox{background-color:white;border:none;} QLineEdit{background-color:white;}");
|
||||||
|
|
||||||
@@ -389,7 +396,7 @@ SignalView::SignalView(ChartsWidget *charts, QWidget *parent) : charts(charts),
|
|||||||
QObject::connect(model, &QAbstractItemModel::modelReset, this, &SignalView::rowsChanged);
|
QObject::connect(model, &QAbstractItemModel::modelReset, this, &SignalView::rowsChanged);
|
||||||
QObject::connect(model, &QAbstractItemModel::rowsInserted, this, &SignalView::rowsChanged);
|
QObject::connect(model, &QAbstractItemModel::rowsInserted, this, &SignalView::rowsChanged);
|
||||||
QObject::connect(model, &QAbstractItemModel::rowsRemoved, this, &SignalView::rowsChanged);
|
QObject::connect(model, &QAbstractItemModel::rowsRemoved, this, &SignalView::rowsChanged);
|
||||||
QObject::connect(dbc(), &DBCManager::signalAdded, [this](uint32_t address, const Signal *sig) { expandSignal(sig); });
|
QObject::connect(dbc(), &DBCManager::signalAdded, [this](uint32_t address, const Signal *sig) { selectSignal(sig); });
|
||||||
|
|
||||||
setWhatsThis(tr(R"(
|
setWhatsThis(tr(R"(
|
||||||
<b>Signal view</b><br />
|
<b>Signal view</b><br />
|
||||||
@@ -404,16 +411,6 @@ void SignalView::setMessage(const MessageId &id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SignalView::rowsChanged() {
|
void SignalView::rowsChanged() {
|
||||||
auto create_btn = [](const QString &id, const QString &tooltip) {
|
|
||||||
auto btn = new QToolButton();
|
|
||||||
btn->setIcon(utils::icon(id));
|
|
||||||
btn->setToolTip(tooltip);
|
|
||||||
btn->setAutoRaise(true);
|
|
||||||
return btn;
|
|
||||||
};
|
|
||||||
|
|
||||||
signal_count_lb->setText(tr("Signals: %1").arg(model->rowCount()));
|
|
||||||
|
|
||||||
for (int i = 0; i < model->rowCount(); ++i) {
|
for (int i = 0; i < model->rowCount(); ++i) {
|
||||||
auto index = model->index(i, 1);
|
auto index = model->index(i, 1);
|
||||||
if (!tree->indexWidget(index)) {
|
if (!tree->indexWidget(index)) {
|
||||||
@@ -422,8 +419,8 @@ void SignalView::rowsChanged() {
|
|||||||
h->setContentsMargins(0, 2, 0, 2);
|
h->setContentsMargins(0, 2, 0, 2);
|
||||||
h->addStretch(1);
|
h->addStretch(1);
|
||||||
|
|
||||||
auto remove_btn = create_btn("x", tr("Remove signal"));
|
auto remove_btn = toolButton("x", tr("Remove signal"));
|
||||||
auto plot_btn = create_btn("graph-up", "");
|
auto plot_btn = toolButton("graph-up", "");
|
||||||
plot_btn->setCheckable(true);
|
plot_btn->setCheckable(true);
|
||||||
h->addWidget(plot_btn);
|
h->addWidget(plot_btn);
|
||||||
h->addWidget(remove_btn);
|
h->addWidget(remove_btn);
|
||||||
@@ -436,6 +433,7 @@ void SignalView::rowsChanged() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
signal_count_lb->setText(tr("Signals: %1").arg(model->rowCount()));
|
||||||
updateChartState();
|
updateChartState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -449,23 +447,26 @@ void SignalView::rowClicked(const QModelIndex &index) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SignalView::expandSignal(const Signal *sig) {
|
void SignalView::selectSignal(const Signal *sig, bool expand) {
|
||||||
if (int row = model->signalRow(sig); row != -1) {
|
if (int row = model->signalRow(sig); row != -1) {
|
||||||
auto idx = model->index(row, 0);
|
auto idx = model->index(row, 0);
|
||||||
bool expand = !tree->isExpanded(idx);
|
if (expand) {
|
||||||
tree->setExpanded(idx, expand);
|
tree->setExpanded(idx, !tree->isExpanded(idx));
|
||||||
|
}
|
||||||
tree->scrollTo(idx, QAbstractItemView::PositionAtTop);
|
tree->scrollTo(idx, QAbstractItemView::PositionAtTop);
|
||||||
if (expand) tree->setCurrentIndex(idx);
|
tree->setCurrentIndex(idx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SignalView::updateChartState() {
|
void SignalView::updateChartState() {
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (auto item : model->root->children) {
|
for (auto item : model->root->children) {
|
||||||
auto plot_btn = tree->indexWidget(model->index(i, 1))->findChildren<QToolButton *>()[0];
|
|
||||||
bool chart_opened = charts->hasSignal(msg_id, item->sig);
|
bool chart_opened = charts->hasSignal(msg_id, item->sig);
|
||||||
plot_btn->setChecked(chart_opened);
|
auto buttons = tree->indexWidget(model->index(i, 1))->findChildren<QToolButton *>();
|
||||||
plot_btn->setToolTip(chart_opened ? tr("Close Plot") : tr("Show Plot\nSHIFT click to add to previous opened plot"));
|
if (buttons.size() > 0) {
|
||||||
|
buttons[0]->setChecked(chart_opened);
|
||||||
|
buttons[0]->setToolTip(chart_opened ? tr("Close Plot") : tr("Show Plot\nSHIFT click to add to previous opened plot"));
|
||||||
|
}
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,12 +80,14 @@ class SignalItemDelegate : public QStyledItemDelegate {
|
|||||||
public:
|
public:
|
||||||
SignalItemDelegate(QObject *parent);
|
SignalItemDelegate(QObject *parent);
|
||||||
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
|
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
|
||||||
|
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const;
|
||||||
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
|
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
|
||||||
QValidator *name_validator, *double_validator;
|
QValidator *name_validator, *double_validator;
|
||||||
QFont small_font;
|
QFont small_font;
|
||||||
|
const int color_label_width = 18;
|
||||||
};
|
};
|
||||||
|
|
||||||
class SignalView : public QWidget {
|
class SignalView : public QFrame {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -93,7 +95,7 @@ public:
|
|||||||
void setMessage(const MessageId &id);
|
void setMessage(const MessageId &id);
|
||||||
void signalHovered(const Signal *sig);
|
void signalHovered(const Signal *sig);
|
||||||
void updateChartState();
|
void updateChartState();
|
||||||
void expandSignal(const Signal *sig);
|
void selectSignal(const Signal *sig, bool expand = false);
|
||||||
void rowClicked(const QModelIndex &index);
|
void rowClicked(const QModelIndex &index);
|
||||||
SignalModel *model = nullptr;
|
SignalModel *model = nullptr;
|
||||||
|
|
||||||
|
|||||||
@@ -31,10 +31,12 @@ bool AbstractStream::updateEvent(const Event *event) {
|
|||||||
data.dat = QByteArray((char *)c.getDat().begin(), c.getDat().size());
|
data.dat = QByteArray((char *)c.getDat().begin(), c.getDat().size());
|
||||||
data.count = ++counters[id];
|
data.count = ++counters[id];
|
||||||
data.freq = data.count / std::max(1.0, current_sec);
|
data.freq = data.count / std::max(1.0, current_sec);
|
||||||
change_trackers[id].compute(data.dat, data.ts, data.freq);
|
|
||||||
data.colors = change_trackers[id].colors;
|
auto &tracker = change_trackers[id];
|
||||||
data.last_change_t = change_trackers[id].last_change_t;
|
tracker.compute(data.dat, data.ts, data.freq);
|
||||||
data.bit_change_counts = change_trackers[id].bit_change_counts;
|
data.colors = tracker.colors;
|
||||||
|
data.last_change_t = tracker.last_change_t;
|
||||||
|
data.bit_change_counts = tracker.bit_change_counts;
|
||||||
}
|
}
|
||||||
|
|
||||||
double ts = millis_since_boot();
|
double ts = millis_since_boot();
|
||||||
|
|||||||
@@ -23,9 +23,11 @@ TEST_CASE("DBCManager::generateDBC") {
|
|||||||
auto &new_m = new_msgs.at(address);
|
auto &new_m = new_msgs.at(address);
|
||||||
REQUIRE(m.name == new_m.name);
|
REQUIRE(m.name == new_m.name);
|
||||||
REQUIRE(m.size == new_m.size);
|
REQUIRE(m.size == new_m.size);
|
||||||
REQUIRE(m.sigs.size() == new_m.sigs.size());
|
REQUIRE(m.getSignals().size() == new_m.getSignals().size());
|
||||||
for (int i = 0; i < m.sigs.size(); ++i) {
|
auto sigs = m.getSignals();
|
||||||
REQUIRE(m.sigs[i] == new_m.sigs[i]);
|
auto new_sigs = new_m.getSignals();
|
||||||
|
for (int i = 0; i < sigs.size(); ++i) {
|
||||||
|
REQUIRE(*sigs[i] == *new_sigs[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -44,9 +46,9 @@ TEST_CASE("Parse can messages") {
|
|||||||
for (const auto &c : e->event.getCan()) {
|
for (const auto &c : e->event.getCan()) {
|
||||||
const auto msg = dbc.msg(c.getAddress());
|
const auto msg = dbc.msg(c.getAddress());
|
||||||
if (c.getSrc() == 0 && msg) {
|
if (c.getSrc() == 0 && msg) {
|
||||||
for (auto &sig : msg->sigs) {
|
for (auto sig : msg->getSignals()) {
|
||||||
double val = get_raw_value((uint8_t *)c.getDat().begin(), c.getDat().size(), sig);
|
double val = get_raw_value((uint8_t *)c.getDat().begin(), c.getDat().size(), *sig);
|
||||||
values_1[{c.getAddress(), sig.name}].push_back(val);
|
values_1[{c.getAddress(), sig->name}].push_back(val);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+27
-22
@@ -3,6 +3,7 @@
|
|||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QFontDatabase>
|
#include <QFontDatabase>
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
|
#include <QPixmapCache>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
|
||||||
#include <limits>
|
#include <limits>
|
||||||
@@ -73,32 +74,23 @@ MessageBytesDelegate::MessageBytesDelegate(QObject *parent) : QStyledItemDelegat
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MessageBytesDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const {
|
void MessageBytesDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const {
|
||||||
QStyleOptionViewItemV4 opt = option;
|
|
||||||
initStyleOption(&opt, index);
|
|
||||||
|
|
||||||
auto byte_list = opt.text.split(" ");
|
|
||||||
if (byte_list.size() <= 1) {
|
|
||||||
QStyledItemDelegate::paint(painter, option, index);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto color_role = option.state & QStyle::State_Selected ? QPalette::HighlightedText: QPalette::Text;
|
auto color_role = option.state & QStyle::State_Selected ? QPalette::HighlightedText: QPalette::Text;
|
||||||
painter->setPen(option.palette.color(color_role));
|
painter->setPen(option.palette.color(color_role));
|
||||||
painter->setFont(fixed_font);
|
painter->setFont(fixed_font);
|
||||||
QRect space = painter->boundingRect(opt.rect, opt.displayAlignment, " ");
|
int space = painter->boundingRect(option.rect, option.displayAlignment, " ").width();
|
||||||
QRect pos = painter->boundingRect(opt.rect, opt.displayAlignment, "00");
|
QRect pos = painter->boundingRect(option.rect, option.displayAlignment, "00").adjusted(0, 0, 2, 0);
|
||||||
pos.moveLeft(pos.x() + space.width());
|
pos.moveLeft(pos.x() + space);
|
||||||
|
int m = space / 2;
|
||||||
int m = space.width() / 2;
|
|
||||||
const QMargins margins(m, m, m, m);
|
const QMargins margins(m, m, m, m);
|
||||||
|
|
||||||
auto colors = index.data(Qt::UserRole).value<QVector<QColor>>();
|
auto colors = index.data(Qt::UserRole).value<QVector<QColor>>();
|
||||||
|
auto byte_list = index.data(Qt::DisplayRole).toString().split(" ");
|
||||||
for (int i = 0; i < byte_list.size(); ++i) {
|
for (int i = 0; i < byte_list.size(); ++i) {
|
||||||
if (i < colors.size()) {
|
if (i < colors.size() && colors[i].alpha() > 0) {
|
||||||
painter->fillRect(pos.marginsAdded(margins), colors[i]);
|
painter->fillRect(pos.marginsAdded(margins), colors[i]);
|
||||||
}
|
}
|
||||||
painter->drawText(pos, opt.displayAlignment, byte_list[i]);
|
painter->drawText(pos, Qt::AlignCenter, byte_list[i]);
|
||||||
pos.moveLeft(pos.right() + space.width());
|
pos.moveLeft(pos.right() + space);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,12 +116,25 @@ namespace utils {
|
|||||||
QPixmap icon(const QString &id) {
|
QPixmap icon(const QString &id) {
|
||||||
static bool dark_theme = QApplication::style()->standardPalette().color(QPalette::WindowText).value() >
|
static bool dark_theme = QApplication::style()->standardPalette().color(QPalette::WindowText).value() >
|
||||||
QApplication::style()->standardPalette().color(QPalette::Background).value();
|
QApplication::style()->standardPalette().color(QPalette::Background).value();
|
||||||
QPixmap pm = bootstrapPixmap(id);
|
QPixmap pm;
|
||||||
if (dark_theme) {
|
QString key = "bootstrap_" % id % (dark_theme ? "1" : "0");
|
||||||
QPainter p(&pm);
|
if (!QPixmapCache::find(key, &pm)) {
|
||||||
p.setCompositionMode(QPainter::CompositionMode_SourceIn);
|
pm = bootstrapPixmap(id);
|
||||||
p.fillRect(pm.rect(), Qt::lightGray);
|
if (dark_theme) {
|
||||||
|
QPainter p(&pm);
|
||||||
|
p.setCompositionMode(QPainter::CompositionMode_SourceIn);
|
||||||
|
p.fillRect(pm.rect(), Qt::lightGray);
|
||||||
|
}
|
||||||
|
QPixmapCache::insert(key, pm);
|
||||||
}
|
}
|
||||||
return pm;
|
return pm;
|
||||||
}
|
}
|
||||||
} // namespace utils
|
} // namespace utils
|
||||||
|
|
||||||
|
QToolButton *toolButton(const QString &icon, const QString &tooltip) {
|
||||||
|
auto btn = new QToolButton();
|
||||||
|
btn->setIcon(utils::icon(icon));
|
||||||
|
btn->setToolTip(tooltip);
|
||||||
|
btn->setAutoRaise(true);
|
||||||
|
return btn;
|
||||||
|
};
|
||||||
|
|||||||
@@ -6,7 +6,9 @@
|
|||||||
#include <QColor>
|
#include <QColor>
|
||||||
#include <QFont>
|
#include <QFont>
|
||||||
#include <QRegExpValidator>
|
#include <QRegExpValidator>
|
||||||
|
#include <QStringBuilder>
|
||||||
#include <QStyledItemDelegate>
|
#include <QStyledItemDelegate>
|
||||||
|
#include <QToolButton>
|
||||||
#include <QVector>
|
#include <QVector>
|
||||||
|
|
||||||
#include "tools/cabana/dbcmanager.h"
|
#include "tools/cabana/dbcmanager.h"
|
||||||
@@ -52,3 +54,5 @@ public:
|
|||||||
namespace utils {
|
namespace utils {
|
||||||
QPixmap icon(const QString &id);
|
QPixmap icon(const QString &id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QToolButton *toolButton(const QString &icon, const QString &tooltip);
|
||||||
|
|||||||
@@ -28,9 +28,9 @@ inline QString formatTime(int seconds) {
|
|||||||
|
|
||||||
VideoWidget::VideoWidget(QWidget *parent) : QWidget(parent) {
|
VideoWidget::VideoWidget(QWidget *parent) : QWidget(parent) {
|
||||||
QVBoxLayout *main_layout = new QVBoxLayout(this);
|
QVBoxLayout *main_layout = new QVBoxLayout(this);
|
||||||
|
main_layout->setContentsMargins(0, 0, 0, 0);
|
||||||
QFrame *frame = new QFrame(this);
|
QFrame *frame = new QFrame(this);
|
||||||
frame->setFrameShape(QFrame::StyledPanel);
|
frame->setFrameStyle(QFrame::StyledPanel | QFrame::Plain);
|
||||||
frame->setFrameShadow(QFrame::Sunken);
|
|
||||||
main_layout->addWidget(frame);
|
main_layout->addWidget(frame);
|
||||||
|
|
||||||
QVBoxLayout *frame_layout = new QVBoxLayout(frame);
|
QVBoxLayout *frame_layout = new QVBoxLayout(frame);
|
||||||
|
|||||||
Reference in New Issue
Block a user