diff --git a/src/PlotDock.cpp b/src/PlotDock.cpp index 7e37c1cc..cf9c33d7 100644 --- a/src/PlotDock.cpp +++ b/src/PlotDock.cpp @@ -9,7 +9,9 @@ PlotDock::PlotDock(QWidget* parent) : QDialog(parent), ui(new Ui::PlotDock), m_currentPlotModel(nullptr), - m_currentTableSettings(nullptr) + m_currentTableSettings(nullptr), + m_showLegend(false), + m_stackedBars(false) { ui->setupUi(this); @@ -50,6 +52,18 @@ PlotDock::PlotDock(QWidget* parent) copy(); }); + QAction* showLegendAction = new QAction(tr("Show legend"), m_contextMenu); + showLegendAction->setCheckable(true); + m_contextMenu->addAction(showLegendAction); + + connect(showLegendAction, SIGNAL(toggled(bool)), this, SLOT(toggleLegendVisible(bool))); + + QAction* stackedBarsAction = new QAction(tr("Stacked bars"), m_contextMenu); + stackedBarsAction->setCheckable(true); + m_contextMenu->addAction(stackedBarsAction); + + connect(stackedBarsAction, SIGNAL(toggled(bool)), this, SLOT(toggleStackedBars(bool))); + connect(ui->plotWidget, &QTableView::customContextMenuRequested, [=](const QPoint& pos) { // Show menu @@ -225,6 +239,8 @@ void PlotDock::updatePlot(SqliteTableModel* model, BrowseDataTableSettings* sett int x = xitem->data(PlotColumnField, Qt::UserRole).toInt(); int xtype = xitem->data(PlotColumnType, Qt::UserRole).toInt(); + ui->plotWidget->xAxis->setTickLabelRotation(0); + // check if we have a x axis with datetime data switch (xtype) { case QVariant::Date: { @@ -247,6 +263,8 @@ void PlotDock::updatePlot(SqliteTableModel* model, BrowseDataTableSettings* sett break; } case QVariant::String: { + // Ticker is set when we have got the labels + ui->plotWidget->xAxis->setTickLabelRotation(60); break; } default: { @@ -334,6 +352,7 @@ void PlotDock::updatePlot(SqliteTableModel* model, BrowseDataTableSettings* sett QCPScatterStyle scatterStyle = QCPScatterStyle(static_cast(shapeIdx), 5); QCPAbstractPlottable* plottable; + // When the X type is String, we draw a bar chart. // 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. @@ -342,38 +361,39 @@ void PlotDock::updatePlot(SqliteTableModel* model, BrowseDataTableSettings* sett QCPBars* bars = new QCPBars(ui->plotWidget->xAxis, ui->plotWidget->yAxis); plottable = bars; bars->setData(xdata, ydata); - QColor brush = item->backgroundColor(PlotColumnY); - if (ui->plotWidget->plottableCount() > 1) - brush.setAlpha(124); - bars->setBrush(brush); - QSharedPointer ticker(new QCPAxisTickerText); - ticker->addTicks(xdata, labels); - ui->plotWidget->xAxis->setTicker(ticker); - ui->plotWidget->xAxis->setTickLabelRotation(60); - } else 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); - ui->plotWidget->xAxis->setTickLabelRotation(0); - + // Set ticker once + if (ui->plotWidget->plottableCount() == 1) { + QSharedPointer ticker(new QCPAxisTickerText); + ticker->addTicks(xdata, labels); + ui->plotWidget->xAxis->setTicker(ticker); + } + QColor color = item->backgroundColor(PlotColumnY); + bars->setBrush(color); + plottable->setPen(QPen(color.darker(150))); } 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); - ui->plotWidget->xAxis->setTickLabelRotation(0); + 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->setPen(QPen(item->backgroundColor(PlotColumnY))); plottable->setSelectable(QCP::stDataRange); + plottable->setName(item->text(PlotColumnField)); // gather Y label column names if(column == RowNumId) @@ -384,6 +404,9 @@ void PlotDock::updatePlot(SqliteTableModel* model, BrowseDataTableSettings* sett } ui->plotWidget->rescaleAxes(true); + ui->plotWidget->legend->setVisible(m_showLegend); + // Legend with slightly transparent background brush: + ui->plotWidget->legend->setBrush(QColor(255, 255, 255, 150)); // set axis labels if(x == RowNumId) @@ -392,6 +415,8 @@ void PlotDock::updatePlot(SqliteTableModel* model, BrowseDataTableSettings* sett ui->plotWidget->xAxis->setLabel(model->headerData(x, Qt::Horizontal).toString()); ui->plotWidget->yAxis->setLabel(yAxisLabels.join("|")); } + + adjustBars(); ui->plotWidget->replot(); // Warn user if not all data has been fetched and hint about the button for loading all the data @@ -789,3 +814,47 @@ void PlotDock::copy() { QApplication::clipboard()->setPixmap(ui->plotWidget->toPixmap()); } + +void PlotDock::toggleLegendVisible(bool visible) +{ + m_showLegend = visible; + ui->plotWidget->legend->setVisible(m_showLegend); + ui->plotWidget->replot(); +} + +// Stack or group bars and set the appropiate bar width (since it is not automatically done by QCustomPlot). +void PlotDock::adjustBars() +{ + const double padding = 0.15; + const double groupedWidth = ui->plotWidget->plottableCount()? 1.0 / ui->plotWidget->plottableCount() : 0.0; + QCPBars* previousBar = nullptr; + QCPBarsGroup* barsGroup = m_stackedBars? nullptr : new QCPBarsGroup(ui->plotWidget); + for (int i = 0, ie = ui->plotWidget->plottableCount(); i < ie; ++i) + { + QCPBars* bar = qobject_cast(ui->plotWidget->plottable(i)); + if (bar) { + if (m_stackedBars) { + // Ungroup if grouped + bar->setBarsGroup(nullptr); + if (previousBar) + bar->moveAbove(previousBar); + // Set width to ocuppy the full coordinate space, less padding + bar->setWidth(1.0 - padding); + } else { + // Unstack if stacked + bar->moveAbove(nullptr); + bar->setBarsGroup(barsGroup); + // Set width to a plot coordinate width, less padding + bar->setWidth(groupedWidth - padding); + } + previousBar = bar; + } + } +} + +void PlotDock::toggleStackedBars(bool stacked) +{ + m_stackedBars = stacked; + adjustBars(); + ui->plotWidget->replot(); +} diff --git a/src/PlotDock.h b/src/PlotDock.h index 6acc02dd..11631582 100644 --- a/src/PlotDock.h +++ b/src/PlotDock.h @@ -85,6 +85,8 @@ private: SqliteTableModel* m_currentPlotModel; BrowseDataTableSettings* m_currentTableSettings; QMenu* m_contextMenu; + bool m_showLegend; + bool m_stackedBars; /*! * \brief guessdatatype try to parse the first 10 rows and decide the datatype @@ -93,6 +95,7 @@ private: * \return the guessed datatype */ QVariant::Type guessDataType(SqliteTableModel* model, int column); + void adjustBars(); private slots: void on_treePlotColumns_itemChanged(QTreeWidgetItem* item, int column); @@ -104,7 +107,8 @@ private slots: void mousePress(); void mouseWheel(); void copy(); - + void toggleLegendVisible(bool visible); + void toggleStackedBars(bool stacked); }; #endif