@ -1,396 +1,19 @@
# include "tools/cabana/chartswidge t.h"
# include "tools/cabana/chart/char t.h"
# include <QActionGroup>
# include <QApplication>
# include <QCompleter>
# include <QDialogButtonBox>
# include <QDrag>
# include <QFutureSynchronizer>
# include <QGraphicsLayout>
# include <QLineEdi t>
# include <QGraphicsDropShadowEffec t>
# include <QMenu>
# include <QMimeData>
# include <QOpenGLWidget>
# include <QPushButton>
# include <QRubberBand>
# include <QScrollBar>
# include <QStylePainter>
# include <QToolBar>
# include <QToolTip>
# include <QtConcurrent>
# include <QtMath>
const int MAX_COLUMN_COUNT = 4 ;
static inline bool xLessThan ( const QPointF & p , float x ) { return p . x ( ) < x ; }
// ChartsWidget
ChartsWidget : : ChartsWidget ( QWidget * parent ) : align_timer ( this ) , auto_scroll_timer ( this ) , QFrame ( parent ) {
setFrameStyle ( QFrame : : StyledPanel | QFrame : : Plain ) ;
QVBoxLayout * main_layout = new QVBoxLayout ( this ) ;
main_layout - > setContentsMargins ( 0 , 0 , 0 , 0 ) ;
// toolbar
QToolBar * toolbar = new QToolBar ( tr ( " Charts " ) , this ) ;
int icon_size = style ( ) - > pixelMetric ( QStyle : : PM_SmallIconSize ) ;
toolbar - > setIconSize ( { icon_size , icon_size } ) ;
QAction * new_plot_btn = toolbar - > addAction ( utils : : icon ( " file-plus " ) , tr ( " New Plot " ) ) ;
toolbar - > addWidget ( title_label = new QLabel ( ) ) ;
title_label - > setContentsMargins ( 0 , 0 , style ( ) - > pixelMetric ( QStyle : : PM_LayoutHorizontalSpacing ) , 0 ) ;
QMenu * menu = new QMenu ( this ) ;
for ( int i = 0 ; i < MAX_COLUMN_COUNT ; + + i ) {
menu - > addAction ( tr ( " %1 " ) . arg ( i + 1 ) , [ = ] ( ) { setColumnCount ( i + 1 ) ; } ) ;
}
columns_action = toolbar - > addAction ( " " ) ;
columns_action - > setMenu ( menu ) ;
qobject_cast < QToolButton * > ( toolbar - > widgetForAction ( columns_action ) ) - > setPopupMode ( QToolButton : : InstantPopup ) ;
QLabel * stretch_label = new QLabel ( this ) ;
stretch_label - > setSizePolicy ( QSizePolicy : : Expanding , QSizePolicy : : Preferred ) ;
toolbar - > addWidget ( stretch_label ) ;
range_lb_action = toolbar - > addWidget ( range_lb = new QLabel ( this ) ) ;
range_slider = new LogSlider ( 1000 , Qt : : Horizontal , this ) ;
range_slider - > setMaximumWidth ( 200 ) ;
range_slider - > setToolTip ( tr ( " Set the chart range " ) ) ;
range_slider - > setRange ( 1 , settings . max_cached_minutes * 60 ) ;
range_slider - > setSingleStep ( 1 ) ;
range_slider - > setPageStep ( 60 ) ; // 1 min
range_slider_action = toolbar - > addWidget ( range_slider ) ;
// zoom controls
zoom_undo_stack = new QUndoStack ( this ) ;
undo_zoom_action = zoom_undo_stack - > createUndoAction ( this ) ;
undo_zoom_action - > setIcon ( utils : : icon ( " arrow-counterclockwise " ) ) ;
toolbar - > addAction ( undo_zoom_action ) ;
redo_zoom_action = zoom_undo_stack - > createRedoAction ( this ) ;
redo_zoom_action - > setIcon ( utils : : icon ( " arrow-clockwise " ) ) ;
toolbar - > addAction ( redo_zoom_action ) ;
reset_zoom_action = toolbar - > addAction ( utils : : icon ( " zoom-out " ) , " " ) ;
reset_zoom_action - > setToolTip ( tr ( " Reset zoom " ) ) ;
qobject_cast < QToolButton * > ( toolbar - > widgetForAction ( reset_zoom_action ) ) - > setToolButtonStyle ( Qt : : ToolButtonTextBesideIcon ) ;
remove_all_btn = toolbar - > addAction ( utils : : icon ( " x " ) , tr ( " Remove all charts " ) ) ;
dock_btn = toolbar - > addAction ( " " ) ;
main_layout - > addWidget ( toolbar ) ;
// charts
charts_layout = new QGridLayout ( ) ;
charts_layout - > setSpacing ( 10 ) ;
charts_container = new QWidget ( this ) ;
QVBoxLayout * charts_main_layout = new QVBoxLayout ( charts_container ) ;
charts_main_layout - > setContentsMargins ( 0 , 0 , 0 , 0 ) ;
charts_main_layout - > addLayout ( charts_layout ) ;
charts_main_layout - > addStretch ( 0 ) ;
charts_scroll = new QScrollArea ( this ) ;
charts_scroll - > setFrameStyle ( QFrame : : NoFrame ) ;
charts_scroll - > setWidgetResizable ( true ) ;
charts_scroll - > setWidget ( charts_container ) ;
charts_scroll - > setHorizontalScrollBarPolicy ( Qt : : ScrollBarAlwaysOff ) ;
main_layout - > addWidget ( charts_scroll ) ;
// init settings
column_count = std : : clamp ( settings . chart_column_count , 1 , MAX_COLUMN_COUNT ) ;
max_chart_range = std : : clamp ( settings . chart_range , 1 , settings . max_cached_minutes * 60 ) ;
display_range = { 0 , max_chart_range } ;
range_slider - > setValue ( max_chart_range ) ;
updateToolBar ( ) ;
align_timer . setSingleShot ( true ) ;
QObject : : connect ( & align_timer , & QTimer : : timeout , this , & ChartsWidget : : alignCharts ) ;
QObject : : connect ( & auto_scroll_timer , & QTimer : : timeout , this , & ChartsWidget : : doAutoScroll ) ;
QObject : : connect ( dbc ( ) , & DBCManager : : DBCFileChanged , this , & ChartsWidget : : removeAll ) ;
QObject : : connect ( can , & AbstractStream : : eventsMerged , this , & ChartsWidget : : eventsMerged ) ;
QObject : : connect ( can , & AbstractStream : : updated , this , & ChartsWidget : : updateState ) ;
QObject : : connect ( range_slider , & QSlider : : valueChanged , this , & ChartsWidget : : setMaxChartRange ) ;
QObject : : connect ( new_plot_btn , & QAction : : triggered , this , & ChartsWidget : : newChart ) ;
QObject : : connect ( remove_all_btn , & QAction : : triggered , this , & ChartsWidget : : removeAll ) ;
QObject : : connect ( reset_zoom_action , & QAction : : triggered , this , & ChartsWidget : : zoomReset ) ;
QObject : : connect ( & settings , & Settings : : changed , this , & ChartsWidget : : settingChanged ) ;
QObject : : connect ( dock_btn , & QAction : : triggered , [ this ] ( ) {
emit dock ( ! docking ) ;
docking = ! docking ;
updateToolBar ( ) ;
} ) ;
setWhatsThis ( tr ( R " (
< b > Chart view < / b > < br / >
< ! - - TODO : add descprition here - - >
) " ));
}
void ChartsWidget : : eventsMerged ( ) {
QFutureSynchronizer < void > future_synchronizer ;
for ( auto c : charts ) {
future_synchronizer . addFuture ( QtConcurrent : : run ( c , & ChartView : : updateSeries , nullptr ) ) ;
}
}
void ChartsWidget : : setZoom ( double min , double max ) {
zoomed_range = { min , max } ;
is_zoomed = zoomed_range ! = display_range ;
updateToolBar ( ) ;
updateState ( ) ;
emit rangeChanged ( min , max , is_zoomed ) ;
}
void ChartsWidget : : zoomIn ( double min , double max ) {
zoom_undo_stack - > push ( new ZoomCommand ( this , { min , max } ) ) ;
}
void ChartsWidget : : zoomReset ( ) {
setZoom ( display_range . first , display_range . second ) ;
zoom_undo_stack - > clear ( ) ;
}
void ChartsWidget : : showValueTip ( double sec ) {
const QRect visible_rect ( - charts_container - > pos ( ) , charts_scroll - > viewport ( ) - > size ( ) ) ;
for ( auto c : charts ) {
if ( sec > = 0 & & visible_rect . contains ( QRect ( c - > mapTo ( charts_container , QPoint ( 0 , 0 ) ) , c - > size ( ) ) ) ) {
c - > showTip ( sec ) ;
} else {
c - > hideTip ( ) ;
}
}
}
void ChartsWidget : : updateState ( ) {
if ( charts . isEmpty ( ) ) return ;
const double cur_sec = can - > currentSec ( ) ;
if ( ! is_zoomed ) {
double pos = ( cur_sec - display_range . first ) / std : : max < float > ( 1.0 , max_chart_range ) ;
if ( pos < 0 | | pos > 0.8 ) {
display_range . first = std : : max ( 0.0 , cur_sec - max_chart_range * 0.1 ) ;
}
double max_sec = std : : min ( std : : floor ( display_range . first + max_chart_range ) , can - > lastEventSecond ( ) ) ;
display_range . first = std : : max ( 0.0 , max_sec - max_chart_range ) ;
display_range . second = display_range . first + max_chart_range ;
} else if ( cur_sec < zoomed_range . first | | cur_sec > = zoomed_range . second ) {
// loop in zoomed range
can - > seekTo ( zoomed_range . first ) ;
}
const auto & range = is_zoomed ? zoomed_range : display_range ;
for ( auto c : charts ) {
c - > updatePlot ( cur_sec , range . first , range . second ) ;
}
}
void ChartsWidget : : setMaxChartRange ( int value ) {
max_chart_range = settings . chart_range = range_slider - > value ( ) ;
updateToolBar ( ) ;
updateState ( ) ;
}
void ChartsWidget : : updateToolBar ( ) {
title_label - > setText ( tr ( " Charts: %1 " ) . arg ( charts . size ( ) ) ) ;
columns_action - > setText ( tr ( " Column: %1 " ) . arg ( column_count ) ) ;
range_lb - > setText ( utils : : formatSeconds ( max_chart_range ) ) ;
range_lb_action - > setVisible ( ! is_zoomed ) ;
range_slider_action - > setVisible ( ! is_zoomed ) ;
undo_zoom_action - > setVisible ( is_zoomed ) ;
redo_zoom_action - > setVisible ( is_zoomed ) ;
reset_zoom_action - > setVisible ( is_zoomed ) ;
reset_zoom_action - > setText ( is_zoomed ? tr ( " %1-%2 " ) . arg ( zoomed_range . first , 0 , ' f ' , 1 ) . arg ( zoomed_range . second , 0 , ' f ' , 1 ) : " " ) ;
remove_all_btn - > setEnabled ( ! charts . isEmpty ( ) ) ;
dock_btn - > setIcon ( utils : : icon ( docking ? " arrow-up-right-square " : " arrow-down-left-square " ) ) ;
dock_btn - > setToolTip ( docking ? tr ( " Undock charts " ) : tr ( " Dock charts " ) ) ;
}
void ChartsWidget : : settingChanged ( ) {
range_slider - > setRange ( 1 , settings . max_cached_minutes * 60 ) ;
for ( auto c : charts ) {
c - > setFixedHeight ( settings . chart_height ) ;
c - > setSeriesType ( ( SeriesType ) settings . chart_series_type ) ;
}
}
ChartView * ChartsWidget : : findChart ( const MessageId & id , const cabana : : Signal * sig ) {
for ( auto c : charts )
if ( c - > hasSeries ( id , sig ) ) return c ;
return nullptr ;
}
ChartView * ChartsWidget : : createChart ( ) {
auto chart = new ChartView ( is_zoomed ? zoomed_range : display_range , this ) ;
chart - > setFixedHeight ( settings . chart_height ) ;
chart - > setMinimumWidth ( CHART_MIN_WIDTH ) ;
chart - > setSizePolicy ( QSizePolicy : : MinimumExpanding , QSizePolicy : : Fixed ) ;
chart - > chart ( ) - > setTheme ( settings . theme = = 2 ? QChart : : QChart : : ChartThemeDark : QChart : : ChartThemeLight ) ;
QObject : : connect ( chart , & ChartView : : remove , [ = ] ( ) { removeChart ( chart ) ; } ) ;
QObject : : connect ( chart , & ChartView : : zoomIn , this , & ChartsWidget : : zoomIn ) ;
QObject : : connect ( chart , & ChartView : : zoomUndo , undo_zoom_action , & QAction : : trigger ) ;
QObject : : connect ( chart , & ChartView : : seriesRemoved , this , & ChartsWidget : : seriesChanged ) ;
QObject : : connect ( chart , & ChartView : : seriesAdded , this , & ChartsWidget : : seriesChanged ) ;
QObject : : connect ( chart , & ChartView : : axisYLabelWidthChanged , & align_timer , qOverload < > ( & QTimer : : start ) ) ;
QObject : : connect ( chart , & ChartView : : hovered , this , & ChartsWidget : : showValueTip ) ;
charts . push_back ( chart ) ;
updateLayout ( ) ;
updateToolBar ( ) ;
return chart ;
}
void ChartsWidget : : showChart ( const MessageId & id , const cabana : : Signal * sig , bool show , bool merge ) {
ChartView * chart = findChart ( id , sig ) ;
if ( show & & ! chart ) {
chart = merge & & charts . size ( ) > 0 ? charts . back ( ) : createChart ( ) ;
chart - > addSeries ( id , sig ) ;
} else if ( ! show & & chart ) {
chart - > removeIf ( [ & ] ( auto & s ) { return s . msg_id = = id & & s . sig = = sig ; } ) ;
}
}
void ChartsWidget : : setColumnCount ( int n ) {
n = std : : clamp ( n , 1 , MAX_COLUMN_COUNT ) ;
if ( column_count ! = n ) {
column_count = settings . chart_column_count = n ;
updateToolBar ( ) ;
updateLayout ( ) ;
}
}
void ChartsWidget : : updateLayout ( ) {
int n = MAX_COLUMN_COUNT ;
for ( ; n > 1 ; - - n ) {
if ( ( n * CHART_MIN_WIDTH + ( n - 1 ) * charts_layout - > spacing ( ) ) < charts_layout - > geometry ( ) . width ( ) ) break ;
}
bool show_column_cb = n > 1 ;
columns_action - > setVisible ( show_column_cb ) ;
n = std : : min ( column_count , n ) ;
if ( charts . size ( ) ! = charts_layout - > count ( ) | | n ! = current_column_count ) {
current_column_count = n ;
charts_layout - > parentWidget ( ) - > setUpdatesEnabled ( false ) ;
for ( int i = 0 ; i < charts . size ( ) ; + + i ) {
charts_layout - > addWidget ( charts [ charts . size ( ) - i - 1 ] , i / n , i % n ) ;
}
QTimer : : singleShot ( 0 , [ this ] ( ) { charts_layout - > parentWidget ( ) - > setUpdatesEnabled ( true ) ; } ) ;
}
}
void ChartsWidget : : startAutoScroll ( ) {
auto_scroll_timer . start ( 50 ) ;
}
void ChartsWidget : : stopAutoScroll ( ) {
auto_scroll_timer . stop ( ) ;
auto_scroll_count = 0 ;
}
void ChartsWidget : : doAutoScroll ( ) {
QScrollBar * scroll = charts_scroll - > verticalScrollBar ( ) ;
if ( auto_scroll_count < scroll - > pageStep ( ) ) {
+ + auto_scroll_count ;
}
int value = scroll - > value ( ) ;
QPoint pos = charts_scroll - > viewport ( ) - > mapFromGlobal ( QCursor : : pos ( ) ) ;
QRect area = charts_scroll - > viewport ( ) - > rect ( ) ;
if ( pos . y ( ) - area . top ( ) < settings . chart_height / 2 ) {
scroll - > setValue ( value - auto_scroll_count ) ;
} else if ( area . bottom ( ) - pos . y ( ) < settings . chart_height / 2 ) {
scroll - > setValue ( value + auto_scroll_count ) ;
}
bool vertical_unchanged = value = = scroll - > value ( ) ;
if ( vertical_unchanged ) {
stopAutoScroll ( ) ;
}
}
void ChartsWidget : : resizeEvent ( QResizeEvent * event ) {
QWidget : : resizeEvent ( event ) ;
updateLayout ( ) ;
}
void ChartsWidget : : newChart ( ) {
SeriesSelector dlg ( tr ( " New Chart " ) , this ) ;
if ( dlg . exec ( ) = = QDialog : : Accepted ) {
auto items = dlg . seletedItems ( ) ;
if ( ! items . isEmpty ( ) ) {
auto c = createChart ( ) ;
for ( auto it : items ) {
c - > addSeries ( it - > msg_id , it - > sig ) ;
}
}
}
}
void ChartsWidget : : removeChart ( ChartView * chart ) {
charts . removeOne ( chart ) ;
chart - > deleteLater ( ) ;
updateToolBar ( ) ;
updateLayout ( ) ;
alignCharts ( ) ;
emit seriesChanged ( ) ;
}
void ChartsWidget : : removeAll ( ) {
if ( ! charts . isEmpty ( ) ) {
for ( auto c : charts ) {
c - > deleteLater ( ) ;
}
charts . clear ( ) ;
updateToolBar ( ) ;
emit seriesChanged ( ) ;
}
}
# include "tools/cabana/chart/chartswidget.h"
void ChartsWidget : : alignCharts ( ) {
int plot_left = 0 ;
for ( auto c : charts ) {
plot_left = std : : max ( plot_left , c - > y_label_width ) ;
}
plot_left = std : : max ( ( plot_left / 10 ) * 10 + 10 , 50 ) ;
for ( auto c : charts ) {
c - > updatePlotArea ( plot_left ) ;
}
}
bool ChartsWidget : : eventFilter ( QObject * obj , QEvent * event ) {
if ( obj ! = this & & event - > type ( ) = = QEvent : : Close ) {
emit dock_btn - > triggered ( ) ;
return true ;
}
return false ;
}
bool ChartsWidget : : event ( QEvent * event ) {
bool back_button = false ;
switch ( event - > type ( ) ) {
case QEvent : : MouseButtonPress : {
QMouseEvent * ev = static_cast < QMouseEvent * > ( event ) ;
back_button = ev - > button ( ) = = Qt : : BackButton ;
break ;
}
case QEvent : : NativeGesture : {
QNativeGestureEvent * ev = static_cast < QNativeGestureEvent * > ( event ) ;
back_button = ( ev - > value ( ) = = 180 ) ;
break ;
}
case QEvent : : WindowActivate :
case QEvent : : WindowDeactivate :
case QEvent : : FocusIn :
case QEvent : : FocusOut :
case QEvent : : Leave :
showValueTip ( - 1 ) ;
break ;
default :
break ;
}
if ( back_button ) {
emit undo_zoom_action - > triggered ( ) ;
return true ;
}
return QFrame : : event ( event ) ;
}
// ChartView
static inline bool xLessThan ( const QPointF & p , float x ) { return p . x ( ) < x ; }
ChartView : : ChartView ( const std : : pair < double , double > & x_range , ChartsWidget * parent ) : charts_widget ( parent ) , tip_label ( this ) , QChartView ( nullptr , parent ) {
series_type = ( SeriesType ) settings . chart_series_type ;
@ -411,6 +34,7 @@ ChartView::ChartView(const std::pair<double, double> &x_range, ChartsWidget *par
// TODO: enable zoomIn/seekTo in live streaming mode.
setRubberBand ( can - > liveStreaming ( ) ? QChartView : : NoRubberBand : QChartView : : HorizontalRubberBand ) ;
setMouseTracking ( true ) ;
setTheme ( settings . theme = = DARK_THEME ? QChart : : QChart : : ChartThemeDark : QChart : : ChartThemeLight ) ;
QObject : : connect ( axis_y , & QValueAxis : : rangeChanged , [ this ] ( ) { resetChartCache ( ) ; } ) ;
QObject : : connect ( axis_y , & QAbstractAxis : : titleTextChanged , [ this ] ( ) { resetChartCache ( ) ; } ) ;
@ -423,9 +47,9 @@ ChartView::ChartView(const std::pair<double, double> &x_range, ChartsWidget *par
void ChartView : : createToolButtons ( ) {
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 move chart " ) ) ;
QToolButton * remove_btn = t oolButton( " x " , tr ( " Remove Chart " ) ) ;
QToolButton * remove_btn = new T oolButton( " x " , tr ( " Remove Chart " ) ) ;
close_btn_proxy = new QGraphicsProxyWidget ( chart ( ) ) ;
close_btn_proxy - > setWidget ( remove_btn ) ;
close_btn_proxy - > setZValue ( chart ( ) - > zValue ( ) + 11 ) ;
@ -445,19 +69,38 @@ void ChartView::createToolButtons() {
menu - > addSeparator ( ) ;
menu - > addAction ( tr ( " Manage series " ) , this , & ChartView : : manageSeries ) ;
QToolButton * manage_btn = t oolButton( " list " , " " ) ;
QToolButton * manage_btn = new T oolButton( " list " , " " ) ;
manage_btn - > setMenu ( menu ) ;
manage_btn - > setPopupMode ( QToolButton : : InstantPopup ) ;
manage_btn - > setStyleSheet ( " QToolButton::menu-indicator { image: none; } " ) ;
manage_btn_proxy = new QGraphicsProxyWidget ( chart ( ) ) ;
manage_btn_proxy - > setWidget ( manage_btn ) ;
manage_btn_proxy - > setZValue ( chart ( ) - > zValue ( ) + 11 ) ;
QObject : : connect ( remove_btn , & QToolButton : : clicked , this , & ChartView : : remove ) ;
QObject : : connect ( remove_btn , & QToolButton : : clicked , [ this ] ( ) { charts_widget - > removeChart ( this ) ; } ) ;
QObject : : connect ( change_series_group , & QActionGroup : : triggered , [ this ] ( QAction * action ) {
setSeriesType ( ( SeriesType ) action - > data ( ) . toInt ( ) ) ;
} ) ;
}
QSize ChartView : : sizeHint ( ) const {
return { CHART_MIN_WIDTH , settings . chart_height } ;
}
void ChartView : : setTheme ( QChart : : ChartTheme theme ) {
chart ( ) - > setTheme ( theme ) ;
if ( theme = = QChart : : ChartThemeDark ) {
axis_x - > setTitleBrush ( palette ( ) . color ( QPalette : : Text ) ) ;
axis_x - > setLabelsBrush ( palette ( ) . color ( QPalette : : Text ) ) ;
axis_y - > setTitleBrush ( palette ( ) . color ( QPalette : : Text ) ) ;
axis_y - > setLabelsBrush ( palette ( ) . color ( QPalette : : Text ) ) ;
chart ( ) - > legend ( ) - > setLabelColor ( palette ( ) . color ( QPalette : : Text ) ) ;
}
for ( auto & s : sigs ) {
s . series - > setColor ( getColor ( s . sig ) ) ;
}
}
void ChartView : : addSeries ( const MessageId & msg_id , const cabana : : Signal * sig ) {
if ( hasSeries ( msg_id , sig ) ) return ;
@ -466,7 +109,7 @@ void ChartView::addSeries(const MessageId &msg_id, const cabana::Signal *sig) {
updateTitle ( ) ;
updateSeries ( sig ) ;
updateSeriesPoints ( ) ;
emit seriesAdded ( msg_id , sig ) ;
emit charts_widget - > seriesChanged ( ) ;
}
bool ChartView : : hasSeries ( const MessageId & msg_id , const cabana : : Signal * sig ) const {
@ -479,17 +122,15 @@ void ChartView::removeIf(std::function<bool(const SigItem &s)> predicate) {
if ( predicate ( * it ) ) {
chart ( ) - > removeSeries ( it - > series ) ;
it - > series - > deleteLater ( ) ;
auto msg_id = it - > msg_id ;
auto sig = it - > sig ;
it = sigs . erase ( it ) ;
emit seriesRemoved ( msg_id , sig ) ;
} else {
+ + it ;
}
}
if ( sigs . empty ( ) ) {
emit remove ( ) ;
charts_widget - > removeChart ( this ) ;
} else if ( sigs . size ( ) ! = prev_size ) {
emit charts_widget - > seriesChanged ( ) ;
updateAxisY ( ) ;
resetChartCache ( ) ;
}
@ -509,7 +150,7 @@ void ChartView::msgUpdated(MessageId id) {
}
void ChartView : : manageSeries ( ) {
Series Selector dlg ( tr ( " Mange Chart " ) , this ) ;
Signal Selector dlg ( tr ( " Mange Chart " ) , this ) ;
for ( auto & s : sigs ) {
dlg . addSelected ( s . msg_id , s . sig ) ;
}
@ -726,25 +367,56 @@ qreal ChartView::niceNumber(qreal x, bool ceiling) {
void ChartView : : leaveEvent ( QEvent * event ) {
if ( tip_label . isVisible ( ) ) {
emit hovered ( - 1 ) ;
charts_widget - > showValueTip ( - 1 ) ;
}
QChartView : : leaveEvent ( event ) ;
}
QPixmap getBlankShadowPixmap ( const QSize & size , int extent ) {
QGraphicsDropShadowEffect * e = new QGraphicsDropShadowEffect ;
e - > setColor ( QColor ( 40 , 40 , 40 , 245 ) ) ;
e - > setOffset ( 0 , 2 ) ;
e - > setBlurRadius ( 10 ) ;
QGraphicsScene scene ;
QGraphicsPixmapItem item ;
QPixmap src ( size ) ;
src . fill ( Qt : : white ) ;
item . setPixmap ( src ) ;
item . setGraphicsEffect ( e ) ;
scene . addItem ( & item ) ;
QImage target ( src . size ( ) + QSize ( extent * 2 , extent * 2 ) , QImage : : Format_ARGB32 ) ;
target . fill ( Qt : : transparent ) ;
QPainter p ( & target ) ;
scene . render ( & p , QRectF ( ) , QRectF ( - extent , - extent , src . width ( ) + extent * 2 , src . height ( ) + extent * 2 ) ) ;
return QPixmap : : fromImage ( target ) ;
}
static QPixmap getDropPixmap ( const QPixmap & src ) {
static QPixmap shadow_px ;
const int extent = 10 ;
if ( shadow_px . size ( ) ! = src . size ( ) + QSize ( extent * 2 , extent * 2 ) ) {
shadow_px = getBlankShadowPixmap ( src . size ( ) , extent ) ;
}
QPixmap px = shadow_px ;
QPainter p ( & px ) ;
int delta_w = px . width ( ) - src . width ( ) ;
int delta_h = px . height ( ) - src . height ( ) ;
p . drawPixmap ( QPoint ( delta_w / 2 , delta_h / 2 ) , src ) ;
p . setCompositionMode ( QPainter : : CompositionMode_DestinationIn ) ;
p . fillRect ( delta_w / 2 , delta_h / 2 , src . width ( ) , src . height ( ) , QColor ( 0 , 0 , 0 , 200 ) ) ;
return px ;
}
void ChartView : : mousePressEvent ( QMouseEvent * event ) {
if ( event - > button ( ) = = Qt : : LeftButton & & move_icon - > sceneBoundingRect ( ) . contains ( event - > pos ( ) ) ) {
QMimeData * mimeData = new QMimeData ;
mimeData - > setData ( mime_type , QByteArray : : number ( ( qulonglong ) this ) ) ;
QPixmap pm = grab ( ) ;
QPainter p ( & pm ) ;
p . setCompositionMode ( QPainter : : CompositionMode_DestinationIn ) ;
p . fillRect ( pm . rect ( ) , QColor ( 0 , 0 , 0 , 180 ) ) ;
p . end ( ) ;
mimeData - > setData ( CHART_MIME_TYPE , QByteArray : : number ( ( qulonglong ) this ) ) ;
QPixmap px = grab ( ) . scaledToWidth ( CHART_MIN_WIDTH , Qt : : SmoothTransformation ) ;
QDrag * drag = new QDrag ( this ) ;
drag - > setMimeData ( mimeData ) ;
drag - > setPixmap ( pm ) ;
drag - > setHotSpot ( event - > pos ( ) ) ;
drag - > setPixmap ( getDropPixmap ( px ) ) ;
drag - > setHotSpot ( - QPoint ( 5 , 5 ) ) ;
drag - > exec ( Qt : : CopyAction | Qt : : MoveAction , Qt : : MoveAction ) ;
charts_widget - > stopAutoScroll ( ) ;
} else if ( event - > button ( ) = = Qt : : LeftButton & & QApplication : : keyboardModifiers ( ) . testFlag ( Qt : : ShiftModifier ) ) {
@ -777,13 +449,13 @@ void ChartView::mouseReleaseEvent(QMouseEvent *event) {
// no rubber dragged, seek to mouse position
can - > seekTo ( min ) ;
} else if ( rubber - > width ( ) > 10 ) {
emit zoomIn ( min , max ) ;
charts_widget - > zoom_undo_stack - > push ( new ZoomCommand ( charts_widget , { min , max } ) ) ;
} else {
viewport ( ) - > update ( ) ;
}
event - > accept ( ) ;
} else if ( ! can - > liveStreaming ( ) & & event - > button ( ) = = Qt : : RightButton ) {
emit zoomU ndo( ) ;
charts_widget - > zoom_undo_stack - > u ndo( ) ;
event - > accept ( ) ;
} else {
QGraphicsView : : mouseReleaseEvent ( event ) ;
@ -812,9 +484,9 @@ void ChartView::mouseMoveEvent(QMouseEvent *ev) {
if ( ! is_zooming & & plot_area . contains ( ev - > pos ( ) ) ) {
const double sec = chart ( ) - > mapToValue ( ev - > pos ( ) ) . x ( ) ;
emit hovered ( sec ) ;
charts_widget - > showValueTip ( sec ) ;
} else if ( tip_label . isVisible ( ) ) {
emit hovered ( - 1 ) ;
charts_widget - > showValueTip ( - 1 ) ;
}
QChartView : : mouseMoveEvent ( ev ) ;
@ -864,32 +536,23 @@ void ChartView::hideTip() {
}
void ChartView : : dragEnterEvent ( QDragEnterEvent * event ) {
can_drop = event - > source ( ) ! = this ;
viewport ( ) - > update ( ) ;
QChartView : : dragEnterEvent ( event ) ;
}
void ChartView : : dragLeaveEvent ( QDragLeaveEvent * event ) {
can_drop = false ;
viewport ( ) - > update ( ) ;
QChartView : : dragLeaveEvent ( event ) ;
if ( event - > mimeData ( ) - > hasFormat ( CHART_MIME_TYPE ) ) {
drawDropIndicator ( event - > source ( ) ! = this ) ;
event - > acceptProposedAction ( ) ;
}
}
void ChartView : : dragMoveEvent ( QDragMoveEvent * event ) {
if ( event - > mimeData ( ) - > hasFormat ( mime_type ) ) {
if ( event - > mimeData ( ) - > hasFormat ( CHART_MIME_TYPE ) ) {
event - > setDropAction ( event - > source ( ) = = this ? Qt : : MoveAction : Qt : : CopyAction ) ;
event - > accept ( ) ;
} else {
event - > ignore ( ) ;
}
charts_widget - > startAutoScroll ( ) ;
}
void ChartView : : dropEvent ( QDropEvent * event ) {
if ( event - > mimeData ( ) - > hasFormat ( mime_type ) ) {
if ( event - > source ( ) = = this ) {
event - > setDropAction ( Qt : : MoveAction ) ;
event - > accept ( ) ;
} else {
if ( event - > mimeData ( ) - > hasFormat ( CHART_MIME_TYPE ) ) {
if ( event - > source ( ) ! = this ) {
ChartView * source_chart = ( ChartView * ) event - > source ( ) ;
for ( auto & s : source_chart - > sigs ) {
source_chart - > chart ( ) - > removeSeries ( s . series ) ;
@ -902,12 +565,10 @@ void ChartView::dropEvent(QDropEvent *event) {
updateTitle ( ) ;
source_chart - > sigs . clear ( ) ;
emit source_char t- > remove ( ) ;
charts_widge t- > removeChart ( source_chart ) ;
event - > acceptProposedAction ( ) ;
}
can_drop = false ;
} else {
event - > ignore ( ) ;
}
}
@ -1064,138 +725,3 @@ void ChartView::handleMarkerClicked() {
updateTitle ( ) ;
}
}
// SeriesSelector
SeriesSelector : : SeriesSelector ( QString title , QWidget * parent ) : QDialog ( parent ) {
setWindowTitle ( title ) ;
QGridLayout * main_layout = new QGridLayout ( this ) ;
// left column
main_layout - > addWidget ( new QLabel ( tr ( " Available Signals " ) ) , 0 , 0 ) ;
main_layout - > addWidget ( msgs_combo = new QComboBox ( this ) , 1 , 0 ) ;
msgs_combo - > setEditable ( true ) ;
msgs_combo - > lineEdit ( ) - > setPlaceholderText ( tr ( " Select a msg... " ) ) ;
msgs_combo - > setInsertPolicy ( QComboBox : : NoInsert ) ;
msgs_combo - > completer ( ) - > setCompletionMode ( QCompleter : : PopupCompletion ) ;
msgs_combo - > completer ( ) - > setFilterMode ( Qt : : MatchContains ) ;
main_layout - > addWidget ( available_list = new QListWidget ( this ) , 2 , 0 ) ;
// buttons
QVBoxLayout * btn_layout = new QVBoxLayout ( ) ;
QPushButton * add_btn = new QPushButton ( utils : : icon ( " chevron-right " ) , " " , this ) ;
add_btn - > setEnabled ( false ) ;
QPushButton * remove_btn = new QPushButton ( utils : : icon ( " chevron-left " ) , " " , this ) ;
remove_btn - > setEnabled ( false ) ;
btn_layout - > addStretch ( 0 ) ;
btn_layout - > addWidget ( add_btn ) ;
btn_layout - > addWidget ( remove_btn ) ;
btn_layout - > addStretch ( 0 ) ;
main_layout - > addLayout ( btn_layout , 0 , 1 , 3 , 1 ) ;
// right column
main_layout - > addWidget ( new QLabel ( tr ( " Selected Signals " ) ) , 0 , 2 ) ;
main_layout - > addWidget ( selected_list = new QListWidget ( this ) , 1 , 2 , 2 , 1 ) ;
auto buttonBox = new QDialogButtonBox ( QDialogButtonBox : : Ok | QDialogButtonBox : : Cancel ) ;
main_layout - > addWidget ( buttonBox , 3 , 2 ) ;
for ( auto it = can - > last_msgs . cbegin ( ) ; it ! = can - > last_msgs . cend ( ) ; + + it ) {
if ( auto m = dbc ( ) - > msg ( it . key ( ) ) ) {
msgs_combo - > addItem ( QString ( " %1 (%2) " ) . arg ( m - > name ) . arg ( it . key ( ) . toString ( ) ) , QVariant : : fromValue ( it . key ( ) ) ) ;
}
}
msgs_combo - > model ( ) - > sort ( 0 ) ;
msgs_combo - > setCurrentIndex ( - 1 ) ;
QObject : : connect ( msgs_combo , qOverload < int > ( & QComboBox : : currentIndexChanged ) , this , & SeriesSelector : : updateAvailableList ) ;
QObject : : connect ( available_list , & QListWidget : : currentRowChanged , [ = ] ( int row ) { add_btn - > setEnabled ( row ! = - 1 ) ; } ) ;
QObject : : connect ( selected_list , & QListWidget : : currentRowChanged , [ = ] ( int row ) { remove_btn - > setEnabled ( row ! = - 1 ) ; } ) ;
QObject : : connect ( available_list , & QListWidget : : itemDoubleClicked , this , & SeriesSelector : : add ) ;
QObject : : connect ( selected_list , & QListWidget : : itemDoubleClicked , this , & SeriesSelector : : remove ) ;
QObject : : connect ( add_btn , & QPushButton : : clicked , [ this ] ( ) { if ( auto item = available_list - > currentItem ( ) ) add ( item ) ; } ) ;
QObject : : connect ( remove_btn , & QPushButton : : clicked , [ this ] ( ) { if ( auto item = selected_list - > currentItem ( ) ) remove ( item ) ; } ) ;
QObject : : connect ( buttonBox , & QDialogButtonBox : : accepted , this , & QDialog : : accept ) ;
QObject : : connect ( buttonBox , & QDialogButtonBox : : rejected , this , & QDialog : : reject ) ;
}
void SeriesSelector : : add ( QListWidgetItem * item ) {
auto it = ( ListItem * ) item ;
addItemToList ( selected_list , it - > msg_id , it - > sig , true ) ;
delete item ;
}
void SeriesSelector : : remove ( QListWidgetItem * item ) {
auto it = ( ListItem * ) item ;
if ( it - > msg_id = = msgs_combo - > currentData ( ) . value < MessageId > ( ) ) {
addItemToList ( available_list , it - > msg_id , it - > sig ) ;
}
delete item ;
}
void SeriesSelector : : updateAvailableList ( int index ) {
if ( index = = - 1 ) return ;
available_list - > clear ( ) ;
MessageId msg_id = msgs_combo - > itemData ( index ) . value < MessageId > ( ) ;
auto selected_items = seletedItems ( ) ;
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 ; } ) ;
if ( ! is_selected ) {
addItemToList ( available_list , msg_id , s ) ;
}
}
}
void SeriesSelector : : addItemToList ( QListWidget * parent , const MessageId id , const cabana : : Signal * sig , bool show_msg_name ) {
QString text = QString ( " <span style= \" color:%0; \" >■ </span> %1 " ) . arg ( getColor ( sig ) . name ( ) , sig - > name ) ;
if ( show_msg_name ) text + = QString ( " <font color= \" gray \" >%0 %1</font> " ) . arg ( msgName ( id ) , id . toString ( ) ) ;
QLabel * label = new QLabel ( text ) ;
label - > setContentsMargins ( 5 , 0 , 5 , 0 ) ;
auto new_item = new ListItem ( id , sig , parent ) ;
new_item - > setSizeHint ( label - > sizeHint ( ) ) ;
parent - > setItemWidget ( new_item , label ) ;
}
QList < SeriesSelector : : ListItem * > SeriesSelector : : seletedItems ( ) {
QList < SeriesSelector : : ListItem * > ret ;
for ( int i = 0 ; i < selected_list - > count ( ) ; + + i ) ret . push_back ( ( ListItem * ) selected_list - > item ( i ) ) ;
return ret ;
}
// ValueTipLabel
ValueTipLabel : : ValueTipLabel ( QWidget * parent ) : QLabel ( parent , Qt : : ToolTip | Qt : : FramelessWindowHint ) {
setForegroundRole ( QPalette : : ToolTipText ) ;
setBackgroundRole ( QPalette : : ToolTipBase ) ;
setPalette ( QToolTip : : palette ( ) ) ;
ensurePolished ( ) ;
setMargin ( 1 + style ( ) - > pixelMetric ( QStyle : : PM_ToolTipLabelFrameWidth , nullptr , this ) ) ;
setAttribute ( Qt : : WA_ShowWithoutActivating ) ;
setTextFormat ( Qt : : RichText ) ;
setVisible ( false ) ;
}
void ValueTipLabel : : showText ( const QPoint & pt , const QString & text , int right_edge ) {
setText ( text ) ;
if ( ! text . isEmpty ( ) ) {
QSize extra ( 1 , 1 ) ;
resize ( sizeHint ( ) + extra ) ;
QPoint tip_pos ( pt . x ( ) + 12 , pt . y ( ) ) ;
if ( tip_pos . x ( ) + size ( ) . width ( ) > = right_edge ) {
tip_pos . rx ( ) = pt . x ( ) - size ( ) . width ( ) - 12 ;
}
move ( tip_pos ) ;
}
setVisible ( ! text . isEmpty ( ) ) ;
}
void ValueTipLabel : : paintEvent ( QPaintEvent * ev ) {
QStylePainter p ( this ) ;
QStyleOptionFrame opt ;
opt . init ( this ) ;
p . drawPrimitive ( QStyle : : PE_PanelTipLabel , opt ) ;
p . end ( ) ;
QLabel : : paintEvent ( ev ) ;
}