@ -1,4 +1,4 @@
# include "tools/cabana/signaledit .h"
# include "tools/cabana/signalview .h"
# include <QApplication>
# include <QCompleter>
@ -62,10 +62,21 @@ void SignalModel::updateState(const QHash<MessageId, CanData> *msgs) {
auto & dat = can - > lastMessage ( msg_id ) . dat ;
int row = 0 ;
for ( auto item : root - > children ) {
item - > sig_val = QString : : number ( get_raw_value ( ( uint8_t * ) dat . constData ( ) , dat . size ( ) , * item - > sig ) , ' f ' , item - > sig - > precision ) ;
double value = get_raw_value ( ( uint8_t * ) dat . constData ( ) , dat . size ( ) , * item - > sig ) ;
item - > sig_val = QString : : number ( value , ' f ' , item - > sig - > precision ) ;
// Show unit
if ( ! item - > sig - > unit . isEmpty ( ) ) {
item - > sig_val + = " " + item - > sig - > unit ;
}
// Show enum string
for ( auto & [ val , desc ] : item - > sig - > val_desc ) {
if ( std : : abs ( value - val . toInt ( ) ) < 1e-6 ) {
item - > sig_val = desc ;
}
}
emit dataChanged ( index ( row , 1 ) , index ( row , 1 ) , { Qt : : DisplayRole } ) ;
+ + row ;
}
@ -157,6 +168,8 @@ QVariant SignalModel::data(const QModelIndex &index, int role) const {
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 ) ? item - > sig - > name : item - > sig_val ;
}
}
return { } ;
@ -312,11 +325,25 @@ QSize SignalItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QMo
int spacing = option . widget - > style ( ) - > pixelMetric ( QStyle : : PM_TreeViewIndentation ) + color_label_width + 8 ;
it = width_cache . insert ( text , option . fontMetrics . width ( text ) + spacing ) ;
}
width = std : : min ( width , it . value ( ) ) ;
width = std : : min < int > ( option . widget - > size ( ) . width ( ) / 3.0 , it . value ( ) ) ;
}
return { width , QApplication : : fontMetrics ( ) . height ( ) } ;
}
bool SignalItemDelegate : : helpEvent ( QHelpEvent * event , QAbstractItemView * view , const QStyleOptionViewItem & option , const QModelIndex & index ) {
if ( event & & event - > type ( ) = = QEvent : : ToolTip & & index . isValid ( ) ) {
auto item = ( SignalModel : : Item * ) index . internalPointer ( ) ;
if ( item - > type = = SignalModel : : Item : : Sig & & index . column ( ) = = 1 ) {
QRect rc = option . rect . adjusted ( 0 , 0 , - option . rect . width ( ) * 0.4 , 0 ) ;
if ( rc . contains ( event - > pos ( ) ) ) {
event - > setAccepted ( false ) ;
return false ;
}
}
}
return QStyledItemDelegate : : helpEvent ( event , view , option , index ) ;
}
void SignalItemDelegate : : paint ( QPainter * painter , const QStyleOptionViewItem & option , const QModelIndex & index ) const {
int h_margin = option . widget - > style ( ) - > pixelMetric ( QStyle : : PM_FocusFrameHMargin ) + 1 ;
int v_margin = option . widget - > style ( ) - > pixelMetric ( QStyle : : PM_FocusFrameVMargin ) ;
@ -352,31 +379,34 @@ void SignalItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &op
painter - > fillRect ( option . rect , option . palette . highlight ( ) ) ;
}
drawSparkline ( painter , option , index ) ;
int adjust_right = ( ( SignalView * ) parent ( ) ) - > tree - > indexWidget ( index ) - > sizeHint ( ) . width ( ) + 2 * h_margin ;
QRect r = option . rect . adjusted ( h_margin , v_margin , - adjust_right , - v_margin ) ;
// draw signal value
int right_offset = ( ( SignalView * ) parent ( ) ) - > tree - > indexWidget ( index ) - > sizeHint ( ) . width ( ) + 2 * h_margin ;
QRect rc = option . rect . adjusted ( 0 , 0 , - right_offset , 0 ) ;
auto text = painter - > fontMetrics ( ) . elidedText ( index . data ( Qt : : DisplayRole ) . toString ( ) , Qt : : ElideRight , rc . width ( ) ) ;
QRect value_rect = r . adjusted ( r . width ( ) * 0.6 + h_margin , 0 , 0 , 0 ) ;
auto text = painter - > fontMetrics ( ) . elidedText ( index . data ( Qt : : DisplayRole ) . toString ( ) , Qt : : ElideRight , value_rect . width ( ) ) ;
painter - > setPen ( option . palette . color ( option . state & QStyle : : State_Selected ? QPalette : : HighlightedText : QPalette : : Text ) ) ;
painter - > drawText ( rc , Qt : : AlignRight | Qt : : AlignVCenter , text ) ;
painter - > drawText ( value_rect , Qt : : AlignRight | Qt : : AlignVCenter , text ) ;
QRect sparkline_rect = r . adjusted ( 0 , 0 , - r . width ( ) * 0.4 - h_margin , 0 ) ;
drawSparkline ( painter , sparkline_rect , index ) ;
} else {
QStyledItemDelegate : : paint ( painter , option , index ) ;
}
}
void SignalItemDelegate : : drawSparkline ( QPainter * painter , const QStyleOptionViewItem & option , const QModelIndex & index ) const {
void SignalItemDelegate : : drawSparkline ( QPainter * painter , const QRect & rect , const QModelIndex & index ) const {
static std : : vector < QPointF > points ;
// TODO: get seconds from settings.
const uint64_t chart_seconds = 15 ; // seconds
const auto & msg_id = ( ( SignalView * ) parent ( ) ) - > msg_id ;
const auto & msgs = can - > events ( ) . at ( msg_id ) ;
uint64_t ts = ( can - > lastMessage ( msg_id ) . ts + can - > routeStartTime ( ) ) * 1e9 ;
auto first = std : : lower_bound ( msgs . cbegin ( ) , msgs . cend ( ) , CanEvent { . mono_time = ( uint64_t ) std : : max < int64_t > ( ts - chart_seconds * 1e9 , 0 ) } ) ;
if ( first ! = msgs . cend ( ) ) {
auto first = std : : lower_bound ( msgs . cbegin ( ) , msgs . cend ( ) , CanEvent { . mono_time = ( uint64_t ) std : : max < int64_t > ( ts - settings . sparkline_range * 1e9 , 0 ) } ) ;
auto last = std : : upper_bound ( first , msgs . cend ( ) , CanEvent { . mono_time = ts } ) ;
if ( first ! = last ) {
double min = std : : numeric_limits < double > : : max ( ) ;
double max = std : : numeric_limits < double > : : lowest ( ) ;
const auto sig = ( ( SignalModel : : Item * ) index . internalPointer ( ) ) - > sig ;
auto last = std : : lower_bound ( first , msgs . cend ( ) , CanEvent { . mono_time = ts } ) ;
points . clear ( ) ;
for ( auto it = first ; it ! = last ; + + it ) {
double value = get_raw_value ( it - > dat , it - > size , * sig ) ;
@ -389,18 +419,22 @@ void SignalItemDelegate::drawSparkline(QPainter *painter, const QStyleOptionView
max + = 1 ;
}
int h_margin = option . widget - > style ( ) - > pixelMetric ( QStyle : : PM_FocusFrameHMargin ) ;
int v_margin = std : : max ( option . widget - > style ( ) - > pixelMetric ( QStyle : : PM_FocusFrameVMargin ) + 2 , 4 ) ;
const double xscale = ( option . rect . width ( ) - 175.0 * option . widget - > devicePixelRatioF ( ) - h_margin * 2 ) / chart_seconds ;
const double yscale = ( option . rect . height ( ) - v_margin * 2 ) / ( max - min ) ;
const int left = option . rect . left ( ) ;
const int top = option . rect . top ( ) + v_margin ;
const double xscale = rect . width ( ) / ( double ) settings . sparkline_range ;
const double yscale = rect . height ( ) / ( max - min ) ;
for ( auto & pt : points ) {
pt . rx ( ) = left + pt . x ( ) * xscale ;
pt . ry ( ) = top + std : : abs ( pt . y ( ) - max ) * yscale ;
pt . rx ( ) = rect . left ( ) + pt . x ( ) * xscale ;
pt . ry ( ) = rect . top ( ) + std : : abs ( pt . y ( ) - max ) * yscale ;
}
painter - > setPen ( getColor ( sig ) ) ;
painter - > drawPolyline ( points . data ( ) , points . size ( ) ) ;
if ( ( points . back ( ) . x ( ) - points . front ( ) . x ( ) ) / points . size ( ) > 10 ) {
painter - > setPen ( Qt : : NoPen ) ;
painter - > setBrush ( getColor ( sig ) ) ;
for ( const auto & pt : points ) {
painter - > drawEllipse ( pt , 2 , 2 ) ;
}
}
}
}
@ -451,6 +485,17 @@ SignalView::SignalView(ChartsWidget *charts, QWidget *parent) : charts(charts),
filter_edit - > setPlaceholderText ( tr ( " filter signals " ) ) ;
hl - > addWidget ( filter_edit ) ;
hl - > addStretch ( 1 ) ;
// WARNING: increasing the maximum range can result in severe performance degradation.
// 30s is a reasonable value at present.
const int max_range = 30 ; // 30s
settings . sparkline_range = std : : clamp ( settings . sparkline_range , 1 , max_range ) ;
hl - > addWidget ( sparkline_label = new QLabel ( ) ) ;
hl - > addWidget ( sparkline_range_slider = new QSlider ( Qt : : Horizontal , this ) ) ;
sparkline_range_slider - > setRange ( 1 , max_range ) ;
sparkline_range_slider - > setValue ( settings . sparkline_range ) ;
sparkline_range_slider - > setToolTip ( tr ( " Sparkline time range " ) ) ;
auto collapse_btn = toolButton ( " dash-square " , tr ( " Collapse All " ) ) ;
collapse_btn - > setIconSize ( { 12 , 12 } ) ;
hl - > addWidget ( collapse_btn ) ;
@ -473,8 +518,10 @@ SignalView::SignalView(ChartsWidget *charts, QWidget *parent) : charts(charts),
main_layout - > setSpacing ( 0 ) ;
main_layout - > addWidget ( title_bar ) ;
main_layout - > addWidget ( tree ) ;
updateToolBar ( ) ;
QObject : : connect ( filter_edit , & QLineEdit : : textEdited , model , & SignalModel : : setFilter ) ;
QObject : : connect ( sparkline_range_slider , & QSlider : : valueChanged , this , & SignalView : : setSparklineRange ) ;
QObject : : connect ( collapse_btn , & QPushButton : : clicked , tree , & QTreeView : : collapseAll ) ;
QObject : : connect ( tree , & QAbstractItemView : : clicked , this , & SignalView : : rowClicked ) ;
QObject : : connect ( tree , & QTreeView : : viewportEntered , [ this ] ( ) { emit highlight ( nullptr ) ; } ) ;
@ -521,7 +568,7 @@ void SignalView::rowsChanged() {
} ) ;
}
}
signal_count_lb - > setText ( tr ( " Signals: %1 " ) . arg ( model - > rowCount ( ) ) ) ;
updateToolBar ( ) ;
updateChartState ( ) ;
}
@ -569,6 +616,17 @@ void SignalView::signalHovered(const cabana::Signal *sig) {
}
}
void SignalView : : updateToolBar ( ) {
signal_count_lb - > setText ( tr ( " Signals: %1 " ) . arg ( model - > rowCount ( ) ) ) ;
sparkline_label - > setText ( QString ( " Range: %1 " ) . arg ( utils : : formatSeconds ( settings . sparkline_range ) ) ) ;
}
void SignalView : : setSparklineRange ( int value ) {
settings . sparkline_range = value ;
updateToolBar ( ) ;
model - > updateState ( nullptr ) ;
}
void SignalView : : leaveEvent ( QEvent * event ) {
emit highlight ( nullptr ) ;
QWidget : : leaveEvent ( event ) ;