Skip to content

Commit 0c701fb

Browse files
committed
GUI implementation to load/save projects in PostgreSQL
1 parent ab83455 commit 0c701fb

9 files changed

+378
-2
lines changed

src/app/qgisapp.cpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,8 @@ Q_GUI_EXPORT extern int qt_defaultDpiX();
250250
#include "qgsproject.h"
251251
#include "qgsprojectlayergroupdialog.h"
252252
#include "qgsprojectproperties.h"
253+
#include "qgsprojectstorage.h"
254+
#include "qgsprojectstorageregistry.h"
253255
#include "qgsproviderregistry.h"
254256
#include "qgspythonrunner.h"
255257
#include "qgsquerybuilder.h"
@@ -1292,6 +1294,10 @@ QgisApp::QgisApp( QSplashScreen *splash, bool restorePlugins, bool skipVersionCh
12921294

12931295
setupDuplicateFeaturesAction();
12941296

1297+
// support for project storage
1298+
connect( mProjectFromStorageMenu, &QMenu::aboutToShow, [this] { populateProjectStorageMenu( mProjectFromStorageMenu, false ); } );
1299+
connect( mProjectToStorageMenu, &QMenu::aboutToShow, [this] { populateProjectStorageMenu( mProjectToStorageMenu, true ); } );
1300+
12951301
QList<QAction *> actions = mPanelMenu->actions();
12961302
std::sort( actions.begin(), actions.end(), cmpByText_ );
12971303
mPanelMenu->insertActions( nullptr, actions );
@@ -13549,3 +13555,37 @@ QgsFeature QgisApp::duplicateFeatureDigitized( QgsMapLayer *mlayer, const QgsFea
1354913555
return QgsFeature();
1355013556
}
1355113557

13558+
13559+
void QgisApp::populateProjectStorageMenu( QMenu *menu, bool saving )
13560+
{
13561+
menu->clear();
13562+
for ( QgsProjectStorage *storage : QgsApplication::projectStorageRegistry()->projectStorages() )
13563+
{
13564+
QString name = storage->visibleName();
13565+
if ( name.isEmpty() )
13566+
continue;
13567+
QAction *action = menu->addAction( name );
13568+
if ( saving )
13569+
{
13570+
connect( action, &QAction::triggered, [storage]
13571+
{
13572+
QString uri = storage->showSaveGui();
13573+
if ( !uri.isEmpty() )
13574+
{
13575+
// TODO: merge with QgisApp::fileSaveAs()
13576+
QgsProject::instance()->setFileName( uri );
13577+
QgsProject::instance()->write();
13578+
}
13579+
} );
13580+
}
13581+
else
13582+
{
13583+
connect( action, &QAction::triggered, [this, storage]
13584+
{
13585+
QString uri = storage->showLoadGui();
13586+
if ( !uri.isEmpty() )
13587+
addProject( uri );
13588+
} );
13589+
}
13590+
}
13591+
}

src/app/qgisapp.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1880,6 +1880,9 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
18801880
*/
18811881
int dockedToolbarIconSize( int standardToolbarIconSize ) const;
18821882

1883+
//! Populates project "load from" / "save to" menu based on project storages (when the menu is about to be shown)
1884+
void populateProjectStorageMenu( QMenu *menu, bool saving );
1885+
18831886
QgisAppStyleSheet *mStyleSheetBuilder = nullptr;
18841887

18851888
// actions for menus and toolbars -----------------

src/providers/postgres/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,12 @@ IF (WITH_GUI)
3030
SET(PG_SRCS ${PG_SRCS}
3131
qgspgsourceselect.cpp
3232
qgspgnewconnection.cpp
33+
qgspostgresprojectstoragedialog.cpp
3334
)
3435
SET(PG_MOC_HDRS ${PG_MOC_HDRS}
3536
qgspgnewconnection.h
3637
qgspgsourceselect.h
38+
qgspostgresprojectstoragedialog.h
3739
)
3840
ENDIF ()
3941

src/providers/postgres/qgspostgresprojectstorage.cpp

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,69 @@ bool QgsPostgresProjectStorage::readProjectMetadata( const QString &uri, QgsProj
220220
}
221221

222222

223+
#ifdef HAVE_GUI
224+
225+
#include "qgspostgresprojectstoragedialog.h"
226+
227+
QString QgsPostgresProjectStorage::visibleName()
228+
{
229+
return QObject::tr( "PostgreSQL" );
230+
}
231+
232+
QString QgsPostgresProjectStorage::showLoadGui()
233+
{
234+
QgsPostgresProjectStorageDialog dlg( false );
235+
if ( !dlg.exec() )
236+
return QString();
237+
238+
QgsPostgresProjectUri postUri;
239+
postUri.connInfo = QgsPostgresConn::connUri( dlg.connectionName() );
240+
postUri.schemaName = dlg.schemaName();
241+
postUri.projectName = dlg.projectName();
242+
return makeUri( postUri );
243+
}
244+
245+
QString QgsPostgresProjectStorage::showSaveGui()
246+
{
247+
QgsPostgresProjectStorageDialog dlg( true );
248+
if ( !dlg.exec() )
249+
return QString();
250+
251+
QgsPostgresProjectUri postUri;
252+
postUri.connInfo = QgsPostgresConn::connUri( dlg.connectionName() );
253+
postUri.schemaName = dlg.schemaName();
254+
postUri.projectName = dlg.projectName();
255+
return makeUri( postUri );
256+
}
257+
258+
#endif
259+
260+
261+
QString QgsPostgresProjectStorage::makeUri( const QgsPostgresProjectUri &postUri )
262+
{
263+
QUrl u;
264+
QUrlQuery urlQuery;
265+
266+
u.setScheme( "postgresql" );
267+
u.setHost( postUri.connInfo.host() );
268+
if ( !postUri.connInfo.port().isEmpty() )
269+
u.setPort( postUri.connInfo.port().toInt() );
270+
u.setUserName( postUri.connInfo.username() );
271+
u.setPassword( postUri.connInfo.password() );
272+
// TODO: sslmode, authcfg, service
273+
274+
urlQuery.addQueryItem( "dbname", postUri.connInfo.database() );
275+
276+
urlQuery.addQueryItem( "schema", postUri.schemaName );
277+
if ( !postUri.projectName.isEmpty() )
278+
urlQuery.addQueryItem( "project", postUri.projectName );
279+
280+
u.setQuery( urlQuery );
281+
282+
return QString::fromUtf8( u.toEncoded() );
283+
}
284+
285+
223286
QgsPostgresProjectUri QgsPostgresProjectStorage::parseUri( const QString &uri )
224287
{
225288
QUrl u = QUrl::fromEncoded( uri.toUtf8() );

src/providers/postgres/qgspostgresprojectstorage.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#ifndef QGSPOSTGRESPROJECTSTORAGE_H
22
#define QGSPOSTGRESPROJECTSTORAGE_H
33

4+
#include "qgsconfig.h"
45
#include "qgsprojectstorage.h"
56

67
#include "qgsdatasourceuri.h"
@@ -12,7 +13,6 @@ typedef struct
1213

1314
QgsDataSourceUri connInfo; // using only the bits about connection info (server, port, username, password, service, ssl mode)
1415

15-
QString dbName;
1616
QString schemaName;
1717
QString projectName;
1818

@@ -37,7 +37,14 @@ class QgsPostgresProjectStorage : public QgsProjectStorage
3737

3838
virtual bool readProjectMetadata( const QString &uri, QgsProjectStorage::Metadata &metadata ) override;
3939

40-
private:
40+
#ifdef HAVE_GUI
41+
// GUI support
42+
virtual QString visibleName() override;
43+
virtual QString showLoadGui() override;
44+
virtual QString showSaveGui() override;
45+
#endif
46+
47+
static QString makeUri( const QgsPostgresProjectUri &postUri );
4148
static QgsPostgresProjectUri parseUri( const QString &uri );
4249
};
4350

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
#include "qgspostgresprojectstoragedialog.h"
2+
3+
#include "qgspostgresconn.h"
4+
#include "qgspostgresconnpool.h"
5+
#include "qgspostgresprojectstorage.h"
6+
7+
#include "qgsapplication.h"
8+
#include "qgsprojectstorage.h"
9+
#include "qgsprojectstorageregistry.h"
10+
11+
#include <QMessageBox>
12+
13+
QgsPostgresProjectStorageDialog::QgsPostgresProjectStorageDialog( bool saving, QWidget *parent )
14+
: QDialog( parent )
15+
, mSaving( saving )
16+
{
17+
setupUi( this );
18+
19+
connect( buttonBox, &QDialogButtonBox::accepted, this, &QgsPostgresProjectStorageDialog::onOK );
20+
21+
if ( saving )
22+
{
23+
setWindowTitle( tr( "Save project to PostgreSQL" ) );
24+
mCboProject->setEditable( true );
25+
}
26+
else
27+
{
28+
setWindowTitle( tr( "Load project from PostgreSQL" ) );
29+
}
30+
31+
connect( mCboConnection, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsPostgresProjectStorageDialog::populateSchemas );
32+
33+
// populate connections
34+
mCboConnection->addItems( QgsPostgresConn::connectionList() );
35+
36+
// If possible, set the item currently displayed database
37+
QString toSelect = QgsPostgresConn::selectedConnection();
38+
mCboConnection->setCurrentIndex( mCboConnection->findText( toSelect ) );
39+
40+
connect( mCboSchema, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsPostgresProjectStorageDialog::populateProjects );
41+
}
42+
43+
QString QgsPostgresProjectStorageDialog::connectionName() const
44+
{
45+
return mCboConnection->currentText();
46+
}
47+
48+
QString QgsPostgresProjectStorageDialog::schemaName() const
49+
{
50+
return mCboSchema->currentText();
51+
}
52+
53+
QString QgsPostgresProjectStorageDialog::projectName() const
54+
{
55+
return mCboProject->currentText();
56+
}
57+
58+
void QgsPostgresProjectStorageDialog::populateSchemas()
59+
{
60+
mCboSchema->clear();
61+
mCboProject->clear();
62+
63+
QString name = mCboConnection->currentText();
64+
QgsDataSourceUri uri = QgsPostgresConn::connUri( name );
65+
66+
QApplication::setOverrideCursor( Qt::WaitCursor );
67+
68+
QgsPostgresConn *conn = QgsPostgresConnPool::instance()->acquireConnection( uri.connectionInfo( false ) );
69+
if ( !conn )
70+
{
71+
QApplication::restoreOverrideCursor();
72+
QMessageBox::critical( this, tr( "Error" ), tr( "Connection failed" ) + "\n" + uri.connectionInfo( false ) );
73+
return;
74+
}
75+
76+
QList<QgsPostgresSchemaProperty> schemas;
77+
bool ok = conn->getSchemas( schemas );
78+
QgsPostgresConnPool::instance()->releaseConnection( conn );
79+
80+
QApplication::restoreOverrideCursor();
81+
82+
if ( !ok )
83+
{
84+
QMessageBox::critical( this, tr( "Error" ), tr( "Failed to get schemas" ) );
85+
return;
86+
}
87+
88+
for ( const QgsPostgresSchemaProperty &schema : qAsConst( schemas ) )
89+
{
90+
mCboSchema->addItem( schema.name );
91+
}
92+
}
93+
94+
void QgsPostgresProjectStorageDialog::populateProjects()
95+
{
96+
mCboProject->clear();
97+
98+
QgsPostgresProjectUri postUri;
99+
postUri.connInfo = QgsPostgresConn::connUri( mCboConnection->currentText() );
100+
postUri.schemaName = mCboSchema->currentText();
101+
QString uri = QgsPostgresProjectStorage::makeUri( postUri );
102+
103+
QgsProjectStorage *storage = QgsApplication::projectStorageRegistry()->projectStorageFromType( "postgresql" );
104+
Q_ASSERT( storage );
105+
mCboProject->addItems( storage->listProjects( uri ) );
106+
}
107+
108+
void QgsPostgresProjectStorageDialog::onOK()
109+
{
110+
// check that the fields are filled in
111+
if ( mCboProject->currentText().isEmpty() )
112+
return;
113+
114+
if ( mSaving )
115+
{
116+
if ( mCboProject->findText( mCboProject->currentText() ) != -1 )
117+
{
118+
int res = QMessageBox::question( this, tr( "Overwrite project" ),
119+
tr( "A project with the same name already exists. Would you like to overwrite it?" ),
120+
QMessageBox::Yes | QMessageBox::No );
121+
if ( res != QMessageBox::Yes )
122+
return;
123+
}
124+
}
125+
126+
accept();
127+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#ifndef QGSPOSTGRESPROJECTSTORAGEDIALOG_H
2+
#define QGSPOSTGRESPROJECTSTORAGEDIALOG_H
3+
4+
#include <QDialog>
5+
6+
#include "ui_qgspostgresprojectstoragedialog.h"
7+
8+
9+
class QgsPostgresProjectStorageDialog : public QDialog, private Ui::QgsPostgresProjectStorageDialog
10+
{
11+
Q_OBJECT
12+
public:
13+
explicit QgsPostgresProjectStorageDialog( bool saving, QWidget *parent = nullptr );
14+
15+
QString connectionName() const;
16+
QString schemaName() const;
17+
QString projectName() const;
18+
19+
signals:
20+
21+
private slots:
22+
void populateSchemas();
23+
void populateProjects();
24+
void onOK();
25+
26+
private:
27+
bool mSaving; //!< Whether using this dialog for loading or saving a project
28+
};
29+
30+
#endif // QGSPOSTGRESPROJECTSTORAGEDIALOG_H

src/ui/qgisapp.ui

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,15 +42,27 @@
4242
<string>New From Template</string>
4343
</property>
4444
</widget>
45+
<widget class="QMenu" name="mProjectToStorageMenu">
46+
<property name="title">
47+
<string>Save To</string>
48+
</property>
49+
</widget>
50+
<widget class="QMenu" name="mProjectFromStorageMenu">
51+
<property name="title">
52+
<string>Open From</string>
53+
</property>
54+
</widget>
4555
<addaction name="mActionNewProject"/>
4656
<addaction name="mActionOpenProject"/>
57+
<addaction name="mProjectFromStorageMenu"/>
4758
<addaction name="mProjectFromTemplateMenu"/>
4859
<addaction name="mRecentProjectsMenu"/>
4960
<addaction name="mActionRevertProject"/>
5061
<addaction name="mActionCloseProject"/>
5162
<addaction name="separator"/>
5263
<addaction name="mActionSaveProject"/>
5364
<addaction name="mActionSaveProjectAs"/>
65+
<addaction name="mProjectToStorageMenu"/>
5466
<addaction name="mActionSaveMapAsImage"/>
5567
<addaction name="mActionSaveMapAsPdf"/>
5668
<addaction name="mActionDxfExport"/>

0 commit comments

Comments
 (0)