[feature] Add stored query support to Execute SQL window

This ports the equivalent functionality from DB Manager over to
the core/browser "Execute SQL" dialog.

Users can insert stored queries into their current query, save
new stored queries, and remove queries via a new toolbar action.

Users can opt to store queries in either the current project
(ie the DB Manager approach, and this is fully compatible with
existing queries stored via DB Manager) or in the local user
profile (so that they are available across different projects)

Sponsored by City of Canning
This commit is contained in:
Nyall Dawson 2025-02-26 11:23:12 +10:00
parent bb4827e9f9
commit de909e57e4
No known key found for this signature in database
GPG Key ID: 4C61673F0BF197FC
4 changed files with 89 additions and 1 deletions

View File

@ -1016,6 +1016,7 @@
<file>themes/default/stacked-diagram.svg</file>
<file>themes/default/mIconStac.svg</file>
<file>themes/default/mIconQt.svg</file>
<file>themes/default/mIconStoredQueries.svg</file>
</qresource>
<qresource prefix="/images/tips">
<file alias="symbol_levels.png">qgis_tips/symbol_levels.png</file>

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.6 KiB

View File

@ -29,11 +29,14 @@
#include "qgsprovidermetadata.h"
#include "qgscodeeditorwidget.h"
#include "qgsfileutils.h"
#include "qgsstoredquerymanager.h"
#include "qgsproject.h"
#include <QClipboard>
#include <QShortcut>
#include <QFileDialog>
#include <QMessageBox>
#include <QInputDialog>
///@cond PRIVATE
const QgsSettingsEntryString *QgsQueryResultWidget::settingLastSourceFolder = new QgsSettingsEntryString( QStringLiteral( "last-source-folder" ), sTreeSqlQueries, QString(), QStringLiteral( "Last used folder for SQL source files" ) );
@ -48,6 +51,16 @@ QgsQueryResultWidget::QgsQueryResultWidget( QWidget *parent, QgsAbstractDatabase
// mSqlEditor->setLineNumbersVisible( true );
mToolBar->setIconSize( QgsGuiUtils::iconSize( false ) );
mPresetQueryMenu = new QMenu( this );
connect( mPresetQueryMenu, &QMenu::aboutToShow, this, &QgsQueryResultWidget::populatePresetQueryMenu );
QToolButton *presetQueryButton = new QToolButton();
presetQueryButton->setMenu( mPresetQueryMenu );
presetQueryButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "mIconStoredQueries.svg" ) ) );
presetQueryButton->setPopupMode( QToolButton::InstantPopup );
mToolBar->addWidget( presetQueryButton );
// explicitly needed for some reason (Qt 5.15)
mainLayout->setSpacing( 6 );
progressLayout->setSpacing( 6 );
@ -732,6 +745,77 @@ void QgsQueryResultWidget::updateDialogTitle()
emit requestDialogTitleUpdate( fileName );
}
void QgsQueryResultWidget::populatePresetQueryMenu()
{
mPresetQueryMenu->clear();
QMenu *storeQueryMenu = new QMenu( tr( "Store Current Query" ), mPresetQueryMenu );
mPresetQueryMenu->addMenu( storeQueryMenu );
QAction *storeInProfileAction = new QAction( tr( "In User Profile…" ), storeQueryMenu );
storeQueryMenu->addAction( storeInProfileAction );
storeInProfileAction->setEnabled( !mSqlEditor->text().isEmpty() );
connect( storeInProfileAction, &QAction::triggered, this, [this] {
storeCurrentQuery( Qgis::QueryStorageBackend::LocalProfile );
} );
QAction *storeInProjectAction = new QAction( tr( "In Current Project…" ), storeQueryMenu );
storeQueryMenu->addAction( storeInProjectAction );
storeInProjectAction->setEnabled( !mSqlEditor->text().isEmpty() );
connect( storeInProjectAction, &QAction::triggered, this, [this] {
storeCurrentQuery( Qgis::QueryStorageBackend::CurrentProject );
} );
const QList< QgsStoredQueryManager::QueryDetails > storedQueries = QgsGui::storedQueryManager()->allQueries();
if ( !storedQueries.isEmpty() )
{
mPresetQueryMenu->addSeparator();
for ( const QgsStoredQueryManager::QueryDetails &query : storedQueries )
{
QAction *action = new QAction( query.name, mPresetQueryMenu );
mPresetQueryMenu->addAction( action );
connect( action, &QAction::triggered, this, [this, query] {
mSqlEditor->insertText( query.definition );
} );
}
mPresetQueryMenu->addSeparator();
QMenu *removeQueryMenu = new QMenu( tr( "Removed Stored Query" ), mPresetQueryMenu );
mPresetQueryMenu->addMenu( removeQueryMenu );
for ( const QgsStoredQueryManager::QueryDetails &query : storedQueries )
{
QAction *action = new QAction( tr( "%1…" ).arg( query.name ), mPresetQueryMenu );
removeQueryMenu->addAction( action );
connect( action, &QAction::triggered, this, [this, query] {
const QMessageBox::StandardButton res = QMessageBox::question( this, tr( "Remove Stored Query" ), tr( "Are you sure you want to remove the stored query “%1”?" ).arg( query.name ), QMessageBox::Yes | QMessageBox::No, QMessageBox::No );
if ( res == QMessageBox::Yes )
{
QgsGui::storedQueryManager()->removeQuery( query.name, query.backend );
if ( query.backend == Qgis::QueryStorageBackend::CurrentProject )
{
QgsProject::instance()->setDirty();
}
}
} );
}
}
}
void QgsQueryResultWidget::storeCurrentQuery( Qgis::QueryStorageBackend backend )
{
bool ok;
const QString name = QInputDialog::getText( this, tr( "Query Name" ), tr( "Name for the stored query" ), QLineEdit::Normal, QString(), &ok );
if ( !ok || name.isEmpty() )
return;
QgsGui::storedQueryManager()->storeQuery( name, mSqlEditor->text(), backend );
if ( backend == Qgis::QueryStorageBackend::CurrentProject )
{
QgsProject::instance()->setDirty();
}
}
///@cond private
void QgsConnectionsApiFetcher::fetchTokens()

View File

@ -223,10 +223,12 @@ class GUI_EXPORT QgsQueryResultWidget : public QWidget, private Ui::QgsQueryResu
void openQuery();
void saveQuery( bool saveAs );
void setHasChanged( bool hasChanged );
void populatePresetQueryMenu();
private:
QgsCodeEditorWidget *mCodeEditorWidget = nullptr;
QgsCodeEditorSQL *mSqlEditor = nullptr;
QMenu *mPresetQueryMenu = nullptr;
std::unique_ptr<QgsAbstractDatabaseProviderConnection> mConnection;
std::unique_ptr<QgsQueryResultModel> mModel;
@ -272,7 +274,7 @@ class GUI_EXPORT QgsQueryResultWidget : public QWidget, private Ui::QgsQueryResu
QgsAbstractDatabaseProviderConnection::SqlVectorLayerOptions sqlVectorLayerOptions() const;
void updateDialogTitle();
void storeCurrentQuery( Qgis::QueryStorageBackend backend );
friend class TestQgsQueryResultWidget;
};