parent
632c484dd5
commit
a8299ef800
|
@ -33,13 +33,17 @@ SignalModel::SignalModel(QObject *parent) : root(new Item), QAbstractItemModel(p
|
|||
QObject::connect(dbc(), &DBCManager::signalRemoved, this, &SignalModel::handleSignalRemoved);
|
||||
}
|
||||
|
||||
void SignalModel::insertItem(SignalModel::Item *parent_item, int pos, const cabana::Signal *sig) {
|
||||
Item *item = new Item{.sig = sig, .parent = parent_item, .title = sig->name, .type = Item::Sig};
|
||||
parent_item->children.insert(pos, item);
|
||||
void SignalModel::insertItem(SignalModel::Item *root_item, int pos, const cabana::Signal *sig) {
|
||||
Item *parent_item = new Item{.sig = sig, .parent = root_item, .title = sig->name, .type = Item::Sig};
|
||||
root_item->children.insert(pos, parent_item);
|
||||
QString titles[]{"Name", "Size", "Receiver Nodes", "Little Endian", "Signed", "Offset", "Factor", "Type",
|
||||
"Multiplex Value", "Extra Info", "Unit", "Comment", "Minimum Value", "Maximum Value", "Value Table"};
|
||||
for (int i = 0; i < std::size(titles); ++i) {
|
||||
item->children.push_back(new Item{.sig = sig, .parent = item, .title = titles[i], .type = (Item::Type)(i + Item::Name)});
|
||||
auto item = new Item{.sig = sig, .parent = parent_item, .title = titles[i], .type = (Item::Type)(i + Item::Name)};
|
||||
parent_item->children.push_back(item);
|
||||
if (item->type == Item::ExtraInfo) {
|
||||
parent_item = item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -75,12 +79,7 @@ SignalModel::Item *SignalModel::getItem(const QModelIndex &index) const {
|
|||
int SignalModel::rowCount(const QModelIndex &parent) const {
|
||||
if (parent.isValid() && parent.column() > 0) return 0;
|
||||
|
||||
auto parent_item = getItem(parent);
|
||||
int row_count = parent_item->children.size();
|
||||
if (parent_item->type == Item::Sig && !parent_item->extra_expanded) {
|
||||
row_count -= (Item::Desc - Item::ExtraInfo);
|
||||
}
|
||||
return row_count;
|
||||
return getItem(parent)->children.size();
|
||||
}
|
||||
|
||||
Qt::ItemFlags SignalModel::flags(const QModelIndex &index) const {
|
||||
|
@ -88,7 +87,7 @@ Qt::ItemFlags SignalModel::flags(const QModelIndex &index) const {
|
|||
|
||||
auto item = getItem(index);
|
||||
Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
|
||||
if (index.column() == 1 && item->type != Item::Sig && item->type != Item::ExtraInfo) {
|
||||
if (index.column() == 1 && item->children.empty()) {
|
||||
flags |= (item->type == Item::Endian || item->type == Item::Signed) ? Qt::ItemIsUserCheckable : Qt::ItemIsEditable;
|
||||
}
|
||||
if (item->type == Item::MultiplexValue && item->sig->type != cabana::Signal::Type::Multiplexed) {
|
||||
|
@ -153,8 +152,6 @@ QVariant SignalModel::data(const QModelIndex &index, int role) const {
|
|||
} else if (role == Qt::CheckStateRole && index.column() == 1) {
|
||||
if (item->type == Item::Endian) return item->sig->is_little_endian ? Qt::Checked : Qt::Unchecked;
|
||||
if (item->type == Item::Signed) return item->sig->is_signed ? Qt::Checked : Qt::Unchecked;
|
||||
} else if (role == Qt::DecorationRole && index.column() == 0 && item->type == Item::ExtraInfo) {
|
||||
return utils::icon(item->parent->extra_expanded ? "chevron-compact-down" : "chevron-compact-up");
|
||||
} else if (role == Qt::ToolTipRole && item->type == Item::Sig) {
|
||||
return (index.column() == 0) ? signalToolTip(item->sig) : QString();
|
||||
}
|
||||
|
@ -189,21 +186,6 @@ bool SignalModel::setData(const QModelIndex &index, const QVariant &value, int r
|
|||
return ret;
|
||||
}
|
||||
|
||||
void SignalModel::showExtraInfo(const QModelIndex &index) {
|
||||
auto item = getItem(index);
|
||||
if (item->type == Item::ExtraInfo) {
|
||||
if (!item->parent->extra_expanded) {
|
||||
item->parent->extra_expanded = true;
|
||||
beginInsertRows(index.parent(), Item::ExtraInfo - 2, Item::Desc - 2);
|
||||
endInsertRows();
|
||||
} else {
|
||||
item->parent->extra_expanded = false;
|
||||
beginRemoveRows(index.parent(), Item::ExtraInfo - 2, Item::Desc - 2);
|
||||
endRemoveRows();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool SignalModel::saveSignal(const cabana::Signal *origin_s, cabana::Signal &s) {
|
||||
auto msg = dbc()->msg(msg_id);
|
||||
if (s.name != origin_s->name && msg->sig(s.name) != nullptr) {
|
||||
|
@ -283,13 +265,9 @@ QSize SignalItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QMo
|
|||
text += item->sig->type == cabana::Signal::Type::Multiplexor ? QString(" M ") : QString(" m%1 ").arg(item->sig->multiplex_value);
|
||||
spacing += (option.widget->style()->pixelMetric(QStyle::PM_FocusFrameHMargin) + 1) * 2;
|
||||
}
|
||||
auto it = width_cache.find(text);
|
||||
if (it == width_cache.end()) {
|
||||
it = width_cache.insert(text, option.fontMetrics.width(text));
|
||||
}
|
||||
width = std::min<int>(option.widget->size().width() / 3.0, it.value() + spacing);
|
||||
width = std::min<int>(option.widget->size().width() / 3.0, option.fontMetrics.width(text) + spacing);
|
||||
}
|
||||
return {width, option.fontMetrics.height()};
|
||||
return {width, option.fontMetrics.height() + option.widget->style()->pixelMetric(QStyle::PM_FocusFrameVMargin) * 2};
|
||||
}
|
||||
|
||||
void SignalItemDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const {
|
||||
|
@ -305,51 +283,55 @@ void SignalItemDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptio
|
|||
}
|
||||
|
||||
void SignalItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const {
|
||||
auto item = (SignalModel::Item *)index.internalPointer();
|
||||
if (item && item->type == SignalModel::Item::Sig) {
|
||||
painter->setRenderHint(QPainter::Antialiasing);
|
||||
if (option.state & QStyle::State_Selected) {
|
||||
painter->fillRect(option.rect, option.palette.brush(QPalette::Normal, QPalette::Highlight));
|
||||
}
|
||||
const int h_margin = option.widget->style()->pixelMetric(QStyle::PM_FocusFrameHMargin) + 1;
|
||||
const int v_margin = option.widget->style()->pixelMetric(QStyle::PM_FocusFrameVMargin);
|
||||
auto item = static_cast<SignalModel::Item*>(index.internalPointer());
|
||||
|
||||
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) {
|
||||
QRect rect = option.rect.adjusted(h_margin, v_margin, -h_margin, -v_margin);
|
||||
painter->setRenderHint(QPainter::Antialiasing);
|
||||
if (option.state & QStyle::State_Selected) {
|
||||
painter->fillRect(option.rect, option.palette.brush(QPalette::Normal, QPalette::Highlight));
|
||||
}
|
||||
|
||||
if (index.column() == 0) {
|
||||
if (item->type == SignalModel::Item::Sig) {
|
||||
// color label
|
||||
QPainterPath path;
|
||||
QRect icon_rect{r.x(), r.y(), color_label_width, r.height()};
|
||||
QRect icon_rect{rect.x(), rect.y(), color_label_width, rect.height()};
|
||||
path.addRoundedRect(icon_rect, 3, 3);
|
||||
painter->setPen(item->highlight ? Qt::white : Qt::black);
|
||||
painter->setFont(label_font);
|
||||
painter->fillPath(path, item->sig->color.darker(item->highlight ? 125 : 0));
|
||||
painter->drawText(icon_rect, Qt::AlignCenter, QString::number(item->row() + 1));
|
||||
|
||||
r.setLeft(icon_rect.right() + h_margin * 2);
|
||||
rect.setLeft(icon_rect.right() + h_margin * 2);
|
||||
// multiplexer indicator
|
||||
if (item->sig->type != cabana::Signal::Type::Normal) {
|
||||
QString indicator = item->sig->type == cabana::Signal::Type::Multiplexor ? QString(" M ") : QString(" m%1 ").arg(item->sig->multiplex_value);
|
||||
QRect indicator_rect{r.x(), r.y(), option.fontMetrics.width(indicator), r.height()};
|
||||
QRect indicator_rect{rect.x(), rect.y(), option.fontMetrics.width(indicator), rect.height()};
|
||||
painter->setBrush(Qt::gray);
|
||||
painter->setPen(Qt::NoPen);
|
||||
painter->drawRoundedRect(indicator_rect, 3, 3);
|
||||
painter->setPen(Qt::white);
|
||||
painter->drawText(indicator_rect, Qt::AlignCenter, indicator);
|
||||
r.setLeft(indicator_rect.right() + h_margin * 2);
|
||||
rect.setLeft(indicator_rect.right() + h_margin * 2);
|
||||
}
|
||||
} else {
|
||||
rect.setLeft(option.widget->style()->pixelMetric(QStyle::PM_TreeViewIndentation) + color_label_width + h_margin * 3);
|
||||
}
|
||||
|
||||
// name
|
||||
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 && !item->sparkline.pixmap.isNull()) {
|
||||
// sparkline
|
||||
// name
|
||||
auto text = option.fontMetrics.elidedText(index.data(Qt::DisplayRole).toString(), Qt::ElideRight, rect.width());
|
||||
painter->setPen(option.palette.color(option.state & QStyle::State_Selected ? QPalette::HighlightedText : QPalette::Text));
|
||||
painter->setFont(option.font);
|
||||
painter->drawText(rect, option.displayAlignment, text);
|
||||
} else if (index.column() == 1) {
|
||||
if (!item->sparkline.pixmap.isNull()) {
|
||||
QSize sparkline_size = item->sparkline.pixmap.size() / item->sparkline.pixmap.devicePixelRatio();
|
||||
painter->drawPixmap(QRect(r.topLeft(), sparkline_size), item->sparkline.pixmap);
|
||||
painter->drawPixmap(QRect(rect.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);
|
||||
rect.adjust(sparkline_size.width() + 1, 0, 0, 0);
|
||||
int value_adjust = 10;
|
||||
if (!item->sparkline.isEmpty() && (item->highlight || option.state & QStyle::State_Selected)) {
|
||||
painter->drawLine(rect.topLeft(), rect.bottomLeft());
|
||||
|
@ -361,7 +343,7 @@ void SignalItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &op
|
|||
painter->drawText(rect, Qt::AlignLeft | Qt::AlignBottom, min);
|
||||
QFontMetrics fm(minmax_font);
|
||||
value_adjust = std::max(fm.width(min), fm.width(max)) + 5;
|
||||
} else if (!item->sparkline.isEmpty() && item->sig->type == cabana::Signal::Type::Multiplexed) {
|
||||
} else if (!item->sparkline.isEmpty() && item->sig->type == cabana::Signal::Type::Multiplexed) {
|
||||
// display freq of multiplexed signal
|
||||
painter->setFont(label_font);
|
||||
QString freq = QString("%1 hz").arg(item->sparkline.freq(), 0, 'g', 2);
|
||||
|
@ -373,9 +355,9 @@ void SignalItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &op
|
|||
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);
|
||||
} else {
|
||||
QStyledItemDelegate::paint(painter, option, index);
|
||||
}
|
||||
} else {
|
||||
QStyledItemDelegate::paint(painter, option, index);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -512,7 +494,6 @@ SignalView::SignalView(ChartsWidget *charts, QWidget *parent) : charts(charts),
|
|||
}
|
||||
|
||||
void SignalView::setMessage(const MessageId &id) {
|
||||
max_value_width = 0;
|
||||
filter_edit->clear();
|
||||
model->setMessage(id);
|
||||
}
|
||||
|
@ -549,11 +530,9 @@ void SignalView::rowsChanged() {
|
|||
|
||||
void SignalView::rowClicked(const QModelIndex &index) {
|
||||
auto item = model->getItem(index);
|
||||
if (item->type == SignalModel::Item::Sig) {
|
||||
auto sig_index = model->index(index.row(), 0, index.parent());
|
||||
tree->setExpanded(sig_index, !tree->isExpanded(sig_index));
|
||||
} else if (item->type == SignalModel::Item::ExtraInfo) {
|
||||
model->showExtraInfo(index);
|
||||
if (item->type == SignalModel::Item::Sig || item->type == SignalModel::Item::ExtraInfo) {
|
||||
auto expand_index = model->index(index.row(), 0, index.parent());
|
||||
tree->setExpanded(expand_index, !tree->isExpanded(expand_index));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -614,39 +593,54 @@ void SignalView::handleSignalUpdated(const cabana::Signal *sig) {
|
|||
updateState();
|
||||
}
|
||||
|
||||
std::pair<QModelIndex, QModelIndex> SignalView::visibleSignalRange() {
|
||||
auto topLevelIndex = [](QModelIndex index) {
|
||||
while (index.isValid() && index.parent().isValid()) index = index.parent();
|
||||
return index;
|
||||
};
|
||||
|
||||
const auto viewport_rect = tree->viewport()->rect();
|
||||
QModelIndex first_visible = tree->indexAt(viewport_rect.topLeft());
|
||||
if (first_visible.parent().isValid()) {
|
||||
first_visible = topLevelIndex(first_visible);
|
||||
first_visible = first_visible.siblingAtRow(first_visible.row() + 1);
|
||||
}
|
||||
|
||||
QModelIndex last_visible = topLevelIndex(tree->indexAt(viewport_rect.bottomRight()));
|
||||
if (!last_visible.isValid()) {
|
||||
last_visible = model->index(model->rowCount() - 1, 0);
|
||||
}
|
||||
return {first_visible, last_visible};
|
||||
}
|
||||
|
||||
void SignalView::updateState(const std::set<MessageId> *msgs) {
|
||||
const auto &last_msg = can->lastMessage(model->msg_id);
|
||||
if (model->rowCount() == 0 || (msgs && !msgs->count(model->msg_id)) || last_msg.dat.size() == 0) return;
|
||||
|
||||
int max_value_width = 0;
|
||||
for (auto item : model->root->children) {
|
||||
double value = 0;
|
||||
if (item->sig->getValue(last_msg.dat.data(), last_msg.dat.size(), &value)) {
|
||||
item->sig_val = item->sig->formatValue(value);
|
||||
max_value_width = std::max(max_value_width, fontMetrics().width(item->sig_val));
|
||||
}
|
||||
max_value_width = std::max(max_value_width, fontMetrics().width(item->sig_val));
|
||||
}
|
||||
|
||||
QModelIndex top = tree->indexAt(QPoint(0, 0));
|
||||
if (top.isValid()) {
|
||||
// update visible sparkline
|
||||
int first_visible_row = top.parent().isValid() ? top.parent().row() + 1 : top.row();
|
||||
int last_visible_row = model->rowCount() - 1;
|
||||
QModelIndex bottom = tree->indexAt(tree->viewport()->rect().bottomLeft());
|
||||
if (bottom.isValid()) {
|
||||
last_visible_row = bottom.parent().isValid() ? bottom.parent().row() : bottom.row();
|
||||
}
|
||||
|
||||
auto [first_visible, last_visible] = visibleSignalRange();
|
||||
if (first_visible.isValid() && last_visible.isValid()) {
|
||||
const static int min_max_width = QFontMetrics(delegate->minmax_font).width("-000.00") + 5;
|
||||
int available_width = value_column_width - delegate->button_size.width();
|
||||
int value_width = std::min<int>(max_value_width + min_max_width, available_width / 2);
|
||||
QSize size(available_width - value_width,
|
||||
delegate->button_size.height() - style()->pixelMetric(QStyle::PM_FocusFrameVMargin) * 2);
|
||||
|
||||
QFutureSynchronizer<void> synchronizer;
|
||||
for (int i = first_visible_row; i <= last_visible_row; ++i) {
|
||||
for (int i = first_visible.row(); i <= last_visible.row(); ++i) {
|
||||
auto item = model->getItem(model->index(i, 1));
|
||||
synchronizer.addFuture(QtConcurrent::run(
|
||||
&item->sparkline, &Sparkline::update, model->msg_id, item->sig, last_msg.ts, settings.sparkline_range, size));
|
||||
}
|
||||
synchronizer.waitForFinished();
|
||||
}
|
||||
|
||||
for (int i = 0; i < model->rowCount(); ++i) {
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <utility>
|
||||
|
||||
#include <QAbstractItemModel>
|
||||
#include <QLabel>
|
||||
|
@ -29,7 +30,6 @@ public:
|
|||
const cabana::Signal *sig = nullptr;
|
||||
QString title;
|
||||
bool highlight = false;
|
||||
bool extra_expanded = false;
|
||||
QString sig_val = "-";
|
||||
Sparkline sparkline;
|
||||
};
|
||||
|
@ -47,10 +47,9 @@ public:
|
|||
bool saveSignal(const cabana::Signal *origin_s, cabana::Signal &s);
|
||||
Item *getItem(const QModelIndex &index) const;
|
||||
int signalRow(const cabana::Signal *sig) const;
|
||||
void showExtraInfo(const QModelIndex &index);
|
||||
|
||||
private:
|
||||
void insertItem(SignalModel::Item *parent_item, int pos, const cabana::Signal *sig);
|
||||
void insertItem(SignalModel::Item *root_item, int pos, const cabana::Signal *sig);
|
||||
void handleSignalAdded(MessageId id, const cabana::Signal *sig);
|
||||
void handleSignalUpdated(const cabana::Signal *sig);
|
||||
void handleSignalRemoved(const cabana::Signal *sig);
|
||||
|
@ -92,7 +91,6 @@ public:
|
|||
QFont label_font, minmax_font;
|
||||
const int color_label_width = 18;
|
||||
mutable QSize button_size;
|
||||
mutable QHash<QString, int> width_cache;
|
||||
};
|
||||
|
||||
class SignalView : public QFrame {
|
||||
|
@ -119,6 +117,7 @@ private:
|
|||
void handleSignalAdded(MessageId id, const cabana::Signal *sig);
|
||||
void handleSignalUpdated(const cabana::Signal *sig);
|
||||
void updateState(const std::set<MessageId> *msgs = nullptr);
|
||||
std::pair<QModelIndex, QModelIndex> visibleSignalRange();
|
||||
|
||||
struct TreeView : public QTreeView {
|
||||
TreeView(QWidget *parent) : QTreeView(parent) {}
|
||||
|
@ -136,7 +135,6 @@ private:
|
|||
QTreeView::leaveEvent(event);
|
||||
}
|
||||
};
|
||||
int max_value_width = 0;
|
||||
int value_column_width = 0;
|
||||
TreeView *tree;
|
||||
QLabel *sparkline_label;
|
||||
|
@ -145,5 +143,4 @@ private:
|
|||
ChartsWidget *charts;
|
||||
QLabel *signal_count_lb;
|
||||
SignalItemDelegate *delegate;
|
||||
friend SignalItemDelegate;
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue