Allow adding/editing 3d symbols in style manager dialog

This commit is contained in:
Nyall Dawson 2020-07-22 15:11:16 +10:00
parent ae3f2c319c
commit d8e44539f1
6 changed files with 277 additions and 7 deletions

View File

@ -40,6 +40,40 @@ Caller takes ownership of the returned symbol.
%End
};
class Qgs3DSymbolDialog : QDialog
{
%Docstring
A dialog for configuring a 3D symbol.
.. versionadded:: 3.16
%End
%TypeHeaderCode
#include "qgs3dsymbolwidget.h"
%End
public:
Qgs3DSymbolDialog( const QgsAbstract3DSymbol *symbol, QWidget *parent /TransferThis/ = 0 );
%Docstring
Constructor for Qgs3DSymbolDialog, initially showing the specified ``symbol``.
%End
QgsAbstract3DSymbol *symbol() const /Factory/;
%Docstring
Returns a new instance of the symbol defined by the dialog.
Caller takes ownership of the returned symbol.
%End
QDialogButtonBox *buttonBox() const;
%Docstring
Returns a reference to the dialog's button box.
%End
};
/************************************************************************
* This file has been generated automatically from *
* *

View File

@ -14,7 +14,47 @@
***************************************************************************/
#include "qgs3dsymbolwidget.h"
#include "qgsapplication.h"
#include "qgs3dsymbolregistry.h"
#include "qgsabstract3dsymbol.h"
#include "qgsgui.h"
#include <QVBoxLayout>
#include <QDialogButtonBox>
Qgs3DSymbolWidget::Qgs3DSymbolWidget( QWidget *parent )
: QWidget( parent )
{}
Qgs3DSymbolDialog::Qgs3DSymbolDialog( const QgsAbstract3DSymbol *symbol, QWidget *parent )
: QDialog( parent )
{
Q_ASSERT( symbol );
QgsGui::enableAutoGeometryRestore( this );
QVBoxLayout *vLayout = new QVBoxLayout();
if ( Qgs3DSymbolAbstractMetadata *metadata = QgsApplication::symbol3DRegistry()->symbolMetadata( symbol->type() ) )
{
mWidget = metadata->createSymbolWidget( nullptr );
vLayout->addWidget( mWidget );
mWidget->setSymbol( symbol, nullptr );
}
mButtonBox = new QDialogButtonBox( QDialogButtonBox::Cancel | QDialogButtonBox::Ok, Qt::Horizontal );
connect( mButtonBox, &QDialogButtonBox::accepted, this, &QDialog::accept );
connect( mButtonBox, &QDialogButtonBox::rejected, this, &QDialog::reject );
vLayout->addWidget( mButtonBox );
setLayout( vLayout );
setWindowTitle( tr( "3D Symbol" ) );
}
QgsAbstract3DSymbol *Qgs3DSymbolDialog::symbol() const
{
return mWidget ? mWidget->symbol() : nullptr;
}
QDialogButtonBox *Qgs3DSymbolDialog::buttonBox() const
{
return mButtonBox;
}

View File

@ -17,11 +17,13 @@
#define QGS3DSYMBOLWIDGET_H
#include <QWidget>
#include <QDialog>
#include "qgis_gui.h"
#include "qgis_sip.h"
class QgsAbstract3DSymbol;
class QgsVectorLayer;
class QDialogButtonBox;
/**
* \ingroup gui
@ -53,4 +55,41 @@ class GUI_EXPORT Qgs3DSymbolWidget : public QWidget
virtual QgsAbstract3DSymbol *symbol() = 0 SIP_FACTORY;
};
/**
* \ingroup gui
* \brief A dialog for configuring a 3D symbol.
* \since QGIS 3.16
*/
class GUI_EXPORT Qgs3DSymbolDialog : public QDialog
{
Q_OBJECT
public:
/**
* Constructor for Qgs3DSymbolDialog, initially showing the specified \a symbol.
*/
Qgs3DSymbolDialog( const QgsAbstract3DSymbol *symbol, QWidget *parent SIP_TRANSFERTHIS = nullptr );
/**
* Returns a new instance of the symbol defined by the dialog.
*
* Caller takes ownership of the returned symbol.
*/
QgsAbstract3DSymbol *symbol() const SIP_FACTORY;
/**
* Returns a reference to the dialog's button box.
*/
QDialogButtonBox *buttonBox() const;
private:
Qgs3DSymbolWidget *mWidget = nullptr;
QDialogButtonBox *mButtonBox = nullptr;
};
#endif // QGS3DSYMBOLWIDGET_H

View File

@ -35,6 +35,8 @@
#include "qgslabelinggui.h"
#include "qgslegendpatchshapewidget.h"
#include "qgsabstract3dsymbol.h"
#include "qgs3dsymbolregistry.h"
#include "qgs3dsymbolwidget.h"
#include <QAction>
#include <QFile>
#include <QFileDialog>
@ -332,6 +334,7 @@ QgsStyleManagerDialog::QgsStyleManagerDialog( QgsStyle *style, QWidget *parent,
mMenuBtnAddItemColorRamp = new QMenu( this );
mMenuBtnAddItemLabelSettings = new QMenu( this );
mMenuBtnAddItemLegendPatchShape = new QMenu( this );
mMenuBtnAddItemSymbol3D = new QMenu( this );
QAction *item = new QAction( QgsApplication::getThemeIcon( QStringLiteral( "mIconPointLayer.svg" ) ), tr( "Marker…" ), this );
connect( item, &QAction::triggered, this, [ = ]( bool ) { addSymbol( QgsSymbol::Marker ); } );
@ -382,6 +385,20 @@ QgsStyleManagerDialog::QgsStyleManagerDialog( QgsStyle *style, QWidget *parent,
mMenuBtnAddItemAll->addAction( item );
mMenuBtnAddItemLegendPatchShape->addAction( item );
mMenuBtnAddItemAll->addSeparator();
item = new QAction( QgsApplication::getThemeIcon( QStringLiteral( "3d.svg" ) ), tr( "3D Point Symbol…" ), this );
connect( item, &QAction::triggered, this, [ = ]( bool ) { addSymbol3D( QStringLiteral( "point" ) ); } );
mMenuBtnAddItemAll->addAction( item );
mMenuBtnAddItemSymbol3D->addAction( item );
item = new QAction( QgsApplication::getThemeIcon( QStringLiteral( "3d.svg" ) ), tr( "3D Line Symbol…" ), this );
connect( item, &QAction::triggered, this, [ = ]( bool ) { addSymbol3D( QStringLiteral( "line" ) ); } );
mMenuBtnAddItemAll->addAction( item );
mMenuBtnAddItemSymbol3D->addAction( item );
item = new QAction( QgsApplication::getThemeIcon( QStringLiteral( "3d.svg" ) ), tr( "3D Polygon Symbol…" ), this );
connect( item, &QAction::triggered, this, [ = ]( bool ) { addSymbol3D( QStringLiteral( "polygon" ) ); } );
mMenuBtnAddItemAll->addAction( item );
mMenuBtnAddItemSymbol3D->addAction( item );
connect( mMenuBtnAddItemColorRamp, &QMenu::triggered,
this, static_cast<bool ( QgsStyleManagerDialog::* )( QAction * )>( &QgsStyleManagerDialog::addColorRamp ) );
}
@ -501,23 +518,30 @@ void QgsStyleManagerDialog::populateTypes()
void QgsStyleManagerDialog::tabItemType_currentChanged( int )
{
// when in Color Ramp tab, add menu to add item button and hide "Export symbols as PNG/SVG"
const bool isSymbol = currentItemType() != 3 && currentItemType() != 4 && currentItemType() != 5 && currentItemType() != 6;
const bool isSymbol = currentItemType() != 3 && currentItemType() != 4 && currentItemType() != 5 && currentItemType() != 6 && currentItemType() != 7;
const bool isColorRamp = currentItemType() == 3;
const bool isTextFormat = currentItemType() == 4;
const bool isLabelSettings = currentItemType() == 5;
const bool isLegendPatchShape = currentItemType() == 6;
const bool isSymbol3D = currentItemType() == 7;
searchBox->setPlaceholderText( isSymbol ? tr( "Filter symbols…" ) :
isColorRamp ? tr( "Filter color ramps…" ) :
isTextFormat ? tr( "Filter text symbols…" ) :
isLabelSettings ? tr( "Filter label settings…" ) : tr( "Filter legend patch shapes…" ) );
isLabelSettings ? tr( "Filter label settings…" ) :
isLegendPatchShape ? tr( "Filter legend patch shapes…" ) : tr( "Filter 3D symbols…" ) );
if ( !mReadOnly && isColorRamp ) // color ramp tab
{
btnAddItem->setMenu( mMenuBtnAddItemColorRamp );
}
if ( !mReadOnly && !isSymbol && !isColorRamp && !isTextFormat && !isLabelSettings ) // legend patch shape tab
if ( !mReadOnly && isLegendPatchShape ) // legend patch shape tab
{
btnAddItem->setMenu( mMenuBtnAddItemLegendPatchShape );
}
else if ( !mReadOnly && isSymbol3D ) // legend patch shape tab
{
btnAddItem->setMenu( mMenuBtnAddItemSymbol3D );
}
else if ( !mReadOnly && isLabelSettings ) // label settings tab
{
btnAddItem->setMenu( mMenuBtnAddItemLabelSettings );
@ -538,7 +562,7 @@ void QgsStyleManagerDialog::tabItemType_currentChanged( int )
actnExportAsPNG->setVisible( isSymbol );
actnExportAsSVG->setVisible( isSymbol );
mModel->setEntityFilter( isSymbol ? QgsStyle::SymbolEntity : ( isColorRamp ? QgsStyle::ColorrampEntity : isTextFormat ? QgsStyle::TextFormatEntity : isLabelSettings ? QgsStyle::LabelSettingsEntity : QgsStyle::LegendPatchShapeEntity ) );
mModel->setEntityFilter( isSymbol ? QgsStyle::SymbolEntity : ( isColorRamp ? QgsStyle::ColorrampEntity : isTextFormat ? QgsStyle::TextFormatEntity : isLabelSettings ? QgsStyle::LabelSettingsEntity : isLegendPatchShape ? QgsStyle::LegendPatchShapeEntity : QgsStyle::Symbol3DEntity ) );
mModel->setEntityFilterEnabled( !allTypesSelected() );
mModel->setSymbolTypeFilterEnabled( isSymbol && !allTypesSelected() );
if ( isSymbol && !allTypesSelected() )
@ -701,6 +725,8 @@ int QgsStyleManagerDialog::selectedItemType()
return 5;
else if ( entity == QgsStyle::LegendPatchShapeEntity )
return 6;
else if ( entity == QgsStyle::Symbol3DEntity )
return 7;
return mModel->data( index, QgsStyleModel::SymbolTypeRole ).toInt();
}
@ -742,6 +768,7 @@ int QgsStyleManagerDialog::copyItems( const QList<QgsStyleManagerDialog::ItemDet
const QStringList favoriteTextFormats = src->symbolsOfFavorite( QgsStyle::TextFormatEntity );
const QStringList favoriteLabelSettings = src->symbolsOfFavorite( QgsStyle::LabelSettingsEntity );
const QStringList favoriteLegendPatchShapes = src->symbolsOfFavorite( QgsStyle::LegendPatchShapeEntity );
const QStringList favorite3dSymbols = src->symbolsOfFavorite( QgsStyle::Symbol3DEntity );
for ( auto &details : items )
{
@ -1025,7 +1052,7 @@ int QgsStyleManagerDialog::copyItems( const QList<QgsStyleManagerDialog::ItemDet
const bool hasDuplicateName = dst->symbol3DNames().contains( details.name );
bool overwriteThis = false;
if ( isImport )
addItemToFavorites = favoriteSymbols.contains( details.name );
addItemToFavorites = favorite3dSymbols.contains( details.name );
if ( hasDuplicateName && prompt )
{
@ -1172,6 +1199,8 @@ int QgsStyleManagerDialog::currentItemType()
return 5;
case 7:
return 6;
case 8:
return 7;
default:
return 0;
}
@ -1211,6 +1240,11 @@ void QgsStyleManagerDialog::addItem()
// actually never hit, because we present a submenu when adding legend patches
// changed = addLegendPatchShape();
}
else if ( currentItemType() == 7 )
{
// actually never hit, because we present a submenu when adding 3d symbols
// changed = addSymbol3D();
}
else
{
Q_ASSERT( false && "not implemented" );
@ -1526,6 +1560,10 @@ void QgsStyleManagerDialog::editItem()
{
editLegendPatchShape();
}
else if ( selectedItemType() == 7 )
{
editSymbol3D();
}
else
{
Q_ASSERT( false && "not implemented" );
@ -1847,6 +1885,104 @@ bool QgsStyleManagerDialog::editLegendPatchShape()
return true;
}
bool QgsStyleManagerDialog::addSymbol3D( const QString &type )
{
std::unique_ptr< QgsAbstract3DSymbol > symbol( QgsApplication::symbol3DRegistry()->createSymbol( type ) );
if ( !symbol )
return false;
Qgs3DSymbolDialog dialog( symbol.get(), this );
if ( mReadOnly )
dialog.buttonBox()->button( QDialogButtonBox::Ok )->setEnabled( false );
if ( !dialog.exec() )
return false;
symbol.reset( dialog.symbol() );
if ( !symbol )
return false;
QgsStyleSaveDialog saveDlg( this, QgsStyle::Symbol3DEntity );
if ( !saveDlg.exec() )
return false;
QString name = saveDlg.name();
// request valid/unique name
bool nameInvalid = true;
while ( nameInvalid )
{
// validate name
if ( name.isEmpty() )
{
QMessageBox::warning( this, tr( "Save 3D Symbol" ),
tr( "Cannot save 3d symbols without a name. Enter a name." ) );
}
else if ( mStyle->symbol3DNames().contains( name ) )
{
int res = QMessageBox::warning( this, tr( "Save 3D Symbol" ),
tr( "A 3D symbol with the name '%1' already exists. Overwrite?" )
.arg( name ),
QMessageBox::Yes | QMessageBox::No );
if ( res == QMessageBox::Yes )
{
mStyle->removeEntityByName( QgsStyle::Symbol3DEntity, name );
nameInvalid = false;
}
}
else
{
// valid name
nameInvalid = false;
}
if ( nameInvalid )
{
bool ok;
name = QInputDialog::getText( this, tr( "3D Symbol Name" ),
tr( "Please enter a name for the new 3D symbol:" ),
QLineEdit::Normal, name, &ok );
if ( !ok )
{
return false;
}
}
}
QStringList symbolTags = saveDlg.tags().split( ',' );
// add new shape to style and re-populate the list
QgsAbstract3DSymbol *newSymbol = symbol.get();
mStyle->addSymbol3D( name, symbol.release() );
mStyle->saveSymbol3D( name, newSymbol, saveDlg.isFavorite(), symbolTags );
mModified = true;
return true;
}
bool QgsStyleManagerDialog::editSymbol3D()
{
const QString symbolName = currentItemName();
if ( symbolName.isEmpty() )
return false;
std::unique_ptr< QgsAbstract3DSymbol > symbol( mStyle->symbol3D( symbolName ) );
if ( !symbol )
return false;
// let the user edit the symbol and update list when done
Qgs3DSymbolDialog dlg( symbol.get(), this );
if ( !dlg.exec() )
return false;
symbol.reset( dlg.symbol() );
if ( !symbol )
return false;
// by adding symbol to style with the same name the old effectively gets overwritten
mStyle->addSymbol3D( symbolName, symbol.release(), true );
mModified = true;
return true;
}
void QgsStyleManagerDialog::removeItem()
{
const QList< ItemDetails > items = selectedItems();
@ -1901,6 +2037,14 @@ void QgsStyleManagerDialog::removeItem()
QMessageBox::No ) )
return;
}
else if ( currentItemType() == 7 )
{
if ( QMessageBox::Yes != QMessageBox::question( this, tr( "Remove 3D Symbols" ),
QString( tr( "Do you really want to remove %n 3D symbols?", nullptr, items.count() ) ),
QMessageBox::Yes,
QMessageBox::No ) )
return;
}
}
QgsTemporaryCursorOverride override( Qt::WaitCursor );

View File

@ -397,6 +397,9 @@ class GUI_EXPORT QgsStyleManagerDialog : public QDialog, private Ui::QgsStyleMan
//! Menu for the "Add item" toolbutton when in legend patch shape mode
QMenu *mMenuBtnAddItemLegendPatchShape = nullptr;
//! Menu for the "Add item" toolbutton when in 3d symbol mode
QMenu *mMenuBtnAddItemSymbol3D = nullptr;
QAction *mActionCopyToDefault = nullptr;
QAction *mActionCopyItem = nullptr;
@ -418,6 +421,9 @@ class GUI_EXPORT QgsStyleManagerDialog : public QDialog, private Ui::QgsStyleMan
bool addLegendPatchShape( QgsSymbol::SymbolType type );
bool editLegendPatchShape();
bool addSymbol3D( const QString &type );
bool editSymbol3D();
friend class QgsStyleExportImportDialog;
};

View File

@ -382,6 +382,15 @@
<string>Legend Patch Shapes</string>
</attribute>
</widget>
<widget class="QWidget" name="tab3DSymbols">
<attribute name="icon">
<iconset resource="../../images/images.qrc">
<normaloff>:/images/themes/default/3d.svg</normaloff>:/images/themes/default/3d.svg</iconset>
</attribute>
<attribute name="title">
<string>3D Symbols</string>
</attribute>
</widget>
</widget>
</item>
<item>
@ -669,8 +678,6 @@
</tabstops>
<resources>
<include location="../../images/images.qrc"/>
<include location="../../images/images.qrc"/>
<include location="../../images/images.qrc"/>
</resources>
<connections>
<connection>