From b08960f5043d7649a601ed19f7d382e0ae031ea1 Mon Sep 17 00:00:00 2001 From: mgrojo Date: Sun, 7 Jan 2018 14:54:29 +0100 Subject: [PATCH] Plot: Respect the "Order by" clause when plotting The tables/queries sorted by X are drawn using QCPGraph as before. Since QCPGraph does automatically sort by X, we change to QCPCurve that requires a third data vector to reflect the order. We get that from the current row order. In the case of curves, only None and Line is supported as line style. Since the order is now important for the plot, it is automatically updated whenever the user sorts by another column in the browsed table. This addresses issue #821 and indirectly fixes the problem of incorrect point->row selection link when the table is not sorted by X, reported in issue #838. --- src/MainWindow.cpp | 2 ++ src/PlotDock.cpp | 75 +++++++++++++++++++++++++++++++++++++--------- 2 files changed, 63 insertions(+), 14 deletions(-) diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index d824ac20..d77281b5 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -1635,6 +1635,8 @@ void MainWindow::browseTableHeaderClicked(int logicalindex) // select the first item in the column so the header is bold // we might try to select the last selected item ui->dataTable->setCurrentIndex(ui->dataTable->currentIndex().sibling(0, logicalindex)); + + plotDock->updatePlot(m_browseTableModel, &browseTableSettings[currentlyBrowsedTableName()]); } void MainWindow::resizeEvent(QResizeEvent*) diff --git a/src/PlotDock.cpp b/src/PlotDock.cpp index 03e208cf..49959798 100644 --- a/src/PlotDock.cpp +++ b/src/PlotDock.cpp @@ -195,7 +195,7 @@ void PlotDock::updatePlot(SqliteTableModel* model, BrowseDataTableSettings* sett QStringList yAxisLabels; // Clear graphs and axis labels - ui->plotWidget->clearGraphs(); + ui->plotWidget->clearPlottables(); ui->plotWidget->xAxis->setLabel(QString()); ui->plotWidget->yAxis->setLabel(QString()); @@ -231,17 +231,16 @@ void PlotDock::updatePlot(SqliteTableModel* model, BrowseDataTableSettings* sett // leading 16 bit are column index uint itemdata = item->data(0, Qt::UserRole).toUInt(); int column = itemdata >> 16; - QCPGraph* graph = ui->plotWidget->addGraph(); - graph->setPen(QPen(item->backgroundColor(PlotColumnY))); - graph->setSelectable (QCP::stDataRange); + bool isSorted = true; // prepare the data vectors for qcustomplot // possible improvement might be a QVector subclass that directly // access the model data, to save memory, we are copying here - QVector xdata(model->rowCount()), ydata(model->rowCount()); + QVector xdata(model->rowCount()), ydata(model->rowCount()), tdata(model->rowCount()); for(int i = 0; i < model->rowCount(); ++i) { + tdata[i] = i; // convert x type axis if it's datetime if(xtype == QVariant::DateTime) { @@ -258,6 +257,9 @@ void PlotDock::updatePlot(SqliteTableModel* model, BrowseDataTableSettings* sett xdata[i] = model->data(model->index(i, x)).toDouble(); } + if (i != 0) + isSorted &= (xdata[i-1] <= xdata[i]); + // Get the y value for this point. If the selected column is -1, i.e. the row number, just use the current row number from the loop // instead of retrieving some value from the model. QVariant pointdata; @@ -271,14 +273,38 @@ void PlotDock::updatePlot(SqliteTableModel* model, BrowseDataTableSettings* sett else ydata[i] = pointdata.toDouble(); } - - // set some graph styles - graph->setData(xdata, ydata); - graph->setLineStyle((QCPGraph::LineStyle) ui->comboLineType->currentIndex()); // WARN: ssDot is removed int shapeIdx = ui->comboPointShape->currentIndex(); if (shapeIdx > 0) shapeIdx += 1; - graph->setScatterStyle(QCPScatterStyle((QCPScatterStyle::ScatterShape)shapeIdx, 5)); + QCPScatterStyle scatterStyle = QCPScatterStyle(static_cast(shapeIdx), 5); + + QCPAbstractPlottable* plottable; + // When it is already sorted by x, we draw a graph. + // When it is not sorted by x, we draw a curve, so the order selected by the user in the table or in the query is + // respected. In this case the line will have loops and only None and Line is supported as line style. + // TODO: how to make the user aware of this without disturbing. + if (isSorted) { + QCPGraph* graph = ui->plotWidget->addGraph(); + plottable = graph; + graph->setData(xdata, ydata, /*alreadySorted*/ true); + // set some graph styles not supported by the abstract plottable + graph->setLineStyle((QCPGraph::LineStyle) ui->comboLineType->currentIndex()); + graph->setScatterStyle(scatterStyle); + + } else { + QCPCurve* curve = new QCPCurve(ui->plotWidget->xAxis, ui->plotWidget->yAxis); + plottable = curve; + curve->setData(tdata, xdata, ydata, /*alreadySorted*/ true); + // set some curve styles not supported by the abstract plottable + if (ui->comboLineType->currentIndex() == QCPCurve::lsNone) + curve->setLineStyle(QCPCurve::lsNone); + else + curve->setLineStyle(QCPCurve::lsLine); + curve->setScatterStyle(scatterStyle); + } + + plottable->setPen(QPen(item->backgroundColor(PlotColumnY))); + plottable->setSelectable (QCP::stDataRange); // gather Y label column names if(column == RowNumId) @@ -497,14 +523,28 @@ void PlotDock::on_comboLineType_currentIndexChanged(int index) { Q_ASSERT(index >= QCPGraph::lsNone && index <= QCPGraph::lsImpulse); + + bool hasCurves = (ui->plotWidget->plottableCount() > ui->plotWidget->graphCount()); QCPGraph::LineStyle lineStyle = (QCPGraph::LineStyle) index; + if (lineStyle > QCPGraph::lsLine && hasCurves) { + QMessageBox::warning(this, qApp->applicationName(), + tr("There are curves in this plot and the selected line style can only be applied to graphs sorted by X. " + "Either sort the table or query by X to remove curves or select one of the styles supported by curves: " + "None or Line.")); + return; + } for (int i = 0, ie = ui->plotWidget->graphCount(); i < ie; ++i) { QCPGraph * graph = ui->plotWidget->graph(i); if (graph) graph->setLineStyle(lineStyle); } - ui->plotWidget->replot(); + // We have changed the style only for graphs, but not for curves. + // If there are any in the plot, we have to update it completely in order to apply the new style + if (hasCurves) + updatePlot(m_currentPlotModel, m_currentTableSettings, false); + else + ui->plotWidget->replot(); // Save settings for this table if(m_currentTableSettings) @@ -525,6 +565,8 @@ void PlotDock::on_comboPointShape_currentIndexChanged(int index) if (index > 0) index += 1; Q_ASSERT(index >= QCPScatterStyle::ssNone && index < QCPScatterStyle::ssPixmap); + + bool hasCurves = (ui->plotWidget->plottableCount() > ui->plotWidget->graphCount()); QCPScatterStyle::ScatterShape shape = (QCPScatterStyle::ScatterShape) index; for (int i = 0, ie = ui->plotWidget->graphCount(); i < ie; ++i) { @@ -532,7 +574,12 @@ void PlotDock::on_comboPointShape_currentIndexChanged(int index) if (graph) graph->setScatterStyle(QCPScatterStyle(shape, 5)); } - ui->plotWidget->replot(); + // We have changed the style only for graphs, but not for curves. + // If there are any in the plot, we have to update it completely in order to apply the new style + if (hasCurves) + updatePlot(m_currentPlotModel, m_currentTableSettings, false); + else + ui->plotWidget->replot(); // Save settings for this table if(m_currentTableSettings) @@ -601,9 +648,9 @@ void PlotDock::fetchAllData() void PlotDock::selectionChanged() { - for (QCPGraph* graph : ui->plotWidget->selectedGraphs()) { + for (QCPAbstractPlottable* plottable : ui->plotWidget->selectedPlottables()) { - for (QCPDataRange dataRange : graph->selection().dataRanges()) { + for (QCPDataRange dataRange : plottable->selection().dataRanges()) { int index = dataRange.begin(); if (dataRange.length() != 0) {