Skip to content
Permalink
Browse files

New table validator

  • Loading branch information
elpaso committed Jul 14, 2020
1 parent d8a45f9 commit d6259680f04a1ebdafcadd56015e2de08bda20c8
@@ -98,25 +98,6 @@ The ``context`` argument gives the wider context under which the context menu is
and contains accessors for useful objects like the application message bar.

The base class method has no effect.
%End

virtual void populateDatabaseContextMenu( QgsDataItem *item,
QMenu *menu,
const QList<QgsDataItem *> &selectedItems,
QgsDataItemGuiContext context ) final;
%Docstring
populateDatabaseContextMenu adds database related actions to QgsDatabaseSchemaItem and :py:class:`QgsDataCollectionItem`
instances for providers that support the connections API and for connections having the required capabilities.

Concrete classes should call this method from :py:func:`~QgsDataItemGuiProvider.populateContextMenu` if they want to add common actions to the
generated context menu.

:param item: item the actions belong to
:param menu: menu that will contain actions created by this method
:param selectedItems: selected items
:param context: data item gui context

.. versionadded:: 3.16
%End

virtual bool rename( QgsDataItem *item, const QString &name, QgsDataItemGuiContext context );
@@ -40,6 +40,7 @@
#include "qgsaddattrdialog.h"
#include "qgsabstractdatabaseproviderconnection.h"
#include "qgsprovidermetadata.h"
#include "qgsnewvectortabledialog.h"

#include <QFileInfo>
#include <QMenu>
@@ -839,3 +840,79 @@ void QgsFieldItemGuiProvider::populateContextMenu( QgsDataItem *item, QMenu *men
}
}
}

QString QgsDatabaseItemGuiProvider::name()
{
return QStringLiteral( "database" );
}

void QgsDatabaseItemGuiProvider::populateContextMenu( QgsDataItem *item, QMenu *menu, const QList<QgsDataItem *> &selectedItems, QgsDataItemGuiContext context )
{
Q_UNUSED( selectedItems )
// Add create new table for connections
if ( QgsDataCollectionItem * collectionItem { qobject_cast<QgsDataCollectionItem *>( item ) } )
{
QgsProviderMetadata *md { QgsProviderRegistry::instance()->providerMetadata( collectionItem->providerKey() ) };
if ( md )
{
const bool isSchema { qobject_cast<QgsDatabaseSchemaItem *>( item ) };
const QString connectionName { isSchema ? collectionItem->parent()->name() : collectionItem->name() };
std::unique_ptr<QgsAbstractDatabaseProviderConnection> conn( static_cast<QgsAbstractDatabaseProviderConnection *>( md->createConnection( connectionName ) ) );
if ( conn && conn->capabilities().testFlag( QgsAbstractDatabaseProviderConnection::Capability::CreateVectorTable ) )
{
QAction *newTableAction = new QAction( QObject::tr( "New Table…" ), menu );
QObject::connect( newTableAction, &QAction::triggered, collectionItem, [ collectionItem, connectionName, md, isSchema, context]
{
std::unique_ptr<QgsAbstractDatabaseProviderConnection> conn2 { static_cast<QgsAbstractDatabaseProviderConnection *>( md->createConnection( connectionName ) ) };
QgsNewVectorTableDialog dlg { conn2.get(), nullptr };
// TODO: dlg.setCrs( QgsProject::instance()->defaultCrsForNewLayers() );
if ( isSchema )
{
dlg.setSchemaName( collectionItem->name() );
}
if ( dlg.exec() == QgsNewVectorTableDialog::DialogCode::Accepted )
{
const QgsFields fields { dlg.fields() };
const QString tableName { dlg.tableName() };
const QString schemaName { dlg.schemaName() };
const QString geometryColumn { dlg.geometryColumnName() };
const QgsWkbTypes::Type geometryType { dlg.geometryType() };
const QgsCoordinateReferenceSystem crs { dlg.crs( ) };
QMap<QString, QVariant> options;

if ( ! geometryColumn.isEmpty() )
{
options[ QStringLiteral( "geometryColumn" ) ] = geometryColumn;
}

QString title;
QString message;

try
{
conn2->createVectorTable( schemaName, tableName, fields, geometryType, crs, true, &options );
collectionItem->refresh();
title = QObject::tr( "New Table Created" );
message = QObject::tr( "Table '%1' was created successfully." ).arg( tableName );
}
catch ( QgsProviderConnectionException &ex )
{
title = QObject::tr( "New Table Creation Error" );
message = QObject::tr( "Error creating new table '%1': %2" ).arg( tableName, ex.what() );
}

if ( context.messageBar() )
{
context.messageBar()->pushSuccess( title, message );
}
else
{
QMessageBox::information( nullptr, title, message );
}
}
} );
menu->addAction( newTableAction );
}
}
}
}
@@ -139,6 +139,22 @@ class QgsFieldItemGuiProvider : public QObject, public QgsDataItemGuiProvider
};


class QgsDatabaseItemGuiProvider : public QObject, public QgsDataItemGuiProvider
{
Q_OBJECT

public:

QgsDatabaseItemGuiProvider() = default;

QString name() override;

void populateContextMenu( QgsDataItem *item, QMenu *menu,
const QList<QgsDataItem *> &selectedItems, QgsDataItemGuiContext context ) override;

};



class QgsProjectItemGuiProvider : public QObject, public QgsDataItemGuiProvider
{
@@ -1214,6 +1214,7 @@ QgisApp::QgisApp( QSplashScreen *splash, bool restorePlugins, bool skipVersionCh
QgsGui::instance()->dataItemGuiProviderRegistry()->addProvider( new QgsBookmarksItemGuiProvider() );
QgsGui::instance()->dataItemGuiProviderRegistry()->addProvider( new QgsFieldsItemGuiProvider() );
QgsGui::instance()->dataItemGuiProviderRegistry()->addProvider( new QgsFieldItemGuiProvider() );
QgsGui::instance()->dataItemGuiProviderRegistry()->addProvider( new QgsDatabaseItemGuiProvider() );

QShortcut *showBrowserDock = new QShortcut( QKeySequence( tr( "Ctrl+2" ) ), this );
connect( showBrowserDock, &QShortcut::activated, mBrowserWidget, &QgsDockWidget::toggleUserVisible );
@@ -40,10 +40,9 @@
#include "qgsgeopackageproviderconnection.h"

void QgsGeoPackageItemGuiProvider::populateContextMenu( QgsDataItem *item, QMenu *menu,
const QList<QgsDataItem *> &selectedItems,
const QList<QgsDataItem *> &,
QgsDataItemGuiContext context )
{
populateDatabaseContextMenu( item, menu, selectedItems, context );
if ( QgsGeoPackageVectorLayerItem *layerItem = qobject_cast< QgsGeoPackageVectorLayerItem * >( item ) )
{
// Check capabilities
@@ -18,7 +18,6 @@
#include "qgsproviderregistry.h"
#include "qgsprovidermetadata.h"
#include "qgsabstractdatabaseproviderconnection.h"
#include "qgsnewvectortabledialog.h"
#include "qgsmessagebar.h"
//
// QgsDataItemGuiContext
@@ -43,67 +42,6 @@ void QgsDataItemGuiProvider::populateContextMenu( QgsDataItem *, QMenu *, const

}

void QgsDataItemGuiProvider::populateDatabaseContextMenu( QgsDataItem *item, QMenu *menu, const QList<QgsDataItem *> &selectedItems, QgsDataItemGuiContext context )
{
Q_UNUSED( selectedItems )
// Add create new table for connections
if ( QgsDataCollectionItem * collectionItem { qobject_cast<QgsDataCollectionItem *>( item ) } )
{
QgsProviderMetadata *md { QgsProviderRegistry::instance()->providerMetadata( collectionItem->providerKey() ) };
if ( md )
{
const bool isSchema { qobject_cast<QgsDatabaseSchemaItem *>( item ) };
const QString connectionName { isSchema ? collectionItem->parent()->name() : collectionItem->name() };
std::unique_ptr<QgsAbstractDatabaseProviderConnection> conn( static_cast<QgsAbstractDatabaseProviderConnection *>( md->createConnection( connectionName ) ) );
if ( conn && conn->capabilities().testFlag( QgsAbstractDatabaseProviderConnection::Capability::CreateVectorTable ) )
{
QAction *newTableAction = new QAction( QObject::tr( "New Table…" ), menu );
QObject::connect( newTableAction, &QAction::triggered, collectionItem, [ collectionItem, connectionName, md, isSchema, context]
{
std::unique_ptr<QgsAbstractDatabaseProviderConnection> conn2 { static_cast<QgsAbstractDatabaseProviderConnection *>( md->createConnection( connectionName ) ) };
QgsNewVectorTableDialog dlg { conn2.get(), nullptr };
// TODO: dlg.setCrs( QgsProject::instance()->defaultCrsForNewLayers() );
if ( isSchema )
{
dlg.setSchemaName( collectionItem->name() );
}
if ( dlg.exec() == QgsNewVectorTableDialog::DialogCode::Accepted )
{
const QgsFields fields { dlg.fields() };
const QString tableName { dlg.tableName() };
const QString schemaName { dlg.schemaName() };
const QString geometryColumn { dlg.geometryColumnName() };
const QgsWkbTypes::Type geometryType { dlg.geometryType() };
const QgsCoordinateReferenceSystem crs { dlg.crs( ) };
QMap<QString, QVariant> options;
if ( ! geometryColumn.isEmpty() )
{
options[ QStringLiteral( "geometryColumn" ) ] = geometryColumn;
}
try
{
conn2->createVectorTable( schemaName, tableName, fields, geometryType, crs, true, &options );
collectionItem->refresh();
if ( context.messageBar() )
{
context.messageBar()->pushSuccess( QObject::tr( "New Table Created" ), QObject::tr( "Table '%1' was created successfully." ).arg( tableName ) );
}
}
catch ( QgsProviderConnectionException &ex )
{
if ( context.messageBar() )
{
context.messageBar()->pushCritical( QObject::tr( "New Table Creation Error" ), QObject::tr( "Error creating new table '%1': %2" ).arg( tableName, ex.what() ) );
}
}
}
} );
menu->addAction( newTableAction );
}
}
}
}

bool QgsDataItemGuiProvider::rename( QgsDataItem *, const QString &, QgsDataItemGuiContext )
{
return false;
@@ -119,24 +119,6 @@ class GUI_EXPORT QgsDataItemGuiProvider
virtual void populateContextMenu( QgsDataItem *item, QMenu *menu,
const QList<QgsDataItem *> &selectedItems, QgsDataItemGuiContext context );

/**
* \brief populateDatabaseContextMenu adds database related actions to QgsDatabaseSchemaItem and QgsDataCollectionItem
* instances for providers that support the connections API and for connections having the required capabilities.
*
* Concrete classes should call this method from populateContextMenu() if they want to add common actions to the
* generated context menu.
*
* \param item item the actions belong to
* \param menu menu that will contain actions created by this method
* \param selectedItems selected items
* \param context data item gui context
* \since QGIS 3.16
*/
virtual void populateDatabaseContextMenu( QgsDataItem *item,
QMenu *menu,
const QList<QgsDataItem *> &selectedItems,
QgsDataItemGuiContext context ) final;

/**
* Sets a new \a name for the item, and returns TRUE if the item was successfully renamed.
*
@@ -28,6 +28,23 @@ QgsNewVectorTableDialog::QgsNewVectorTableDialog( QgsAbstractDatabaseProviderCon
QgsGui::enableAutoGeometryRestore( this );
setWindowTitle( tr( "New Table" ) );

auto updateTableNames = [ = ]( const QString &schema = QString( ) )
{
mTableNames.clear();
try
{
const auto constTables { conn->tables( schema ) };
for ( const auto &tp : constTables )
{
mTableNames.push_back( tp.tableName() );
}
}
catch ( QgsProviderConnectionException &ex )
{
QgsDebugMsg( QStringLiteral( "Error retrieving tables from connection: %1" ).arg( ex.what() ) );
}
};

mTableName->setText( QStringLiteral( "new_table_name" ) );
mFieldsTableView->setModel( mFieldModel );
QgsNewVectorTableDialogFieldsDelegate *delegate { new QgsNewVectorTableDialogFieldsDelegate( mConnection->nativeTypes(), this )};
@@ -36,24 +53,39 @@ QgsNewVectorTableDialog::QgsNewVectorTableDialog( QgsAbstractDatabaseProviderCon
mFieldsTableView->setSelectionMode( QAbstractItemView::SelectionMode::SingleSelection );

// Cosmetics
mFieldsTableView->setColumnWidth( QgsNewVectorTableFieldModel::ColumnHeaders::Name, 200 );
mFieldsTableView->horizontalHeader()->setStretchLastSection( true );
mFieldsTableView->setColumnWidth( QgsNewVectorTableFieldModel::ColumnHeaders::Type, 300 );
/*mFieldsTableView->setColumnWidth( QgsNewVectorTableFieldModel::ColumnHeaders::Name, 200 );
mFieldsTableView->setColumnWidth( QgsNewVectorTableFieldModel::ColumnHeaders::Type, 400 );
mFieldsTableView->setColumnWidth( QgsNewVectorTableFieldModel::ColumnHeaders::ProviderType, 300 );
mFieldsTableView->setColumnWidth( QgsNewVectorTableFieldModel::ColumnHeaders::Length, 100 );
mFieldsTableView->setColumnWidth( QgsNewVectorTableFieldModel::ColumnHeaders::Precision, 150 );
mFieldsTableView->setColumnWidth( QgsNewVectorTableFieldModel::ColumnHeaders::Comment, 300 );
mFieldsTableView->setColumnWidth( QgsNewVectorTableFieldModel::ColumnHeaders::Comment, 300 );*/

// Schema is not supported by all providers
if ( mConnection->capabilities().testFlag( QgsAbstractDatabaseProviderConnection::Capability::Schemas ) )
{
mSchemaCbo->addItems( mConnection->schemas() );
connect( mSchemaCbo, &QComboBox::currentTextChanged, this, updateTableNames );
}
else
{
updateTableNames();
mSchemaCbo->hide();
mSchemaLabel->hide();
}

// Validators
connect( mTableName, &QLineEdit::textChanged, this, [ = ]( const QString & )
{
validate();
} );

connect( mGeomColumn, &QLineEdit::textChanged, this, [ = ]( const QString & )
{
validate();
} );

connect( mGeomTypeCbo, qgis::overload<int>::of( &QComboBox::currentIndexChanged ), this, [ = ]( int index )
{
mGeomColumn->setEnabled( index != 0 );
@@ -208,6 +240,7 @@ QgsCoordinateReferenceSystem QgsNewVectorTableDialog::crs() const
void QgsNewVectorTableDialog::setFields( const QgsFields &fields )
{
mFieldModel->setFields( fields );
validate();
}

void QgsNewVectorTableDialog::updateButtons()
@@ -228,6 +261,23 @@ void QgsNewVectorTableDialog::selectRow( int row )

void QgsNewVectorTableDialog::validate()
{
mValidationErrors.clear();
if ( mTableNames.contains( mTableName->text() ) )
{
mValidationErrors.push_back( tr( "Table '%1' already exists!" ).arg( mTableName->text() ) );
}
// Check for field names and geom col name
if ( mGeomTypeCbo->currentIndex() != 0 && ! mGeomColumn->text().isEmpty() && fields().names().contains( mGeomColumn->text() ) )
{
mValidationErrors.push_back( tr( "Geometry column name cannot be equal to an existing field name!" ) );
}
const bool isValid { mValidationErrors.isEmpty() };
if ( ! isValid )
{
mValidationResults->setText( mValidationErrors.join( '\n' ) );
}
mValidationFrame->setVisible( ! isValid );
mButtonBox->button( QDialogButtonBox::StandardButton::Ok )->setEnabled( isValid && fields().count() > 0 );
}

void QgsNewVectorTableDialog::showEvent( QShowEvent *event )
@@ -79,6 +79,9 @@ class GUI_EXPORT QgsNewVectorTableDialog : public QDialog, private Ui_QgsNewVect
QgsAbstractDatabaseProviderConnection *mConnection;
QgsNewVectorTableFieldModel *mFieldModel;
int mCurrentRow = -1;
// Used by validator
QStringList mTableNames;
QStringList mValidationErrors;

void updateButtons();
void selectRow( int row );
@@ -23,9 +23,8 @@
#include <QMessageBox>


void QgsMssqlDataItemGuiProvider::populateContextMenu( QgsDataItem *item, QMenu *menu, const QList<QgsDataItem *> &selectedItems, QgsDataItemGuiContext context )
void QgsMssqlDataItemGuiProvider::populateContextMenu( QgsDataItem *item, QMenu *menu, const QList<QgsDataItem *> &, QgsDataItemGuiContext )
{
populateDatabaseContextMenu( item, menu, selectedItems, context );
if ( QgsMssqlRootItem *rootItem = qobject_cast< QgsMssqlRootItem * >( item ) )
{
QAction *actionNew = new QAction( tr( "New Connection…" ), menu );
@@ -20,15 +20,13 @@
#include "qgspgnewconnection.h"
#include "qgsnewnamedialog.h"
#include "qgspgsourceselect.h"
#include "qgsnewvectortabledialog.h"

#include <QInputDialog>
#include <QMessageBox>


void QgsPostgresDataItemGuiProvider::populateContextMenu( QgsDataItem *item, QMenu *menu, const QList<QgsDataItem *> &selectedItems, QgsDataItemGuiContext context )
void QgsPostgresDataItemGuiProvider::populateContextMenu( QgsDataItem *item, QMenu *menu, const QList<QgsDataItem *> &, QgsDataItemGuiContext )
{
populateDatabaseContextMenu( item, menu, selectedItems, context );
if ( QgsPGRootItem *rootItem = qobject_cast< QgsPGRootItem * >( item ) )
{
QAction *actionNew = new QAction( tr( "New Connection…" ), this );

0 comments on commit d625968

Please sign in to comment.
You can’t perform that action at this time.