QueryTool now uses the same DataGrid as the CRUD tool.

This commit is contained in:
eelke 2026-10-18 10:29:24 +02:00
parent bbe52c9db8
commit 5e7fb68c55
15 changed files with 135 additions and 203 deletions

View file

@ -30,9 +30,9 @@ set(PGLAB_SOURCES
codeeditor/GutterPainter.cpp
crud/CrudModel.cpp
crud/CrudTab.cpp
crud/DataGridWidget.cpp
querytool/QueryExplainModel.cpp
querytool/QueryParamListController.cpp
querytool/QueryResultModel.cpp
querytool/QueryTool.cpp
serverinspector/DatabasesPage.cpp
serverinspector/RolesPage.cpp

View file

@ -10,6 +10,7 @@
#include "CustomDataRole.h"
#include <QtConcurrent>
#include <QFuture>
#include <QBrush>
#include <QMessageBox>
#include "Pgsql_oids.h"
#include "Pgsql_PgException.h"
@ -217,6 +218,8 @@ QVariant CrudModel::data(const QModelIndex &index, int role) const
if (value)
return StringToVariant(value->c_str(), typ);
}
else if (role == Qt::ForegroundRole && !m_wholeTable && columnIsReadOnly(index.column()))
return QBrush(Qt::gray);
return {};
}

View file

@ -3,16 +3,19 @@
#include "CrudModel.h"
#include "CustomDataRole.h"
#include "ResultTableModelUtil.h"
#include "util/PgLabItemDelegate.h"
#include "DataGridWidget.h"
#include "IntegerRange.h"
#include "OpenDatabase.h"
#include "catalog/PgClassContainer.h"
#include "catalog/PgDatabaseCatalog.h"
#include <QDebug>
#include <QHeaderView>
#include <QMenu>
#include <QSortFilterProxyModel>
#include <QMessageBox>
#include <iterator>
#include <set>
#include <QTableView>
CrudTab::CrudTab(IDatabaseWindow *context, QWidget *parent)
@ -24,25 +27,18 @@ CrudTab::CrudTab(IDatabaseWindow *context, QWidget *parent)
m_db = context->openDatabase();
SetTableViewDefault(ui->tableView);
auto delegate = new PgLabItemDelegate(ui->tableView);
ui->tableView->setItemDelegate(delegate);
m_crudModel = new CrudModel(nullptr);
m_SortFilterProxy = new QSortFilterProxyModel(this);
m_SortFilterProxy->setSourceModel(m_crudModel);
m_SortFilterProxy->setSortRole(CustomSortRole);
ui->tableView->setModel(m_SortFilterProxy);
ui->tableView->setSortingEnabled(true);
m_SortFilterProxy->sort(0, Qt::AscendingOrder);
m_grid = new DataGridWidget(this);
ui->verticalLayout->addWidget(m_grid);
m_grid->setSourceModel(m_crudModel);
m_grid->view()->verticalHeader()->setVisible(false);
ui->tableView->setSelectionMode(QAbstractItemView::ExtendedSelection);
ui->tableView->setSelectionBehavior(QAbstractItemView::SelectRows);
ui->tableView->addAction(ui->actionRemove_rows);
m_grid->view()->setSelectionMode(QAbstractItemView::ExtendedSelection);
m_grid->view()->setSelectionBehavior(QAbstractItemView::SelectRows);
m_grid->view()->addAction(ui->actionRemove_rows);
auto horizontal_header = ui->tableView->horizontalHeader();
auto horizontal_header = m_grid->view()->horizontalHeader();
horizontal_header->setContextMenuPolicy(Qt::ContextMenuPolicy::CustomContextMenu);
connect(horizontal_header, &QHeaderView::customContextMenuRequested,
this, &CrudTab::headerCustomContextMenu);
@ -70,7 +66,7 @@ void CrudTab::refresh()
void CrudTab::on_actionRemove_rows_triggered()
{
std::set<IntegerRange<int>> row_ranges;
auto selection = m_SortFilterProxy->mapSelectionToSource(ui->tableView->selectionModel()->selection());
auto selection = m_grid->proxy()->mapSelectionToSource(m_grid->view()->selectionModel()->selection());
for (auto& range : selection) {
row_ranges.emplace(range.top(), range.height());
}
@ -110,7 +106,7 @@ void CrudTab::headerCustomContextMenu(const QPoint &pos)
if (headerContextMenu)
{
auto horizontal_header = ui->tableView->horizontalHeader();
auto horizontal_header = m_grid->view()->horizontalHeader();
headerContextMenu->popup(horizontal_header->mapToGlobal(pos));
}
}
@ -122,9 +118,9 @@ void CrudTab::gotoColumn(QString column)
auto name = m_crudModel->headerData(i, Qt::Horizontal).toString();
if (name == column)
{
auto index = ui->tableView->currentIndex();
index = ui->tableView->model()->index(index.row(), i);
ui->tableView->setCurrentIndex(index);
auto index = m_grid->view()->currentIndex();
index = m_grid->view()->model()->index(index.row(), i);
m_grid->view()->setCurrentIndex(index);
break;
}
}

View file

@ -1,7 +1,6 @@
#ifndef CRUDTAB_H
#define CRUDTAB_H
#include <QSortFilterProxyModel>
#include "catalog/PgClass.h"
#include "IDatabaseWindow.h"
#include <QWidget>
@ -14,6 +13,7 @@ namespace Ui {
class OpenDatabase;
class CrudModel;
class DataGridWidget;
class QMenu;
class CrudTab : public QWidget {
@ -33,7 +33,7 @@ private:
std::optional<PgClass> m_table;
CrudModel *m_crudModel = nullptr;
QSortFilterProxyModel *m_SortFilterProxy = nullptr;
DataGridWidget *m_grid = nullptr;
QMenu *headerContextMenu = nullptr;

View file

@ -13,15 +13,7 @@
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QTableView" name="tableView">
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
</widget>
</item>
</layout>
<layout class="QVBoxLayout" name="verticalLayout"/>
<action name="actionRemove_rows">
<property name="text">
<string>Remove rows</string>

View file

@ -0,0 +1,34 @@
#include "DataGridWidget.h"
#include "CustomDataRole.h"
#include "ResultTableModelUtil.h"
#include "util/PgLabItemDelegate.h"
#include <QSortFilterProxyModel>
#include <QTableView>
#include <QVBoxLayout>
DataGridWidget::DataGridWidget(QWidget *parent)
: QWidget(parent)
{
auto layout = new QVBoxLayout(this);
layout->setContentsMargins(0, 0, 0, 0);
m_view = new QTableView(this);
layout->addWidget(m_view);
SetTableViewDefault(m_view);
auto delegate = new PgLabItemDelegate(m_view);
m_view->setItemDelegate(delegate);
}
void DataGridWidget::setSourceModel(QAbstractItemModel *model)
{
if (!m_proxy) {
m_proxy = new QSortFilterProxyModel(this);
m_proxy->setSortRole(CustomSortRole);
}
m_proxy->setSourceModel(model);
m_view->setModel(m_proxy);
m_view->setSortingEnabled(true);
m_proxy->sort(0, Qt::AscendingOrder);
}

View file

@ -0,0 +1,28 @@
#ifndef DATAGRIDWIDGET_H
#define DATAGRIDWIDGET_H
#include <QWidget>
class QAbstractItemModel;
class QSortFilterProxyModel;
class QTableView;
/** Sortable and editable grid shared by the CRUD tab and the query result tab.
*/
class DataGridWidget : public QWidget {
Q_OBJECT
public:
explicit DataGridWidget(QWidget *parent = nullptr);
/// Sets the model to show, sorting is done by a proxy that uses CustomSortRole
void setSourceModel(QAbstractItemModel *model);
QTableView *view() const { return m_view; }
QSortFilterProxyModel *proxy() const { return m_proxy; }
private:
QTableView *m_view = nullptr;
QSortFilterProxyModel *m_proxy = nullptr;
};
#endif // DATAGRIDWIDGET_H

View file

@ -1,85 +0,0 @@
#include "QueryResultModel.h"
#include "ResultTableModelUtil.h"
#include "Pgsql_declare.h"
#include "Pgsql_oids.h"
#include "catalog/PgDatabaseCatalog.h"
#include "CustomDataRole.h"
#include <QBrush>
#include <QColor>
using namespace Pgsql;
QueryResultModel::QueryResultModel(QObject *parent, std::shared_ptr<Pgsql::Result> r
, std::shared_ptr<PgDatabaseCatalog> cat)
: QAbstractTableModel(parent)
, result(std::move(r))
, catalog(cat)
{}
int QueryResultModel::rowCount(const QModelIndex &) const
{
int r = result->rows();
return r;
}
int QueryResultModel::columnCount(const QModelIndex &) const
{
int r = result->cols();
return r;
}
QVariant QueryResultModel::data(const QModelIndex &index, int role) const
{
// static const QString null_str("null");
QVariant v;
const int col = index.column();
const int rij = index.row();
const Oid oid = result->type(col);
if (role == Qt::DisplayRole || role == Qt::EditRole) {
if (result->null(col, rij)) {
//v = nullptr;
}
else {
Value value = result->get(col, rij);
if (oid == bool_oid) {
bool b = value;
v = b;
}
else {
QString s = value;
v = s;
}
}
}
else if (role == CustomDataTypeRole) {
v = oid;
}
return v;
}
QVariant QueryResultModel::headerData(int section, Qt::Orientation orientation, int role) const
{
QVariant r;
if (role == Qt::DisplayRole) {
if (orientation == Qt::Horizontal) {
QString s(result->getColName(section));
s += "\n";
s += getTypeDisplayString(*catalog, result->type(section));
r = s;
}
else {
r = QString::number(section + 1);
}
}
Qt::ItemFlags f;
return r;
}
Qt::ItemFlags QueryResultModel::flags(const QModelIndex &) const
{
return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable;
}

View file

@ -1,31 +0,0 @@
#ifndef QUERYRESULTMODEL_H
#define QUERYRESULTMODEL_H
#include <QAbstractTableModel>
#include "catalog/models/BaseTableModel.h"
#include "Pgsql_Connection.h"
class PgDatabaseCatalog;
class QueryResultModel : public QAbstractTableModel
{
Q_OBJECT
public:
QueryResultModel(QObject *parent, std::shared_ptr<Pgsql::Result> r, std::shared_ptr<PgDatabaseCatalog> cat);
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
virtual Qt::ItemFlags flags(const QModelIndex &index) const override;
std::shared_ptr<const Pgsql::Result> GetPgsqlResult() const { return result; }
protected:
// virtual Oid getType(int column) const override;
// virtual QVariant getData(const QModelIndex &index) const override;
private:
std::shared_ptr<Pgsql::Result> result;
std::shared_ptr<PgDatabaseCatalog> catalog;
};
#endif // QUERYRESULTMODEL_H

View file

@ -476,10 +476,8 @@ void QueryTool::query_ready(std::shared_ptr<Pgsql::Result> dbres, qint64 elapsed
//int n_rows = dbres->getRows();
//QString rowcount_str = QString("rows: %1").arg(dbres->getRows());
auto result_model = std::make_shared<QueryResultModel>(nullptr , dbres,
m_catalog);
TuplesResultWidget *trw = new TuplesResultWidget;
trw->setResult(result_model, elapsedms);
trw->setResult(m_context->openDatabase(), dbres, elapsedms);
resultList.push_back(trw);
ui->tabWidget->addTab(trw, "Data");
if (resultList.size() == 1)

View file

@ -3,7 +3,6 @@
#include "ManagedPage.h"
#include "ASyncDBConnection.h"
#include "QueryResultModel.h"
#include "QueryExplainModel.h"
#include "stopwatch.h"
#include "tuplesresultwidget.h"
@ -26,7 +25,6 @@ class QTableView;
class QTabWidget;
class SqlSyntaxHighlighter;
class ExplainRoot;
class QueryResultModel;
class QueryExplainModel;
class PgTypeContainer;
class OpenDatabase;

View file

@ -1,11 +1,15 @@
#include "tuplesresultwidget.h"
#include "tuplesresultwidget.h"
#include "ui_tuplesresultwidget.h"
#include "ResultTableModelUtil.h"
#include "util.h"
#include "crud/CrudModel.h"
#include "crud/DataGridWidget.h"
#include "OpenDatabase.h"
#include <QFile>
#include <QTextStream>
#include "util/PgLabItemDelegate.h"
#include <QTableView>
#include <QHeaderView>
TuplesResultWidget::TuplesResultWidget(QWidget *parent) :
@ -15,9 +19,13 @@ TuplesResultWidget::TuplesResultWidget(QWidget *parent) :
ui->setupUi(this);
ui->lblRowCount->setText(QString());
SetTableViewDefault(ui->ResultView);
auto delegate = new PgLabItemDelegate(ui->ResultView);
ui->ResultView->setItemDelegate(delegate);
m_grid = new DataGridWidget(this);
ui->verticalLayout->insertWidget(0, m_grid);
m_grid->view()->setFont(QFont("Source Sans Pro", 10));
m_grid->view()->setAlternatingRowColors(true);
m_grid->view()->setWordWrap(false);
m_grid->view()->verticalHeader()->setDefaultSectionSize(21);
m_grid->view()->verticalHeader()->setMinimumSectionSize(16);
}
TuplesResultWidget::~TuplesResultWidget()
@ -25,14 +33,17 @@ TuplesResultWidget::~TuplesResultWidget()
delete ui;
}
void TuplesResultWidget::setResult(std::shared_ptr<QueryResultModel> res, float ms)
void TuplesResultWidget::setResult(std::shared_ptr<OpenDatabase> db, std::shared_ptr<Pgsql::Result> res, float ms)
{
resultModel = res;
ui->ResultView->setModel(resultModel.get());
m_result = res;
ui->ResultView->resizeColumnsToContents();
m_model = new CrudModel(m_grid);
m_model->setResult(db, res);
m_grid->setSourceModel(m_model);
QString rowcount_str = QString("rows: %1").arg(resultModel->rowCount());
m_grid->view()->resizeColumnsToContents();
QString rowcount_str = QString("rows: %1").arg(m_model->rowCount());
ui->lblRowCount->setText(rowcount_str);
ui->lblElapsedTime->setText(msfloatToHumanReadableString(ms));
}
@ -42,8 +53,6 @@ void TuplesResultWidget::exportData(const QString &file_name) const
QFile file(file_name);
if (file.open(QIODevice::WriteOnly)) {
QTextStream out(&file);
::exportTable(ui->ResultView, out);
::exportTable(m_grid->view(), out);
}
}

View file

@ -1,13 +1,21 @@
#ifndef TUPLESRESULTWIDGET_H
#define TUPLESRESULTWIDGET_H
#include "querytool/QueryResultModel.h"
#include <QWidget>
#include <memory>
namespace Ui {
class TuplesResultWidget;
}
namespace Pgsql {
class Result;
}
class OpenDatabase;
class CrudModel;
class DataGridWidget;
class TuplesResultWidget : public QWidget
{
Q_OBJECT
@ -16,14 +24,15 @@ public:
explicit TuplesResultWidget(QWidget *parent = 0);
~TuplesResultWidget();
void setResult(std::shared_ptr<QueryResultModel> res, float ms);
void setResult(std::shared_ptr<OpenDatabase> db, std::shared_ptr<Pgsql::Result> res, float ms);
void exportData(const QString &file_name) const;
std::shared_ptr<const Pgsql::Result> GetPgsqlResult() const { return resultModel->GetPgsqlResult(); }
std::shared_ptr<const Pgsql::Result> GetPgsqlResult() const { return m_result; }
private:
Ui::TuplesResultWidget *ui;
std::shared_ptr<QueryResultModel> resultModel;
DataGridWidget *m_grid = nullptr;
CrudModel *m_model = nullptr;
std::shared_ptr<Pgsql::Result> m_result;
};
#endif // TUPLESRESULTWIDGET_H

View file

@ -29,37 +29,6 @@
<property name="bottomMargin">
<number>2</number>
</property>
<item>
<widget class="QTableView" name="ResultView">
<property name="font">
<font>
<family>Source Sans Pro</family>
<pointsize>10</pointsize>
</font>
</property>
<property name="editTriggers">
<set>QAbstractItemView::DoubleClicked|QAbstractItemView::EditKeyPressed</set>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="verticalScrollMode">
<enum>QAbstractItemView::ScrollPerPixel</enum>
</property>
<property name="horizontalScrollMode">
<enum>QAbstractItemView::ScrollPerPixel</enum>
</property>
<property name="wordWrap">
<bool>false</bool>
</property>
<attribute name="verticalHeaderDefaultSectionSize">
<number>21</number>
</attribute>
<attribute name="verticalHeaderMinimumSectionSize">
<number>16</number>
</attribute>
</widget>
</item>
<item>
<widget class="QWidget" name="widget" native="true">
<layout class="QHBoxLayout" name="horizontalLayout">