mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-14 00:07:35 -04:00
Allow copying and pasting symbols between QgsSymbolButtons
This commit is contained in:
parent
15f3bbf9c8
commit
78b05c1a7f
@ -88,7 +88,7 @@ return new default symbol for specified geometry type
|
||||
:rtype: QgsSymbolLayer
|
||||
%End
|
||||
|
||||
int symbolLayerCount();
|
||||
int symbolLayerCount() const;
|
||||
%Docstring
|
||||
Returns total number of symbol layers contained in the symbol.
|
||||
:return: count of symbol layers
|
||||
|
@ -231,8 +231,10 @@ class QgsSymbolLayer
|
||||
|
||||
virtual QgsSymbol *subSymbol();
|
||||
%Docstring
|
||||
Returns the symbol's sub symbol, if present.
|
||||
:rtype: QgsSymbol
|
||||
%End
|
||||
|
||||
virtual bool setSubSymbol( QgsSymbol *symbol /Transfer/ );
|
||||
%Docstring
|
||||
set layer's subsymbol. takes ownership of the passed symbol
|
||||
|
@ -548,6 +548,25 @@ Writes a collection of symbols to XML with specified tagName for the top-level e
|
||||
|
||||
static void clearSymbolMap( QgsSymbolMap &symbols );
|
||||
|
||||
static QMimeData *symbolToMimeData( QgsSymbol *symbol ) /Factory/;
|
||||
%Docstring
|
||||
Creates new mime data from a ``symbol``.
|
||||
This also sets the mime color data to match the symbol's color, so that copied symbols
|
||||
can be paste in places where a color is expected.
|
||||
.. seealso:: symbolFromMimeData()
|
||||
.. versionadded:: 3.0
|
||||
:rtype: QMimeData
|
||||
%End
|
||||
|
||||
static QgsSymbol *symbolFromMimeData( const QMimeData *data ) /Factory/;
|
||||
%Docstring
|
||||
Attempts to parse ``mime`` data as a symbol. A new symbol instance will be returned
|
||||
if the data was successfully converted to a symbol.
|
||||
.. seealso:: symbolToMimeData()
|
||||
.. versionadded:: 3.0
|
||||
:rtype: QgsSymbol
|
||||
%End
|
||||
|
||||
static QgsColorRamp *loadColorRamp( QDomElement &element ) /Factory/;
|
||||
%Docstring
|
||||
Creates a color ramp from the settings encoded in an XML element
|
||||
|
@ -32,6 +32,21 @@ class QgsSymbolButton : QToolButton
|
||||
|
||||
virtual QSize minimumSizeHint() const;
|
||||
|
||||
void setSymbolType( QgsSymbol::SymbolType type );
|
||||
%Docstring
|
||||
Sets the symbol ``type`` which the button requires.
|
||||
If the type differs from the current symbol type, the symbol will be reset
|
||||
to a default symbol style of the new type.
|
||||
.. seealso:: symbolType()
|
||||
%End
|
||||
|
||||
QgsSymbol::SymbolType symbolType() const;
|
||||
%Docstring
|
||||
Returns the symbol type which the button requires.
|
||||
.. seealso:: setSymbolType()
|
||||
:rtype: QgsSymbol.SymbolType
|
||||
%End
|
||||
|
||||
void setDialogTitle( const QString &title );
|
||||
%Docstring
|
||||
Sets the ``title`` for the symbol settings dialog window.
|
||||
@ -105,6 +120,19 @@ class QgsSymbolButton : QToolButton
|
||||
to the previous symbol color.
|
||||
%End
|
||||
|
||||
void copySymbol();
|
||||
%Docstring
|
||||
Copies the current symbol to the clipboard.
|
||||
.. seealso:: pasteSymbol()
|
||||
%End
|
||||
|
||||
void pasteSymbol();
|
||||
%Docstring
|
||||
Pastes a symbol from the clipboard. If clipboard does not contain a valid
|
||||
symbol then no change is applied.
|
||||
.. seealso:: copySymbol()
|
||||
%End
|
||||
|
||||
void copyColor();
|
||||
%Docstring
|
||||
Copies the current symbol color to the clipboard.
|
||||
|
@ -45,6 +45,8 @@ QgsComposerShapeWidget::QgsComposerShapeWidget( QgsComposerShape *composerShape
|
||||
mShapeComboBox->addItem( tr( "Rectangle" ) );
|
||||
mShapeComboBox->addItem( tr( "Triangle" ) );
|
||||
|
||||
mShapeStyleButton->setSymbolType( QgsSymbol::Fill );
|
||||
|
||||
setGuiElementValues();
|
||||
|
||||
blockAllSignals( false );
|
||||
|
@ -33,6 +33,9 @@ QgsAnnotationWidget::QgsAnnotationWidget( QgsMapCanvasAnnotationItem *item, QWid
|
||||
setupUi( this );
|
||||
mLayerComboBox->setAllowEmptyLayer( true );
|
||||
|
||||
mMapMarkerButton->setSymbolType( QgsSymbol::Marker );
|
||||
mFrameStyleButton->setSymbolType( QgsSymbol::Fill );
|
||||
|
||||
if ( mItem && mItem->annotation() )
|
||||
{
|
||||
QgsAnnotation *annotation = mItem->annotation();
|
||||
|
@ -34,6 +34,9 @@ QgsDecorationGridDialog::QgsDecorationGridDialog( QgsDecorationGrid &deco, QWidg
|
||||
{
|
||||
setupUi( this );
|
||||
|
||||
mMarkerSymbolButton->setSymbolType( QgsSymbol::Marker );
|
||||
mLineSymbolButton->setSymbolType( QgsSymbol::Line );
|
||||
|
||||
mAnnotationFontButton->setMode( QgsFontButton::ModeQFont );
|
||||
|
||||
QgsSettings settings;
|
||||
@ -65,6 +68,7 @@ QgsDecorationGridDialog::QgsDecorationGridDialog( QgsDecorationGrid &deco, QWidg
|
||||
connect( mAnnotationFontButton, &QgsFontButton::changed, this, &QgsDecorationGridDialog::annotationFontChanged );
|
||||
|
||||
mMarkerSymbolButton->setMapCanvas( QgisApp::instance()->mapCanvas() );
|
||||
mLineSymbolButton->setMapCanvas( QgisApp::instance()->mapCanvas() );
|
||||
}
|
||||
|
||||
void QgsDecorationGridDialog::updateGuiElements()
|
||||
|
@ -35,6 +35,8 @@ QgsDecorationLayoutExtentDialog::QgsDecorationLayoutExtentDialog( QgsDecorationL
|
||||
{
|
||||
setupUi( this );
|
||||
|
||||
mSymbolButton->setSymbolType( QgsSymbol::Fill );
|
||||
|
||||
QgsSettings settings;
|
||||
restoreGeometry( settings.value( "/Windows/DecorationLayoutExtent/geometry" ).toByteArray() );
|
||||
|
||||
|
@ -136,7 +136,7 @@ class CORE_EXPORT QgsSymbol
|
||||
* \see symbolLayers
|
||||
* \see symbolLayer
|
||||
*/
|
||||
int symbolLayerCount() { return mLayers.count(); }
|
||||
int symbolLayerCount() const { return mLayers.count(); }
|
||||
|
||||
/**
|
||||
* Insert symbol layer to specified index
|
||||
|
@ -252,7 +252,11 @@ class CORE_EXPORT QgsSymbolLayer
|
||||
|
||||
virtual void drawPreviewIcon( QgsSymbolRenderContext &context, QSize size ) = 0;
|
||||
|
||||
/**
|
||||
* Returns the symbol's sub symbol, if present.
|
||||
*/
|
||||
virtual QgsSymbol *subSymbol() { return nullptr; }
|
||||
|
||||
//! set layer's subsymbol. takes ownership of the passed symbol
|
||||
virtual bool setSubSymbol( QgsSymbol *symbol SIP_TRANSFER ) { delete symbol; return false; }
|
||||
|
||||
|
@ -2809,6 +2809,48 @@ void QgsSymbolLayerUtils::clearSymbolMap( QgsSymbolMap &symbols )
|
||||
symbols.clear();
|
||||
}
|
||||
|
||||
QMimeData *QgsSymbolLayerUtils::symbolToMimeData( QgsSymbol *symbol )
|
||||
{
|
||||
if ( !symbol )
|
||||
return nullptr;
|
||||
|
||||
std::unique_ptr< QMimeData >mimeData( new QMimeData );
|
||||
|
||||
QDomDocument symbolDoc;
|
||||
QDomElement symbolElem = saveSymbol( QStringLiteral( "symbol" ), symbol, symbolDoc, QgsReadWriteContext() );
|
||||
symbolDoc.appendChild( symbolElem );
|
||||
mimeData->setText( symbolDoc.toString() );
|
||||
|
||||
mimeData->setImageData( symbolPreviewPixmap( symbol, QSize( 100, 100 ), 18 ).toImage() );
|
||||
mimeData->setColorData( symbol->color() );
|
||||
|
||||
return mimeData.release();
|
||||
}
|
||||
|
||||
QgsSymbol *QgsSymbolLayerUtils::symbolFromMimeData( const QMimeData *data )
|
||||
{
|
||||
if ( !data )
|
||||
return nullptr;
|
||||
|
||||
QString text = data->text();
|
||||
if ( !text.isEmpty() )
|
||||
{
|
||||
QDomDocument doc;
|
||||
QDomElement elem;
|
||||
|
||||
if ( doc.setContent( text ) )
|
||||
{
|
||||
elem = doc.documentElement();
|
||||
|
||||
if ( elem.nodeName() != QStringLiteral( "symbol" ) )
|
||||
elem = elem.firstChildElement( QStringLiteral( "symbol" ) );
|
||||
|
||||
return loadSymbol( elem, QgsReadWriteContext() );
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
QgsColorRamp *QgsSymbolLayerUtils::loadColorRamp( QDomElement &element )
|
||||
{
|
||||
|
@ -365,6 +365,23 @@ class CORE_EXPORT QgsSymbolLayerUtils
|
||||
|
||||
static void clearSymbolMap( QgsSymbolMap &symbols );
|
||||
|
||||
/**
|
||||
* Creates new mime data from a \a symbol.
|
||||
* This also sets the mime color data to match the symbol's color, so that copied symbols
|
||||
* can be paste in places where a color is expected.
|
||||
* \see symbolFromMimeData()
|
||||
* \since QGIS 3.0
|
||||
*/
|
||||
static QMimeData *symbolToMimeData( QgsSymbol *symbol ) SIP_FACTORY;
|
||||
|
||||
/**
|
||||
* Attempts to parse \a mime data as a symbol. A new symbol instance will be returned
|
||||
* if the data was successfully converted to a symbol.
|
||||
* \see symbolToMimeData()
|
||||
* \since QGIS 3.0
|
||||
*/
|
||||
static QgsSymbol *symbolFromMimeData( const QMimeData *data ) SIP_FACTORY;
|
||||
|
||||
/** Creates a color ramp from the settings encoded in an XML element
|
||||
* \param element DOM element
|
||||
* \returns new color ramp. Caller takes responsibility for deleting the returned value.
|
||||
|
@ -47,6 +47,7 @@ QgsFieldConditionalFormatWidget::QgsFieldConditionalFormatWidget( QWidget *paren
|
||||
mModel = new QStandardItemModel( listView );
|
||||
listView->setModel( mModel );
|
||||
mPresetsList->setModel( mPresetsModel );
|
||||
btnChangeIcon->setSymbolType( QgsSymbol::Marker );
|
||||
btnChangeIcon->setSymbol( QgsSymbol::defaultSymbol( QgsWkbTypes::PointGeometry ) );
|
||||
|
||||
setPresets( defaultPresets() );
|
||||
|
@ -52,6 +52,29 @@ QSize QgsSymbolButton::minimumSizeHint() const
|
||||
return QSize( size.width(), qMax( size.height(), fontHeight ) );
|
||||
}
|
||||
|
||||
void QgsSymbolButton::setSymbolType( QgsSymbol::SymbolType type )
|
||||
{
|
||||
if ( type != mType )
|
||||
{
|
||||
switch ( type )
|
||||
{
|
||||
case QgsSymbol::Marker:
|
||||
mSymbol.reset( QgsMarkerSymbol::createSimple( QgsStringMap() ) );
|
||||
break;
|
||||
|
||||
case QgsSymbol::Line:
|
||||
mSymbol.reset( QgsLineSymbol::createSimple( QgsStringMap() ) );
|
||||
break;
|
||||
|
||||
case QgsSymbol::Fill:
|
||||
mSymbol.reset( QgsFillSymbol::createSimple( QgsStringMap() ) );
|
||||
break;
|
||||
}
|
||||
}
|
||||
updatePreview();
|
||||
mType = type;
|
||||
}
|
||||
|
||||
void QgsSymbolButton::showSettingsDialog()
|
||||
{
|
||||
QgsExpressionContext context;
|
||||
@ -155,6 +178,18 @@ void QgsSymbolButton::setColor( const QColor &color )
|
||||
emit changed();
|
||||
}
|
||||
|
||||
void QgsSymbolButton::copySymbol()
|
||||
{
|
||||
QApplication::clipboard()->setMimeData( QgsSymbolLayerUtils::symbolToMimeData( mSymbol.get() ) );
|
||||
}
|
||||
|
||||
void QgsSymbolButton::pasteSymbol()
|
||||
{
|
||||
std::unique_ptr< QgsSymbol > symbol( QgsSymbolLayerUtils::symbolFromMimeData( QApplication::clipboard()->mimeData() ) );
|
||||
if ( symbol && symbol->type() == mType )
|
||||
setSymbol( symbol.release() );
|
||||
}
|
||||
|
||||
void QgsSymbolButton::copyColor()
|
||||
{
|
||||
QApplication::clipboard()->setMimeData( QgsSymbolLayerUtils::colorToMimeData( mSymbol->color() ) );
|
||||
@ -265,6 +300,24 @@ void QgsSymbolButton::prepareMenu()
|
||||
mMenu->addAction( configureAction );
|
||||
connect( configureAction, &QAction::triggered, this, &QgsSymbolButton::showSettingsDialog );
|
||||
|
||||
QAction *copySymbolAction = new QAction( tr( "Copy symbol" ), this );
|
||||
mMenu->addAction( copySymbolAction );
|
||||
connect( copySymbolAction, &QAction::triggered, this, &QgsSymbolButton::copySymbol );
|
||||
QAction *pasteSymbolAction = new QAction( tr( "Paste symbol" ), this );
|
||||
//enable or disable paste action based on current clipboard contents. We always show the paste
|
||||
//action, even if it's disabled, to give hint to the user that pasting symbols is possible
|
||||
std::unique_ptr< QgsSymbol > tempSymbol( QgsSymbolLayerUtils::symbolFromMimeData( QApplication::clipboard()->mimeData() ) );
|
||||
if ( tempSymbol && tempSymbol->type() == mType )
|
||||
{
|
||||
pasteSymbolAction->setIcon( QgsSymbolLayerUtils::symbolPreviewIcon( tempSymbol.get(), QSize( 16, 16 ), 1 ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
pasteSymbolAction->setEnabled( false );
|
||||
}
|
||||
mMenu->addAction( pasteSymbolAction );
|
||||
connect( pasteSymbolAction, &QAction::triggered, this, &QgsSymbolButton::pasteSymbol );
|
||||
|
||||
mMenu->addSeparator();
|
||||
|
||||
QgsColorWheel *colorWheel = new QgsColorWheel( mMenu );
|
||||
|
@ -53,6 +53,20 @@ class GUI_EXPORT QgsSymbolButton : public QToolButton
|
||||
|
||||
virtual QSize minimumSizeHint() const override;
|
||||
|
||||
/**
|
||||
* Sets the symbol \a type which the button requires.
|
||||
* If the type differs from the current symbol type, the symbol will be reset
|
||||
* to a default symbol style of the new type.
|
||||
* \see symbolType()
|
||||
*/
|
||||
void setSymbolType( QgsSymbol::SymbolType type );
|
||||
|
||||
/**
|
||||
* Returns the symbol type which the button requires.
|
||||
* \see setSymbolType()
|
||||
*/
|
||||
QgsSymbol::SymbolType symbolType() const { return mType; }
|
||||
|
||||
/**
|
||||
* Sets the \a title for the symbol settings dialog window.
|
||||
* \see dialogTitle()
|
||||
@ -143,6 +157,18 @@ class GUI_EXPORT QgsSymbolButton : public QToolButton
|
||||
*/
|
||||
void setColor( const QColor &color );
|
||||
|
||||
/** Copies the current symbol to the clipboard.
|
||||
* \see pasteSymbol()
|
||||
*/
|
||||
void copySymbol();
|
||||
|
||||
/**
|
||||
* Pastes a symbol from the clipboard. If clipboard does not contain a valid
|
||||
* symbol then no change is applied.
|
||||
* \see copySymbol()
|
||||
*/
|
||||
void pasteSymbol();
|
||||
|
||||
/**
|
||||
* Copies the current symbol color to the clipboard.
|
||||
* \see pasteColor()
|
||||
@ -200,6 +226,8 @@ class GUI_EXPORT QgsSymbolButton : public QToolButton
|
||||
|
||||
QString mDialogTitle;
|
||||
|
||||
QgsSymbol::SymbolType mType = QgsSymbol::Fill;
|
||||
|
||||
QgsMapCanvas *mMapCanvas = nullptr;
|
||||
|
||||
QPoint mDragStartPosition;
|
||||
|
@ -53,6 +53,8 @@ QgsPointClusterRendererWidget::QgsPointClusterRendererWidget( QgsVectorLayer *la
|
||||
mDistanceUnitWidget->setUnits( QgsUnitTypes::RenderUnitList() << QgsUnitTypes::RenderMillimeters << QgsUnitTypes::RenderMapUnits << QgsUnitTypes::RenderPixels
|
||||
<< QgsUnitTypes::RenderPoints << QgsUnitTypes::RenderInches );
|
||||
|
||||
mCenterSymbolToolButton->setSymbolType( QgsSymbol::Marker );
|
||||
|
||||
if ( renderer )
|
||||
{
|
||||
mRenderer = QgsPointClusterRenderer::convertFromRenderer( renderer );
|
||||
|
@ -53,6 +53,7 @@ QgsPointDisplacementRendererWidget::QgsPointDisplacementRendererWidget( QgsVecto
|
||||
mLabelFontButton->setMode( QgsFontButton::ModeQFont );
|
||||
mDistanceUnitWidget->setUnits( QgsUnitTypes::RenderUnitList() << QgsUnitTypes::RenderMillimeters << QgsUnitTypes::RenderMetersInMapUnits << QgsUnitTypes::RenderMapUnits << QgsUnitTypes::RenderPixels
|
||||
<< QgsUnitTypes::RenderPoints << QgsUnitTypes::RenderInches );
|
||||
mCenterSymbolToolButton->setSymbolType( QgsSymbol::Marker );
|
||||
|
||||
if ( renderer )
|
||||
{
|
||||
|
@ -14,7 +14,7 @@ __revision__ = '$Format:%H$'
|
||||
|
||||
import qgis # NOQA
|
||||
|
||||
from qgis.core import QgsFillSymbol, QgsMarkerSymbol
|
||||
from qgis.core import QgsFillSymbol, QgsMarkerSymbol, QgsSymbol
|
||||
from qgis.gui import QgsSymbolButton, QgsMapCanvas
|
||||
from qgis.testing import start_app, unittest
|
||||
from qgis.PyQt.QtGui import QColor, QFont
|
||||
@ -36,6 +36,50 @@ class TestQgsSymbolButton(unittest.TestCase):
|
||||
button.setMapCanvas(canvas)
|
||||
self.assertEqual(button.mapCanvas(), canvas)
|
||||
|
||||
button.setSymbolType(QgsSymbol.Line)
|
||||
self.assertEqual(button.symbolType(), QgsSymbol.Line)
|
||||
|
||||
def testSettingSymbolType(self)
|
||||
button = QgsSymbolButton()
|
||||
button.setSymbolType(QgsSymbol.Marker)
|
||||
symbol = QgsMarkerSymbol.createSimple({})
|
||||
symbol.setColor(QColor(255, 0, 0))
|
||||
button.setSymbol(symbol)
|
||||
|
||||
# if same symbol type, existing symbol should be kept
|
||||
button.setSymbolType(QgsSymbol.Marker)
|
||||
self.assertEqual(button.symbol(), symbol)
|
||||
|
||||
# if setting different symbol type, symbol should be reset to new type
|
||||
button.setSymbolType(QgsSymbol.Fill)
|
||||
self.assertTrue(isinstance(button.symbol(), QgsFillSymbol))
|
||||
|
||||
def testPasteSymbol(self):
|
||||
button = QgsSymbolButton()
|
||||
button.setSymbolType(QgsSymbol.Marker)
|
||||
symbol = QgsMarkerSymbol.createSimple({})
|
||||
symbol.setColor(QColor(255, 0, 0))
|
||||
button.setSymbol(symbol)
|
||||
|
||||
button2 = QgsSymbolButton()
|
||||
button2.setSymbolType(QgsSymbol.Marker)
|
||||
symbol2 = QgsMarkerSymbol.createSimple({})
|
||||
symbol2.setColor(QColor(0, 255, 0))
|
||||
button2.setSymbol(symbol2)
|
||||
|
||||
button.copySymbol()
|
||||
button2.pasteSymbol()
|
||||
self.assertEqual(button2.symbol().color(), QColor(255, 0, 0))
|
||||
|
||||
# try pasting incompatible symbol
|
||||
button2.setSymbolType(QgsSymbol.Fill)
|
||||
fill_symbol = QgsFillSymbol.createSimple({})
|
||||
fill_symbol.setColor(QColor(0,0,255))
|
||||
button2.setSymbol(fill_symbol)
|
||||
button.copySymbol() # copied a marker symbol
|
||||
button2.pasteSymbol() # should have no effect
|
||||
self.assertEqual(button2.symbol(), fill_symbol)
|
||||
|
||||
def testSetGetSymbol(self):
|
||||
button = QgsSymbolButton()
|
||||
symbol = QgsMarkerSymbol.createSimple({})
|
||||
|
@ -14,7 +14,7 @@ __revision__ = '$Format:%H$'
|
||||
|
||||
import qgis # NOQA
|
||||
|
||||
from qgis.core import QgsSymbolLayerUtils
|
||||
from qgis.core import QgsSymbolLayerUtils, QgsMarkerSymbol
|
||||
from qgis.PyQt.QtCore import QSizeF, QPointF
|
||||
from qgis.testing import unittest
|
||||
|
||||
@ -49,6 +49,20 @@ class PyQgsSymbolLayerUtils(unittest.TestCase):
|
||||
s2 = QgsSymbolLayerUtils.decodePoint('')
|
||||
self.assertEqual(s2, QPointF())
|
||||
|
||||
def testSymbolToFromMimeData(self):
|
||||
"""
|
||||
Test converting symbols to and from mime data
|
||||
"""
|
||||
symbol = QgsMarkerSymbol.createSimple({})
|
||||
symbol.setColor(QColor(255, 0, 255))
|
||||
self.assertFalse(QgsSymbolLayerUtils.symbolFromMimeData(None))
|
||||
self.assertFalse(QgsSymbolLayerUtils.symbolToMimeData(None))
|
||||
mime = QgsSymbolLayerUtils.symbolToMimeData(symbol)
|
||||
self.assertTrue(mime is not None)
|
||||
symbol2 = QgsSymbolLayerUtils.symbolFromMimeData(mime)
|
||||
self.assertTrue(symbol2 is not None)
|
||||
self.assertEqual(symbol2.color().name(), symbol.color().name())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
Loading…
x
Reference in New Issue
Block a user