From 35c1ef53874a25c25d7f41890c3345aadcde39a8 Mon Sep 17 00:00:00 2001 From: Collin Kidder Date: Sun, 2 May 2021 12:48:47 -0400 Subject: [PATCH] Added ability to create multiple graphing windows that act separately. Added ability to right check on signals in main view and graph them directly. --- dbc/dbchandler.cpp | 2 +- mainwindow.cpp | 158 ++++++++++++++++++++++++++++++++++-------- mainwindow.h | 12 +++- re/graphingwindow.cpp | 38 +++++++++- re/graphingwindow.h | 7 +- ui/graphingwindow.ui | 4 +- ui/mainwindow.ui | 4 +- 7 files changed, 185 insertions(+), 40 deletions(-) diff --git a/dbc/dbchandler.cpp b/dbc/dbchandler.cpp index fd6b046..b2cb654 100644 --- a/dbc/dbchandler.cpp +++ b/dbc/dbchandler.cpp @@ -1944,7 +1944,7 @@ DBC_MESSAGE* DBCHandler::findMessageForFilter(uint32_t id, MatchingCriteria_t * DBC_MESSAGE* msg = loadedFiles[i].messageHandler->findMsgByID(id); if (msg != nullptr) { - *matchingCriteria = loadedFiles[i].messageHandler->getMatchingCriteria(); + if (matchingCriteria) *matchingCriteria = loadedFiles[i].messageHandler->getMatchingCriteria(); return msg; } } diff --git a/mainwindow.cpp b/mainwindow.cpp index 7775e6e..be72ea0 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -64,7 +64,7 @@ MainWindow::MainWindow(QWidget *parent) : HorzHdr->setStretchLastSection(true); //causes the data column to automatically fill the tableview connect(HorzHdr, SIGNAL(sectionClicked(int)), this, SLOT(headerClicked(int))); - graphingWindow = nullptr; + lastGraphingWindow = nullptr; frameInfoWindow = nullptr; playbackWindow = nullptr; flowViewWindow = nullptr; @@ -95,11 +95,11 @@ MainWindow::MainWindow(QWidget *parent) : continuousLogging = false; continuousLogFlushCounter = 0; + //handlers for all menu entries connect(ui->actionSetup, SIGNAL(triggered(bool)), SLOT(showConnectionSettingsWindow())); connect(ui->actionOpen_Log_File, &QAction::triggered, this, &MainWindow::handleLoadFile); connect(ui->actionGraph_Dta, &QAction::triggered, this, &MainWindow::showGraphingWindow); connect(ui->actionFrame_Data_Analysis, &QAction::triggered, this, &MainWindow::showFrameDataAnalysis); - connect(ui->btnClearFrames, &QAbstractButton::clicked, this, &MainWindow::clearFrames); connect(ui->actionSave_Log_File, &QAction::triggered, this, &MainWindow::handleSaveFile); connect(ui->actionSave_Filtered_Log_File, &QAction::triggered, this, &MainWindow::handleSaveFilteredFile); connect(ui->actionLoad_Filter_Definition, &QAction::triggered, this, &MainWindow::handleLoadFilters); @@ -107,11 +107,6 @@ MainWindow::MainWindow(QWidget *parent) : connect(ui->action_Playback, &QAction::triggered, this, &MainWindow::showPlaybackWindow); connect(ui->actionFlow_View, &QAction::triggered, this, &MainWindow::showFlowViewWindow); connect(ui->action_Custom, &QAction::triggered, this, &MainWindow::showFrameSenderWindow); - connect(ui->canFramesView, &QAbstractItemView::pressed, this, &MainWindow::gridClicked); - connect(ui->canFramesView, &QAbstractItemView::doubleClicked, this, &MainWindow::gridDoubleClicked); - connect(ui->cbInterpret, &QAbstractButton::toggled, this, &MainWindow::interpretToggled); - connect(ui->cbOverwrite, &QAbstractButton::toggled, this, &MainWindow::overwriteToggled); - connect(ui->btnCaptureToggle, &QAbstractButton::clicked, this, &MainWindow::toggleCapture); connect(ui->actionExit_Application, &QAction::triggered, this, &MainWindow::exitApp); connect(ui->actionFuzzy_Scope, &QAction::triggered, this, &MainWindow::showFuzzyScopeWindow); connect(ui->actionRange_State_2, &QAction::triggered, this, &MainWindow::showRangeWindow); @@ -120,13 +115,7 @@ MainWindow::MainWindow(QWidget *parent) : connect(ui->actionFile_Comparison, &QAction::triggered, this, &MainWindow::showComparisonWindow); connect(ui->actionDBC_Comparison, &QAction::triggered, this, &MainWindow::showDBCComparisonWindow); connect(ui->actionScripting_INterface, &QAction::triggered, this, &MainWindow::showScriptingWindow); - connect(ui->btnNormalize, &QAbstractButton::clicked, this, &MainWindow::normalizeTiming); connect(ui->actionPreferences, &QAction::triggered, this, &MainWindow::showSettingsDialog); - connect(model, &CANFrameModel::updatedFiltersList, this, &MainWindow::updateFilterList); - connect(ui->listFilters, &QListWidget::itemChanged, this, &MainWindow::filterListItemChanged); - connect(ui->listBusFilters, &QListWidget::itemChanged, this, &MainWindow::busFilterListItemChanged); - connect(ui->btnFilterAll, &QAbstractButton::clicked, this, &MainWindow::filterSetAll); - connect(ui->btnFilterNone, &QAbstractButton::clicked, this, &MainWindow::filterClearAll); connect(ui->actionFirmware_Update, &QAction::triggered, this, &MainWindow::showFirmwareUploaderWindow); connect(ui->actionDBC_File_Manager, &QAction::triggered, this, &MainWindow::showDBCFileWindow); connect(ui->actionFuzzing, &QAction::triggered, this, &MainWindow::showFuzzingWindow); @@ -138,14 +127,31 @@ MainWindow::MainWindow(QWidget *parent) : 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); + + //handlers fror interactions with the main can frame view table + connect(ui->canFramesView, &QAbstractItemView::clicked, this, &MainWindow::gridClicked); + connect(ui->canFramesView, &QAbstractItemView::doubleClicked, this, &MainWindow::gridDoubleClicked); + ui->canFramesView->setContextMenuPolicy(Qt::CustomContextMenu); + connect(ui->canFramesView, &QAbstractItemView::customContextMenuRequested, this, &MainWindow::gridContextMenuRequest); + + connect(model, &CANFrameModel::updatedFiltersList, this, &MainWindow::updateFilterList); + connect(CANConManager::getInstance(), &CANConManager::framesReceived, model, &CANFrameModel::addFrames); + //new implementation for continuous logging + connect(CANConManager::getInstance(), &CANConManager::framesReceived, this, &MainWindow::logReceivedFrame); + + connect(ui->cbInterpret, &QAbstractButton::toggled, this, &MainWindow::interpretToggled); + connect(ui->cbOverwrite, &QAbstractButton::toggled, this, &MainWindow::overwriteToggled); + connect(ui->listFilters, &QListWidget::itemChanged, this, &MainWindow::filterListItemChanged); + connect(ui->listBusFilters, &QListWidget::itemChanged, this, &MainWindow::busFilterListItemChanged); + + connect(ui->btnCaptureToggle, &QAbstractButton::clicked, this, &MainWindow::toggleCapture); + connect(ui->btnClearFrames, &QAbstractButton::clicked, this, &MainWindow::clearFrames); + connect(ui->btnNormalize, &QAbstractButton::clicked, this, &MainWindow::normalizeTiming); + connect(ui->btnFilterAll, &QAbstractButton::clicked, this, &MainWindow::filterSetAll); + connect(ui->btnFilterNone, &QAbstractButton::clicked, this, &MainWindow::filterClearAll); connect(ui->btnExpandAll, &QAbstractButton::clicked, this, &MainWindow::expandAllRows); connect(ui->btnCollapseAll, &QAbstractButton::clicked, this, &MainWindow::collapseAllRows); - connect(CANConManager::getInstance(), &CANConManager::framesReceived, model, &CANFrameModel::addFrames); - - //new implementation for continuous logging: - connect(CANConManager::getInstance(), &CANConManager::framesReceived, this, &MainWindow::logReceivedFrame); - lbStatusConnected.setText(tr("Connected to 0 buses")); lbHelp.setText(tr("Press F1 on any screen for help")); lbHelp.setAlignment(Qt::AlignCenter); @@ -223,7 +229,10 @@ MainWindow::~MainWindow() //but eventually each window should be registered and be able to be iterated. void MainWindow::killEmAll() { - killWindow(graphingWindow); + foreach (GraphingWindow *win, graphWindows) + { + killWindow(win); + } killWindow(frameInfoWindow); killWindow(playbackWindow); killWindow(flowViewWindow); @@ -472,12 +481,87 @@ void MainWindow::gridClicked(QModelIndex idx) void MainWindow::gridDoubleClicked(QModelIndex idx) { - //qDebug() << "Grid double clicked"; + qDebug() << "Grid double clicked"; //grab ID and timestamp and send them away CANFrame frame = model->getListReference()->at(idx.row()); emit sendCenterTimeID(frame.frameId(), frame.timeStamp().microSeconds() / 1000000.0); } +void MainWindow::gridContextMenuRequest(QPoint pos) +{ + QModelIndex idx = ui->canFramesView->indexAt(pos); //figure out where in the view we clicked (row, column) + qDebug() << "Pos: " << pos << " Row :" << idx.row() << " Col: " << idx.column(); + qDebug() << "Data: " << idx.data(); + if (idx.column() == 8) //we're over the DATA column + { + contextMenuPosition = pos; + + QMenu *menu = new QMenu(this); + menu->setAttribute(Qt::WA_DeleteOnClose); + + menu->addAction(tr("Add to a new graphing window"), this, SLOT(setupAddToNewGraph())); + menu->addSeparator(); + menu->addAction(tr("Add to latest graphing window"), this, SLOT(setupSendToLatestGraphWindow())); + + menu->popup(ui->canFramesView->mapToGlobal(pos)); + } +} + +QString MainWindow::getSignalNameFromPosition(QPoint pos) +{ + //there's a bit of an issue to solve. The data column is one big string even if there are a number + //of signals in there. So, the basic idea is to find out how tall the font is and where the user + //clicked within the cell. Then find out which line that puts us over. + QModelIndex idx = ui->canFramesView->indexAt(pos); //figure out where in the view we clicked (row, column) + int fontHeight = ui->canFramesView->fontMetrics().height(); + int cellBaseY = ui->canFramesView->rowViewportPosition(idx.row()); + int lineOffset = (pos.y() - cellBaseY) / fontHeight; + qDebug() << "Offset: " << lineOffset; + QString lineText = idx.data().toString().split("\n")[lineOffset]; + qDebug() << "Line Text: " << lineText; + return lineText.split(":")[0]; +} + +uint32_t MainWindow::getMessageIDFromPosition(QPoint pos) +{ + QModelIndex idx = ui->canFramesView->indexAt(pos); //figure out where in the view we clicked (row, column) + QString idText = ui->canFramesView->model()->index(idx.row(), 1).data().toString(); + return Utility::ParseStringToNum(idText); +} + +void MainWindow::setupAddToNewGraph() +{ + showGraphingWindow(); //creates a new window and sets it as latest + setupSendToLatestGraphWindow(); //then call the other function to finish +} + +void MainWindow::setupSendToLatestGraphWindow() +{ + if (!lastGraphingWindow) showGraphingWindow(); + GraphParams param; + QString signalName = getSignalNameFromPosition(contextMenuPosition); + param.ID = getMessageIDFromPosition(contextMenuPosition); + DBC_MESSAGE *msg = dbcHandler->findMessageForFilter(param.ID, nullptr); + DBC_SIGNAL *sig = msg->sigHandler->findSignalByName(signalName); + param.associatedSignal = sig; + param.bias = sig->bias; + param.intelFormat = sig->intelByteOrder; + param.isSigned = sig->valType == SIGNED_INT ? true : false; + param.numBits = sig->signalSize; + param.scale = sig->factor; + param.startBit = sig->startBit; + param.stride = 1; + param.graphName = sig->name; + param.lineColor = QColor(QRandomGenerator::global()->bounded(160), QRandomGenerator::global()->bounded(160), QRandomGenerator::global()->bounded(160)); + param.lineWidth = 1; + param.fillColor = QColor(128, 128, 128, 0); + param.mask = 0xFFFFFFFFFFFFFFFFull; + param.drawOnlyPoints = false; + param.pointType = 0; + + lastGraphingWindow->createGraph(param); //add the new graph to the window +} + void MainWindow::interpretToggled(bool state) { model->setInterpretMode(state); @@ -954,20 +1038,34 @@ void MainWindow::showSettingsDialog() } //always gets unfiltered list. You ask for the graphs so there is no need to send filtered frames +//now always creates a new window. This allows for multiple independent graphing windows void MainWindow::showGraphingWindow() { - if (!graphingWindow) { - graphingWindow = new GraphingWindow(model->getListReference()); - connect(graphingWindow, SIGNAL(sendCenterTimeID(uint32_t,double)), this, SLOT(gotCenterTimeID(int32_t,double))); - connect(this, SIGNAL(sendCenterTimeID(uint32_t,double)), graphingWindow, SLOT(gotCenterTimeID(int32_t,double))); +/* could only allow the latest window to have these centering signals. + if (lastGraphingWindow) + { + disconnect(lastGraphingWindow, SIGNAL(sendCenterTimeID(uint32_t,double)), this, SLOT(gotCenterTimeID(int32_t,double))); + disconnect(this, SIGNAL(sendCenterTimeID(uint32_t,double)), lastGraphingWindow, SLOT(gotCenterTimeID(int32_t,double))); + if (flowViewWindow) + { + disconnect(lastGraphingWindow, SIGNAL(sendCenterTimeID(uint32_t,double)), flowViewWindow, SLOT(gotCenterTimeID(int32_t,double))); + disconnect(flowViewWindow, SIGNAL(sendCenterTimeID(uint32_t,double)), lastGraphingWindow, SLOT(gotCenterTimeID(int32_t,double))); + } } +*/ + lastGraphingWindow = new GraphingWindow(model->getListReference()); + graphWindows.append(lastGraphingWindow); + + connect(lastGraphingWindow, SIGNAL(sendCenterTimeID(uint32_t,double)), this, SLOT(gotCenterTimeID(int32_t,double))); + connect(this, SIGNAL(sendCenterTimeID(uint32_t,double)), lastGraphingWindow, SLOT(gotCenterTimeID(int32_t,double))); if (flowViewWindow) //connect the two external windows together { - connect(graphingWindow, SIGNAL(sendCenterTimeID(uint32_t,double)), flowViewWindow, SLOT(gotCenterTimeID(int32_t,double))); - connect(flowViewWindow, SIGNAL(sendCenterTimeID(uint32_t,double)), graphingWindow, SLOT(gotCenterTimeID(int32_t,double))); + connect(lastGraphingWindow, SIGNAL(sendCenterTimeID(uint32_t,double)), flowViewWindow, SLOT(gotCenterTimeID(int32_t,double))); + connect(flowViewWindow, SIGNAL(sendCenterTimeID(uint32_t,double)), lastGraphingWindow, SLOT(gotCenterTimeID(int32_t,double))); } - graphingWindow->show(); + + lastGraphingWindow->show(); } void MainWindow::showTemporalGraphWindow() @@ -1148,10 +1246,10 @@ void MainWindow::showFlowViewWindow() connect(this, SIGNAL(sendCenterTimeID(uint32_t,double)), flowViewWindow, SLOT(gotCenterTimeID(int32_t,double))); } - if (graphingWindow) + if (lastGraphingWindow) { - connect(graphingWindow, SIGNAL(sendCenterTimeID(uint32_t,double)), flowViewWindow, SLOT(gotCenterTimeID(int32_t,double))); - connect(flowViewWindow, SIGNAL(sendCenterTimeID(uint32_t,double)), graphingWindow, SLOT(gotCenterTimeID(int32_t,double))); + connect(lastGraphingWindow, SIGNAL(sendCenterTimeID(uint32_t,double)), flowViewWindow, SLOT(gotCenterTimeID(int32_t,double))); + connect(flowViewWindow, SIGNAL(sendCenterTimeID(uint32_t,double)), lastGraphingWindow, SLOT(gotCenterTimeID(int32_t,double))); } flowViewWindow->show(); diff --git a/mainwindow.h b/mainwindow.h index 5505b51..0513ebb 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -92,6 +92,9 @@ private slots: void connectionStatusUpdated(int conns); void gridClicked(QModelIndex); void gridDoubleClicked(QModelIndex); + void gridContextMenuRequest(QPoint pos); + void setupAddToNewGraph(); + void setupSendToLatestGraphWindow(); void interpretToggled(bool); void overwriteToggled(bool); void logReceivedFrame(CANConnection*, QVector); @@ -147,7 +150,11 @@ private: int continuousLogFlushCounter; //References to other windows we can display - GraphingWindow *graphingWindow; + + //Graph window is allowed to instantiate more than once. All the rest are not (yet). + GraphingWindow *lastGraphingWindow; + QList graphWindows; + FrameInfoWindow *frameInfoWindow; FramePlaybackWindow *playbackWindow; FlowViewWindow *flowViewWindow; @@ -178,8 +185,11 @@ private: QLabel lbHelp; int normalRowHeight; bool isConnected; + QPoint contextMenuPosition; //private methods + QString getSignalNameFromPosition(QPoint pos); + uint32_t getMessageIDFromPosition(QPoint pos); void saveDecodedTextFile(QString); void addFrameToDisplay(CANFrame &, bool); void updateFileStatus(); diff --git a/re/graphingwindow.cpp b/re/graphingwindow.cpp index 000fa56..a0b442d 100644 --- a/re/graphingwindow.cpp +++ b/re/graphingwindow.cpp @@ -120,6 +120,26 @@ void GraphingWindow::closeEvent(QCloseEvent *event) writeSettings(); } +void GraphingWindow::changeEvent(QEvent *event) +{ + QWidget::changeEvent(event); + if (event->type() == QEvent::ActivationChange) + { + if(this->isActiveWindow()) + { + setWindowOpacity(1); + ui->graphingView->repaint(); + qDebug() << "Show"; + } + else + { + setWindowOpacity(0.25); + // widget is now inactive + qDebug() << "Hide"; + } + } +} + void GraphingWindow::readSettings() { QSettings settings; @@ -268,6 +288,8 @@ void GraphingWindow::gotCenterTimeID(int32_t ID, double timestamp) //{ //} + qDebug() << "Trying to center graph on timestamp: " << timestamp; + QCPRange range = ui->graphingView->xAxis->range(); double offset = range.size() / 2.0; if (!secondsMode) timestamp *= 1000000.0; //timestamp is always in seconds when being passed so convert if necessary @@ -1312,6 +1334,17 @@ void GraphingWindow::createGraph(GraphParams ¶ms, bool createGraphParam) ui->graphingView->graph()->setBrush(fillBrush); } + double xRange = (xmaxval - xminval); + double yRange = (ymaxval - yminval); + double xMid = xminval + (xRange / 2.0); + double yMid = yminval + (yRange / 2.0); + + //creates a slightly larger view than the actual boundary values to give some padding + xminval = xMid - (xRange / 1.95); + xmaxval = xMid + (xRange / 1.95); + yminval = yMid - (yRange / 1.85); + ymaxval = yMid + (yRange / 1.85); + qDebug() << "xmin: " << xminval; qDebug() << "xmax: " << xmaxval; qDebug() << "ymin: " << yminval; @@ -1320,10 +1353,11 @@ void GraphingWindow::createGraph(GraphParams ¶ms, bool createGraphParam) if (needScaleSetup) { needScaleSetup = false; - ui->graphingView->xAxis->setRange(xminval, xmaxval); - ui->graphingView->yAxis->setRange(yminval, ymaxval); + ui->graphingView->xAxis->setRange(xminval, xmaxval); ui->graphingView->axisRect()->setupFullAxesBox(); } + //always recalculate Y range so that new graphs actually show up in view + ui->graphingView->yAxis->setRange(yminval, ymaxval); ui->graphingView->replot(); } diff --git a/re/graphingwindow.h b/re/graphingwindow.h index 3ddd805..c56e439 100644 --- a/re/graphingwindow.h +++ b/re/graphingwindow.h @@ -45,6 +45,9 @@ public: ~GraphingWindow(); void showEvent(QShowEvent*); +public slots: + void createGraph(GraphParams ¶ms, bool createGraphParam = true); + private slots: void titleDoubleClick(QMouseEvent *event, QCPTextElement *title); void axisDoubleClick(QCPAxis* axis, QCPAxis::SelectablePart part); @@ -66,8 +69,7 @@ private slots: void rescaleAxis(QCPAxis* axis); void rescaleToData(); void toggleFollowMode(); - void addNewGraph(); - void createGraph(GraphParams ¶ms, bool createGraphParam = true); + void addNewGraph(); void appendToGraph(GraphParams ¶ms, CANFrame &frame, QVector &x, QVector &y); void editSelectedGraph(); void updatedFrames(int); @@ -99,6 +101,7 @@ private: void readSettings(); void writeSettings(); bool eventFilter(QObject *obj, QEvent *event); + void changeEvent(QEvent *event); }; #endif // GRAPHINGWINDOW_H diff --git a/ui/graphingwindow.ui b/ui/graphingwindow.ui index 7b7c67b..71a347d 100644 --- a/ui/graphingwindow.ui +++ b/ui/graphingwindow.ui @@ -18,8 +18,8 @@ - 800 - 600 + 700 + 300 diff --git a/ui/mainwindow.ui b/ui/mainwindow.ui index a532e93..e4c0be4 100644 --- a/ui/mainwindow.ui +++ b/ui/mainwindow.ui @@ -361,7 +361,7 @@ 0 0 800 - 21 + 22 @@ -447,7 +447,7 @@ - Graph Data + New Graphing Window