mirror of
https://github.com/qgis/QGIS.git
synced 2025-10-16 00:05:45 -04:00
Gui enhancements
This commit is contained in:
parent
3479a66e4e
commit
70c93d4aea
@ -261,7 +261,7 @@ Returns the (possibly NULL) raster attribute table for the given band ``bandNumb
|
|||||||
.. versionadded:: 3.30
|
.. versionadded:: 3.30
|
||||||
%End
|
%End
|
||||||
|
|
||||||
int attributeTableCount( );
|
int attributeTableCount( ) const;
|
||||||
%Docstring
|
%Docstring
|
||||||
Returns the number of attribute tables for the raster by counting the number of bands that have an associated attribute table.
|
Returns the number of attribute tables for the raster by counting the number of bands that have an associated attribute table.
|
||||||
|
|
||||||
|
@ -46,6 +46,33 @@ Sets the raster layer and an optional band number.
|
|||||||
bool isDirty( ) const;
|
bool isDirty( ) const;
|
||||||
%Docstring
|
%Docstring
|
||||||
Returns ``True`` if the associated raster attribute table is dirty
|
Returns ``True`` if the associated raster attribute table is dirty
|
||||||
|
%End
|
||||||
|
|
||||||
|
void setMessageBar( QgsMessageBar *bar );
|
||||||
|
%Docstring
|
||||||
|
Sets the message ``bar`` associated with the widget. This allows the widget to push feedback messages
|
||||||
|
to the appropriate message bar.
|
||||||
|
|
||||||
|
.. seealso:: :py:func:`messageBar`
|
||||||
|
%End
|
||||||
|
|
||||||
|
signals:
|
||||||
|
|
||||||
|
void rendererChanged( );
|
||||||
|
%Docstring
|
||||||
|
This signal is emitted after a successful classify operation which changed the raster renderer.
|
||||||
|
%End
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
|
||||||
|
void saveChanges();
|
||||||
|
%Docstring
|
||||||
|
Save the changes in the raster attribute table.
|
||||||
|
%End
|
||||||
|
|
||||||
|
bool setEditable( bool editable );
|
||||||
|
%Docstring
|
||||||
|
Set the editable state, it may trigger save changes if the attribute table has unsave changes.
|
||||||
%End
|
%End
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -4380,6 +4380,12 @@ void QgisApp::setupConnections()
|
|||||||
|
|
||||||
connect( QgsProject::instance(), &QgsProject::transactionGroupsChanged, this, &QgisApp::onTransactionGroupsChanged );
|
connect( QgsProject::instance(), &QgsProject::transactionGroupsChanged, this, &QgisApp::onTransactionGroupsChanged );
|
||||||
|
|
||||||
|
// Handle dirty raster attribute tables
|
||||||
|
connect( QgsProject::instance(), qOverload<const QList< QgsMapLayer * > & >( &QgsProject::layersWillBeRemoved ), this, [ = ]( const QList< QgsMapLayer * > &layers )
|
||||||
|
{
|
||||||
|
checkUnsavedRasterAttributeTableEdits( layers, false );
|
||||||
|
} );
|
||||||
|
|
||||||
// connect preview modes actions
|
// connect preview modes actions
|
||||||
connect( mActionPreviewModeOff, &QAction::triggered, this, &QgisApp::disablePreviewMode );
|
connect( mActionPreviewModeOff, &QAction::triggered, this, &QgisApp::disablePreviewMode );
|
||||||
connect( mActionPreviewModeMono, &QAction::triggered, this, &QgisApp::activateMonoPreview );
|
connect( mActionPreviewModeMono, &QAction::triggered, this, &QgisApp::activateMonoPreview );
|
||||||
@ -5600,7 +5606,7 @@ void QgisApp::fileExit()
|
|||||||
}
|
}
|
||||||
|
|
||||||
QgsCanvasRefreshBlocker refreshBlocker;
|
QgsCanvasRefreshBlocker refreshBlocker;
|
||||||
if ( checkUnsavedLayerEdits() && checkMemoryLayers() && saveDirty() && checkExitBlockers() )
|
if ( checkUnsavedLayerEdits() && checkMemoryLayers() && saveDirty() && checkExitBlockers() && checkUnsavedRasterAttributeTableEdits() )
|
||||||
{
|
{
|
||||||
closeProject();
|
closeProject();
|
||||||
userProfileManager()->setDefaultFromActive();
|
userProfileManager()->setDefaultFromActive();
|
||||||
@ -5638,7 +5644,7 @@ bool QgisApp::fileNew( bool promptToSaveFlag, bool forceBlank )
|
|||||||
|
|
||||||
if ( promptToSaveFlag )
|
if ( promptToSaveFlag )
|
||||||
{
|
{
|
||||||
if ( !checkUnsavedLayerEdits() || !checkMemoryLayers() || !saveDirty() )
|
if ( !checkUnsavedLayerEdits() || !checkMemoryLayers() || !saveDirty() || !checkUnsavedRasterAttributeTableEdits() )
|
||||||
{
|
{
|
||||||
return false; //cancel pressed
|
return false; //cancel pressed
|
||||||
}
|
}
|
||||||
@ -5710,7 +5716,7 @@ bool QgisApp::fileNewFromTemplate( const QString &fileName )
|
|||||||
if ( checkTasksDependOnProject() )
|
if ( checkTasksDependOnProject() )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if ( !checkUnsavedLayerEdits() || !checkMemoryLayers() || !saveDirty() )
|
if ( !checkUnsavedLayerEdits() || !checkMemoryLayers() || !saveDirty() || !checkUnsavedRasterAttributeTableEdits() )
|
||||||
{
|
{
|
||||||
return false; //cancel pressed
|
return false; //cancel pressed
|
||||||
}
|
}
|
||||||
@ -6206,7 +6212,7 @@ void QgisApp::fileOpen()
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
// possibly save any pending work before opening a new project
|
// possibly save any pending work before opening a new project
|
||||||
if ( checkUnsavedLayerEdits() && checkMemoryLayers() && saveDirty() )
|
if ( checkUnsavedLayerEdits() && checkMemoryLayers() && saveDirty() && checkUnsavedRasterAttributeTableEdits() )
|
||||||
{
|
{
|
||||||
// Retrieve last used project dir from persistent settings
|
// Retrieve last used project dir from persistent settings
|
||||||
QgsSettings settings;
|
QgsSettings settings;
|
||||||
@ -6262,7 +6268,7 @@ void QgisApp::fileRevert()
|
|||||||
QMessageBox::Yes | QMessageBox::No, QMessageBox::No ) == QMessageBox::No )
|
QMessageBox::Yes | QMessageBox::No, QMessageBox::No ) == QMessageBox::No )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if ( !checkUnsavedLayerEdits() || !checkMemoryLayers() )
|
if ( !checkUnsavedLayerEdits() || !checkMemoryLayers() || ! checkUnsavedRasterAttributeTableEdits() )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// re-open the current project
|
// re-open the current project
|
||||||
@ -6760,7 +6766,7 @@ void QgisApp::openProject( QAction *action )
|
|||||||
if ( checkTasksDependOnProject() )
|
if ( checkTasksDependOnProject() )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if ( checkUnsavedLayerEdits() && checkMemoryLayers() && saveDirty() )
|
if ( checkUnsavedLayerEdits() && checkMemoryLayers() && saveDirty() && checkUnsavedRasterAttributeTableEdits() )
|
||||||
addProject( project );
|
addProject( project );
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -6805,7 +6811,7 @@ void QgisApp::openProject( const QString &fileName )
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
// possibly save any pending work before opening a different project
|
// possibly save any pending work before opening a different project
|
||||||
if ( checkUnsavedLayerEdits() && checkMemoryLayers() && saveDirty() )
|
if ( checkUnsavedLayerEdits() && checkMemoryLayers() && saveDirty() && checkUnsavedRasterAttributeTableEdits() )
|
||||||
{
|
{
|
||||||
// error handling and reporting is in addProject() function
|
// error handling and reporting is in addProject() function
|
||||||
addProject( fileName );
|
addProject( fileName );
|
||||||
@ -12027,6 +12033,30 @@ void QgisApp::openRasterAttributeTable()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void QgisApp::loadRasterAttributeTableFromFile( )
|
||||||
|
{
|
||||||
|
if ( !mLayerTreeView )
|
||||||
|
return;
|
||||||
|
|
||||||
|
//find current Layer
|
||||||
|
QgsMapLayer *currentLayer = mLayerTreeView->currentLayer();
|
||||||
|
if ( !currentLayer )
|
||||||
|
return;
|
||||||
|
|
||||||
|
QgsRasterLayer *layer = qobject_cast<QgsRasterLayer *>( currentLayer );
|
||||||
|
|
||||||
|
int bandNumber { 0 };
|
||||||
|
|
||||||
|
if ( layer )
|
||||||
|
{
|
||||||
|
QgsCreateRasterAttributeTableDialog dlg { layer };
|
||||||
|
if ( dlg.exec() == QDialog::Accepted )
|
||||||
|
{
|
||||||
|
// TODO!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void QgisApp::createRasterAttributeTable()
|
void QgisApp::createRasterAttributeTable()
|
||||||
{
|
{
|
||||||
if ( !mLayerTreeView )
|
if ( !mLayerTreeView )
|
||||||
@ -12053,7 +12083,7 @@ void QgisApp::createRasterAttributeTable()
|
|||||||
if ( ! rat )
|
if ( ! rat )
|
||||||
{
|
{
|
||||||
visibleMessageBar()->pushMessage( tr( "Error Creating Raster Attribute Table" ),
|
visibleMessageBar()->pushMessage( tr( "Error Creating Raster Attribute Table" ),
|
||||||
tr( "The Raster Attribute Table could not be created." ),
|
tr( "The raster attribute table could not be created." ),
|
||||||
Qgis::MessageLevel::Critical );
|
Qgis::MessageLevel::Critical );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -13381,6 +13411,94 @@ bool QgisApp::checkUnsavedLayerEdits()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool QgisApp::checkUnsavedRasterAttributeTableEdits( const QList<QgsMapLayer *> &mapLayers, bool allowCancel )
|
||||||
|
{
|
||||||
|
bool retVal { true };
|
||||||
|
|
||||||
|
QVector<QgsRasterLayer *> rasterLayers;
|
||||||
|
|
||||||
|
if ( ! mapLayers.isEmpty() )
|
||||||
|
{
|
||||||
|
for ( QgsMapLayer *mapLayer : std::as_const( mapLayers ) )
|
||||||
|
{
|
||||||
|
if ( QgsRasterLayer *rasterLayer = qobject_cast< QgsRasterLayer *>( mapLayer ) )
|
||||||
|
{
|
||||||
|
rasterLayers.push_back( rasterLayer );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rasterLayers = QgsProject::instance()->layers<QgsRasterLayer *>();
|
||||||
|
}
|
||||||
|
|
||||||
|
for ( QgsRasterLayer *rasterLayer : std::as_const( rasterLayers ) )
|
||||||
|
{
|
||||||
|
QStringList dirtyBands;
|
||||||
|
QList<QgsRasterAttributeTable *> dirtyRats;
|
||||||
|
|
||||||
|
for ( int bandNo = 1; bandNo < rasterLayer->bandCount(); ++bandNo )
|
||||||
|
{
|
||||||
|
if ( QgsRasterAttributeTable *rat = rasterLayer->attributeTable( bandNo ); rat && rat->isDirty() )
|
||||||
|
{
|
||||||
|
dirtyBands.push_back( QString::number( bandNo ) );
|
||||||
|
dirtyRats.push_back( rat );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( ! dirtyBands.isEmpty( ) )
|
||||||
|
{
|
||||||
|
QMessageBox::StandardButtons buttons = QMessageBox::Save | QMessageBox::Discard;
|
||||||
|
if ( allowCancel )
|
||||||
|
{
|
||||||
|
buttons |= QMessageBox::Cancel;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ( QMessageBox::question( nullptr,
|
||||||
|
tr( "Save Raster Attribute Table" ),
|
||||||
|
tr( "Do you want to save the changes to the attribute tables (bands: %1) associated with layer '%2'?" ).arg( dirtyBands.join( QStringLiteral( ", " ) ), rasterLayer->name() ),
|
||||||
|
buttons ) )
|
||||||
|
{
|
||||||
|
|
||||||
|
case QMessageBox::Save:
|
||||||
|
{
|
||||||
|
for ( QgsRasterAttributeTable *rat : std::as_const( dirtyRats ) )
|
||||||
|
{
|
||||||
|
QString errorMessage;
|
||||||
|
if ( rat->filePath().isEmpty( ) )
|
||||||
|
{
|
||||||
|
if ( ! rasterLayer->dataProvider()->writeNativeAttributeTable( &errorMessage ) )
|
||||||
|
{
|
||||||
|
visibleMessageBar()->pushMessage( tr( "Error Saving Raster Attribute Table" ),
|
||||||
|
tr( "An error occourred while saving raster attribute table for layer '%1': %2" ).arg( rasterLayer->name(), errorMessage ),
|
||||||
|
Qgis::MessageLevel::Critical );
|
||||||
|
retVal = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ( ! rat->writeToFile( rat->filePath(), &errorMessage ) )
|
||||||
|
{
|
||||||
|
visibleMessageBar()->pushMessage( tr( "Error Saving Raster Attribute Table" ),
|
||||||
|
tr( "An error occourred while saving raster attribute table for layer '%1' to VAT.DBF file '%2': %3" ).arg( rasterLayer->name(), rat->filePath(), errorMessage ),
|
||||||
|
Qgis::MessageLevel::Critical );
|
||||||
|
retVal = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case QMessageBox::Cancel:
|
||||||
|
retVal = false;
|
||||||
|
break;
|
||||||
|
case QMessageBox::Discard:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
bool QgisApp::checkMemoryLayers()
|
bool QgisApp::checkMemoryLayers()
|
||||||
{
|
{
|
||||||
if ( !QgsSettings().value( QStringLiteral( "askToSaveMemoryLayers" ), true, QgsSettings::App ).toBool() )
|
if ( !QgsSettings().value( QStringLiteral( "askToSaveMemoryLayers" ), true, QgsSettings::App ).toBool() )
|
||||||
@ -16175,6 +16293,8 @@ void QgisApp::showLayerProperties( QgsMapLayer *mapLayer, const QString &page )
|
|||||||
{
|
{
|
||||||
rasterLayerPropertiesDialog->deleteLater();
|
rasterLayerPropertiesDialog->deleteLater();
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1106,7 +1106,7 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
|
|||||||
void legendLayerStretchUsingCurrentExtent();
|
void legendLayerStretchUsingCurrentExtent();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Open the RasterAttributeTable for the raster layer.
|
* Open the Raster Attribute Table for the raster layer.
|
||||||
* Only works on raster layers.
|
* Only works on raster layers.
|
||||||
*
|
*
|
||||||
* \since QGIS 3.30
|
* \since QGIS 3.30
|
||||||
@ -1114,7 +1114,7 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
|
|||||||
void openRasterAttributeTable();
|
void openRasterAttributeTable();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new RasterAttributeTable from the raster layer renderer if the
|
* Creates a new Raster Attribute Table from the raster layer renderer if the
|
||||||
* renderer supports it.
|
* renderer supports it.
|
||||||
*
|
*
|
||||||
* Only works on raster layers.
|
* Only works on raster layers.
|
||||||
@ -1123,6 +1123,15 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
|
|||||||
*/
|
*/
|
||||||
void createRasterAttributeTable();
|
void createRasterAttributeTable();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads a Raster Attribute Table from a VAT.DBF file.
|
||||||
|
*
|
||||||
|
* Only works on raster layers.
|
||||||
|
*
|
||||||
|
* \since QGIS 3.30
|
||||||
|
*/
|
||||||
|
void loadRasterAttributeTableFromFile();
|
||||||
|
|
||||||
//! Watch for QFileOpenEvent.
|
//! Watch for QFileOpenEvent.
|
||||||
bool event( QEvent *event ) override;
|
bool event( QEvent *event ) override;
|
||||||
|
|
||||||
@ -1339,6 +1348,19 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
|
|||||||
*/
|
*/
|
||||||
bool checkUnsavedLayerEdits();
|
bool checkUnsavedLayerEdits();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks for unsaved changes in raster attribute tables and prompts the user to save
|
||||||
|
* or discard these changes for each raster attribute table.
|
||||||
|
*
|
||||||
|
* Returns TRUE if there are no unsaved raster attribute table remaining, or the user
|
||||||
|
* opted to discard them all. Returns FALSE if the user opted to cancel
|
||||||
|
* on any raster attribute table.
|
||||||
|
*
|
||||||
|
* \param mapLayers optional list of layers to check, if empty all project raster layers will be checked.
|
||||||
|
* \param allowCancel optional flag (default TRUE) that switches on the "Cancel" button.
|
||||||
|
*/
|
||||||
|
bool checkUnsavedRasterAttributeTableEdits( const QList<QgsMapLayer *> &mapLayers = QList<QgsMapLayer *>(), bool allowCancel = true );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks whether memory layers (with features) exist in the project, and if so
|
* Checks whether memory layers (with features) exist in the project, and if so
|
||||||
* shows a warning to users that their contents will be lost on
|
* shows a warning to users that their contents will be lost on
|
||||||
|
@ -229,8 +229,10 @@ QMenu *QgsAppLayerTreeViewMenuProvider::createContextMenu()
|
|||||||
}
|
}
|
||||||
else if ( rlayer->canCreateRasterAttributeTable() )
|
else if ( rlayer->canCreateRasterAttributeTable() )
|
||||||
{
|
{
|
||||||
menu->addAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionAddTable.svg" ) ), tr( "Create Raster Attribute Table" ), QgisApp::instance(), &QgisApp::createRasterAttributeTable );
|
menu->addAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionCreateTable.svg" ) ), tr( "Create Raster Attribute Table" ), QgisApp::instance(), &QgisApp::createRasterAttributeTable );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
menu->addAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionAddTable.svg" ) ), tr( "Load Raster Attribute Table from VAT.DBF" ), QgisApp::instance(), &QgisApp::loadRasterAttributeTableFromFile );
|
||||||
}
|
}
|
||||||
|
|
||||||
// No raster support in createSqlVectorLayer (yet)
|
// No raster support in createSqlVectorLayer (yet)
|
||||||
|
@ -3212,25 +3212,10 @@ bool QgsGdalProvider::writeNativeAttributeTable( QString *errorMessage ) //#spel
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Needs to be in write mode for HFA and perhaps other formats!
|
// Needs to be in write mode for HFA and perhaps other formats!
|
||||||
if ( GDALGetAccess( mGdalDataset ) == GA_ReadOnly )
|
if ( ! isEditable() )
|
||||||
{
|
{
|
||||||
QgsDebugMsg( QStringLiteral( "re-opening the dataset in read/write mode" ) );
|
QgsDebugMsg( QStringLiteral( "re-opening the dataset in read/write mode" ) );
|
||||||
GDALClose( mGdalDataset );
|
setEditable( true );
|
||||||
|
|
||||||
mGdalBaseDataset = gdalOpen( dataSourceUri( true ), GDAL_OF_UPDATE );
|
|
||||||
|
|
||||||
// if the dataset couldn't be opened in read / write mode, tell the user
|
|
||||||
if ( !mGdalBaseDataset )
|
|
||||||
{
|
|
||||||
mGdalBaseDataset = gdalOpen( dataSourceUri( true ), GDAL_OF_READONLY );
|
|
||||||
//Since we are not a virtual warped dataset, mGdalDataSet and mGdalBaseDataset are supposed to be the same
|
|
||||||
mGdalDataset = mGdalBaseDataset;
|
|
||||||
if ( errorMessage )
|
|
||||||
{
|
|
||||||
*errorMessage = tr( "GDAL Error reopening dataset in write mode, raster attribute table could not be saved." );
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
wasReopenedReadWrite = true;
|
wasReopenedReadWrite = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3327,17 +3312,7 @@ bool QgsGdalProvider::writeNativeAttributeTable( QString *errorMessage ) //#spel
|
|||||||
if ( wasReopenedReadWrite )
|
if ( wasReopenedReadWrite )
|
||||||
{
|
{
|
||||||
QgsDebugMsg( QStringLiteral( "re-opening the dataset in read-only mode" ) );
|
QgsDebugMsg( QStringLiteral( "re-opening the dataset in read-only mode" ) );
|
||||||
GDALClose( mGdalDataset );
|
setEditable( false );
|
||||||
|
|
||||||
mGdalBaseDataset = gdalOpen( dataSourceUri( true ), GDAL_OF_READONLY );
|
|
||||||
|
|
||||||
// if the dataset couldn't be opened in read / write mode, tell the user
|
|
||||||
if ( !mGdalBaseDataset )
|
|
||||||
{
|
|
||||||
mGdalBaseDataset = gdalOpen( dataSourceUri( true ), GDAL_OF_UPDATE );
|
|
||||||
//Since we are not a virtual warped dataset, mGdalDataSet and mGdalBaseDataset are supposed to be the same
|
|
||||||
mGdalDataset = mGdalBaseDataset;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return success;
|
return success;
|
||||||
|
@ -479,6 +479,7 @@ bool QgsRasterAttributeTable::removeRow( int position, QString *errorMessage )
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
mData.removeAt( position );
|
mData.removeAt( position );
|
||||||
|
setDirty( true );
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -723,8 +724,12 @@ bool QgsRasterAttributeTable::setValue( const int row, const int column, const Q
|
|||||||
}
|
}
|
||||||
|
|
||||||
const QVariant oldVal = mData[ row ][ column ];
|
const QVariant oldVal = mData[ row ][ column ];
|
||||||
mData[ row ][ column ] = newVal;
|
|
||||||
setDirty( newVal != oldVal );
|
if ( newVal != oldVal )
|
||||||
|
{
|
||||||
|
mData[ row ][ column ] = newVal;
|
||||||
|
setDirty( true );
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -1508,7 +1513,7 @@ QgsRasterRenderer *QgsRasterAttributeTable::createRenderer( QgsRasterDataProvide
|
|||||||
pseudoColorRenderer->setClassificationMin( minValue() );
|
pseudoColorRenderer->setClassificationMin( minValue() );
|
||||||
pseudoColorRenderer->setClassificationMax( maxValue() );
|
pseudoColorRenderer->setClassificationMax( maxValue() );
|
||||||
// Use discrete for single colors, interpolated for ramps
|
// Use discrete for single colors, interpolated for ramps
|
||||||
pseudoColorRenderer->createShader( ramp, hasColor() ? QgsColorRampShader::Type::Discrete : QgsColorRampShader::Type::Interpolated, QgsColorRampShader::ClassificationMode::Continuous, ramp->stops().count() + 2, true );
|
pseudoColorRenderer->createShader( ramp, hasRamp() ? QgsColorRampShader::Type::Interpolated : QgsColorRampShader::Type::Discrete, QgsColorRampShader::ClassificationMode::Continuous, ramp->stops().count() + 2, true );
|
||||||
pseudoColorRenderer->shader()->setMaximumValue( maxValue() );
|
pseudoColorRenderer->shader()->setMaximumValue( maxValue() );
|
||||||
pseudoColorRenderer->shader()->setMinimumValue( minValue() );
|
pseudoColorRenderer->shader()->setMinimumValue( minValue() );
|
||||||
// Set labels
|
// Set labels
|
||||||
@ -1516,10 +1521,30 @@ QgsRasterRenderer *QgsRasterAttributeTable::createRenderer( QgsRasterDataProvide
|
|||||||
{
|
{
|
||||||
shaderFunction->setMinimumValue( minValue() );
|
shaderFunction->setMinimumValue( minValue() );
|
||||||
shaderFunction->setMaximumValue( maxValue() );
|
shaderFunction->setMaximumValue( maxValue() );
|
||||||
const QList<QgsColorRampShader::ColorRampItem> itemList { shaderFunction->colorRampItemList() };
|
const bool labelsAreUsable { ramp->count() > 2 && labels.count() == ramp->count() - 1 };
|
||||||
const bool labelsAreUsable { labels.count() == itemList.count() };
|
|
||||||
if ( ! itemList.isEmpty() )
|
if ( labelsAreUsable )
|
||||||
{
|
{
|
||||||
|
QList<QgsColorRampShader::ColorRampItem> deduplicatedDitemList;
|
||||||
|
const double range { maxValue() - minValue() };
|
||||||
|
int stopIdx { 0 };
|
||||||
|
for ( const QString &label : std::as_const( labels ) )
|
||||||
|
{
|
||||||
|
if ( stopIdx >= ramp->count() - 2 )
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
double value { minValue() + ramp->stops().at( stopIdx ).offset * range };
|
||||||
|
QgsColorRampShader::ColorRampItem item { value, ramp->stops().at( stopIdx ).color, label };
|
||||||
|
deduplicatedDitemList.push_back( item );
|
||||||
|
stopIdx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
QgsColorRampShader::ColorRampItem item { maxValue(), ramp->color2(), labels.last() };
|
||||||
|
deduplicatedDitemList.push_back( item );
|
||||||
|
|
||||||
|
|
||||||
|
#if 0
|
||||||
QList<QgsColorRampShader::ColorRampItem> deduplicatedDitemList;
|
QList<QgsColorRampShader::ColorRampItem> deduplicatedDitemList;
|
||||||
// Deduplicate entries, this is necessary when the ramp for athematic
|
// Deduplicate entries, this is necessary when the ramp for athematic
|
||||||
// ramp rasters creates a stop for each class limit (min and max) which
|
// ramp rasters creates a stop for each class limit (min and max) which
|
||||||
@ -1548,6 +1573,8 @@ QgsRasterRenderer *QgsRasterAttributeTable::createRenderer( QgsRasterDataProvide
|
|||||||
deduplicatedDitemList.push_back( item );
|
deduplicatedDitemList.push_back( item );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
shaderFunction->setColorRampItemList( deduplicatedDitemList );
|
shaderFunction->setColorRampItemList( deduplicatedDitemList );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -257,7 +257,7 @@ QgsRasterAttributeTable *QgsRasterLayer::attributeTable( int bandNoInt ) const
|
|||||||
return mDataProvider->attributeTable( bandNoInt );
|
return mDataProvider->attributeTable( bandNoInt );
|
||||||
}
|
}
|
||||||
|
|
||||||
int QgsRasterLayer::attributeTableCount()
|
int QgsRasterLayer::attributeTableCount() const
|
||||||
{
|
{
|
||||||
if ( !mDataProvider )
|
if ( !mDataProvider )
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -326,7 +326,7 @@ class CORE_EXPORT QgsRasterLayer : public QgsMapLayer, public QgsAbstractProfile
|
|||||||
* Returns the number of attribute tables for the raster by counting the number of bands that have an associated attribute table.
|
* Returns the number of attribute tables for the raster by counting the number of bands that have an associated attribute table.
|
||||||
* \since QGIS 3.30
|
* \since QGIS 3.30
|
||||||
*/
|
*/
|
||||||
int attributeTableCount( );
|
int attributeTableCount( ) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns TRUE if the raster renderer is suitable for creation of a raster attribute table. The supported renderers are QgsPalettedRasterRenderer and QgsSingleBandPseudoColorRenderer.
|
* Returns TRUE if the raster renderer is suitable for creation of a raster attribute table. The supported renderers are QgsPalettedRasterRenderer and QgsSingleBandPseudoColorRenderer.
|
||||||
|
@ -16,11 +16,9 @@
|
|||||||
#include "qgsrasterattributetabledialog.h"
|
#include "qgsrasterattributetabledialog.h"
|
||||||
#include "qgsrasterlayer.h"
|
#include "qgsrasterlayer.h"
|
||||||
|
|
||||||
#include <QMessageBox>
|
|
||||||
|
|
||||||
QgsRasterAttributeTableDialog::QgsRasterAttributeTableDialog( QgsRasterLayer *rasterLayer, int bandNumber, QWidget *parent )
|
QgsRasterAttributeTableDialog::QgsRasterAttributeTableDialog( QgsRasterLayer *rasterLayer, int bandNumber, QWidget *parent )
|
||||||
: QDialog( parent )
|
: QDialog( parent )
|
||||||
, mRasterLayer( rasterLayer )
|
|
||||||
{
|
{
|
||||||
Q_ASSERT( rasterLayer );
|
Q_ASSERT( rasterLayer );
|
||||||
setupUi( this );
|
setupUi( this );
|
||||||
@ -28,40 +26,15 @@ QgsRasterAttributeTableDialog::QgsRasterAttributeTableDialog( QgsRasterLayer *ra
|
|||||||
setWindowTitle( tr( "Raster Attribute Table for %1" ).arg( rasterLayer->name() ) );
|
setWindowTitle( tr( "Raster Attribute Table for %1" ).arg( rasterLayer->name() ) );
|
||||||
|
|
||||||
connect( mButtonBox, &QDialogButtonBox::rejected, this, &QgsRasterAttributeTableDialog::reject );
|
connect( mButtonBox, &QDialogButtonBox::rejected, this, &QgsRasterAttributeTableDialog::reject );
|
||||||
|
connect( rasterLayer, &QgsRasterLayer::dataSourceChanged, this, &QgsRasterAttributeTableDialog::reject );
|
||||||
|
connect( rasterLayer, &QgsRasterLayer::willBeDeleted, this, &QgsRasterAttributeTableDialog::reject );
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void QgsRasterAttributeTableDialog::reject()
|
void QgsRasterAttributeTableDialog::reject()
|
||||||
{
|
{
|
||||||
QList<int> dirtyBands;
|
if ( mRatWidget->setEditable( false ) )
|
||||||
for ( int bandNo = 1; bandNo <= mRasterLayer->bandCount(); ++bandNo )
|
|
||||||
{
|
{
|
||||||
if ( QgsRasterAttributeTable *rat = mRasterLayer->attributeTable( bandNo ) )
|
QDialog::reject();
|
||||||
{
|
|
||||||
if ( rat->isDirty() )
|
|
||||||
{
|
|
||||||
dirtyBands.push_back( bandNo );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( ! dirtyBands.isEmpty() )
|
|
||||||
{
|
|
||||||
QString bandsStr;
|
|
||||||
for ( int i = 0; i < dirtyBands.size(); i++ )
|
|
||||||
{
|
|
||||||
bandsStr.append( QString::number( dirtyBands[i] ) );
|
|
||||||
if ( i < dirtyBands.size() - 1 )
|
|
||||||
bandsStr.append( QStringLiteral( ", " ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
QString msg { dirtyBands.count( ) > 1 ? tr( "Attribute table bands (%1) contain unsaved changes, close without saving?" ).arg( bandsStr ) : tr( "Attribute table band %1 contains unsaved changes, close without saving?" ).arg( bandsStr ) };
|
|
||||||
|
|
||||||
if ( QMessageBox::question( nullptr, tr( "Save Attribute Table" ), msg ) != QMessageBox::Yes )
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QDialog::reject();
|
|
||||||
}
|
}
|
||||||
|
@ -26,13 +26,13 @@ class QgsRasterLayer;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* \ingroup gui
|
* \ingroup gui
|
||||||
* \brief The QgsRasterAttributeTableDialog class embeds an attribute table widget
|
* \brief The QgsRasterAttributeTableDialog class embeds an attribute table widget.
|
||||||
* and contains logic to handle confirmation when closing with unsaved changes.
|
|
||||||
* \since QGIS 3.30
|
* \since QGIS 3.30
|
||||||
*/
|
*/
|
||||||
class GUI_EXPORT QgsRasterAttributeTableDialog: public QDialog, private Ui::QRasterAttributeTableDialogBase
|
class GUI_EXPORT QgsRasterAttributeTableDialog: public QDialog, private Ui::QRasterAttributeTableDialogBase
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
QgsRasterAttributeTableDialog( QgsRasterLayer *rasterLayer, int bandNumber = 0, QWidget *parent SIP_TRANSFERTHIS = nullptr );
|
QgsRasterAttributeTableDialog( QgsRasterLayer *rasterLayer, int bandNumber = 0, QWidget *parent SIP_TRANSFERTHIS = nullptr );
|
||||||
@ -42,9 +42,6 @@ class GUI_EXPORT QgsRasterAttributeTableDialog: public QDialog, private Ui::QRas
|
|||||||
|
|
||||||
void reject() override;
|
void reject() override;
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
QgsRasterLayer *mRasterLayer = nullptr;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // QGSRASTERATTRIBUTETABLEDIALOG_H
|
#endif // QGSRASTERATTRIBUTETABLEDIALOG_H
|
||||||
|
@ -90,14 +90,14 @@ void QgsRasterAttributeTableWidget::setRasterLayer( QgsRasterLayer *rasterLayer,
|
|||||||
|
|
||||||
bool QgsRasterAttributeTableWidget::isDirty() const
|
bool QgsRasterAttributeTableWidget::isDirty() const
|
||||||
{
|
{
|
||||||
return mAttributeTable && mAttributeTable->isDirty();
|
return mAttributeTableBuffer && mAttributeTableBuffer->isDirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
void QgsRasterAttributeTableWidget::init( int bandNumber )
|
void QgsRasterAttributeTableWidget::init( int bandNumber )
|
||||||
{
|
{
|
||||||
|
|
||||||
disconnect( mRasterBandsComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsRasterAttributeTableWidget::bandChanged );
|
disconnect( mRasterBandsComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsRasterAttributeTableWidget::bandChanged );
|
||||||
mAttributeTable = nullptr;
|
mAttributeTableBuffer = nullptr;
|
||||||
mCurrentBand = 0;
|
mCurrentBand = 0;
|
||||||
mRasterBandsComboBox->clear();
|
mRasterBandsComboBox->clear();
|
||||||
|
|
||||||
@ -120,22 +120,22 @@ void QgsRasterAttributeTableWidget::init( int bandNumber )
|
|||||||
if ( availableRats.contains( bandNumber ) )
|
if ( availableRats.contains( bandNumber ) )
|
||||||
{
|
{
|
||||||
mCurrentBand = bandNumber;
|
mCurrentBand = bandNumber;
|
||||||
mAttributeTable = mRasterLayer->attributeTable( mCurrentBand );
|
|
||||||
mRasterBandsComboBox->setCurrentIndex( availableRats.indexOf( mCurrentBand ) );
|
|
||||||
}
|
}
|
||||||
else if ( ! availableRats.isEmpty() )
|
else if ( ! availableRats.isEmpty() )
|
||||||
{
|
{
|
||||||
mCurrentBand = availableRats.first();
|
mCurrentBand = availableRats.first();
|
||||||
mAttributeTable = mRasterLayer->attributeTable( mCurrentBand );
|
|
||||||
mRasterBandsComboBox->setCurrentIndex( availableRats.indexOf( mCurrentBand ) );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mAttributeTableBuffer = std::make_unique<QgsRasterAttributeTable>( *mRasterLayer->attributeTable( mCurrentBand ) );
|
||||||
|
mRasterBandsComboBox->setCurrentIndex( availableRats.indexOf( mCurrentBand ) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( mAttributeTable )
|
if ( mAttributeTableBuffer )
|
||||||
{
|
{
|
||||||
mModel.reset( new QgsRasterAttributeTableModel( mAttributeTable ) );
|
mModel.reset( new QgsRasterAttributeTableModel( mAttributeTableBuffer.get() ) );
|
||||||
mModel->setEditable( mEditable );
|
mModel->setEditable( mEditable );
|
||||||
|
|
||||||
connect( mModel.get(), &QgsRasterAttributeTableModel::dataChanged, this, [ = ]( const QModelIndex &, const QModelIndex &, const QVector<int> & )
|
connect( mModel.get(), &QgsRasterAttributeTableModel::dataChanged, this, [ = ]( const QModelIndex &, const QModelIndex &, const QVector<int> & )
|
||||||
{
|
{
|
||||||
updateButtons();
|
updateButtons();
|
||||||
@ -166,14 +166,15 @@ void QgsRasterAttributeTableWidget::init( int bandNumber )
|
|||||||
|
|
||||||
void QgsRasterAttributeTableWidget::updateButtons()
|
void QgsRasterAttributeTableWidget::updateButtons()
|
||||||
{
|
{
|
||||||
const bool enableEditingButtons { static_cast<bool>( mAttributeTable ) &&mEditable &&mRATView->selectionModel()->currentIndex().isValid() };
|
const bool enableEditingButtons( static_cast<bool>( mAttributeTableBuffer ) && mEditable && mRATView->selectionModel()->currentIndex().isValid() );
|
||||||
|
mActionToggleEditing->setChecked( mEditable );
|
||||||
mActionAddColumn->setEnabled( mEditable );
|
mActionAddColumn->setEnabled( mEditable );
|
||||||
mActionRemoveColumn->setEnabled( enableEditingButtons );
|
mActionRemoveColumn->setEnabled( enableEditingButtons );
|
||||||
mActionAddRow->setEnabled( enableEditingButtons );
|
mActionAddRow->setEnabled( enableEditingButtons );
|
||||||
mActionRemoveRow->setEnabled( enableEditingButtons );
|
mActionRemoveRow->setEnabled( enableEditingButtons );
|
||||||
mActionSaveChanges->setEnabled( mAttributeTable && mAttributeTable->isDirty() );
|
mActionSaveChanges->setEnabled( mAttributeTableBuffer && mAttributeTableBuffer->isDirty() );
|
||||||
mClassifyButton->setEnabled( mAttributeTable && mRasterLayer );
|
mClassifyButton->setEnabled( mAttributeTableBuffer && mRasterLayer );
|
||||||
mClassifyComboBox->setEnabled( mAttributeTable && mRasterLayer );
|
mClassifyComboBox->setEnabled( mAttributeTableBuffer && mRasterLayer );
|
||||||
}
|
}
|
||||||
|
|
||||||
void QgsRasterAttributeTableWidget::setMessageBar( QgsMessageBar *bar )
|
void QgsRasterAttributeTableWidget::setMessageBar( QgsMessageBar *bar )
|
||||||
@ -181,83 +182,109 @@ void QgsRasterAttributeTableWidget::setMessageBar( QgsMessageBar *bar )
|
|||||||
mMessageBar = bar;
|
mMessageBar = bar;
|
||||||
}
|
}
|
||||||
|
|
||||||
void QgsRasterAttributeTableWidget::setEditable( bool editable )
|
bool QgsRasterAttributeTableWidget::setEditable( bool editable )
|
||||||
{
|
{
|
||||||
bool isDirty { false };
|
const bool isDirty { mAttributeTableBuffer &&mAttributeTableBuffer->isDirty() &&mCurrentBand > 0 && mRasterLayer->attributeTable( mCurrentBand ) };
|
||||||
|
bool retVal { true };
|
||||||
for ( int bandNo = 1; bandNo <= mRasterLayer->bandCount(); ++bandNo )
|
// Switch to read-only
|
||||||
|
if ( ! editable && isDirty )
|
||||||
{
|
{
|
||||||
if ( mRasterLayer->dataProvider()->attributeTable( bandNo ) && mRasterLayer->dataProvider()->attributeTable( bandNo )->isDirty() )
|
const QMessageBox::StandardButtons buttons { QMessageBox::Button::Cancel | QMessageBox::Button::Yes | QMessageBox::Button::No };
|
||||||
|
switch ( QMessageBox::question( nullptr, tr( "Save Attribute Table" ), tr( "Attribute table contains unsaved changes, do you want to save the changes?" ), buttons ) )
|
||||||
{
|
{
|
||||||
isDirty = true;
|
case QMessageBox::Button::Cancel:
|
||||||
break;
|
{
|
||||||
|
retVal = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case QMessageBox::Button::Yes:
|
||||||
|
{
|
||||||
|
saveChanges();
|
||||||
|
retVal = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case QMessageBox::Button::No:
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
// Reset to its original state
|
||||||
|
mAttributeTableBuffer = std::make_unique<QgsRasterAttributeTable>( *mRasterLayer->attributeTable( mCurrentBand ) );
|
||||||
|
init( mCurrentBand );
|
||||||
|
retVal = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ( isDirty && QMessageBox::question( nullptr, tr( "Save Attribute Table" ), tr( "Attribute table contains unsaved changes, do you want to save the changes?" ) ) == QMessageBox::Yes )
|
|
||||||
|
if ( retVal )
|
||||||
{
|
{
|
||||||
saveChanges();
|
mEditable = editable;
|
||||||
|
mModel->setEditable( editable );
|
||||||
}
|
}
|
||||||
|
|
||||||
mEditable = editable;
|
|
||||||
mModel->setEditable( editable );
|
|
||||||
updateButtons();
|
updateButtons();
|
||||||
|
|
||||||
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
void QgsRasterAttributeTableWidget::saveChanges()
|
void QgsRasterAttributeTableWidget::saveChanges()
|
||||||
{
|
{
|
||||||
if ( mRasterLayer )
|
if ( mRasterLayer && mAttributeTableBuffer && mAttributeTableBuffer->isDirty() && mCurrentBand > 0 )
|
||||||
{
|
{
|
||||||
for ( int bandNo = 1; bandNo <= mRasterLayer->bandCount(); ++bandNo )
|
QgsRasterAttributeTable *attributeTable { mRasterLayer->dataProvider()->attributeTable( mCurrentBand ) };
|
||||||
|
if ( ! attributeTable )
|
||||||
{
|
{
|
||||||
QgsRasterAttributeTable *attributeTable { mRasterLayer->dataProvider()->attributeTable( bandNo ) };
|
QgsDebugMsg( QStringLiteral( "Error saving RAT: RAT for band %1 is unexpectedly gone!" ).arg( mCurrentBand ) );
|
||||||
if ( attributeTable && attributeTable->isDirty() )
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*attributeTable = *mAttributeTableBuffer;
|
||||||
|
QString errorMessage;
|
||||||
|
QString newPath { attributeTable->filePath() };
|
||||||
|
const bool nativeRatSupported = mRasterLayer->dataProvider()->providerCapabilities().testFlag( QgsRasterDataProvider::ProviderCapability::NativeRasterAttributeTable );
|
||||||
|
bool saveToNative { false };
|
||||||
|
|
||||||
|
if ( newPath.isEmpty() && ! nativeRatSupported )
|
||||||
{
|
{
|
||||||
QString errorMessage;
|
newPath = QFileDialog::getOpenFileName( nullptr, tr( "Save Raster Attribute Table (band %1) To File" ).arg( mCurrentBand ), QFile::exists( mRasterLayer->dataProvider()->dataSourceUri( ) ) ? mRasterLayer->dataProvider()->dataSourceUri( ) + ".vat.dbf" : QString(), QStringLiteral( "VAT DBF Files (*.vat.dbf)" ) );
|
||||||
QString newPath { attributeTable->filePath() };
|
if ( newPath.isEmpty() )
|
||||||
const bool nativeRatSupported = mRasterLayer->dataProvider()->providerCapabilities().testFlag( QgsRasterDataProvider::ProviderCapability::NativeRasterAttributeTable );
|
|
||||||
bool saveToNative { false };
|
|
||||||
|
|
||||||
if ( newPath.isEmpty() && ! nativeRatSupported )
|
|
||||||
{
|
|
||||||
newPath = QFileDialog::getOpenFileName( nullptr, tr( "Save Raster Attribute Table (band %1) To File" ).arg( bandNo ), QFile::exists( mRasterLayer->dataProvider()->dataSourceUri( ) ) ? mRasterLayer->dataProvider()->dataSourceUri( ) + ".vat.dbf" : QString(), QStringLiteral( "VAT DBF Files (*.vat.dbf)" ) );
|
|
||||||
if ( newPath.isEmpty() )
|
|
||||||
{
|
|
||||||
// Aborted by user
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if ( newPath.isEmpty() )
|
|
||||||
{
|
|
||||||
saveToNative = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool writeSuccess { false };
|
|
||||||
|
|
||||||
// Save to file
|
|
||||||
if ( ! saveToNative && ! newPath.isEmpty() )
|
|
||||||
{
|
|
||||||
writeSuccess = attributeTable->writeToFile( attributeTable->filePath(), &errorMessage );
|
|
||||||
}
|
|
||||||
else if ( saveToNative )
|
|
||||||
{
|
|
||||||
writeSuccess = mRasterLayer->dataProvider()->writeNativeAttributeTable( &errorMessage );
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( writeSuccess )
|
|
||||||
{
|
|
||||||
notify( tr( "Attribute Table Write Success" ), tr( "The raster attibute table has been successfully saved." ), Qgis::MessageLevel::Success );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
notify( tr( "Attribute Table Write Error" ), errorMessage, Qgis::MessageLevel::Critical );
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save to native saves RATs for all bands, no need to loop further.
|
|
||||||
if ( saveToNative )
|
|
||||||
{
|
{
|
||||||
|
// Aborted by user
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if ( newPath.isEmpty() )
|
||||||
|
{
|
||||||
|
saveToNative = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool writeSuccess { false };
|
||||||
|
|
||||||
|
// Save to file
|
||||||
|
if ( ! saveToNative && ! newPath.isEmpty() )
|
||||||
|
{
|
||||||
|
writeSuccess = attributeTable->writeToFile( attributeTable->filePath(), &errorMessage );
|
||||||
|
}
|
||||||
|
else if ( saveToNative )
|
||||||
|
{
|
||||||
|
writeSuccess = mRasterLayer->dataProvider()->writeNativeAttributeTable( &errorMessage );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( writeSuccess )
|
||||||
|
{
|
||||||
|
mAttributeTableBuffer->setDirty( false );
|
||||||
|
notify( tr( "Attribute Table Write Success" ), tr( "The raster attibute table has been successfully saved." ), Qgis::MessageLevel::Success );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
notify( tr( "Attribute Table Write Error" ), errorMessage, Qgis::MessageLevel::Critical );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save to native saves RATs for all bands, no need to loop further.
|
||||||
|
if ( saveToNative )
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -267,7 +294,7 @@ void QgsRasterAttributeTableWidget::saveChanges()
|
|||||||
void QgsRasterAttributeTableWidget::classify()
|
void QgsRasterAttributeTableWidget::classify()
|
||||||
{
|
{
|
||||||
|
|
||||||
if ( ! mAttributeTable )
|
if ( ! mAttributeTableBuffer )
|
||||||
{
|
{
|
||||||
notify( tr( "Classification Error" ), tr( "The raster attribute table is not set." ), Qgis::MessageLevel::Critical );
|
notify( tr( "Classification Error" ), tr( "The raster attribute table is not set." ), Qgis::MessageLevel::Critical );
|
||||||
return;
|
return;
|
||||||
@ -279,13 +306,22 @@ void QgsRasterAttributeTableWidget::classify()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( QMessageBox::question( nullptr, tr( "Apply Style From Attribute Table" ), tr( "The existing style for the raster will be replaced by a new style from the attribute table and any unsaved changes to the current style will be lost, do you want to proceed?" ) ) == QMessageBox::Yes )
|
QString confirmMessage;
|
||||||
|
QString errorMessage;
|
||||||
|
|
||||||
|
if ( ! mAttributeTableBuffer->isValid( &errorMessage ) )
|
||||||
|
{
|
||||||
|
confirmMessage = tr( "The attribute table does not seem to be valid and it may produce an unusable symbology, validation errors:<br>%1<br>" ).arg( errorMessage );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( QMessageBox::question( nullptr, tr( "Apply Style From Attribute Table" ), confirmMessage.append( tr( "The existing symbology for the raster will be replaced by a new symbology from the attribute table and any unsaved changes to the current symbology will be lost, do you want to proceed?" ) ) ) == QMessageBox::Yes )
|
||||||
{
|
{
|
||||||
|
|
||||||
if ( QgsRasterRenderer *renderer = mAttributeTable->createRenderer( mRasterLayer->dataProvider(), mCurrentBand, mClassifyComboBox->currentData().toInt() ) )
|
if ( QgsRasterRenderer *renderer = mAttributeTableBuffer->createRenderer( mRasterLayer->dataProvider(), mCurrentBand, mClassifyComboBox->currentData().toInt() ) )
|
||||||
{
|
{
|
||||||
mRasterLayer->setRenderer( renderer );
|
mRasterLayer->setRenderer( renderer );
|
||||||
mRasterLayer->triggerRepaint( );
|
mRasterLayer->triggerRepaint( );
|
||||||
|
emit rendererChanged();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -296,9 +332,9 @@ void QgsRasterAttributeTableWidget::classify()
|
|||||||
|
|
||||||
void QgsRasterAttributeTableWidget::addColumn()
|
void QgsRasterAttributeTableWidget::addColumn()
|
||||||
{
|
{
|
||||||
if ( mAttributeTable )
|
if ( mAttributeTableBuffer )
|
||||||
{
|
{
|
||||||
QgsRasterAttributeTableAddColumnDialog dlg { mAttributeTable };
|
QgsRasterAttributeTableAddColumnDialog dlg { mAttributeTableBuffer.get() };
|
||||||
if ( dlg.exec() == QDialog::Accepted )
|
if ( dlg.exec() == QDialog::Accepted )
|
||||||
{
|
{
|
||||||
QString errorMessage;
|
QString errorMessage;
|
||||||
@ -330,7 +366,7 @@ void QgsRasterAttributeTableWidget::addColumn()
|
|||||||
void QgsRasterAttributeTableWidget::removeColumn()
|
void QgsRasterAttributeTableWidget::removeColumn()
|
||||||
{
|
{
|
||||||
const QModelIndex currentIndex { mProxyModel->mapToSource( mRATView->selectionModel()->currentIndex() ) };
|
const QModelIndex currentIndex { mProxyModel->mapToSource( mRATView->selectionModel()->currentIndex() ) };
|
||||||
if ( mAttributeTable && currentIndex.isValid() && currentIndex.column() < mAttributeTable->fields().count() )
|
if ( mAttributeTableBuffer && currentIndex.isValid() && currentIndex.column() < mAttributeTableBuffer->fields().count() )
|
||||||
{
|
{
|
||||||
if ( QMessageBox::question( nullptr, tr( "Remove Column" ), tr( "Do you want to remove the selected column? This action cannot be undone." ) ) == QMessageBox::Yes )
|
if ( QMessageBox::question( nullptr, tr( "Remove Column" ), tr( "Do you want to remove the selected column? This action cannot be undone." ) ) == QMessageBox::Yes )
|
||||||
{
|
{
|
||||||
@ -345,7 +381,7 @@ void QgsRasterAttributeTableWidget::removeColumn()
|
|||||||
|
|
||||||
void QgsRasterAttributeTableWidget::addRow()
|
void QgsRasterAttributeTableWidget::addRow()
|
||||||
{
|
{
|
||||||
if ( mAttributeTable )
|
if ( mAttributeTableBuffer )
|
||||||
{
|
{
|
||||||
// Default to append
|
// Default to append
|
||||||
int position { mModel->rowCount( QModelIndex() ) };
|
int position { mModel->rowCount( QModelIndex() ) };
|
||||||
@ -372,7 +408,7 @@ void QgsRasterAttributeTableWidget::addRow()
|
|||||||
|
|
||||||
QVariantList rowData;
|
QVariantList rowData;
|
||||||
|
|
||||||
QList<QgsRasterAttributeTable::Field> fields { mAttributeTable->fields() };
|
QList<QgsRasterAttributeTable::Field> fields { mAttributeTableBuffer->fields() };
|
||||||
for ( const QgsRasterAttributeTable::Field &field : std::as_const( fields ) )
|
for ( const QgsRasterAttributeTable::Field &field : std::as_const( fields ) )
|
||||||
{
|
{
|
||||||
rowData.push_back( QVariant( field.type ) );
|
rowData.push_back( QVariant( field.type ) );
|
||||||
@ -393,7 +429,7 @@ void QgsRasterAttributeTableWidget::addRow()
|
|||||||
|
|
||||||
void QgsRasterAttributeTableWidget::removeRow()
|
void QgsRasterAttributeTableWidget::removeRow()
|
||||||
{
|
{
|
||||||
if ( mAttributeTable && mRATView->selectionModel()->currentIndex().isValid() )
|
if ( mAttributeTableBuffer && mRATView->selectionModel()->currentIndex().isValid() )
|
||||||
{
|
{
|
||||||
if ( QMessageBox::question( nullptr, tr( "Remove Row" ), tr( "Do you want to remove the selected row? This action cannot be undone." ) ) == QMessageBox::Yes )
|
if ( QMessageBox::question( nullptr, tr( "Remove Row" ), tr( "Do you want to remove the selected row? This action cannot be undone." ) ) == QMessageBox::Yes )
|
||||||
{
|
{
|
||||||
@ -409,8 +445,13 @@ void QgsRasterAttributeTableWidget::removeRow()
|
|||||||
void QgsRasterAttributeTableWidget::bandChanged( const int index )
|
void QgsRasterAttributeTableWidget::bandChanged( const int index )
|
||||||
{
|
{
|
||||||
const QVariant itemData = mRasterBandsComboBox->itemData( index );
|
const QVariant itemData = mRasterBandsComboBox->itemData( index );
|
||||||
|
|
||||||
if ( itemData.isValid() )
|
if ( itemData.isValid() )
|
||||||
{
|
{
|
||||||
|
if ( mEditable )
|
||||||
|
{
|
||||||
|
setEditable( false );
|
||||||
|
}
|
||||||
init( itemData.toInt( ) );
|
init( itemData.toInt( ) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -449,9 +490,9 @@ void QgsRasterAttributeTableWidget::notify( const QString &title, const QString
|
|||||||
void QgsRasterAttributeTableWidget::setDelegates()
|
void QgsRasterAttributeTableWidget::setDelegates()
|
||||||
{
|
{
|
||||||
mClassifyComboBox->clear();
|
mClassifyComboBox->clear();
|
||||||
if ( mAttributeTable )
|
if ( mAttributeTableBuffer )
|
||||||
{
|
{
|
||||||
const QList<QgsRasterAttributeTable::Field> tableFields { mAttributeTable->fields() };
|
const QList<QgsRasterAttributeTable::Field> tableFields { mAttributeTableBuffer->fields() };
|
||||||
int fieldIdx { 0 };
|
int fieldIdx { 0 };
|
||||||
const QHash<Qgis::RasterAttributeTableFieldUsage, QgsRasterAttributeTable::UsageInformation> usageInfo { QgsRasterAttributeTable::usageInformation() };
|
const QHash<Qgis::RasterAttributeTableFieldUsage, QgsRasterAttributeTable::UsageInformation> usageInfo { QgsRasterAttributeTable::usageInformation() };
|
||||||
for ( const QgsRasterAttributeTable::Field &f : std::as_const( tableFields ) )
|
for ( const QgsRasterAttributeTable::Field &f : std::as_const( tableFields ) )
|
||||||
@ -465,26 +506,26 @@ void QgsRasterAttributeTableWidget::setDelegates()
|
|||||||
fieldIdx++;
|
fieldIdx++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( mAttributeTable->hasColor() )
|
if ( mAttributeTableBuffer->hasColor() )
|
||||||
{
|
{
|
||||||
if ( mAttributeTable->usages().contains( Qgis::RasterAttributeTableFieldUsage::Alpha ) )
|
if ( mAttributeTableBuffer->usages().contains( Qgis::RasterAttributeTableFieldUsage::Alpha ) )
|
||||||
{
|
{
|
||||||
mRATView->setItemDelegateForColumn( mAttributeTable->fields().count( ), new ColorAlphaDelegate( mRATView ) );
|
mRATView->setItemDelegateForColumn( mAttributeTableBuffer->fields().count( ), new ColorAlphaDelegate( mRATView ) );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
mRATView->setItemDelegateForColumn( mAttributeTable->fields().count( ), new ColorDelegate( mRATView ) );
|
mRATView->setItemDelegateForColumn( mAttributeTableBuffer->fields().count( ), new ColorDelegate( mRATView ) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if ( mAttributeTable->hasRamp() )
|
else if ( mAttributeTableBuffer->hasRamp() )
|
||||||
{
|
{
|
||||||
if ( mAttributeTable->usages().contains( Qgis::RasterAttributeTableFieldUsage::AlphaMin ) )
|
if ( mAttributeTableBuffer->usages().contains( Qgis::RasterAttributeTableFieldUsage::AlphaMin ) )
|
||||||
{
|
{
|
||||||
mRATView->setItemDelegateForColumn( mAttributeTable->fields().count( ), new ColorRampAlphaDelegate( mRATView ) );
|
mRATView->setItemDelegateForColumn( mAttributeTableBuffer->fields().count( ), new ColorRampAlphaDelegate( mRATView ) );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
mRATView->setItemDelegateForColumn( mAttributeTable->fields().count( ), new ColorRampDelegate( mRATView ) );
|
mRATView->setItemDelegateForColumn( mAttributeTableBuffer->fields().count( ), new ColorRampDelegate( mRATView ) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -122,10 +122,48 @@ class GUI_EXPORT QgsRasterAttributeTableWidget : public QWidget, private Ui::Qgs
|
|||||||
*/
|
*/
|
||||||
bool isDirty( ) const;
|
bool isDirty( ) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the message \a bar associated with the widget. This allows the widget to push feedback messages
|
||||||
|
* to the appropriate message bar.
|
||||||
|
* \see messageBar()
|
||||||
|
*/
|
||||||
|
void setMessageBar( QgsMessageBar *bar );
|
||||||
|
|
||||||
|
signals:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This signal is emitted after a successful classify operation which changed the raster renderer.
|
||||||
|
*/
|
||||||
|
void rendererChanged( );
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save the changes in the raster attribute table.
|
||||||
|
*/
|
||||||
|
void saveChanges();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the editable state, it may trigger save changes if the attribute table has unsave changes.
|
||||||
|
*/
|
||||||
|
bool setEditable( bool editable );
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
|
||||||
|
|
||||||
|
void classify();
|
||||||
|
void addColumn();
|
||||||
|
void removeColumn();
|
||||||
|
void addRow();
|
||||||
|
void removeRow();
|
||||||
|
void bandChanged( const int index );
|
||||||
|
void notify( const QString &title, const QString &message, Qgis::MessageLevel level = Qgis::MessageLevel::Info );
|
||||||
|
void setDelegates( );
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
QgsRasterLayer *mRasterLayer = nullptr;
|
QgsRasterLayer *mRasterLayer = nullptr;
|
||||||
QgsRasterAttributeTable *mAttributeTable = nullptr;
|
std::unique_ptr<QgsRasterAttributeTable> mAttributeTableBuffer;
|
||||||
// Default to invalid (bands are 1-indexed)
|
// Default to invalid (bands are 1-indexed)
|
||||||
int mCurrentBand = 0;
|
int mCurrentBand = 0;
|
||||||
bool mEditable = false;
|
bool mEditable = false;
|
||||||
@ -144,28 +182,6 @@ class GUI_EXPORT QgsRasterAttributeTableWidget : public QWidget, private Ui::Qgs
|
|||||||
|
|
||||||
void init( int bandNumber = 0 );
|
void init( int bandNumber = 0 );
|
||||||
void updateButtons();
|
void updateButtons();
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the message \a bar associated with the widget. This allows the widget to push feedback messages
|
|
||||||
* to the appropriate message bar.
|
|
||||||
* \see messageBar()
|
|
||||||
*/
|
|
||||||
void setMessageBar( QgsMessageBar *bar );
|
|
||||||
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
|
|
||||||
void setEditable( bool editable );
|
|
||||||
void saveChanges();
|
|
||||||
void classify();
|
|
||||||
void addColumn();
|
|
||||||
void removeColumn();
|
|
||||||
void addRow();
|
|
||||||
void removeRow();
|
|
||||||
void bandChanged( const int index );
|
|
||||||
void notify( const QString &title, const QString &message, Qgis::MessageLevel level = Qgis::MessageLevel::Info );
|
|
||||||
void setDelegates( );
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // QGSRASTERATTRIBUTETABLEWIDGET_H
|
#endif // QGSRASTERATTRIBUTETABLEWIDGET_H
|
||||||
|
@ -68,6 +68,7 @@
|
|||||||
#include "qgsdoublevalidator.h"
|
#include "qgsdoublevalidator.h"
|
||||||
#include "qgsmaplayerconfigwidgetfactory.h"
|
#include "qgsmaplayerconfigwidgetfactory.h"
|
||||||
#include "qgsprojectutils.h"
|
#include "qgsprojectutils.h"
|
||||||
|
#include "qgsrasterattributetablewidget.h"
|
||||||
|
|
||||||
#include "qgsrasterlayertemporalpropertieswidget.h"
|
#include "qgsrasterlayertemporalpropertieswidget.h"
|
||||||
#include "qgsprojecttimesettings.h"
|
#include "qgsprojecttimesettings.h"
|
||||||
@ -234,6 +235,21 @@ QgsRasterLayerProperties::QgsRasterLayerProperties( QgsMapLayer *lyr, QgsMapCanv
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Setup raster attribute table
|
||||||
|
if ( mRasterLayer->attributeTableCount() > 0 )
|
||||||
|
{
|
||||||
|
mRasterAttributeTableWidget = new QgsRasterAttributeTableWidget( this, mRasterLayer );
|
||||||
|
mOptsPage_RasterAttributeTable->layout()->addWidget( mRasterAttributeTableWidget );
|
||||||
|
// When the renderer changes we need to sync the style options page
|
||||||
|
connect( mRasterAttributeTableWidget, &QgsRasterAttributeTableWidget::rendererChanged, this, &QgsRasterLayerProperties::syncToLayer );
|
||||||
|
mNoRasterAttributeTableWidget->hide();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mNoRasterAttributeTableWidget->show();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
mBackupCrs = mRasterLayer->crs();
|
mBackupCrs = mRasterLayer->crs();
|
||||||
|
|
||||||
// Handles window modality raising canvas
|
// Handles window modality raising canvas
|
||||||
|
@ -46,6 +46,7 @@ class QgsMapLayerConfigWidgetFactory;
|
|||||||
class QgsMapLayerConfigWidget;
|
class QgsMapLayerConfigWidget;
|
||||||
class QgsPropertyOverrideButton;
|
class QgsPropertyOverrideButton;
|
||||||
class QgsRasterTransparencyWidget;
|
class QgsRasterTransparencyWidget;
|
||||||
|
class QgsRasterAttributeTableWidget;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -296,5 +297,7 @@ class GUI_EXPORT QgsRasterLayerProperties : public QgsOptionsDialogBase, private
|
|||||||
friend class QgsAppScreenShots;
|
friend class QgsAppScreenShots;
|
||||||
|
|
||||||
QgsCoordinateReferenceSystem mBackupCrs;
|
QgsCoordinateReferenceSystem mBackupCrs;
|
||||||
|
|
||||||
|
QgsRasterAttributeTableWidget *mRasterAttributeTableWidget = nullptr;
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
@ -93,7 +93,7 @@
|
|||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
<property name="currentRow">
|
<property name="currentRow">
|
||||||
<number>0</number>
|
<number>-1</number>
|
||||||
</property>
|
</property>
|
||||||
<item>
|
<item>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
@ -203,6 +203,18 @@
|
|||||||
<normaloff>:/images/themes/default/legend.svg</normaloff>:/images/themes/default/legend.svg</iconset>
|
<normaloff>:/images/themes/default/legend.svg</normaloff>:/images/themes/default/legend.svg</iconset>
|
||||||
</property>
|
</property>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Attribute Tables</string>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Manage Raster Attribute Tables</string>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="../../images/images.qrc">
|
||||||
|
<normaloff>:/images/themes/default/propertyicons/attributes.svg</normaloff>:/images/themes/default/propertyicons/attributes.svg</iconset>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>QGIS Server</string>
|
<string>QGIS Server</string>
|
||||||
@ -254,7 +266,7 @@
|
|||||||
</sizepolicy>
|
</sizepolicy>
|
||||||
</property>
|
</property>
|
||||||
<property name="currentIndex">
|
<property name="currentIndex">
|
||||||
<number>0</number>
|
<number>10</number>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QWidget" name="mOptsPage_Information">
|
<widget class="QWidget" name="mOptsPage_Information">
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_20">
|
<layout class="QVBoxLayout" name="verticalLayout_20">
|
||||||
@ -299,8 +311,8 @@
|
|||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>253</width>
|
<width>648</width>
|
||||||
<height>392</height>
|
<height>690</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_7">
|
<layout class="QVBoxLayout" name="verticalLayout_7">
|
||||||
@ -1438,6 +1450,55 @@ p, li { white-space: pre-wrap; }
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
|
<widget class="QWidget" name="mOptsPage_RasterAttributeTable">
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_rat">
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QWidget" name="mNoRasterAttributeTableWidget" native="true">
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="mRasterAttributeTableLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>There are no raster attribute tables associated with this data source.
|
||||||
|
|
||||||
|
If the current symbology can be converted to an attribute table you can create a new attribute table using the context menu available in the layer tree.
|
||||||
|
|
||||||
|
From the layer tree context menu it you can also import an existing raster attribute table from a VAT.DBF file and associate it with a raster band.</string>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer_2">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
<widget class="QWidget" name="mOptsPage_Server">
|
<widget class="QWidget" name="mOptsPage_Server">
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_8">
|
<layout class="QVBoxLayout" name="verticalLayout_8">
|
||||||
<property name="leftMargin">
|
<property name="leftMargin">
|
||||||
@ -1466,7 +1527,7 @@ p, li { white-space: pre-wrap; }
|
|||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>263</width>
|
<width>263</width>
|
||||||
<height>566</height>
|
<height>600</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QGridLayout" name="gridLayout_12">
|
<layout class="QGridLayout" name="gridLayout_12">
|
||||||
@ -1994,6 +2055,8 @@ p, li { white-space: pre-wrap; }
|
|||||||
</tabstops>
|
</tabstops>
|
||||||
<resources>
|
<resources>
|
||||||
<include location="../../images/images.qrc"/>
|
<include location="../../images/images.qrc"/>
|
||||||
|
<include location="../../images/images.qrc"/>
|
||||||
|
<include location="../../images/images.qrc"/>
|
||||||
</resources>
|
</resources>
|
||||||
<connections>
|
<connections>
|
||||||
<connection>
|
<connection>
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="mCreateInfoLabel">
|
<widget class="QLabel" name="mCreateInfoLabel">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string><html><head/><body><p>Create a new Raster Attribute Table (RAT) from the current symbology.</p></body></html></string>
|
<string><html><head/><body><p>Create a new Raster Attribute Table from the current symbology.</p></body></html></string>
|
||||||
</property>
|
</property>
|
||||||
<property name="wordWrap">
|
<property name="wordWrap">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
|
@ -111,6 +111,9 @@
|
|||||||
<property name="text">
|
<property name="text">
|
||||||
<string>After</string>
|
<string>After</string>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="0">
|
<item row="3" column="0">
|
||||||
@ -119,7 +122,7 @@
|
|||||||
<string>Before</string>
|
<string>Before</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="checked">
|
<property name="checked">
|
||||||
<bool>true</bool>
|
<bool>false</bool>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user