@ -1,174 +1,194 @@
# include "tools/cabana/signaledit.h"
# include <QDoubleValidator>
# include <QFormLayout>
# include <QGuiApplication>
# include <QHBoxLayout>
# include <QHeaderView>
# include <QMessageBox>
# include <QPushButton>
# include <QToolButton>
# include <QVBoxLayout>
# include "tools/cabana/commands.h"
# include "selfdrive/ui/qt/util.h"
// SignalForm
// SignalModel
SignalForm : : SignalForm ( QWidget * parent ) : QWidget ( parent ) {
auto double_validator = new QDoubleValidator ( this ) ;
double_validator - > setLocale ( QLocale : : C ) ; // Match locale of QString::toDouble() instead of system
SignalModel : : SignalModel ( QObject * parent ) : root ( new Item ) , QAbstractItemModel ( parent ) {
QObject : : connect ( dbc ( ) , & DBCManager : : DBCFileChanged , this , & SignalModel : : refresh ) ;
QObject : : connect ( dbc ( ) , & DBCManager : : msgUpdated , this , & SignalModel : : handleMsgChanged ) ;
QObject : : connect ( dbc ( ) , & DBCManager : : msgRemoved , this , & SignalModel : : handleMsgChanged ) ;
QObject : : connect ( dbc ( ) , & DBCManager : : signalAdded , this , & SignalModel : : handleSignalAdded ) ;
QObject : : connect ( dbc ( ) , & DBCManager : : signalUpdated , this , & SignalModel : : handleSignalUpdated ) ;
QObject : : connect ( dbc ( ) , & DBCManager : : signalRemoved , this , & SignalModel : : handleSignalRemoved ) ;
QObject : : connect ( can , & AbstractStream : : msgsReceived , this , & SignalModel : : updateState ) ;
}
QVBoxLayout * main_layout = new QVBoxLayout ( this ) ;
QFormLayout * form_layout = new QFormLayout ( ) ;
main_layout - > addLayout ( form_layout ) ;
name = new QLineEdit ( ) ;
name - > setValidator ( new NameValidator ( name ) ) ;
form_layout - > addRow ( tr ( " Name " ) , name ) ;
QHBoxLayout * hl = new QHBoxLayout ( this ) ;
size = new QSpinBox ( ) ;
size - > setMinimum ( 1 ) ;
hl - > addWidget ( size ) ;
endianness = new QComboBox ( ) ;
endianness - > addItems ( { " Little Endianness " , " Big Endianness " } ) ;
hl - > addWidget ( endianness ) ;
sign = new QComboBox ( ) ;
sign - > addItems ( { " Signed " , " Unsigned " } ) ;
hl - > addWidget ( sign ) ;
form_layout - > addRow ( tr ( " Size " ) , hl ) ;
offset = new QLineEdit ( ) ;
offset - > setValidator ( double_validator ) ;
form_layout - > addRow ( tr ( " Offset " ) , offset ) ;
factor = new QLineEdit ( ) ;
factor - > setValidator ( double_validator ) ;
form_layout - > addRow ( tr ( " Factor " ) , factor ) ;
expand_btn = new QToolButton ( this ) ;
expand_btn - > setText ( tr ( " more... " ) ) ;
main_layout - > addWidget ( expand_btn , 0 , Qt : : AlignRight ) ;
// TODO: parse the following parameters in opendbc
QWidget * extra_container = new QWidget ( this ) ;
QFormLayout * extra_layout = new QFormLayout ( extra_container ) ;
unit = new QLineEdit ( ) ;
extra_layout - > addRow ( tr ( " Unit " ) , unit ) ;
comment = new QLineEdit ( ) ;
extra_layout - > addRow ( tr ( " Comment " ) , comment ) ;
min_val = new QLineEdit ( ) ;
min_val - > setValidator ( double_validator ) ;
extra_layout - > addRow ( tr ( " Minimum value " ) , min_val ) ;
max_val = new QLineEdit ( ) ;
max_val - > setValidator ( double_validator ) ;
extra_layout - > addRow ( tr ( " Maximum value " ) , max_val ) ;
val_desc = new QLineEdit ( ) ;
extra_layout - > addRow ( tr ( " Value descriptions " ) , val_desc ) ;
main_layout - > addWidget ( extra_container ) ;
extra_container - > setVisible ( false ) ;
QObject : : connect ( name , & QLineEdit : : editingFinished , this , & SignalForm : : textBoxEditingFinished ) ;
QObject : : connect ( factor , & QLineEdit : : editingFinished , this , & SignalForm : : textBoxEditingFinished ) ;
QObject : : connect ( offset , & QLineEdit : : editingFinished , this , & SignalForm : : textBoxEditingFinished ) ;
QObject : : connect ( size , & QSpinBox : : editingFinished , this , & SignalForm : : changed ) ;
QObject : : connect ( sign , SIGNAL ( activated ( int ) ) , SIGNAL ( changed ( ) ) ) ;
QObject : : connect ( endianness , SIGNAL ( activated ( int ) ) , SIGNAL ( changed ( ) ) ) ;
QObject : : connect ( expand_btn , & QToolButton : : clicked , [ = ] ( ) {
extra_container - > setVisible ( ! extra_container - > isVisible ( ) ) ;
expand_btn - > setText ( extra_container - > isVisible ( ) ? tr ( " less... " ) : tr ( " more... " ) ) ;
} ) ;
void SignalModel : : insertItem ( SignalModel : : Item * parent_item , int pos , const Signal * sig ) {
Item * item = new Item { . sig = sig , . parent = parent_item , . title = sig - > name . c_str ( ) , . type = Item : : Sig } ;
parent_item - > children . insert ( pos , item ) ;
QString titles [ ] { " Name " , " Size " , " Little Endian " , " Signed " , " Offset " , " Factor " , " Extra Info " , " Unit " , " Comment " , " Minimum " , " Maximum " , " Description " } ;
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 ) } ) ;
}
}
void SignalForm : : textBoxEditingFinished ( ) {
QLineEdit * edit = qobject_cast < QLineEdit * > ( QObject : : sender ( ) ) ;
if ( edit & & edit - > isModified ( ) ) {
edit - > setModified ( false ) ;
emit changed ( ) ;
void SignalModel : : setMessage ( const QString & id ) {
msg_id = id ;
filter_str = " " ;
refresh ( ) ;
updateState ( nullptr ) ;
}
void SignalModel : : setFilter ( const QString & txt ) {
filter_str = txt ;
refresh ( ) ;
}
// SignalEdit
void SignalModel : : refresh ( ) {
beginResetModel ( ) ;
root . reset ( new SignalModel : : Item ) ;
if ( auto msg = dbc ( ) - > msg ( msg_id ) ) {
for ( auto & s : msg - > getSignals ( ) ) {
if ( filter_str . isEmpty ( ) | | QString : : fromStdString ( s - > name ) . contains ( filter_str , Qt : : CaseInsensitive ) ) {
insertItem ( root . get ( ) , root - > children . size ( ) , s ) ;
}
}
}
endResetModel ( ) ;
}
SignalEdit : : SignalEdit ( int index , QWidget * parent ) : form_idx ( index ) , QWidget ( parent ) {
QVBoxLayout * main_layout = new QVBoxLayout ( this ) ;
main_layout - > setContentsMargins ( 0 , 0 , 0 , 0 ) ;
main_layout - > setSpacing ( 0 ) ;
void SignalModel : : updateState ( const QHash < QString , CanData > * msgs ) {
if ( ! msgs | | ( msgs - > contains ( msg_id ) ) ) {
auto & dat = can - > lastMessage ( msg_id ) . dat ;
int row = 0 ;
for ( auto item : root - > children ) {
double value = get_raw_value ( ( uint8_t * ) dat . begin ( ) , dat . size ( ) , * item - > sig ) ;
item - > sig_val = QString : : number ( value ) ;
emit dataChanged ( index ( row , 1 ) , index ( row , 1 ) , { Qt : : DisplayRole } ) ;
+ + row ;
}
}
}
bg_color = QColor ( getColor ( form_idx ) ) ;
int SignalModel : : rowCount ( const QModelIndex & parent ) const {
if ( parent . column ( ) > 0 ) return 0 ;
// title bar
auto title_bar = new QWidget ( this ) ;
title_bar - > setFixedHeight ( 32 ) ;
QHBoxLayout * title_layout = new QHBoxLayout ( title_bar ) ;
title_layout - > setContentsMargins ( 0 , 0 , 0 , 0 ) ;
title_bar - > setStyleSheet ( " QToolButton {width:15px;height:15px;font-size:15px} " ) ;
color_label = new QLabel ( this ) ;
color_label - > setFixedWidth ( 25 ) ;
color_label - > setContentsMargins ( 5 , 0 , 0 , 0 ) ;
title_layout - > addWidget ( color_label ) ;
icon = new QLabel ( this ) ;
title_layout - > addWidget ( icon ) ;
title = new ElidedLabel ( this ) ;
title - > setSizePolicy ( QSizePolicy : : Expanding , QSizePolicy : : Preferred ) ;
title_layout - > addWidget ( title ) ;
plot_btn = new QToolButton ( this ) ;
plot_btn - > setIcon ( bootstrapPixmap ( " graph-up " ) ) ;
plot_btn - > setCheckable ( true ) ;
plot_btn - > setAutoRaise ( true ) ;
title_layout - > addWidget ( plot_btn ) ;
auto remove_btn = new QToolButton ( this ) ;
remove_btn - > setAutoRaise ( true ) ;
remove_btn - > setIcon ( bootstrapPixmap ( " x " ) ) ;
remove_btn - > setToolTip ( tr ( " Remove signal " ) ) ;
title_layout - > addWidget ( remove_btn ) ;
main_layout - > addWidget ( title_bar ) ;
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 ;
}
// signal form
form = new SignalForm ( this ) ;
form - > setVisible ( false ) ;
main_layout - > addWidget ( form ) ;
Qt : : ItemFlags SignalModel : : flags ( const QModelIndex & index ) const {
if ( ! index . isValid ( ) ) return Qt : : NoItemFlags ;
// bottom line
QFrame * hline = new QFrame ( ) ;
hline - > setFrameShape ( QFrame : : HLine ) ;
hline - > setFrameShadow ( QFrame : : Sunken ) ;
main_layout - > addWidget ( hline ) ;
auto item = getItem ( index ) ;
Qt : : ItemFlags flags = Qt : : ItemIsSelectable | Qt : : ItemIsEnabled ;
if ( index . column ( ) = = 1 & & item - > type ! = Item : : Sig & & item - > type ! = Item : : ExtraInfo ) {
flags | = ( item - > type = = Item : : Endian | | item - > type = = Item : : Signed ) ? Qt : : ItemIsUserCheckable : Qt : : ItemIsEditable ;
}
return flags ;
}
QObject : : connect ( title , & ElidedLabel : : clicked , [ this ] ( ) { emit showFormClicked ( sig ) ; } ) ;
QObject : : connect ( plot_btn , & QToolButton : : clicked , [ this ] ( bool checked ) {
emit showChart ( msg_id , sig , checked , QGuiApplication : : keyboardModifiers ( ) & Qt : : ShiftModifier ) ;
} ) ;
QObject : : connect ( remove_btn , & QToolButton : : clicked , [ this ] ( ) { emit remove ( sig ) ; } ) ;
QObject : : connect ( form , & SignalForm : : changed , this , & SignalEdit : : saveSignal ) ;
setSizePolicy ( QSizePolicy : : Preferred , QSizePolicy : : Fixed ) ;
int SignalModel : : signalRow ( const Signal * sig ) const {
auto & children = root - > children ;
for ( int i = 0 ; i < children . size ( ) ; + + i ) {
if ( children [ i ] - > sig = = sig ) return i ;
}
return - 1 ;
}
void SignalEdit : : setSignal ( const QString & message_id , const Signal * signal ) {
sig = signal ;
updateForm ( msg_id = = message_id & & form - > isVisible ( ) ) ;
msg_id = message_id ;
color_label - > setText ( QString : : number ( form_idx + 1 ) ) ;
color_label - > setStyleSheet ( QString ( " color:black; background-color:%2 " ) . arg ( bg_color . name ( ) ) ) ;
title - > setText ( sig - > name . c_str ( ) ) ;
show ( ) ;
QModelIndex SignalModel : : index ( int row , int column , const QModelIndex & parent ) const {
if ( ! hasIndex ( row , column , parent ) ) return { } ;
return createIndex ( row , column , getItem ( parent ) - > children [ row ] ) ;
}
void SignalEdit : : saveSignal ( ) {
Signal s = * sig ;
s . name = form - > name - > text ( ) . toStdString ( ) ;
s . size = form - > size - > text ( ) . toInt ( ) ;
s . offset = form - > offset - > text ( ) . toDouble ( ) ;
s . factor = form - > factor - > text ( ) . toDouble ( ) ;
s . is_signed = form - > sign - > currentIndex ( ) = = 0 ;
bool little_endian = form - > endianness - > currentIndex ( ) = = 0 ;
if ( little_endian ! = s . is_little_endian ) {
QModelIndex SignalModel : : parent ( const QModelIndex & index ) const {
if ( ! index . isValid ( ) ) return { } ;
Item * parent_item = getItem ( index ) - > parent ;
return parent_item = = root . get ( ) ? QModelIndex ( ) : createIndex ( parent_item - > row ( ) , 0 , parent_item ) ;
}
QVariant SignalModel : : data ( const QModelIndex & index , int role ) const {
if ( index . isValid ( ) ) {
const Item * item = getItem ( index ) ;
if ( role = = Qt : : DisplayRole | | role = = Qt : : EditRole ) {
if ( index . column ( ) = = 0 ) {
return item - > type = = Item : : Sig ? QString : : fromStdString ( item - > sig - > name ) : item - > title ;
} else {
switch ( item - > type ) {
case Item : : Sig : return item - > sig_val ;
case Item : : Name : return QString : : fromStdString ( item - > sig - > name ) ;
case Item : : Size : return item - > sig - > size ;
case Item : : Offset : return QString : : number ( item - > sig - > offset , ' f ' , 6 ) ;
case Item : : Factor : return QString : : number ( item - > sig - > factor , ' f ' , 6 ) ;
default : break ;
}
}
} 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 bootstrapPixmap ( item - > parent - > extra_expanded ? " chevron-compact-down " : " chevron-compact-up " ) ;
}
}
return { } ;
}
bool SignalModel : : setData ( const QModelIndex & index , const QVariant & value , int role ) {
if ( role ! = Qt : : EditRole & & role ! = Qt : : CheckStateRole ) return false ;
Item * item = getItem ( index ) ;
Signal s = * item - > sig ;
switch ( item - > type ) {
case Item : : Name : s . name = value . toString ( ) . toStdString ( ) ; break ;
case Item : : Size : s . size = value . toInt ( ) ; break ;
case Item : : Endian : s . is_little_endian = value . toBool ( ) ; break ;
case Item : : Signed : s . is_signed = value . toBool ( ) ; break ;
case Item : : Offset : s . offset = value . toDouble ( ) ; break ;
case Item : : Factor : s . factor = value . toDouble ( ) ; break ;
default : return false ;
}
bool ret = saveSignal ( item - > sig , s ) ;
emit dataChanged ( index , index , { Qt : : DisplayRole , Qt : : EditRole , Qt : : CheckStateRole } ) ;
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 ( ) , 7 , 13 ) ;
endInsertRows ( ) ;
} else {
item - > parent - > extra_expanded = false ;
beginRemoveRows ( index . parent ( ) , 7 , 13 ) ;
endRemoveRows ( ) ;
}
}
}
bool SignalModel : : saveSignal ( const Signal * origin_s , Signal & s ) {
auto msg = dbc ( ) - > msg ( msg_id ) ;
if ( s . name ! = origin_s - > name & & msg - > sigs . count ( s . name . c_str ( ) ) ! = 0 ) {
QString text = tr ( " There is already a signal with the same name '%1' " ) . arg ( s . name . c_str ( ) ) ;
QMessageBox : : warning ( nullptr , tr ( " Failed to save signal " ) , text ) ;
return false ;
}
if ( s . is_little_endian ! = origin_s - > is_little_endian ) {
int start = std : : floor ( s . start_bit / 8 ) ;
if ( little_endian ) {
if ( s . is_ little_endian) {
int end = std : : floor ( ( s . start_bit - s . size + 1 ) / 8 ) ;
s . start_bit = start = = end ? s . start_bit - s . size + 1 : bigEndianStartBitsIndex ( s . start_bit ) ;
} else {
int end = std : : floor ( ( s . start_bit + s . size - 1 ) / 8 ) ;
s . start_bit = start = = end ? s . start_bit + s . size - 1 : bigEndianBitIndex ( s . start_bit ) ;
}
s . is_little_endian = little_endian ;
}
if ( s . is_little_endian ) {
s . lsb = s . start_bit ;
@ -177,43 +197,254 @@ void SignalEdit::saveSignal() {
s . lsb = bigEndianStartBitsIndex ( bigEndianBitIndex ( s . start_bit ) + s . size - 1 ) ;
s . msb = s . start_bit ;
}
if ( s ! = * sig ) {
emit save ( this - > sig , s ) ;
UndoStack : : push ( new EditSignalCommand ( msg_id , origin_s , s ) ) ;
return true ;
}
void SignalModel : : addSignal ( int start_bit , int size , bool little_endian ) {
auto msg = dbc ( ) - > msg ( msg_id ) ;
for ( int i = 1 ; ! msg ; + + i ) {
QString name = QString ( " NEW_MSG_%1 " ) . arg ( i ) ;
if ( std : : none_of ( dbc ( ) - > messages ( ) . begin ( ) , dbc ( ) - > messages ( ) . end ( ) , [ & ] ( auto & m ) { return m . second . name = = name ; } ) ) {
UndoStack : : push ( new EditMsgCommand ( msg_id , name , can - > lastMessage ( msg_id ) . dat . size ( ) ) ) ;
msg = dbc ( ) - > msg ( msg_id ) ;
}
}
Signal sig = { . is_little_endian = little_endian , . factor = 1 } ;
for ( int i = 1 ; /**/ ; + + i ) {
sig . name = " NEW_SIGNAL_ " + std : : to_string ( i ) ;
if ( msg - > sigs . count ( sig . name . c_str ( ) ) = = 0 ) break ;
}
updateSigSizeParamsFromRange ( sig , start_bit , size ) ;
UndoStack : : push ( new AddSigCommand ( msg_id , sig ) ) ;
}
void SignalModel : : resizeSignal ( const Signal * sig , int start_bit , int size ) {
Signal s = * sig ;
updateSigSizeParamsFromRange ( s , start_bit , size ) ;
saveSignal ( sig , s ) ;
}
void SignalModel : : removeSignal ( const Signal * sig ) {
UndoStack : : push ( new RemoveSigCommand ( msg_id , sig ) ) ;
}
void SignalModel : : handleMsgChanged ( uint32_t address ) {
if ( address = = DBCManager : : parseId ( msg_id ) . second ) {
refresh ( ) ;
}
}
void SignalModel : : handleSignalAdded ( uint32_t address , const Signal * sig ) {
if ( address = = DBCManager : : parseId ( msg_id ) . second ) {
int i = 0 ;
for ( ; i < root - > children . size ( ) ; + + i ) {
if ( sig - > start_bit < root - > children [ i ] - > sig - > start_bit ) break ;
}
beginInsertRows ( { } , i , i ) ;
insertItem ( root . get ( ) , i , sig ) ;
endInsertRows ( ) ;
}
}
void SignalModel : : handleSignalUpdated ( const Signal * sig ) {
if ( int row = signalRow ( sig ) ; row ! = - 1 ) {
emit dataChanged ( index ( row , 0 ) , index ( row , 1 ) , { Qt : : DisplayRole , Qt : : EditRole , Qt : : CheckStateRole } ) ;
}
}
void SignalModel : : handleSignalRemoved ( const Signal * sig ) {
if ( int row = signalRow ( sig ) ; row ! = - 1 ) {
beginRemoveRows ( { } , row , row ) ;
delete root - > children . takeAt ( row ) ;
endRemoveRows ( ) ;
}
}
// SignalItemDelegate
SignalItemDelegate : : SignalItemDelegate ( QObject * parent ) {
name_validator = new NameValidator ( this ) ;
double_validator = new QDoubleValidator ( this ) ;
double_validator - > setLocale ( QLocale : : C ) ; // Match locale of QString::toDouble() instead of system
}
void SignalItemDelegate : : paint ( QPainter * painter , const QStyleOptionViewItem & option , const QModelIndex & index ) const {
auto item = ( SignalModel : : Item * ) index . internalPointer ( ) ;
if ( item & & ! index . parent ( ) . isValid ( ) & & index . column ( ) = = 0 ) {
painter - > save ( ) ;
painter - > setRenderHint ( QPainter : : Antialiasing ) ;
if ( option . state & QStyle : : State_Selected ) {
painter - > fillRect ( option . rect , option . palette . highlight ( ) ) ;
}
// color label
auto bg_color = QColor ( getColor ( item - > row ( ) ) ) ;
QRect rc { option . rect . left ( ) + 3 , option . rect . top ( ) , 22 , option . rect . height ( ) } ;
painter - > setPen ( Qt : : NoPen ) ;
painter - > setBrush ( item - > highlight ? bg_color . darker ( 125 ) : bg_color ) ;
painter - > drawRoundedRect ( rc . adjusted ( 0 , 2 , 0 , - 2 ) , 5 , 5 ) ;
painter - > setPen ( item - > highlight ? Qt : : white : Qt : : black ) ;
painter - > drawText ( rc , Qt : : AlignCenter , QString : : number ( item - > row ( ) + 1 ) ) ;
// signal name
QFont font ;
font . setBold ( true ) ;
painter - > setFont ( font ) ;
painter - > setPen ( ( option . state & QStyle : : State_Selected ? option . palette . highlightedText ( ) : option . palette . text ( ) ) . color ( ) ) ;
painter - > drawText ( option . rect . adjusted ( rc . width ( ) + 9 , 0 , 0 , 0 ) , option . displayAlignment , index . data ( Qt : : DisplayRole ) . toString ( ) ) ;
painter - > restore ( ) ;
} else {
QStyledItemDelegate : : paint ( painter , option , index ) ;
}
}
void SignalEdit : : setChartOpened ( bool opened ) {
plot_btn - > setToolTip ( opened ? tr ( " Close Plot " ) : tr ( " Show Plot \n SHIFT click to add to previous opened chart " ) ) ;
plot_btn - > setChecked ( opened ) ;
QWidget * SignalItemDelegate : : createEditor ( QWidget * parent , const QStyleOptionViewItem & option , const QModelIndex & index ) const {
auto item = ( SignalModel : : Item * ) index . internalPointer ( ) ;
if ( item - > type = = SignalModel : : Item : : Name | | item - > type = = SignalModel : : Item : : Offset | | item - > type = = SignalModel : : Item : : Factor ) {
QLineEdit * e = new QLineEdit ( parent ) ;
e - > setFrame ( false ) ;
e - > setValidator ( index . row ( ) = = 0 ? name_validator : double_validator ) ;
return e ;
} else if ( item - > type = = SignalModel : : Item : : Size ) {
QSpinBox * spin = new QSpinBox ( parent ) ;
spin - > setFrame ( false ) ;
spin - > setRange ( 1 , 64 ) ;
return spin ;
}
return QStyledItemDelegate : : createEditor ( parent , option , index ) ;
}
// SignalView
SignalView : : SignalView ( ChartsWidget * charts , QWidget * parent ) : charts ( charts ) , QWidget ( parent ) {
// title bar
QWidget * title_bar = new QWidget ( this ) ;
title_bar - > setAutoFillBackground ( true ) ;
QHBoxLayout * hl = new QHBoxLayout ( title_bar ) ;
hl - > addWidget ( signal_count_lb = new QLabel ( ) ) ;
filter_edit = new QLineEdit ( this ) ;
filter_edit - > setClearButtonEnabled ( true ) ;
filter_edit - > setPlaceholderText ( tr ( " filter signals by name " ) ) ;
hl - > addWidget ( filter_edit ) ;
hl - > addStretch ( 1 ) ;
auto collapse_btn = new QToolButton ( ) ;
collapse_btn - > setIcon ( bootstrapPixmap ( " dash-square " ) ) ;
collapse_btn - > setIconSize ( { 12 , 12 } ) ;
collapse_btn - > setAutoRaise ( true ) ;
collapse_btn - > setToolTip ( tr ( " Collapse All " ) ) ;
hl - > addWidget ( collapse_btn ) ;
// tree view
tree = new QTreeView ( this ) ;
tree - > setModel ( model = new SignalModel ( this ) ) ;
tree - > setItemDelegate ( new SignalItemDelegate ( this ) ) ;
tree - > setFrameShape ( QFrame : : NoFrame ) ;
tree - > setHeaderHidden ( true ) ;
tree - > setMouseTracking ( true ) ;
tree - > setExpandsOnDoubleClick ( false ) ;
tree - > header ( ) - > setSectionResizeMode ( QHeaderView : : Stretch ) ;
tree - > setMinimumHeight ( 300 ) ;
tree - > setStyleSheet ( " QSpinBox{background-color:white;border:none;} QLineEdit{background-color:white;} " ) ;
QVBoxLayout * main_layout = new QVBoxLayout ( this ) ;
main_layout - > setContentsMargins ( 0 , 0 , 0 , 0 ) ;
main_layout - > setSpacing ( 0 ) ;
main_layout - > addWidget ( title_bar ) ;
main_layout - > addWidget ( tree ) ;
QObject : : connect ( filter_edit , & QLineEdit : : textEdited , model , & SignalModel : : setFilter ) ;
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 ) ; } ) ;
QObject : : connect ( tree , & QTreeView : : entered , [ this ] ( const QModelIndex & index ) { emit highlight ( model - > getItem ( index ) - > sig ) ; } ) ;
QObject : : connect ( model , & QAbstractItemModel : : modelReset , this , & SignalView : : rowsChanged ) ;
QObject : : connect ( model , & QAbstractItemModel : : rowsInserted , 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 ) ; } ) ;
}
void SignalView : : setMessage ( const QString & id ) {
msg_id = id ;
filter_edit - > clear ( ) ;
model - > setMessage ( id ) ;
}
void SignalEdit : : updateForm ( bool visible ) {
if ( visible & & sig ) {
if ( form - > name - > text ( ) ! = sig - > name . c_str ( ) ) {
form - > name - > setText ( sig - > name . c_str ( ) ) ;
void SignalView : : rowsChanged ( ) {
auto create_btn = [ ] ( const QString & id , const QString & tooltip ) {
auto btn = new QToolButton ( ) ;
btn - > setIcon ( bootstrapPixmap ( 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 ) {
auto index = model - > index ( i , 1 ) ;
if ( ! tree - > indexWidget ( index ) ) {
QWidget * w = new QWidget ( this ) ;
QHBoxLayout * h = new QHBoxLayout ( w ) ;
h - > setContentsMargins ( 0 , 2 , 0 , 2 ) ;
h - > addStretch ( 1 ) ;
auto remove_btn = create_btn ( " x " , tr ( " Remove signal " ) ) ;
auto plot_btn = create_btn ( " graph-up " , " " ) ;
plot_btn - > setCheckable ( true ) ;
h - > addWidget ( plot_btn ) ;
h - > addWidget ( remove_btn ) ;
tree - > setIndexWidget ( index , w ) ;
auto sig = model - > getItem ( index ) - > sig ;
QObject : : connect ( remove_btn , & QToolButton : : clicked , [ = ] ( ) { model - > removeSignal ( sig ) ; } ) ;
QObject : : connect ( plot_btn , & QToolButton : : clicked , [ = ] ( bool checked ) {
emit showChart ( msg_id , sig , checked , QGuiApplication : : keyboardModifiers ( ) & Qt : : ShiftModifier ) ;
} ) ;
}
form - > endianness - > setCurrentIndex ( sig - > is_little_endian ? 0 : 1 ) ;
form - > sign - > setCurrentIndex ( sig - > is_signed ? 0 : 1 ) ;
form - > factor - > setText ( QString : : number ( sig - > factor ) ) ;
form - > offset - > setText ( QString : : number ( sig - > offset ) ) ;
form - > size - > setValue ( sig - > size ) ;
}
form - > setVisible ( visible ) ;
icon - > setText ( visible ? " ▼ " : " > " ) ;
updateChartState ( ) ;
}
void SignalEdit : : signalHovered ( const Signal * s ) {
auto text_color = sig = = s ? " white " : " black " ;
auto _bg_color = sig = = s ? bg_color . darker ( 125 ) : bg_color ; // 4/5x brightness
color_label - > setStyleSheet ( QString ( " color:%1; background-color:%2 " ) . arg ( text_color ) . arg ( _bg_color . name ( ) ) ) ;
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 ) ;
}
}
void SignalEdit : : enterEvent ( QEvent * event ) {
emit highlight ( sig ) ;
QWidget : : enterEvent ( event ) ;
void SignalView : : expandSignal ( const Signal * sig ) {
if ( int row = model - > signalRow ( sig ) ; row ! = - 1 ) {
auto idx = model - > index ( row , 0 ) ;
bool expand = ! tree - > isExpanded ( idx ) ;
tree - > setExpanded ( idx , expand ) ;
tree - > scrollTo ( idx , QAbstractItemView : : PositionAtTop ) ;
if ( expand ) tree - > setCurrentIndex ( idx ) ;
}
}
void SignalEdit : : leaveEvent ( QEvent * event ) {
emit highlight ( nullptr ) ;
QWidget : : leaveEvent ( event ) ;
void SignalView : : updateChartState ( ) {
int i = 0 ;
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 ) ;
plot_btn - > setChecked ( chart_opened ) ;
plot_btn - > setToolTip ( chart_opened ? tr ( " Close Plot " ) : tr ( " Show Plot \n SHIFT click to add to previous opened plot " ) ) ;
+ + i ;
}
}
void SignalView : : signalHovered ( const Signal * sig ) {
auto & children = model - > root - > children ;
for ( int i = 0 ; i < children . size ( ) ; + + i ) {
bool highlight = children [ i ] - > sig = = sig ;
if ( std : : exchange ( children [ i ] - > highlight , highlight ) ! = highlight ) {
emit model - > dataChanged ( model - > index ( i , 0 ) , model - > index ( i , 0 ) ) ;
}
}
}