mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-16 00:03:12 -04:00
[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
This commit is contained in:
parent
9a12249b02
commit
22c4740f63
@ -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
|
||||
|
159
python/gui/qgssymbolbutton.sip
Normal file
159
python/gui/qgssymbolbutton.sip
Normal file
@ -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 *
|
||||
************************************************************************/
|
@ -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
|
||||
|
446
src/gui/qgssymbolbutton.cpp
Normal file
446
src/gui/qgssymbolbutton.cpp
Normal file
@ -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 <QMenu>
|
||||
#include <QClipboard>
|
||||
#include <QDrag>
|
||||
|
||||
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<QgsSymbolSelectorWidget *>( sender() ) )
|
||||
setSymbol( w->symbol()->clone() );
|
||||
}
|
||||
|
||||
void QgsSymbolButton::cleanUpSymbolSelector( QgsPanelWidget *container )
|
||||
{
|
||||
QgsSymbolSelectorWidget *w = qobject_cast<QgsSymbolSelectorWidget *>( 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();
|
||||
}
|
218
src/gui/qgssymbolbutton.h
Normal file
218
src/gui/qgssymbolbutton.h
Normal file
@ -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 <QToolButton>
|
||||
#include <QPointer>
|
||||
#include <memory>
|
||||
|
||||
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
|
@ -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)
|
||||
|
77
tests/src/python/test_qgssymbolbutton.py
Normal file
77
tests/src/python/test_qgssymbolbutton.py
Normal file
@ -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()
|
Loading…
x
Reference in New Issue
Block a user