From 22c4740f63c55b8dd461438953d6e3558dada6df Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Sun, 23 Jul 2017 13:17:24 +1000 Subject: [PATCH 01/11] [FEATURE] New standard widget for symbol buttons Button widgets for configuring symbol properties were reimplemented multiple times throughout the codebase. This commit creates a new standard QgsSymbolButton widget which should be used whenever a button for configuring symbol properties is required. Features include: - automatic use of inline panels whenever possible - dropdown menu with shortcuts to color settings, copy/pasting colors - accepts drag and dropped colors to set symbol color --- python/gui/gui_auto.sip | 1 + python/gui/qgssymbolbutton.sip | 159 ++++++++ src/gui/CMakeLists.txt | 2 + src/gui/qgssymbolbutton.cpp | 446 +++++++++++++++++++++++ src/gui/qgssymbolbutton.h | 218 +++++++++++ tests/src/python/CMakeLists.txt | 1 + tests/src/python/test_qgssymbolbutton.py | 77 ++++ 7 files changed, 904 insertions(+) create mode 100644 python/gui/qgssymbolbutton.sip create mode 100644 src/gui/qgssymbolbutton.cpp create mode 100644 src/gui/qgssymbolbutton.h create mode 100644 tests/src/python/test_qgssymbolbutton.py diff --git a/python/gui/gui_auto.sip b/python/gui/gui_auto.sip index 3a4b9138d8e..93d4816f782 100644 --- a/python/gui/gui_auto.sip +++ b/python/gui/gui_auto.sip @@ -188,6 +188,7 @@ %Include qgsstatusbar.sip %Include qgssublayersdialog.sip %Include qgssubstitutionlistwidget.sip +%Include qgssymbolbutton.sip %Include qgstablewidgetbase.sip %Include qgstabwidget.sip %Include qgstaskmanagerwidget.sip diff --git a/python/gui/qgssymbolbutton.sip b/python/gui/qgssymbolbutton.sip new file mode 100644 index 00000000000..8c5566fdfdf --- /dev/null +++ b/python/gui/qgssymbolbutton.sip @@ -0,0 +1,159 @@ +/************************************************************************ + * This file has been generated automatically from * + * * + * src/gui/qgssymbolbutton.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ + + + +class QgsSymbolButton : QToolButton +{ +%Docstring + A button for creating and modifying QgsSymbol settings. + + The button shows a preview icon for the current symbol, and will open a detailed symbol editor dialog (or + panel widget) when clicked. + +.. versionadded:: 3.0 +%End + +%TypeHeaderCode +#include "qgssymbolbutton.h" +%End + public: + + QgsSymbolButton( QWidget *parent /TransferThis/ = 0, const QString &dialogTitle = QString() ); +%Docstring + Construct a new symbol button. + Use ``dialogTitle`` string to define the title to show in the symbol settings dialog. +%End + + virtual QSize minimumSizeHint() const; + + void setDialogTitle( const QString &title ); +%Docstring + Sets the ``title`` for the symbol settings dialog window. +.. seealso:: dialogTitle() +%End + + QString dialogTitle() const; +%Docstring + Returns the title for the symbol settings dialog window. +.. seealso:: setDialogTitle() + :rtype: str +%End + + QgsSymbol *symbol(); +%Docstring + Returns the current symbol defined by the button. +.. seealso:: setSymbol() +.. seealso:: changed() + :rtype: QgsSymbol +%End + + QgsMapCanvas *mapCanvas() const; +%Docstring + Returns the map canvas associated with the widget. +.. seealso:: setMapCanvas() + :rtype: QgsMapCanvas +%End + + void setMapCanvas( QgsMapCanvas *canvas ); +%Docstring + Sets a map ``canvas`` to associate with the widget. This allows the + widget to fetch current settings from the map canvas, such as current scale. +.. seealso:: mapCanvas() +%End + + QgsVectorLayer *layer() const; +%Docstring + Returns the layer associated with the widget. +.. seealso:: setLayer() + :rtype: QgsVectorLayer +%End + + void setLayer( QgsVectorLayer *layer ); +%Docstring + Sets a ``layer`` to associate with the widget. This allows the + widget to setup layer related settings within the symbol settings dialog, + such as correctly populating data defined override buttons. +.. seealso:: layer() +%End + + void registerExpressionContextGenerator( QgsExpressionContextGenerator *generator ); +%Docstring + Register an expression context generator class that will be used to retrieve + an expression context for the button when required. +%End + + public slots: + + void setSymbol( QgsSymbol *symbol /Transfer/ ); +%Docstring + Sets the ``symbol`` for the button. Ownership of ``symbol`` is transferred to the + button. +.. seealso:: symbol() +.. seealso:: changed() +%End + + void setColor( const QColor &color ); +%Docstring + Sets the current ``color`` for the symbol. Will emit a changed() signal if the color is different + to the previous symbol color. +%End + + void copyColor(); +%Docstring + Copies the current symbol color to the clipboard. +.. seealso:: pasteColor() +%End + + void pasteColor(); +%Docstring + Pastes a color from the clipboard to the symbol. If clipboard does not contain a valid + color or string representation of a color, then no change is applied. +.. seealso:: copyColor() +%End + + signals: + + void changed(); +%Docstring + Emitted when the symbol's settings are changed. +.. seealso:: symbol() +.. seealso:: setSymbol() +%End + + protected: + + virtual void changeEvent( QEvent *e ); + + virtual void showEvent( QShowEvent *e ); + + virtual void resizeEvent( QResizeEvent *event ); + + + virtual void mousePressEvent( QMouseEvent *e ); + + virtual void mouseMoveEvent( QMouseEvent *e ); + + virtual void dragEnterEvent( QDragEnterEvent *e ); + + + virtual void dragLeaveEvent( QDragLeaveEvent *e ); + + + virtual void dropEvent( QDropEvent *e ); + + +}; + +/************************************************************************ + * This file has been generated automatically from * + * * + * src/gui/qgssymbolbutton.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index d6f4ad57731..5dacf06a4ad 100755 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -336,6 +336,7 @@ SET(QGIS_GUI_SRCS qgssubstitutionlistwidget.cpp qgssqlcomposerdialog.cpp qgsstatusbar.cpp + qgssymbolbutton.cpp qgstablewidgetbase.cpp qgstabwidget.cpp qgstablewidgetitem.cpp @@ -492,6 +493,7 @@ SET(QGIS_GUI_MOC_HDRS qgsstatusbar.h qgssublayersdialog.h qgssubstitutionlistwidget.h + qgssymbolbutton.h qgstablewidgetbase.h qgstabwidget.h qgstaskmanagerwidget.h diff --git a/src/gui/qgssymbolbutton.cpp b/src/gui/qgssymbolbutton.cpp new file mode 100644 index 00000000000..b658f5b2f3a --- /dev/null +++ b/src/gui/qgssymbolbutton.cpp @@ -0,0 +1,446 @@ +/*************************************************************************** + qgssymbolbutton.h + ----------------- + Date : July 2017 + Copyright : (C) 2017 by Nyall Dawson + Email : nyall dot dawson at gmail dot com + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "qgssymbolbutton.h" +#include "qgspanelwidget.h" +#include "qgsexpressioncontext.h" +#include "qgsexpressioncontextgenerator.h" +#include "qgsvectorlayer.h" +#include "qgssymbolselectordialog.h" +#include "qgsstyle.h" +#include "qgscolorwidgets.h" +#include "qgscolorschemeregistry.h" +#include "qgscolorswatchgrid.h" +#include "qgssymbollayerutils.h" +#include +#include +#include + +QgsSymbolButton::QgsSymbolButton( QWidget *parent, const QString &dialogTitle ) + : QToolButton( parent ) + , mDialogTitle( dialogTitle.isEmpty() ? tr( "Symbol Settings" ) : dialogTitle ) +{ + mSymbol.reset( QgsFillSymbol::createSimple( QgsStringMap() ) ); + + setAcceptDrops( true ); + connect( this, &QAbstractButton::clicked, this, &QgsSymbolButton::showSettingsDialog ); + + //setup dropdown menu + mMenu = new QMenu( this ); + connect( mMenu, &QMenu::aboutToShow, this, &QgsSymbolButton::prepareMenu ); + setMenu( mMenu ); + setPopupMode( QToolButton::MenuButtonPopup ); +} + +QSize QgsSymbolButton::minimumSizeHint() const +{ + //make sure height of button looks good under different platforms + QSize size = QToolButton::minimumSizeHint(); + int fontHeight = fontMetrics().height() * 1.4; + return QSize( size.width(), qMax( size.height(), fontHeight ) ); +} + +void QgsSymbolButton::showSettingsDialog() +{ + QgsExpressionContext context; + if ( mExpressionContextGenerator ) + context = mExpressionContextGenerator->createExpressionContext(); + else + { + context.appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( mLayer.data() ) ); + } + QgsSymbol *newSymbol = mSymbol->clone(); + + QgsSymbolWidgetContext symbolContext; + symbolContext.setExpressionContext( &context ); + symbolContext.setMapCanvas( mMapCanvas ); + + QgsPanelWidget *panel = QgsPanelWidget::findParentPanel( this ); + if ( panel && panel->dockMode() ) + { + QgsSymbolSelectorWidget *d = new QgsSymbolSelectorWidget( newSymbol, QgsStyle::defaultStyle(), mLayer, nullptr ); + d->setContext( symbolContext ); + connect( d, &QgsPanelWidget::widgetChanged, this, &QgsSymbolButton::updateSymbolFromWidget ); + connect( d, &QgsPanelWidget::panelAccepted, this, &QgsSymbolButton::cleanUpSymbolSelector ); + panel->openPanel( d ); + } + else + { + QgsSymbolSelectorDialog dialog( newSymbol, QgsStyle::defaultStyle(), mLayer, nullptr ); + dialog.setWindowTitle( mDialogTitle ); + dialog.setContext( symbolContext ); + if ( dialog.exec() ) + { + setSymbol( newSymbol ); + } + else + { + delete newSymbol; + } + + // reactivate button's window + activateWindow(); + } +} + +void QgsSymbolButton::updateSymbolFromWidget() +{ + if ( QgsSymbolSelectorWidget *w = qobject_cast( sender() ) ) + setSymbol( w->symbol()->clone() ); +} + +void QgsSymbolButton::cleanUpSymbolSelector( QgsPanelWidget *container ) +{ + QgsSymbolSelectorWidget *w = qobject_cast( container ); + if ( !w ) + return; + + delete w->symbol(); +} + +QgsMapCanvas *QgsSymbolButton::mapCanvas() const +{ + return mMapCanvas; +} + +void QgsSymbolButton::setMapCanvas( QgsMapCanvas *mapCanvas ) +{ + mMapCanvas = mapCanvas; +} + +QgsVectorLayer *QgsSymbolButton::layer() const +{ + return mLayer; +} + +void QgsSymbolButton::setLayer( QgsVectorLayer *layer ) +{ + mLayer = layer; +} + +void QgsSymbolButton::registerExpressionContextGenerator( QgsExpressionContextGenerator *generator ) +{ + mExpressionContextGenerator = generator; +} + +void QgsSymbolButton::setSymbol( QgsSymbol *symbol ) +{ + mSymbol.reset( symbol ); + updatePreview(); + emit changed(); +} + +void QgsSymbolButton::setColor( const QColor &color ) +{ + QColor opaque = color; + opaque.setAlphaF( 1.0 ); + + if ( opaque == mSymbol->color() ) + return; + + mSymbol->setColor( opaque ); + updatePreview(); + emit changed(); +} + +void QgsSymbolButton::copyColor() +{ + QApplication::clipboard()->setMimeData( QgsSymbolLayerUtils::colorToMimeData( mSymbol->color() ) ); +} + +void QgsSymbolButton::pasteColor() +{ + QColor clipColor; + bool hasAlpha = false; + if ( colorFromMimeData( QApplication::clipboard()->mimeData(), clipColor, hasAlpha ) ) + { + //paste color + setColor( clipColor ); + QgsRecentColorScheme::addRecentColor( clipColor ); + } +} + +void QgsSymbolButton::mousePressEvent( QMouseEvent *e ) +{ + if ( e->button() == Qt::RightButton ) + { + QToolButton::showMenu(); + return; + } + else if ( e->button() == Qt::LeftButton ) + { + mDragStartPosition = e->pos(); + } + QToolButton::mousePressEvent( e ); +} + +void QgsSymbolButton::mouseMoveEvent( QMouseEvent *e ) +{ + //handle dragging colors/symbols from button + + if ( !( e->buttons() & Qt::LeftButton ) ) + { + //left button not depressed, so not a drag + QToolButton::mouseMoveEvent( e ); + return; + } + + if ( ( e->pos() - mDragStartPosition ).manhattanLength() < QApplication::startDragDistance() ) + { + //mouse not moved, so not a drag + QToolButton::mouseMoveEvent( e ); + return; + } + + //user is dragging + QDrag *drag = new QDrag( this ); + drag->setMimeData( QgsSymbolLayerUtils::colorToMimeData( mSymbol->color() ) ); + drag->setPixmap( QgsColorWidget::createDragIcon( mSymbol->color() ) ); + drag->exec( Qt::CopyAction ); + setDown( false ); +} + +void QgsSymbolButton::dragEnterEvent( QDragEnterEvent *e ) +{ + //is dragged data valid color data? + QColor mimeColor; + bool hasAlpha = false; + + if ( colorFromMimeData( e->mimeData(), mimeColor, hasAlpha ) ) + { + //if so, we accept the drag, and temporarily change the button's color + //to match the dragged color. This gives immediate feedback to the user + //that colors can be dropped here + e->acceptProposedAction(); + updatePreview( mimeColor ); + } +} + +void QgsSymbolButton::dragLeaveEvent( QDragLeaveEvent *e ) +{ + Q_UNUSED( e ); + //reset button color + updatePreview(); +} + +void QgsSymbolButton::dropEvent( QDropEvent *e ) +{ + //is dropped data valid format data? + QColor mimeColor; + bool hasAlpha = false; + if ( colorFromMimeData( e->mimeData(), mimeColor, hasAlpha ) ) + { + //accept drop and set new color + e->acceptProposedAction(); + + if ( hasAlpha ) + { + mSymbol->setOpacity( mimeColor.alphaF() ); + } + mimeColor.setAlphaF( 1.0 ); + mSymbol->setColor( mimeColor ); + QgsRecentColorScheme::addRecentColor( mimeColor ); + updatePreview(); + emit changed(); + } + updatePreview(); +} + +void QgsSymbolButton::prepareMenu() +{ + //we need to tear down and rebuild this menu every time it is shown. Otherwise the space allocated to any + //QgsColorSwatchGridAction is not recalculated by Qt and the swatch grid may not be the correct size + //for the number of colors shown in the grid. Note that we MUST refresh color swatch grids every time this + //menu is opened, otherwise color schemes like the recent color scheme grid are meaningless + mMenu->clear(); + + QAction *configureAction = new QAction( tr( "Configure symbol..." ), this ); + mMenu->addAction( configureAction ); + connect( configureAction, &QAction::triggered, this, &QgsSymbolButton::showSettingsDialog ); + + mMenu->addSeparator(); + + QgsColorWheel *colorWheel = new QgsColorWheel( mMenu ); + colorWheel->setColor( mSymbol->color() ); + QgsColorWidgetAction *colorAction = new QgsColorWidgetAction( colorWheel, mMenu, mMenu ); + colorAction->setDismissOnColorSelection( false ); + connect( colorAction, &QgsColorWidgetAction::colorChanged, this, &QgsSymbolButton::setColor ); + mMenu->addAction( colorAction ); + + //get schemes with ShowInColorButtonMenu flag set + QList< QgsColorScheme * > schemeList = QgsApplication::colorSchemeRegistry()->schemes( QgsColorScheme::ShowInColorButtonMenu ); + QList< QgsColorScheme * >::iterator it = schemeList.begin(); + for ( ; it != schemeList.end(); ++it ) + { + QgsColorSwatchGridAction *colorAction = new QgsColorSwatchGridAction( *it, mMenu, QStringLiteral( "symbology" ), this ); + colorAction->setBaseColor( mSymbol->color() ); + mMenu->addAction( colorAction ); + connect( colorAction, &QgsColorSwatchGridAction::colorChanged, this, &QgsSymbolButton::setColor ); + connect( colorAction, &QgsColorSwatchGridAction::colorChanged, this, &QgsSymbolButton::addRecentColor ); + } + + mMenu->addSeparator(); + + QAction *copyColorAction = new QAction( tr( "Copy color" ), this ); + mMenu->addAction( copyColorAction ); + connect( copyColorAction, &QAction::triggered, this, &QgsSymbolButton::copyColor ); + + QAction *pasteColorAction = new QAction( tr( "Paste color" ), 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 colors is possible + QColor clipColor; + bool hasAlpha = false; + if ( colorFromMimeData( QApplication::clipboard()->mimeData(), clipColor, hasAlpha ) ) + { + pasteColorAction->setIcon( createColorIcon( clipColor ) ); + } + else + { + pasteColorAction->setEnabled( false ); + } + mMenu->addAction( pasteColorAction ); + connect( pasteColorAction, &QAction::triggered, this, &QgsSymbolButton::pasteColor ); +} + +void QgsSymbolButton::addRecentColor( const QColor &color ) +{ + QgsRecentColorScheme::addRecentColor( color ); +} + + +void QgsSymbolButton::changeEvent( QEvent *e ) +{ + if ( e->type() == QEvent::EnabledChange ) + { + updatePreview(); + } + QToolButton::changeEvent( e ); +} + +void QgsSymbolButton::showEvent( QShowEvent *e ) +{ + updatePreview(); + QToolButton::showEvent( e ); +} + +void QgsSymbolButton::resizeEvent( QResizeEvent *event ) +{ + QToolButton::resizeEvent( event ); + //recalculate icon size and redraw icon + mIconSize = QSize(); + updatePreview(); +} + +void QgsSymbolButton::updatePreview( const QColor &color, QgsSymbol *tempSymbol ) +{ + std::unique_ptr< QgsSymbol > previewSymbol; + + if ( tempSymbol ) + previewSymbol.reset( tempSymbol->clone() ); + else + previewSymbol.reset( mSymbol->clone() ); + + if ( color.isValid() ) + previewSymbol->setColor( color ); + + QSize currentIconSize; + //icon size is button size with a small margin + if ( menu() ) + { + if ( !mIconSize.isValid() ) + { + //calculate size of push button part of widget (ie, without the menu dropdown button part) + QStyleOptionToolButton opt; + initStyleOption( &opt ); + QRect buttonSize = QApplication::style()->subControlRect( QStyle::CC_ToolButton, &opt, QStyle::SC_ToolButton, + this ); + //make sure height of icon looks good under different platforms +#ifdef Q_OS_WIN + mIconSize = QSize( buttonSize.width() - 10, height() - 6 ); +#else + mIconSize = QSize( buttonSize.width() - 10, height() - 12 ); +#endif + } + currentIconSize = mIconSize; + } + else + { + //no menu +#ifdef Q_OS_WIN + currentIconSize = QSize( width() - 10, height() - 6 ); +#else + currentIconSize = QSize( width() - 10, height() - 12 ); +#endif + } + + if ( !currentIconSize.isValid() || currentIconSize.width() <= 0 || currentIconSize.height() <= 0 ) + { + return; + } + + //create an icon pixmap + QIcon icon = QgsSymbolLayerUtils::symbolPreviewIcon( previewSymbol.get(), currentIconSize ); + setIconSize( currentIconSize ); + setIcon( icon ); +} + +bool QgsSymbolButton::colorFromMimeData( const QMimeData *mimeData, QColor &resultColor, bool &hasAlpha ) +{ + hasAlpha = false; + QColor mimeColor = QgsSymbolLayerUtils::colorFromMimeData( mimeData, hasAlpha ); + + if ( mimeColor.isValid() ) + { + resultColor = mimeColor; + return true; + } + + //could not get color from mime data + return false; +} + +QPixmap QgsSymbolButton::createColorIcon( const QColor &color ) const +{ + //create an icon pixmap + QPixmap pixmap( 16, 16 ); + pixmap.fill( Qt::transparent ); + + QPainter p; + p.begin( &pixmap ); + + //draw color over pattern + p.setBrush( QBrush( color ) ); + + //draw border + p.setPen( QColor( 197, 197, 197 ) ); + p.drawRect( 0, 0, 15, 15 ); + p.end(); + return pixmap; +} + +void QgsSymbolButton::setDialogTitle( const QString &title ) +{ + mDialogTitle = title; +} + +QString QgsSymbolButton::dialogTitle() const +{ + return mDialogTitle; +} + +QgsSymbol *QgsSymbolButton::symbol() +{ + return mSymbol.get(); +} diff --git a/src/gui/qgssymbolbutton.h b/src/gui/qgssymbolbutton.h new file mode 100644 index 00000000000..972de290a0b --- /dev/null +++ b/src/gui/qgssymbolbutton.h @@ -0,0 +1,218 @@ +/*************************************************************************** + qgssymbolbutton.h + ----------------- + Date : July 2017 + Copyright : (C) 2017 by Nyall Dawson + Email : nyall dot dawson at gmail dot com + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#ifndef QGSSYMBOLBUTTON_H +#define QGSSYMBOLBUTTON_H + +#include "qgis_gui.h" +#include "qgis.h" +#include "qgssymbol.h" +#include +#include +#include + +class QgsMapCanvas; +class QgsVectorLayer; +class QgsExpressionContextGenerator; +class QgsPanelWidget; + +/** + * \ingroup gui + * \class QgsSymbolButton + * A button for creating and modifying QgsSymbol settings. + * + * The button shows a preview icon for the current symbol, and will open a detailed symbol editor dialog (or + * panel widget) when clicked. + * + * \since QGIS 3.0 + */ +class GUI_EXPORT QgsSymbolButton : public QToolButton +{ + Q_OBJECT + + Q_PROPERTY( QString dialogTitle READ dialogTitle WRITE setDialogTitle ) + + public: + + /** + * Construct a new symbol button. + * Use \a dialogTitle string to define the title to show in the symbol settings dialog. + */ + QgsSymbolButton( QWidget *parent SIP_TRANSFERTHIS = nullptr, const QString &dialogTitle = QString() ); + + virtual QSize minimumSizeHint() const override; + + /** + * Sets the \a title for the symbol settings dialog window. + * \see dialogTitle() + */ + void setDialogTitle( const QString &title ); + + /** + * Returns the title for the symbol settings dialog window. + * \see setDialogTitle() + */ + QString dialogTitle() const; + + /** + * Returns the current symbol defined by the button. + * \see setSymbol() + * \see changed() + */ + QgsSymbol *symbol(); + + /** + * Returns the map canvas associated with the widget. + * \see setMapCanvas() + */ + QgsMapCanvas *mapCanvas() const; + + /** + * Sets a map \a canvas to associate with the widget. This allows the + * widget to fetch current settings from the map canvas, such as current scale. + * \see mapCanvas() + */ + void setMapCanvas( QgsMapCanvas *canvas ); + + /** + * Returns the layer associated with the widget. + * \see setLayer() + */ + QgsVectorLayer *layer() const; + + /** + * Sets a \a layer to associate with the widget. This allows the + * widget to setup layer related settings within the symbol settings dialog, + * such as correctly populating data defined override buttons. + * \see layer() + */ + void setLayer( QgsVectorLayer *layer ); + + /** + * Register an expression context generator class that will be used to retrieve + * an expression context for the button when required. + */ + void registerExpressionContextGenerator( QgsExpressionContextGenerator *generator ); + + public slots: + + /** + * Sets the \a symbol for the button. Ownership of \a symbol is transferred to the + * button. + * \see symbol() + * \see changed() + */ + void setSymbol( QgsSymbol *symbol SIP_TRANSFER ); + + /** + * Sets the current \a color for the symbol. Will emit a changed() signal if the color is different + * to the previous symbol color. + */ + void setColor( const QColor &color ); + + /** + * Copies the current symbol color to the clipboard. + * \see pasteColor() + */ + void copyColor(); + + /** + * Pastes a color from the clipboard to the symbol. If clipboard does not contain a valid + * color or string representation of a color, then no change is applied. + * \see copyColor() + */ + void pasteColor(); + + signals: + + /** + * Emitted when the symbol's settings are changed. + * \see symbol() + * \see setSymbol() + */ + void changed(); + + protected: + + void changeEvent( QEvent *e ) override; + void showEvent( QShowEvent *e ) override; + void resizeEvent( QResizeEvent *event ) override; + + // Reimplemented to detect right mouse button clicks on the color button and allow dragging colors + void mousePressEvent( QMouseEvent *e ) override; + // Reimplemented to allow dragging colors/symbols from button + void mouseMoveEvent( QMouseEvent *e ) override; + // Reimplemented to accept dragged colors + void dragEnterEvent( QDragEnterEvent *e ) override; + + // Reimplemented to reset button appearance after drag leave + void dragLeaveEvent( QDragLeaveEvent *e ) override; + + // Reimplemented to accept dropped colors + void dropEvent( QDropEvent *e ) override; + + private slots: + + void showSettingsDialog(); + void updateSymbolFromWidget(); + void cleanUpSymbolSelector( QgsPanelWidget *container ); + + /** Creates the drop-down menu entries + */ + void prepareMenu(); + + void addRecentColor( const QColor &color ); + + private: + + QString mDialogTitle; + + QgsMapCanvas *mMapCanvas = nullptr; + + QPoint mDragStartPosition; + + QMenu *mMenu = nullptr; + + QPointer< QgsVectorLayer > mLayer; + + QSize mIconSize; + + std::unique_ptr< QgsSymbol > mSymbol; + + QgsExpressionContextGenerator *mExpressionContextGenerator = nullptr; + + /** + * Regenerates the text preview. If \a color is specified, a temporary color preview + * is shown instead. + */ + void updatePreview( const QColor &color = QColor(), QgsSymbol *tempSymbol = nullptr ); + + /** Attempts to parse mimeData as a color, either via the mime data's color data or by + * parsing a textual representation of a color. + * \returns true if mime data could be intrepreted as a color + * \param mimeData mime data + * \param resultColor QColor to store evaluated color + * \param hasAlpha will be set to true if mime data also included an alpha component + * \see formatFromMimeData + */ + bool colorFromMimeData( const QMimeData *mimeData, QColor &resultColor, bool &hasAlpha ); + + /** + * Create a \a color icon for display in the drop-down menu. + */ + QPixmap createColorIcon( const QColor &color ) const; + +}; + +#endif // QGSSYMBOLBUTTON_H diff --git a/tests/src/python/CMakeLists.txt b/tests/src/python/CMakeLists.txt index 0bef93f7235..03131951c2f 100755 --- a/tests/src/python/CMakeLists.txt +++ b/tests/src/python/CMakeLists.txt @@ -126,6 +126,7 @@ ADD_PYTHON_TEST(PyQgsRenderer test_qgsrenderer.py) ADD_PYTHON_TEST(PyQgsRulebasedRenderer test_qgsrulebasedrenderer.py) ADD_PYTHON_TEST(PyQgsSingleSymbolRenderer test_qgssinglesymbolrenderer.py) ADD_PYTHON_TEST(PyQgsShapefileProvider test_provider_shapefile.py) +ADD_PYTHON_TEST(PyQgsSymbolButton test_qgssymbolbutton.py) ADD_PYTHON_TEST(PyQgsTabfileProvider test_provider_tabfile.py) ADD_PYTHON_TEST(PyQgsTabWidget test_qgstabwidget.py) ADD_PYTHON_TEST(PyQgsTextRenderer test_qgstextrenderer.py) diff --git a/tests/src/python/test_qgssymbolbutton.py b/tests/src/python/test_qgssymbolbutton.py new file mode 100644 index 00000000000..ca53e1f8fe2 --- /dev/null +++ b/tests/src/python/test_qgssymbolbutton.py @@ -0,0 +1,77 @@ +# -*- coding: utf-8 -*- +"""QGIS Unit tests for QgsSymbolButton. + +.. note:: This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. +""" +__author__ = 'Nyall Dawson' +__date__ = '23/07/2017' +__copyright__ = 'Copyright 2017, The QGIS Project' +# This will get replaced with a git SHA1 when you do a git archive +__revision__ = '$Format:%H$' + +import qgis # NOQA + +from qgis.core import QgsFillSymbol, QgsMarkerSymbol +from qgis.gui import QgsSymbolButton, QgsMapCanvas +from qgis.testing import start_app, unittest +from qgis.PyQt.QtGui import QColor, QFont +from qgis.PyQt.QtTest import QSignalSpy +from utilities import getTestFont + +start_app() + + +class TestQgsSymbolButton(unittest.TestCase): + + def testGettersSetters(self): + button = QgsSymbolButton() + canvas = QgsMapCanvas() + + button.setDialogTitle('test title') + self.assertEqual(button.dialogTitle(), 'test title') + + button.setMapCanvas(canvas) + self.assertEqual(button.mapCanvas(), canvas) + + def testSetGetSymbol(self): + button = QgsSymbolButton() + symbol = QgsMarkerSymbol.createSimple({}) + symbol.setColor(QColor(255, 0, 0)) + + signal_spy = QSignalSpy(button.changed) + button.setSymbol(symbol) + self.assertEqual(len(signal_spy), 1) + + r = button.symbol() + self.assertEqual(r.color(), QColor(255, 0, 0)) + + def testSetColor(self): + button = QgsSymbolButton() + + symbol = QgsMarkerSymbol.createSimple({}) + symbol.setColor(QColor(255, 255, 0)) + + button.setSymbol(symbol) + + signal_spy = QSignalSpy(button.changed) + button.setColor(QColor(0, 255, 0)) + self.assertEqual(len(signal_spy), 1) + + r = button.symbol() + self.assertEqual(r.color().name(), QColor(0, 255, 0).name()) + + # set same color, should not emit signal + button.setColor(QColor(0, 255, 0)) + self.assertEqual(len(signal_spy), 1) + + # color with transparency - should be stripped + button.setColor(QColor(0, 255, 0, 100)) + r = button.symbol() + self.assertEqual(r.color(), QColor(0, 255, 0)) + + +if __name__ == '__main__': + unittest.main() From 46f6f83fb973d4f09b67ad6893c99e845d42881d Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Sun, 23 Jul 2017 14:48:07 +1000 Subject: [PATCH 02/11] Port some symbol buttons across to QgsSymbolButton - composer shape style button (no other composer ones for now- they're all getting removed with layouts anyway) - point cluster/displacement renderer buttons --- python/gui/qgssymbolbutton.sip | 1 + .../qgspointclusterrendererwidget.sip | 5 +- .../qgspointdisplacementrendererwidget.sip | 5 +- src/app/composer/qgscomposershapewidget.cpp | 53 ++---------- src/app/composer/qgscomposershapewidget.h | 6 +- src/gui/qgssymbolbutton.h | 22 +++++ .../qgspointclusterrendererwidget.cpp | 72 ++++++---------- .../qgspointclusterrendererwidget.h | 11 +-- .../qgspointdisplacementrendererwidget.cpp | 84 +++++++------------ .../qgspointdisplacementrendererwidget.h | 10 +-- src/ui/composer/qgscomposershapewidgetbase.ui | 19 +++-- src/ui/composer/qgscompositionwidgetbase.ui | 10 +-- .../qgspointdisplacementrendererwidgetbase.ui | 41 +++++---- .../qgspointclusterrendererwidgetbase.ui | 27 ++++-- 14 files changed, 167 insertions(+), 199 deletions(-) diff --git a/python/gui/qgssymbolbutton.sip b/python/gui/qgssymbolbutton.sip index 8c5566fdfdf..af8b1e4f9e1 100644 --- a/python/gui/qgssymbolbutton.sip +++ b/python/gui/qgssymbolbutton.sip @@ -53,6 +53,7 @@ class QgsSymbolButton : QToolButton :rtype: QgsSymbol %End + QgsMapCanvas *mapCanvas() const; %Docstring Returns the map canvas associated with the widget. diff --git a/python/gui/symbology-ng/qgspointclusterrendererwidget.sip b/python/gui/symbology-ng/qgspointclusterrendererwidget.sip index 90eb36b629f..84c1c659178 100644 --- a/python/gui/symbology-ng/qgspointclusterrendererwidget.sip +++ b/python/gui/symbology-ng/qgspointclusterrendererwidget.sip @@ -11,7 +11,7 @@ -class QgsPointClusterRendererWidget: QgsRendererWidget +class QgsPointClusterRendererWidget: QgsRendererWidget, QgsExpressionContextGenerator { %Docstring A widget which allows configuration of the properties for a QgsPointClusterRenderer. @@ -48,6 +48,9 @@ class QgsPointClusterRendererWidget: QgsRendererWidget virtual void setContext( const QgsSymbolWidgetContext &context ); + virtual QgsExpressionContext createExpressionContext() const; + + }; /************************************************************************ diff --git a/python/gui/symbology-ng/qgspointdisplacementrendererwidget.sip b/python/gui/symbology-ng/qgspointdisplacementrendererwidget.sip index 4b67f1eaee4..6a22045526b 100644 --- a/python/gui/symbology-ng/qgspointdisplacementrendererwidget.sip +++ b/python/gui/symbology-ng/qgspointdisplacementrendererwidget.sip @@ -10,7 +10,7 @@ -class QgsPointDisplacementRendererWidget: QgsRendererWidget +class QgsPointDisplacementRendererWidget: QgsRendererWidget, QgsExpressionContextGenerator { %TypeHeaderCode @@ -29,6 +29,9 @@ class QgsPointDisplacementRendererWidget: QgsRendererWidget virtual void setContext( const QgsSymbolWidgetContext &context ); + virtual QgsExpressionContext createExpressionContext() const; + + }; /************************************************************************ diff --git a/src/app/composer/qgscomposershapewidget.cpp b/src/app/composer/qgscomposershapewidget.cpp index c41874f0bbc..355c48b3c48 100644 --- a/src/app/composer/qgscomposershapewidget.cpp +++ b/src/app/composer/qgscomposershapewidget.cpp @@ -52,7 +52,10 @@ QgsComposerShapeWidget::QgsComposerShapeWidget( QgsComposerShape *composerShape if ( mComposerShape ) { connect( mComposerShape, &QgsComposerObject::itemChanged, this, &QgsComposerShapeWidget::setGuiElementValues ); + mShapeStyleButton->registerExpressionContextGenerator( mComposerShape ); } + connect( mShapeStyleButton, &QgsSymbolButton::changed, this, &QgsComposerShapeWidget::symbolChanged ); + mShapeStyleButton->setLayer( atlasCoverageLayer() ); } QgsComposerShapeWidget::~QgsComposerShapeWidget() @@ -76,7 +79,7 @@ void QgsComposerShapeWidget::setGuiElementValues() blockAllSignals( true ); - updateShapeStyle(); + mShapeStyleButton->setSymbol( mComposerShape->shapeStyleSymbol()->clone() ); mCornerRadiusSpinBox->setValue( mComposerShape->cornerRadius() ); if ( mComposerShape->shapeType() == QgsComposerShape::Ellipse ) @@ -98,38 +101,16 @@ void QgsComposerShapeWidget::setGuiElementValues() blockAllSignals( false ); } -void QgsComposerShapeWidget::on_mShapeStyleButton_clicked() +void QgsComposerShapeWidget::symbolChanged() { if ( !mComposerShape ) { return; } - // use the atlas coverage layer, if any - QgsVectorLayer *coverageLayer = atlasCoverageLayer(); - - QgsFillSymbol *newSymbol = mComposerShape->shapeStyleSymbol()->clone(); - QgsExpressionContext context = mComposerShape->createExpressionContext(); - - QgsSymbolSelectorWidget *d = new QgsSymbolSelectorWidget( newSymbol, QgsStyle::defaultStyle(), coverageLayer, nullptr ); - QgsSymbolWidgetContext symbolContext; - symbolContext.setExpressionContext( &context ); - d->setContext( symbolContext ); - - connect( d, &QgsPanelWidget::widgetChanged, this, &QgsComposerShapeWidget::updateSymbolFromWidget ); - connect( d, &QgsPanelWidget::panelAccepted, this, &QgsComposerShapeWidget::cleanUpSymbolSelector ); - openPanel( d ); mComposerShape->beginCommand( tr( "Shape style changed" ) ); -} - -void QgsComposerShapeWidget::updateShapeStyle() -{ - if ( mComposerShape ) - { - mComposerShape->refreshSymbol(); - QIcon icon = QgsSymbolLayerUtils::symbolPreviewIcon( mComposerShape->shapeStyleSymbol(), mShapeStyleButton->iconSize() ); - mShapeStyleButton->setIcon( icon ); - } + mComposerShape->setShapeStyleSymbol( mShapeStyleButton->clonedSymbol() ); + mComposerShape->endCommand(); } void QgsComposerShapeWidget::on_mCornerRadiusSpinBox_valueChanged( double val ) @@ -179,23 +160,3 @@ void QgsComposerShapeWidget::toggleRadiusSpin( const QString &shapeText ) mCornerRadiusSpinBox->setEnabled( false ); } } - -void QgsComposerShapeWidget::updateSymbolFromWidget() -{ - if ( QgsSymbolSelectorWidget *w = qobject_cast( sender() ) ) - mComposerShape->setShapeStyleSymbol( static_cast< QgsFillSymbol * >( w->symbol() ) ); -} - -void QgsComposerShapeWidget::cleanUpSymbolSelector( QgsPanelWidget *container ) -{ - QgsSymbolSelectorWidget *w = qobject_cast( container ); - if ( !w ) - return; - - delete w->symbol(); - updateShapeStyle(); - mComposerShape->endCommand(); -} - - - diff --git a/src/app/composer/qgscomposershapewidget.h b/src/app/composer/qgscomposershapewidget.h index 8aa62b8c73f..21aaccefdd7 100644 --- a/src/app/composer/qgscomposershapewidget.h +++ b/src/app/composer/qgscomposershapewidget.h @@ -40,17 +40,13 @@ class QgsComposerShapeWidget: public QgsComposerItemBaseWidget, private Ui::QgsC private slots: void on_mShapeComboBox_currentIndexChanged( const QString &text ); void on_mCornerRadiusSpinBox_valueChanged( double val ); - void on_mShapeStyleButton_clicked(); + void symbolChanged(); //! Sets the GUI elements to the currentValues of mComposerShape void setGuiElementValues(); - void updateShapeStyle(); - //! Enables or disables the rounded radius spin box based on shape type void toggleRadiusSpin( const QString &shapeText ); - void updateSymbolFromWidget(); - void cleanUpSymbolSelector( QgsPanelWidget *container ); }; #endif // QGSCOMPOSERSHAPEWIDGET_H diff --git a/src/gui/qgssymbolbutton.h b/src/gui/qgssymbolbutton.h index 972de290a0b..8df81616981 100644 --- a/src/gui/qgssymbolbutton.h +++ b/src/gui/qgssymbolbutton.h @@ -72,6 +72,28 @@ class GUI_EXPORT QgsSymbolButton : public QToolButton */ QgsSymbol *symbol(); + /** + * Returns a clone of the current symbol (as the specified template type) defined by the button. + * \see setSymbol() + * \see changed() + * \note Not available in Python bindings. + */ + template SymbolType *clonedSymbol() SIP_SKIP + { + QgsSymbol *tmpSymbol = mSymbol.get(); + SymbolType *symbolCastToType = dynamic_cast( tmpSymbol ); + + if ( symbolCastToType ) + { + return symbolCastToType->clone(); + } + else + { + //could not cast + return nullptr; + } + } + /** * Returns the map canvas associated with the widget. * \see setMapCanvas() diff --git a/src/gui/symbology-ng/qgspointclusterrendererwidget.cpp b/src/gui/symbology-ng/qgspointclusterrendererwidget.cpp index 350f670e70c..0a91a9a5700 100644 --- a/src/gui/symbology-ng/qgspointclusterrendererwidget.cpp +++ b/src/gui/symbology-ng/qgspointclusterrendererwidget.cpp @@ -79,6 +79,7 @@ QgsPointClusterRendererWidget::QgsPointClusterRendererWidget( QgsVectorLayer *la mDistanceSpinBox->setValue( mRenderer->tolerance() ); mDistanceUnitWidget->setUnit( mRenderer->toleranceUnit() ); mDistanceUnitWidget->setMapUnitScale( mRenderer->toleranceMapUnitScale() ); + mCenterSymbolToolButton->setSymbol( mRenderer->clusterSymbol()->clone() ); blockAllSignals( false ); @@ -94,7 +95,10 @@ QgsPointClusterRendererWidget::QgsPointClusterRendererWidget( QgsVectorLayer *la } } - updateCenterIcon(); + connect( mCenterSymbolToolButton, &QgsSymbolButton::changed, this, &QgsPointClusterRendererWidget::centerSymbolChanged ); + mCenterSymbolToolButton->setDialogTitle( tr( "Cluster symbol" ) ); + mCenterSymbolToolButton->setLayer( mLayer ); + mCenterSymbolToolButton->registerExpressionContextGenerator( this ); } QgsPointClusterRendererWidget::~QgsPointClusterRendererWidget() @@ -112,6 +116,8 @@ void QgsPointClusterRendererWidget::setContext( const QgsSymbolWidgetContext &co QgsRendererWidget::setContext( context ); if ( mDistanceUnitWidget ) mDistanceUnitWidget->setMapCanvas( context.mapCanvas() ); + if ( mCenterSymbolToolButton ) + mCenterSymbolToolButton->setMapCanvas( context.mapCanvas() ); } void QgsPointClusterRendererWidget::on_mRendererComboBox_currentIndexChanged( int index ) @@ -175,53 +181,34 @@ void QgsPointClusterRendererWidget::on_mDistanceUnitWidget_changed() void QgsPointClusterRendererWidget::blockAllSignals( bool block ) { mRendererComboBox->blockSignals( block ); - mCenterSymbolPushButton->blockSignals( block ); + mCenterSymbolToolButton->blockSignals( block ); mDistanceSpinBox->blockSignals( block ); mDistanceUnitWidget->blockSignals( block ); } -void QgsPointClusterRendererWidget::on_mCenterSymbolPushButton_clicked() +QgsExpressionContext QgsPointClusterRendererWidget::createExpressionContext() const { - if ( !mRenderer || !mRenderer->clusterSymbol() ) - { - return; - } - QgsMarkerSymbol *markerSymbol = mRenderer->clusterSymbol()->clone(); - QgsSymbolSelectorWidget *dlg = new QgsSymbolSelectorWidget( markerSymbol, QgsStyle::defaultStyle(), mLayer, this ); - dlg->setPanelTitle( tr( "Cluster symbol" ) ); - dlg->setDockMode( this->dockMode() ); - - QgsSymbolWidgetContext context = mContext; + QgsExpressionContext context; + if ( mContext.expressionContext() ) + context = *mContext.expressionContext(); + else + context.appendScopes( mContext.globalProjectAtlasMapLayerScopes( mLayer ) ); QgsExpressionContextScope scope; scope.addVariable( QgsExpressionContextScope::StaticVariable( QgsExpressionContext::EXPR_CLUSTER_COLOR, "", true ) ); scope.addVariable( QgsExpressionContextScope::StaticVariable( QgsExpressionContext::EXPR_CLUSTER_SIZE, 0, true ) ); - QList< QgsExpressionContextScope > scopes = context.additionalExpressionContextScopes(); + QList< QgsExpressionContextScope > scopes = mContext.additionalExpressionContextScopes(); scopes << scope; - context.setAdditionalExpressionContextScopes( scopes ); - - dlg->setContext( context ); - - connect( dlg, &QgsPanelWidget::widgetChanged, this, &QgsPointClusterRendererWidget::updateCenterSymbolFromWidget ); - connect( dlg, &QgsPanelWidget::panelAccepted, this, &QgsPointClusterRendererWidget::cleanUpSymbolSelector ); - openPanel( dlg ); -} - -void QgsPointClusterRendererWidget::updateCenterSymbolFromWidget() -{ - QgsSymbolSelectorWidget *dlg = qobject_cast( sender() ); - QgsSymbol *symbol = dlg->symbol()->clone(); - mRenderer->setClusterSymbol( static_cast< QgsMarkerSymbol * >( symbol ) ); - updateCenterIcon(); - emit widgetChanged(); -} - -void QgsPointClusterRendererWidget::cleanUpSymbolSelector( QgsPanelWidget *container ) -{ - if ( container ) + Q_FOREACH ( const QgsExpressionContextScope &s, scopes ) { - QgsSymbolSelectorWidget *dlg = qobject_cast( container ); - delete dlg->symbol(); + context << new QgsExpressionContextScope( s ); } + return context; +} + +void QgsPointClusterRendererWidget::centerSymbolChanged() +{ + mRenderer->setClusterSymbol( mCenterSymbolToolButton->clonedSymbol< QgsMarkerSymbol >() ); + emit widgetChanged(); } void QgsPointClusterRendererWidget::updateRendererFromWidget() @@ -234,17 +221,6 @@ void QgsPointClusterRendererWidget::updateRendererFromWidget() emit widgetChanged(); } -void QgsPointClusterRendererWidget::updateCenterIcon() -{ - QgsMarkerSymbol *symbol = mRenderer->clusterSymbol(); - if ( !symbol ) - { - return; - } - QIcon icon = QgsSymbolLayerUtils::symbolPreviewIcon( symbol, mCenterSymbolPushButton->iconSize() ); - mCenterSymbolPushButton->setIcon( icon ); -} - void QgsPointClusterRendererWidget::setupBlankUi( const QString &layerName ) { QGridLayout *layout = new QGridLayout( this ); diff --git a/src/gui/symbology-ng/qgspointclusterrendererwidget.h b/src/gui/symbology-ng/qgspointclusterrendererwidget.h index fa3207e30e3..4d11872165d 100644 --- a/src/gui/symbology-ng/qgspointclusterrendererwidget.h +++ b/src/gui/symbology-ng/qgspointclusterrendererwidget.h @@ -21,6 +21,7 @@ #include "ui_qgspointclusterrendererwidgetbase.h" #include "qgis.h" #include "qgsrendererwidget.h" +#include "qgsexpressioncontextgenerator.h" #include "qgis_gui.h" class QgsPointClusterRenderer; @@ -31,9 +32,10 @@ class QgsPointClusterRenderer; * \since QGIS 3.0 */ -class GUI_EXPORT QgsPointClusterRendererWidget: public QgsRendererWidget, private Ui::QgsPointClusterRendererWidgetBase +class GUI_EXPORT QgsPointClusterRendererWidget: public QgsRendererWidget, public QgsExpressionContextGenerator, private Ui::QgsPointClusterRendererWidgetBase { Q_OBJECT + public: /** Returns a new QgsPointClusterRendererWidget. @@ -56,11 +58,12 @@ class GUI_EXPORT QgsPointClusterRendererWidget: public QgsRendererWidget, privat QgsFeatureRenderer *renderer() override; void setContext( const QgsSymbolWidgetContext &context ) override; + QgsExpressionContext createExpressionContext() const override; + private: QgsPointClusterRenderer *mRenderer = nullptr; void blockAllSignals( bool block ); - void updateCenterIcon(); void setupBlankUi( const QString &layerName ); private slots: @@ -68,10 +71,8 @@ class GUI_EXPORT QgsPointClusterRendererWidget: public QgsRendererWidget, privat void on_mRendererComboBox_currentIndexChanged( int index ); void on_mDistanceSpinBox_valueChanged( double d ); void on_mDistanceUnitWidget_changed(); - void on_mCenterSymbolPushButton_clicked(); void on_mRendererSettingsButton_clicked(); - void updateCenterSymbolFromWidget(); - void cleanUpSymbolSelector( QgsPanelWidget *container ); + void centerSymbolChanged(); void updateRendererFromWidget(); }; diff --git a/src/gui/symbology-ng/qgspointdisplacementrendererwidget.cpp b/src/gui/symbology-ng/qgspointdisplacementrendererwidget.cpp index 8e5a771acba..c457a02c17a 100644 --- a/src/gui/symbology-ng/qgspointdisplacementrendererwidget.cpp +++ b/src/gui/symbology-ng/qgspointdisplacementrendererwidget.cpp @@ -118,6 +118,7 @@ QgsPointDisplacementRendererWidget::QgsPointDisplacementRendererWidget( QgsVecto mDistanceSpinBox->setValue( mRenderer->tolerance() ); mDistanceUnitWidget->setUnit( mRenderer->toleranceUnit() ); mDistanceUnitWidget->setMapUnitScale( mRenderer->toleranceMapUnitScale() ); + mCenterSymbolToolButton->setSymbol( mRenderer->centerSymbol()->clone() ); mPlacementComboBox->setCurrentIndex( mPlacementComboBox->findData( mRenderer->placement() ) ); @@ -150,8 +151,10 @@ QgsPointDisplacementRendererWidget::QgsPointDisplacementRendererWidget( QgsVecto connect( mMinLabelScaleWidget, &QgsScaleWidget::scaleChanged, this, &QgsPointDisplacementRendererWidget::minLabelScaleChanged ); connect( mLabelFontButton, &QgsFontButton::changed, this, &QgsPointDisplacementRendererWidget::labelFontChanged ); - - updateCenterIcon(); + connect( mCenterSymbolToolButton, &QgsSymbolButton::changed, this, &QgsPointDisplacementRendererWidget::centerSymbolChanged ); + mCenterSymbolToolButton->setDialogTitle( tr( "Center symbol" ) ); + mCenterSymbolToolButton->setLayer( mLayer ); + mCenterSymbolToolButton->registerExpressionContextGenerator( this ); } QgsPointDisplacementRendererWidget::~QgsPointDisplacementRendererWidget() @@ -174,6 +177,29 @@ void QgsPointDisplacementRendererWidget::setContext( const QgsSymbolWidgetContex mMinLabelScaleWidget->setMapCanvas( context.mapCanvas() ); mMinLabelScaleWidget->setShowCurrentScaleButton( true ); } + if ( mCenterSymbolToolButton ) + { + mCenterSymbolToolButton->setMapCanvas( context.mapCanvas() ); + } +} + +QgsExpressionContext QgsPointDisplacementRendererWidget::createExpressionContext() const +{ + QgsExpressionContext context; + if ( mContext.expressionContext() ) + context = *mContext.expressionContext(); + else + context.appendScopes( mContext.globalProjectAtlasMapLayerScopes( mLayer ) ); + QgsExpressionContextScope scope; + scope.addVariable( QgsExpressionContextScope::StaticVariable( QgsExpressionContext::EXPR_CLUSTER_COLOR, "", true ) ); + scope.addVariable( QgsExpressionContextScope::StaticVariable( QgsExpressionContext::EXPR_CLUSTER_SIZE, 0, true ) ); + QList< QgsExpressionContextScope > scopes = mContext.additionalExpressionContextScopes(); + scopes << scope; + Q_FOREACH ( const QgsExpressionContextScope &s, scopes ) + { + context << new QgsExpressionContextScope( s ); + } + return context; } void QgsPointDisplacementRendererWidget::on_mLabelFieldComboBox_currentIndexChanged( const QString &text ) @@ -348,55 +374,18 @@ void QgsPointDisplacementRendererWidget::blockAllSignals( bool block ) mCircleModificationSpinBox->blockSignals( block ); mScaleDependentLabelsCheckBox->blockSignals( block ); mMinLabelScaleWidget->blockSignals( block ); - mCenterSymbolPushButton->blockSignals( block ); + mCenterSymbolToolButton->blockSignals( block ); mDistanceSpinBox->blockSignals( block ); mDistanceUnitWidget->blockSignals( block ); mPlacementComboBox->blockSignals( block ); } -void QgsPointDisplacementRendererWidget::on_mCenterSymbolPushButton_clicked() +void QgsPointDisplacementRendererWidget::centerSymbolChanged() { - if ( !mRenderer || !mRenderer->centerSymbol() ) - { - return; - } - QgsMarkerSymbol *markerSymbol = mRenderer->centerSymbol()->clone(); - QgsSymbolSelectorWidget *dlg = new QgsSymbolSelectorWidget( markerSymbol, QgsStyle::defaultStyle(), mLayer, this ); - dlg->setPanelTitle( tr( "Center symbol" ) ); - - QgsSymbolWidgetContext context = mContext; - - QgsExpressionContextScope scope; - scope.addVariable( QgsExpressionContextScope::StaticVariable( QgsExpressionContext::EXPR_CLUSTER_COLOR, "", true ) ); - scope.addVariable( QgsExpressionContextScope::StaticVariable( QgsExpressionContext::EXPR_CLUSTER_SIZE, 0, true ) ); - QList< QgsExpressionContextScope > scopes = context.additionalExpressionContextScopes(); - scopes << scope; - context.setAdditionalExpressionContextScopes( scopes ); - dlg->setContext( context ); - - connect( dlg, &QgsPanelWidget::widgetChanged, this, &QgsPointDisplacementRendererWidget::updateCenterSymbolFromWidget ); - connect( dlg, &QgsPanelWidget::panelAccepted, this, &QgsPointDisplacementRendererWidget::cleanUpSymbolSelector ); - openPanel( dlg ); -} - -void QgsPointDisplacementRendererWidget::updateCenterSymbolFromWidget() -{ - QgsSymbolSelectorWidget *dlg = qobject_cast( sender() ); - QgsSymbol *symbol = dlg->symbol()->clone(); - mRenderer->setCenterSymbol( static_cast< QgsMarkerSymbol * >( symbol ) ); - updateCenterIcon(); + mRenderer->setCenterSymbol( mCenterSymbolToolButton->clonedSymbol< QgsMarkerSymbol >() ); emit widgetChanged(); } -void QgsPointDisplacementRendererWidget::cleanUpSymbolSelector( QgsPanelWidget *container ) -{ - if ( container ) - { - QgsSymbolSelectorWidget *dlg = qobject_cast( container ); - delete dlg->symbol(); - } -} - void QgsPointDisplacementRendererWidget::updateRendererFromWidget() { QgsRendererWidget *w = qobject_cast( sender() ); @@ -407,17 +396,6 @@ void QgsPointDisplacementRendererWidget::updateRendererFromWidget() emit widgetChanged(); } -void QgsPointDisplacementRendererWidget::updateCenterIcon() -{ - QgsMarkerSymbol *symbol = mRenderer->centerSymbol(); - if ( !symbol ) - { - return; - } - QIcon icon = QgsSymbolLayerUtils::symbolPreviewIcon( symbol, mCenterSymbolPushButton->iconSize() ); - mCenterSymbolPushButton->setIcon( icon ); -} - void QgsPointDisplacementRendererWidget::setupBlankUi( const QString &layerName ) { QLabel *label = new QLabel( tr( "The point displacement renderer only applies to (single) point layers. \n'%1' is not a point layer and cannot be displayed by the point displacement renderer" ).arg( layerName ), this ); diff --git a/src/gui/symbology-ng/qgspointdisplacementrendererwidget.h b/src/gui/symbology-ng/qgspointdisplacementrendererwidget.h index 498299a13a3..e339f97fc35 100644 --- a/src/gui/symbology-ng/qgspointdisplacementrendererwidget.h +++ b/src/gui/symbology-ng/qgspointdisplacementrendererwidget.h @@ -21,6 +21,7 @@ #include "ui_qgspointdisplacementrendererwidgetbase.h" #include "qgis.h" #include "qgsrendererwidget.h" +#include "qgsexpressioncontextgenerator.h" #include "qgis_gui.h" class QgsPointDisplacementRenderer; @@ -28,7 +29,7 @@ class QgsPointDisplacementRenderer; /** \ingroup gui * \class QgsPointDisplacementRendererWidget */ -class GUI_EXPORT QgsPointDisplacementRendererWidget: public QgsRendererWidget, private Ui::QgsPointDisplacementRendererWidgetBase +class GUI_EXPORT QgsPointDisplacementRendererWidget: public QgsRendererWidget, public QgsExpressionContextGenerator, private Ui::QgsPointDisplacementRendererWidgetBase { Q_OBJECT public: @@ -39,11 +40,12 @@ class GUI_EXPORT QgsPointDisplacementRendererWidget: public QgsRendererWidget, p QgsFeatureRenderer *renderer() override; void setContext( const QgsSymbolWidgetContext &context ) override; + QgsExpressionContext createExpressionContext() const override; + private: QgsPointDisplacementRenderer *mRenderer = nullptr; void blockAllSignals( bool block ); - void updateCenterIcon(); void setupBlankUi( const QString &layerName ); private slots: @@ -59,10 +61,8 @@ class GUI_EXPORT QgsPointDisplacementRendererWidget: public QgsRendererWidget, p void on_mCircleModificationSpinBox_valueChanged( double d ); void on_mScaleDependentLabelsCheckBox_stateChanged( int state ); void minLabelScaleChanged( double scale ); - void on_mCenterSymbolPushButton_clicked(); void on_mRendererSettingsButton_clicked(); - void updateCenterSymbolFromWidget(); - void cleanUpSymbolSelector( QgsPanelWidget *container ); + void centerSymbolChanged(); void updateRendererFromWidget(); }; diff --git a/src/ui/composer/qgscomposershapewidgetbase.ui b/src/ui/composer/qgscomposershapewidgetbase.ui index b6f1428600b..442c4e4b255 100644 --- a/src/ui/composer/qgscomposershapewidgetbase.ui +++ b/src/ui/composer/qgscomposershapewidgetbase.ui @@ -96,8 +96,11 @@ + + + - + 0 @@ -109,9 +112,6 @@ - - - @@ -122,6 +122,11 @@ + + QgsDoubleSpinBox + QDoubleSpinBox +
qgsdoublespinbox.h
+
QgsScrollArea QScrollArea @@ -135,9 +140,9 @@ 1 - QgsDoubleSpinBox - QDoubleSpinBox -
qgsdoublespinbox.h
+ QgsSymbolButton + QToolButton +
qgssymbolbutton.h
diff --git a/src/ui/composer/qgscompositionwidgetbase.ui b/src/ui/composer/qgscompositionwidgetbase.ui index a366e3d94eb..98fffd9d545 100644 --- a/src/ui/composer/qgscompositionwidgetbase.ui +++ b/src/ui/composer/qgscompositionwidgetbase.ui @@ -663,6 +663,11 @@ + + QgsDoubleSpinBox + QDoubleSpinBox +
qgsdoublespinbox.h
+
QgsScrollArea QScrollArea @@ -681,11 +686,6 @@
qgscollapsiblegroupbox.h
1
- - QgsDoubleSpinBox - QDoubleSpinBox -
qgsdoublespinbox.h
-
QgsSpinBox QSpinBox diff --git a/src/ui/qgspointdisplacementrendererwidgetbase.ui b/src/ui/qgspointdisplacementrendererwidgetbase.ui index 2f5e5ab5bee..07fd8057935 100755 --- a/src/ui/qgspointdisplacementrendererwidgetbase.ui +++ b/src/ui/qgspointdisplacementrendererwidgetbase.ui @@ -136,13 +136,6 @@ - - - - - - - @@ -272,14 +265,26 @@ + + + + + 0 + 0 + + + + + + + - QgsColorButton - QToolButton -
qgscolorbutton.h
- 1 + QgsDoubleSpinBox + QDoubleSpinBox +
qgsdoublespinbox.h
QgsCollapsibleGroupBoxBasic @@ -288,9 +293,15 @@ 1 - QgsDoubleSpinBox - QDoubleSpinBox -
qgsdoublespinbox.h
+ QgsSymbolButton + QToolButton +
qgssymbolbutton.h
+
+ + QgsColorButton + QToolButton +
qgscolorbutton.h
+ 1
QgsFontButton @@ -310,7 +321,7 @@
- mCenterSymbolPushButton + mCenterSymbolToolButton mRendererComboBox mRendererSettingsButton mDistanceSpinBox diff --git a/src/ui/symbollayer/qgspointclusterrendererwidgetbase.ui b/src/ui/symbollayer/qgspointclusterrendererwidgetbase.ui index e93debe9256..650b909e953 100644 --- a/src/ui/symbollayer/qgspointclusterrendererwidgetbase.ui +++ b/src/ui/symbollayer/qgspointclusterrendererwidgetbase.ui @@ -26,13 +26,6 @@ 0 - - - - - - - @@ -98,6 +91,19 @@ + + + + + 0 + 0 + + + + + + + @@ -106,6 +112,11 @@ QDoubleSpinBox
qgsdoublespinbox.h
+ + QgsSymbolButton + QToolButton +
qgssymbolbutton.h
+
QgsUnitSelectionWidget QWidget @@ -114,7 +125,7 @@
- mCenterSymbolPushButton + mCenterSymbolToolButton mRendererComboBox mRendererSettingsButton mDistanceSpinBox From 041479478756f4bb1f129a9ba552312e8bb2cb5e Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Sun, 23 Jul 2017 14:58:52 +1000 Subject: [PATCH 03/11] Use symbol button in annotation config widgets --- src/app/qgsannotationwidget.cpp | 71 ++--------------------------- src/app/qgsannotationwidget.h | 6 --- src/app/qgsformannotationdialog.cpp | 1 + src/app/qgshtmlannotationdialog.cpp | 1 + src/app/qgssvgannotationdialog.cpp | 1 + src/ui/qgsannotationwidgetbase.ui | 21 ++++++++- 6 files changed, 26 insertions(+), 75 deletions(-) diff --git a/src/app/qgsannotationwidget.cpp b/src/app/qgsannotationwidget.cpp index 04071d1c1c0..98363a5b4b9 100644 --- a/src/app/qgsannotationwidget.cpp +++ b/src/app/qgsannotationwidget.cpp @@ -28,7 +28,6 @@ QgsAnnotationWidget::QgsAnnotationWidget( QgsMapCanvasAnnotationItem *item, QWidget *parent, Qt::WindowFlags f ) : QWidget( parent, f ) , mItem( item ) - , mMarkerSymbol( nullptr ) { setupUi( this ); mLayerComboBox->setAllowEmptyLayer( true ); @@ -57,14 +56,12 @@ QgsAnnotationWidget::QgsAnnotationWidget( QgsMapCanvasAnnotationItem *item, QWid const QgsMarkerSymbol *symbol = annotation->markerSymbol(); if ( symbol ) { - mMarkerSymbol.reset( symbol->clone() ); - updateCenterIcon(); + mMapMarkerButton->setSymbol( symbol->clone() ); } const QgsFillSymbol *fill = annotation->fillSymbol(); if ( fill ) { - mFillSymbol.reset( fill->clone() ); - updateFillIcon(); + mFrameStyleButton->setSymbol( fill->clone() ); } blockAllSignals( false ); @@ -83,8 +80,8 @@ void QgsAnnotationWidget::apply() if ( annotation ) { annotation->setHasFixedMapPosition( mMapPositionFixedCheckBox->checkState() == Qt::Checked ); - annotation->setFillSymbol( mFillSymbol->clone() ); - annotation->setMarkerSymbol( mMarkerSymbol->clone() ); + annotation->setFillSymbol( mFrameStyleButton->clonedSymbol() ); + annotation->setMarkerSymbol( mMapMarkerButton->clonedSymbol() ); annotation->setMapLayer( mLayerComboBox->currentLayer() ); annotation->setContentsMargin( QgsMargins( mSpinLeftMargin->value(), mSpinTopMargin->value(), @@ -101,63 +98,3 @@ void QgsAnnotationWidget::blockAllSignals( bool block ) mMapMarkerButton->blockSignals( block ); mLayerComboBox->blockSignals( block ); } - -void QgsAnnotationWidget::on_mMapMarkerButton_clicked() -{ - if ( !mMarkerSymbol ) - { - return; - } - QgsMarkerSymbol *markerSymbol = mMarkerSymbol->clone(); - QgsSymbolSelectorDialog dlg( markerSymbol, QgsStyle::defaultStyle(), nullptr, this ); - if ( dlg.exec() == QDialog::Rejected ) - { - delete markerSymbol; - } - else - { - mMarkerSymbol.reset( markerSymbol ); - updateCenterIcon(); - } -} - -void QgsAnnotationWidget::on_mFrameStyleButton_clicked() -{ - if ( !mFillSymbol ) - { - return; - } - QgsFillSymbol *fillSymbol = mFillSymbol->clone(); - QgsSymbolSelectorDialog dlg( fillSymbol, QgsStyle::defaultStyle(), nullptr, this ); - if ( dlg.exec() == QDialog::Rejected ) - { - delete fillSymbol; - } - else - { - mFillSymbol.reset( fillSymbol ); - updateFillIcon(); - backgroundColorChanged( fillSymbol->color() ); - } -} - -void QgsAnnotationWidget::updateCenterIcon() -{ - if ( !mMarkerSymbol ) - { - return; - } - QIcon icon = QgsSymbolLayerUtils::symbolPreviewIcon( mMarkerSymbol.get(), mMapMarkerButton->iconSize() ); - mMapMarkerButton->setIcon( icon ); -} - -void QgsAnnotationWidget::updateFillIcon() -{ - if ( !mFillSymbol ) - { - return; - } - QIcon icon = QgsSymbolLayerUtils::symbolPreviewIcon( mFillSymbol.get(), mFrameStyleButton->iconSize() ); - mFrameStyleButton->setIcon( icon ); -} - diff --git a/src/app/qgsannotationwidget.h b/src/app/qgsannotationwidget.h index 52ccb1808b1..2e499165ce3 100644 --- a/src/app/qgsannotationwidget.h +++ b/src/app/qgsannotationwidget.h @@ -42,14 +42,8 @@ class APP_EXPORT QgsAnnotationWidget: public QWidget, private Ui::QgsAnnotationW //! Emitted when the background color of the annotation is changed void backgroundColorChanged( const QColor &color ); - private slots: - void on_mMapMarkerButton_clicked(); - void on_mFrameStyleButton_clicked(); - private: QgsMapCanvasAnnotationItem *mItem = nullptr; - std::unique_ptr< QgsMarkerSymbol > mMarkerSymbol; - std::unique_ptr< QgsFillSymbol > mFillSymbol; void blockAllSignals( bool block ); void updateCenterIcon(); diff --git a/src/app/qgsformannotationdialog.cpp b/src/app/qgsformannotationdialog.cpp index 90ee0b3f2e9..e292d166b01 100644 --- a/src/app/qgsformannotationdialog.cpp +++ b/src/app/qgsformannotationdialog.cpp @@ -22,6 +22,7 @@ #include #include #include +#include QgsFormAnnotationDialog::QgsFormAnnotationDialog( QgsMapCanvasAnnotationItem *item, QWidget *parent, Qt::WindowFlags f ) : QDialog( parent, f ) diff --git a/src/app/qgshtmlannotationdialog.cpp b/src/app/qgshtmlannotationdialog.cpp index ccad90fdddb..7c1f4488600 100644 --- a/src/app/qgshtmlannotationdialog.cpp +++ b/src/app/qgshtmlannotationdialog.cpp @@ -22,6 +22,7 @@ #include #include #include +#include QgsHtmlAnnotationDialog::QgsHtmlAnnotationDialog( QgsMapCanvasAnnotationItem *item, QWidget *parent, Qt::WindowFlags f ) : QDialog( parent, f ) diff --git a/src/app/qgssvgannotationdialog.cpp b/src/app/qgssvgannotationdialog.cpp index 27ad40e1452..89bad2b72d4 100644 --- a/src/app/qgssvgannotationdialog.cpp +++ b/src/app/qgssvgannotationdialog.cpp @@ -24,6 +24,7 @@ #include #include #include +#include QgsSvgAnnotationDialog::QgsSvgAnnotationDialog( QgsMapCanvasAnnotationItem *item, QWidget *parent, Qt::WindowFlags f ) : QDialog( parent, f ) diff --git a/src/ui/qgsannotationwidgetbase.ui b/src/ui/qgsannotationwidgetbase.ui index 0c512f1385d..002b7b5dff9 100644 --- a/src/ui/qgsannotationwidgetbase.ui +++ b/src/ui/qgsannotationwidgetbase.ui @@ -15,7 +15,13 @@ - + + + + 0 + 0 + + @@ -206,7 +212,13 @@ - + + + + 0 + 0 + + @@ -241,6 +253,11 @@
qgscollapsiblegroupbox.h
1 + + QgsSymbolButton + QToolButton +
qgssymbolbutton.h
+
mMapPositionFixedCheckBox From 6398b74426527754822c5ee634d53e1cfa4db090 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Sun, 23 Jul 2017 17:35:55 +1000 Subject: [PATCH 04/11] Add opacity slider to symbol button menu --- src/gui/qgssymbolbutton.cpp | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/gui/qgssymbolbutton.cpp b/src/gui/qgssymbolbutton.cpp index b658f5b2f3a..3a640b8742e 100644 --- a/src/gui/qgssymbolbutton.cpp +++ b/src/gui/qgssymbolbutton.cpp @@ -244,11 +244,6 @@ void QgsSymbolButton::dropEvent( QDropEvent *e ) { //accept drop and set new color e->acceptProposedAction(); - - if ( hasAlpha ) - { - mSymbol->setOpacity( mimeColor.alphaF() ); - } mimeColor.setAlphaF( 1.0 ); mSymbol->setColor( mimeColor ); QgsRecentColorScheme::addRecentColor( mimeColor ); @@ -279,6 +274,23 @@ void QgsSymbolButton::prepareMenu() connect( colorAction, &QgsColorWidgetAction::colorChanged, this, &QgsSymbolButton::setColor ); mMenu->addAction( colorAction ); + QgsColorRampWidget *alphaRamp = new QgsColorRampWidget( mMenu, QgsColorWidget::Alpha, QgsColorRampWidget::Horizontal ); + QColor alphaColor = mSymbol->color(); + alphaColor.setAlphaF( mSymbol->opacity() ); + alphaRamp->setColor( alphaColor ); + QgsColorWidgetAction *alphaAction = new QgsColorWidgetAction( alphaRamp, mMenu, mMenu ); + alphaAction->setDismissOnColorSelection( false ); + connect( alphaAction, &QgsColorWidgetAction::colorChanged, this, [ = ]( const QColor & color ) + { + double opacity = color.alphaF(); + mSymbol->setOpacity( opacity ); + updatePreview(); + emit changed(); + } ); + connect( colorAction, &QgsColorWidgetAction::colorChanged, alphaRamp, [alphaRamp]( const QColor & color ) { alphaRamp->setColor( color, false ); } + ); + mMenu->addAction( alphaAction ); + //get schemes with ShowInColorButtonMenu flag set QList< QgsColorScheme * > schemeList = QgsApplication::colorSchemeRegistry()->schemes( QgsColorScheme::ShowInColorButtonMenu ); QList< QgsColorScheme * >::iterator it = schemeList.begin(); From 67b272406eddf0dbae4475b79dfee5cb280c8b13 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Sun, 23 Jul 2017 17:50:33 +1000 Subject: [PATCH 05/11] Port decoration buttons to QgsSymbolButton --- src/app/qgsannotationwidget.cpp | 3 + src/app/qgsdecorationgriddialog.cpp | 85 ++--------------- src/app/qgsdecorationgriddialog.h | 4 - src/app/qgsdecorationlayoutextentdialog.cpp | 39 +------- src/app/qgsdecorationlayoutextentdialog.h | 2 - .../qgsnullsymbolrendererwidget.h | 1 - src/gui/symbology-ng/qgsrendererwidget.h | 1 - src/ui/qgsdecorationgriddialog.ui | 93 ++++++++++--------- src/ui/qgsdecorationlayoutextentdialog.ui | 51 +++++----- 9 files changed, 90 insertions(+), 189 deletions(-) diff --git a/src/app/qgsannotationwidget.cpp b/src/app/qgsannotationwidget.cpp index 98363a5b4b9..95014b8d095 100644 --- a/src/app/qgsannotationwidget.cpp +++ b/src/app/qgsannotationwidget.cpp @@ -22,6 +22,7 @@ #include "qgssymbollayerutils.h" #include "qgssymbol.h" #include "qgssymbolselectordialog.h" +#include "qgisapp.h" #include @@ -66,6 +67,8 @@ QgsAnnotationWidget::QgsAnnotationWidget( QgsMapCanvasAnnotationItem *item, QWid blockAllSignals( false ); } + mMapMarkerButton->setMapCanvas( QgisApp::instance()->mapCanvas() ); + mFrameStyleButton->setMapCanvas( QgisApp::instance()->mapCanvas() ); } QgsAnnotationWidget::~QgsAnnotationWidget() diff --git a/src/app/qgsdecorationgriddialog.cpp b/src/app/qgsdecorationgriddialog.cpp index 5aa8e256aad..070c6094956 100644 --- a/src/app/qgsdecorationgriddialog.cpp +++ b/src/app/qgsdecorationgriddialog.cpp @@ -31,8 +31,6 @@ QgsDecorationGridDialog::QgsDecorationGridDialog( QgsDecorationGrid &deco, QWidget *parent ) : QDialog( parent ) , mDeco( deco ) - , mLineSymbol( nullptr ) - , mMarkerSymbol( nullptr ) { setupUi( this ); @@ -65,6 +63,8 @@ QgsDecorationGridDialog::QgsDecorationGridDialog( QgsDecorationGrid &deco, QWidg connect( buttonBox->button( QDialogButtonBox::Apply ), &QAbstractButton::clicked, this, &QgsDecorationGridDialog::apply ); connect( mAnnotationFontButton, &QgsFontButton::changed, this, &QgsDecorationGridDialog::annotationFontChanged ); + + mMarkerSymbolButton->setMapCanvas( QgisApp::instance()->mapCanvas() ); } void QgsDecorationGridDialog::updateGuiElements() @@ -88,22 +88,8 @@ void QgsDecorationGridDialog::updateGuiElements() // mLineWidthSpinBox->setValue( gridPen.widthF() ); // mLineColorButton->setColor( gridPen.color() ); - if ( mLineSymbol ) - delete mLineSymbol; - if ( mDeco.lineSymbol() ) - { - mLineSymbol = static_cast( mDeco.lineSymbol()->clone() ); - QIcon icon = QgsSymbolLayerUtils::symbolPreviewIcon( mLineSymbol, mLineSymbolButton->iconSize() ); - mLineSymbolButton->setIcon( icon ); - } - if ( mMarkerSymbol ) - delete mMarkerSymbol; - if ( mDeco.markerSymbol() ) - { - mMarkerSymbol = static_cast( mDeco.markerSymbol()->clone() ); - QIcon icon = QgsSymbolLayerUtils::symbolPreviewIcon( mMarkerSymbol, mMarkerSymbolButton->iconSize() ); - mMarkerSymbolButton->setIcon( icon ); - } + mLineSymbolButton->setSymbol( mDeco.lineSymbol()->clone() ); + mMarkerSymbolButton->setSymbol( mDeco.markerSymbol()->clone() ); whileBlocking( mAnnotationFontButton )->setCurrentFont( mDeco.gridAnnotationFont() ); @@ -157,26 +143,14 @@ void QgsDecorationGridDialog::updateDecoFromGui() mDeco.setGridAnnotationDirection( QgsDecorationGrid::BoundaryDirection ); } mDeco.setGridAnnotationPrecision( mCoordinatePrecisionSpinBox->value() ); - if ( mLineSymbol ) - { - mDeco.setLineSymbol( mLineSymbol ); - mLineSymbol = mDeco.lineSymbol()->clone(); - } - if ( mMarkerSymbol ) - { - mDeco.setMarkerSymbol( mMarkerSymbol ); - mMarkerSymbol = mDeco.markerSymbol()->clone(); - } + mDeco.setLineSymbol( mLineSymbolButton->clonedSymbol< QgsLineSymbol >() ); + mDeco.setMarkerSymbol( mMarkerSymbolButton->clonedSymbol< QgsMarkerSymbol >() ); } QgsDecorationGridDialog::~QgsDecorationGridDialog() { QgsSettings settings; settings.setValue( QStringLiteral( "/Windows/DecorationGrid/geometry" ), saveGeometry() ); - if ( mLineSymbol ) - delete mLineSymbol; - if ( mMarkerSymbol ) - delete mMarkerSymbol; } void QgsDecorationGridDialog::on_buttonBox_helpRequested() @@ -210,53 +184,6 @@ void QgsDecorationGridDialog::on_mGridTypeComboBox_currentIndexChanged( int inde mMarkerSymbolButton->setEnabled( index == QgsDecorationGrid::Marker ); } - -void QgsDecorationGridDialog::on_mLineSymbolButton_clicked() -{ - if ( ! mLineSymbol ) - return; - - QgsLineSymbol *lineSymbol = mLineSymbol->clone(); - QgsSymbolSelectorDialog dlg( lineSymbol, QgsStyle::defaultStyle(), nullptr, this ); - if ( dlg.exec() == QDialog::Rejected ) - { - delete lineSymbol; - } - else - { - delete mLineSymbol; - mLineSymbol = lineSymbol; - if ( mLineSymbol ) - { - QIcon icon = QgsSymbolLayerUtils::symbolPreviewIcon( mLineSymbol, mLineSymbolButton->iconSize() ); - mLineSymbolButton->setIcon( icon ); - } - } -} - -void QgsDecorationGridDialog::on_mMarkerSymbolButton_clicked() -{ - if ( ! mMarkerSymbol ) - return; - - QgsMarkerSymbol *markerSymbol = mMarkerSymbol->clone(); - QgsSymbolSelectorDialog dlg( markerSymbol, QgsStyle::defaultStyle(), nullptr, this ); - if ( dlg.exec() == QDialog::Rejected ) - { - delete markerSymbol; - } - else - { - delete mMarkerSymbol; - mMarkerSymbol = markerSymbol; - if ( mMarkerSymbol ) - { - QIcon icon = QgsSymbolLayerUtils::symbolPreviewIcon( mMarkerSymbol, mMarkerSymbolButton->iconSize() ); - mMarkerSymbolButton->setIcon( icon ); - } - } -} - void QgsDecorationGridDialog::on_mPbtnUpdateFromExtents_clicked() { updateInterval( true ); diff --git a/src/app/qgsdecorationgriddialog.h b/src/app/qgsdecorationgriddialog.h index 510dea44276..5c81a4d6283 100644 --- a/src/app/qgsdecorationgriddialog.h +++ b/src/app/qgsdecorationgriddialog.h @@ -40,8 +40,6 @@ class APP_EXPORT QgsDecorationGridDialog : public QDialog, private Ui::QgsDecora void on_buttonBox_rejected(); void on_buttonBox_helpRequested(); void on_mGridTypeComboBox_currentIndexChanged( int index ); - void on_mLineSymbolButton_clicked(); - void on_mMarkerSymbolButton_clicked(); void on_mPbtnUpdateFromExtents_clicked(); void on_mPbtnUpdateFromLayer_clicked(); @@ -51,8 +49,6 @@ class APP_EXPORT QgsDecorationGridDialog : public QDialog, private Ui::QgsDecora private: QgsDecorationGrid &mDeco; - QgsLineSymbol *mLineSymbol = nullptr; - QgsMarkerSymbol *mMarkerSymbol = nullptr; void updateGuiElements(); void updateDecoFromGui(); diff --git a/src/app/qgsdecorationlayoutextentdialog.cpp b/src/app/qgsdecorationlayoutextentdialog.cpp index 29179e20a05..1db51351f41 100644 --- a/src/app/qgsdecorationlayoutextentdialog.cpp +++ b/src/app/qgsdecorationlayoutextentdialog.cpp @@ -44,19 +44,14 @@ QgsDecorationLayoutExtentDialog::QgsDecorationLayoutExtentDialog( QgsDecorationL updateGuiElements(); connect( buttonBox->button( QDialogButtonBox::Apply ), &QAbstractButton::clicked, this, &QgsDecorationLayoutExtentDialog::apply ); - connect( mSymbolButton, &QPushButton::clicked, this, &QgsDecorationLayoutExtentDialog::changeSymbol ); + + mSymbolButton->setMapCanvas( QgisApp::instance()->mapCanvas() ); } void QgsDecorationLayoutExtentDialog::updateGuiElements() { grpEnable->setChecked( mDeco.enabled() ); - - if ( mDeco.symbol() ) - { - mSymbol.reset( static_cast( mDeco.symbol()->clone() ) ); - QIcon icon = QgsSymbolLayerUtils::symbolPreviewIcon( mSymbol.get(), mSymbolButton->iconSize() ); - mSymbolButton->setIcon( icon ); - } + mSymbolButton->setSymbol( mDeco.symbol()->clone() ); mButtonFontStyle->setTextFormat( mDeco.textFormat() ); mCheckBoxLabelExtents->setChecked( mDeco.labelExtents() ); } @@ -64,11 +59,7 @@ void QgsDecorationLayoutExtentDialog::updateGuiElements() void QgsDecorationLayoutExtentDialog::updateDecoFromGui() { mDeco.setEnabled( grpEnable->isChecked() ); - - if ( mSymbol ) - { - mDeco.setSymbol( mSymbol->clone() ); - } + mDeco.setSymbol( mSymbolButton->clonedSymbol< QgsFillSymbol >() ); mDeco.setTextFormat( mButtonFontStyle->textFormat() ); mDeco.setLabelExtents( mCheckBoxLabelExtents->isChecked() ); } @@ -95,25 +86,3 @@ void QgsDecorationLayoutExtentDialog::on_buttonBox_rejected() { reject(); } - -void QgsDecorationLayoutExtentDialog::changeSymbol() -{ - if ( !mSymbol ) - return; - - QgsFillSymbol *symbol = mSymbol->clone(); - QgsSymbolSelectorDialog dlg( symbol, QgsStyle::defaultStyle(), nullptr, this ); - if ( dlg.exec() == QDialog::Rejected ) - { - delete symbol; - } - else - { - mSymbol.reset( symbol ); - if ( mSymbol ) - { - QIcon icon = QgsSymbolLayerUtils::symbolPreviewIcon( mSymbol.get(), mSymbolButton->iconSize() ); - mSymbolButton->setIcon( icon ); - } - } -} diff --git a/src/app/qgsdecorationlayoutextentdialog.h b/src/app/qgsdecorationlayoutextentdialog.h index 06505c62ec8..5b87ae9a8e6 100644 --- a/src/app/qgsdecorationlayoutextentdialog.h +++ b/src/app/qgsdecorationlayoutextentdialog.h @@ -39,11 +39,9 @@ class APP_EXPORT QgsDecorationLayoutExtentDialog : public QDialog, private Ui::Q void on_buttonBox_accepted(); void on_buttonBox_rejected(); - void changeSymbol(); private: QgsDecorationLayoutExtent &mDeco; - std::unique_ptr< QgsFillSymbol > mSymbol; void updateGuiElements(); void updateDecoFromGui(); diff --git a/src/gui/symbology-ng/qgsnullsymbolrendererwidget.h b/src/gui/symbology-ng/qgsnullsymbolrendererwidget.h index ec74027413f..4ce0e8bd04e 100644 --- a/src/gui/symbology-ng/qgsnullsymbolrendererwidget.h +++ b/src/gui/symbology-ng/qgsnullsymbolrendererwidget.h @@ -20,7 +20,6 @@ #include "qgis_gui.h" class QgsNullSymbolRenderer; -class QgsSymbolSelectorDialog; class QMenu; diff --git a/src/gui/symbology-ng/qgsrendererwidget.h b/src/gui/symbology-ng/qgsrendererwidget.h index e5a01683df9..bb82da9c3f3 100644 --- a/src/gui/symbology-ng/qgsrendererwidget.h +++ b/src/gui/symbology-ng/qgsrendererwidget.h @@ -28,7 +28,6 @@ class QgsDataDefinedSizeLegendWidget; class QgsVectorLayer; class QgsStyle; class QgsFeatureRenderer; -class QgsSymbolSelectorDialog; class QgsMapCanvas; /** \ingroup gui diff --git a/src/ui/qgsdecorationgriddialog.ui b/src/ui/qgsdecorationgriddialog.ui index b1fbe0fc3dc..6e6e2a4ecaa 100755 --- a/src/ui/qgsdecorationgriddialog.ui +++ b/src/ui/qgsdecorationgriddialog.ui @@ -180,28 +180,6 @@
- - - - false - - - - 0 - 0 - - - - - 100 - 0 - - - - - - - @@ -209,28 +187,6 @@ - - - - false - - - - 0 - 0 - - - - - 100 - 0 - - - - - - - @@ -300,6 +256,50 @@ + + + + false + + + + 0 + 0 + + + + + 100 + 0 + + + + + + + + + + + false + + + + 0 + 0 + + + + + 100 + 0 + + + + + + +
mOffsetYEdit mOffsetXEdit @@ -334,6 +334,11 @@ + + QgsSymbolButton + QToolButton +
qgssymbolbutton.h
+
QgsFontButton QToolButton diff --git a/src/ui/qgsdecorationlayoutextentdialog.ui b/src/ui/qgsdecorationlayoutextentdialog.ui index 62b00518e07..2dd8a18672c 100755 --- a/src/ui/qgsdecorationlayoutextentdialog.ui +++ b/src/ui/qgsdecorationlayoutextentdialog.ui @@ -62,28 +62,6 @@ - - - - false - - - - 0 - 0 - - - - - 100 - 0 - - - - - - - @@ -110,6 +88,28 @@ + + + + false + + + + 0 + 0 + + + + + 100 + 0 + + + + + + + @@ -126,6 +126,11 @@ + + QgsSymbolButton + QToolButton +
qgssymbolbutton.h
+
QgsFontButton QToolButton @@ -142,7 +147,7 @@ grpEnable toggled(bool) - mSymbolButton + mLineSymbolLabel setEnabled(bool) From f7ada8138ff91085127005f48be6f97f76f3e52c Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Sun, 23 Jul 2017 18:10:13 +1000 Subject: [PATCH 06/11] Port conditional formatting symbol button to QgsSymbolButton --- .../qgsfieldconditionalformatwidget.cpp | 37 +++---------------- .../qgsfieldconditionalformatwidget.h | 2 - src/ui/qgsfieldconditionalformatwidget.ui | 20 +++------- 3 files changed, 11 insertions(+), 48 deletions(-) diff --git a/src/gui/attributetable/qgsfieldconditionalformatwidget.cpp b/src/gui/attributetable/qgsfieldconditionalformatwidget.cpp index 0efe7ebfa9a..7c0a85cdf61 100644 --- a/src/gui/attributetable/qgsfieldconditionalformatwidget.cpp +++ b/src/gui/attributetable/qgsfieldconditionalformatwidget.cpp @@ -26,7 +26,6 @@ QgsFieldConditionalFormatWidget::QgsFieldConditionalFormatWidget( QWidget *paren , mLayer( nullptr ) , mEditIndex( 0 ) , mEditing( false ) - , mSymbol( nullptr ) { setupUi( this ); mDeleteButton->hide(); @@ -38,7 +37,6 @@ QgsFieldConditionalFormatWidget::QgsFieldConditionalFormatWidget( QWidget *paren connect( mCancelButton, &QAbstractButton::clicked, this, &QgsFieldConditionalFormatWidget::cancelRule ); connect( mDeleteButton, &QAbstractButton::clicked, this, &QgsFieldConditionalFormatWidget::deleteRule ); connect( listView, &QAbstractItemView::clicked, this, &QgsFieldConditionalFormatWidget::ruleClicked ); - connect( btnChangeIcon, &QAbstractButton::clicked, this, &QgsFieldConditionalFormatWidget::updateIcon ); connect( btnBuildExpression, &QAbstractButton::clicked, this, &QgsFieldConditionalFormatWidget::setExpression ); connect( mPresetsList, static_cast( &QComboBox::currentIndexChanged ), this, &QgsFieldConditionalFormatWidget::presetSet ); btnBackgroundColor->setAllowOpacity( true ); @@ -49,27 +47,13 @@ QgsFieldConditionalFormatWidget::QgsFieldConditionalFormatWidget( QWidget *paren mModel = new QStandardItemModel( listView ); listView->setModel( mModel ); mPresetsList->setModel( mPresetsModel ); + btnChangeIcon->setSymbol( QgsSymbol::defaultSymbol( QgsWkbTypes::PointGeometry ) ); setPresets( defaultPresets() ); } QgsFieldConditionalFormatWidget::~QgsFieldConditionalFormatWidget() { - delete mSymbol; -} - -void QgsFieldConditionalFormatWidget::updateIcon() -{ - mSymbol = QgsSymbol::defaultSymbol( QgsWkbTypes::PointGeometry ); - - QgsSymbolSelectorDialog dlg( mSymbol, QgsStyle::defaultStyle(), nullptr, this ); - if ( !dlg.exec() ) - { - return; - } - - QIcon icon = QgsSymbolLayerUtils::symbolPreviewIcon( mSymbol, btnChangeIcon->iconSize() ); - btnChangeIcon->setIcon( icon ); } void QgsFieldConditionalFormatWidget::setExpression() @@ -130,24 +114,14 @@ void QgsFieldConditionalFormatWidget::setFormattingFromStyle( const QgsCondition { btnBackgroundColor->setColor( style.backgroundColor() ); btnTextColor->setColor( style.textColor() ); - if ( !style.icon().isNull() ) + if ( style.symbol() ) { + btnChangeIcon->setSymbol( style.symbol()->clone() ); checkIcon->setChecked( true ); - QIcon icon( style.icon() ); - btnChangeIcon->setIcon( icon ); } else { checkIcon->setChecked( false ); - btnChangeIcon->setIcon( QIcon() ); - } - if ( style.symbol() ) - { - mSymbol = style.symbol()->clone(); - } - else - { - mSymbol = nullptr; } QFont font = style.font(); mFontBoldBtn->setChecked( font.bold() ); @@ -206,7 +180,6 @@ void QgsFieldConditionalFormatWidget::addNewRule() void QgsFieldConditionalFormatWidget::reset() { - mSymbol = nullptr; mNameEdit->clear(); mRuleEdit->clear(); if ( fieldRadio->isChecked() ) @@ -297,9 +270,9 @@ void QgsFieldConditionalFormatWidget::saveRule() style.setFont( font ); style.setBackgroundColor( backColor ); style.setTextColor( fontColor ); - if ( mSymbol && checkIcon->isChecked() ) + if ( checkIcon->isChecked() ) { - style.setSymbol( mSymbol ); + style.setSymbol( btnChangeIcon->clonedSymbol< QgsMarkerSymbol >() ); } else { diff --git a/src/gui/attributetable/qgsfieldconditionalformatwidget.h b/src/gui/attributetable/qgsfieldconditionalformatwidget.h index b839df14308..26794335a24 100644 --- a/src/gui/attributetable/qgsfieldconditionalformatwidget.h +++ b/src/gui/attributetable/qgsfieldconditionalformatwidget.h @@ -96,7 +96,6 @@ class GUI_EXPORT QgsFieldConditionalFormatWidget : public QWidget, private Ui::Q bool mEditing; QStandardItemModel *mModel = nullptr; QStandardItemModel *mPresetsModel = nullptr; - QgsSymbol *mSymbol = nullptr; QList mPresets; QList getStyles(); @@ -105,7 +104,6 @@ class GUI_EXPORT QgsFieldConditionalFormatWidget : public QWidget, private Ui::Q private slots: void setExpression(); - void updateIcon(); void presetSet( int index ); bool isCustomSet(); void ruleClicked( const QModelIndex &index ); diff --git a/src/ui/qgsfieldconditionalformatwidget.ui b/src/ui/qgsfieldconditionalformatwidget.ui index c67ee3efdb8..42a53dbe768 100644 --- a/src/ui/qgsfieldconditionalformatwidget.ui +++ b/src/ui/qgsfieldconditionalformatwidget.ui @@ -357,7 +357,7 @@ - + false @@ -384,19 +384,6 @@ - - - - Qt::Horizontal - - - - 40 - 20 - - - - @@ -599,6 +586,11 @@ + + QgsSymbolButton + QToolButton +
qgssymbolbutton.h
+
QgsColorButton QToolButton From 43d094eab3815ec43fe909d013527cb15ae09b83 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Sun, 23 Jul 2017 18:23:02 +1000 Subject: [PATCH 07/11] Custom widget plugin for QgsSymbolButton --- src/customwidgets/CMakeLists.txt | 2 + src/customwidgets/qgiscustomwidgets.cpp | 3 +- src/customwidgets/qgssymbolbuttonplugin.cpp | 97 +++++++++++++++++++++ src/customwidgets/qgssymbolbuttonplugin.h | 51 +++++++++++ 4 files changed, 152 insertions(+), 1 deletion(-) create mode 100644 src/customwidgets/qgssymbolbuttonplugin.cpp create mode 100644 src/customwidgets/qgssymbolbuttonplugin.h diff --git a/src/customwidgets/CMakeLists.txt b/src/customwidgets/CMakeLists.txt index 253a5615bc5..4aeb0f9b2e5 100644 --- a/src/customwidgets/CMakeLists.txt +++ b/src/customwidgets/CMakeLists.txt @@ -38,6 +38,7 @@ SET (QGIS_CUSTOMWIDGETS_SRCS qgsscalewidgetplugin.cpp qgsscrollareawidgetplugin.cpp qgsspinboxplugin.cpp + qgssymbolbuttonplugin.cpp ) SET (QGIS_CUSTOMWIDGETS_MOC_HDRS @@ -68,6 +69,7 @@ SET (QGIS_CUSTOMWIDGETS_MOC_HDRS qgsscalewidgetplugin.h qgsscrollareawidgetplugin.h qgsspinboxplugin.h + qgssymbolbuttonplugin.h ) IF(MSVC) diff --git a/src/customwidgets/qgiscustomwidgets.cpp b/src/customwidgets/qgiscustomwidgets.cpp index c3387701f68..d97ce1d307b 100644 --- a/src/customwidgets/qgiscustomwidgets.cpp +++ b/src/customwidgets/qgiscustomwidgets.cpp @@ -41,7 +41,7 @@ #include "qgsscalewidgetplugin.h" #include "qgsscrollareawidgetplugin.h" #include "qgsspinboxplugin.h" - +#include "qgssymbolbuttonplugin.h" QgisCustomWidgets::QgisCustomWidgets( QObject *parent ) : QObject( parent ) @@ -71,6 +71,7 @@ QgisCustomWidgets::QgisCustomWidgets( QObject *parent ) mWidgets.append( new QgsScaleWidgetPlugin( this ) ); mWidgets.append( new QgsScrollAreaWidgetPlugin( this ) ); mWidgets.append( new QgsSpinBoxPlugin( this ) ); + mWidgets.append( new QgsSymbolButtonPlugin( this ) ); } QList QgisCustomWidgets::customWidgets() const diff --git a/src/customwidgets/qgssymbolbuttonplugin.cpp b/src/customwidgets/qgssymbolbuttonplugin.cpp new file mode 100644 index 00000000000..0ecee3c9c48 --- /dev/null +++ b/src/customwidgets/qgssymbolbuttonplugin.cpp @@ -0,0 +1,97 @@ +/*************************************************************************** + qgssymbolbuttonplugin.cpp + ------------------------ + Date : 23.07.2017 + Copyright : (C) 2017 Nyall Dawson + Email : nyall dot dawson at gmail dot com +*************************************************************************** +* * +* This program is free software; you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation; either version 2 of the License, or * +* (at your option) any later version. * +* * +***************************************************************************/ + +#include "qgiscustomwidgets.h" +#include "qgssymbolbuttonplugin.h" +#include "qgssymbolbutton.h" + + +QgsSymbolButtonPlugin::QgsSymbolButtonPlugin( QObject *parent ) + : QObject( parent ) + , mInitialized( false ) +{ +} + + +QString QgsSymbolButtonPlugin::name() const +{ + return "QgsSymbolButton"; +} + +QString QgsSymbolButtonPlugin::group() const +{ + return QgisCustomWidgets::groupName(); +} + +QString QgsSymbolButtonPlugin::includeFile() const +{ + return "qgssymbolbutton.h"; +} + +QIcon QgsSymbolButtonPlugin::icon() const +{ + return QIcon( ":/images/icons/qgis-icon-60x60.png" ); +} + +bool QgsSymbolButtonPlugin::isContainer() const +{ + return false; +} + +QWidget *QgsSymbolButtonPlugin::createWidget( QWidget *parent ) +{ + return new QgsSymbolButton( parent ); +} + +bool QgsSymbolButtonPlugin::isInitialized() const +{ + return mInitialized; +} + +void QgsSymbolButtonPlugin::initialize( QDesignerFormEditorInterface *core ) +{ + Q_UNUSED( core ); + if ( mInitialized ) + return; + mInitialized = true; +} + + +QString QgsSymbolButtonPlugin::toolTip() const +{ + return tr( "Select symbol" ); +} + +QString QgsSymbolButtonPlugin::whatsThis() const +{ + return ""; +} + +QString QgsSymbolButtonPlugin::domXml() const +{ + return QString( "\n" + " \n" + " \n" + " \n" + " 0\n" + " 0\n" + " 27\n" + " 27\n" + " \n" + " \n" + " \n" + "\n" ) + .arg( name() ); +} diff --git a/src/customwidgets/qgssymbolbuttonplugin.h b/src/customwidgets/qgssymbolbuttonplugin.h new file mode 100644 index 00000000000..3f4c7b09500 --- /dev/null +++ b/src/customwidgets/qgssymbolbuttonplugin.h @@ -0,0 +1,51 @@ +/*************************************************************************** + qgssymbolbuttonplugin.h + ---------------------- + Date : 23.07.2017 + Copyright : (C) 2017 Nyall Dawson + Email : nyall dot dawson at gmail dot com +*************************************************************************** +* * +* This program is free software; you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation; either version 2 of the License, or * +* (at your option) any later version. * +* * +***************************************************************************/ + +#ifndef QGSSYMBOLBUTTONPLUGIN_H +#define QGSSYMBOLBUTTONPLUGIN_H + + +#include +#include +#include +#include "qgis_customwidgets.h" + + +class CUSTOMWIDGETS_EXPORT QgsSymbolButtonPlugin : public QObject, public QDesignerCustomWidgetInterface +{ + Q_OBJECT + Q_INTERFACES( QDesignerCustomWidgetInterface ) + + public: + explicit QgsSymbolButtonPlugin( QObject *parent = 0 ); + + private: + bool mInitialized; + + // QDesignerCustomWidgetInterface interface + public: + QString name() const override; + QString group() const override; + QString includeFile() const override; + QIcon icon() const override; + bool isContainer() const override; + QWidget *createWidget( QWidget *parent ) override; + bool isInitialized() const override; + void initialize( QDesignerFormEditorInterface *core ) override; + QString toolTip() const override; + QString whatsThis() const override; + QString domXml() const override; +}; +#endif // QGSSYMBOLBUTTONPLUGIN_H From 15f3bbf9c805e038fde89736d91f37cd8c2a6e23 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Mon, 24 Jul 2017 13:02:20 +1000 Subject: [PATCH 08/11] Add some missing /Factory/ annotations --- python/core/qgscolorramp.sip | 8 ++++---- python/core/symbology-ng/qgssymbollayerregistry.sip | 10 +++++----- python/core/symbology-ng/qgssymbollayerutils.sip | 12 ++++++------ src/core/qgscolorramp.h | 8 ++++---- src/core/symbology-ng/qgssymbollayerregistry.h | 10 +++++----- src/core/symbology-ng/qgssymbollayerutils.h | 12 ++++++------ 6 files changed, 30 insertions(+), 30 deletions(-) diff --git a/python/core/qgscolorramp.sip b/python/core/qgscolorramp.sip index d88365c3ef5..a266ebfb531 100644 --- a/python/core/qgscolorramp.sip +++ b/python/core/qgscolorramp.sip @@ -144,7 +144,7 @@ class QgsGradientColorRamp : QgsColorRamp \param stops optional list of additional color stops %End - static QgsColorRamp *create( const QgsStringMap &properties = QgsStringMap() ); + static QgsColorRamp *create( const QgsStringMap &properties = QgsStringMap() ) /Factory/; %Docstring Creates a new QgsColorRamp from a map of properties :rtype: QgsColorRamp @@ -477,7 +477,7 @@ class QgsPresetSchemeColorRamp : QgsColorRamp, QgsColorScheme not available in Python bindings - use setColors instead %End - static QgsColorRamp *create( const QgsStringMap &properties = QgsStringMap() ); + static QgsColorRamp *create( const QgsStringMap &properties = QgsStringMap() ) /Factory/; %Docstring Returns a new QgsPresetSchemeColorRamp color ramp created using the properties encoded in a string map. @@ -541,7 +541,7 @@ class QgsColorBrewerColorRamp : QgsColorRamp \param inverted invert ramp ordering %End - static QgsColorRamp *create( const QgsStringMap &properties = QgsStringMap() ); + static QgsColorRamp *create( const QgsStringMap &properties = QgsStringMap() ) /Factory/; %Docstring Returns a new QgsColorBrewerColorRamp color ramp created using the properties encoded in a string map. @@ -647,7 +647,7 @@ class QgsCptCityColorRamp : QgsGradientColorRamp \param doLoadFile load cpt-city ramp from file %End - static QgsColorRamp *create( const QgsStringMap &properties = QgsStringMap() ); + static QgsColorRamp *create( const QgsStringMap &properties = QgsStringMap() ) /Factory/; %Docstring :rtype: QgsColorRamp %End diff --git a/python/core/symbology-ng/qgssymbollayerregistry.sip b/python/core/symbology-ng/qgssymbollayerregistry.sip index addd7f864f4..ee62f32b649 100644 --- a/python/core/symbology-ng/qgssymbollayerregistry.sip +++ b/python/core/symbology-ng/qgssymbollayerregistry.sip @@ -52,7 +52,7 @@ Create a symbol layer of this type given the map of properties. Create widget for symbol layer of this type. Can return NULL if there's no GUI :rtype: QgsSymbolLayerWidget %End - virtual QgsSymbolLayer *createSymbolLayerFromSld( QDomElement & ); + virtual QgsSymbolLayer *createSymbolLayerFromSld( QDomElement & ) /Factory/; %Docstring Create a symbol layer of this type given the map of properties. :rtype: QgsSymbolLayer @@ -85,9 +85,9 @@ Convenience metadata class that uses static functions to create symbol layer and - virtual QgsSymbolLayer *createSymbolLayer( const QgsStringMap &map ); - virtual QgsSymbolLayerWidget *createSymbolLayerWidget( const QgsVectorLayer *vl ); - virtual QgsSymbolLayer *createSymbolLayerFromSld( QDomElement &elem ); + virtual QgsSymbolLayer *createSymbolLayer( const QgsStringMap &map ) /Factory/; + virtual QgsSymbolLayerWidget *createSymbolLayerWidget( const QgsVectorLayer *vl ) /Factory/; + virtual QgsSymbolLayer *createSymbolLayerFromSld( QDomElement &elem ) /Factory/; virtual void resolvePaths( QgsStringMap &properties, const QgsPathResolver &pathResolver, bool saving ); protected: @@ -133,7 +133,7 @@ create a new instance of symbol layer given symbol layer name and properties :rtype: QgsSymbolLayer %End - QgsSymbolLayer *createSymbolLayerFromSld( const QString &name, QDomElement &element ) const; + QgsSymbolLayer *createSymbolLayerFromSld( const QString &name, QDomElement &element ) const /Factory/; %Docstring create a new instance of symbol layer given symbol layer name and SLD :rtype: QgsSymbolLayer diff --git a/python/core/symbology-ng/qgssymbollayerutils.sip b/python/core/symbology-ng/qgssymbollayerutils.sip index b62581308f1..97701625a1f 100644 --- a/python/core/symbology-ng/qgssymbollayerutils.sip +++ b/python/core/symbology-ng/qgssymbollayerutils.sip @@ -337,15 +337,15 @@ Writes a symbol definition to XML :rtype: bool %End - static QgsSymbolLayer *createFillLayerFromSld( QDomElement &element ); + static QgsSymbolLayer *createFillLayerFromSld( QDomElement &element ) /Factory/; %Docstring :rtype: QgsSymbolLayer %End - static QgsSymbolLayer *createLineLayerFromSld( QDomElement &element ); + static QgsSymbolLayer *createLineLayerFromSld( QDomElement &element ) /Factory/; %Docstring :rtype: QgsSymbolLayer %End - static QgsSymbolLayer *createMarkerLayerFromSld( QDomElement &element ); + static QgsSymbolLayer *createMarkerLayerFromSld( QDomElement &element ) /Factory/; %Docstring :rtype: QgsSymbolLayer %End @@ -577,7 +577,7 @@ Writes a collection of symbols to XML with specified tagName for the top-level e :rtype: QVariant %End - static QgsColorRamp *loadColorRamp( const QVariant &value ); + static QgsColorRamp *loadColorRamp( const QVariant &value ) /Factory/; %Docstring Load a color ramp from a QVariantMap, wrapped in a QVariant. You can use QgsXmlUtils.readVariant to load it from an XML document. @@ -605,7 +605,7 @@ Writes a collection of symbols to XML with specified tagName for the top-level e :rtype: list of QColor %End - static QMimeData *colorToMimeData( const QColor &color ); + static QMimeData *colorToMimeData( const QColor &color ) /Factory/; %Docstring Creates mime data from a color. Sets both the mime data's color data, and the mime data's text with the color's hex code. @@ -636,7 +636,7 @@ Writes a collection of symbols to XML with specified tagName for the top-level e :rtype: QgsNamedColorList %End - static QMimeData *colorListToMimeData( const QgsNamedColorList &colorList, const bool allFormats = true ); + static QMimeData *colorListToMimeData( const QgsNamedColorList &colorList, const bool allFormats = true ) /Factory/; %Docstring Creates mime data from a list of named colors \param colorList list of named colors diff --git a/src/core/qgscolorramp.h b/src/core/qgscolorramp.h index ee0b81641b1..3bacf474ddc 100644 --- a/src/core/qgscolorramp.h +++ b/src/core/qgscolorramp.h @@ -142,7 +142,7 @@ class CORE_EXPORT QgsGradientColorRamp : public QgsColorRamp const QgsGradientStopsList &stops = QgsGradientStopsList() ); //! Creates a new QgsColorRamp from a map of properties - static QgsColorRamp *create( const QgsStringMap &properties = QgsStringMap() ); + static QgsColorRamp *create( const QgsStringMap &properties = QgsStringMap() ) SIP_FACTORY; virtual int count() const override { return mStops.count() + 2; } virtual double value( int index ) const override; @@ -439,7 +439,7 @@ class CORE_EXPORT QgsPresetSchemeColorRamp : public QgsColorRamp, public QgsColo * \param properties color ramp properties * \see properties() */ - static QgsColorRamp *create( const QgsStringMap &properties = QgsStringMap() ); + static QgsColorRamp *create( const QgsStringMap &properties = QgsStringMap() ) SIP_FACTORY; /** Sets the list of colors used by the ramp. * \param colors list of colors @@ -496,7 +496,7 @@ class CORE_EXPORT QgsColorBrewerColorRamp : public QgsColorRamp * \param properties color ramp properties * \see properties() */ - static QgsColorRamp *create( const QgsStringMap &properties = QgsStringMap() ); + static QgsColorRamp *create( const QgsStringMap &properties = QgsStringMap() ) SIP_FACTORY; virtual double value( int index ) const override; virtual QColor color( double value ) const override; @@ -586,7 +586,7 @@ class CORE_EXPORT QgsCptCityColorRamp : public QgsGradientColorRamp const QString &variantName = QString(), bool inverted = false, bool doLoadFile = true ); - static QgsColorRamp *create( const QgsStringMap &properties = QgsStringMap() ); + static QgsColorRamp *create( const QgsStringMap &properties = QgsStringMap() ) SIP_FACTORY; virtual QString type() const override { return QStringLiteral( "cpt-city" ); } diff --git a/src/core/symbology-ng/qgssymbollayerregistry.h b/src/core/symbology-ng/qgssymbollayerregistry.h index 80e914f3b06..b75cd7aaa4f 100644 --- a/src/core/symbology-ng/qgssymbollayerregistry.h +++ b/src/core/symbology-ng/qgssymbollayerregistry.h @@ -50,7 +50,7 @@ class CORE_EXPORT QgsSymbolLayerAbstractMetadata //! Create widget for symbol layer of this type. Can return NULL if there's no GUI virtual QgsSymbolLayerWidget *createSymbolLayerWidget( const QgsVectorLayer * ) SIP_FACTORY { return nullptr; } //! Create a symbol layer of this type given the map of properties. - virtual QgsSymbolLayer *createSymbolLayerFromSld( QDomElement & ) { return nullptr; } + virtual QgsSymbolLayer *createSymbolLayerFromSld( QDomElement & ) SIP_FACTORY { return nullptr; } /** Resolve paths in symbol layer's properties (if there are any paths). * When saving is true, paths are converted from absolute to relative, @@ -109,9 +109,9 @@ class CORE_EXPORT QgsSymbolLayerMetadata : public QgsSymbolLayerAbstractMetadata //! \note not available in Python bindings void setWidgetFunction( QgsSymbolLayerWidgetFunc f ) { mWidgetFunc = f; } SIP_SKIP - virtual QgsSymbolLayer *createSymbolLayer( const QgsStringMap &map ) override { return mCreateFunc ? mCreateFunc( map ) : nullptr; } SIP_FACTORY - virtual QgsSymbolLayerWidget *createSymbolLayerWidget( const QgsVectorLayer *vl ) override { return mWidgetFunc ? mWidgetFunc( vl ) : nullptr; } SIP_FACTORY - virtual QgsSymbolLayer *createSymbolLayerFromSld( QDomElement &elem ) override { return mCreateFromSldFunc ? mCreateFromSldFunc( elem ) : nullptr; } SIP_FACTORY + virtual QgsSymbolLayer *createSymbolLayer( const QgsStringMap &map ) override SIP_FACTORY { return mCreateFunc ? mCreateFunc( map ) : nullptr; } + virtual QgsSymbolLayerWidget *createSymbolLayerWidget( const QgsVectorLayer *vl ) override SIP_FACTORY { return mWidgetFunc ? mWidgetFunc( vl ) : nullptr; } + virtual QgsSymbolLayer *createSymbolLayerFromSld( QDomElement &elem ) override SIP_FACTORY { return mCreateFromSldFunc ? mCreateFromSldFunc( elem ) : nullptr; } virtual void resolvePaths( QgsStringMap &properties, const QgsPathResolver &pathResolver, bool saving ) override { if ( mPathResolverFunc ) @@ -159,7 +159,7 @@ class CORE_EXPORT QgsSymbolLayerRegistry QgsSymbolLayer *createSymbolLayer( const QString &name, const QgsStringMap &properties = QgsStringMap() ) const SIP_FACTORY; //! create a new instance of symbol layer given symbol layer name and SLD - QgsSymbolLayer *createSymbolLayerFromSld( const QString &name, QDomElement &element ) const; + QgsSymbolLayer *createSymbolLayerFromSld( const QString &name, QDomElement &element ) const SIP_FACTORY; /** Resolve paths in properties of a particular symbol layer. * This normally means converting relative paths to absolute paths when loading diff --git a/src/core/symbology-ng/qgssymbollayerutils.h b/src/core/symbology-ng/qgssymbollayerutils.h index 38ac75fb5f8..f8c2f72f1f4 100644 --- a/src/core/symbology-ng/qgssymbollayerutils.h +++ b/src/core/symbology-ng/qgssymbollayerutils.h @@ -254,9 +254,9 @@ class CORE_EXPORT QgsSymbolLayerUtils static bool createSymbolLayerListFromSld( QDomElement &element, QgsWkbTypes::GeometryType geomType, QgsSymbolLayerList &layers ); - static QgsSymbolLayer *createFillLayerFromSld( QDomElement &element ); - static QgsSymbolLayer *createLineLayerFromSld( QDomElement &element ); - static QgsSymbolLayer *createMarkerLayerFromSld( QDomElement &element ); + static QgsSymbolLayer *createFillLayerFromSld( QDomElement &element ) SIP_FACTORY; + static QgsSymbolLayer *createLineLayerFromSld( QDomElement &element ) SIP_FACTORY; + static QgsSymbolLayer *createMarkerLayerFromSld( QDomElement &element ) SIP_FACTORY; static bool convertPolygonSymbolizerToPointMarker( QDomElement &element, QgsSymbolLayerList &layerList ); static bool hasExternalGraphic( QDomElement &element ); @@ -395,7 +395,7 @@ class CORE_EXPORT QgsSymbolLayerUtils * * \see colorRampToVariant() */ - static QgsColorRamp *loadColorRamp( const QVariant &value ); + static QgsColorRamp *loadColorRamp( const QVariant &value ) SIP_FACTORY; /** * Returns a friendly display name for a color @@ -421,7 +421,7 @@ class CORE_EXPORT QgsSymbolLayerUtils * \see colorFromMimeData * \since QGIS 2.5 */ - static QMimeData *colorToMimeData( const QColor &color ); + static QMimeData *colorToMimeData( const QColor &color ) SIP_FACTORY; /** * Attempts to parse mime data as a color @@ -449,7 +449,7 @@ class CORE_EXPORT QgsSymbolLayerUtils * \returns mime data containing encoded colors * \since QGIS 2.5 */ - static QMimeData *colorListToMimeData( const QgsNamedColorList &colorList, const bool allFormats = true ); + static QMimeData *colorListToMimeData( const QgsNamedColorList &colorList, const bool allFormats = true ) SIP_FACTORY; /** * Exports colors to a gpl GIMP palette file From 78b05c1a7fece0241701d0b4854f8c29af195438 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Mon, 24 Jul 2017 14:52:22 +1000 Subject: [PATCH 09/11] Allow copying and pasting symbols between QgsSymbolButtons --- python/core/symbology-ng/qgssymbol.sip | 2 +- python/core/symbology-ng/qgssymbollayer.sip | 2 + .../core/symbology-ng/qgssymbollayerutils.sip | 19 +++++++ python/gui/qgssymbolbutton.sip | 28 ++++++++++ src/app/composer/qgscomposershapewidget.cpp | 2 + src/app/qgsannotationwidget.cpp | 3 ++ src/app/qgsdecorationgriddialog.cpp | 4 ++ src/app/qgsdecorationlayoutextentdialog.cpp | 2 + src/core/symbology-ng/qgssymbol.h | 2 +- src/core/symbology-ng/qgssymbollayer.h | 4 ++ src/core/symbology-ng/qgssymbollayerutils.cpp | 42 +++++++++++++++ src/core/symbology-ng/qgssymbollayerutils.h | 17 ++++++ .../qgsfieldconditionalformatwidget.cpp | 1 + src/gui/qgssymbolbutton.cpp | 53 +++++++++++++++++++ src/gui/qgssymbolbutton.h | 28 ++++++++++ .../qgspointclusterrendererwidget.cpp | 2 + .../qgspointdisplacementrendererwidget.cpp | 1 + tests/src/python/test_qgssymbolbutton.py | 46 +++++++++++++++- tests/src/python/test_qgssymbollayerutils.py | 16 +++++- 19 files changed, 270 insertions(+), 4 deletions(-) diff --git a/python/core/symbology-ng/qgssymbol.sip b/python/core/symbology-ng/qgssymbol.sip index 9d0ea813837..9399b01b1d2 100644 --- a/python/core/symbology-ng/qgssymbol.sip +++ b/python/core/symbology-ng/qgssymbol.sip @@ -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 diff --git a/python/core/symbology-ng/qgssymbollayer.sip b/python/core/symbology-ng/qgssymbollayer.sip index 767651d5d1d..11548c8ec5e 100644 --- a/python/core/symbology-ng/qgssymbollayer.sip +++ b/python/core/symbology-ng/qgssymbollayer.sip @@ -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 diff --git a/python/core/symbology-ng/qgssymbollayerutils.sip b/python/core/symbology-ng/qgssymbollayerutils.sip index 97701625a1f..0a0d68a29f8 100644 --- a/python/core/symbology-ng/qgssymbollayerutils.sip +++ b/python/core/symbology-ng/qgssymbollayerutils.sip @@ -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 diff --git a/python/gui/qgssymbolbutton.sip b/python/gui/qgssymbolbutton.sip index af8b1e4f9e1..5f2a04258ed 100644 --- a/python/gui/qgssymbolbutton.sip +++ b/python/gui/qgssymbolbutton.sip @@ -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. diff --git a/src/app/composer/qgscomposershapewidget.cpp b/src/app/composer/qgscomposershapewidget.cpp index 355c48b3c48..ec928db0ef3 100644 --- a/src/app/composer/qgscomposershapewidget.cpp +++ b/src/app/composer/qgscomposershapewidget.cpp @@ -45,6 +45,8 @@ QgsComposerShapeWidget::QgsComposerShapeWidget( QgsComposerShape *composerShape mShapeComboBox->addItem( tr( "Rectangle" ) ); mShapeComboBox->addItem( tr( "Triangle" ) ); + mShapeStyleButton->setSymbolType( QgsSymbol::Fill ); + setGuiElementValues(); blockAllSignals( false ); diff --git a/src/app/qgsannotationwidget.cpp b/src/app/qgsannotationwidget.cpp index 95014b8d095..6b4a32901a7 100644 --- a/src/app/qgsannotationwidget.cpp +++ b/src/app/qgsannotationwidget.cpp @@ -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(); diff --git a/src/app/qgsdecorationgriddialog.cpp b/src/app/qgsdecorationgriddialog.cpp index 070c6094956..a568ceaad63 100644 --- a/src/app/qgsdecorationgriddialog.cpp +++ b/src/app/qgsdecorationgriddialog.cpp @@ -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() diff --git a/src/app/qgsdecorationlayoutextentdialog.cpp b/src/app/qgsdecorationlayoutextentdialog.cpp index 1db51351f41..f605e66b14c 100644 --- a/src/app/qgsdecorationlayoutextentdialog.cpp +++ b/src/app/qgsdecorationlayoutextentdialog.cpp @@ -35,6 +35,8 @@ QgsDecorationLayoutExtentDialog::QgsDecorationLayoutExtentDialog( QgsDecorationL { setupUi( this ); + mSymbolButton->setSymbolType( QgsSymbol::Fill ); + QgsSettings settings; restoreGeometry( settings.value( "/Windows/DecorationLayoutExtent/geometry" ).toByteArray() ); diff --git a/src/core/symbology-ng/qgssymbol.h b/src/core/symbology-ng/qgssymbol.h index d09c34af93f..71c3fb65895 100644 --- a/src/core/symbology-ng/qgssymbol.h +++ b/src/core/symbology-ng/qgssymbol.h @@ -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 diff --git a/src/core/symbology-ng/qgssymbollayer.h b/src/core/symbology-ng/qgssymbollayer.h index 95f565d0eb5..2a370b23d36 100644 --- a/src/core/symbology-ng/qgssymbollayer.h +++ b/src/core/symbology-ng/qgssymbollayer.h @@ -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; } diff --git a/src/core/symbology-ng/qgssymbollayerutils.cpp b/src/core/symbology-ng/qgssymbollayerutils.cpp index a9c20e8f601..eebd35a3b5c 100644 --- a/src/core/symbology-ng/qgssymbollayerutils.cpp +++ b/src/core/symbology-ng/qgssymbollayerutils.cpp @@ -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 ) { diff --git a/src/core/symbology-ng/qgssymbollayerutils.h b/src/core/symbology-ng/qgssymbollayerutils.h index f8c2f72f1f4..45435e04887 100644 --- a/src/core/symbology-ng/qgssymbollayerutils.h +++ b/src/core/symbology-ng/qgssymbollayerutils.h @@ -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. diff --git a/src/gui/attributetable/qgsfieldconditionalformatwidget.cpp b/src/gui/attributetable/qgsfieldconditionalformatwidget.cpp index 7c0a85cdf61..3f230be0958 100644 --- a/src/gui/attributetable/qgsfieldconditionalformatwidget.cpp +++ b/src/gui/attributetable/qgsfieldconditionalformatwidget.cpp @@ -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() ); diff --git a/src/gui/qgssymbolbutton.cpp b/src/gui/qgssymbolbutton.cpp index 3a640b8742e..248bcd55782 100644 --- a/src/gui/qgssymbolbutton.cpp +++ b/src/gui/qgssymbolbutton.cpp @@ -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 ); diff --git a/src/gui/qgssymbolbutton.h b/src/gui/qgssymbolbutton.h index 8df81616981..f19cb5a2e1e 100644 --- a/src/gui/qgssymbolbutton.h +++ b/src/gui/qgssymbolbutton.h @@ -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; diff --git a/src/gui/symbology-ng/qgspointclusterrendererwidget.cpp b/src/gui/symbology-ng/qgspointclusterrendererwidget.cpp index 0a91a9a5700..e2914e042cf 100644 --- a/src/gui/symbology-ng/qgspointclusterrendererwidget.cpp +++ b/src/gui/symbology-ng/qgspointclusterrendererwidget.cpp @@ -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 ); diff --git a/src/gui/symbology-ng/qgspointdisplacementrendererwidget.cpp b/src/gui/symbology-ng/qgspointdisplacementrendererwidget.cpp index c457a02c17a..acd7dc50c5d 100644 --- a/src/gui/symbology-ng/qgspointdisplacementrendererwidget.cpp +++ b/src/gui/symbology-ng/qgspointdisplacementrendererwidget.cpp @@ -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 ) { diff --git a/tests/src/python/test_qgssymbolbutton.py b/tests/src/python/test_qgssymbolbutton.py index ca53e1f8fe2..66ac2d0ddad 100644 --- a/tests/src/python/test_qgssymbolbutton.py +++ b/tests/src/python/test_qgssymbolbutton.py @@ -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({}) diff --git a/tests/src/python/test_qgssymbollayerutils.py b/tests/src/python/test_qgssymbollayerutils.py index 79309e654a1..966029d0c35 100644 --- a/tests/src/python/test_qgssymbollayerutils.py +++ b/tests/src/python/test_qgssymbollayerutils.py @@ -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() From 7e749a5afb6eadafb4cc85ecbba6c17f795701c2 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Mon, 24 Jul 2017 19:46:26 +1000 Subject: [PATCH 10/11] Fix failing tests --- src/core/qgsunittypes.cpp | 0 src/gui/qgssymbolbutton.cpp | 3 +++ src/gui/qgsunitselectionwidget.cpp | 0 tests/src/python/test_qgssymbolbutton.py | 4 ++-- tests/src/python/test_qgssymbollayerutils.py | 1 + 5 files changed, 6 insertions(+), 2 deletions(-) mode change 100755 => 100644 src/core/qgsunittypes.cpp mode change 100755 => 100644 src/gui/qgsunitselectionwidget.cpp diff --git a/src/core/qgsunittypes.cpp b/src/core/qgsunittypes.cpp old mode 100755 new mode 100644 diff --git a/src/gui/qgssymbolbutton.cpp b/src/gui/qgssymbolbutton.cpp index 248bcd55782..d6072da0f0e 100644 --- a/src/gui/qgssymbolbutton.cpp +++ b/src/gui/qgssymbolbutton.cpp @@ -69,6 +69,9 @@ void QgsSymbolButton::setSymbolType( QgsSymbol::SymbolType type ) case QgsSymbol::Fill: mSymbol.reset( QgsFillSymbol::createSimple( QgsStringMap() ) ); break; + + case QgsSymbol::Hybrid: + break; } } updatePreview(); diff --git a/src/gui/qgsunitselectionwidget.cpp b/src/gui/qgsunitselectionwidget.cpp old mode 100755 new mode 100644 diff --git a/tests/src/python/test_qgssymbolbutton.py b/tests/src/python/test_qgssymbolbutton.py index 66ac2d0ddad..410d0459cc9 100644 --- a/tests/src/python/test_qgssymbolbutton.py +++ b/tests/src/python/test_qgssymbolbutton.py @@ -39,7 +39,7 @@ class TestQgsSymbolButton(unittest.TestCase): button.setSymbolType(QgsSymbol.Line) self.assertEqual(button.symbolType(), QgsSymbol.Line) - def testSettingSymbolType(self) + def testSettingSymbolType(self): button = QgsSymbolButton() button.setSymbolType(QgsSymbol.Marker) symbol = QgsMarkerSymbol.createSimple({}) @@ -74,7 +74,7 @@ class TestQgsSymbolButton(unittest.TestCase): # try pasting incompatible symbol button2.setSymbolType(QgsSymbol.Fill) fill_symbol = QgsFillSymbol.createSimple({}) - fill_symbol.setColor(QColor(0,0,255)) + fill_symbol.setColor(QColor(0, 0, 255)) button2.setSymbol(fill_symbol) button.copySymbol() # copied a marker symbol button2.pasteSymbol() # should have no effect diff --git a/tests/src/python/test_qgssymbollayerutils.py b/tests/src/python/test_qgssymbollayerutils.py index 966029d0c35..3328baf6c69 100644 --- a/tests/src/python/test_qgssymbollayerutils.py +++ b/tests/src/python/test_qgssymbollayerutils.py @@ -15,6 +15,7 @@ __revision__ = '$Format:%H$' import qgis # NOQA from qgis.core import QgsSymbolLayerUtils, QgsMarkerSymbol +from qgis.PyQt.QtGui import QColor from qgis.PyQt.QtCore import QSizeF, QPointF from qgis.testing import unittest From 6202f06e985125aa20f1c99b7daea3c9acb395b5 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Mon, 24 Jul 2017 20:06:36 +1000 Subject: [PATCH 11/11] Fix more tests --- tests/src/python/test_qgssymbollayerutils.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/src/python/test_qgssymbollayerutils.py b/tests/src/python/test_qgssymbollayerutils.py index 3328baf6c69..488cfb12063 100644 --- a/tests/src/python/test_qgssymbollayerutils.py +++ b/tests/src/python/test_qgssymbollayerutils.py @@ -17,7 +17,9 @@ import qgis # NOQA from qgis.core import QgsSymbolLayerUtils, QgsMarkerSymbol from qgis.PyQt.QtGui import QColor from qgis.PyQt.QtCore import QSizeF, QPointF -from qgis.testing import unittest +from qgis.testing import unittest, start_app + +start_app() class PyQgsSymbolLayerUtils(unittest.TestCase):