Added a new window - Temporal Graph which shows all frame IDs and the

time stamp when they came in. Shows the temporal relationship between
all frames.
This commit is contained in:
Collin Kidder
2019-05-05 11:52:32 -04:00
parent c363059951
commit 164d5adadb
7 changed files with 494 additions and 3 deletions
+6 -3
View File
@@ -67,7 +67,8 @@ SOURCES += main.cpp\
helpwindow.cpp \
blfhandler.cpp \
re/sniffer/SnifferDelegate.cpp \
connections/newconnectiondialog.cpp
connections/newconnectiondialog.cpp \
re/temporalgraphwindow.cpp
HEADERS += mainwindow.h \
can_structs.h \
@@ -125,7 +126,8 @@ HEADERS += mainwindow.h \
helpwindow.h \
blfhandler.h \
re/sniffer/SnifferDelegate.h \
connections/newconnectiondialog.h
connections/newconnectiondialog.h \
re/temporalgraphwindow.h
FORMS += ui/candatagrid.ui \
ui/connectionwindow.ui \
@@ -153,7 +155,8 @@ FORMS += ui/candatagrid.ui \
ui/bisectwindow.ui \
ui/signalviewerwindow.ui \
helpwindow.ui \
ui/newconnectiondialog.ui
ui/newconnectiondialog.ui \
ui/temporalgraphwindow.ui
DISTFILES +=
+16
View File
@@ -83,6 +83,7 @@ MainWindow::MainWindow(QWidget *parent) :
snifferWindow = nullptr;
bisectWindow = nullptr;
signalViewerWindow = nullptr;
temporalGraphWindow = nullptr;
dbcHandler = DBCHandler::getReference();
bDirty = false;
inhibitFilterUpdate = false;
@@ -131,6 +132,7 @@ MainWindow::MainWindow(QWidget *parent) :
connect(ui->actionCapture_Bisector, &QAction::triggered, this, &MainWindow::showBisectWindow);
connect(ui->actionSignal_Viewer, &QAction::triggered, this, &MainWindow::showSignalViewer);
connect(ui->actionSave_Continuous_Logfile, &QAction::triggered, this, &MainWindow::handleContinousLogging);
connect(ui->actionTemporal_Graph, &QAction::triggered, this, &MainWindow::showTemporalGraphWindow);
connect(CANConManager::getInstance(), &CANConManager::framesReceived, model, &CANFrameModel::addFrames);
@@ -226,6 +228,7 @@ void MainWindow::killEmAll()
killWindow(motorctrlConfigWindow);
killWindow(signalViewerWindow);
killWindow(connectionWindow);
killWindow(temporalGraphWindow);
}
//forcefully close the window, kill it, and salt the earth
@@ -837,6 +840,19 @@ void MainWindow::showGraphingWindow()
graphingWindow->show();
}
void MainWindow::showTemporalGraphWindow()
{
//only create an instance of the object if we dont have one. Otherwise just display the existing one.
if (!temporalGraphWindow)
{
if (!useFiltered)
temporalGraphWindow = new TemporalGraphWindow(model->getListReference());
else
temporalGraphWindow = new TemporalGraphWindow(model->getFilteredListReference());
}
temporalGraphWindow->show();
}
void MainWindow::showFrameDataAnalysis()
{
//only create an instance of the object if we dont have one. Otherwise just display the existing one.
+3
View File
@@ -31,6 +31,7 @@
#include "re/isotp_interpreterwindow.h"
#include "motorcontrollerconfigwindow.h"
#include "signalviewerwindow.h"
#include "re/temporalgraphwindow.h"
class CANConnection;
class ConnectionWindow;
@@ -81,6 +82,7 @@ private slots:
void showSnifferWindow();
void showBisectWindow();
void showSignalViewer();
void showTemporalGraphWindow();
void exitApp();
void handleSaveDecoded();
void connectionStatusUpdated(int conns);
@@ -159,6 +161,7 @@ private:
MotorControllerConfigWindow *motorctrlConfigWindow;
BisectWindow* bisectWindow;
SignalViewerWindow *signalViewerWindow;
TemporalGraphWindow *temporalGraphWindow;
//various private storage
QLabel lbStatusConnected;
+381
View File
@@ -0,0 +1,381 @@
#include "temporalgraphwindow.h"
#include "ui_temporalgraphwindow.h"
#include "helpwindow.h"
#include "mainwindow.h"
QString HexTicker::getTickLabel (double tick, const QLocale& locale, QChar formatChar, int precision)
{
int valu = static_cast<int>(tick);
//qDebug() << valu;
return "0x" + QString::number(valu, 16).toUpper().rightJustified(3,'0');
}
TemporalGraphWindow::TemporalGraphWindow(const QVector<CANFrame> *frames, QWidget *parent) :
QDialog(parent),
ui(new Ui::TemporalGraphWindow)
{
ui->setupUi(this);
setWindowFlags(Qt::Window);
readSettings();
modelFrames = frames;
ui->graphingView->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectAxes);
ui->graphingView->xAxis->setRange(0, 8);
ui->graphingView->yAxis->setRange(0, 255);
ui->graphingView->axisRect()->setupFullAxesBox();
ui->graphingView->xAxis->setLabel("Elapsed Time");
ui->graphingView->yAxis->setLabel("ID");
ui->graphingView->xAxis->setNumberFormat("f");
ui->graphingView->xAxis->setNumberPrecision(6);
HexTicker *tick = new HexTicker;
QSharedPointer<QCPAxisTicker> sharedTicker(tick);
tick->setTickCount(10);
ui->graphingView->yAxis->setTicker(sharedTicker);
// connect slot that ties some axis selections together (especially opposite axes):
connect(ui->graphingView, SIGNAL(selectionChangedByUser()), this, SLOT(selectionChanged()));
connect(ui->graphingView, SIGNAL(mousePress(QMouseEvent*)), this, SLOT(mousePress()));
connect(ui->graphingView, SIGNAL(mouseWheel(QWheelEvent*)), this, SLOT(mouseWheel()));
connect(MainWindow::getReference(), SIGNAL(framesUpdated(int)), this, SLOT(updatedFrames(int)));
// make bottom and left axes transfer their ranges to top and right axes:
connect(ui->graphingView->xAxis, SIGNAL(rangeChanged(QCPRange)), ui->graphingView->xAxis2, SLOT(setRange(QCPRange)));
connect(ui->graphingView->yAxis, SIGNAL(rangeChanged(QCPRange)), ui->graphingView->yAxis2, SLOT(setRange(QCPRange)));
if (useOpenGL)
{
ui->graphingView->setAntialiasedElements(QCP::aeAll);
//ui->graphingView->setNoAntialiasingOnDrag(true);
ui->graphingView->setOpenGl(true);
}
else
{
ui->graphingView->setOpenGl(false);
ui->graphingView->setAntialiasedElements(QCP::aeNone);
}
}
TemporalGraphWindow::~TemporalGraphWindow()
{
delete ui;
}
void TemporalGraphWindow::showEvent(QShowEvent* event)
{
QDialog::showEvent(event);
installEventFilter(this);
readSettings();
generateGraph();
ui->graphingView->replot();
}
void TemporalGraphWindow::closeEvent(QCloseEvent *event)
{
Q_UNUSED(event);
removeEventFilter(this);
writeSettings();
}
void TemporalGraphWindow::readSettings()
{
QSettings settings;
if (settings.value("Main/SaveRestorePositions", false).toBool())
{
resize(settings.value("Temporal/WindowSize", QSize(800, 600)).toSize());
move(settings.value("Temporal/WindowPos", QPoint(50, 50)).toPoint());
}
useOpenGL = settings.value("Main/UseOpenGL", false).toBool();
}
void TemporalGraphWindow::writeSettings()
{
QSettings settings;
if (settings.value("Main/SaveRestorePositions", false).toBool())
{
settings.setValue("Temporal/WindowSize", size());
settings.setValue("Temporal/WindowPos", pos());
}
}
void TemporalGraphWindow::updatedFrames(int numFrames)
{
CANFrame thisFrame;
QVector<double> x, y;
bool appendedToGraph = false;
bool needReplot = false;
if (numFrames == -1) //all frames deleted. Kill the display
{
//removeAllGraphs();
//now instead of removing the graphs regenerate them which will blank them out but leave them there in case
//more traffic that matches comes in or someone otherwise loads more data
ui->graphingView->clearGraphs(); //temporarily remove the graphs from the graph view
ui->graphingView->replot(); //now, redisplay them all
}
else if (numFrames == -2) //all new set of frames. Reset
{
//there shouldn't be any need to actually remove the graphs.
//regenerate them instead
ui->graphingView->clearGraphs(); //temporarily remove the graphs from the graph view
//needScaleSetup = true;
generateGraph();
//ui->graphingView->replot(); //now, redisplay them all
}
else //just got some new frames. See if they are relevant.
{
if (numFrames > modelFrames->count()) return;
appendedToGraph = false;
x.clear();
y.clear();
for (int i = modelFrames->count() - numFrames; i < modelFrames->count(); i++)
{
thisFrame = modelFrames->at(i);
/*
if (graphParams[j].ID == thisFrame.ID)
{
appendToGraph(graphParams[j], thisFrame, x, y);
appendedToGraph = true;
}*/
}
if (appendedToGraph)
{
//graphParams[j].ref->addData(x, y);
needReplot = true;
}
if (needReplot)
{
if (followGraphEnd)
{
//find the current X span and maintain that span but move the end of it over to match the new end
//of the actual graph. This causes the view to move with the data to always show the end
QCPRange range = ui->graphingView->xAxis->range();
double size = range.size();
bool foundRange;
QCPRange keyRange = ui->graphingView->graph()->getKeyRange(foundRange);
if (foundRange)
{
double end, start;
end = keyRange.upper;
start = end - size;
ui->graphingView->xAxis->setRange(start, end);
}
}
ui->graphingView->replot();
}
}
}
void TemporalGraphWindow::generateGraph()
{
if (modelFrames->count() == 0) return;
qDebug() << "Regenerating the graph";
ui->graphingView->addGraph();
graph = ui->graphingView->graph();
QVector<double> x, y;
double xminval = 0, xmaxval = 0, yminval = 0, ymaxval = 0;
int frameCount = modelFrames->count();
x.reserve(frameCount);
y.reserve(frameCount);
xminval = xmaxval = modelFrames->at(0).timestamp / 1000000.0;
yminval = ymaxval = modelFrames->at(0).ID;
for (int i = 0; i < frameCount; i++)
{
x.append(modelFrames->at(i).timestamp / 1000000.0);
y.append(modelFrames->at(i).ID);
if (x[i] > xmaxval) xmaxval = x[i];
if (x[i] < xminval) xminval = x[i];
if (y[i] > ymaxval) ymaxval = y[i];
if (y[i] < yminval) yminval = y[i];
}
ui->graphingView->graph()->setData(x,y);
ui->graphingView->graph()->setLineStyle(QCPGraph::lsNone); //no lines
ui->graphingView->graph()->setScatterStyle(QCPScatterStyle::ssPlus);
QPen graphPen;
graphPen.setColor(Qt::blue);
graphPen.setWidth(1);
ui->graphingView->graph()->setPen(graphPen);
qDebug() << "xmin: " << xminval;
qDebug() << "xmax: " << xmaxval;
qDebug() << "ymin: " << yminval;
qDebug() << "ymax: " << ymaxval;
ui->graphingView->xAxis->setRange(xminval, xmaxval);
ui->graphingView->yAxis->setRange(yminval, ymaxval);
ui->graphingView->axisRect()->setupFullAxesBox();
ui->graphingView->replot();
}
void TemporalGraphWindow::selectionChanged()
{
/*
normally, axis base line, axis tick labels and axis labels are selectable separately, but we want
the user only to be able to select the axis as a whole, so we tie the selected states of the tick labels
and the axis base line together. However, the axis label shall be selectable individually.
The selection state of the left and right axes shall be synchronized as well as the state of the
bottom and top axes.
Further, we want to synchronize the selection of the graphs with the selection state of the respective
legend item belonging to that graph. So the user can select a graph by either clicking on the graph itself
or on its legend item.
*/
qDebug() << "SelectionChanged";
// make top and bottom axes be selected synchronously, and handle axis and tick labels as one selectable object:
if (ui->graphingView->xAxis->selectedParts().testFlag(QCPAxis::spAxis) || ui->graphingView->xAxis->selectedParts().testFlag(QCPAxis::spTickLabels) ||
ui->graphingView->xAxis2->selectedParts().testFlag(QCPAxis::spAxis) || ui->graphingView->xAxis2->selectedParts().testFlag(QCPAxis::spTickLabels))
{
ui->graphingView->xAxis2->setSelectedParts(QCPAxis::spAxis|QCPAxis::spTickLabels);
ui->graphingView->xAxis->setSelectedParts(QCPAxis::spAxis|QCPAxis::spTickLabels);
}
// make left and right axes be selected synchronously, and handle axis and tick labels as one selectable object:
if (ui->graphingView->yAxis->selectedParts().testFlag(QCPAxis::spAxis) || ui->graphingView->yAxis->selectedParts().testFlag(QCPAxis::spTickLabels) ||
ui->graphingView->yAxis2->selectedParts().testFlag(QCPAxis::spAxis) || ui->graphingView->yAxis2->selectedParts().testFlag(QCPAxis::spTickLabels))
{
ui->graphingView->yAxis2->setSelectedParts(QCPAxis::spAxis|QCPAxis::spTickLabels);
ui->graphingView->yAxis->setSelectedParts(QCPAxis::spAxis|QCPAxis::spTickLabels);
}
}
void TemporalGraphWindow::mousePress()
{
// if an axis is selected, only allow the direction of that axis to be dragged
// if no axis is selected, both directions may be dragged
if (ui->graphingView->xAxis->selectedParts().testFlag(QCPAxis::spAxis))
{
ui->graphingView->axisRect()->setRangeDrag(ui->graphingView->xAxis->orientation());
}
else if (ui->graphingView->yAxis->selectedParts().testFlag(QCPAxis::spAxis))
{
ui->graphingView->axisRect()->setRangeDrag(ui->graphingView->yAxis->orientation());
}
else
{
ui->graphingView->axisRect()->setRangeDrag(Qt::Horizontal|Qt::Vertical);
}
}
void TemporalGraphWindow::mouseWheel()
{
qDebug() << "Mouse WHeel";
// if an axis is selected, only allow the direction of that axis to be zoomed
// if no axis is selected, both directions may be zoomed
if (ui->graphingView->xAxis->selectedParts().testFlag(QCPAxis::spAxis))
ui->graphingView->axisRect()->setRangeZoom(ui->graphingView->xAxis->orientation());
else if (ui->graphingView->yAxis->selectedParts().testFlag(QCPAxis::spAxis))
ui->graphingView->axisRect()->setRangeZoom(ui->graphingView->yAxis->orientation());
else
ui->graphingView->axisRect()->setRangeZoom(Qt::Horizontal|Qt::Vertical);
}
bool TemporalGraphWindow::eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::KeyRelease) {
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
switch (keyEvent->key())
{
case Qt::Key_Plus:
zoomIn();
break;
case Qt::Key_Minus:
zoomOut();
break;
case Qt::Key_F1:
HelpWindow::getRef()->showHelp("temporalwindow.html");
break;
}
return true;
} else {
// standard event processing
return QObject::eventFilter(obj, event);
}
return false;
}
void TemporalGraphWindow::resetView()
{
double yminval=10000000.0, ymaxval = -1000000.0;
double xminval=100000000000, xmaxval = -10000000000.0;
/*
for (int i = 0; i < graphParams.count(); i++)
{
for (int j = 0; j < graphParams[i].x.count(); j++)
{
if (graphParams[i].x[j] < xminval) xminval = graphParams[i].x[j];
if (graphParams[i].x[j] > xmaxval) xmaxval = graphParams[i].x[j];
if (graphParams[i].y[j] < yminval) yminval = graphParams[i].y[j];
if (graphParams[i].y[j] > ymaxval) ymaxval = graphParams[i].y[j];
}
}
ui->graphingView->xAxis->setRange(xminval, xmaxval);
ui->graphingView->yAxis->setRange(yminval, ymaxval);
ui->graphingView->axisRect()->setupFullAxesBox();
ui->graphingView->replot();
*/
}
void TemporalGraphWindow::zoomIn()
{
QCPRange xrange = ui->graphingView->xAxis->range();
QCPRange yrange = ui->graphingView->yAxis->range();
if (ui->graphingView->xAxis->selectedParts().testFlag(QCPAxis::spAxis))
{
ui->graphingView->xAxis->scaleRange(0.666, xrange.center());
}
else if (ui->graphingView->yAxis->selectedParts().testFlag(QCPAxis::spAxis))
{
ui->graphingView->yAxis->scaleRange(0.666, yrange.center());
}
else
{
ui->graphingView->xAxis->scaleRange(0.666, xrange.center());
ui->graphingView->yAxis->scaleRange(0.666, yrange.center());
}
ui->graphingView->replot();
}
void TemporalGraphWindow::zoomOut()
{
QCPRange xrange = ui->graphingView->xAxis->range();
QCPRange yrange = ui->graphingView->yAxis->range();
if (ui->graphingView->xAxis->selectedParts().testFlag(QCPAxis::spAxis))
{
ui->graphingView->xAxis->scaleRange(1.5, xrange.center());
}
else if (ui->graphingView->yAxis->selectedParts().testFlag(QCPAxis::spAxis))
{
ui->graphingView->yAxis->scaleRange(1.5, yrange.center());
}
else
{
ui->graphingView->xAxis->scaleRange(1.5, xrange.center());
ui->graphingView->yAxis->scaleRange(1.5, yrange.center());
}
ui->graphingView->replot();
}
+50
View File
@@ -0,0 +1,50 @@
#ifndef TEMPORALGRAPHWINDOW_H
#define TEMPORALGRAPHWINDOW_H
#include <QDialog>
#include "qcustomplot.h"
#include "can_structs.h"
namespace Ui {
class TemporalGraphWindow;
}
class HexTicker : public QCPAxisTicker
{
QString getTickLabel (double tick, const QLocale& locale, QChar formatChar, int precision);
};
class TemporalGraphWindow : public QDialog
{
Q_OBJECT
public:
explicit TemporalGraphWindow(const QVector<CANFrame> *, QWidget *parent = nullptr);
~TemporalGraphWindow();
void showEvent(QShowEvent*);
private slots:
void updatedFrames(int);
void mousePress();
void mouseWheel();
void resetView();
void zoomIn();
void zoomOut();
void selectionChanged();
private:
Ui::TemporalGraphWindow *ui;
const QVector<CANFrame> *modelFrames;
bool useOpenGL;
bool followGraphEnd;
QCPGraph *graph;
void closeEvent(QCloseEvent *event);
bool eventFilter(QObject *obj, QEvent *event);
void readSettings();
void writeSettings();
void generateGraph();
};
#endif // TEMPORALGRAPHWINDOW_H
+6
View File
@@ -282,6 +282,7 @@
<addaction name="actionSniffer"/>
<addaction name="actionCapture_Bisector"/>
<addaction name="actionSignal_Viewer"/>
<addaction name="actionTemporal_Graph"/>
</widget>
<widget class="QMenu" name="menuSend_Frames">
<property name="title">
@@ -497,6 +498,11 @@
<string>Start Continuous Logging</string>
</property>
</action>
<action name="actionTemporal_Graph">
<property name="text">
<string>Temporal Graph</string>
</property>
</action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources/>
+32
View File
@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>TemporalGraphWindow</class>
<widget class="QDialog" name="TemporalGraphWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>886</width>
<height>683</height>
</rect>
</property>
<property name="windowTitle">
<string>Temporal Graph</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QCustomPlot" name="graphingView" native="true"/>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>QCustomPlot</class>
<extends>QWidget</extends>
<header>qcustomplot.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>