Add table view to database tree
This commit is contained in:
+181
-24
@@ -1,20 +1,31 @@
|
||||
#include "databasetree.h"
|
||||
|
||||
#include "database.h"
|
||||
#include "databaseview.h"
|
||||
#include <QComboBox>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
#include <QSettings>
|
||||
#include <QSqlError>
|
||||
#include <QStackedWidget>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
DatabaseTreeSettings::DatabaseTreeSettings(const QString &filter, const QStringList &keywords, QWidget *parent) : QDialog(parent)
|
||||
DatabaseTreeSettings::DatabaseTreeSettings(const QStringList &data, QStringList keywords, QWidget *parent) : QDialog(parent)
|
||||
{
|
||||
setWindowTitle(tr("Add tree filter"));
|
||||
QVBoxLayout *vlayout = new QVBoxLayout(this);
|
||||
setLayout(vlayout);
|
||||
|
||||
QStringList key = filter.split('/');
|
||||
QStringList key = data[0].split('/');
|
||||
|
||||
qsizetype dateobsindex = keywords.indexOf("DATE-OBS");
|
||||
if(dateobsindex != -1)
|
||||
{
|
||||
keywords.insert(dateobsindex + 1, "DATE-OBS_YEAR-MONTH-DAY");
|
||||
keywords.insert(dateobsindex + 1, "DATE-OBS_YEAR-MONTH");
|
||||
keywords.insert(dateobsindex + 1, "DATE-OBS_YEAR");
|
||||
}
|
||||
|
||||
for(int i = 0; i < 10; i++)
|
||||
{
|
||||
@@ -27,8 +38,15 @@ DatabaseTreeSettings::DatabaseTreeSettings(const QString &filter, const QStringL
|
||||
comboxBox->setCurrentText(key[i]);
|
||||
}
|
||||
|
||||
vlayout->addWidget(new QLabel(tr("Aggregate function"), this));
|
||||
_aggregateFunction = new QComboBox(this);
|
||||
_aggregateFunction->addItems({"", "SUM", "COUNT", "AVG", "MIN", "MAX", "MEDIAN"});
|
||||
vlayout->addWidget(_aggregateFunction);
|
||||
_aggregateFunction->setToolTip(tr("This aggregate function will be applied to last level of grouping"));
|
||||
_aggregateFunction->setCurrentText(data[1]);
|
||||
|
||||
QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
|
||||
connect(buttonBox, &QDialogButtonBox::accepted, this, &DatabaseTreeSettings::accept);
|
||||
connect(buttonBox, &QDialogButtonBox::accepted, this, &DatabaseTreeSettings::acceptButton);
|
||||
connect(buttonBox, &QDialogButtonBox::rejected, this, &DatabaseTreeSettings::reject);
|
||||
vlayout->addWidget(buttonBox);
|
||||
}
|
||||
@@ -44,6 +62,24 @@ QString DatabaseTreeSettings::keywords() const
|
||||
return keywords.join('/');
|
||||
}
|
||||
|
||||
QString DatabaseTreeSettings::aggregrationFunc() const
|
||||
{
|
||||
return _aggregateFunction->currentText();
|
||||
}
|
||||
|
||||
void DatabaseTreeSettings::acceptButton()
|
||||
{
|
||||
for(QComboBox *box : _keywordsSelect)
|
||||
{
|
||||
if(box->currentIndex() > 0)
|
||||
{
|
||||
QDialog::accept();
|
||||
return;
|
||||
}
|
||||
}
|
||||
QDialog::reject();
|
||||
}
|
||||
|
||||
class TreeNode
|
||||
{
|
||||
public:
|
||||
@@ -130,6 +166,11 @@ void DatabaseTree::setKeys(const QStringList &keys)
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
QStringList DatabaseTree::keys() const
|
||||
{
|
||||
return _keys;
|
||||
}
|
||||
|
||||
QModelIndex DatabaseTree::index(int row, int column, const QModelIndex &parent) const
|
||||
{
|
||||
if(!hasIndex(row, column, parent))
|
||||
@@ -262,6 +303,51 @@ void DatabaseTree::load()
|
||||
}
|
||||
}
|
||||
|
||||
QSqlQuery DatabaseTree::getGroupQuery(const QString &aggregateFunc) const
|
||||
{
|
||||
QStringList cols;
|
||||
QString join;
|
||||
QString sum;
|
||||
|
||||
for(int i = 0; i < _keys.size(); i++)
|
||||
{
|
||||
join += QString(" LEFT JOIN fits_headers AS h%1 ON f.id = h%1.id_file AND h%1.key = ?").arg(i);
|
||||
if(_keys[i] == "DATE-OBS_YEAR")
|
||||
cols.append(QString("STRFTIME('%Y', h%1.value)").arg(i));
|
||||
else if(_keys[i] == "DATE-OBS_YEAR-MONTH")
|
||||
cols.append(QString("STRFTIME('%Y-%m', h%1.value)").arg(i));
|
||||
else if(_keys[i] == "DATE-OBS_YEAR-MONTH-DAY")
|
||||
cols.append(QString("STRFTIME('%Y-%m-%d', h%1.value)").arg(i));
|
||||
else
|
||||
cols.append(QString("h%1.value").arg(i));
|
||||
|
||||
if(i == _keys.size() - 1)
|
||||
{
|
||||
QString tmp = aggregateFunc + "(" + cols.last() + ")";
|
||||
cols.last() = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
QStringList group = cols;
|
||||
group.removeLast();
|
||||
QString sql = "SELECT " + cols.join(',') + " FROM fits_files AS f" + join + " GROUP BY " + group.join(',');
|
||||
|
||||
QSqlQuery query(sql, _database->db());
|
||||
for(auto &val : _keys)
|
||||
{
|
||||
if(val.startsWith("DATE-OBS_"))
|
||||
query.addBindValue("DATE-OBS");
|
||||
else
|
||||
query.addBindValue(val);
|
||||
}
|
||||
|
||||
qDebug() << "Group query" << sql;
|
||||
if(!query.exec())
|
||||
qWarning() << "Group query failed" << query.lastError();
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
void DatabaseTree::prepareQueries()
|
||||
{
|
||||
if(!_loaded)return;
|
||||
@@ -276,22 +362,42 @@ void DatabaseTree::prepareQueries()
|
||||
|
||||
for(int i = 0; i < _keys.size(); i++)
|
||||
{
|
||||
QString sql = QString("SELECT h%1.value FROM fits_files AS f").arg(i) + join + where + QString(" GROUP BY h%1.value ORDER BY h%1.value").arg(i);
|
||||
qDebug() << _keys[i] << sql;
|
||||
QString sql;
|
||||
QString col = QString("h%1.value").arg(i);
|
||||
if(_keys[i] == "DATE-OBS_YEAR")
|
||||
col = QString("STRFTIME('%Y', h%1.value)").arg(i);
|
||||
else if(_keys[i] == "DATE-OBS_YEAR-MONTH")
|
||||
col = QString("STRFTIME('%Y-%m', h%1.value)").arg(i);
|
||||
else if(_keys[i] == "DATE-OBS_YEAR-MONTH-DAY")
|
||||
col = QString("STRFTIME('%Y-%m-%d', h%1.value)").arg(i);
|
||||
|
||||
sql = QString("SELECT %1 FROM fits_files AS f").arg(col) + join + where + QString(" GROUP BY %1 ORDER BY %1").arg(col);
|
||||
|
||||
qDebug() << "Tree query for" << _keys[i] << sql;
|
||||
QSqlQuery query(sql, _database->db());
|
||||
for(auto &val : _keys)
|
||||
query.addBindValue(val);
|
||||
{
|
||||
if(val.startsWith("DATE-OBS_"))
|
||||
query.addBindValue("DATE-OBS");
|
||||
else
|
||||
query.addBindValue(val);
|
||||
}
|
||||
|
||||
if(where.isEmpty())
|
||||
where += QString(" WHERE h%1.value IS ?").arg(i);
|
||||
where += QString(" WHERE %1 IS ?").arg(col);
|
||||
else
|
||||
where += QString(" AND h%1.value IS ?").arg(i);
|
||||
where += QString(" AND %1 IS ?").arg(col);
|
||||
_queries.append(std::move(query));
|
||||
}
|
||||
|
||||
QSqlQuery files("SELECT f.file FROM fits_files AS f " + join + where + " GROUP BY f.id ORDER BY f.file", _database->db());
|
||||
QSqlQuery files("SELECT f.file FROM fits_files AS f" + join + where + " GROUP BY f.id ORDER BY f.file", _database->db());
|
||||
for(auto &val : _keys)
|
||||
files.addBindValue(val);
|
||||
{
|
||||
if(val.startsWith("DATE-OBS_"))
|
||||
files.addBindValue("DATE-OBS");
|
||||
else
|
||||
files.addBindValue(val);
|
||||
}
|
||||
qDebug() << files.lastQuery();
|
||||
_queries.append(std::move(files));
|
||||
}
|
||||
@@ -322,7 +428,7 @@ void DatabaseTree::fillNode(TreeNode *node)
|
||||
q.bindValue(i + _keys.size(), vals[i]);
|
||||
if(!q.exec())
|
||||
{
|
||||
qWarning() << "Failed to execute query" << q.lastError();
|
||||
qWarning() << "Failed to execute query" << q.lastError() << q.lastQuery() << q.boundValues();
|
||||
node->fill({});
|
||||
return;
|
||||
}
|
||||
@@ -345,25 +451,44 @@ DatabaseTreeView::DatabaseTreeView(Database *database, QWidget *parent) : QWidge
|
||||
_treeView->setModel(_model);
|
||||
_treeView->setHeaderHidden(true);
|
||||
|
||||
_tableView = new CopyTableView(this);
|
||||
_sqlModel = new QSqlQueryModel(this);
|
||||
_tableView->setModel(_sqlModel);
|
||||
|
||||
QSettings settings;
|
||||
QStringList filters = settings.value("databasetreeview/filters", QStringList{"OBJECT", "OBJECT/IMAGETYP", "OBJECT/IMAGETYP/FILTER", "OBJECT/IMAGETYP/FILTER/EXPTIME", "IMAGETYP/OBJECT/IMAGETYP/FILTER/EXPTIME"}).toStringList();
|
||||
QStringList filters = settings.value("databasetreeview/filters", QStringList{"OBJECT", "OBJECT/IMAGETYP", "OBJECT/IMAGETYP/FILTER", "OBJECT/IMAGETYP/FILTER/EXPTIME",
|
||||
"IMAGETYP/OBJECT/IMAGETYP/FILTER/EXPTIME", "IMAGETYP/DATE-OBS_YEAR/EXPTIME"}).toStringList();
|
||||
QStringList aggrFuncs = settings.value("databasetreeview/aggrFuncs", QStringList{"", "", "", "SUM", "SUM", "SUM"}).toStringList();
|
||||
int selectedFilter = settings.value("databasetreeview/selectedFilter", 2).toInt();
|
||||
|
||||
_filters = new QComboBox(this);
|
||||
_filters->addItems(filters);
|
||||
for(int i = 0; i < std::min(filters.size(), aggrFuncs.size()); i++)
|
||||
{
|
||||
_filters->addItem(filters[i] + " " + aggrFuncs[i], QStringList{filters[i], aggrFuncs[i]});
|
||||
}
|
||||
_filters->setCurrentIndex(selectedFilter);
|
||||
connect(_filters, &QComboBox::currentTextChanged, this, &DatabaseTreeView::filterChanged);
|
||||
filterChanged(_filters->currentText());
|
||||
connect(_filters, &QComboBox::currentIndexChanged, this, &DatabaseTreeView::filterChanged);
|
||||
filterChanged(_filters->currentIndex());
|
||||
|
||||
QStackedWidget *stackedWidget = new QStackedWidget;
|
||||
stackedWidget->addWidget(_treeView);
|
||||
stackedWidget->addWidget(_tableView);
|
||||
|
||||
QPushButton *addButton = new QPushButton(tr("Add"), this);
|
||||
QPushButton *removeButton = new QPushButton(tr("Remove"), this);
|
||||
QPushButton *treeTableButton = new QPushButton(tr("Tree/Table"), this);
|
||||
treeTableButton->setCheckable(true);
|
||||
connect(treeTableButton, &QPushButton::clicked, [stackedWidget](bool checked){
|
||||
stackedWidget->setCurrentIndex(checked ? 1 : 0);
|
||||
});
|
||||
|
||||
hlayout->addWidget(_filters, 1);
|
||||
hlayout->addWidget(addButton);
|
||||
hlayout->addWidget(removeButton);
|
||||
hlayout->addWidget(treeTableButton);
|
||||
|
||||
vlayout->addLayout(hlayout);
|
||||
vlayout->addWidget(_treeView);
|
||||
vlayout->addWidget(stackedWidget);
|
||||
|
||||
connect(_treeView, &QTreeView::activated, [this](const QModelIndex &index){
|
||||
if(!_model->hasChildren(index))
|
||||
@@ -380,27 +505,36 @@ DatabaseTreeView::DatabaseTreeView(Database *database, QWidget *parent) : QWidge
|
||||
DatabaseTreeView::~DatabaseTreeView()
|
||||
{
|
||||
QStringList filters;
|
||||
QStringList aggrFuncs;
|
||||
for(int i = 0; i < _filters->count(); i++)
|
||||
filters.append(_filters->itemText(i));
|
||||
{
|
||||
QStringList data = _filters->itemData(i).toStringList();
|
||||
filters.append(data[0]);
|
||||
aggrFuncs.append(data[1]);
|
||||
}
|
||||
|
||||
QSettings settings;
|
||||
settings.setValue("databasetreeview/filters", filters);
|
||||
settings.setValue("databasetreeview/aggrFuncs", aggrFuncs);
|
||||
settings.setValue("databasetreeview/selectedFilter", _filters->currentIndex());
|
||||
}
|
||||
|
||||
void DatabaseTreeView::addFilter()
|
||||
{
|
||||
QStringList keywords = _database->getFitsKeywords();
|
||||
DatabaseTreeSettings settings(_filters->currentText(), keywords, this);
|
||||
QStringList data = _filters->currentData().toStringList();
|
||||
DatabaseTreeSettings settings(data, keywords, this);
|
||||
int result = settings.exec();
|
||||
if(result == QDialog::Accepted)
|
||||
{
|
||||
QString keywords = settings.keywords();
|
||||
int idx = _filters->findText(keywords);
|
||||
QString aggrFunc = settings.aggregrationFunc();
|
||||
QString text = keywords + " " + aggrFunc;
|
||||
int idx = _filters->findText(text);
|
||||
if(idx == -1)
|
||||
{
|
||||
_filters->addItem(keywords);
|
||||
_filters->setCurrentText(keywords);
|
||||
_filters->addItem(text, QStringList{keywords, aggrFunc});
|
||||
_filters->setCurrentText(text);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -415,14 +549,37 @@ void DatabaseTreeView::removeFilter()
|
||||
_filters->removeItem(_filters->currentIndex());
|
||||
}
|
||||
|
||||
void DatabaseTreeView::filterChanged(const QString &filter)
|
||||
void DatabaseTreeView::filterChanged(int index)
|
||||
{
|
||||
QStringList keys = filter.split('/');
|
||||
QStringList data = _filters->itemData(index).toStringList();
|
||||
QStringList keys = data[0].split('/');
|
||||
_model->setKeys(keys);
|
||||
setQuery(data[1]);
|
||||
}
|
||||
|
||||
void DatabaseTreeView::visible(bool visible)
|
||||
{
|
||||
if(visible)
|
||||
if(visible && !_loaded)
|
||||
{
|
||||
_loaded = true;
|
||||
_model->load();
|
||||
QStringList data = _filters->currentData().toStringList();
|
||||
setQuery(data[1]);
|
||||
}
|
||||
}
|
||||
|
||||
void DatabaseTreeView::setQuery(const QString &func)
|
||||
{
|
||||
QStringList keys = _model->keys();
|
||||
int i = 0;
|
||||
_sqlModel->setQuery(_model->getGroupQuery(func));
|
||||
if(!func.isEmpty())
|
||||
{
|
||||
QString tmp = func + "(" + keys.last() + ")";
|
||||
keys.last() = tmp;
|
||||
}
|
||||
for(auto &key : keys)
|
||||
_sqlModel->setHeaderData(i++, Qt::Horizontal, key);
|
||||
|
||||
_tableView->resizeColumnsToContents();
|
||||
}
|
||||
|
||||
+20
-6
@@ -2,11 +2,13 @@
|
||||
#define DATABASETREE_H
|
||||
|
||||
#include <QAbstractItemModel>
|
||||
#include <QTreeView>
|
||||
#include <QSqlQuery>
|
||||
#include <QFont>
|
||||
#include <QDialog>
|
||||
#include <QComboBox>
|
||||
#include <QDialog>
|
||||
#include <QFont>
|
||||
#include <QSqlQuery>
|
||||
#include <QSqlQueryModel>
|
||||
#include <QTableView>
|
||||
#include <QTreeView>
|
||||
#include <memory>
|
||||
|
||||
class Database;
|
||||
@@ -14,11 +16,16 @@ class TreeNode;
|
||||
|
||||
class DatabaseTreeSettings : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit DatabaseTreeSettings(const QString &filter, const QStringList &keywords, QWidget *parent = nullptr);
|
||||
explicit DatabaseTreeSettings(const QStringList &data, QStringList keywords, QWidget *parent = nullptr);
|
||||
QString keywords() const;
|
||||
QString aggregrationFunc() const;
|
||||
public slots:
|
||||
void acceptButton();
|
||||
private:
|
||||
QVector<QComboBox*> _keywordsSelect;
|
||||
QComboBox *_aggregateFunction;
|
||||
};
|
||||
|
||||
class DatabaseTree : public QAbstractItemModel
|
||||
@@ -26,6 +33,7 @@ class DatabaseTree : public QAbstractItemModel
|
||||
public:
|
||||
explicit DatabaseTree(Database *database, QObject *parent = nullptr);
|
||||
void setKeys(const QStringList &keys);
|
||||
QStringList keys() const;
|
||||
QModelIndex index(int row, int column, const QModelIndex &parent) const override;
|
||||
QModelIndex parent(const QModelIndex &index) const override;
|
||||
int rowCount(const QModelIndex &index) const override;
|
||||
@@ -35,6 +43,7 @@ public:
|
||||
void fetchMore(const QModelIndex &parent) override;
|
||||
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
|
||||
void load();
|
||||
QSqlQuery getGroupQuery(const QString &aggregateFunc) const;
|
||||
private:
|
||||
void prepareQueries();
|
||||
void fillNode(TreeNode *node);
|
||||
@@ -55,15 +64,20 @@ public:
|
||||
public slots:
|
||||
void addFilter();
|
||||
void removeFilter();
|
||||
void filterChanged(const QString &filter);
|
||||
void filterChanged(int index);
|
||||
void visible(bool visible);
|
||||
private:
|
||||
void setQuery(const QString &func);
|
||||
signals:
|
||||
void loadFile(const QString &file);
|
||||
private:
|
||||
QComboBox *_filters = nullptr;
|
||||
QTreeView *_treeView = nullptr;
|
||||
QTableView *_tableView = nullptr;
|
||||
DatabaseTree *_model = nullptr;
|
||||
QSqlQueryModel *_sqlModel = nullptr;
|
||||
Database *_database = nullptr;
|
||||
bool _loaded = false;
|
||||
};
|
||||
|
||||
#endif // DATABASETREE_H
|
||||
|
||||
Reference in New Issue
Block a user