Browse Source

Almost finished flexible data view, only minor improvements needed

QGC4.4
pixhawk 15 years ago
parent
commit
cd785b3de2
  1. 147
      src/ui/QGCDataPlot2D.cc
  2. 81
      src/ui/QGCDataPlot2D.ui
  3. 81
      src/ui/linechart/IncrementalPlot.cc
  4. 19
      src/ui/linechart/IncrementalPlot.h

147
src/ui/QGCDataPlot2D.cc

@ -34,6 +34,8 @@ QGCDataPlot2D::QGCDataPlot2D(QWidget *parent) :
connect(ui->reloadButton, SIGNAL(clicked()), this, SLOT(reloadFile())); connect(ui->reloadButton, SIGNAL(clicked()), this, SLOT(reloadFile()));
connect(ui->savePlotButton, SIGNAL(clicked()), this, SLOT(savePlot())); connect(ui->savePlotButton, SIGNAL(clicked()), this, SLOT(savePlot()));
connect(ui->printButton, SIGNAL(clicked()), this, SLOT(print())); connect(ui->printButton, SIGNAL(clicked()), this, SLOT(print()));
connect(ui->legendCheckBox, SIGNAL(clicked(bool)), plot, SLOT(showLegend(bool)));
connect(ui->style, SIGNAL(currentIndexChanged(QString)), plot, SLOT(setStyleText(QString)));
} }
void QGCDataPlot2D::reloadFile() void QGCDataPlot2D::reloadFile()
@ -214,6 +216,24 @@ void QGCDataPlot2D::loadRawLog(QString file, QString xAxisName, QString yAxisFil
loadCsvLog(logFile->fileName(), xAxisName, yAxisFilter); loadCsvLog(logFile->fileName(), xAxisName, yAxisFilter);
} }
/**
* This function loads a CSV file into the plot. It tries to assign the dimension names
* based on the first data row and tries to guess the separator char.
*
* @param file Name of the file to open
* @param xAxisName Optional paramater. If given, the x axis dimension will be selected to match this string
* @param yAxisFilter Optional parameter. If given, only data dimension names present in the filter string will be
* plotted
*
* @code
*
* QString file = "/home/user/datalog.txt"; // With header: x<tab>y<tab>z
* QString xAxis = "x";
* QString yAxis = "z";
*
* // Plotted result will be x vs z with y ignored.
* @endcode
*/
void QGCDataPlot2D::loadCsvLog(QString file, QString xAxisName, QString yAxisFilter) void QGCDataPlot2D::loadCsvLog(QString file, QString xAxisName, QString yAxisFilter)
{ {
if (logFile != NULL) if (logFile != NULL)
@ -235,9 +255,44 @@ void QGCDataPlot2D::loadCsvLog(QString file, QString xAxisName, QString yAxisFil
// First line is header // First line is header
QString header = in.readLine(); QString header = in.readLine();
QString separator = "\t";
qDebug() << "READING CSV:" << header; bool charRead = false;
QString separator = "";
QList<QChar> sepCandidates;
sepCandidates << '\t';
sepCandidates << ',';
sepCandidates << ';';
sepCandidates << ' ';
sepCandidates << '~';
sepCandidates << '|';
// Iterate until separator is found
// or full header is parsed
for (int i = 0; i < header.length(); i++)
{
if (sepCandidates.contains(header.at(i)))
{
// Separator found
if (charRead)
{
separator += header[i];
}
}
else
{
// Char found
charRead = true;
// If the separator is not empty, this char
// has been read after a separator, so detection
// is now complete
if (separator != "") break;
}
}
QString out = separator;
out.replace("\t", "<tab>");
ui->filenameLabel->setText(file.split("/").last().split("\\").last()+" Separator: \""+out+"\"");
//qDebug() << "READING CSV:" << header;
// Clear plot // Clear plot
plot->removeData(); plot->removeData();
@ -245,7 +300,7 @@ void QGCDataPlot2D::loadCsvLog(QString file, QString xAxisName, QString yAxisFil
QVector<double> xValues; QVector<double> xValues;
QMap<QString, QVector<double>* > yValues; QMap<QString, QVector<double>* > yValues;
QStringList curveNames = header.split(separator); QStringList curveNames = header.split(separator, QString::SkipEmptyParts);
QString curveName; QString curveName;
// Clear UI elements // Clear UI elements
@ -254,73 +309,89 @@ void QGCDataPlot2D::loadCsvLog(QString file, QString xAxisName, QString yAxisFil
int curveNameIndex = 0; int curveNameIndex = 0;
int xValueIndex = curveNames.indexOf(xAxisName); //int xValueIndex = curveNames.indexOf(xAxisName);
if (xValueIndex < 0 || xValueIndex > (curveNames.size() - 1)) xValueIndex = 0;
QString xAxisFilter;
if (xAxisName == "")
{
xAxisFilter = curveNames.first();
}
else
{
xAxisFilter = xAxisName;
}
foreach(curveName, curveNames) foreach(curveName, curveNames)
{ {
if (curveNameIndex != xValueIndex) ui->xAxis->addItem(curveName);
if (curveName != xAxisFilter)
{ {
// FIXME Add check for y value filter if ((yAxisFilter == "") || yAxisFilter.contains(curveName))
if ((ui->yAxis->text() == "") && yValues.contains(curveName))
{ {
yValues.insert(curveName, new QVector<double>()); yValues.insert(curveName, new QVector<double>());
ui->xAxis->addItem(curveName);
// Add separator starting with second item // Add separator starting with second item
if (curveNameIndex > 0 && curveNameIndex < curveNames.size()) if (curveNameIndex > 0 && curveNameIndex < curveNames.size())
{ {
ui->yAxis->setText(ui->yAxis->text()+"|"); ui->yAxis->setText(ui->yAxis->text()+"|");
} }
ui->yAxis->setText(ui->yAxis->text()+curveName); ui->yAxis->setText(ui->yAxis->text()+curveName);
curveNameIndex++;
} }
} }
curveNameIndex++;
} }
// Select current axis in UI
ui->xAxis->setCurrentIndex(curveNames.indexOf(xAxisFilter));
// Read data // Read data
double x,y; double x,y;
while (!in.atEnd()) { while (!in.atEnd())
{
QString line = in.readLine(); QString line = in.readLine();
QStringList values = line.split(separator); QStringList values = line.split(separator, QString::SkipEmptyParts);
bool okx; foreach(curveName, curveNames)
x = values.at(xValueIndex).toDouble(&okx);
if(okx)
{ {
// Append X value bool okx;
xValues.append(x); if (curveName == xAxisFilter)
QString yStr; {
bool oky; // X AXIS HANDLING
int yCount = 0; // Take this value as x if it is selected
foreach(yStr, values) x = values.at(curveNames.indexOf(curveName)).toDouble(&okx);
xValues.append(x - 1270125570000LL);
qDebug() << "x" << x - 1270125570000LL;
}
else
{ {
// We have already x, so only handle // Y AXIS HANDLING
// true y values
if (yCount != xValueIndex && yCount < curveNames.size()) if(yAxisFilter == "" || yAxisFilter.contains(curveName))
{ {
y = yStr.toDouble(&oky); // Only append y values where a valid x value is present
// Append one of the Y values if (yValues.value(curveName)->size() == xValues.size() - 1)
yValues.value(curveNames.at(yCount))->append(y); {
bool oky;
int curveNameIndex = curveNames.indexOf(curveName);
if (values.size() > curveNameIndex)
{
y = values.at(curveNameIndex).toDouble(&oky);
yValues.value(curveName)->append(y);
}
}
} }
yCount++;
} }
} }
} }
// Add data array of each curve to the plot at once (fast)
QVector<double>* yCurve; // Iterates through all x-y curve combinations
int yCurveIndex = 0; for (int i = 0; i < yValues.size(); i++)
foreach(yCurve, yValues)
{ {
plot->appendData(yValues.keys().at(yCurveIndex), xValues.data(), yCurve->data(), xValues.size()); plot->appendData(yValues.keys().at(i), xValues.data(), yValues.values().at(i)->data(), xValues.size());
yCurveIndex++;
} }
} }
@ -339,7 +410,7 @@ void QGCDataPlot2D::loadCsvLog(QString file, QString xAxisName, QString yAxisFil
* the match of the regression. * the match of the regression.
* @return 1 on success, 0 on failure (e.g. because of infinite slope) * @return 1 on success, 0 on failure (e.g. because of infinite slope)
*/ */
int QGCDataPlot2D::linearRegression(double *x,double *y,int n,double *a,double *b,double *r) int QGCDataPlot2D::linearRegression(double* x,double* y,int n,double* a,double* b,double* r)
{ {
int i; int i;
double sumx=0,sumy=0,sumx2=0,sumy2=0,sumxy=0; double sumx=0,sumy=0,sumx2=0,sumy2=0,sumxy=0;

81
src/ui/QGCDataPlot2D.ui

@ -6,14 +6,14 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>736</width> <width>807</width>
<height>308</height> <height>308</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
<string>Form</string> <string>Form</string>
</property> </property>
<layout class="QGridLayout" name="gridLayout" columnstretch="2,10,2,0,0,0,0,0,0,0,0,0"> <layout class="QGridLayout" name="gridLayout" columnstretch="2,10,2,0,0,0,0,0,0,0,0,0,0">
<item row="0" column="0" colspan="2"> <item row="0" column="0" colspan="2">
<widget class="QLabel" name="label_2"> <widget class="QLabel" name="label_2">
<property name="text"> <property name="text">
@ -43,24 +43,74 @@
</item> </item>
<item> <item>
<property name="text"> <property name="text">
<string>Dots</string> <string>Only crosses</string>
</property> </property>
</item> </item>
<item> <item>
<property name="text"> <property name="text">
<string>Lines</string> <string>Only circles</string>
</property>
</item>
<item>
<property name="text">
<string>Only dots</string>
</property>
</item>
<item>
<property name="text">
<string>Lines and crosses</string>
</property>
</item>
<item>
<property name="text">
<string>Lines and circles</string>
</property>
</item>
<item>
<property name="text">
<string>Lines and dots</string>
</property>
</item>
<item>
<property name="text">
<string>Dotted lines and crosses</string>
</property>
</item>
<item>
<property name="text">
<string>Dotted lines and circles</string>
</property>
</item>
<item>
<property name="text">
<string>Dotted lines and dots</string>
</property>
</item>
<item>
<property name="text">
<string>Dashed lines and crosses</string>
</property>
</item>
<item>
<property name="text">
<string>Dashed lines and circles</string>
</property>
</item>
<item>
<property name="text">
<string>Dashed lines and dots</string>
</property> </property>
</item> </item>
</widget> </widget>
</item> </item>
<item row="0" column="8"> <item row="0" column="9">
<widget class="QPushButton" name="reloadButton"> <widget class="QPushButton" name="reloadButton">
<property name="text"> <property name="text">
<string>Reload</string> <string>Reload</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="0" column="9"> <item row="0" column="10">
<spacer name="horizontalSpacer_2"> <spacer name="horizontalSpacer_2">
<property name="orientation"> <property name="orientation">
<enum>Qt::Horizontal</enum> <enum>Qt::Horizontal</enum>
@ -73,14 +123,14 @@
</property> </property>
</spacer> </spacer>
</item> </item>
<item row="0" column="10"> <item row="0" column="11">
<widget class="QPushButton" name="savePlotButton"> <widget class="QPushButton" name="savePlotButton">
<property name="text"> <property name="text">
<string>Save Plot</string> <string>Save Plot</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="0" colspan="12"> <item row="1" column="0" colspan="13">
<widget class="QFrame" name="plotFrame"> <widget class="QFrame" name="plotFrame">
<property name="frameShape"> <property name="frameShape">
<enum>QFrame::StyledPanel</enum> <enum>QFrame::StyledPanel</enum>
@ -123,7 +173,7 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="9"> <item row="2" column="10">
<spacer name="horizontalSpacer"> <spacer name="horizontalSpacer">
<property name="orientation"> <property name="orientation">
<enum>Qt::Horizontal</enum> <enum>Qt::Horizontal</enum>
@ -136,21 +186,21 @@
</property> </property>
</spacer> </spacer>
</item> </item>
<item row="0" column="11"> <item row="0" column="12">
<widget class="QPushButton" name="printButton"> <widget class="QPushButton" name="printButton">
<property name="text"> <property name="text">
<string>Print</string> <string>Print</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="11"> <item row="2" column="12">
<widget class="QPushButton" name="saveCsvButton"> <widget class="QPushButton" name="saveCsvButton">
<property name="text"> <property name="text">
<string>Save CSV</string> <string>Save CSV</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="8"> <item row="2" column="9">
<widget class="QPushButton" name="selectFileButton"> <widget class="QPushButton" name="selectFileButton">
<property name="text"> <property name="text">
<string>Select file</string> <string>Select file</string>
@ -164,6 +214,13 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="0" column="8">
<widget class="QCheckBox" name="legendCheckBox">
<property name="text">
<string>Legend</string>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
<resources/> <resources/>

81
src/ui/linechart/IncrementalPlot.cc

@ -62,6 +62,7 @@ IncrementalPlot::IncrementalPlot(QWidget *parent):
setFrameStyle(QFrame::NoFrame); setFrameStyle(QFrame::NoFrame);
setLineWidth(0); setLineWidth(0);
setStyleText("solid crosses");
setCanvasLineWidth(2); setCanvasLineWidth(2);
plotLayout()->setAlignCanvasToScales(true); plotLayout()->setAlignCanvasToScales(true);
@ -84,6 +85,7 @@ IncrementalPlot::IncrementalPlot(QWidget *parent):
zoomer->setRubberBandPen(QPen(Qt::red, 2, Qt::DotLine)); zoomer->setRubberBandPen(QPen(Qt::red, 2, Qt::DotLine));
zoomer->setTrackerPen(QPen(Qt::red)); zoomer->setTrackerPen(QPen(Qt::red));
//zoomer->setZoomBase(QwtDoubleRect()); //zoomer->setZoomBase(QwtDoubleRect());
legend = NULL;
colors = QList<QColor>(); colors = QList<QColor>();
nextColor = 0; nextColor = 0;
@ -117,6 +119,75 @@ IncrementalPlot::~IncrementalPlot()
} }
void IncrementalPlot::showLegend(bool show)
{
if (show)
{
if (legend == NULL)
{
legend = new QwtLegend;
legend->setFrameStyle(QFrame::Box);
}
insertLegend(legend, QwtPlot::RightLegend);
}
else
{
delete legend;
legend = NULL;
}
replot();
}
/**
* Set datapoint and line style. This interface is intented
* to be directly connected to the UI and allows to parse
* human-readable, textual descriptions into plot specs.
*
* Data points: Either "circles", "crosses" or the default "dots"
* Lines: Either "dotted", ("solid"/"line") or no lines if not used
*
* No special formatting is needed, as long as the keywords are contained
* in the string. Lower/uppercase is ignored as well.
*
* @param style Formatting string for line/data point style
*/
void IncrementalPlot::setStyleText(QString style)
{
foreach (QwtPlotCurve* curve, d_curve)
{
// Style of datapoints
if (style.toLower().contains("circles"))
{
curve->setSymbol(QwtSymbol(QwtSymbol::Ellipse,
QBrush(curve->pen().color()), curve->pen(), QSize(5, 5)) );
}
else if (style.toLower().contains("crosses"))
{
curve->setSymbol(QwtSymbol(QwtSymbol::XCross,
QBrush(curve->pen().color()), curve->pen(), QSize(5, 5)) );
}
else // Always show dots (style.toLower().contains("dots"))
{
curve->setSymbol(QwtSymbol(QwtSymbol::Rect,
QBrush(curve->pen().color()), curve->pen(), QSize(1, 1)) );
}
// Style of lines
if (style.toLower().contains("dotted"))
{
curve->setStyle(QwtPlotCurve::Dots);
}
else if (style.toLower().contains("line") || style.toLower().contains("solid"))
{
curve->setStyle(QwtPlotCurve::Lines);
}
else
{
curve->setStyle(QwtPlotCurve::NoCurve);
}
}
}
void IncrementalPlot::resetScaling() void IncrementalPlot::resetScaling()
{ {
xmin = 0; xmin = 0;
@ -233,11 +304,11 @@ void IncrementalPlot::appendData(QString key, double *x, double *y, int size)
canvas()->setPaintAttribute(QwtPlotCanvas::PaintCached, false); canvas()->setPaintAttribute(QwtPlotCanvas::PaintCached, false);
// FIXME Check if here all curves should be drawn // FIXME Check if here all curves should be drawn
// QwtPlotCurve* plotCurve; // QwtPlotCurve* plotCurve;
// foreach(plotCurve, d_curve) // foreach(plotCurve, d_curve)
// { // {
// plotCurve->draw(0, curve->dataSize()-1); // plotCurve->draw(0, curve->dataSize()-1);
// } // }
curve->draw(curve->dataSize() - size, curve->dataSize() - 1); curve->draw(curve->dataSize() - size, curve->dataSize() - 1);
canvas()->setPaintAttribute(QwtPlotCanvas::PaintCached, cacheMode); canvas()->setPaintAttribute(QwtPlotCanvas::PaintCached, cacheMode);

19
src/ui/linechart/IncrementalPlot.h

@ -4,6 +4,7 @@
#include <QTimer> #include <QTimer>
#include <qwt_array.h> #include <qwt_array.h>
#include <qwt_plot.h> #include <qwt_plot.h>
#include <qwt_legend.h>
#include <QMap> #include <QMap>
#include "ScrollZoomer.h" #include "ScrollZoomer.h"
@ -38,12 +39,6 @@ public:
IncrementalPlot(QWidget *parent = NULL); IncrementalPlot(QWidget *parent = NULL);
virtual ~IncrementalPlot(); virtual ~IncrementalPlot();
void appendData(QString key, double x, double y);
void appendData(QString key, double *x, double *y, int size);
void resetScaling();
void removeData();
/** @brief Get color map of this plot */ /** @brief Get color map of this plot */
QList<QColor> IncrementalPlot::getColorMap(); QList<QColor> IncrementalPlot::getColorMap();
/** @brief Get next color of color map */ /** @brief Get next color of color map */
@ -51,10 +46,22 @@ public:
/** @brief Get color for curve id */ /** @brief Get color for curve id */
QColor IncrementalPlot::getColorForCurve(QString id); QColor IncrementalPlot::getColorForCurve(QString id);
public slots:
void appendData(QString key, double x, double y);
void appendData(QString key, double *x, double *y, int size);
void resetScaling();
void removeData();
/** @brief Show the plot legend */
void showLegend(bool show);
/** @brief Set new plot style */
void setStyleText(QString style);
protected: protected:
QList<QColor> colors; QList<QColor> colors;
int nextColor; int nextColor;
ScrollZoomer* zoomer; ScrollZoomer* zoomer;
QwtLegend* legend;
double xmin; double xmin;
double xmax; double xmax;
double ymin; double ymin;

Loading…
Cancel
Save