@ -12,6 +12,7 @@
# include <QOpenGLWidget>
# include <QOpenGLWidget>
# include <QPushButton>
# include <QPushButton>
# include <QRubberBand>
# include <QRubberBand>
# include <QStylePainter>
# include <QToolBar>
# include <QToolBar>
# include <QToolTip>
# include <QToolTip>
# include <QtConcurrent>
# include <QtConcurrent>
@ -56,10 +57,16 @@ ChartsWidget::ChartsWidget(QWidget *parent) : align_timer(this), QFrame(parent)
range_slider - > setPageStep ( 60 ) ; // 1 min
range_slider - > setPageStep ( 60 ) ; // 1 min
range_slider_action = toolbar - > addWidget ( range_slider ) ;
range_slider_action = toolbar - > addWidget ( range_slider ) ;
undo_zoom_action = toolbar - > addAction ( utils : : icon ( " arrow-counterclockwise " ) , tr ( " Previous zoom " ) ) ;
// zoom controls
qobject_cast < QToolButton * > ( toolbar - > widgetForAction ( undo_zoom_action ) ) - > setToolButtonStyle ( Qt : : ToolButtonTextBesideIcon ) ;
zoom_undo_stack = new QUndoStack ( this ) ;
undo_zoom_action = zoom_undo_stack - > createUndoAction ( this ) ;
reset_zoom_action = toolbar - > addAction ( utils : : icon ( " zoom-out " ) , tr ( " Reset Zoom " ) ) ;
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 ) ;
qobject_cast < QToolButton * > ( toolbar - > widgetForAction ( reset_zoom_action ) ) - > setToolButtonStyle ( Qt : : ToolButtonTextBesideIcon ) ;
remove_all_btn = toolbar - > addAction ( utils : : icon ( " x " ) , tr ( " Remove all charts " ) ) ;
remove_all_btn = toolbar - > addAction ( utils : : icon ( " x " ) , tr ( " Remove all charts " ) ) ;
@ -70,13 +77,13 @@ ChartsWidget::ChartsWidget(QWidget *parent) : align_timer(this), QFrame(parent)
charts_layout = new QGridLayout ( ) ;
charts_layout = new QGridLayout ( ) ;
charts_layout - > setSpacing ( 10 ) ;
charts_layout - > setSpacing ( 10 ) ;
QWidget * charts_container = new QWidget ( this ) ;
charts_container = new QWidget ( this ) ;
QVBoxLayout * charts_main_layout = new QVBoxLayout ( charts_container ) ;
QVBoxLayout * charts_main_layout = new QVBoxLayout ( charts_container ) ;
charts_main_layout - > setContentsMargins ( 0 , 0 , 0 , 0 ) ;
charts_main_layout - > setContentsMargins ( 0 , 0 , 0 , 0 ) ;
charts_main_layout - > addLayout ( charts_layout ) ;
charts_main_layout - > addLayout ( charts_layout ) ;
charts_main_layout - > addStretch ( 0 ) ;
charts_main_layout - > addStretch ( 0 ) ;
QScrollArea * charts_scroll = new QScrollArea ( this ) ;
charts_scroll = new QScrollArea ( this ) ;
charts_scroll - > setFrameStyle ( QFrame : : NoFrame ) ;
charts_scroll - > setFrameStyle ( QFrame : : NoFrame ) ;
charts_scroll - > setWidgetResizable ( true ) ;
charts_scroll - > setWidgetResizable ( true ) ;
charts_scroll - > setWidget ( charts_container ) ;
charts_scroll - > setWidget ( charts_container ) ;
@ -84,8 +91,6 @@ ChartsWidget::ChartsWidget(QWidget *parent) : align_timer(this), QFrame(parent)
main_layout - > addWidget ( charts_scroll ) ;
main_layout - > addWidget ( charts_scroll ) ;
// init settings
// init settings
use_dark_theme = QApplication : : palette ( ) . color ( QPalette : : WindowText ) . value ( ) >
QApplication : : palette ( ) . color ( QPalette : : Background ) . value ( ) ;
column_count = std : : clamp ( settings . chart_column_count , 1 , MAX_COLUMN_COUNT ) ;
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 ) ;
max_chart_range = std : : clamp ( settings . chart_range , 1 , settings . max_cached_minutes * 60 ) ;
display_range = { 0 , max_chart_range } ;
display_range = { 0 , max_chart_range } ;
@ -100,7 +105,6 @@ ChartsWidget::ChartsWidget(QWidget *parent) : align_timer(this), QFrame(parent)
QObject : : connect ( range_slider , & QSlider : : valueChanged , this , & ChartsWidget : : setMaxChartRange ) ;
QObject : : connect ( range_slider , & QSlider : : valueChanged , this , & ChartsWidget : : setMaxChartRange ) ;
QObject : : connect ( new_plot_btn , & QAction : : triggered , this , & ChartsWidget : : newChart ) ;
QObject : : connect ( new_plot_btn , & QAction : : triggered , this , & ChartsWidget : : newChart ) ;
QObject : : connect ( remove_all_btn , & QAction : : triggered , this , & ChartsWidget : : removeAll ) ;
QObject : : connect ( remove_all_btn , & QAction : : triggered , this , & ChartsWidget : : removeAll ) ;
QObject : : connect ( undo_zoom_action , & QAction : : triggered , this , & ChartsWidget : : zoomUndo ) ;
QObject : : connect ( reset_zoom_action , & QAction : : triggered , this , & ChartsWidget : : zoomReset ) ;
QObject : : connect ( reset_zoom_action , & QAction : : triggered , this , & ChartsWidget : : zoomReset ) ;
QObject : : connect ( & settings , & Settings : : changed , this , & ChartsWidget : : settingChanged ) ;
QObject : : connect ( & settings , & Settings : : changed , this , & ChartsWidget : : settingChanged ) ;
QObject : : connect ( dock_btn , & QAction : : triggered , [ this ] ( ) {
QObject : : connect ( dock_btn , & QAction : : triggered , [ this ] ( ) {
@ -116,14 +120,9 @@ ChartsWidget::ChartsWidget(QWidget *parent) : align_timer(this), QFrame(parent)
}
}
void ChartsWidget : : eventsMerged ( ) {
void ChartsWidget : : eventsMerged ( ) {
{
QFutureSynchronizer < void > future_synchronizer ;
QFutureSynchronizer < void > future_synchronizer ;
for ( auto c : charts ) {
for ( auto c : charts ) {
future_synchronizer . addFuture ( QtConcurrent : : run ( c , & ChartView : : updateSeries , nullptr ) ) ;
future_synchronizer . addFuture ( QtConcurrent : : run ( c , & ChartView : : updateSeries , nullptr ) ) ;
}
}
if ( can - > isPaused ( ) ) {
updateState ( ) ;
}
}
}
}
@ -136,24 +135,22 @@ void ChartsWidget::setZoom(double min, double max) {
}
}
void ChartsWidget : : zoomIn ( double min , double max ) {
void ChartsWidget : : zoomIn ( double min , double max ) {
// Save previous zoom on undo stack
zoom_undo_stack - > push ( new ZoomCommand ( this , { min , max } ) ) ;
if ( is_zoomed ) {
zoom_stack . push ( { zoomed_range . first , zoomed_range . second } ) ;
}
setZoom ( min , max ) ;
}
}
void ChartsWidget : : zoomReset ( ) {
void ChartsWidget : : zoomReset ( ) {
setZoom ( display_range . first , display_range . second ) ;
setZoom ( display_range . first , display_range . second ) ;
zoom_stack . clear ( ) ;
zoom_undo_stack - > clear ( ) ;
}
}
void ChartsWidget : : zoomUndo ( ) {
void ChartsWidget : : showValueTip ( double sec ) {
if ( ! zoom_stack . isEmpty ( ) ) {
const QRect visible_rect ( - charts_container - > pos ( ) , charts_scroll - > viewport ( ) - > size ( ) ) ;
auto r = zoom_stack . pop ( ) ;
for ( auto c : charts ) {
setZoom ( r . first , r . second ) ;
if ( sec > = 0 & & visible_rect . contains ( QRect ( c - > mapTo ( charts_container , QPoint ( 0 , 0 ) ) , c - > size ( ) ) ) ) {
} else {
c - > showTip ( sec ) ;
zoomReset ( ) ;
} else {
c - > hideTip ( ) ;
}
}
}
}
}
@ -189,12 +186,13 @@ void ChartsWidget::setMaxChartRange(int value) {
void ChartsWidget : : updateToolBar ( ) {
void ChartsWidget : : updateToolBar ( ) {
title_label - > setText ( tr ( " Charts: %1 " ) . arg ( charts . size ( ) ) ) ;
title_label - > setText ( tr ( " Charts: %1 " ) . arg ( charts . size ( ) ) ) ;
columns_action - > setText ( tr ( " Column: %1 " ) . arg ( column_count ) ) ;
columns_action - > setText ( tr ( " Column: %1 " ) . arg ( column_count ) ) ;
range_lb - > setText ( QString ( " Range: %1 " ) . arg ( utils : : formatSeconds ( max_chart_range ) ) ) ;
range_lb - > setText ( utils : : formatSeconds ( max_chart_range ) ) ;
range_lb_action - > setVisible ( ! is_zoomed ) ;
range_lb_action - > setVisible ( ! is_zoomed ) ;
range_slider_action - > setVisible ( ! is_zoomed ) ;
range_slider_action - > setVisible ( ! is_zoomed ) ;
undo_zoom_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 - > setVisible ( is_zoomed ) ;
reset_zoom_action - > setText ( is_zoomed ? tr ( " Zoomin: %1-%2" ) . arg ( zoomed_range . first , 0 , ' f ' , 1 ) . arg ( zoomed_range . second , 0 , ' f ' , 1 ) : " " ) ;
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 ( ) ) ;
remove_all_btn - > setEnabled ( ! charts . isEmpty ( ) ) ;
dock_btn - > setIcon ( utils : : icon ( docking ? " arrow-up-right-square " : " arrow-down-left-square " ) ) ;
dock_btn - > setIcon ( utils : : icon ( docking ? " arrow-up-right-square " : " arrow-down-left-square " ) ) ;
dock_btn - > setToolTip ( docking ? tr ( " Undock charts " ) : tr ( " Dock charts " ) ) ;
dock_btn - > setToolTip ( docking ? tr ( " Undock charts " ) : tr ( " Dock charts " ) ) ;
@ -215,19 +213,21 @@ ChartView *ChartsWidget::findChart(const MessageId &id, const cabana::Signal *si
}
}
ChartView * ChartsWidget : : createChart ( ) {
ChartView * ChartsWidget : : createChart ( ) {
auto chart = new ChartView ( this ) ;
auto chart = new ChartView ( is_zoomed ? zoomed_range : display_range , this ) ;
chart - > setFixedHeight ( settings . chart_height ) ;
chart - > setFixedHeight ( settings . chart_height ) ;
chart - > setMinimumWidth ( CHART_MIN_WIDTH ) ;
chart - > setMinimumWidth ( CHART_MIN_WIDTH ) ;
chart - > setSizePolicy ( QSizePolicy : : MinimumExpanding , QSizePolicy : : Fixed ) ;
chart - > setSizePolicy ( QSizePolicy : : MinimumExpanding , QSizePolicy : : Fixed ) ;
chart - > chart ( ) - > setTheme ( use_dark_theme ? QChart : : QChart : : ChartThemeDark : QChart : : ChartThemeLight ) ;
chart - > chart ( ) - > setTheme ( settings . theme = = 2 ? QChart : : QChart : : ChartThemeDark : QChart : : ChartThemeLight ) ;
QObject : : connect ( chart , & ChartView : : remove , [ = ] ( ) { removeChart ( chart ) ; } ) ;
QObject : : connect ( chart , & ChartView : : remove , [ = ] ( ) { removeChart ( chart ) ; } ) ;
QObject : : connect ( chart , & ChartView : : zoomIn , this , & ChartsWidget : : zoomIn ) ;
QObject : : connect ( chart , & ChartView : : zoomIn , this , & ChartsWidget : : zoomIn ) ;
QObject : : connect ( chart , & ChartView : : zoomUndo , this , & ChartsWidget : : zoomUndo ) ;
QObject : : connect ( chart , & ChartView : : zoomUndo , undo_zoom_action , & QAction : : trigger ) ;
QObject : : connect ( chart , & ChartView : : seriesRemoved , this , & ChartsWidget : : seriesChanged ) ;
QObject : : connect ( chart , & ChartView : : seriesRemoved , this , & ChartsWidget : : seriesChanged ) ;
QObject : : connect ( chart , & ChartView : : seriesAdded , 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 : : axisYLabelWidthChanged , & align_timer , qOverload < > ( & QTimer : : start ) ) ;
QObject : : connect ( chart , & ChartView : : hovered , this , & ChartsWidget : : showValueTip ) ;
charts . push_back ( chart ) ;
charts . push_back ( chart ) ;
updateLayout ( ) ;
updateLayout ( ) ;
updateToolBar ( ) ;
return chart ;
return chart ;
}
}
@ -236,11 +236,9 @@ void ChartsWidget::showChart(const MessageId &id, const cabana::Signal *sig, boo
if ( show & & ! chart ) {
if ( show & & ! chart ) {
chart = merge & & charts . size ( ) > 0 ? charts . back ( ) : createChart ( ) ;
chart = merge & & charts . size ( ) > 0 ? charts . back ( ) : createChart ( ) ;
chart - > addSeries ( id , sig ) ;
chart - > addSeries ( id , sig ) ;
updateState ( ) ;
} else if ( ! show & & chart ) {
} else if ( ! show & & chart ) {
chart - > removeIf ( [ & ] ( auto & s ) { return s . msg_id = = id & & s . sig = = sig ; } ) ;
chart - > removeIf ( [ & ] ( auto & s ) { return s . msg_id = = id & & s . sig = = sig ; } ) ;
}
}
updateToolBar ( ) ;
}
}
void ChartsWidget : : setColumnCount ( int n ) {
void ChartsWidget : : setColumnCount ( int n ) {
@ -294,18 +292,20 @@ void ChartsWidget::removeChart(ChartView *chart) {
charts . removeOne ( chart ) ;
charts . removeOne ( chart ) ;
chart - > deleteLater ( ) ;
chart - > deleteLater ( ) ;
updateToolBar ( ) ;
updateToolBar ( ) ;
alignCharts ( ) ;
updateLayout ( ) ;
updateLayout ( ) ;
alignCharts ( ) ;
emit seriesChanged ( ) ;
emit seriesChanged ( ) ;
}
}
void ChartsWidget : : removeAll ( ) {
void ChartsWidget : : removeAll ( ) {
for ( auto c : charts ) {
if ( ! charts . isEmpty ( ) ) {
c - > deleteLater ( ) ;
for ( auto c : charts ) {
c - > deleteLater ( ) ;
}
charts . clear ( ) ;
updateToolBar ( ) ;
emit seriesChanged ( ) ;
}
}
charts . clear ( ) ;
updateToolBar ( ) ;
emit seriesChanged ( ) ;
}
}
void ChartsWidget : : alignCharts ( ) {
void ChartsWidget : : alignCharts ( ) {
@ -329,16 +329,31 @@ bool ChartsWidget::eventFilter(QObject *obj, QEvent *event) {
bool ChartsWidget : : event ( QEvent * event ) {
bool ChartsWidget : : event ( QEvent * event ) {
bool back_button = false ;
bool back_button = false ;
if ( event - > type ( ) = = QEvent : : MouseButtonPress ) {
switch ( event - > type ( ) ) {
QMouseEvent * ev = static_cast < QMouseEvent * > ( event ) ;
case QEvent : : MouseButtonPress : {
back_button = ev - > button ( ) = = Qt : : BackButton ;
QMouseEvent * ev = static_cast < QMouseEvent * > ( event ) ;
} else if ( event - > type ( ) = = QEvent : : NativeGesture ) { // MacOS emulates a back swipe on pressing the mouse back button
back_button = ev - > button ( ) = = Qt : : BackButton ;
QNativeGestureEvent * ev = static_cast < QNativeGestureEvent * > ( event ) ;
break ;
back_button = ( ev - > value ( ) = = 180 ) ;
}
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 ) {
if ( back_button ) {
zoomUndo ( ) ;
emit undo_zoom_action - > triggered ( ) ;
return true ;
return true ;
}
}
return QFrame : : event ( event ) ;
return QFrame : : event ( event ) ;
@ -346,7 +361,7 @@ bool ChartsWidget::event(QEvent *event) {
// ChartView
// ChartView
ChartView : : ChartView ( QWidget * parent ) : QChartView ( nullptr , parent ) {
ChartView : : ChartView ( const std : : pair < double , double > & x_range , QWidget * parent ) : tip_label ( this ) , QChartView ( nullptr , parent ) {
series_type = ( SeriesType ) settings . chart_series_type ;
series_type = ( SeriesType ) settings . chart_series_type ;
QChart * chart = new QChart ( ) ;
QChart * chart = new QChart ( ) ;
chart - > setBackgroundVisible ( false ) ;
chart - > setBackgroundVisible ( false ) ;
@ -358,19 +373,17 @@ ChartView::ChartView(QWidget *parent) : QChartView(nullptr, parent) {
chart - > legend ( ) - > setShowToolTips ( true ) ;
chart - > legend ( ) - > setShowToolTips ( true ) ;
chart - > setMargins ( { 0 , 0 , 0 , 0 } ) ;
chart - > setMargins ( { 0 , 0 , 0 , 0 } ) ;
background = new QGraphicsRectItem ( chart ) ;
axis_x - > setRange ( x_range . first , x_range . second ) ;
background - > setBrush ( QApplication : : palette ( ) . color ( QPalette : : Base ) ) ;
background - > setPen ( Qt : : NoPen ) ;
background - > setZValue ( chart - > zValue ( ) - 1 ) ;
setChart ( chart ) ;
setChart ( chart ) ;
createToolButtons ( ) ;
createToolButtons ( ) ;
setRenderHint ( QPainter : : Antialiasing ) ;
// TODO: enable zoomIn/seekTo in live streaming mode.
// TODO: enable zoomIn/seekTo in live streaming mode.
setRubberBand ( can - > liveStreaming ( ) ? QChartView : : NoRubberBand : QChartView : : HorizontalRubberBand ) ;
setRubberBand ( can - > liveStreaming ( ) ? QChartView : : NoRubberBand : QChartView : : HorizontalRubberBand ) ;
setMouseTracking ( true ) ;
setMouseTracking ( true ) ;
QObject : : connect ( axis_y , & QValueAxis : : rangeChanged , [ this ] ( ) { resetChartCache ( ) ; } ) ;
QObject : : connect ( axis_y , & QAbstractAxis : : titleTextChanged , [ this ] ( ) { resetChartCache ( ) ; } ) ;
QObject : : connect ( dbc ( ) , & DBCManager : : signalRemoved , this , & ChartView : : signalRemoved ) ;
QObject : : connect ( dbc ( ) , & DBCManager : : signalRemoved , this , & ChartView : : signalRemoved ) ;
QObject : : connect ( dbc ( ) , & DBCManager : : signalUpdated , this , & ChartView : : signalUpdated ) ;
QObject : : connect ( dbc ( ) , & DBCManager : : signalUpdated , this , & ChartView : : signalUpdated ) ;
QObject : : connect ( dbc ( ) , & DBCManager : : msgRemoved , this , & ChartView : : msgRemoved ) ;
QObject : : connect ( dbc ( ) , & DBCManager : : msgRemoved , this , & ChartView : : msgRemoved ) ;
@ -447,6 +460,7 @@ void ChartView::removeIf(std::function<bool(const SigItem &s)> predicate) {
emit remove ( ) ;
emit remove ( ) ;
} else if ( sigs . size ( ) ! = prev_size ) {
} else if ( sigs . size ( ) ! = prev_size ) {
updateAxisY ( ) ;
updateAxisY ( ) ;
resetChartCache ( ) ;
}
}
}
}
@ -488,15 +502,14 @@ void ChartView::resizeEvent(QResizeEvent *event) {
manage_btn_proxy - > setPos ( x , top ) ;
manage_btn_proxy - > setPos ( x , top ) ;
chart ( ) - > legend ( ) - > setGeometry ( { move_icon - > sceneBoundingRect ( ) . topRight ( ) , manage_btn_proxy - > sceneBoundingRect ( ) . bottomLeft ( ) } ) ;
chart ( ) - > legend ( ) - > setGeometry ( { move_icon - > sceneBoundingRect ( ) . topRight ( ) , manage_btn_proxy - > sceneBoundingRect ( ) . bottomLeft ( ) } ) ;
if ( align_to > 0 ) {
if ( align_to > 0 ) {
updatePlotArea ( align_to ) ;
updatePlotArea ( align_to , true ) ;
}
}
QChartView : : resizeEvent ( event ) ;
QChartView : : resizeEvent ( event ) ;
}
}
void ChartView : : updatePlotArea ( int left_pos ) {
void ChartView : : updatePlotArea ( int left_pos , bool force ) {
if ( align_to ! = left_pos | | rect ( ) ! = background - > rect ( ) ) {
if ( align_to ! = left_pos | | force ) {
align_to = left_pos ;
align_to = left_pos ;
background - > setRect ( rect ( ) ) ;
qreal left , top , right , bottom ;
qreal left , top , right , bottom ;
chart ( ) - > layout ( ) - > getContentsMargins ( & left , & top , & right , & bottom ) ;
chart ( ) - > layout ( ) - > getContentsMargins ( & left , & top , & right , & bottom ) ;
@ -505,9 +518,7 @@ void ChartView::updatePlotArea(int left_pos) {
int adjust_top = chart ( ) - > legend ( ) - > geometry ( ) . height ( ) + style ( ) - > pixelMetric ( QStyle : : PM_LayoutTopMargin ) ;
int adjust_top = chart ( ) - > legend ( ) - > geometry ( ) . height ( ) + style ( ) - > pixelMetric ( QStyle : : PM_LayoutTopMargin ) ;
chart ( ) - > setPlotArea ( rect ( ) . adjusted ( align_to + left , adjust_top + top , - x_label_size . width ( ) / 2 - right , - x_label_size . height ( ) - bottom ) ) ;
chart ( ) - > setPlotArea ( rect ( ) . adjusted ( align_to + left , adjust_top + top , - x_label_size . width ( ) / 2 - right , - x_label_size . height ( ) - bottom ) ) ;
chart ( ) - > layout ( ) - > invalidate ( ) ;
chart ( ) - > layout ( ) - > invalidate ( ) ;
if ( can - > isPaused ( ) ) {
resetChartCache ( ) ;
update ( ) ;
}
}
}
}
}
@ -519,6 +530,7 @@ void ChartView::updateTitle() {
auto decoration = s . series - > isVisible ( ) ? " none " : " line-through " ;
auto decoration = s . series - > isVisible ( ) ? " none " : " line-through " ;
s . series - > setName ( QString ( " <span style= \" text-decoration:%1 \" ><b>%2</b> <font color= \" gray \" >%3 %4</font></span> " ) . arg ( decoration , s . sig - > name , msgName ( s . msg_id ) , s . msg_id . toString ( ) ) ) ;
s . series - > setName ( QString ( " <span style= \" text-decoration:%1 \" ><b>%2</b> <font color= \" gray \" >%3 %4</font></span> " ) . arg ( decoration , s . sig - > name , msgName ( s . msg_id ) , s . msg_id . toString ( ) ) ) ;
}
}
resetChartCache ( ) ;
}
}
void ChartView : : updatePlot ( double cur , double min , double max ) {
void ChartView : : updatePlot ( double cur , double min , double max ) {
@ -527,8 +539,13 @@ void ChartView::updatePlot(double cur, double min, double max) {
axis_x - > setRange ( min , max ) ;
axis_x - > setRange ( min , max ) ;
updateAxisY ( ) ;
updateAxisY ( ) ;
updateSeriesPoints ( ) ;
updateSeriesPoints ( ) ;
// update tooltip
if ( tooltip_x > = 0 ) {
showTip ( chart ( ) - > mapToValue ( { tooltip_x , 0 } ) . x ( ) ) ;
}
resetChartCache ( ) ;
}
}
scene ( ) - > invalidate ( { } , QGraphicsScene : : ForegroundLayer ) ;
viewport ( ) - > update ( ) ;
}
}
void ChartView : : updateSeriesPoints ( ) {
void ChartView : : updateSeriesPoints ( ) {
@ -542,7 +559,11 @@ void ChartView::updateSeriesPoints() {
double pixels_per_point = ( chart ( ) - > mapToPosition ( right_pt ) . x ( ) - chart ( ) - > mapToPosition ( * begin ) . x ( ) ) / num_points ;
double pixels_per_point = ( chart ( ) - > mapToPosition ( right_pt ) . x ( ) - chart ( ) - > mapToPosition ( * begin ) . x ( ) ) / num_points ;
if ( series_type = = SeriesType : : Scatter ) {
if ( series_type = = SeriesType : : Scatter ) {
( ( QScatterSeries * ) s . series ) - > setMarkerSize ( std : : clamp ( pixels_per_point / 2.0 , 2.0 , 8.0 ) * devicePixelRatioF ( ) ) ;
qreal size = std : : clamp ( pixels_per_point / 2.0 , 2.0 , 8.0 ) ;
if ( s . series - > useOpenGL ( ) ) {
size * = devicePixelRatioF ( ) ;
}
( ( QScatterSeries * ) s . series ) - > setMarkerSize ( size ) ;
} else {
} else {
s . series - > setPointsVisible ( pixels_per_point > 20 ) ;
s . series - > setPointsVisible ( pixels_per_point > 20 ) ;
}
}
@ -586,6 +607,7 @@ void ChartView::updateSeries(const cabana::Signal *sig) {
}
}
}
}
updateAxisY ( ) ;
updateAxisY ( ) ;
chart_pixmap = QPixmap ( ) ;
}
}
// auto zoom on yaxis
// auto zoom on yaxis
@ -606,16 +628,20 @@ void ChartView::updateAxisY() {
auto first = std : : lower_bound ( s . vals . begin ( ) , s . vals . end ( ) , axis_x - > min ( ) , xLessThan ) ;
auto first = std : : lower_bound ( s . vals . begin ( ) , s . vals . end ( ) , axis_x - > min ( ) , xLessThan ) ;
auto last = std : : lower_bound ( first , s . vals . end ( ) , axis_x - > max ( ) , xLessThan ) ;
auto last = std : : lower_bound ( first , s . vals . end ( ) , axis_x - > max ( ) , xLessThan ) ;
s . min = std : : numeric_limits < double > : : max ( ) ;
s . max = std : : numeric_limits < double > : : lowest ( ) ;
if ( can - > liveStreaming ( ) ) {
if ( can - > liveStreaming ( ) ) {
for ( auto it = first ; it ! = last ; + + it ) {
for ( auto it = first ; it ! = last ; + + it ) {
if ( it - > y ( ) < min ) min = it - > y ( ) ;
if ( it - > y ( ) < s . min ) s . min = it - > y ( ) ;
if ( it - > y ( ) > max ) max = it - > y ( ) ;
if ( it - > y ( ) > s . max ) s . max = it - > y ( ) ;
}
}
} else {
} else {
auto [ min_y , max_y ] = s . segment_tree . minmax ( std : : distance ( s . vals . begin ( ) , first ) , std : : distance ( s . vals . begin ( ) , last ) ) ;
auto [ min_y , max_y ] = s . segment_tree . minmax ( std : : distance ( s . vals . begin ( ) , first ) , std : : distance ( s . vals . begin ( ) , last ) ) ;
min = std : : min ( min , min_y ) ;
s . min = min_y ;
max = std : : max ( max , max_y ) ;
s . max = max_y ;
}
}
min = std : : min ( min , s . min ) ;
max = std : : max ( max , s . max ) ;
}
}
if ( min = = std : : numeric_limits < double > : : max ( ) ) min = 0 ;
if ( min = = std : : numeric_limits < double > : : max ( ) ) min = 0 ;
if ( max = = std : : numeric_limits < double > : : lowest ( ) ) max = 0 ;
if ( max = = std : : numeric_limits < double > : : lowest ( ) ) max = 0 ;
@ -668,8 +694,9 @@ qreal ChartView::niceNumber(qreal x, bool ceiling) {
}
}
void ChartView : : leaveEvent ( QEvent * event ) {
void ChartView : : leaveEvent ( QEvent * event ) {
clearTrackPoints ( ) ;
if ( tip_label . isVisible ( ) ) {
scene ( ) - > update ( ) ;
emit hovered ( - 1 ) ;
}
QChartView : : leaveEvent ( event ) ;
QChartView : : leaveEvent ( event ) ;
}
}
@ -716,7 +743,7 @@ void ChartView::mouseReleaseEvent(QMouseEvent *event) {
} else if ( rubber - > width ( ) > 10 ) {
} else if ( rubber - > width ( ) > 10 ) {
emit zoomIn ( min_rounded , max_rounded ) ;
emit zoomIn ( min_rounded , max_rounded ) ;
} else {
} else {
scene ( ) - > invalidate ( { } , QGraphicsScene : : ForegroundLayer ) ;
viewport ( ) - > update ( ) ;
}
}
event - > accept ( ) ;
event - > accept ( ) ;
} else if ( ! can - > liveStreaming ( ) & & event - > button ( ) = = Qt : : RightButton ) {
} else if ( ! can - > liveStreaming ( ) & & event - > button ( ) = = Qt : : RightButton ) {
@ -741,42 +768,17 @@ void ChartView::mouseMoveEvent(QMouseEvent *ev) {
if ( plot_area . contains ( ev - > pos ( ) ) ) {
if ( plot_area . contains ( ev - > pos ( ) ) ) {
can - > seekTo ( std : : clamp ( chart ( ) - > mapToValue ( ev - > pos ( ) ) . x ( ) , 0. , can - > totalSeconds ( ) ) ) ;
can - > seekTo ( std : : clamp ( chart ( ) - > mapToValue ( ev - > pos ( ) ) . x ( ) , 0. , can - > totalSeconds ( ) ) ) ;
}
}
return ;
}
}
auto rubber = findChild < QRubberBand * > ( ) ;
auto rubber = findChild < QRubberBand * > ( ) ;
bool is_zooming = rubber & & rubber - > isVisible ( ) ;
bool is_zooming = rubber & & rubber - > isVisible ( ) ;
is_scrubbing = false ;
clearTrackPoints ( ) ;
clearTrackPoints ( ) ;
if ( ! is_zooming & & plot_area . contains ( ev - > pos ( ) ) ) {
if ( ! is_zooming & & plot_area . contains ( ev - > pos ( ) ) ) {
QStringList text_list ;
const double sec = chart ( ) - > mapToValue ( ev - > pos ( ) ) . x ( ) ;
const double sec = chart ( ) - > mapToValue ( ev - > pos ( ) ) . x ( ) ;
qreal x = - 1 ;
emit hovered ( sec ) ;
for ( auto & s : sigs ) {
} else if ( tip_label . isVisible ( ) ) {
if ( ! s . series - > isVisible ( ) ) continue ;
emit hovered ( - 1 ) ;
// use reverse iterator to find last item <= sec.
double value = 0 ;
auto it = std : : lower_bound ( s . vals . rbegin ( ) , s . vals . rend ( ) , sec , [ ] ( auto & p , double x ) { return p . x ( ) > x ; } ) ;
if ( it ! = s . vals . rend ( ) & & it - > x ( ) > = axis_x - > min ( ) ) {
value = it - > y ( ) ;
s . track_pt = chart ( ) - > mapToPosition ( * it ) ;
x = std : : max ( x , s . track_pt . x ( ) ) ;
}
text_list . push_back ( QString ( " <span style= \" color:%1; \" >■ </span>%2: <b>%3</b> " )
. arg ( s . series - > color ( ) . name ( ) , s . sig - > name ,
s . track_pt . isNull ( ) ? " -- " : QString : : number ( value ) ) ) ;
}
if ( x < 0 ) {
x = ev - > pos ( ) . x ( ) ;
}
text_list . push_front ( QString : : number ( chart ( ) - > mapToValue ( { x , 0 } ) . x ( ) , ' f ' , 3 ) ) ;
QPointF tooltip_pt ( x + 12 , plot_area . top ( ) - 20 ) ;
QToolTip : : showText ( mapToGlobal ( tooltip_pt . toPoint ( ) ) , text_list . join ( " <br /> " ) , this , plot_area . toRect ( ) ) ;
scene ( ) - > invalidate ( { } , QGraphicsScene : : ForegroundLayer ) ;
} else {
QToolTip : : hideText ( ) ;
}
}
QChartView : : mouseMoveEvent ( ev ) ;
QChartView : : mouseMoveEvent ( ev ) ;
@ -787,8 +789,42 @@ void ChartView::mouseMoveEvent(QMouseEvent *ev) {
if ( rubber_rect ! = rubber - > geometry ( ) ) {
if ( rubber_rect ! = rubber - > geometry ( ) ) {
rubber - > setGeometry ( rubber_rect ) ;
rubber - > setGeometry ( rubber_rect ) ;
}
}
scene ( ) - > invalidate ( { } , QGraphicsScene : : ForegroundLayer ) ;
viewport ( ) - > update ( ) ;
}
}
void ChartView : : showTip ( double sec ) {
tooltip_x = chart ( ) - > mapToPosition ( { sec , 0 } ) . x ( ) ;
qreal x = tooltip_x ;
QStringList text_list ( QString : : number ( chart ( ) - > mapToValue ( { x , 0 } ) . x ( ) , ' f ' , 3 ) ) ;
for ( auto & s : sigs ) {
if ( s . series - > isVisible ( ) ) {
QString value = " -- " ;
// use reverse iterator to find last item <= sec.
auto it = std : : lower_bound ( s . vals . rbegin ( ) , s . vals . rend ( ) , sec , [ ] ( auto & p , double x ) { return p . x ( ) > x ; } ) ;
if ( it ! = s . vals . rend ( ) & & it - > x ( ) > = axis_x - > min ( ) ) {
value = QString : : number ( it - > y ( ) ) ;
s . track_pt = * it ;
x = std : : max ( x , chart ( ) - > mapToPosition ( * it ) . x ( ) ) ;
}
QString name = sigs . size ( ) > 1 ? s . sig - > name + " : " : " " ;
QString min = s . min = = std : : numeric_limits < double > : : max ( ) ? " -- " : QString : : number ( s . min ) ;
QString max = s . max = = std : : numeric_limits < double > : : lowest ( ) ? " -- " : QString : : number ( s . max ) ;
text_list < < QString ( " <span style= \" color:%1; \" >■ </span>%2<b>%3</b> (%4, %5) " )
. arg ( s . series - > color ( ) . name ( ) , name , value , min , max ) ;
}
}
}
QPointF tooltip_pt ( x , chart ( ) - > plotArea ( ) . top ( ) ) ;
int plot_right = mapToGlobal ( chart ( ) - > plotArea ( ) . topRight ( ) . toPoint ( ) ) . x ( ) ;
tip_label . showText ( mapToGlobal ( tooltip_pt . toPoint ( ) ) , " <p style='white-space:pre'> " + text_list . join ( " <br /> " ) + " </p> " , plot_right ) ;
viewport ( ) - > update ( ) ;
}
void ChartView : : hideTip ( ) {
clearTrackPoints ( ) ;
tooltip_x = - 1 ;
tip_label . hide ( ) ;
viewport ( ) - > update ( ) ;
}
}
void ChartView : : dragMoveEvent ( QDragMoveEvent * event ) {
void ChartView : : dragMoveEvent ( QDragMoveEvent * event ) {
@ -818,6 +854,38 @@ void ChartView::dropEvent(QDropEvent *event) {
}
}
}
}
void ChartView : : resetChartCache ( ) {
chart_pixmap = QPixmap ( ) ;
viewport ( ) - > update ( ) ;
}
void ChartView : : paintEvent ( QPaintEvent * event ) {
if ( ! can - > liveStreaming ( ) ) {
if ( chart_pixmap . isNull ( ) ) {
const qreal dpr = viewport ( ) - > devicePixelRatioF ( ) ;
chart_pixmap = QPixmap ( viewport ( ) - > size ( ) * dpr ) ;
chart_pixmap . setDevicePixelRatio ( dpr ) ;
QPainter p ( & chart_pixmap ) ;
p . setRenderHints ( QPainter : : Antialiasing ) ;
drawBackground ( & p , viewport ( ) - > rect ( ) ) ;
scene ( ) - > setSceneRect ( viewport ( ) - > rect ( ) ) ;
scene ( ) - > render ( & p ) ;
}
QPainter painter ( viewport ( ) ) ;
painter . setRenderHints ( QPainter : : Antialiasing ) ;
painter . drawPixmap ( QPoint ( ) , chart_pixmap ) ;
QRectF exposed_rect = mapToScene ( event - > region ( ) . boundingRect ( ) ) . boundingRect ( ) ;
drawForeground ( & painter , exposed_rect ) ;
} else {
QChartView : : paintEvent ( event ) ;
}
}
void ChartView : : drawBackground ( QPainter * painter , const QRectF & rect ) {
painter - > fillRect ( rect , palette ( ) . color ( QPalette : : Base ) ) ;
}
void ChartView : : drawForeground ( QPainter * painter , const QRectF & rect ) {
void ChartView : : drawForeground ( QPainter * painter , const QRectF & rect ) {
// draw time line
// draw time line
qreal x = chart ( ) - > mapToPosition ( QPointF { cur_sec , 0 } ) . x ( ) ;
qreal x = chart ( ) - > mapToPosition ( QPointF { cur_sec , 0 } ) . x ( ) ;
@ -833,8 +901,9 @@ void ChartView::drawForeground(QPainter *painter, const QRectF &rect) {
for ( auto & s : sigs ) {
for ( auto & s : sigs ) {
if ( ! s . track_pt . isNull ( ) & & s . series - > isVisible ( ) ) {
if ( ! s . track_pt . isNull ( ) & & s . series - > isVisible ( ) ) {
painter - > setBrush ( s . series - > color ( ) . darker ( 125 ) ) ;
painter - > setBrush ( s . series - > color ( ) . darker ( 125 ) ) ;
painter - > drawEllipse ( s . track_pt , 5.5 , 5.5 ) ;
QPointF pos = chart ( ) - > mapToPosition ( s . track_pt ) ;
track_line_x = std : : max ( track_line_x , s . track_pt . x ( ) ) ;
painter - > drawEllipse ( pos , 5.5 , 5.5 ) ;
track_line_x = std : : max ( track_line_x , pos . x ( ) ) ;
}
}
}
}
if ( track_line_x > 0 ) {
if ( track_line_x > 0 ) {
@ -1033,3 +1102,39 @@ QList<SeriesSelector::ListItem *> SeriesSelector::seletedItems() {
for ( int i = 0 ; i < selected_list - > count ( ) ; + + i ) ret . push_back ( ( ListItem * ) selected_list - > item ( i ) ) ;
for ( int i = 0 ; i < selected_list - > count ( ) ; + + i ) ret . push_back ( ( ListItem * ) selected_list - > item ( i ) ) ;
return ret ;
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 ) ;
}