mirror of
https://github.com/sqlitebrowser/sqlitebrowser.git
synced 2026-05-08 04:49:58 -05:00
Merge branch 'plotrefactoring'
This commit is contained in:
@@ -105,6 +105,7 @@ set(SQLB_MOC_HDR
|
||||
src/FilterLineEdit.h
|
||||
src/RemoteDatabase.h
|
||||
src/ForeignKeyEditorDelegate.h
|
||||
src/PlotDock.h
|
||||
)
|
||||
|
||||
set(SQLB_SRC
|
||||
@@ -139,6 +140,7 @@ set(SQLB_SRC
|
||||
src/FilterLineEdit.cpp
|
||||
src/RemoteDatabase.cpp
|
||||
src/ForeignKeyEditorDelegate.cpp
|
||||
src/PlotDock.cpp
|
||||
)
|
||||
|
||||
set(SQLB_FORMS
|
||||
@@ -155,6 +157,7 @@ set(SQLB_FORMS
|
||||
src/CipherDialog.ui
|
||||
src/ExportSqlDialog.ui
|
||||
src/ColumnDisplayFormatDialog.ui
|
||||
src/PlotDock.ui
|
||||
)
|
||||
|
||||
set(SQLB_RESOURCES
|
||||
|
||||
+9
-502
@@ -55,6 +55,7 @@ MainWindow::MainWindow(QWidget* parent)
|
||||
m_browseTableModel(new SqliteTableModel(db, this, Settings::getSettingsValue("db", "prefetchsize").toInt())),
|
||||
m_currentTabTableModel(m_browseTableModel),
|
||||
editDock(new EditDialog(this)),
|
||||
plotDock(new PlotDock(this)),
|
||||
gotoValidator(new QIntValidator(0, 0, this))
|
||||
{
|
||||
ui->setupUi(this);
|
||||
@@ -102,18 +103,16 @@ void MainWindow::init()
|
||||
// Set up the table combo box in the Browse Data tab
|
||||
ui->comboBrowseTable->setModel(dbStructureModel);
|
||||
|
||||
// Edit dock
|
||||
// Create docks
|
||||
ui->dockEdit->setWidget(editDock);
|
||||
ui->dockPlot->setWidget(plotDock);
|
||||
|
||||
// Restore window geometry
|
||||
restoreGeometry(Settings::getSettingsValue("MainWindow", "geometry").toByteArray());
|
||||
restoreState(Settings::getSettingsValue("MainWindow", "windowState").toByteArray());
|
||||
|
||||
// Restore various dock state settings
|
||||
// Restore dock state settings
|
||||
ui->comboLogSubmittedBy->setCurrentIndex(ui->comboLogSubmittedBy->findText(Settings::getSettingsValue("SQLLogDock", "Log").toString()));
|
||||
ui->splitterForPlot->restoreState(Settings::getSettingsValue("PlotDock", "splitterSize").toByteArray());
|
||||
ui->comboLineType->setCurrentIndex(Settings::getSettingsValue("PlotDock", "lineType").toInt());
|
||||
ui->comboPointShape->setCurrentIndex(Settings::getSettingsValue("PlotDock", "pointShape").toInt());
|
||||
|
||||
// Add keyboard shortcuts
|
||||
QList<QKeySequence> shortcuts = ui->actionExecuteSql->shortcuts();
|
||||
@@ -225,9 +224,6 @@ void MainWindow::init()
|
||||
connect(ui->dockEdit, SIGNAL(visibilityChanged(bool)), this, SLOT(toggleEditDock(bool)));
|
||||
connect(&m_remoteDb, SIGNAL(openFile(QString)), this, SLOT(fileOpen(QString)));
|
||||
|
||||
// plot widgets
|
||||
ui->treePlotColumns->setSelectionMode(QAbstractItemView::NoSelection);
|
||||
|
||||
// Set other window settings
|
||||
setAcceptDrops(true);
|
||||
setWindowTitle(QApplication::applicationName());
|
||||
@@ -476,7 +472,7 @@ void MainWindow::populateTable()
|
||||
m_browseTableModel->setEncoding(tableIt.value().encoding);
|
||||
|
||||
// Plot
|
||||
updatePlot(m_browseTableModel, true, false);
|
||||
plotDock->updatePlot(m_browseTableModel, &browseTableSettings[ui->comboBrowseTable->currentText()], true, false);
|
||||
} else {
|
||||
// There aren't any information stored for this table yet, so use some default values
|
||||
|
||||
@@ -495,7 +491,7 @@ void MainWindow::populateTable()
|
||||
m_browseTableModel->setEncoding(defaultBrowseTableEncoding);
|
||||
|
||||
// Plot
|
||||
updatePlot(m_browseTableModel);
|
||||
plotDock->updatePlot(m_browseTableModel, &browseTableSettings[ui->comboBrowseTable->currentText()]);
|
||||
|
||||
// The filters can be left empty as they are
|
||||
}
|
||||
@@ -537,7 +533,7 @@ bool MainWindow::fileClose()
|
||||
setRecordsetLabel();
|
||||
|
||||
// Reset the plot dock model
|
||||
updatePlot(0);
|
||||
plotDock->updatePlot(0);
|
||||
|
||||
activateFields(false);
|
||||
|
||||
@@ -558,9 +554,6 @@ void MainWindow::closeEvent( QCloseEvent* event )
|
||||
Settings::setSettingsValue("MainWindow", "geometry", saveGeometry());
|
||||
Settings::setSettingsValue("MainWindow", "windowState", saveState());
|
||||
Settings::setSettingsValue("SQLLogDock", "Log", ui->comboLogSubmittedBy->currentText());
|
||||
Settings::setSettingsValue("PlotDock", "splitterSize", ui->splitterForPlot->saveState());
|
||||
Settings::setSettingsValue("PlotDock", "lineType", ui->comboLineType->currentIndex());
|
||||
Settings::setSettingsValue("PlotDock", "pointShape", ui->comboPointShape->currentIndex());
|
||||
QMainWindow::closeEvent(event);
|
||||
} else {
|
||||
event->ignore();
|
||||
@@ -1008,7 +1001,7 @@ void MainWindow::executeQuery()
|
||||
execution_start_index = execution_end_index;
|
||||
} while( tail && *tail != 0 && (sql3status == SQLITE_OK || sql3status == SQLITE_DONE));
|
||||
sqlWidget->finishExecution(statusMessage);
|
||||
updatePlot(sqlWidget->getModel());
|
||||
plotDock->updatePlot(sqlWidget->getModel());
|
||||
|
||||
connect(sqlWidget->getTableResult(), SIGNAL(clicked(QModelIndex)), this, SLOT(dataTableSelectionChanged(QModelIndex)));
|
||||
|
||||
@@ -1397,6 +1390,7 @@ void MainWindow::activateFields(bool enable)
|
||||
ui->actionEncryption->setEnabled(enable && write);
|
||||
ui->buttonClearFilters->setEnabled(enable);
|
||||
ui->dockEdit->setEnabled(enable && write);
|
||||
ui->dockPlot->setEnabled(enable);
|
||||
ui->actionSave_Remote->setEnabled(enable);
|
||||
}
|
||||
|
||||
@@ -1779,416 +1773,6 @@ void MainWindow::httpresponse(QNetworkReply *reply)
|
||||
reply->deleteLater();
|
||||
}
|
||||
|
||||
namespace {
|
||||
/*!
|
||||
* \brief guessdatatype try to parse the first 10 rows and decide the datatype
|
||||
* \param model model to check the data
|
||||
* \param column index of the column to check
|
||||
* \return the guessed datatype
|
||||
*/
|
||||
QVariant::Type guessdatatype(SqliteTableModel* model, int column)
|
||||
{
|
||||
QVariant::Type type = QVariant::Invalid;
|
||||
for(int i = 0; i < std::min(10, model->rowCount()) && type != QVariant::String; ++i)
|
||||
{
|
||||
QVariant data = model->data(model->index(i, column), Qt::EditRole);
|
||||
if(data.isNull() || data.convert(QVariant::Double))
|
||||
{
|
||||
type = QVariant::Double;
|
||||
} else {
|
||||
QString s = model->data(model->index(i, column)).toString();
|
||||
QDate d = QDate::fromString(s, Qt::ISODate);
|
||||
if(d.isValid())
|
||||
type = QVariant::DateTime;
|
||||
else
|
||||
type = QVariant::String;
|
||||
}
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::updatePlot(SqliteTableModel *model, bool update, bool keepOrResetSelection)
|
||||
{
|
||||
// add columns to x/y selection tree widget
|
||||
if(update)
|
||||
{
|
||||
// disconnect treeplotcolumns item changed updates
|
||||
disconnect(ui->treePlotColumns, SIGNAL(itemChanged(QTreeWidgetItem*,int)),
|
||||
this,SLOT(on_treePlotColumns_itemChanged(QTreeWidgetItem*,int)));
|
||||
|
||||
m_currentPlotModel = model;
|
||||
|
||||
// save current selected columns, so we can restore them after the update
|
||||
QString sItemX; // selected X column
|
||||
QMap<QString, QColor> mapItemsY; // selected Y columns with color
|
||||
|
||||
if(keepOrResetSelection)
|
||||
{
|
||||
// Store the currently selected plot columns to restore them later
|
||||
for(int i = 0; i < ui->treePlotColumns->topLevelItemCount(); ++i)
|
||||
{
|
||||
QTreeWidgetItem* item = ui->treePlotColumns->topLevelItem(i);
|
||||
if(item->checkState(PlotColumnX) == Qt::Checked)
|
||||
sItemX = item->text(PlotColumnField);
|
||||
|
||||
if(item->checkState(PlotColumnY) == Qt::Checked)
|
||||
mapItemsY[item->text(PlotColumnField)] = item->backgroundColor(PlotColumnY);
|
||||
}
|
||||
} else {
|
||||
// Get the plot columns to select from the stored browse table information
|
||||
sItemX = browseTableSettings[ui->comboBrowseTable->currentText()].plotXAxis;
|
||||
|
||||
QMap<QString, PlotSettings> axesY = browseTableSettings[ui->comboBrowseTable->currentText()].plotYAxes;
|
||||
auto it = axesY.constBegin();
|
||||
while(it != axesY.constEnd())
|
||||
{
|
||||
mapItemsY.insert(it.key(), it.value().colour);
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
ui->treePlotColumns->clear();
|
||||
|
||||
if(model)
|
||||
{
|
||||
// Add each column with a supported data type to the column selection view
|
||||
for(int i=0;i<model->columnCount();++i)
|
||||
{
|
||||
QVariant::Type columntype = guessdatatype(model, i);
|
||||
if(columntype != QVariant::String && columntype != QVariant::Invalid)
|
||||
{
|
||||
QTreeWidgetItem* columnitem = new QTreeWidgetItem(ui->treePlotColumns);
|
||||
// maybe i make this more complicated than i should
|
||||
// but store the model column index in the first 16 bit and the type
|
||||
// in the other 16 bits
|
||||
uint itemdata = 0;
|
||||
itemdata = i << 16;
|
||||
itemdata |= columntype;
|
||||
columnitem->setData(PlotColumnField, Qt::UserRole, itemdata);
|
||||
columnitem->setText(PlotColumnField, model->headerData(i, Qt::Horizontal).toString());
|
||||
|
||||
// restore previous check state
|
||||
if(mapItemsY.contains(columnitem->text(PlotColumnField)))
|
||||
{
|
||||
columnitem->setCheckState(PlotColumnY, Qt::Checked);
|
||||
columnitem->setBackgroundColor(PlotColumnY, mapItemsY[columnitem->text(PlotColumnField)]);
|
||||
}
|
||||
else
|
||||
columnitem->setCheckState(PlotColumnY, Qt::Unchecked);
|
||||
if(sItemX == columnitem->text(PlotColumnField))
|
||||
columnitem->setCheckState(PlotColumnX, Qt::Checked);
|
||||
else
|
||||
columnitem->setCheckState(PlotColumnX, Qt::Unchecked);
|
||||
}
|
||||
}
|
||||
|
||||
// Add a row number column at the beginning of the column list, but only when there were (other) columns added
|
||||
if(ui->treePlotColumns->topLevelItemCount())
|
||||
{
|
||||
QTreeWidgetItem* columnitem = new QTreeWidgetItem(ui->treePlotColumns);
|
||||
|
||||
// Just set all bits in the user role information field here to somehow indicate what column this is
|
||||
uint itemdata = -1;
|
||||
columnitem->setData(PlotColumnField, Qt::UserRole, itemdata);
|
||||
columnitem->setText(PlotColumnField, tr("Row #"));
|
||||
|
||||
// restore previous check state
|
||||
if(mapItemsY.contains(columnitem->text(PlotColumnField)))
|
||||
{
|
||||
columnitem->setCheckState(PlotColumnY, Qt::Checked);
|
||||
columnitem->setBackgroundColor(PlotColumnY, mapItemsY[columnitem->text(PlotColumnField)]);
|
||||
} else {
|
||||
columnitem->setCheckState(PlotColumnY, Qt::Unchecked);
|
||||
}
|
||||
if(sItemX == columnitem->text(PlotColumnField))
|
||||
columnitem->setCheckState(PlotColumnX, Qt::Checked);
|
||||
else
|
||||
columnitem->setCheckState(PlotColumnX, Qt::Unchecked);
|
||||
|
||||
ui->treePlotColumns->takeTopLevelItem(ui->treePlotColumns->indexOfTopLevelItem(columnitem));
|
||||
ui->treePlotColumns->insertTopLevelItem(0, columnitem);
|
||||
}
|
||||
}
|
||||
|
||||
ui->plotWidget->yAxis->setLabel("Y");
|
||||
ui->plotWidget->xAxis->setLabel("X");
|
||||
connect(ui->treePlotColumns, &QTreeWidget::itemChanged, this, &MainWindow::on_treePlotColumns_itemChanged);
|
||||
}
|
||||
|
||||
// search for the x axis select
|
||||
QTreeWidgetItem* xitem = 0;
|
||||
for(int i = 0; i < ui->treePlotColumns->topLevelItemCount(); ++i)
|
||||
{
|
||||
xitem = ui->treePlotColumns->topLevelItem(i);
|
||||
if(xitem->checkState(PlotColumnX) == Qt::Checked)
|
||||
break;
|
||||
|
||||
xitem = 0;
|
||||
}
|
||||
|
||||
QStringList yAxisLabels;
|
||||
|
||||
ui->plotWidget->clearGraphs();
|
||||
if(xitem)
|
||||
{
|
||||
// regain the model column index and the datatype
|
||||
// leading 16 bit are column index, the other 16 bit are the datatype
|
||||
// right now datatype is only important for X axis (date, non date)
|
||||
uint xitemdata = xitem->data(PlotColumnField, Qt::UserRole).toUInt();
|
||||
int x = xitemdata >> 16;
|
||||
int xtype = xitemdata & (uint)0xFF;
|
||||
|
||||
// check if we have a x axis with datetime data
|
||||
if(xtype == QVariant::DateTime)
|
||||
{
|
||||
ui->plotWidget->xAxis->setTickLabelType(QCPAxis::ltDateTime);
|
||||
ui->plotWidget->xAxis->setDateTimeFormat("yyyy-MM-dd");
|
||||
} else {
|
||||
ui->plotWidget->xAxis->setTickLabelType(QCPAxis::ltNumber);
|
||||
}
|
||||
|
||||
// add graph for each selected y axis
|
||||
for(int i = 0; i < ui->treePlotColumns->topLevelItemCount(); ++i)
|
||||
{
|
||||
QTreeWidgetItem* item = ui->treePlotColumns->topLevelItem(i);
|
||||
if(item->checkState((PlotColumnY)) == Qt::Checked)
|
||||
{
|
||||
// regain the model column index and the datatype
|
||||
// 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)));
|
||||
|
||||
// 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<double> xdata(model->rowCount()), ydata(model->rowCount());
|
||||
for(int i = 0; i < model->rowCount(); ++i)
|
||||
{
|
||||
// convert x type axis if it's datetime
|
||||
if(xtype == QVariant::DateTime)
|
||||
{
|
||||
QString s = model->data(model->index(i, x)).toString();
|
||||
QDateTime d = QDateTime::fromString(s, Qt::ISODate);
|
||||
xdata[i] = d.toTime_t();
|
||||
} else {
|
||||
// Get the x 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.
|
||||
if(x == 0xFFFF)
|
||||
xdata[i] = i+1;
|
||||
|
||||
else
|
||||
xdata[i] = model->data(model->index(i, x)).toDouble();
|
||||
}
|
||||
|
||||
// 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;
|
||||
if(column == 0xFFFF)
|
||||
pointdata = i+1;
|
||||
else
|
||||
pointdata = model->data(model->index(i, column), Qt::EditRole);
|
||||
|
||||
if(pointdata.isNull())
|
||||
ydata[i] = qQNaN();
|
||||
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));
|
||||
|
||||
// gather Y label column names
|
||||
yAxisLabels << model->headerData(column, Qt::Horizontal).toString();
|
||||
}
|
||||
}
|
||||
|
||||
ui->plotWidget->rescaleAxes(true);
|
||||
|
||||
// set axis labels
|
||||
ui->plotWidget->xAxis->setLabel(model->headerData(x, Qt::Horizontal).toString());
|
||||
ui->plotWidget->yAxis->setLabel(yAxisLabels.join("|"));
|
||||
}
|
||||
ui->plotWidget->replot();
|
||||
}
|
||||
|
||||
void MainWindow::on_treePlotColumns_itemChanged(QTreeWidgetItem *changeitem, int column)
|
||||
{
|
||||
// disable change updates, or we get unwanted redrawing and weird behavior
|
||||
disconnect(ui->treePlotColumns, SIGNAL(itemChanged(QTreeWidgetItem*,int)),
|
||||
this,SLOT(on_treePlotColumns_itemChanged(QTreeWidgetItem*,int)));
|
||||
|
||||
// make sure only 1 X axis is selected
|
||||
QString current_table = ui->comboBrowseTable->currentText();
|
||||
if(column == PlotColumnX)
|
||||
{
|
||||
for(int i = 0; i < ui->treePlotColumns->topLevelItemCount(); ++i)
|
||||
{
|
||||
QTreeWidgetItem* item = ui->treePlotColumns->topLevelItem(i);
|
||||
if(item->checkState(column) == Qt::Checked && item != changeitem)
|
||||
{
|
||||
item->setCheckState(column, Qt::Unchecked);
|
||||
}
|
||||
}
|
||||
|
||||
// Save settings for this table
|
||||
if(changeitem->checkState(column) == Qt::Checked)
|
||||
browseTableSettings[current_table].plotXAxis = changeitem->text(PlotColumnField);
|
||||
else
|
||||
browseTableSettings[current_table].plotXAxis = QString();
|
||||
} else if(column == PlotColumnY) {
|
||||
if(changeitem->checkState(column) == Qt::Checked)
|
||||
{
|
||||
// Generate a default colour if none isn't set yet
|
||||
QColor colour = changeitem->backgroundColor(column);
|
||||
if(!colour.isValid())
|
||||
{
|
||||
static int last_colour_index = 0;
|
||||
switch(last_colour_index++)
|
||||
{
|
||||
case 0:
|
||||
colour = QColor(0, 69, 134);
|
||||
break;
|
||||
case 1:
|
||||
colour = QColor(255, 66, 14);
|
||||
break;
|
||||
case 2:
|
||||
colour = QColor(255, 211, 32);
|
||||
break;
|
||||
case 3:
|
||||
colour = QColor(87, 157, 28);
|
||||
break;
|
||||
case 4:
|
||||
colour = QColor(126, 0, 33);
|
||||
break;
|
||||
case 5:
|
||||
colour = QColor(131, 202, 255);
|
||||
break;
|
||||
case 6:
|
||||
colour = QColor(49, 64, 4);
|
||||
break;
|
||||
case 7:
|
||||
colour = QColor(174, 207, 0);
|
||||
break;
|
||||
case 8:
|
||||
colour = QColor(75, 31, 111);
|
||||
break;
|
||||
case 9:
|
||||
colour = QColor(255, 149, 14);
|
||||
break;
|
||||
case 10:
|
||||
colour = QColor(197, 00, 11);
|
||||
break;
|
||||
case 11:
|
||||
colour = QColor(0, 132, 209);
|
||||
|
||||
// Since this is the last colour in our table, reset the counter back
|
||||
// to the first colour
|
||||
last_colour_index = 0;
|
||||
break;
|
||||
default:
|
||||
// NOTE: This shouldn't happen!
|
||||
colour = QColor(0, 0, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Set colour
|
||||
changeitem->setBackgroundColor(column, colour);
|
||||
|
||||
// Save settings for this table
|
||||
PlotSettings& plot_settings = browseTableSettings[current_table].plotYAxes[changeitem->text(PlotColumnField)];
|
||||
plot_settings.colour = colour;
|
||||
plot_settings.lineStyle = ui->comboLineType->currentIndex();
|
||||
plot_settings.pointShape = (ui->comboPointShape->currentIndex() > 0 ? (ui->comboPointShape->currentIndex()+1) : ui->comboPointShape->currentIndex());
|
||||
}
|
||||
}
|
||||
|
||||
connect(ui->treePlotColumns, SIGNAL(itemChanged(QTreeWidgetItem*,int)),
|
||||
this,SLOT(on_treePlotColumns_itemChanged(QTreeWidgetItem*,int)));
|
||||
|
||||
updatePlot(m_currentPlotModel, false);
|
||||
}
|
||||
|
||||
void MainWindow::on_treePlotColumns_itemDoubleClicked(QTreeWidgetItem *item, int column)
|
||||
{
|
||||
// disable change updates, or we get unwanted redrawing and weird behavior
|
||||
disconnect(ui->treePlotColumns, SIGNAL(itemChanged(QTreeWidgetItem*,int)),
|
||||
this,SLOT(on_treePlotColumns_itemChanged(QTreeWidgetItem*,int)));
|
||||
|
||||
if(column == PlotColumnY)
|
||||
{
|
||||
// On double click open the colordialog
|
||||
QColorDialog colordialog(this);
|
||||
QColor curbkcolor = item->backgroundColor(column);
|
||||
QColor precolor = !curbkcolor.isValid() ? (Qt::GlobalColor)(qrand() % 13 + 5) : curbkcolor;
|
||||
QColor color = colordialog.getColor(precolor, this, tr("Choose a axis color"));
|
||||
QString current_table = ui->comboBrowseTable->currentText();
|
||||
if(color.isValid())
|
||||
{
|
||||
item->setCheckState(column, Qt::Checked);
|
||||
item->setBackgroundColor(column, color);
|
||||
|
||||
// Save settings for this table
|
||||
PlotSettings& plot_settings = browseTableSettings[current_table].plotYAxes[item->text(PlotColumnField)];
|
||||
plot_settings.colour = color;
|
||||
plot_settings.lineStyle = ui->comboLineType->currentIndex();
|
||||
plot_settings.pointShape = (ui->comboPointShape->currentIndex() > 0 ? (ui->comboPointShape->currentIndex()+1) : ui->comboPointShape->currentIndex());
|
||||
} else {
|
||||
item->setCheckState(column, Qt::Unchecked);
|
||||
|
||||
// Save settings for this table
|
||||
browseTableSettings[current_table].plotYAxes.remove(item->text(PlotColumnField));
|
||||
}
|
||||
}
|
||||
|
||||
connect(ui->treePlotColumns, SIGNAL(itemChanged(QTreeWidgetItem*,int)),
|
||||
this,SLOT(on_treePlotColumns_itemChanged(QTreeWidgetItem*,int)));
|
||||
|
||||
updatePlot(m_currentPlotModel, false);
|
||||
}
|
||||
|
||||
void MainWindow::on_butSavePlot_clicked()
|
||||
{
|
||||
QString fileName = FileDialog::getSaveFileName(this,
|
||||
tr("Choose a filename to save under"),
|
||||
tr("PNG(*.png);;JPG(*.jpg);;PDF(*.pdf);;BMP(*.bmp);;All Files(*)")
|
||||
);
|
||||
if(!fileName.isEmpty())
|
||||
{
|
||||
if(fileName.endsWith(".png", Qt::CaseInsensitive))
|
||||
{
|
||||
ui->plotWidget->savePng(fileName);
|
||||
}
|
||||
else if(fileName.endsWith(".jpg", Qt::CaseInsensitive))
|
||||
{
|
||||
ui->plotWidget->saveJpg(fileName);
|
||||
}
|
||||
else if(fileName.endsWith(".pdf", Qt::CaseInsensitive))
|
||||
{
|
||||
ui->plotWidget->savePdf(fileName);
|
||||
}
|
||||
else if(fileName.endsWith(".bmp", Qt::CaseInsensitive))
|
||||
{
|
||||
ui->plotWidget->saveBmp(fileName);
|
||||
}
|
||||
else
|
||||
{
|
||||
fileName += ".png";
|
||||
ui->plotWidget->savePng(fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::on_actionOpen_Remote_triggered()
|
||||
{
|
||||
QString url = QInputDialog::getText(this, qApp->applicationName(), tr("Please enter the URL of the database file to open."));
|
||||
@@ -2591,54 +2175,6 @@ void MainWindow::copyCurrentCreateStatement()
|
||||
QApplication::clipboard()->setText(stmt);
|
||||
}
|
||||
|
||||
void MainWindow::on_comboLineType_currentIndexChanged(int index)
|
||||
{
|
||||
Q_ASSERT(index >= QCPGraph::lsNone &&
|
||||
index <= QCPGraph::lsImpulse);
|
||||
QCPGraph::LineStyle lineStyle = (QCPGraph::LineStyle) index;
|
||||
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();
|
||||
|
||||
// Save settings for this table
|
||||
QMap<QString, PlotSettings>& graphs = browseTableSettings[ui->comboBrowseTable->currentText()].plotYAxes;
|
||||
auto it = graphs.begin();
|
||||
while(it != graphs.end())
|
||||
{
|
||||
it.value().lineStyle = lineStyle;
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::on_comboPointShape_currentIndexChanged(int index)
|
||||
{
|
||||
// WARN: because ssDot point shape is removed
|
||||
if (index > 0) index += 1;
|
||||
Q_ASSERT(index >= QCPScatterStyle::ssNone &&
|
||||
index < QCPScatterStyle::ssPixmap);
|
||||
QCPScatterStyle::ScatterShape shape = (QCPScatterStyle::ScatterShape) index;
|
||||
for (int i = 0, ie = ui->plotWidget->graphCount(); i < ie; ++i)
|
||||
{
|
||||
QCPGraph * graph = ui->plotWidget->graph(i);
|
||||
if (graph)
|
||||
graph->setScatterStyle(QCPScatterStyle(shape, 5));
|
||||
}
|
||||
ui->plotWidget->replot();
|
||||
|
||||
// Save settings for this table
|
||||
QMap<QString, PlotSettings>& graphs = browseTableSettings[ui->comboBrowseTable->currentText()].plotYAxes;
|
||||
auto it = graphs.begin();
|
||||
while(it != graphs.end())
|
||||
{
|
||||
it.value().pointShape = shape;
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::jumpToRow(const QString& table, QString column, const QByteArray& value)
|
||||
{
|
||||
// First check if table exists
|
||||
@@ -2800,35 +2336,6 @@ void MainWindow::browseDataSetDefaultTableEncoding()
|
||||
browseDataSetTableEncoding(true);
|
||||
}
|
||||
|
||||
void MainWindow::browseDataFetchAllData()
|
||||
{
|
||||
if(m_currentPlotModel)
|
||||
{
|
||||
// Show progress dialog because fetching all data might take some time
|
||||
QProgressDialog progress(tr("Fetching all data..."),
|
||||
tr("Cancel"), m_browseTableModel->rowCount(), m_browseTableModel->totalRowCount());
|
||||
progress.setWindowModality(Qt::ApplicationModal);
|
||||
progress.show();
|
||||
qApp->processEvents();
|
||||
|
||||
// Make sure all data is loaded
|
||||
while(m_currentPlotModel->canFetchMore())
|
||||
{
|
||||
// Fetch the next bunch of data
|
||||
m_currentPlotModel->fetchMore();
|
||||
|
||||
// Update the progress dialog and stop loading data when the cancel button was pressed
|
||||
progress.setValue(m_currentPlotModel->rowCount());
|
||||
qApp->processEvents();
|
||||
if(progress.wasCanceled())
|
||||
break;
|
||||
}
|
||||
|
||||
// Update plot
|
||||
updatePlot(m_currentPlotModel);
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::fileOpenReadOnly()
|
||||
{
|
||||
// Redirect to 'standard' fileOpen(), with the read only flag set
|
||||
|
||||
+55
-92
@@ -4,12 +4,14 @@
|
||||
#include "sqltextedit.h"
|
||||
#include "sqlitedb.h"
|
||||
#include "RemoteDatabase.h"
|
||||
#include "PlotDock.h"
|
||||
|
||||
#include <QMainWindow>
|
||||
#include <QMap>
|
||||
|
||||
class QDragEnterEvent;
|
||||
class EditDialog;
|
||||
class PlotDock;
|
||||
class QIntValidator;
|
||||
class QLabel;
|
||||
class QModelIndex;
|
||||
@@ -18,12 +20,62 @@ class SqliteTableModel;
|
||||
class DbStructureModel;
|
||||
class QNetworkReply;
|
||||
class QNetworkAccessManager;
|
||||
class QTreeWidgetItem;
|
||||
|
||||
namespace Ui {
|
||||
class MainWindow;
|
||||
}
|
||||
|
||||
struct BrowseDataTableSettings
|
||||
{
|
||||
int sortOrderIndex;
|
||||
Qt::SortOrder sortOrderMode;
|
||||
QMap<int, int> columnWidths;
|
||||
QMap<int, QString> filterValues;
|
||||
QMap<int, QString> displayFormats;
|
||||
bool showRowid;
|
||||
QString encoding;
|
||||
QString plotXAxis;
|
||||
QMap<QString, PlotDock::PlotSettings> plotYAxes;
|
||||
|
||||
friend QDataStream& operator<<(QDataStream& stream, const BrowseDataTableSettings& object)
|
||||
{
|
||||
stream << object.sortOrderIndex;
|
||||
stream << static_cast<int>(object.sortOrderMode);
|
||||
stream << object.columnWidths;
|
||||
stream << object.filterValues;
|
||||
stream << object.displayFormats;
|
||||
stream << object.showRowid;
|
||||
stream << object.encoding;
|
||||
stream << object.plotXAxis;
|
||||
stream << object.plotYAxes;
|
||||
|
||||
return stream;
|
||||
}
|
||||
friend QDataStream& operator>>(QDataStream& stream, BrowseDataTableSettings& object)
|
||||
{
|
||||
stream >> object.sortOrderIndex;
|
||||
int sortordermode;
|
||||
stream >> sortordermode;
|
||||
object.sortOrderMode = static_cast<Qt::SortOrder>(sortordermode);
|
||||
stream >> object.columnWidths;
|
||||
stream >> object.filterValues;
|
||||
stream >> object.displayFormats;
|
||||
stream >> object.showRowid;
|
||||
stream >> object.encoding;
|
||||
|
||||
// Versions pre 3.10.0 didn't store the following information in their project files.
|
||||
// To be absolutely sure that nothing strange happens when we read past the stream for
|
||||
// those cases, check for the end of the stream here.
|
||||
if(stream.atEnd())
|
||||
return stream;
|
||||
|
||||
stream >> object.plotXAxis;
|
||||
stream >> object.plotYAxes;
|
||||
|
||||
return stream;
|
||||
}
|
||||
};
|
||||
|
||||
class MainWindow : public QMainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
@@ -35,81 +87,6 @@ public:
|
||||
DBBrowserDB& getDb() { return db; }
|
||||
const RemoteDatabase& getRemote() const { return m_remoteDb; }
|
||||
|
||||
struct PlotSettings
|
||||
{
|
||||
int lineStyle;
|
||||
int pointShape;
|
||||
QColor colour;
|
||||
|
||||
friend QDataStream& operator<<(QDataStream& stream, const MainWindow::PlotSettings& object)
|
||||
{
|
||||
stream << object.lineStyle;
|
||||
stream << object.pointShape;
|
||||
stream << object.colour;
|
||||
|
||||
return stream;
|
||||
}
|
||||
friend QDataStream& operator>>(QDataStream& stream, MainWindow::PlotSettings& object)
|
||||
{
|
||||
stream >> object.lineStyle;
|
||||
stream >> object.pointShape;
|
||||
stream >> object.colour;
|
||||
|
||||
return stream;
|
||||
}
|
||||
};
|
||||
|
||||
struct BrowseDataTableSettings
|
||||
{
|
||||
int sortOrderIndex;
|
||||
Qt::SortOrder sortOrderMode;
|
||||
QMap<int, int> columnWidths;
|
||||
QMap<int, QString> filterValues;
|
||||
QMap<int, QString> displayFormats;
|
||||
bool showRowid;
|
||||
QString encoding;
|
||||
QString plotXAxis;
|
||||
QMap<QString, PlotSettings> plotYAxes;
|
||||
|
||||
friend QDataStream& operator<<(QDataStream& stream, const MainWindow::BrowseDataTableSettings& object)
|
||||
{
|
||||
stream << object.sortOrderIndex;
|
||||
stream << static_cast<int>(object.sortOrderMode);
|
||||
stream << object.columnWidths;
|
||||
stream << object.filterValues;
|
||||
stream << object.displayFormats;
|
||||
stream << object.showRowid;
|
||||
stream << object.encoding;
|
||||
stream << object.plotXAxis;
|
||||
stream << object.plotYAxes;
|
||||
|
||||
return stream;
|
||||
}
|
||||
friend QDataStream& operator>>(QDataStream& stream, MainWindow::BrowseDataTableSettings& object)
|
||||
{
|
||||
stream >> object.sortOrderIndex;
|
||||
int sortordermode;
|
||||
stream >> sortordermode;
|
||||
object.sortOrderMode = static_cast<Qt::SortOrder>(sortordermode);
|
||||
stream >> object.columnWidths;
|
||||
stream >> object.filterValues;
|
||||
stream >> object.displayFormats;
|
||||
stream >> object.showRowid;
|
||||
stream >> object.encoding;
|
||||
|
||||
// Versions pre 3.10.0 didn't store the following information in their project files.
|
||||
// To be absolutely sure that nothing strange happens when we read past the stream for
|
||||
// those cases, check for the end of the stream here.
|
||||
if(stream.atEnd())
|
||||
return stream;
|
||||
|
||||
stream >> object.plotXAxis;
|
||||
stream >> object.plotYAxes;
|
||||
|
||||
return stream;
|
||||
}
|
||||
};
|
||||
|
||||
enum Tabs
|
||||
{
|
||||
StructureTab,
|
||||
@@ -140,19 +117,11 @@ private:
|
||||
int wal_autocheckpoint;
|
||||
} pragmaValues;
|
||||
|
||||
enum PlotColumns
|
||||
{
|
||||
PlotColumnField = 0,
|
||||
PlotColumnX = 1,
|
||||
PlotColumnY = 2,
|
||||
PlotColumnDummy = 3
|
||||
};
|
||||
|
||||
Ui::MainWindow* ui;
|
||||
|
||||
SqliteTableModel* m_browseTableModel;
|
||||
SqliteTableModel* m_currentTabTableModel;
|
||||
SqliteTableModel* m_currentPlotModel;
|
||||
|
||||
QMenu *popupTableMenu;
|
||||
QMenu *recentFilesMenu;
|
||||
QMenu *popupSaveSqlFileMenu;
|
||||
@@ -171,6 +140,7 @@ private:
|
||||
QMap<QString, BrowseDataTableSettings> browseTableSettings;
|
||||
|
||||
EditDialog* editDock;
|
||||
PlotDock* plotDock;
|
||||
QIntValidator* gotoValidator;
|
||||
|
||||
DBBrowserDB db;
|
||||
@@ -256,10 +226,6 @@ private slots:
|
||||
void loadExtension();
|
||||
void reloadSettings();
|
||||
void httpresponse(QNetworkReply* reply);
|
||||
void updatePlot(SqliteTableModel* model, bool update = true, bool keepOrResetSelection = true);
|
||||
void on_treePlotColumns_itemChanged(QTreeWidgetItem *item, int column);
|
||||
void on_treePlotColumns_itemDoubleClicked(QTreeWidgetItem *item, int column);
|
||||
void on_butSavePlot_clicked();
|
||||
void on_actionWiki_triggered();
|
||||
void on_actionBug_report_triggered();
|
||||
void on_actionSqlCipherFaq_triggered();
|
||||
@@ -272,15 +238,12 @@ private slots:
|
||||
void editEncryption();
|
||||
void on_buttonClearFilters_clicked();
|
||||
void copyCurrentCreateStatement();
|
||||
void on_comboLineType_currentIndexChanged(int index);
|
||||
void on_comboPointShape_currentIndexChanged(int index);
|
||||
void showDataColumnPopupMenu(const QPoint& pos);
|
||||
void showRecordPopupMenu(const QPoint& pos);
|
||||
void editDataColumnDisplayFormat();
|
||||
void showRowidColumn(bool show);
|
||||
void browseDataSetTableEncoding(bool forAllTables = false);
|
||||
void browseDataSetDefaultTableEncoding();
|
||||
void browseDataFetchAllData();
|
||||
void fileOpenReadOnly();
|
||||
};
|
||||
|
||||
|
||||
+1
-282
@@ -1077,261 +1077,7 @@
|
||||
<number>2</number>
|
||||
</attribute>
|
||||
<widget class="QWidget" name="dockWidgetContents_2">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_6">
|
||||
<item>
|
||||
<widget class="QSplitter" name="splitterForPlot">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<widget class="QTreeWidget" name="treePlotColumns">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>2</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="columnCount">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<attribute name="headerDefaultSectionSize">
|
||||
<number>100</number>
|
||||
</attribute>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Columns</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>X</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Y</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>_</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
<widget class="QCustomPlot" name="plotWidget" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>8</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="widget" native="true">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Line type:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="comboLineType">
|
||||
<property name="currentIndex">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>None</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Line</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>StepLeft</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>StepRight</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>StepCenter</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Impulse</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Point shape:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="comboPointShape">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>None</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Cross</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Plus</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Circle</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Disc</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Square</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Diamond</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Star</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Triangle</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>TriangleInverted</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>CrossSquare</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>PlusSquare</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>CrossCircle</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>PlusCircle</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Peace</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_4">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="butSavePlot">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Save current plot...</p><p>File format chosen by extension (png, jpg, pdf, bmp)</p></body></html></string>
|
||||
</property>
|
||||
<property name="statusTip">
|
||||
<string>Save current plot...</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="icons/icons.qrc">
|
||||
<normaloff>:/icons/save_table</normaloff>:/icons/save_table</iconset>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="autoDefault">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="default">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="buttonLoadAllData">
|
||||
<property name="toolTip">
|
||||
<string>Load all data. This has only an effect if not all data has been fetched from the table yet due to the partial fetch mechanism.</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="icons/icons.qrc">
|
||||
<normaloff>:/icons/keyword</normaloff>:/icons/keyword</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_6"/>
|
||||
</widget>
|
||||
</widget>
|
||||
<widget class="QDockWidget" name="dockSchema">
|
||||
@@ -2031,23 +1777,12 @@
|
||||
<signal>foreignKeyClicked(QString,QString,QByteArray)</signal>
|
||||
</slots>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>QCustomPlot</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>qcustomplot.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
<tabstop>mainTab</tabstop>
|
||||
<tabstop>dbTreeWidget</tabstop>
|
||||
<tabstop>comboLogSubmittedBy</tabstop>
|
||||
<tabstop>buttonLogClear</tabstop>
|
||||
<tabstop>treePlotColumns</tabstop>
|
||||
<tabstop>comboLineType</tabstop>
|
||||
<tabstop>comboPointShape</tabstop>
|
||||
<tabstop>butSavePlot</tabstop>
|
||||
<tabstop>buttonLoadAllData</tabstop>
|
||||
<tabstop>treeSchemaDock</tabstop>
|
||||
<tabstop>comboBrowseTable</tabstop>
|
||||
<tabstop>buttonRefresh</tabstop>
|
||||
@@ -3013,22 +2748,6 @@
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonLoadAllData</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>MainWindow</receiver>
|
||||
<slot>browseDataFetchAllData()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>1003</x>
|
||||
<y>389</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>1005</x>
|
||||
<y>412</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>fileExportJsonAction</sender>
|
||||
<signal>triggered()</signal>
|
||||
|
||||
@@ -0,0 +1,530 @@
|
||||
#include "PlotDock.h"
|
||||
#include "ui_PlotDock.h"
|
||||
#include "Settings.h"
|
||||
#include "sqlitetablemodel.h"
|
||||
#include "FileDialog.h"
|
||||
#include "MainWindow.h" // Just for BrowseDataTableSettings, not for the actual main window class
|
||||
|
||||
PlotDock::PlotDock(QWidget* parent)
|
||||
: QDialog(parent),
|
||||
ui(new Ui::PlotDock),
|
||||
m_currentPlotModel(nullptr),
|
||||
m_currentTableSettings(nullptr)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
// Init widgets
|
||||
ui->treePlotColumns->setSelectionMode(QAbstractItemView::NoSelection);
|
||||
|
||||
// Restore state
|
||||
ui->splitterForPlot->restoreState(Settings::getSettingsValue("PlotDock", "splitterSize").toByteArray());
|
||||
ui->comboLineType->setCurrentIndex(Settings::getSettingsValue("PlotDock", "lineType").toInt());
|
||||
ui->comboPointShape->setCurrentIndex(Settings::getSettingsValue("PlotDock", "pointShape").toInt());
|
||||
}
|
||||
|
||||
PlotDock::~PlotDock()
|
||||
{
|
||||
// Save state
|
||||
Settings::setSettingsValue("PlotDock", "splitterSize", ui->splitterForPlot->saveState());
|
||||
Settings::setSettingsValue("PlotDock", "lineType", ui->comboLineType->currentIndex());
|
||||
Settings::setSettingsValue("PlotDock", "pointShape", ui->comboPointShape->currentIndex());
|
||||
|
||||
// Finally, delete all widgets
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void PlotDock::updatePlot(SqliteTableModel* model, BrowseDataTableSettings* settings, bool update, bool keepOrResetSelection)
|
||||
{
|
||||
// add columns to x/y selection tree widget
|
||||
if(update)
|
||||
{
|
||||
// Store pointer to the current browse table settings in the main window
|
||||
m_currentTableSettings = settings;
|
||||
|
||||
// disconnect treeplotcolumns item changed updates
|
||||
disconnect(ui->treePlotColumns, SIGNAL(itemChanged(QTreeWidgetItem*,int)),
|
||||
this,SLOT(on_treePlotColumns_itemChanged(QTreeWidgetItem*,int)));
|
||||
|
||||
m_currentPlotModel = model;
|
||||
|
||||
// save current selected columns, so we can restore them after the update
|
||||
QString sItemX; // selected X column
|
||||
QMap<QString, QColor> mapItemsY; // selected Y columns with color
|
||||
|
||||
if(keepOrResetSelection)
|
||||
{
|
||||
// Store the currently selected plot columns to restore them later
|
||||
for(int i = 0; i < ui->treePlotColumns->topLevelItemCount(); ++i)
|
||||
{
|
||||
QTreeWidgetItem* item = ui->treePlotColumns->topLevelItem(i);
|
||||
if(item->checkState(PlotColumnX) == Qt::Checked)
|
||||
sItemX = item->text(PlotColumnField);
|
||||
|
||||
if(item->checkState(PlotColumnY) == Qt::Checked)
|
||||
mapItemsY[item->text(PlotColumnField)] = item->backgroundColor(PlotColumnY);
|
||||
}
|
||||
} else {
|
||||
// Get the plot columns to select from the stored browse table information
|
||||
sItemX = m_currentTableSettings->plotXAxis;
|
||||
|
||||
QMap<QString, PlotSettings> axesY = m_currentTableSettings->plotYAxes;
|
||||
auto it = axesY.constBegin();
|
||||
while(it != axesY.constEnd())
|
||||
{
|
||||
mapItemsY.insert(it.key(), it.value().colour);
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
ui->treePlotColumns->clear();
|
||||
|
||||
if(model)
|
||||
{
|
||||
// Add each column with a supported data type to the column selection view
|
||||
for(int i=0;i<model->columnCount();++i)
|
||||
{
|
||||
QVariant::Type columntype = guessDataType(model, i);
|
||||
if(columntype != QVariant::String && columntype != QVariant::Invalid)
|
||||
{
|
||||
QTreeWidgetItem* columnitem = new QTreeWidgetItem(ui->treePlotColumns);
|
||||
// maybe i make this more complicated than i should
|
||||
// but store the model column index in the first 16 bit and the type
|
||||
// in the other 16 bits
|
||||
uint itemdata = 0;
|
||||
itemdata = i << 16;
|
||||
itemdata |= columntype;
|
||||
columnitem->setData(PlotColumnField, Qt::UserRole, itemdata);
|
||||
columnitem->setText(PlotColumnField, model->headerData(i, Qt::Horizontal).toString());
|
||||
|
||||
// restore previous check state
|
||||
if(mapItemsY.contains(columnitem->text(PlotColumnField)))
|
||||
{
|
||||
columnitem->setCheckState(PlotColumnY, Qt::Checked);
|
||||
columnitem->setBackgroundColor(PlotColumnY, mapItemsY[columnitem->text(PlotColumnField)]);
|
||||
}
|
||||
else
|
||||
columnitem->setCheckState(PlotColumnY, Qt::Unchecked);
|
||||
if(sItemX == columnitem->text(PlotColumnField))
|
||||
columnitem->setCheckState(PlotColumnX, Qt::Checked);
|
||||
else
|
||||
columnitem->setCheckState(PlotColumnX, Qt::Unchecked);
|
||||
}
|
||||
}
|
||||
|
||||
// Add a row number column at the beginning of the column list, but only when there were (other) columns added
|
||||
if(ui->treePlotColumns->topLevelItemCount())
|
||||
{
|
||||
QTreeWidgetItem* columnitem = new QTreeWidgetItem(ui->treePlotColumns);
|
||||
|
||||
// Just set all bits in the user role information field here to somehow indicate what column this is
|
||||
uint itemdata = -1;
|
||||
columnitem->setData(PlotColumnField, Qt::UserRole, itemdata);
|
||||
columnitem->setText(PlotColumnField, tr("Row #"));
|
||||
|
||||
// restore previous check state
|
||||
if(mapItemsY.contains(columnitem->text(PlotColumnField)))
|
||||
{
|
||||
columnitem->setCheckState(PlotColumnY, Qt::Checked);
|
||||
columnitem->setBackgroundColor(PlotColumnY, mapItemsY[columnitem->text(PlotColumnField)]);
|
||||
} else {
|
||||
columnitem->setCheckState(PlotColumnY, Qt::Unchecked);
|
||||
}
|
||||
if(sItemX == columnitem->text(PlotColumnField))
|
||||
columnitem->setCheckState(PlotColumnX, Qt::Checked);
|
||||
else
|
||||
columnitem->setCheckState(PlotColumnX, Qt::Unchecked);
|
||||
|
||||
ui->treePlotColumns->takeTopLevelItem(ui->treePlotColumns->indexOfTopLevelItem(columnitem));
|
||||
ui->treePlotColumns->insertTopLevelItem(0, columnitem);
|
||||
}
|
||||
}
|
||||
|
||||
ui->plotWidget->yAxis->setLabel("Y");
|
||||
ui->plotWidget->xAxis->setLabel("X");
|
||||
connect(ui->treePlotColumns, &QTreeWidget::itemChanged, this, &PlotDock::on_treePlotColumns_itemChanged);
|
||||
}
|
||||
|
||||
// search for the x axis select
|
||||
QTreeWidgetItem* xitem = 0;
|
||||
for(int i = 0; i < ui->treePlotColumns->topLevelItemCount(); ++i)
|
||||
{
|
||||
xitem = ui->treePlotColumns->topLevelItem(i);
|
||||
if(xitem->checkState(PlotColumnX) == Qt::Checked)
|
||||
break;
|
||||
|
||||
xitem = 0;
|
||||
}
|
||||
|
||||
QStringList yAxisLabels;
|
||||
|
||||
ui->plotWidget->clearGraphs();
|
||||
if(xitem)
|
||||
{
|
||||
// regain the model column index and the datatype
|
||||
// leading 16 bit are column index, the other 16 bit are the datatype
|
||||
// right now datatype is only important for X axis (date, non date)
|
||||
uint xitemdata = xitem->data(PlotColumnField, Qt::UserRole).toUInt();
|
||||
int x = xitemdata >> 16;
|
||||
int xtype = xitemdata & (uint)0xFF;
|
||||
|
||||
// check if we have a x axis with datetime data
|
||||
if(xtype == QVariant::DateTime)
|
||||
{
|
||||
ui->plotWidget->xAxis->setTickLabelType(QCPAxis::ltDateTime);
|
||||
ui->plotWidget->xAxis->setDateTimeFormat("yyyy-MM-dd");
|
||||
} else {
|
||||
ui->plotWidget->xAxis->setTickLabelType(QCPAxis::ltNumber);
|
||||
}
|
||||
|
||||
// add graph for each selected y axis
|
||||
for(int i = 0; i < ui->treePlotColumns->topLevelItemCount(); ++i)
|
||||
{
|
||||
QTreeWidgetItem* item = ui->treePlotColumns->topLevelItem(i);
|
||||
if(item->checkState((PlotColumnY)) == Qt::Checked)
|
||||
{
|
||||
// regain the model column index and the datatype
|
||||
// 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)));
|
||||
|
||||
// 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<double> xdata(model->rowCount()), ydata(model->rowCount());
|
||||
for(int i = 0; i < model->rowCount(); ++i)
|
||||
{
|
||||
// convert x type axis if it's datetime
|
||||
if(xtype == QVariant::DateTime)
|
||||
{
|
||||
QString s = model->data(model->index(i, x)).toString();
|
||||
QDateTime d = QDateTime::fromString(s, Qt::ISODate);
|
||||
xdata[i] = d.toTime_t();
|
||||
} else {
|
||||
// Get the x 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.
|
||||
if(x == 0xFFFF)
|
||||
xdata[i] = i+1;
|
||||
|
||||
else
|
||||
xdata[i] = model->data(model->index(i, x)).toDouble();
|
||||
}
|
||||
|
||||
// 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;
|
||||
if(column == 0xFFFF)
|
||||
pointdata = i+1;
|
||||
else
|
||||
pointdata = model->data(model->index(i, column), Qt::EditRole);
|
||||
|
||||
if(pointdata.isNull())
|
||||
ydata[i] = qQNaN();
|
||||
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));
|
||||
|
||||
// gather Y label column names
|
||||
yAxisLabels << model->headerData(column, Qt::Horizontal).toString();
|
||||
}
|
||||
}
|
||||
|
||||
ui->plotWidget->rescaleAxes(true);
|
||||
|
||||
// set axis labels
|
||||
ui->plotWidget->xAxis->setLabel(model->headerData(x, Qt::Horizontal).toString());
|
||||
ui->plotWidget->yAxis->setLabel(yAxisLabels.join("|"));
|
||||
}
|
||||
ui->plotWidget->replot();
|
||||
}
|
||||
|
||||
void PlotDock::on_treePlotColumns_itemChanged(QTreeWidgetItem* changeitem, int column)
|
||||
{
|
||||
// disable change updates, or we get unwanted redrawing and weird behavior
|
||||
disconnect(ui->treePlotColumns, SIGNAL(itemChanged(QTreeWidgetItem*,int)),
|
||||
this,SLOT(on_treePlotColumns_itemChanged(QTreeWidgetItem*,int)));
|
||||
|
||||
// make sure only 1 X axis is selected
|
||||
if(column == PlotColumnX)
|
||||
{
|
||||
for(int i = 0; i < ui->treePlotColumns->topLevelItemCount(); ++i)
|
||||
{
|
||||
QTreeWidgetItem* item = ui->treePlotColumns->topLevelItem(i);
|
||||
if(item->checkState(column) == Qt::Checked && item != changeitem)
|
||||
{
|
||||
item->setCheckState(column, Qt::Unchecked);
|
||||
}
|
||||
}
|
||||
|
||||
// Save settings for this table
|
||||
if(m_currentTableSettings)
|
||||
{
|
||||
if(changeitem->checkState(column) == Qt::Checked)
|
||||
m_currentTableSettings->plotXAxis = changeitem->text(PlotColumnField);
|
||||
else
|
||||
m_currentTableSettings->plotXAxis = QString();
|
||||
}
|
||||
} else if(column == PlotColumnY) {
|
||||
if(changeitem->checkState(column) == Qt::Checked)
|
||||
{
|
||||
// Generate a default colour if none isn't set yet
|
||||
QColor colour = changeitem->backgroundColor(column);
|
||||
if(!colour.isValid())
|
||||
{
|
||||
static int last_colour_index = 0;
|
||||
switch(last_colour_index++)
|
||||
{
|
||||
case 0:
|
||||
colour = QColor(0, 69, 134);
|
||||
break;
|
||||
case 1:
|
||||
colour = QColor(255, 66, 14);
|
||||
break;
|
||||
case 2:
|
||||
colour = QColor(255, 211, 32);
|
||||
break;
|
||||
case 3:
|
||||
colour = QColor(87, 157, 28);
|
||||
break;
|
||||
case 4:
|
||||
colour = QColor(126, 0, 33);
|
||||
break;
|
||||
case 5:
|
||||
colour = QColor(131, 202, 255);
|
||||
break;
|
||||
case 6:
|
||||
colour = QColor(49, 64, 4);
|
||||
break;
|
||||
case 7:
|
||||
colour = QColor(174, 207, 0);
|
||||
break;
|
||||
case 8:
|
||||
colour = QColor(75, 31, 111);
|
||||
break;
|
||||
case 9:
|
||||
colour = QColor(255, 149, 14);
|
||||
break;
|
||||
case 10:
|
||||
colour = QColor(197, 00, 11);
|
||||
break;
|
||||
case 11:
|
||||
colour = QColor(0, 132, 209);
|
||||
|
||||
// Since this is the last colour in our table, reset the counter back
|
||||
// to the first colour
|
||||
last_colour_index = 0;
|
||||
break;
|
||||
default:
|
||||
// NOTE: This shouldn't happen!
|
||||
colour = QColor(0, 0, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Set colour
|
||||
changeitem->setBackgroundColor(column, colour);
|
||||
|
||||
// Save settings for this table
|
||||
if(m_currentTableSettings)
|
||||
{
|
||||
PlotSettings& plot_settings = m_currentTableSettings->plotYAxes[changeitem->text(PlotColumnField)];
|
||||
plot_settings.colour = colour;
|
||||
plot_settings.lineStyle = ui->comboLineType->currentIndex();
|
||||
plot_settings.pointShape = (ui->comboPointShape->currentIndex() > 0 ? (ui->comboPointShape->currentIndex()+1) : ui->comboPointShape->currentIndex());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
connect(ui->treePlotColumns, SIGNAL(itemChanged(QTreeWidgetItem*,int)),
|
||||
this,SLOT(on_treePlotColumns_itemChanged(QTreeWidgetItem*,int)));
|
||||
|
||||
updatePlot(m_currentPlotModel, m_currentTableSettings, false);
|
||||
}
|
||||
|
||||
void PlotDock::on_treePlotColumns_itemDoubleClicked(QTreeWidgetItem* item, int column)
|
||||
{
|
||||
// disable change updates, or we get unwanted redrawing and weird behavior
|
||||
disconnect(ui->treePlotColumns, SIGNAL(itemChanged(QTreeWidgetItem*,int)),
|
||||
this,SLOT(on_treePlotColumns_itemChanged(QTreeWidgetItem*,int)));
|
||||
|
||||
if(column == PlotColumnY)
|
||||
{
|
||||
// On double click open the colordialog
|
||||
QColorDialog colordialog(this);
|
||||
QColor curbkcolor = item->backgroundColor(column);
|
||||
QColor precolor = !curbkcolor.isValid() ? (Qt::GlobalColor)(qrand() % 13 + 5) : curbkcolor;
|
||||
QColor color = colordialog.getColor(precolor, this, tr("Choose a axis color"));
|
||||
if(color.isValid())
|
||||
{
|
||||
item->setCheckState(column, Qt::Checked);
|
||||
item->setBackgroundColor(column, color);
|
||||
|
||||
// Save settings for this table
|
||||
if(m_currentTableSettings)
|
||||
{
|
||||
PlotSettings& plot_settings = m_currentTableSettings->plotYAxes[item->text(PlotColumnField)];
|
||||
plot_settings.colour = color;
|
||||
plot_settings.lineStyle = ui->comboLineType->currentIndex();
|
||||
plot_settings.pointShape = (ui->comboPointShape->currentIndex() > 0 ? (ui->comboPointShape->currentIndex()+1) : ui->comboPointShape->currentIndex());
|
||||
}
|
||||
} else {
|
||||
item->setCheckState(column, Qt::Unchecked);
|
||||
|
||||
// Save settings for this table
|
||||
if(m_currentTableSettings)
|
||||
m_currentTableSettings->plotYAxes.remove(item->text(PlotColumnField));
|
||||
}
|
||||
}
|
||||
|
||||
connect(ui->treePlotColumns, SIGNAL(itemChanged(QTreeWidgetItem*,int)),
|
||||
this,SLOT(on_treePlotColumns_itemChanged(QTreeWidgetItem*,int)));
|
||||
|
||||
updatePlot(m_currentPlotModel, m_currentTableSettings, false);
|
||||
}
|
||||
|
||||
void PlotDock::on_butSavePlot_clicked()
|
||||
{
|
||||
QString fileName = FileDialog::getSaveFileName(this,
|
||||
tr("Choose a filename to save under"),
|
||||
tr("PNG(*.png);;JPG(*.jpg);;PDF(*.pdf);;BMP(*.bmp);;All Files(*)")
|
||||
);
|
||||
if(!fileName.isEmpty())
|
||||
{
|
||||
if(fileName.endsWith(".png", Qt::CaseInsensitive))
|
||||
{
|
||||
ui->plotWidget->savePng(fileName);
|
||||
}
|
||||
else if(fileName.endsWith(".jpg", Qt::CaseInsensitive))
|
||||
{
|
||||
ui->plotWidget->saveJpg(fileName);
|
||||
}
|
||||
else if(fileName.endsWith(".pdf", Qt::CaseInsensitive))
|
||||
{
|
||||
ui->plotWidget->savePdf(fileName);
|
||||
}
|
||||
else if(fileName.endsWith(".bmp", Qt::CaseInsensitive))
|
||||
{
|
||||
ui->plotWidget->saveBmp(fileName);
|
||||
}
|
||||
else
|
||||
{
|
||||
fileName += ".png";
|
||||
ui->plotWidget->savePng(fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PlotDock::on_comboLineType_currentIndexChanged(int index)
|
||||
{
|
||||
Q_ASSERT(index >= QCPGraph::lsNone &&
|
||||
index <= QCPGraph::lsImpulse);
|
||||
QCPGraph::LineStyle lineStyle = (QCPGraph::LineStyle) index;
|
||||
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();
|
||||
|
||||
// Save settings for this table
|
||||
if(m_currentTableSettings)
|
||||
{
|
||||
QMap<QString, PlotSettings>& graphs = m_currentTableSettings->plotYAxes;
|
||||
auto it = graphs.begin();
|
||||
while(it != graphs.end())
|
||||
{
|
||||
it.value().lineStyle = lineStyle;
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PlotDock::on_comboPointShape_currentIndexChanged(int index)
|
||||
{
|
||||
// WARN: because ssDot point shape is removed
|
||||
if (index > 0) index += 1;
|
||||
Q_ASSERT(index >= QCPScatterStyle::ssNone &&
|
||||
index < QCPScatterStyle::ssPixmap);
|
||||
QCPScatterStyle::ScatterShape shape = (QCPScatterStyle::ScatterShape) index;
|
||||
for (int i = 0, ie = ui->plotWidget->graphCount(); i < ie; ++i)
|
||||
{
|
||||
QCPGraph * graph = ui->plotWidget->graph(i);
|
||||
if (graph)
|
||||
graph->setScatterStyle(QCPScatterStyle(shape, 5));
|
||||
}
|
||||
ui->plotWidget->replot();
|
||||
|
||||
// Save settings for this table
|
||||
if(m_currentTableSettings)
|
||||
{
|
||||
QMap<QString, PlotSettings>& graphs = m_currentTableSettings->plotYAxes;
|
||||
auto it = graphs.begin();
|
||||
while(it != graphs.end())
|
||||
{
|
||||
it.value().pointShape = shape;
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QVariant::Type PlotDock::guessDataType(SqliteTableModel* model, int column)
|
||||
{
|
||||
QVariant::Type type = QVariant::Invalid;
|
||||
for(int i = 0; i < std::min(10, model->rowCount()) && type != QVariant::String; ++i)
|
||||
{
|
||||
QVariant data = model->data(model->index(i, column), Qt::EditRole);
|
||||
if(data.isNull() || data.convert(QVariant::Double))
|
||||
{
|
||||
type = QVariant::Double;
|
||||
} else {
|
||||
QString s = model->data(model->index(i, column)).toString();
|
||||
QDate d = QDate::fromString(s, Qt::ISODate);
|
||||
if(d.isValid())
|
||||
type = QVariant::DateTime;
|
||||
else
|
||||
type = QVariant::String;
|
||||
}
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
void PlotDock::fetchAllData()
|
||||
{
|
||||
if(m_currentPlotModel)
|
||||
{
|
||||
// Show progress dialog because fetching all data might take some time
|
||||
QProgressDialog progress(tr("Fetching all data..."),
|
||||
tr("Cancel"), m_currentPlotModel->rowCount(), m_currentPlotModel->totalRowCount());
|
||||
progress.setWindowModality(Qt::ApplicationModal);
|
||||
progress.show();
|
||||
qApp->processEvents();
|
||||
|
||||
// Make sure all data is loaded
|
||||
while(m_currentPlotModel->canFetchMore())
|
||||
{
|
||||
// Fetch the next bunch of data
|
||||
m_currentPlotModel->fetchMore();
|
||||
|
||||
// Update the progress dialog and stop loading data when the cancel button was pressed
|
||||
progress.setValue(m_currentPlotModel->rowCount());
|
||||
qApp->processEvents();
|
||||
if(progress.wasCanceled())
|
||||
break;
|
||||
}
|
||||
|
||||
// Update plot
|
||||
updatePlot(m_currentPlotModel, m_currentTableSettings);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
#ifndef PLOTDOCK_H
|
||||
#define PLOTDOCK_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QVariant>
|
||||
|
||||
class SqliteTableModel;
|
||||
class QTreeWidgetItem;
|
||||
class BrowseDataTableSettings;
|
||||
|
||||
namespace Ui {
|
||||
class PlotDock;
|
||||
}
|
||||
|
||||
class PlotDock : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit PlotDock(QWidget* parent = 0);
|
||||
~PlotDock();
|
||||
|
||||
struct PlotSettings
|
||||
{
|
||||
int lineStyle;
|
||||
int pointShape;
|
||||
QColor colour;
|
||||
|
||||
friend QDataStream& operator<<(QDataStream& stream, const PlotDock::PlotSettings& object)
|
||||
{
|
||||
stream << object.lineStyle;
|
||||
stream << object.pointShape;
|
||||
stream << object.colour;
|
||||
|
||||
return stream;
|
||||
}
|
||||
friend QDataStream& operator>>(QDataStream& stream, PlotDock::PlotSettings& object)
|
||||
{
|
||||
stream >> object.lineStyle;
|
||||
stream >> object.pointShape;
|
||||
stream >> object.colour;
|
||||
|
||||
return stream;
|
||||
}
|
||||
};
|
||||
|
||||
public slots:
|
||||
void updatePlot(SqliteTableModel* model, BrowseDataTableSettings* settings = nullptr, bool update = true, bool keepOrResetSelection = true);
|
||||
void fetchAllData();
|
||||
|
||||
private:
|
||||
enum PlotColumns
|
||||
{
|
||||
PlotColumnField = 0,
|
||||
PlotColumnX = 1,
|
||||
PlotColumnY = 2,
|
||||
PlotColumnDummy = 3
|
||||
};
|
||||
|
||||
Ui::PlotDock* ui;
|
||||
|
||||
SqliteTableModel* m_currentPlotModel;
|
||||
BrowseDataTableSettings* m_currentTableSettings;
|
||||
|
||||
/*!
|
||||
* \brief guessdatatype try to parse the first 10 rows and decide the datatype
|
||||
* \param model model to check the data
|
||||
* \param column index of the column to check
|
||||
* \return the guessed datatype
|
||||
*/
|
||||
QVariant::Type guessDataType(SqliteTableModel* model, int column);
|
||||
|
||||
private slots:
|
||||
void on_treePlotColumns_itemChanged(QTreeWidgetItem* item, int column);
|
||||
void on_treePlotColumns_itemDoubleClicked(QTreeWidgetItem* item, int column);
|
||||
void on_butSavePlot_clicked();
|
||||
void on_comboLineType_currentIndexChanged(int index);
|
||||
void on_comboPointShape_currentIndexChanged(int index);
|
||||
};
|
||||
|
||||
#endif
|
||||
+314
@@ -0,0 +1,314 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>PlotDock</class>
|
||||
<widget class="QDialog" name="PlotDock">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>478</width>
|
||||
<height>553</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Plot</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QSplitter" name="splitterForPlot">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<widget class="QTreeWidget" name="treePlotColumns">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>2</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="columnCount">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<attribute name="headerDefaultSectionSize">
|
||||
<number>100</number>
|
||||
</attribute>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Columns</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>X</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Y</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>_</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
<widget class="QCustomPlot" name="plotWidget" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>8</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Line type:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>comboLineType</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="widget" native="true">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QComboBox" name="comboLineType">
|
||||
<property name="currentIndex">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>None</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Line</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>StepLeft</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>StepRight</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>StepCenter</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Impulse</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Point shape:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>comboPointShape</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="comboPointShape">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>None</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Cross</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Plus</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Circle</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Disc</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Square</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Diamond</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Star</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Triangle</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>TriangleInverted</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>CrossSquare</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>PlusSquare</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>CrossCircle</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>PlusCircle</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Peace</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_4">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="butSavePlot">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Save current plot...</p><p>File format chosen by extension (png, jpg, pdf, bmp)</p></body></html></string>
|
||||
</property>
|
||||
<property name="statusTip">
|
||||
<string>Save current plot...</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="icons/icons.qrc">
|
||||
<normaloff>:/icons/save_table</normaloff>:/icons/save_table</iconset>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="autoDefault">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="default">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="buttonLoadAllData">
|
||||
<property name="toolTip">
|
||||
<string>Load all data. This has only an effect if not all data has been fetched from the table yet due to the partial fetch mechanism.</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="icons/icons.qrc">
|
||||
<normaloff>:/icons/keyword</normaloff>:/icons/keyword</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>QCustomPlot</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>qcustomplot.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources>
|
||||
<include location="icons/icons.qrc"/>
|
||||
</resources>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonLoadAllData</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>PlotDock</receiver>
|
||||
<slot>fetchAllData()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>463</x>
|
||||
<y>526</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>477</x>
|
||||
<y>495</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
<slots>
|
||||
<slot>fetchAllData()</slot>
|
||||
</slots>
|
||||
</ui>
|
||||
+6
-3
@@ -53,7 +53,8 @@ HEADERS += \
|
||||
ColumnDisplayFormatDialog.h \
|
||||
FilterLineEdit.h \
|
||||
RemoteDatabase.h \
|
||||
ForeignKeyEditorDelegate.h
|
||||
ForeignKeyEditorDelegate.h \
|
||||
PlotDock.h
|
||||
|
||||
SOURCES += \
|
||||
sqlitedb.cpp \
|
||||
@@ -85,7 +86,8 @@ SOURCES += \
|
||||
ColumnDisplayFormatDialog.cpp \
|
||||
FilterLineEdit.cpp \
|
||||
RemoteDatabase.cpp \
|
||||
ForeignKeyEditorDelegate.cpp
|
||||
ForeignKeyEditorDelegate.cpp \
|
||||
PlotDock.cpp
|
||||
|
||||
RESOURCES += icons/icons.qrc \
|
||||
translations/flags/flags.qrc \
|
||||
@@ -105,7 +107,8 @@ FORMS += \
|
||||
VacuumDialog.ui \
|
||||
CipherDialog.ui \
|
||||
ExportSqlDialog.ui \
|
||||
ColumnDisplayFormatDialog.ui
|
||||
ColumnDisplayFormatDialog.ui \
|
||||
PlotDock.ui
|
||||
|
||||
TRANSLATIONS += \
|
||||
translations/sqlb_ar_SA.ts \
|
||||
|
||||
Reference in New Issue
Block a user