mirror of
https://github.com/sqlitebrowser/sqlitebrowser.git
synced 2026-01-20 11:00:44 -06:00
Stacked or grouped bars and plot legend
Two new options added to the context menu of the plot: - Stacked bars: switches between stacked bars or grouped bars. The former overlapped layout is avoided since it doesn't make much sense. - Show legend: toggles the display of a plot legend with a translucent background. Possible future improvement is dragging the legend with the mouse.
This commit is contained in:
127
src/PlotDock.cpp
127
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<QCPScatterStyle::ScatterShape>(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<QCPAxisTickerText> 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<QCPAxisTickerText> 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<QCPBars*>(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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user