From 4ab888cd7747dc1e6f4ffa6b765a2f1560eab45b Mon Sep 17 00:00:00 2001 From: Collin Kidder Date: Wed, 10 Apr 2019 20:07:43 -0400 Subject: [PATCH 01/12] Fix bug where it was impossible to exit Overwrite mode. --- mainwindow.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/mainwindow.cpp b/mainwindow.cpp index 1d6f896..1471a72 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -406,10 +406,14 @@ void MainWindow::overwriteToggled(bool state) QMessageBox::Yes|QMessageBox::No); if (confirmDialog == QMessageBox::Yes) { - model->setOverwriteMode(state); + model->setOverwriteMode(true); } else ui->cbOverwrite->setCheckState(Qt::Unchecked); } + else + { + model->setOverwriteMode(false); + } } void MainWindow::updateFilterList() From 67b7de48a671a0109be7957a99ef80a9d66a70d5 Mon Sep 17 00:00:00 2001 From: Collin Kidder Date: Wed, 10 Apr 2019 20:29:13 -0400 Subject: [PATCH 02/12] Add frame count so that overwrite mode can show it. Frame count replaces remote frame indicator when in overwrite mode. --- can_structs.h | 13 +++++++++++++ canframemodel.cpp | 9 +++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/can_structs.h b/can_structs.h index da60766..74e5b0a 100644 --- a/can_structs.h +++ b/can_structs.h @@ -16,11 +16,24 @@ public: uint32_t len; unsigned char data[8]; uint64_t timestamp; + uint32_t frameCount; //used in overwrite mode friend bool operator<(const CANFrame& l, const CANFrame& r) { return l.timestamp < r.timestamp; } + + CANFrame() + { + ID = 0; + bus = 0; + extended = false; + remote = false; + isReceived = true; + len = 0; + timestamp = 0; + frameCount = 1; + } }; class CANFltObserver diff --git a/canframemodel.cpp b/canframemodel.cpp index 4221e4f..5da2b2e 100644 --- a/canframemodel.cpp +++ b/canframemodel.cpp @@ -293,7 +293,8 @@ QVariant CANFrameModel::data(const QModelIndex &index, int role) const case Column::Extended: return QString::number(thisFrame.extended); case Column::Remote: - return QString::number(thisFrame.remote); + if (!overwriteDups) return QString::number(thisFrame.remote); + return QString::number(thisFrame.frameCount); case Column::Direction: if (thisFrame.isReceived) return QString(tr("Rx")); return QString(tr("Tx")); @@ -387,7 +388,8 @@ QVariant CANFrameModel::headerData(int section, Qt::Orientation orientation, case Column::Extended: return QString(tr("Ext")); case Column::Remote: - return QString(tr("Rem")); + if (!overwriteDups) return QString(tr("Rem")); + return QString(tr("Cnt")); case Column::Direction: return QString(tr("Dir")); case Column::Bus: @@ -433,6 +435,7 @@ void CANFrameModel::addFrame(const CANFrame& frame, bool autoRefresh = false) if (filters[tempFrame.ID]) { if (autoRefresh) beginInsertRows(QModelIndex(), filteredFrames.count(), filteredFrames.count()); + tempFrame.frameCount = 1; filteredFrames.append(tempFrame); if (autoRefresh) endInsertRows(); } @@ -444,6 +447,7 @@ void CANFrameModel::addFrame(const CANFrame& frame, bool autoRefresh = false) { if (frames[i].ID == tempFrame.ID) { + tempFrame.frameCount = frames[i].frameCount + 1; frames.replace(i, tempFrame); found = true; break; @@ -455,6 +459,7 @@ void CANFrameModel::addFrame(const CANFrame& frame, bool autoRefresh = false) if (filters[tempFrame.ID]) { if (autoRefresh) beginInsertRows(QModelIndex(), filteredFrames.count(), filteredFrames.count()); + tempFrame.frameCount = 1; filteredFrames.append(tempFrame); if (autoRefresh) endInsertRows(); } From f14247cc35c6579aa9e05c0b1eb0771280916203 Mon Sep 17 00:00:00 2001 From: Collin Kidder Date: Thu, 11 Apr 2019 22:23:47 -0400 Subject: [PATCH 03/12] Added a format tester for every input format and an auto-loader that tries to guess the file type based on its contents. Not completely stable yet but still a lot more fun than trying to find the file format in a list of 19 file formats. --- framefileio.cpp | 1109 ++++++++++++++++++++++++++++++++++++++++++++++- framefileio.h | 23 + 2 files changed, 1111 insertions(+), 21 deletions(-) diff --git a/framefileio.cpp b/framefileio.cpp index fd6d01b..8adbff2 100644 --- a/framefileio.cpp +++ b/framefileio.cpp @@ -3,7 +3,7 @@ #include #include #include - +#include #include #include "utility.h" @@ -139,6 +139,7 @@ bool FrameFileIO::loadFrameFile(QString &fileName, QVector* frameCache bool result = false; QStringList filters; + filters.append(QString(tr("Autodetect File Type (*.*)"))); filters.append(QString(tr("GVRET Logs (*.csv *.CSV)"))); filters.append(QString(tr("CRTD Logs (*.txt *.TXT)"))); filters.append(QString(tr("Generic ID/Data CSV (*.csv *.CSV)"))); @@ -176,24 +177,25 @@ bool FrameFileIO::loadFrameFile(QString &fileName, QVector* frameCache qApp->processEvents(); - if (dialog.selectedNameFilter() == filters[0]) result = loadNativeCSVFile(filename, frameCache); - if (dialog.selectedNameFilter() == filters[1]) result = loadCRTDFile(filename, frameCache); - if (dialog.selectedNameFilter() == filters[2]) result = loadGenericCSVFile(filename, frameCache); - if (dialog.selectedNameFilter() == filters[3]) result = loadLogFile(filename, frameCache); - if (dialog.selectedNameFilter() == filters[4]) result = loadMicrochipFile(filename, frameCache); - if (dialog.selectedNameFilter() == filters[5]) result = loadTraceFile(filename, frameCache); - if (dialog.selectedNameFilter() == filters[6]) result = loadIXXATFile(filename, frameCache); - if (dialog.selectedNameFilter() == filters[7]) result = loadCANDOFile(filename, frameCache); - if (dialog.selectedNameFilter() == filters[8]) result = loadVehicleSpyFile(filename, frameCache); - if (dialog.selectedNameFilter() == filters[9]) result = loadCanDumpFile(filename, frameCache); - if (dialog.selectedNameFilter() == filters[10]) result = loadPCANFile(filename, frameCache); - if (dialog.selectedNameFilter() == filters[11]) result = loadKvaserFile(filename, frameCache, false); - if (dialog.selectedNameFilter() == filters[12]) result = loadKvaserFile(filename, frameCache, true); - if (dialog.selectedNameFilter() == filters[13]) result = loadCanalyzerASC(filename, frameCache); - if (dialog.selectedNameFilter() == filters[14]) result = loadCanalyzerBLF(filename, frameCache); - if (dialog.selectedNameFilter() == filters[15]) result = loadCANHackerFile(filename, frameCache); - if (dialog.selectedNameFilter() == filters[16]) result = loadCabanaFile(filename, frameCache); - if (dialog.selectedNameFilter() == filters[17]) result = loadCANOpenFile(filename, frameCache); + if (dialog.selectedNameFilter() == filters[0]) result = autoDetectLoadFile(filename, frameCache); + if (dialog.selectedNameFilter() == filters[1]) result = loadNativeCSVFile(filename, frameCache); + if (dialog.selectedNameFilter() == filters[2]) result = loadCRTDFile(filename, frameCache); + if (dialog.selectedNameFilter() == filters[3]) result = loadGenericCSVFile(filename, frameCache); + if (dialog.selectedNameFilter() == filters[4]) result = loadLogFile(filename, frameCache); + if (dialog.selectedNameFilter() == filters[5]) result = loadMicrochipFile(filename, frameCache); + if (dialog.selectedNameFilter() == filters[6]) result = loadTraceFile(filename, frameCache); + if (dialog.selectedNameFilter() == filters[7]) result = loadIXXATFile(filename, frameCache); + if (dialog.selectedNameFilter() == filters[8]) result = loadCANDOFile(filename, frameCache); + if (dialog.selectedNameFilter() == filters[9]) result = loadVehicleSpyFile(filename, frameCache); + if (dialog.selectedNameFilter() == filters[10]) result = loadCanDumpFile(filename, frameCache); + if (dialog.selectedNameFilter() == filters[11]) result = loadPCANFile(filename, frameCache); + if (dialog.selectedNameFilter() == filters[12]) result = loadKvaserFile(filename, frameCache, false); + if (dialog.selectedNameFilter() == filters[13]) result = loadKvaserFile(filename, frameCache, true); + if (dialog.selectedNameFilter() == filters[14]) result = loadCanalyzerASC(filename, frameCache); + if (dialog.selectedNameFilter() == filters[15]) result = loadCanalyzerBLF(filename, frameCache); + if (dialog.selectedNameFilter() == filters[16]) result = loadCANHackerFile(filename, frameCache); + if (dialog.selectedNameFilter() == filters[17]) result = loadCabanaFile(filename, frameCache); + if (dialog.selectedNameFilter() == filters[18]) result = loadCANOpenFile(filename, frameCache); progress.cancel(); @@ -215,6 +217,220 @@ bool FrameFileIO::loadFrameFile(QString &fileName, QVector* frameCache } +//Try every format by first uses the "is" functions which try to detect whether a given file is a good match to that +//file format or not. Those functions are much less tolerant than the load functions and so should help to discriminate +//whether a file could be loaded or not by a given loader. The loader return is still used in case the guess was wrong. +bool FrameFileIO::autoDetectLoadFile(QString filename, QVector* frames) +{ + if (isCanalyzerBLF(filename)) + { + if (loadCanalyzerBLF(filename, frames)) + { + qDebug() << "Loaded as Canalyzer BLF successfully!"; + return true; + } + } + + if (isNativeCSVFile(filename)) + { + if (loadNativeCSVFile(filename, frames)) + { + qDebug() << "Loaded as native CSV successfully!"; + return true; + } + } + + if (isCanalyzerASC(filename)) + { + if (loadCanalyzerASC(filename, frames)) + { + qDebug() << "Loaded as Canalyzer ASC successfully!"; + return true; + } + } + + if (isCRTDFile(filename)) + { + if (loadCRTDFile(filename, frames)) + { + qDebug() << "Loaded as CRTD successfully!"; + return true; + } + } + + + if (isTraceFile(filename)) + { + if (loadTraceFile(filename, frames)) + { + qDebug() << "Loaded as trace successfully!"; + return true; + } + } + + if (isVehicleSpyFile(filename)) + { + if (loadVehicleSpyFile(filename, frames)) + { + qDebug() << "Loaded as vehicle spy successfully!"; + return true; + } + } + + if (isCanDumpFile(filename)) + { + if (loadCanDumpFile(filename, frames)) + { + qDebug() << "Loaded as candump successfully!"; + return true; + } + } + + if (isCANHackerFile(filename)) + { + if (loadCANHackerFile(filename, frames)) + { + qDebug() << "Loaded as CANHacker successfully!"; + return true; + } + } + + if (isCabanaFile(filename)) + { + if (loadCabanaFile(filename, frames)) + { + qDebug() << "Loaded as Cabana successfully!"; + return true; + } + } + + if (isCANOpenFile(filename)) + { + if (loadCANOpenFile(filename, frames)) + { + qDebug() << "Loaded as CANOpen Magic successfully!"; + return true; + } + } + + if (isLogFile(filename)) + { + if (loadLogFile(filename, frames)) + { + qDebug() << "Loaded as Busmaster Log successfully!"; + return true; + } + } + + if (isPCANFile(filename)) + { + if (loadPCANFile(filename, frames)) + { + qDebug() << "Loaded as PCAN successfully!"; + return true; + } + } + + if (isIXXATFile(filename)) + { + if (loadIXXATFile(filename, frames)) + { + qDebug() << "Loaded as IXXAT successfully!"; + return true; + } + } + + if (isMicrochipFile(filename)) + { + if (loadMicrochipFile(filename, frames)) + { + qDebug() << "Loaded as microchip successfully!"; + return true; + } + } + + if (isCANDOFile(filename)) + { + if (loadCANDOFile(filename, frames)) + { + qDebug() << "Loaded as CANDO successfully!"; + return true; + } + } + + if (isKvaserFile(filename)) + { + if (loadKvaserFile(filename, frames,true)) + { + qDebug() << "Loaded as Kvaser HEX successfully!"; + return true; + } + if (loadKvaserFile(filename, frames,false)) + { + qDebug() << "Loaded as KVaser Decimal successfully!"; + return true; + } + } + + if (isGenericCSVFile(filename)) + { + if (loadGenericCSVFile(filename, frames)) + { + qDebug() << "Loaded as generic CSV successfully!"; + return true; + } + } + + return false; +} + + +bool FrameFileIO::isVehicleSpyFile(QString filename) +{ + QFile *inFile = new QFile(filename); + QByteArray line; + bool foundProbableHeader = false; + bool isMatch = false; + if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text)) + { + delete inFile; + return false; + } + try { + for (int i = 0; i < 10; i++) + { + if (!inFile->atEnd()) + { + line = inFile->readLine().simplified().toUpper(); + if (line.startsWith("LINE") && line.contains("TIME") && line.contains("B1")) + { + foundProbableHeader = true; + } + } + } + if (foundProbableHeader) + { + if (!inFile->atEnd()) + { + line = inFile->readLine().simplified().toUpper(); + inFile->close(); + QList tokens = line.split(','); + if (tokens.length() > 20) + { + if (tokens[9].toInt(NULL, 16) > 0) isMatch = true; + } + } + } + } + catch (...) + { + isMatch = false; + } + inFile->close(); + return isMatch; +} + + //2,2550.368293675,0.003818174999651092,67371008,F,F,HS CAN $119,HS CAN,,119,F,F,00,00,00,00,00,00,0D,8B,,, //Line,Abs Time(Sec),Rel Time (Sec),Status,Er,Tx,Description,Network,Node,Arb ID,Remote,Xtd,B1,B2,B3,B4,B5,B6,B7,B8,Value,Trigger,Signals // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 @@ -294,6 +510,52 @@ bool FrameFileIO::saveVehicleSpyFile(QString filename, const QVector * return true; } +bool FrameFileIO::isCRTDFile(QString filename) +{ + QFile *inFile = new QFile(filename); + QByteArray line; + int lineCounter = 0; + bool isMatch = false; + + if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text)) + { + delete inFile; + return false; + } + + try + { + line = inFile->readLine().toUpper(); //read out the header first and discard it. + + while (!inFile->atEnd() && lineCounter < 100) { + lineCounter++; + line = inFile->readLine().simplified(); + if (line.length() > 2) + { + QList tokens = line.split(' '); + if (tokens.length() > 2) + { + char firstChar = tokens[1].left(1)[0]; + if (firstChar == 'R' || firstChar == 'T') + { + if (tokens[1] == "R29" || tokens[1] == "T29") isMatch = true; + if (tokens[1] == "R11" || tokens[1] == "T11") isMatch = true; + } + } + else isMatch = false; + } + if (lineCounter > 10) break; + } + } + catch (...) + { + isMatch = false; + } + inFile->close(); + delete inFile; + return isMatch; +} + //CRTD format from Mark Webb-Johnson / OVMS project /* Sample data in CRTD format @@ -389,6 +651,55 @@ bool FrameFileIO::loadCRTDFile(QString filename, QVector* frames) return !foundErrors; } + +bool FrameFileIO::isCANHackerFile(QString filename) +{ + QFile *inFile = new QFile(filename); + QByteArray line; + int lineCounter = 0; + bool isMatch = false; + + if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text)) + { + delete inFile; + return false; + } + try + { + line = inFile->readLine().toUpper(); //read out the header first and discard it. + if (!line.contains("CANHACKER")) return false; + + while (!inFile->atEnd()) { + lineCounter++; + line = inFile->readLine().simplified(); + if (line.length() > 2) + { + QList tokens = line.split(' '); + if (tokens.length() > 3) + { + if (tokens[1].toInt(NULL, 16) > 0) + { + int len = tokens[2].toInt(); + if (len > -1 && len < 9) + { + isMatch = true; + } + } + } + else isMatch = false; + } + if (lineCounter > 10) break; + } + } + catch (...) + { + isMatch = false; + } + inFile->close(); + delete inFile; + return isMatch; +} + // CANHacker trace format // Time ID DLC Data Comment // 00.000 00004000 8 36 47 19 43 01 00 00 80 @@ -459,6 +770,59 @@ bool FrameFileIO::loadCANHackerFile(QString filename, QVector* frames) return !foundErrors; } +bool FrameFileIO::isCANOpenFile(QString filename) +{ + QFile *inFile = new QFile(filename); + QByteArray line; + int lineCounter = 0; + bool isMatch = false; + + if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text)) + { + delete inFile; + return false; + } + + try + { + line = inFile->readLine().toUpper(); + if (!line.contains("CANOPEN MAGIC")) return false; + line = inFile->readLine(); + line = inFile->readLine(); + line = inFile->readLine(); + line = inFile->readLine(); + + while (!inFile->atEnd()) { + lineCounter++; + if (lineCounter > 10) + { + break; + } + + line = inFile->readLine().replace('\"', ' ').simplified(); + if (line.length() > 2) + { + QList tokens = line.split(','); + if (tokens.length() > 11) + { + if (Utility::ParseStringToNum(tokens[5].simplified()) > 0) + { + QList dataTok = tokens[11].simplified().split(' '); + if ( dataTok.length() > -1 && dataTok.length() < 9) isMatch = true; + } + } + else isMatch = false; + } + } + } + catch (...) + { + isMatch = false; + } + inFile->close(); + delete inFile; + return isMatch; +} //"Message Number","Time (ms)","Time","Excel Time","Count","ID","Flags","Message Type","Node","Details","Process Data","Data (Hex)","Data (Text)","Data (Decimal)","Length","Raw Message" //"0","0.000","8:09:42:48.7953090'",43447.7100146116,"","0x2E1","","Default: PDO","","Default: TPDO 2 of Node 0x61 (97)","","10 21 04 00 00 00 00 00 ",". ! . . . . . . ","U:0 S:0","8","10 21 04 00 00 00 00 00" @@ -576,6 +940,69 @@ bool FrameFileIO::saveCRTDFile(QString filename, const QVector* frames } +bool FrameFileIO::isPCANFile(QString filename) +{ + QFile *inFile = new QFile(filename); + QByteArray line; + int lineCounter = 0; + int fileVersion = 1; + bool isMatch = false; + + if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text)) + { + delete inFile; + return false; + } + + try + { + while (!inFile->atEnd()) { + lineCounter++; + if (lineCounter > 10) + { + break; + } + line = inFile->readLine(); + if (line.startsWith(";$FILEVERSION=2.0")) fileVersion = 2; + if (line.startsWith(';')) continue; + if (line.length() > 2) + { + if (fileVersion == 1) + { + int id = line.mid(28, 8).simplified().toUInt(NULL, 16); + if (id > 0 && id < 0x1FFFFFFF) + { + int len = line.mid(38,1).toInt(); + if (len > - 1 && len < 9) isMatch = true; + } + } + + else if (fileVersion == 2) + { + int id = line.mid(25, 8).simplified().toUInt(NULL, 16); + if (id > 0 && id < 0x1FFFFFFF) + { + int len = line.mid(37,2).trimmed().toInt(); + if (len > -1 && len < 9) + { + QList tokens = line.mid(40, len * 3).split(' '); + if (tokens.length() == len) isMatch = true; + } + } + } + } + //else foundErrors = true; + } + } + catch (...) + { + isMatch = false; + } + inFile->close(); + delete inFile; + return isMatch; +} + /*Fixed length lines Version 1.1 //; Message Number @@ -699,6 +1126,66 @@ bool FrameFileIO::loadPCANFile(QString filename, QVector* frames) return !foundErrors; } +bool FrameFileIO::isCanalyzerASC(QString filename) +{ + QFile *inFile = new QFile(filename); + QByteArray line; + int lineCounter = 0; + bool isMatch = false; + bool inHeader = true; + QList tokens; + + if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text)) + { + delete inFile; + return false; + } + try + { + while (!inFile->atEnd() && lineCounter < 10) { + lineCounter++; + if (lineCounter > 10) + { + qApp->processEvents(); + lineCounter = 0; + } + line = inFile->readLine(); + if (inHeader) + { + if (line.startsWith("//") || lineCounter > 4) + { + inHeader = false; + continue; + } + } + if (inHeader) continue; + if (line.length() > 2) + { + tokens = line.simplified().split(' '); + if (tokens.length() > 5) + { + uint64_t time = (uint64_t)(tokens[0].toDouble() * (double)1000000.0); + if (time <= 0) continue; + int id = tokens[2].toUInt(NULL, 16); + if (id < 1) continue; + int len = tokens[5].toUInt(); + if (len < 0 || len > 8) continue; + if (tokens.count() > len + 6) isMatch = true; + } + else isMatch = false; + } + else isMatch = false; + } + } + catch (...) + { + isMatch = false; + } + inFile->close(); + delete inFile; + return isMatch; +} + //There tends to be four lines of header first. The last of which starts with // so first burn off lines //until a line starting with // is seen, then the rest are formatted like this: // 47.971842 2 248 Rx d 8 FF FF FF FF FF FF FF FF @@ -748,6 +1235,7 @@ bool FrameFileIO::loadCanalyzerASC(QString filename, QVector* frames) thisFrame.timestamp = (uint64_t)(tokens[0].toDouble() * (double)1000000.0); thisFrame.ID = tokens[2].toUInt(NULL, 16); thisFrame.len = tokens[5].toUInt(); + if (thisFrame.len > 8) return false; thisFrame.isReceived = tokens[3].toUpper().contains("RX"); thisFrame.bus = tokens[1].toUInt(); thisFrame.extended = (thisFrame.ID > 0x7FF); @@ -850,6 +1338,32 @@ bool FrameFileIO::saveCanalyzerASC(QString filename, const QVector* fr return true; } +bool FrameFileIO::isCanalyzerBLF(QString filename) +{ + BLF_FILE_HEADER header; + + QFile *inFile = new QFile(filename); + bool isMatch = false; + + if (!inFile->open(QIODevice::ReadOnly)) + { + delete inFile; + return false; + } + inFile->read((char *)&header, sizeof(header)); + if (qFromLittleEndian(header.sig) == 0x47474F4C) + { + qDebug() << "Proper BLF file header token"; + isMatch = true; + } + else isMatch = false; + + inFile->close(); + delete inFile; + + return isMatch; +} + //this one is pretty complicated and handled by it's own class bool FrameFileIO::loadCanalyzerBLF(QString filename, QVector *frames) { @@ -857,6 +1371,58 @@ bool FrameFileIO::loadCanalyzerBLF(QString filename, QVector *frames) return blf.loadBLF(filename, frames); } +bool FrameFileIO::isNativeCSVFile(QString filename) +{ + QFile *inFile = new QFile(filename); + QByteArray line; + int fileVersion = 1; + bool isMatch = true; + + if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text)) + { + delete inFile; + return false; + } + try + { + line = inFile->readLine().toUpper(); //read out the header first and discard it. + if (line.length() < 23) isMatch = false; + else if (line.at(23) == 'D') fileVersion = 2; //Dir is found starting at position 23 if this is a V2 file + + if (!line.contains("TIME STAMP")) isMatch = false; + if (!line.contains("EXTENDED")) isMatch = false; + if (!line.contains("D1")) isMatch = false; + + if (!inFile->atEnd()) { + line = inFile->readLine().simplified(); + if (line.length() > 2) + { + QList tokens = line.split(','); + if (tokens.length() >= 5) + { + if (fileVersion == 1) + { + if (tokens[4].toUInt() > 8) isMatch = false; + } + else if (fileVersion == 2) + { + if ( tokens[5].toUInt() > 8) isMatch = false; + } + } + else isMatch = false; + } + } + } + catch (...) + { + isMatch = false; + } + inFile->close(); + delete inFile; + + return isMatch; +} + //The "native" file format for this program //Time Stamp,ID,Extended,Dir,Bus,LEN,D1,D2,D3,D4,D5,D6,D7,D8 //39747828,000005EB,false,Rx,0,8,E8,45,85,4B,4A,28,36,69, @@ -1089,6 +1655,56 @@ bool FrameFileIO::flushContinuousNative() return false; } + +bool FrameFileIO::isGenericCSVFile(QString filename) +{ + QFile *inFile = new QFile(filename); + QByteArray line; + bool isMatch = true; + + if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text)) + { + delete inFile; + return false; + } + try + { + line = inFile->readLine(); //read out the header first and discard it. + + if (!inFile->atEnd()) { + line = inFile->readLine(); + if (line.length() > 2) + { + QList tokens = line.split(','); + + int ID = tokens[0].toInt(NULL, 16); + if (ID < 1 || ID > 0x1FFFFFFF) isMatch = false; + + if (tokens.count() < 2) + { + isMatch = false; + } + + if (isMatch) + { + QList dataTok = tokens[1].split(' '); + int len = dataTok.length(); + if (len > 8) isMatch = false; + if (len < 2) isMatch = false; + } + } + else isMatch = false; + } + } + catch (...) + { + isMatch = false; + } + inFile->close(); + delete inFile; + return isMatch; +} + bool FrameFileIO::loadGenericCSVFile(QString filename, QVector* frames) { QFile *inFile = new QFile(filename); @@ -1182,6 +1798,58 @@ bool FrameFileIO::saveGenericCSVFile(QString filename, const QVector* return true; } +bool FrameFileIO::isLogFile(QString filename) +{ + QFile *inFile = new QFile(filename); + QByteArray line; + bool isMatch = true; + + if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text)) + { + delete inFile; + return false; + } + + try + { + line = inFile->readLine().toUpper(); + if (!line.contains("BUSMASTER")) isMatch = false; + + while (!inFile->atEnd() && line.startsWith("***")) { + line = inFile->readLine().toUpper(); + } + + if (!inFile->atEnd()) + { + line = inFile->readLine().toUpper(); + if (line.length() > 1) + { + QList tokens = line.split(' '); + if (tokens.length() >= 6) + { + QList timeToks = tokens[0].split(':'); + if (timeToks.count() != 4) isMatch = false; + + int ID = tokens[3].right(tokens[3].length() - 2).toInt(NULL, 16); + if (ID < 1 || ID > 0x1FFFFFFF) isMatch = false; + if (tokens[4] != "S" && tokens[4] != "X" && tokens[4] != "SR" && tokens[4] != "XR") isMatch = false; + int len = tokens[5].toUInt(); + if (len > 8) isMatch = false; + } + else isMatch = false; + } + } + } + catch (...) + { + isMatch = false; + } + + inFile->close(); + delete inFile; + return isMatch; +} + //busmaster log file /* tokens: @@ -1356,6 +2024,41 @@ bool FrameFileIO::saveLogFile(QString filename, const QVector* frames) return true; } +bool FrameFileIO::isIXXATFile(QString filename) +{ + QFile *inFile = new QFile(filename); + QByteArray line; + bool isMatch = true; + + if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text)) + { + delete inFile; + return false; + } + + try + { + line = inFile->readLine().toUpper(); + if (!line.contains("IXXAT")) isMatch = false; + + for (int i = 0; i < 6; i++) + { + if (!inFile->atEnd()) line = inFile->readLine().toUpper(); + else isMatch = false; + } + + if (!line.contains("FORMAT")) isMatch = false; + } + catch (...) + { + isMatch = false; + } + + inFile->close(); + delete inFile; + return isMatch; +} + //"00:01:03.03","223","Std","","00 00 00 00 49 00 00 01 " bool FrameFileIO::loadIXXATFile(QString filename, QVector* frames) { @@ -1400,6 +2103,7 @@ bool FrameFileIO::loadIXXATFile(QString filename, QVector* frames) { timeStamp = 0; foundErrors = true; + return false; } thisFrame.timestamp = timeStamp; thisFrame.ID = Utility::unQuote(tokens[1]).toInt(NULL, 16); @@ -1413,6 +2117,7 @@ bool FrameFileIO::loadIXXATFile(QString filename, QVector* frames) { thisFrame.extended = false; foundErrors = true; + return false; } thisFrame.isReceived = true; @@ -1421,10 +2126,11 @@ bool FrameFileIO::loadIXXATFile(QString filename, QVector* frames) QStringList dataToks = Utility::unQuote(tokens[4]).simplified().split(' '); thisFrame.len = dataToks.length(); + if (thisFrame.len > 8) return false; for (unsigned int d = 0; d < thisFrame.len; d++) thisFrame.data[d] = dataToks[d].toInt(NULL, 16); frames->append(thisFrame); } - else foundErrors = true; + else return false; } } inFile->close(); @@ -1485,6 +2191,54 @@ bool FrameFileIO::saveIXXATFile(QString filename, const QVector* frame return true; } +bool FrameFileIO::isCANDOFile(QString filename) +{ + QFile *inFile = new QFile(filename); + int lineCounter = 0; + QByteArray data; + bool isMatch = true; + + if (!inFile->open(QIODevice::ReadOnly)) + { + delete inFile; + return false; + } + + //this file format is in static 12 byte blocks. + //Bytes 0 - 1 are a time stamp + //Bytes 2 - 3 are the data length (top 4 bits) then ID (bottom 11 bits) + //Bytes 4 - 11 are the data bytes (padded with FF for bytes not used) + try + { + while (!inFile->atEnd() && lineCounter < 200) + { + lineCounter++; + + data = inFile->read(12); + + int ID = (((unsigned int)data[3] & 0x0F) * 256 + (unsigned int)data[2]); + int len = (unsigned char)data[3] >> 4; + + if (len <= 8 && ID <= 0x7FF) + { + if (len < 8) + { + if (data[4 + len] != (char)0xFF) isMatch = false; + } + } + else isMatch = false; + } + } + catch (...) + { + isMatch = false; + } + + inFile->close(); + delete inFile; + return isMatch; +} + bool FrameFileIO::loadCANDOFile(QString filename, QVector* frames) { QFile *inFile = new QFile(filename); @@ -1601,6 +2355,59 @@ bool FrameFileIO::saveCANDOFile(QString filename, const QVector* frame return true; } +bool FrameFileIO::isMicrochipFile(QString filename) +{ + QFile *inFile = new QFile(filename); + QByteArray line; + bool inComment = false; + int lineCounter = 0; + bool isMatch = true; + + if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text)) + { + delete inFile; + return false; + } + try + { + while (!inFile->atEnd() && lineCounter < 100) { + lineCounter++; + + line = inFile->readLine(); + if (line.length() > 2) + { + if (line.startsWith("//")) + { + inComment = !inComment; + } + else + { + if (!inComment) + { + QList tokens = line.split(';'); + if (tokens.length() >= 4) + { + int ID = Utility::ParseStringToNum(tokens[2]); + if (ID < 1 || ID > 0x1FFFFFFF) isMatch = false; + int len = tokens[3].toInt(); + if ( len > 8 || len < 0 ) isMatch = false; + if ( (len + 4) < tokens.length() ) isMatch = false; + } + else isMatch = false; + } + } + } + } + } + catch (...) + { + isMatch = false; + } + inFile->close(); + delete inFile; + return isMatch; +} + //log file from microchip tool /* tokens: @@ -1739,6 +2546,61 @@ bool FrameFileIO::saveMicrochipFile(QString filename, const QVector* f return true; } +bool FrameFileIO::isTraceFile(QString filename) +{ + QFile *inFile = new QFile(filename); + QByteArray line; + int lineCounter = 0; + bool isMatch = true; + + if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text)) + { + delete inFile; + return false; + } + + try + { + while (!inFile->atEnd() && lineCounter < 100) { + lineCounter++; + + line = inFile->readLine(); + line = line.trimmed(); + if (line.length() > 2) + { + if (line.startsWith(";")) + { + // a comment. Ignore it. + } + else + { + QList tokens = line.split('\t'); + if (tokens.length() > 3) + { + QList timestampToks = tokens[1].split(':'); + if (timestampToks.count() != 4) isMatch = false; + + int ID = tokens[2].toLong(NULL, 16); + if (ID < 1 || ID > 0x1FFFFFFF) isMatch = false; + int len = tokens[3].toInt(); + if (len > 8 || len < 0) isMatch = false; + QList dataToks = tokens[4].split(' '); + if (len > (unsigned int) dataToks.length()) isMatch = false; + } + else isMatch = false; + } + } + } + } + catch (...) + { + isMatch = false; + } + + inFile->close(); + delete inFile; + return isMatch; +} /* ; CAN Logger trace file @@ -1980,6 +2842,123 @@ bool FrameFileIO::saveCanDumpFile(QString filename, const QVector * fr return true; } +bool FrameFileIO::isCanDumpFile(QString filename) +{ + QFile *inFile = new QFile(filename); + QByteArray line; + QList tokens; + QRegExp timeExp("^\\((\\S+)\\)$"); + QRegExp IdValExp("^(\\S+)#(\\S+)$"); + QRegExp valExp("(\\S{2})"); + int lineCounter = 0; + int pos = 0; + bool isMatch = true; + bool ret; + + if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text)) + { + delete inFile; + return false; + } + + try + { + while (!inFile->atEnd() && lineCounter < 100) { + lineCounter++; + + line = inFile->readLine().toUpper(); + if (line.length() > 1) + { + /* tokenize */ + tokens.clear(); + tokens = line.simplified().split(' '); + if(tokens.count() < 3) isMatch = false; + + /* timestamp */ + ret = timeExp.exactMatch(tokens[0]); + if(!ret) isMatch = false; + + uint64_t timestamp = (uint64_t)(timeExp.cap(1).toDouble(&ret) * (double)1000000.0); + if(!ret) isMatch = false; + + if (line.contains('[')) //the expanded format + { + //(1551774790.942758) can1 7A8 [8] F4 DC D1 83 0E 02 00 00 + // 0 1 2 3 4 5 6 7 8 9 10 11 + if (tokens.count() < 3) + { + isMatch = false; + continue; + } + int ID = tokens[2].toULong(nullptr, 16); + if (ID > 0x1FFFFFFF || ID == 0) isMatch = false; + int len = tokens[3].at(1) - '0'; + if (len < 0 || len > 8) isMatch = false; + } + else //the more concise format + { + /* ID & value */ + //qDebug() << tokens[2]; + if (tokens.count() < 3) + { + isMatch = false; + continue; + } + ret = IdValExp.exactMatch(tokens[2]); + if(!ret) + { + isMatch = false; + continue; + } + + /* ID */ + int ID = IdValExp.cap(1).toInt(&ret, 16); + + QString val= IdValExp.cap(2); + + pos = 0; + int len = 0; + if (val.startsWith("R") && val.at(1).isDigit()) { + len = val.at(1).toLatin1() - '0'; + if (len < 0 || len > 8) + { + isMatch = false; + continue; + } + } else { + /* val byte per byte */ + int lng = 0; + while ((pos = valExp.indexIn(val, pos)) != -1) + { + lng++; + if (lng > 8) + { + isMatch = false; + break; + } + int data = valExp.cap(1).toInt(&ret, 16); + if(!ret) + { + isMatch = false; + break; + } + + pos += valExp.matchedLength(); + } + } + } + } + } + } + catch (...) + { + isMatch = false; + } + inFile->close(); + delete inFile; + return isMatch; +} + /* (0.003800) vcan0 164#0000c01aa8000013 or @@ -2092,6 +3071,51 @@ bool FrameFileIO::loadCanDumpFile(QString filename, QVector* frames) return true; } + +bool FrameFileIO::isKvaserFile(QString filename) +{ + QFile *inFile = new QFile(filename); + QByteArray line; + int lineCounter = 0; + bool isMatch = true; + + if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text)) + { + delete inFile; + return false; + } + try + { + line = inFile->readLine().simplified().toUpper(); + if (!line.contains("CHN")) isMatch = false; + if (!line.contains("FLG")) isMatch = false; + if (!line.contains("D0")) isMatch = false; + + if (inFile->atEnd()) isMatch = false; + + while (!inFile->atEnd() && lineCounter < 10) { + lineCounter++; + + line = inFile->readLine().toUpper(); + + if (line.length() > 70) { + //Chn Identifier Flg DLC D0...1...2...3...4...5...6..D7 Time Dir + // 0 000000AD 8 FF FF 00 00 00 00 00 00 154.266550 R + int len = line.mid(21, 3).simplified().toInt(); + if (len > 8 || len < 0) isMatch = false; + } + else isMatch = false; + } + } + catch (...) + { + isMatch = false; + } + inFile->close(); + delete inFile; + return isMatch; +} + //Chn Identifier Flg DLC D0...1...2...3...4...5...6..D7 Time Dir // 0 000000AD 8 FF FF 00 00 00 00 00 00 154.266550 R bool FrameFileIO::loadKvaserFile(QString filename, QVector *frames, bool useHex) @@ -2153,6 +3177,50 @@ bool FrameFileIO::loadKvaserFile(QString filename, QVector *frames, bo return !foundErrors; } +bool FrameFileIO::isCabanaFile(QString filename) +{ + QFile *inFile = new QFile(filename); + QByteArray line; + int lineCounter = 0; + bool isMatch = true; + + if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text)) + { + delete inFile; + return false; + } + try + { + line = inFile->readLine().toUpper(); //read out the header first and discard it. + if (!line.contains("TIME")) isMatch = false; + if (!line.contains("ADDR")) isMatch = false; + + while (!inFile->atEnd() || lineCounter < 100) { + lineCounter++; + + line = inFile->readLine().simplified(); + if (line.length() > 2) + { + QList tokens = line.split(','); + if (tokens.length() >= 3 && tokens.length() < 5) + { + int ID = tokens[1].toInt(); + if (ID < 1 || ID > 0x1FFFFFFF) isMatch = false; + } + else isMatch = false; + } + } + } + catch (...) + { + isMatch = false; + } + + inFile->close(); + delete inFile; + return isMatch; +} + //Cabana uses a CSV file with four columns //time,addr,bus,data //time is in seconds, with quite a bit of resolution after the decimal point. Does not start at 0, probably time-since-boot? @@ -2264,7 +3332,6 @@ bool FrameFileIO::saveCabanaFile(QString filename, const QVector* fram } outFile->write("time,addr,bus,data"); - //outFile->write("Time Stamp,ID,Extended,Dir,Bus,LEN,D1,D2,D3,D4,D5,D6,D7,D8"); outFile->write("\n"); for (int c = 0; c < frames->count(); c++) diff --git a/framefileio.h b/framefileio.h index 820426c..8839e76 100644 --- a/framefileio.h +++ b/framefileio.h @@ -30,6 +30,7 @@ public: static bool saveFrameFile(QString &, const QVector*); //These do the actual loading and saving and can be used directly if you'd prefer + static bool autoDetectLoadFile(QString, QVector*); static bool loadCRTDFile(QString, QVector*); static bool loadNativeCSVFile(QString, QVector*); static bool loadGenericCSVFile(QString, QVector*); @@ -47,6 +48,27 @@ public: static bool loadCANHackerFile(QString filename, QVector* frames); static bool loadCabanaFile(QString filename, QVector* frames); static bool loadCANOpenFile(QString filename, QVector* frames); + + //functions that pre-scan a file to try to figure out if they could read it. Used to automatically determine + //file type and load it. + static bool isCRTDFile(QString); + static bool isNativeCSVFile(QString); + static bool isGenericCSVFile(QString); + static bool isLogFile(QString); + static bool isMicrochipFile(QString); + static bool isTraceFile(QString); + static bool isIXXATFile(QString); + static bool isCANDOFile(QString); + static bool isVehicleSpyFile(QString); + static bool isCanDumpFile(QString); + static bool isPCANFile(QString); + static bool isKvaserFile(QString); + static bool isCanalyzerASC(QString); + static bool isCanalyzerBLF(QString); + static bool isCANHackerFile(QString filename); + static bool isCabanaFile(QString filename); + static bool isCANOpenFile(QString filename); + static bool saveCRTDFile(QString, const QVector*); static bool saveNativeCSVFile(QString, const QVector*); static bool saveGenericCSVFile(QString, const QVector*); @@ -59,6 +81,7 @@ public: static bool saveCanDumpFile(QString filename, const QVector * frames); static bool saveCabanaFile(QString filename, const QVector* frames); static bool saveCanalyzerASC(QString filename, const QVector* frames); + static bool openContinuousNative(); static bool closeContinuousNative(); static bool writeContinuousNative(const QVector*, int); From c0f9ece30cae2b71cc6ee5d2346d01bb1d78b034 Mon Sep 17 00:00:00 2001 From: Collin Kidder Date: Sun, 14 Apr 2019 16:57:09 -0400 Subject: [PATCH 04/12] Update the embedded QCustomPlot files to the newest release. These files are GPL licensed files from QCustomPlot and were not written by me. They retain the relevant license of that project --- qcustomplot.cpp | 295 +++++++++++++++++++++++++++++++----------------- qcustomplot.h | 157 ++++++++++++++------------ 2 files changed, 278 insertions(+), 174 deletions(-) diff --git a/qcustomplot.cpp b/qcustomplot.cpp index 154576a..51722e2 100644 --- a/qcustomplot.cpp +++ b/qcustomplot.cpp @@ -1,7 +1,7 @@ /*************************************************************************** ** ** ** QCustomPlot, an easy to use, modern plotting widget for Qt ** -** Copyright (C) 2011-2017 Emanuel Eichhammer ** +** Copyright (C) 2011-2018 Emanuel Eichhammer ** ** ** ** This program is free software: you can redistribute it and/or modify ** ** it under the terms of the GNU General Public License as published by ** @@ -19,15 +19,15 @@ **************************************************************************** ** Author: Emanuel Eichhammer ** ** Website/Contact: http://www.qcustomplot.com/ ** -** Date: 04.09.17 ** -** Version: 2.0.0 ** +** Date: 25.06.18 ** +** Version: 2.0.1 ** ****************************************************************************/ #include "qcustomplot.h" /* including file 'src/vector2d.cpp', size 7340 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPVector2D @@ -260,7 +260,7 @@ QCPVector2D &QCPVector2D::operator-=(const QCPVector2D &vector) /* including file 'src/painter.cpp', size 8670 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPPainter @@ -478,7 +478,7 @@ void QCPPainter::makeNonCosmetic() /* including file 'src/paintbuffer.cpp', size 18502 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPAbstractPaintBuffer @@ -950,8 +950,8 @@ void QCPPaintBufferGlFbo::reallocateBuffer() /* end of 'src/paintbuffer.cpp' */ -/* including file 'src/layer.cpp', size 37064 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* including file 'src/layer.cpp', size 37304 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPLayer @@ -1464,9 +1464,13 @@ bool QCPLayerable::realVisibility() const placed in \a details. So in the subsequent \ref selectEvent, the decision which part was selected doesn't have to be done a second time for a single selection operation. + In the case of 1D Plottables (\ref QCPAbstractPlottable1D, like \ref QCPGraph or \ref QCPBars) \a + details will be set to a \ref QCPDataSelection, describing the closest data point to \a pos. + You may pass 0 as \a details to indicate that you are not interested in those selection details. - \see selectEvent, deselectEvent, mousePressEvent, wheelEvent, QCustomPlot::setInteractions + \see selectEvent, deselectEvent, mousePressEvent, wheelEvent, QCustomPlot::setInteractions, + QCPAbstractPlottable1D::selectTestRect */ double QCPLayerable::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const { @@ -1804,7 +1808,7 @@ void QCPLayerable::wheelEvent(QWheelEvent *event) /* including file 'src/axis/range.cpp', size 12221 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPRange @@ -2125,8 +2129,8 @@ bool QCPRange::validRange(const QCPRange &range) /* end of 'src/axis/range.cpp' */ -/* including file 'src/selection.cpp', size 21906 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* including file 'src/selection.cpp', size 21941 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPDataRange @@ -2136,8 +2140,9 @@ bool QCPRange::validRange(const QCPRange &range) \brief Describes a data range given by begin and end index QCPDataRange holds two integers describing the begin (\ref setBegin) and end (\ref setEnd) index - of a contiguous set of data points. The end index points to the data point above the last data point that's part of - the data range, similarly to the nomenclature used in standard iterators. + of a contiguous set of data points. The end index points to the data point just after the last + data point that's part of the data range, similarly to the nomenclature used in standard + iterators. Data Ranges are not bound to a certain plottable, thus they can be freely exchanged, created and modified. If a non-contiguous data set shall be described, the class \ref QCPDataSelection is @@ -2184,7 +2189,7 @@ bool QCPRange::validRange(const QCPRange &range) /*! \fn void QCPDataRange::setEnd(int end) - Sets the end of this data range. The \a end index points to the data point just above the last + Sets the end of this data range. The \a end index points to the data point just after the last data point that is part of the data range. No checks or corrections are made to ensure the resulting range is valid (\ref isValid). @@ -2300,7 +2305,7 @@ bool QCPDataRange::intersects(const QCPDataRange &other) const } /*! - Returns whether all data points described by this data range are also in \a other. + Returns whether all data points of \a other are also contained inside this data range. \see intersects */ @@ -2631,7 +2636,8 @@ void QCPDataSelection::enforceType(QCP::SelectionType type) } case QCP::stDataRange: { - mDataRanges = QList() << span(); + if (!isEmpty()) + mDataRanges = QList() << span(); break; } case QCP::stMultipleDataRanges: @@ -2726,7 +2732,7 @@ QCPDataSelection QCPDataSelection::inverse(const QCPDataRange &outerRange) const /* including file 'src/selectionrect.cpp', size 9224 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPSelectionRect @@ -2954,8 +2960,8 @@ void QCPSelectionRect::draw(QCPPainter *painter) /* end of 'src/selectionrect.cpp' */ -/* including file 'src/layout.cpp', size 79064 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* including file 'src/layout.cpp', size 79139 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPMarginGroup @@ -4074,7 +4080,7 @@ QCPLayoutGrid::QCPLayoutGrid() : mColumnSpacing(5), mRowSpacing(5), mWrap(0), - mFillOrder(foRowsFirst) + mFillOrder(foColumnsFirst) { } @@ -4350,7 +4356,8 @@ void QCPLayoutGrid::setWrap(int count) The specified \a order defines whether rows or columns are filled first. Using \ref setWrap, you can control at which row/column count wrapping into the next column/row will occur. If you set it to zero, no wrapping will ever occur. Changing the fill order also changes the meaning of the - linear index used e.g. in \ref elementAt and \ref takeAt. + linear index used e.g. in \ref elementAt and \ref takeAt. The default fill order for \ref + QCPLayoutGrid is \ref foColumnsFirst. If you want to have all current elements arranged in the new order, set \a rearrange to true. The elements will be rearranged in a way that tries to preserve their linear index. However, empty @@ -5125,7 +5132,7 @@ void QCPLayoutInset::addElement(QCPLayoutElement *element, const QRectF &rect) /* including file 'src/lineending.cpp', size 11536 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPLineEnding @@ -5424,7 +5431,7 @@ void QCPLineEnding::draw(QCPPainter *painter, const QCPVector2D &pos, double ang /* including file 'src/axis/axisticker.cpp', size 18664 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPAxisTicker @@ -5844,7 +5851,7 @@ double QCPAxisTicker::cleanMantissa(double input) const /* including file 'src/axis/axistickerdatetime.cpp', size 14443 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPAxisTickerDateTime @@ -6141,7 +6148,7 @@ double QCPAxisTickerDateTime::dateTimeToKey(const QDate date) /* including file 'src/axis/axistickertime.cpp', size 11747 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPAxisTickerTime @@ -6390,7 +6397,7 @@ void QCPAxisTickerTime::replaceUnit(QString &text, QCPAxisTickerTime::TimeUnit u /* including file 'src/axis/axistickerfixed.cpp', size 5583 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPAxisTickerFixed @@ -6491,8 +6498,8 @@ double QCPAxisTickerFixed::getTickStep(const QCPRange &range) /* end of 'src/axis/axistickerfixed.cpp' */ -/* including file 'src/axis/axistickertext.cpp', size 8653 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* including file 'src/axis/axistickertext.cpp', size 8661 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPAxisTickerText @@ -6562,7 +6569,7 @@ void QCPAxisTickerText::setTicks(const QMap &ticks) \see addTicks, addTick, clear */ -void QCPAxisTickerText::setTicks(const QVector &positions, const QVector labels) +void QCPAxisTickerText::setTicks(const QVector &positions, const QVector &labels) { clear(); addTicks(positions, labels); @@ -6600,7 +6607,7 @@ void QCPAxisTickerText::clear() \see addTicks, setTicks, clear */ -void QCPAxisTickerText::addTick(double position, QString label) +void QCPAxisTickerText::addTick(double position, const QString &label) { mTicks.insert(position, label); } @@ -6705,7 +6712,7 @@ QVector QCPAxisTickerText::createTickVector(double tickStep, const QCPRa /* including file 'src/axis/axistickerpi.cpp', size 11170 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPAxisTickerPi @@ -6992,7 +6999,7 @@ QString QCPAxisTickerPi::unicodeSubscript(int number) const /* including file 'src/axis/axistickerlog.cpp', size 7106 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPAxisTickerLog @@ -7132,8 +7139,8 @@ QVector QCPAxisTickerLog::createTickVector(double tickStep, const QCPRan /* end of 'src/axis/axistickerlog.cpp' */ -/* including file 'src/axis/axis.cpp', size 99397 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* including file 'src/axis/axis.cpp', size 99515 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -7653,10 +7660,14 @@ QCPLineEnding QCPAxis::upperEnding() const /*! Sets whether the axis uses a linear scale or a logarithmic scale. - Note that this method controls the coordinate transformation. You will likely also want to use a - logarithmic tick spacing and labeling, which can be achieved by setting an instance of \ref - QCPAxisTickerLog via \ref setTicker. See the documentation of \ref QCPAxisTickerLog about the - details of logarithmic axis tick creation. + Note that this method controls the coordinate transformation. For logarithmic scales, you will + likely also want to use a logarithmic tick spacing and labeling, which can be achieved by setting + the axis ticker to an instance of \ref QCPAxisTickerLog : + + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpaxisticker-log-creation + + See the documentation of \ref QCPAxisTickerLog about the details of logarithmic axis tick + creation. \ref setNumberPrecision */ @@ -9819,7 +9830,7 @@ void QCPAxisPainterPrivate::getMaxTickLabelSize(const QFont &font, const QString /* including file 'src/scatterstyle.cpp', size 17450 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPScatterStyle @@ -10293,7 +10304,7 @@ void QCPScatterStyle::drawShape(QCPPainter *painter, double x, double y) const //amalgamation: add datacontainer.cpp /* including file 'src/plottable.cpp', size 38845 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPSelectionDecorator @@ -11265,8 +11276,8 @@ void QCPAbstractPlottable::deselectEvent(bool *selectionStateChanged) /* end of 'src/plottable.cpp' */ -/* including file 'src/item.cpp', size 49269 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* including file 'src/item.cpp', size 49271 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPItemAnchor @@ -12411,7 +12422,7 @@ double QCPAbstractItem::rectDistance(const QRectF &rect, const QPointF &pos, boo QList lines; lines << QLineF(rect.topLeft(), rect.topRight()) << QLineF(rect.bottomLeft(), rect.bottomRight()) << QLineF(rect.topLeft(), rect.bottomLeft()) << QLineF(rect.topRight(), rect.bottomRight()); - double minDistSqr = std::numeric_limits::max(); + double minDistSqr = (std::numeric_limits::max)(); for (int i=0; i QCustomPlot::axisRects() const { @@ -15718,8 +15745,8 @@ void QCustomPlot::toPainter(QCPPainter *painter, int width, int height) //amalgamation: add plottable1d.cpp -/* including file 'src/colorgradient.cpp', size 24646 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* including file 'src/colorgradient.cpp', size 25342 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -16282,10 +16309,22 @@ void QCPColorGradient::updateColorBuffer() QMap::const_iterator it = mColorStops.lowerBound(position); if (it == mColorStops.constEnd()) // position is on or after last stop, use color of last stop { - mColorBuffer[i] = (it-1).value().rgba(); + if (useAlpha) + { + const QColor col = (it-1).value(); + const float alphaPremultiplier = col.alpha()/255.0f; // since we use QImage::Format_ARGB32_Premultiplied + mColorBuffer[i] = qRgba(col.red()*alphaPremultiplier, col.green()*alphaPremultiplier, col.blue()*alphaPremultiplier, col.alpha()); + } else + mColorBuffer[i] = (it-1).value().rgba(); } else if (it == mColorStops.constBegin()) // position is on or before first stop, use color of first stop { - mColorBuffer[i] = it.value().rgba(); + if (useAlpha) + { + const QColor col = it.value(); + const float alphaPremultiplier = col.alpha()/255.0f; // since we use QImage::Format_ARGB32_Premultiplied + mColorBuffer[i] = qRgba(col.red()*alphaPremultiplier, col.green()*alphaPremultiplier, col.blue()*alphaPremultiplier, col.alpha()); + } else + mColorBuffer[i] = it.value().rgba(); } else // position is in between stops (or on an intermediate stop), interpolate color { QMap::const_iterator high = it; @@ -16359,7 +16398,7 @@ void QCPColorGradient::updateColorBuffer() /* including file 'src/selectiondecorator-bracket.cpp', size 12313 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPSelectionDecoratorBracket @@ -16645,7 +16684,7 @@ QPointF QCPSelectionDecoratorBracket::getPixelCoordinates(const QCPPlottableInte /* including file 'src/layoutelements/layoutelement-axisrect.cpp', size 47584 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -17919,8 +17958,8 @@ void QCPAxisRect::wheelEvent(QWheelEvent *event) /* end of 'src/layoutelements/layoutelement-axisrect.cpp' */ -/* including file 'src/layoutelements/layoutelement-legend.cpp', size 31097 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* including file 'src/layoutelements/layoutelement-legend.cpp', size 31153 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPAbstractLegendItem @@ -18259,7 +18298,7 @@ QSize QCPPlottableLegendItem::minimumOuterSizeHint() const Use the methods \ref setFillOrder and \ref setWrap inherited from \ref QCPLayoutGrid to control in which order (column first or row first) the legend is filled up when calling \ref addItem, and - at which column or row wrapping occurs. + at which column or row wrapping occurs. The default fill order for legends is \ref foRowsFirst. By default, every QCustomPlot has one legend (\ref QCustomPlot::legend) which is placed in the inset layout of the main axis rect (\ref QCPAxisRect::insetLayout). To move the legend to another @@ -18836,7 +18875,7 @@ void QCPLegend::parentPlotInitialized(QCustomPlot *parentPlot) /* including file 'src/layoutelements/layoutelement-textelement.cpp', size 12761 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPTextElement @@ -19239,8 +19278,8 @@ QColor QCPTextElement::mainTextColor() const /* end of 'src/layoutelements/layoutelement-textelement.cpp' */ -/* including file 'src/layoutelements/layoutelement-colorscale.cpp', size 25770 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* including file 'src/layoutelements/layoutelement-colorscale.cpp', size 26246 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -19459,13 +19498,22 @@ void QCPColorScale::setDataRange(const QCPRange &dataRange) } /*! - Sets the scale type of the color scale, i.e. whether values are linearly associated with colors + Sets the scale type of the color scale, i.e. whether values are associated with colors linearly or logarithmically. It is equivalent to calling QCPColorMap::setDataScaleType on any of the connected color maps. It is also equivalent to directly accessing the \ref axis and setting its scale type with \ref QCPAxis::setScaleType. + Note that this method controls the coordinate transformation. For logarithmic scales, you will + likely also want to use a logarithmic tick spacing and labeling, which can be achieved by setting + the color scale's \ref axis ticker to an instance of \ref QCPAxisTickerLog : + + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpaxisticker-log-colorscale + + See the documentation of \ref QCPAxisTickerLog about the details of logarithmic axis tick + creation. + \see setDataRange, setGradient */ void QCPColorScale::setDataScaleType(QCPAxis::ScaleType scaleType) @@ -19899,8 +19947,8 @@ void QCPColorScaleAxisRectPrivate::axisSelectableChanged(QCPAxis::SelectablePart /* end of 'src/layoutelements/layoutelement-colorscale.cpp' */ -/* including file 'src/plottables/plottable-graph.cpp', size 73960 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* including file 'src/plottables/plottable-graph.cpp', size 74194 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPGraphData @@ -20259,7 +20307,14 @@ void QCPGraph::addData(double key, double value) mDataContainer->add(QCPGraphData(key, value)); } -/* inherits documentation from base class */ +/*! + Implements a selectTest specific to this plottable's point geometry. + + If \a details is not 0, it will be set to a \ref QCPDataSelection, describing the closest data + point to \a pos. + + \seebaseclassmethod \ref QCPAbstractPlottable::selectTest +*/ double QCPGraph::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const { if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty()) @@ -20857,11 +20912,11 @@ void QCPGraph::getOptimizedLineData(QVector *lineData, const QCPGr if (begin == end) return; int dataCount = end-begin; - int maxCount = std::numeric_limits::max(); + int maxCount = (std::numeric_limits::max)(); if (mAdaptiveSampling) { double keyPixelSpan = qAbs(keyAxis->coordToPixel(begin->key)-keyAxis->coordToPixel((end-1)->key)); - if (2*keyPixelSpan+2 < (double)std::numeric_limits::max()) + if (2*keyPixelSpan+2 < static_cast((std::numeric_limits::max)())) maxCount = 2*keyPixelSpan+2; } @@ -20958,7 +21013,7 @@ void QCPGraph::getOptimizedScatterData(QVector *scatterData, QCPGr } if (begin == end) return; int dataCount = end-begin; - int maxCount = std::numeric_limits::max(); + int maxCount = (std::numeric_limits::max)(); if (mAdaptiveSampling) { int keyPixelSpan = qAbs(keyAxis->coordToPixel(begin->key)-keyAxis->coordToPixel((end-1)->key)); @@ -21583,7 +21638,7 @@ double QCPGraph::pointDistance(const QPointF &pixelPoint, QCPGraphDataContainer: return -1.0; // calculate minimum distances to graph data points and find closestData iterator: - double minDistSqr = std::numeric_limits::max(); + double minDistSqr = (std::numeric_limits::max)(); // determine which key range comes into question, taking selection tolerance around pos into account: double posKeyMin, posKeyMax, dummy; pixelsToCoords(pixelPoint-QPointF(mParentPlot->selectionTolerance(), mParentPlot->selectionTolerance()), posKeyMin, dummy); @@ -21647,8 +21702,8 @@ int QCPGraph::findIndexBelowY(const QVector *data, double y) const /* end of 'src/plottables/plottable-graph.cpp' */ -/* including file 'src/plottables/plottable-curve.cpp', size 63527 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* including file 'src/plottables/plottable-curve.cpp', size 63742 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPCurveData @@ -22014,7 +22069,14 @@ void QCPCurve::addData(double key, double value) mDataContainer->add(QCPCurveData(0.0, key, value)); } -/* inherits documentation from base class */ +/*! + Implements a selectTest specific to this plottable's point geometry. + + If \a details is not 0, it will be set to a \ref QCPDataSelection, describing the closest data + point to \a pos. + + \seebaseclassmethod \ref QCPAbstractPlottable::selectTest +*/ double QCPCurve::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const { if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty()) @@ -23064,7 +23126,7 @@ double QCPCurve::pointDistance(const QPointF &pixelPoint, QCPCurveDataContainer: } // calculate minimum distances to curve data points and find closestData iterator: - double minDistSqr = std::numeric_limits::max(); + double minDistSqr = (std::numeric_limits::max)(); // iterate over found data points and then choose the one with the shortest distance to pos: QCPCurveDataContainer::const_iterator begin = mDataContainer->constBegin(); QCPCurveDataContainer::const_iterator end = mDataContainer->constEnd(); @@ -23096,8 +23158,8 @@ double QCPCurve::pointDistance(const QPointF &pixelPoint, QCPCurveDataContainer: /* end of 'src/plottables/plottable-curve.cpp' */ -/* including file 'src/plottables/plottable-bars.cpp', size 43512 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* including file 'src/plottables/plottable-bars.cpp', size 43725 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -23844,7 +23906,14 @@ QCPDataSelection QCPBars::selectTestRect(const QRectF &rect, bool onlySelectable return result; } -/* inherits documentation from base class */ +/*! + Implements a selectTest specific to this plottable's point geometry. + + If \a details is not 0, it will be set to a \ref QCPDataSelection, describing the closest data + point to \a pos. + + \seebaseclassmethod \ref QCPAbstractPlottable::selectTest +*/ double QCPBars::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const { Q_UNUSED(details) @@ -24264,8 +24333,8 @@ void QCPBars::connectBars(QCPBars *lower, QCPBars *upper) /* end of 'src/plottables/plottable-bars.cpp' */ -/* including file 'src/plottables/plottable-statisticalbox.cpp', size 28622 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* including file 'src/plottables/plottable-statisticalbox.cpp', size 28837 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPStatisticalBoxData @@ -24669,7 +24738,14 @@ QCPDataSelection QCPStatisticalBox::selectTestRect(const QRectF &rect, bool only return result; } -/* inherits documentation from base class */ +/*! + Implements a selectTest specific to this plottable's point geometry. + + If \a details is not 0, it will be set to a \ref QCPDataSelection, describing the closest data + point to \a pos. + + \seebaseclassmethod \ref QCPAbstractPlottable::selectTest +*/ double QCPStatisticalBox::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const { Q_UNUSED(details) @@ -24684,7 +24760,7 @@ double QCPStatisticalBox::selectTest(const QPointF &pos, bool onlySelectable, QV QCPStatisticalBoxDataContainer::const_iterator visibleBegin, visibleEnd; QCPStatisticalBoxDataContainer::const_iterator closestDataPoint = mDataContainer->constEnd(); getVisibleDataBounds(visibleBegin, visibleEnd); - double minDistSqr = std::numeric_limits::max(); + double minDistSqr = (std::numeric_limits::max)(); for (QCPStatisticalBoxDataContainer::const_iterator it=visibleBegin; it!=visibleEnd; ++it) { if (getQuartileBox(it).contains(pos)) // quartile box @@ -24919,7 +24995,7 @@ QVector QCPStatisticalBox::getWhiskerBarLines(QCPStatisticalBoxDataConta /* including file 'src/plottables/plottable-colormap.cpp', size 47881 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPColorMapData @@ -26053,8 +26129,8 @@ void QCPColorMap::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const /* end of 'src/plottables/plottable-colormap.cpp' */ -/* including file 'src/plottables/plottable-financial.cpp', size 42610 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* including file 'src/plottables/plottable-financial.cpp', size 42827 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPFinancialData @@ -26459,7 +26535,14 @@ QCPDataSelection QCPFinancial::selectTestRect(const QRectF &rect, bool onlySelec return result; } -/* inherits documentation from base class */ +/*! + Implements a selectTest specific to this plottable's point geometry. + + If \a details is not 0, it will be set to a \ref QCPDataSelection, describing the closest data + point to \a pos. + + \seebaseclassmethod \ref QCPAbstractPlottable::selectTest +*/ double QCPFinancial::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const { Q_UNUSED(details) @@ -26852,7 +26935,7 @@ double QCPFinancial::ohlcSelectTest(const QPointF &pos, const QCPFinancialDataCo QCPAxis *valueAxis = mValueAxis.data(); if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; } - double minDistSqr = std::numeric_limits::max(); + double minDistSqr = (std::numeric_limits::max)(); if (keyAxis->orientation() == Qt::Horizontal) { for (QCPFinancialDataContainer::const_iterator it=begin; it!=end; ++it) @@ -26899,7 +26982,7 @@ double QCPFinancial::candlestickSelectTest(const QPointF &pos, const QCPFinancia QCPAxis *valueAxis = mValueAxis.data(); if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; } - double minDistSqr = std::numeric_limits::max(); + double minDistSqr = (std::numeric_limits::max)(); if (keyAxis->orientation() == Qt::Horizontal) { for (QCPFinancialDataContainer::const_iterator it=begin; it!=end; ++it) @@ -27008,8 +27091,8 @@ QRectF QCPFinancial::selectionHitBox(QCPFinancialDataContainer::const_iterator i /* end of 'src/plottables/plottable-financial.cpp' */ -/* including file 'src/plottables/plottable-errorbar.cpp', size 37355 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* including file 'src/plottables/plottable-errorbar.cpp', size 37570 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPErrorBarsData @@ -27451,7 +27534,14 @@ int QCPErrorBars::findEnd(double sortKey, bool expandedRange) const return 0; } -/* inherits documentation from base class */ +/*! + Implements a selectTest specific to this plottable's point geometry. + + If \a details is not 0, it will be set to a \ref QCPDataSelection, describing the closest data + point to \a pos. + + \seebaseclassmethod \ref QCPAbstractPlottable::selectTest +*/ double QCPErrorBars::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const { if (!mDataPlottable) return -1; @@ -27870,7 +27960,7 @@ double QCPErrorBars::pointDistance(const QPointF &pixelPoint, QCPErrorBarsDataCo getVisibleDataBounds(begin, end, QCPDataRange(0, dataCount())); // calculate minimum distances to error backbones (whiskers are ignored for speed) and find closestData iterator: - double minDistSqr = std::numeric_limits::max(); + double minDistSqr = (std::numeric_limits::max)(); QVector backbones, whiskers; for (QCPErrorBarsDataContainer::const_iterator it=begin; it!=end; ++it) { @@ -27970,7 +28060,7 @@ bool QCPErrorBars::rectIntersectsLine(const QRectF &pixelRect, const QLineF &lin /* including file 'src/items/item-straightline.cpp', size 7592 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPItemStraightLine @@ -28151,7 +28241,7 @@ QPen QCPItemStraightLine::mainPen() const /* including file 'src/items/item-line.cpp', size 8498 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPItemLine @@ -28381,8 +28471,8 @@ QPen QCPItemLine::mainPen() const /* end of 'src/items/item-line.cpp' */ -/* including file 'src/items/item-curve.cpp', size 7159 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* including file 'src/items/item-curve.cpp', size 7248 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPItemCurve @@ -28492,9 +28582,12 @@ double QCPItemCurve::selectTest(const QPointF &pos, bool onlySelectable, QVarian QPainterPath cubicPath(startVec); cubicPath.cubicTo(startDirVec, endDirVec, endVec); - QPolygonF polygon = cubicPath.toSubpathPolygons().first(); + QList polygons = cubicPath.toSubpathPolygons(); + if (polygons.isEmpty()) + return -1; + const QPolygonF polygon = polygons.first(); QCPVector2D p(pos); - double minDistSqr = std::numeric_limits::max(); + double minDistSqr = (std::numeric_limits::max)(); for (int i=1; i &candidates) const; double getMantissa(double input, double *magnitude=0) const; double cleanMantissa(double input) const; + +private: + Q_DISABLE_COPY(QCPAxisTicker) + }; Q_DECLARE_METATYPE(QCPAxisTicker::TickStepStrategy) Q_DECLARE_METATYPE(QSharedPointer) @@ -1589,7 +1598,7 @@ Q_DECLARE_METATYPE(QSharedPointer) /* including file 'src/axis/axistickerdatetime.h', size 3289 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ class QCP_LIB_DECL QCPAxisTickerDateTime : public QCPAxisTicker { @@ -1630,7 +1639,7 @@ protected: /* including file 'src/axis/axistickertime.h', size 3542 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ class QCP_LIB_DECL QCPAxisTickerTime : public QCPAxisTicker { @@ -1682,7 +1691,7 @@ Q_DECLARE_METATYPE(QCPAxisTickerTime::TimeUnit) /* including file 'src/axis/axistickerfixed.h', size 3308 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ class QCP_LIB_DECL QCPAxisTickerFixed : public QCPAxisTicker { @@ -1723,8 +1732,8 @@ Q_DECLARE_METATYPE(QCPAxisTickerFixed::ScaleStrategy) /* end of 'src/axis/axistickerfixed.h' */ -/* including file 'src/axis/axistickertext.h', size 3085 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* including file 'src/axis/axistickertext.h', size 3090 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ class QCP_LIB_DECL QCPAxisTickerText : public QCPAxisTicker { @@ -1737,12 +1746,12 @@ public: // setters: void setTicks(const QMap &ticks); - void setTicks(const QVector &positions, const QVector labels); + void setTicks(const QVector &positions, const QVector &labels); void setSubTickCount(int subTicks); // non-virtual methods: void clear(); - void addTick(double position, QString label); + void addTick(double position, const QString &label); void addTicks(const QMap &ticks); void addTicks(const QVector &positions, const QVector &labels); @@ -1756,14 +1765,13 @@ protected: virtual int getSubTickCount(double tickStep) Q_DECL_OVERRIDE; virtual QString getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision) Q_DECL_OVERRIDE; virtual QVector createTickVector(double tickStep, const QCPRange &range) Q_DECL_OVERRIDE; - }; /* end of 'src/axis/axistickertext.h' */ /* including file 'src/axis/axistickerpi.h', size 3911 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ class QCP_LIB_DECL QCPAxisTickerPi : public QCPAxisTicker { @@ -1822,7 +1830,7 @@ Q_DECLARE_METATYPE(QCPAxisTickerPi::FractionStyle) /* including file 'src/axis/axistickerlog.h', size 2663 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ class QCP_LIB_DECL QCPAxisTickerLog : public QCPAxisTicker { @@ -1854,8 +1862,8 @@ protected: /* end of 'src/axis/axistickerlog.h' */ -/* including file 'src/axis/axis.h', size 20634 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* including file 'src/axis/axis.h', size 20698 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ class QCP_LIB_DECL QCPGrid :public QCPLayerable { @@ -2183,10 +2191,10 @@ protected: virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged) Q_DECL_OVERRIDE; virtual void deselectEvent(bool *selectionStateChanged) Q_DECL_OVERRIDE; // mouse events: - virtual void mousePressEvent(QMouseEvent *event, const QVariant &details); - virtual void mouseMoveEvent(QMouseEvent *event, const QPointF &startPos); - virtual void mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos); - virtual void wheelEvent(QWheelEvent *event); + virtual void mousePressEvent(QMouseEvent *event, const QVariant &details) Q_DECL_OVERRIDE; + virtual void mouseMoveEvent(QMouseEvent *event, const QPointF &startPos) Q_DECL_OVERRIDE; + virtual void mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos) Q_DECL_OVERRIDE; + virtual void wheelEvent(QWheelEvent *event) Q_DECL_OVERRIDE; // non-virtual methods: void setupTickVectors(); @@ -2283,7 +2291,7 @@ protected: /* including file 'src/scatterstyle.h', size 7275 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ class QCP_LIB_DECL QCPScatterStyle { @@ -2390,7 +2398,7 @@ Q_DECLARE_METATYPE(QCPScatterStyle::ScatterShape) /* including file 'src/datacontainer.h', size 4596 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ /*! \relates QCPDataContainer Returns whether the sort key of \a a is less than the sort key of \a b. @@ -2460,7 +2468,7 @@ protected: // include implementation in header since it is a class template: /* including file 'src/datacontainer.cpp', size 31349 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPDataContainer @@ -3234,8 +3242,8 @@ void QCPDataContainer::performAutoSqueeze() /* end of 'src/datacontainer.h' */ -/* including file 'src/plottable.h', size 8312 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* including file 'src/plottable.h', size 8433 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ class QCP_LIB_DECL QCPSelectionDecorator { @@ -3329,7 +3337,7 @@ public: void setSelectionDecorator(QCPSelectionDecorator *decorator); // introduced virtual methods: - virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const = 0; + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE = 0; // actually introduced in QCPLayerable as non-pure, but we want to force reimplementation for plottables virtual QCPPlottableInterface1D *interface1D() { return 0; } virtual QCPRange getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth) const = 0; virtual QCPRange getValueRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth, const QCPRange &inKeyRange=QCPRange()) const = 0; @@ -3392,7 +3400,7 @@ private: /* including file 'src/item.h', size 9384 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ class QCP_LIB_DECL QCPItemAnchor { @@ -3577,7 +3585,7 @@ private: /* including file 'src/core.h', size 14886 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ class QCP_LIB_DECL QCustomPlot : public QWidget { @@ -3845,7 +3853,7 @@ Q_DECLARE_METATYPE(QCustomPlot::RefreshPriority) /* including file 'src/plottable1d.h', size 4544 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ class QCPPlottableInterface1D { @@ -3904,8 +3912,8 @@ private: // include implementation in header since it is a class template: -/* including file 'src/plottable1d.cpp', size 22240 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* including file 'src/plottable1d.cpp', size 22361 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPPlottableInterface1D @@ -4279,6 +4287,9 @@ int QCPAbstractPlottable1D::findEnd(double sortKey, bool expandedRange point-like. Most subclasses will want to reimplement this method again, to provide a more accurate hit test based on the true data visualization geometry. + If \a details is not 0, it will be set to a \ref QCPDataSelection, describing the closest data point + to \a pos. + \seebaseclassmethod */ template @@ -4290,7 +4301,7 @@ double QCPAbstractPlottable1D::selectTest(const QPointF &pos, bool onl return -1; QCPDataSelection selectionResult; - double minDistSqr = std::numeric_limits::max(); + double minDistSqr = (std::numeric_limits::max)(); int minDistIndex = mDataContainer->size(); typename QCPDataContainer::const_iterator begin = mDataContainer->constBegin(); @@ -4425,7 +4436,7 @@ void QCPAbstractPlottable1D::drawPolyline(QCPPainter *painter, const Q /* including file 'src/colorgradient.h', size 6243 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ class QCP_LIB_DECL QCPColorGradient { @@ -4508,7 +4519,7 @@ Q_DECLARE_METATYPE(QCPColorGradient::GradientPreset) /* including file 'src/selectiondecorator-bracket.h', size 4442 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ class QCP_LIB_DECL QCPSelectionDecoratorBracket : public QCPSelectionDecorator { @@ -4577,7 +4588,7 @@ Q_DECLARE_METATYPE(QCPSelectionDecoratorBracket::BracketStyle) /* including file 'src/layoutelements/layoutelement-axisrect.h', size 7507 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ class QCP_LIB_DECL QCPAxisRect : public QCPLayoutElement { @@ -4703,7 +4714,7 @@ private: /* including file 'src/layoutelements/layoutelement-legend.h', size 10397 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ class QCP_LIB_DECL QCPAbstractLegendItem : public QCPLayoutElement { @@ -4921,7 +4932,7 @@ Q_DECLARE_METATYPE(QCPLegend::SelectablePart) /* including file 'src/layoutelements/layoutelement-textelement.h', size 5353 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ class QCP_LIB_DECL QCPTextElement : public QCPLayoutElement { @@ -5008,7 +5019,7 @@ private: /* including file 'src/layoutelements/layoutelement-colorscale.h', size 5923 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ class QCPColorScaleAxisRectPrivate : public QCPAxisRect @@ -5116,7 +5127,7 @@ private: /* including file 'src/plottables/plottable-graph.h', size 9294 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ class QCP_LIB_DECL QCPGraphData { @@ -5255,7 +5266,7 @@ Q_DECLARE_METATYPE(QCPGraph::LineStyle) /* including file 'src/plottables/plottable-curve.h', size 7409 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ class QCP_LIB_DECL QCPCurveData { @@ -5369,8 +5380,8 @@ Q_DECLARE_METATYPE(QCPCurve::LineStyle) /* end of 'src/plottables/plottable-curve.h' */ -/* including file 'src/plottables/plottable-bars.h', size 8924 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* including file 'src/plottables/plottable-bars.h', size 8933 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ class QCP_LIB_DECL QCPBarsGroup : public QObject { @@ -5392,7 +5403,7 @@ public: }; Q_ENUMS(SpacingType) - QCPBarsGroup(QCustomPlot *parentPlot); + explicit QCPBarsGroup(QCustomPlot *parentPlot); virtual ~QCPBarsGroup(); // getters: @@ -5558,7 +5569,7 @@ Q_DECLARE_METATYPE(QCPBars::WidthType) /* including file 'src/plottables/plottable-statisticalbox.h', size 7516 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ class QCP_LIB_DECL QCPStatisticalBoxData { @@ -5675,7 +5686,7 @@ protected: /* including file 'src/plottables/plottable-colormap.h', size 7070 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ class QCP_LIB_DECL QCPColorMapData { @@ -5811,7 +5822,7 @@ protected: /* including file 'src/plottables/plottable-financial.h', size 8622 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ class QCP_LIB_DECL QCPFinancialData { @@ -5950,7 +5961,7 @@ Q_DECLARE_METATYPE(QCPFinancial::ChartStyle) /* including file 'src/plottables/plottable-errorbar.h', size 7727 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ class QCP_LIB_DECL QCPErrorBarsData { @@ -6075,7 +6086,7 @@ protected: /* including file 'src/items/item-straightline.h', size 3117 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ class QCP_LIB_DECL QCPItemStraightLine : public QCPAbstractItem { @@ -6118,7 +6129,7 @@ protected: /* including file 'src/items/item-line.h', size 3407 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ class QCP_LIB_DECL QCPItemLine : public QCPAbstractItem { @@ -6168,7 +6179,7 @@ protected: /* including file 'src/items/item-curve.h', size 3379 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ class QCP_LIB_DECL QCPItemCurve : public QCPAbstractItem { @@ -6219,7 +6230,7 @@ protected: /* including file 'src/items/item-rect.h', size 3688 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ class QCP_LIB_DECL QCPItemRect : public QCPAbstractItem { @@ -6278,7 +6289,7 @@ protected: /* including file 'src/items/item-text.h', size 5554 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ class QCP_LIB_DECL QCPItemText : public QCPAbstractItem { @@ -6375,7 +6386,7 @@ protected: /* including file 'src/items/item-ellipse.h', size 3868 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ class QCP_LIB_DECL QCPItemEllipse : public QCPAbstractItem { @@ -6437,7 +6448,7 @@ protected: /* including file 'src/items/item-pixmap.h', size 4373 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ class QCP_LIB_DECL QCPItemPixmap : public QCPAbstractItem { @@ -6506,7 +6517,7 @@ protected: /* including file 'src/items/item-tracer.h', size 4762 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ class QCP_LIB_DECL QCPItemTracer : public QCPAbstractItem { @@ -6592,7 +6603,7 @@ Q_DECLARE_METATYPE(QCPItemTracer::TracerStyle) /* including file 'src/items/item-bracket.h', size 3969 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ class QCP_LIB_DECL QCPItemBracket : public QCPAbstractItem { From f08430c8125f832cd25cd9d908e0bd9ec804ae6f Mon Sep 17 00:00:00 2001 From: Collin Kidder Date: Sun, 14 Apr 2019 16:59:30 -0400 Subject: [PATCH 05/12] Add proper parent reference to the creation of DBC edit windows. This fixes an annoyance where the main window would gain focus improperly. --- dbc/dbcloadsavewindow.cpp | 2 +- dbc/dbcmaineditor.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dbc/dbcloadsavewindow.cpp b/dbc/dbcloadsavewindow.cpp index e4dcb5c..07f34ab 100644 --- a/dbc/dbcloadsavewindow.cpp +++ b/dbc/dbcloadsavewindow.cpp @@ -34,7 +34,7 @@ DBCLoadSaveWindow::DBCLoadSaveWindow(const QVector *frames, QWidget *p connect(ui->tableFiles, &QTableWidget::cellChanged, this, &DBCLoadSaveWindow::cellChanged); connect(ui->tableFiles, &QTableWidget::cellDoubleClicked, this, &DBCLoadSaveWindow::cellDoubleClicked); - editorWindow = new DBCMainEditor(frames); + editorWindow = new DBCMainEditor(frames, this); currentlyEditingFile = nullptr; installEventFilter(this); diff --git a/dbc/dbcmaineditor.cpp b/dbc/dbcmaineditor.cpp index 56a9899..62819e9 100644 --- a/dbc/dbcmaineditor.cpp +++ b/dbc/dbcmaineditor.cpp @@ -49,7 +49,7 @@ DBCMainEditor::DBCMainEditor( const QVector *frames, QWidget *parent) connect(ui->MessagesTable, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(onCustomMenuMessage(QPoint))); ui->MessagesTable->setContextMenuPolicy(Qt::CustomContextMenu); - sigEditor = new DBCSignalEditor(); + sigEditor = new DBCSignalEditor(this); installEventFilter(this); } From 1ff3426627befc91b943745e0f009b5e88a281ff Mon Sep 17 00:00:00 2001 From: Collin Kidder Date: Sun, 14 Apr 2019 17:00:14 -0400 Subject: [PATCH 06/12] Cause the program to properly exit when the main window is closed. Previously sometimes other opened windows would persist and that was annoying and not the expected behavior. --- mainwindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mainwindow.cpp b/mainwindow.cpp index 1471a72..15181e1 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -200,7 +200,6 @@ MainWindow::~MainWindow() delete model; delete elapsedTime; delete dbcHandler; - } //kill every sub window that could be open. At the moment a hard coded list @@ -249,6 +248,7 @@ void MainWindow::killWindow(QDialog *win) void MainWindow::exitApp() { this->close(); + QApplication::quit(); //forces the whole application to terminate when the main window is closed } void MainWindow::closeEvent(QCloseEvent *event) From b8c83bbfd75fbfbe885a4679ffcf073049e088de Mon Sep 17 00:00:00 2001 From: Collin Kidder Date: Sun, 14 Apr 2019 17:01:30 -0400 Subject: [PATCH 07/12] Fix parent for new graph window, fix empty graphs are impossible to selectively remove or edit, add associated signal to graphs so they bring up the proper signal in the list on the right in the editing window. --- re/graphingwindow.cpp | 18 +++++++++++++++++- re/graphingwindow.h | 1 + re/newgraphdialog.cpp | 33 +++++++++++++++++++++++++++++++-- re/newgraphdialog.h | 1 + 4 files changed, 50 insertions(+), 3 deletions(-) diff --git a/re/graphingwindow.cpp b/re/graphingwindow.cpp index b311623..7316fc5 100644 --- a/re/graphingwindow.cpp +++ b/re/graphingwindow.cpp @@ -1020,7 +1020,7 @@ void GraphingWindow::showParamsDialog(int idx = -1) { dbcHandler = DBCHandler::getReference(); - NewGraphDialog *thisDialog = new NewGraphDialog(dbcHandler); + NewGraphDialog *thisDialog = new NewGraphDialog(dbcHandler, this); if (idx > -1) { @@ -1096,7 +1096,23 @@ void GraphingWindow::createGraph(GraphParams ¶ms, bool createGraphParam) if (thisFrame.ID == params.ID) frameCache.append(thisFrame); } + //to fix weirdness where a graph that has no data won't be able to be edited, selected, or deleted properly + //we'll check for the condition that there is nothing to graph and add a single dummy frame to the cache + //that has all data bytes = 0. This allows the graph to be edited and deleted. No idea why you can't otherwise. + if (frameCache.count() == 0) + { + CANFrame dummy; + dummy.ID = params.ID; + dummy.bus = 0; + dummy.len = 8; + dummy.remote = false; + dummy.timestamp = 0; + for (int i = 0; i < 8; i++) dummy.data[i] = 0; + frameCache.append(dummy); + } + int numEntries = frameCache.count() / params.stride; + if (numEntries < 1) numEntries = 1; //could happen if stride is larger than frame count params.x.reserve(numEntries); params.y.reserve(numEntries); diff --git a/re/graphingwindow.h b/re/graphingwindow.h index cc79cbc..2d08220 100644 --- a/re/graphingwindow.h +++ b/re/graphingwindow.h @@ -26,6 +26,7 @@ public: QColor color; QCPGraph *ref; QString graphName; + DBC_SIGNAL *associatedSignal; //the below stuff is used for internal purposes only - code should be refactored so these can be private QVector x, y; double xbias; diff --git a/re/newgraphdialog.cpp b/re/newgraphdialog.cpp index 0297a39..1d0954e 100644 --- a/re/newgraphdialog.cpp +++ b/re/newgraphdialog.cpp @@ -32,6 +32,7 @@ NewGraphDialog::NewGraphDialog(DBCHandler *handler, QWidget *parent) : startBit = 0; dataLen = 1; + assocSignal = nullptr; loadMessages(); @@ -104,6 +105,9 @@ void NewGraphDialog::setParams(GraphParams ¶ms) ui->cbSigned->setChecked(params.isSigned); ui->cbIntel->setChecked(params.intelFormat); + assocSignal = params.associatedSignal; + qDebug() << "Signal addr: " << params.associatedSignal; + startBit = params.startBit; dataLen = params.numBits; ui->txtDataLen->setText(QString::number(dataLen)); @@ -113,6 +117,7 @@ void NewGraphDialog::setParams(GraphParams ¶ms) p.setColor(QPalette::Button, params.color); ui->colorSwatch->setPalette(p); loadMessages(); + loadSignals(0); drawBitfield(); } @@ -132,6 +137,8 @@ void NewGraphDialog::getParams(GraphParams ¶ms) params.startBit = startBit; params.numBits = dataLen; + params.associatedSignal = assocSignal; + //now catch stupidity and bring it to defaults if (params.mask == 0) params.mask = 0xFFFFFFFF; if (fabs(params.scale) < 0.00000001) params.scale = 1.0f; @@ -140,6 +147,7 @@ void NewGraphDialog::getParams(GraphParams ¶ms) void NewGraphDialog::loadMessages() { + DBC_MESSAGE *msg; ui->cbMessages->clear(); if (dbcHandler == NULL) return; if (dbcHandler->getFileCount() == 0) return; @@ -147,7 +155,16 @@ void NewGraphDialog::loadMessages() { for (int x = 0; x < dbcHandler->getFileByIdx(y)->messageHandler->getCount(); x++) { - ui->cbMessages->addItem(dbcHandler->getFileByIdx(y)->messageHandler->findMsgByIdx(x)->name); + msg = dbcHandler->getFileByIdx(y)->messageHandler->findMsgByIdx(x); + if (msg) + { + ui->cbMessages->addItem(msg->name); + if (assocSignal && msg->name == assocSignal->parentMessage->name) + { + ui->cbMessages->setCurrentIndex(ui->cbMessages->count() -1); + qDebug() << "Found my parent"; + } + } } } } @@ -160,12 +177,23 @@ void NewGraphDialog::loadSignals(int idx) //look it up based on index but by name is probably safer and this operation //is not time critical at all. DBC_MESSAGE *msg = dbcHandler->getFileByIdx(0)->messageHandler->findMsgByName(ui->cbMessages->currentText()); + DBC_SIGNAL *sig; if (msg == NULL) return; + ui->cbSignals->clear(); for (int x = 0; x < msg->sigHandler->getCount(); x++) { - ui->cbSignals->addItem(msg->sigHandler->findSignalByIdx(x)->name); + sig = msg->sigHandler->findSignalByIdx(x); + if (sig) + { + ui->cbSignals->addItem(sig->name); + if (assocSignal && sig->name == assocSignal->name) + { + ui->cbSignals->setCurrentIndex(ui->cbSignals->count() - 1); + qDebug() << "Found me"; + } + } } } @@ -242,4 +270,5 @@ void NewGraphDialog::copySignalToParamsUI() if (sig->valType == SIGNED_INT) ui->cbSigned->setChecked(true); else ui->cbSigned->setChecked(false); drawBitfield(); + assocSignal = sig; } diff --git a/re/newgraphdialog.h b/re/newgraphdialog.h index 365456d..1b8b20d 100644 --- a/re/newgraphdialog.h +++ b/re/newgraphdialog.h @@ -36,6 +36,7 @@ private: Ui::NewGraphDialog *ui; DBCHandler *dbcHandler; + DBC_SIGNAL *assocSignal; int startBit, dataLen; }; From 428eab683eb646c27a83876b14609a2f98062aed Mon Sep 17 00:00:00 2001 From: Collin Kidder Date: Sun, 14 Apr 2019 18:27:25 -0400 Subject: [PATCH 08/12] Add a new function to DBC handler to make it easier to find messages by name --- dbc/dbchandler.cpp | 15 +++++++++++++++ dbc/dbchandler.h | 1 + 2 files changed, 16 insertions(+) diff --git a/dbc/dbchandler.cpp b/dbc/dbchandler.cpp index b1353c2..6bf7c85 100644 --- a/dbc/dbchandler.cpp +++ b/dbc/dbchandler.cpp @@ -1472,6 +1472,21 @@ DBC_MESSAGE* DBCHandler::findMessage(const CANFrame &frame) return nullptr; } +/* + * As above, a real shortcut function that searches all files in order to try to find a message with the given name +*/ +DBC_MESSAGE* DBCHandler::findMessage(const QString msgName) +{ + DBC_MESSAGE *msg = nullptr; + for(int i = 0; i < loadedFiles.count(); i++) + { + DBCFile * file = getFileByIdx(i); + msg = file->messageHandler->findMsgByName(msgName); + if (msg) return msg; //if it's not null then we have a match so return it + } + return nullptr; //no match, tough luck, return null +} + int DBCHandler::getFileCount() { return loadedFiles.count(); diff --git a/dbc/dbchandler.h b/dbc/dbchandler.h index eb4a32b..ebe48e1 100644 --- a/dbc/dbchandler.h +++ b/dbc/dbchandler.h @@ -96,6 +96,7 @@ public: void removeAllFiles(); void swapFiles(int pos1, int pos2); DBC_MESSAGE* findMessage(const CANFrame &frame); + DBC_MESSAGE* findMessage(const QString msgName); int getFileCount(); DBCFile* getFileByIdx(int idx); DBCFile* getFileByName(QString name); From 74f234a9f72f5b8fdbea8517644a7dda192b782d Mon Sep 17 00:00:00 2001 From: Collin Kidder Date: Sun, 14 Apr 2019 18:28:12 -0400 Subject: [PATCH 09/12] Tweaks to the associated signal tracking. Now there is a label that tells you whether the parameters match the currently selected signal or not. --- re/newgraphdialog.cpp | 65 ++++++++++++++++++++++++++++++++++++++++--- re/newgraphdialog.h | 1 + ui/newgraphdialog.ui | 14 ++++++++++ 3 files changed, 76 insertions(+), 4 deletions(-) diff --git a/re/newgraphdialog.cpp b/re/newgraphdialog.cpp index 1d0954e..1e13110 100644 --- a/re/newgraphdialog.cpp +++ b/re/newgraphdialog.cpp @@ -29,6 +29,7 @@ NewGraphDialog::NewGraphDialog(DBCHandler *handler, QWidget *parent) : connect(ui->txtDataLen, SIGNAL(textChanged(QString)), this, SLOT(handleDataLenUpdate())); connect(ui->cbIntel, SIGNAL(toggled(bool)), this, SLOT(drawBitfield())); connect(ui->btnCopySignal, SIGNAL(clicked(bool)), this, SLOT(copySignalToParamsUI())); + connect(ui->cbSignals, SIGNAL(currentIndexChanged(int)), this, SLOT(drawBitfield())); startBit = 0; dataLen = 1; @@ -86,6 +87,56 @@ void NewGraphDialog::colorSwatchClick() } +//check whether the current values on the left match the signal selected on the right +void NewGraphDialog::checkSignalAgreement() +{ + GraphParams testingParams; + bool bAgree = true; + bool sigSigned = false; + DBC_SIGNAL *sig; + DBC_MESSAGE *msg; + + if (dbcHandler == nullptr) return; + if (dbcHandler->getFileCount() == 0) return; + + msg = dbcHandler->findMessage(ui->cbMessages->currentText()); + if (msg) + { + sig = msg->sigHandler->findSignalByName(ui->cbSignals->currentText()); + } + + if (sig) + { + if (sig->valType == SIGNED_INT) sigSigned = true; + + testingParams.ID = Utility::ParseStringToNum(ui->txtID->text()); + testingParams.bias = ui->txtBias->text().toFloat(); + testingParams.isSigned = ui->cbSigned->isChecked(); + testingParams.intelFormat = ui->cbIntel->isChecked(); + testingParams.scale = ui->txtScale->text().toFloat(); + testingParams.startBit = startBit; + testingParams.numBits = dataLen; + + if (testingParams.ID != msg->ID) bAgree = false; + if (abs(testingParams.bias - sig->bias) > 0.01) bAgree = false; + if (testingParams.isSigned != sigSigned) bAgree = false; + if (testingParams.intelFormat != sig->intelByteOrder) bAgree = false; + if (abs(testingParams.scale - sig->factor) > 0.01) bAgree = false; + if (testingParams.startBit != sig->startBit) bAgree = false; + if (testingParams.numBits != sig->signalSize) bAgree = false; + } + if (bAgree) + { + ui->lblMsgStatus->setText("Graph params match this signal"); + //assocSignal = sig; + } + else + { + ui->lblMsgStatus->setText("Signal and Graph Params do not match"); + //assocSignal = nullptr; //ya done broke it, null the associated signal since the user is changing things + } +} + void NewGraphDialog::clearParams() { ui->txtID->clear(); @@ -105,9 +156,6 @@ void NewGraphDialog::setParams(GraphParams ¶ms) ui->cbSigned->setChecked(params.isSigned); ui->cbIntel->setChecked(params.intelFormat); - assocSignal = params.associatedSignal; - qDebug() << "Signal addr: " << params.associatedSignal; - startBit = params.startBit; dataLen = params.numBits; ui->txtDataLen->setText(QString::number(dataLen)); @@ -116,9 +164,13 @@ void NewGraphDialog::setParams(GraphParams ¶ms) QPalette p = ui->colorSwatch->palette(); p.setColor(QPalette::Button, params.color); ui->colorSwatch->setPalette(p); + + assocSignal = params.associatedSignal; + loadMessages(); loadSignals(0); drawBitfield(); + checkSignalAgreement(); } void NewGraphDialog::getParams(GraphParams ¶ms) @@ -195,6 +247,7 @@ void NewGraphDialog::loadSignals(int idx) } } } + checkSignalAgreement(); } void NewGraphDialog::bitfieldClicked(int x,int y) @@ -203,7 +256,7 @@ void NewGraphDialog::bitfieldClicked(int x,int y) qDebug() << "Clicked bit: " << bit; startBit = bit; - drawBitfield(); + drawBitfield(); } void NewGraphDialog::drawBitfield() @@ -241,6 +294,8 @@ void NewGraphDialog::drawBitfield() } ui->gridData->updateData((unsigned char *)&bitField, true); + + checkSignalAgreement(); } void NewGraphDialog::handleDataLenUpdate() @@ -249,6 +304,7 @@ void NewGraphDialog::handleDataLenUpdate() if (dataLen < 1) dataLen = 1; if (dataLen > 63) dataLen = 63; drawBitfield(); + checkSignalAgreement(); } void NewGraphDialog::copySignalToParamsUI() @@ -271,4 +327,5 @@ void NewGraphDialog::copySignalToParamsUI() else ui->cbSigned->setChecked(false); drawBitfield(); assocSignal = sig; + checkSignalAgreement(); } diff --git a/re/newgraphdialog.h b/re/newgraphdialog.h index 1b8b20d..9200f47 100644 --- a/re/newgraphdialog.h +++ b/re/newgraphdialog.h @@ -33,6 +33,7 @@ private slots: private: bool eventFilter(QObject *obj, QEvent *event); + void checkSignalAgreement(); Ui::NewGraphDialog *ui; DBCHandler *dbcHandler; diff --git a/ui/newgraphdialog.ui b/ui/newgraphdialog.ui index 6ee8ae2..c99f7c1 100644 --- a/ui/newgraphdialog.ui +++ b/ui/newgraphdialog.ui @@ -241,6 +241,20 @@ + + + + + 12 + 75 + true + + + + + + + From 1fbd381df7a80acb7b3464062e5ff1e26d3b4630 Mon Sep 17 00:00:00 2001 From: Collin Kidder Date: Sun, 14 Apr 2019 20:01:12 -0400 Subject: [PATCH 10/12] A few improvements to automatic file format detection. --- framefileio.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/framefileio.cpp b/framefileio.cpp index 8adbff2..aa38478 100644 --- a/framefileio.cpp +++ b/framefileio.cpp @@ -965,7 +965,7 @@ bool FrameFileIO::isPCANFile(QString filename) line = inFile->readLine(); if (line.startsWith(";$FILEVERSION=2.0")) fileVersion = 2; if (line.startsWith(';')) continue; - if (line.length() > 2) + if (line.length() > 41) { if (fileVersion == 1) { @@ -975,6 +975,7 @@ bool FrameFileIO::isPCANFile(QString filename) int len = line.mid(38,1).toInt(); if (len > - 1 && len < 9) isMatch = true; } + else isMatch = false; } else if (fileVersion == 2) @@ -983,11 +984,12 @@ bool FrameFileIO::isPCANFile(QString filename) if (id > 0 && id < 0x1FFFFFFF) { int len = line.mid(37,2).trimmed().toInt(); - if (len > -1 && len < 9) + if (len > 0 && len < 9) { QList tokens = line.mid(40, len * 3).split(' '); if (tokens.length() == len) isMatch = true; } + else isMatch = false; } } } @@ -1052,7 +1054,7 @@ bool FrameFileIO::loadPCANFile(QString filename, QVector* frames) line = inFile->readLine(); if (line.startsWith(";$FILEVERSION=2.0")) fileVersion = 2; if (line.startsWith(';')) continue; - if (line.length() > 2) + if (line.length() > 41) { if (fileVersion == 1) { @@ -1131,7 +1133,7 @@ bool FrameFileIO::isCanalyzerASC(QString filename) QFile *inFile = new QFile(filename); QByteArray line; int lineCounter = 0; - bool isMatch = false; + bool isMatch = true; bool inHeader = true; QList tokens; @@ -1165,12 +1167,12 @@ bool FrameFileIO::isCanalyzerASC(QString filename) if (tokens.length() > 5) { uint64_t time = (uint64_t)(tokens[0].toDouble() * (double)1000000.0); - if (time <= 0) continue; + if (time <= 0) isMatch = false; int id = tokens[2].toUInt(NULL, 16); - if (id < 1) continue; + if (id < 1 || id > 0x1FFFFFFF) isMatch = false; int len = tokens[5].toUInt(); - if (len < 0 || len > 8) continue; - if (tokens.count() > len + 6) isMatch = true; + if (len < 0 || len > 8) isMatch = false; + if (tokens.count() < (len + 6)) isMatch = false; } else isMatch = false; } From 2e4e6d5ba1ee0b0ea22e9e3dddd81e2014e215b8 Mon Sep 17 00:00:00 2001 From: Collin Kidder Date: Sun, 14 Apr 2019 20:08:49 -0400 Subject: [PATCH 11/12] Fix to code that allows for pseudo frame sending without any actual CAN buses connected. --- connections/canconmanager.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/connections/canconmanager.cpp b/connections/canconmanager.cpp index 014248f..2b285b4 100644 --- a/connections/canconmanager.cpp +++ b/connections/canconmanager.cpp @@ -94,10 +94,10 @@ void CANConManager::refreshCanList() if (mConns.count() == 0) { + tempFrames.clear(); //TODO: Seems to crash under heavy load. Find out why. - if(buslessFrames.size()) { - tempFrames.clear(); - tempFrames.append(buslessFrames); //make a copy and pass that copy + if(buslessFrames.size()) { + tempFrames = buslessFrames; //make a copy and pass that copy buslessFrames.clear(); //delete all frames from the original emit framesReceived(nullptr, tempFrames); } From 4394f56b0ef102ccc05f7b301f797afc2bcbec84 Mon Sep 17 00:00:00 2001 From: Collin Kidder Date: Sun, 14 Apr 2019 20:09:25 -0400 Subject: [PATCH 12/12] Increment the version number --- config.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.h b/config.h index b072063..c9a1b1c 100644 --- a/config.h +++ b/config.h @@ -1,7 +1,7 @@ #ifndef CONFIG #define CONFIG -#define VERSION 196 +#define VERSION 197 //try to keep this in sync. //SavvyCAN will complain if you connect a GVRET board with a revision