mirror of
https://github.com/qgis/QGIS.git
synced 2025-10-15 00:02:52 -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
|
||||
%End
|
||||
|
||||
int attributeTableCount( );
|
||||
int attributeTableCount( ) const;
|
||||
%Docstring
|
||||
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;
|
||||
%Docstring
|
||||
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
|
||||
|
||||
};
|
||||
|
@ -4380,6 +4380,12 @@ void QgisApp::setupConnections()
|
||||
|
||||
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( mActionPreviewModeOff, &QAction::triggered, this, &QgisApp::disablePreviewMode );
|
||||
connect( mActionPreviewModeMono, &QAction::triggered, this, &QgisApp::activateMonoPreview );
|
||||
@ -5600,7 +5606,7 @@ void QgisApp::fileExit()
|
||||
}
|
||||
|
||||
QgsCanvasRefreshBlocker refreshBlocker;
|
||||
if ( checkUnsavedLayerEdits() && checkMemoryLayers() && saveDirty() && checkExitBlockers() )
|
||||
if ( checkUnsavedLayerEdits() && checkMemoryLayers() && saveDirty() && checkExitBlockers() && checkUnsavedRasterAttributeTableEdits() )
|
||||
{
|
||||
closeProject();
|
||||
userProfileManager()->setDefaultFromActive();
|
||||
@ -5638,7 +5644,7 @@ bool QgisApp::fileNew( bool promptToSaveFlag, bool forceBlank )
|
||||
|
||||
if ( promptToSaveFlag )
|
||||
{
|
||||
if ( !checkUnsavedLayerEdits() || !checkMemoryLayers() || !saveDirty() )
|
||||
if ( !checkUnsavedLayerEdits() || !checkMemoryLayers() || !saveDirty() || !checkUnsavedRasterAttributeTableEdits() )
|
||||
{
|
||||
return false; //cancel pressed
|
||||
}
|
||||
@ -5710,7 +5716,7 @@ bool QgisApp::fileNewFromTemplate( const QString &fileName )
|
||||
if ( checkTasksDependOnProject() )
|
||||
return false;
|
||||
|
||||
if ( !checkUnsavedLayerEdits() || !checkMemoryLayers() || !saveDirty() )
|
||||
if ( !checkUnsavedLayerEdits() || !checkMemoryLayers() || !saveDirty() || !checkUnsavedRasterAttributeTableEdits() )
|
||||
{
|
||||
return false; //cancel pressed
|
||||
}
|
||||
@ -6206,7 +6212,7 @@ void QgisApp::fileOpen()
|
||||
return;
|
||||
|
||||
// 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
|
||||
QgsSettings settings;
|
||||
@ -6262,7 +6268,7 @@ void QgisApp::fileRevert()
|
||||
QMessageBox::Yes | QMessageBox::No, QMessageBox::No ) == QMessageBox::No )
|
||||
return;
|
||||
|
||||
if ( !checkUnsavedLayerEdits() || !checkMemoryLayers() )
|
||||
if ( !checkUnsavedLayerEdits() || !checkMemoryLayers() || ! checkUnsavedRasterAttributeTableEdits() )
|
||||
return;
|
||||
|
||||
// re-open the current project
|
||||
@ -6760,7 +6766,7 @@ void QgisApp::openProject( QAction *action )
|
||||
if ( checkTasksDependOnProject() )
|
||||
return;
|
||||
|
||||
if ( checkUnsavedLayerEdits() && checkMemoryLayers() && saveDirty() )
|
||||
if ( checkUnsavedLayerEdits() && checkMemoryLayers() && saveDirty() && checkUnsavedRasterAttributeTableEdits() )
|
||||
addProject( project );
|
||||
}
|
||||
|
||||
@ -6805,7 +6811,7 @@ void QgisApp::openProject( const QString &fileName )
|
||||
return;
|
||||
|
||||
// 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
|
||||
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()
|
||||
{
|
||||
if ( !mLayerTreeView )
|
||||
@ -12053,7 +12083,7 @@ void QgisApp::createRasterAttributeTable()
|
||||
if ( ! rat )
|
||||
{
|
||||
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 );
|
||||
return;
|
||||
}
|
||||
@ -13381,6 +13411,94 @@ bool QgisApp::checkUnsavedLayerEdits()
|
||||
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()
|
||||
{
|
||||
if ( !QgsSettings().value( QStringLiteral( "askToSaveMemoryLayers" ), true, QgsSettings::App ).toBool() )
|
||||
@ -16175,6 +16293,8 @@ void QgisApp::showLayerProperties( QgsMapLayer *mapLayer, const QString &page )
|
||||
{
|
||||
rasterLayerPropertiesDialog->deleteLater();
|
||||
} );
|
||||
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -1106,7 +1106,7 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
|
||||
void legendLayerStretchUsingCurrentExtent();
|
||||
|
||||
/**
|
||||
* Open the RasterAttributeTable for the raster layer.
|
||||
* Open the Raster Attribute Table for the raster layer.
|
||||
* Only works on raster layers.
|
||||
*
|
||||
* \since QGIS 3.30
|
||||
@ -1114,7 +1114,7 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
|
||||
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.
|
||||
*
|
||||
* Only works on raster layers.
|
||||
@ -1123,6 +1123,15 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
|
||||
*/
|
||||
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.
|
||||
bool event( QEvent *event ) override;
|
||||
|
||||
@ -1339,6 +1348,19 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
|
||||
*/
|
||||
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
|
||||
* shows a warning to users that their contents will be lost on
|
||||
|
@ -229,8 +229,10 @@ QMenu *QgsAppLayerTreeViewMenuProvider::createContextMenu()
|
||||
}
|
||||
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)
|
||||
|
@ -3212,25 +3212,10 @@ bool QgsGdalProvider::writeNativeAttributeTable( QString *errorMessage ) //#spel
|
||||
}
|
||||
|
||||
// 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" ) );
|
||||
GDALClose( mGdalDataset );
|
||||
|
||||
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;
|
||||
}
|
||||
setEditable( true );
|
||||
wasReopenedReadWrite = true;
|
||||
}
|
||||
|
||||
@ -3327,17 +3312,7 @@ bool QgsGdalProvider::writeNativeAttributeTable( QString *errorMessage ) //#spel
|
||||
if ( wasReopenedReadWrite )
|
||||
{
|
||||
QgsDebugMsg( QStringLiteral( "re-opening the dataset in read-only mode" ) );
|
||||
GDALClose( mGdalDataset );
|
||||
|
||||
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;
|
||||
}
|
||||
setEditable( false );
|
||||
}
|
||||
|
||||
return success;
|
||||
|
@ -479,6 +479,7 @@ bool QgsRasterAttributeTable::removeRow( int position, QString *errorMessage )
|
||||
return false;
|
||||
}
|
||||
mData.removeAt( position );
|
||||
setDirty( true );
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -723,8 +724,12 @@ bool QgsRasterAttributeTable::setValue( const int row, const int column, const Q
|
||||
}
|
||||
|
||||
const QVariant oldVal = mData[ row ][ column ];
|
||||
mData[ row ][ column ] = newVal;
|
||||
setDirty( newVal != oldVal );
|
||||
|
||||
if ( newVal != oldVal )
|
||||
{
|
||||
mData[ row ][ column ] = newVal;
|
||||
setDirty( true );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -1508,7 +1513,7 @@ QgsRasterRenderer *QgsRasterAttributeTable::createRenderer( QgsRasterDataProvide
|
||||
pseudoColorRenderer->setClassificationMin( minValue() );
|
||||
pseudoColorRenderer->setClassificationMax( maxValue() );
|
||||
// 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()->setMinimumValue( minValue() );
|
||||
// Set labels
|
||||
@ -1516,10 +1521,30 @@ QgsRasterRenderer *QgsRasterAttributeTable::createRenderer( QgsRasterDataProvide
|
||||
{
|
||||
shaderFunction->setMinimumValue( minValue() );
|
||||
shaderFunction->setMaximumValue( maxValue() );
|
||||
const QList<QgsColorRampShader::ColorRampItem> itemList { shaderFunction->colorRampItemList() };
|
||||
const bool labelsAreUsable { labels.count() == itemList.count() };
|
||||
if ( ! itemList.isEmpty() )
|
||||
const bool labelsAreUsable { ramp->count() > 2 && labels.count() == ramp->count() - 1 };
|
||||
|
||||
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;
|
||||
// Deduplicate entries, this is necessary when the ramp for athematic
|
||||
// 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 );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
shaderFunction->setColorRampItemList( deduplicatedDitemList );
|
||||
}
|
||||
}
|
||||
|
@ -257,7 +257,7 @@ QgsRasterAttributeTable *QgsRasterLayer::attributeTable( int bandNoInt ) const
|
||||
return mDataProvider->attributeTable( bandNoInt );
|
||||
}
|
||||
|
||||
int QgsRasterLayer::attributeTableCount()
|
||||
int QgsRasterLayer::attributeTableCount() const
|
||||
{
|
||||
if ( !mDataProvider )
|
||||
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.
|
||||
* \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.
|
||||
|
@ -16,11 +16,9 @@
|
||||
#include "qgsrasterattributetabledialog.h"
|
||||
#include "qgsrasterlayer.h"
|
||||
|
||||
#include <QMessageBox>
|
||||
|
||||
QgsRasterAttributeTableDialog::QgsRasterAttributeTableDialog( QgsRasterLayer *rasterLayer, int bandNumber, QWidget *parent )
|
||||
: QDialog( parent )
|
||||
, mRasterLayer( rasterLayer )
|
||||
{
|
||||
Q_ASSERT( rasterLayer );
|
||||
setupUi( this );
|
||||
@ -28,40 +26,15 @@ QgsRasterAttributeTableDialog::QgsRasterAttributeTableDialog( QgsRasterLayer *ra
|
||||
setWindowTitle( tr( "Raster Attribute Table for %1" ).arg( rasterLayer->name() ) );
|
||||
|
||||
connect( mButtonBox, &QDialogButtonBox::rejected, this, &QgsRasterAttributeTableDialog::reject );
|
||||
connect( rasterLayer, &QgsRasterLayer::dataSourceChanged, this, &QgsRasterAttributeTableDialog::reject );
|
||||
connect( rasterLayer, &QgsRasterLayer::willBeDeleted, this, &QgsRasterAttributeTableDialog::reject );
|
||||
|
||||
}
|
||||
|
||||
void QgsRasterAttributeTableDialog::reject()
|
||||
{
|
||||
QList<int> dirtyBands;
|
||||
for ( int bandNo = 1; bandNo <= mRasterLayer->bandCount(); ++bandNo )
|
||||
if ( mRatWidget->setEditable( false ) )
|
||||
{
|
||||
if ( QgsRasterAttributeTable *rat = mRasterLayer->attributeTable( bandNo ) )
|
||||
{
|
||||
if ( rat->isDirty() )
|
||||
{
|
||||
dirtyBands.push_back( bandNo );
|
||||
}
|
||||
}
|
||||
QDialog::reject();
|
||||
}
|
||||
|
||||
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
|
||||
* \brief The QgsRasterAttributeTableDialog class embeds an attribute table widget
|
||||
* and contains logic to handle confirmation when closing with unsaved changes.
|
||||
* \brief The QgsRasterAttributeTableDialog class embeds an attribute table widget.
|
||||
* \since QGIS 3.30
|
||||
*/
|
||||
class GUI_EXPORT QgsRasterAttributeTableDialog: public QDialog, private Ui::QRasterAttributeTableDialogBase
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
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;
|
||||
|
||||
private:
|
||||
|
||||
QgsRasterLayer *mRasterLayer = nullptr;
|
||||
};
|
||||
|
||||
#endif // QGSRASTERATTRIBUTETABLEDIALOG_H
|
||||
|
@ -90,14 +90,14 @@ void QgsRasterAttributeTableWidget::setRasterLayer( QgsRasterLayer *rasterLayer,
|
||||
|
||||
bool QgsRasterAttributeTableWidget::isDirty() const
|
||||
{
|
||||
return mAttributeTable && mAttributeTable->isDirty();
|
||||
return mAttributeTableBuffer && mAttributeTableBuffer->isDirty();
|
||||
}
|
||||
|
||||
void QgsRasterAttributeTableWidget::init( int bandNumber )
|
||||
{
|
||||
|
||||
disconnect( mRasterBandsComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsRasterAttributeTableWidget::bandChanged );
|
||||
mAttributeTable = nullptr;
|
||||
mAttributeTableBuffer = nullptr;
|
||||
mCurrentBand = 0;
|
||||
mRasterBandsComboBox->clear();
|
||||
|
||||
@ -120,22 +120,22 @@ void QgsRasterAttributeTableWidget::init( int bandNumber )
|
||||
if ( availableRats.contains( bandNumber ) )
|
||||
{
|
||||
mCurrentBand = bandNumber;
|
||||
mAttributeTable = mRasterLayer->attributeTable( mCurrentBand );
|
||||
mRasterBandsComboBox->setCurrentIndex( availableRats.indexOf( mCurrentBand ) );
|
||||
}
|
||||
else if ( ! availableRats.isEmpty() )
|
||||
{
|
||||
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 );
|
||||
|
||||
connect( mModel.get(), &QgsRasterAttributeTableModel::dataChanged, this, [ = ]( const QModelIndex &, const QModelIndex &, const QVector<int> & )
|
||||
{
|
||||
updateButtons();
|
||||
@ -166,14 +166,15 @@ void QgsRasterAttributeTableWidget::init( int bandNumber )
|
||||
|
||||
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 );
|
||||
mActionRemoveColumn->setEnabled( enableEditingButtons );
|
||||
mActionAddRow->setEnabled( enableEditingButtons );
|
||||
mActionRemoveRow->setEnabled( enableEditingButtons );
|
||||
mActionSaveChanges->setEnabled( mAttributeTable && mAttributeTable->isDirty() );
|
||||
mClassifyButton->setEnabled( mAttributeTable && mRasterLayer );
|
||||
mClassifyComboBox->setEnabled( mAttributeTable && mRasterLayer );
|
||||
mActionSaveChanges->setEnabled( mAttributeTableBuffer && mAttributeTableBuffer->isDirty() );
|
||||
mClassifyButton->setEnabled( mAttributeTableBuffer && mRasterLayer );
|
||||
mClassifyComboBox->setEnabled( mAttributeTableBuffer && mRasterLayer );
|
||||
}
|
||||
|
||||
void QgsRasterAttributeTableWidget::setMessageBar( QgsMessageBar *bar )
|
||||
@ -181,83 +182,109 @@ void QgsRasterAttributeTableWidget::setMessageBar( QgsMessageBar *bar )
|
||||
mMessageBar = bar;
|
||||
}
|
||||
|
||||
void QgsRasterAttributeTableWidget::setEditable( bool editable )
|
||||
bool QgsRasterAttributeTableWidget::setEditable( bool editable )
|
||||
{
|
||||
bool isDirty { false };
|
||||
|
||||
for ( int bandNo = 1; bandNo <= mRasterLayer->bandCount(); ++bandNo )
|
||||
const bool isDirty { mAttributeTableBuffer &&mAttributeTableBuffer->isDirty() &&mCurrentBand > 0 && mRasterLayer->attributeTable( mCurrentBand ) };
|
||||
bool retVal { true };
|
||||
// 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;
|
||||
break;
|
||||
case QMessageBox::Button::Cancel:
|
||||
{
|
||||
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();
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
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 ) };
|
||||
if ( attributeTable && attributeTable->isDirty() )
|
||||
QgsDebugMsg( QStringLiteral( "Error saving RAT: RAT for band %1 is unexpectedly gone!" ).arg( mCurrentBand ) );
|
||||
}
|
||||
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;
|
||||
QString newPath { attributeTable->filePath() };
|
||||
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 )
|
||||
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)" ) );
|
||||
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 )
|
||||
{
|
||||
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()
|
||||
{
|
||||
|
||||
if ( ! mAttributeTable )
|
||||
if ( ! mAttributeTableBuffer )
|
||||
{
|
||||
notify( tr( "Classification Error" ), tr( "The raster attribute table is not set." ), Qgis::MessageLevel::Critical );
|
||||
return;
|
||||
@ -279,13 +306,22 @@ void QgsRasterAttributeTableWidget::classify()
|
||||
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->triggerRepaint( );
|
||||
emit rendererChanged();
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -296,9 +332,9 @@ void QgsRasterAttributeTableWidget::classify()
|
||||
|
||||
void QgsRasterAttributeTableWidget::addColumn()
|
||||
{
|
||||
if ( mAttributeTable )
|
||||
if ( mAttributeTableBuffer )
|
||||
{
|
||||
QgsRasterAttributeTableAddColumnDialog dlg { mAttributeTable };
|
||||
QgsRasterAttributeTableAddColumnDialog dlg { mAttributeTableBuffer.get() };
|
||||
if ( dlg.exec() == QDialog::Accepted )
|
||||
{
|
||||
QString errorMessage;
|
||||
@ -330,7 +366,7 @@ void QgsRasterAttributeTableWidget::addColumn()
|
||||
void QgsRasterAttributeTableWidget::removeColumn()
|
||||
{
|
||||
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 )
|
||||
{
|
||||
@ -345,7 +381,7 @@ void QgsRasterAttributeTableWidget::removeColumn()
|
||||
|
||||
void QgsRasterAttributeTableWidget::addRow()
|
||||
{
|
||||
if ( mAttributeTable )
|
||||
if ( mAttributeTableBuffer )
|
||||
{
|
||||
// Default to append
|
||||
int position { mModel->rowCount( QModelIndex() ) };
|
||||
@ -372,7 +408,7 @@ void QgsRasterAttributeTableWidget::addRow()
|
||||
|
||||
QVariantList rowData;
|
||||
|
||||
QList<QgsRasterAttributeTable::Field> fields { mAttributeTable->fields() };
|
||||
QList<QgsRasterAttributeTable::Field> fields { mAttributeTableBuffer->fields() };
|
||||
for ( const QgsRasterAttributeTable::Field &field : std::as_const( fields ) )
|
||||
{
|
||||
rowData.push_back( QVariant( field.type ) );
|
||||
@ -393,7 +429,7 @@ void QgsRasterAttributeTableWidget::addRow()
|
||||
|
||||
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 )
|
||||
{
|
||||
@ -409,8 +445,13 @@ void QgsRasterAttributeTableWidget::removeRow()
|
||||
void QgsRasterAttributeTableWidget::bandChanged( const int index )
|
||||
{
|
||||
const QVariant itemData = mRasterBandsComboBox->itemData( index );
|
||||
|
||||
if ( itemData.isValid() )
|
||||
{
|
||||
if ( mEditable )
|
||||
{
|
||||
setEditable( false );
|
||||
}
|
||||
init( itemData.toInt( ) );
|
||||
}
|
||||
}
|
||||
@ -449,9 +490,9 @@ void QgsRasterAttributeTableWidget::notify( const QString &title, const QString
|
||||
void QgsRasterAttributeTableWidget::setDelegates()
|
||||
{
|
||||
mClassifyComboBox->clear();
|
||||
if ( mAttributeTable )
|
||||
if ( mAttributeTableBuffer )
|
||||
{
|
||||
const QList<QgsRasterAttributeTable::Field> tableFields { mAttributeTable->fields() };
|
||||
const QList<QgsRasterAttributeTable::Field> tableFields { mAttributeTableBuffer->fields() };
|
||||
int fieldIdx { 0 };
|
||||
const QHash<Qgis::RasterAttributeTableFieldUsage, QgsRasterAttributeTable::UsageInformation> usageInfo { QgsRasterAttributeTable::usageInformation() };
|
||||
for ( const QgsRasterAttributeTable::Field &f : std::as_const( tableFields ) )
|
||||
@ -465,26 +506,26 @@ void QgsRasterAttributeTableWidget::setDelegates()
|
||||
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
|
||||
{
|
||||
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
|
||||
{
|
||||
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;
|
||||
|
||||
/**
|
||||
* 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:
|
||||
|
||||
QgsRasterLayer *mRasterLayer = nullptr;
|
||||
QgsRasterAttributeTable *mAttributeTable = nullptr;
|
||||
std::unique_ptr<QgsRasterAttributeTable> mAttributeTableBuffer;
|
||||
// Default to invalid (bands are 1-indexed)
|
||||
int mCurrentBand = 0;
|
||||
bool mEditable = false;
|
||||
@ -144,28 +182,6 @@ class GUI_EXPORT QgsRasterAttributeTableWidget : public QWidget, private Ui::Qgs
|
||||
|
||||
void init( int bandNumber = 0 );
|
||||
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
|
||||
|
@ -68,6 +68,7 @@
|
||||
#include "qgsdoublevalidator.h"
|
||||
#include "qgsmaplayerconfigwidgetfactory.h"
|
||||
#include "qgsprojectutils.h"
|
||||
#include "qgsrasterattributetablewidget.h"
|
||||
|
||||
#include "qgsrasterlayertemporalpropertieswidget.h"
|
||||
#include "qgsprojecttimesettings.h"
|
||||
@ -234,6 +235,21 @@ QgsRasterLayerProperties::QgsRasterLayerProperties( QgsMapLayer *lyr, QgsMapCanv
|
||||
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();
|
||||
|
||||
// Handles window modality raising canvas
|
||||
|
@ -46,6 +46,7 @@ class QgsMapLayerConfigWidgetFactory;
|
||||
class QgsMapLayerConfigWidget;
|
||||
class QgsPropertyOverrideButton;
|
||||
class QgsRasterTransparencyWidget;
|
||||
class QgsRasterAttributeTableWidget;
|
||||
|
||||
|
||||
/**
|
||||
@ -296,5 +297,7 @@ class GUI_EXPORT QgsRasterLayerProperties : public QgsOptionsDialogBase, private
|
||||
friend class QgsAppScreenShots;
|
||||
|
||||
QgsCoordinateReferenceSystem mBackupCrs;
|
||||
|
||||
QgsRasterAttributeTableWidget *mRasterAttributeTableWidget = nullptr;
|
||||
};
|
||||
#endif
|
||||
|
@ -93,7 +93,7 @@
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="currentRow">
|
||||
<number>0</number>
|
||||
<number>-1</number>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
@ -203,6 +203,18 @@
|
||||
<normaloff>:/images/themes/default/legend.svg</normaloff>:/images/themes/default/legend.svg</iconset>
|
||||
</property>
|
||||
</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>
|
||||
<property name="text">
|
||||
<string>QGIS Server</string>
|
||||
@ -254,7 +266,7 @@
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
<number>10</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="mOptsPage_Information">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_20">
|
||||
@ -299,8 +311,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>253</width>
|
||||
<height>392</height>
|
||||
<width>648</width>
|
||||
<height>690</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_7">
|
||||
@ -1438,6 +1450,55 @@ p, li { white-space: pre-wrap; }
|
||||
</item>
|
||||
</layout>
|
||||
</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">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_8">
|
||||
<property name="leftMargin">
|
||||
@ -1466,7 +1527,7 @@ p, li { white-space: pre-wrap; }
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>263</width>
|
||||
<height>566</height>
|
||||
<height>600</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_12">
|
||||
@ -1994,6 +2055,8 @@ p, li { white-space: pre-wrap; }
|
||||
</tabstops>
|
||||
<resources>
|
||||
<include location="../../images/images.qrc"/>
|
||||
<include location="../../images/images.qrc"/>
|
||||
<include location="../../images/images.qrc"/>
|
||||
</resources>
|
||||
<connections>
|
||||
<connection>
|
||||
|
@ -17,7 +17,7 @@
|
||||
<item>
|
||||
<widget class="QLabel" name="mCreateInfoLabel">
|
||||
<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 name="wordWrap">
|
||||
<bool>true</bool>
|
||||
|
@ -111,6 +111,9 @@
|
||||
<property name="text">
|
||||
<string>After</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
@ -119,7 +122,7 @@
|
||||
<string>Before</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
Loading…
x
Reference in New Issue
Block a user