QGIS API Documentation  3.14.0-Pi (9f7028fd23)
qgslegendpatchshapebutton.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgslegendpatchshapebutton.cpp
3  -----------------
4  Date : April 2020
5  Copyright : (C) 2020 by Nyall Dawson
6  Email : nyall dot dawson at gmail dot com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
18 #include "qgis.h"
19 #include "qgsguiutils.h"
20 #include <QMenu>
21 
22 QgsLegendPatchShapeButton::QgsLegendPatchShapeButton( QWidget *parent, const QString &dialogTitle )
23  : QToolButton( parent )
24  , mShape( QgsStyle::defaultStyle()->defaultPatch( QgsSymbol::Fill, QSizeF( 10, 5 ) ) )
25  , mDialogTitle( dialogTitle.isEmpty() ? tr( "Legend Patch Shape" ) : dialogTitle )
26 {
27  mPreviewSymbol.reset( QgsFillSymbol::createSimple( QgsStringMap() ) );
28 
29  connect( this, &QAbstractButton::clicked, this, &QgsLegendPatchShapeButton::showSettingsDialog );
30 
31  //setup dropdown menu
32  mMenu = new QMenu( this );
33  connect( mMenu, &QMenu::aboutToShow, this, &QgsLegendPatchShapeButton::prepareMenu );
34  setMenu( mMenu );
35  setPopupMode( QToolButton::MenuButtonPopup );
36 
37  //make sure height of button looks good under different platforms
38  QSize size = QToolButton::minimumSizeHint();
39  int fontHeight = static_cast< int >( Qgis::UI_SCALE_FACTOR * fontMetrics().height() * 2.0 );
40  mSizeHint = QSize( size.width(), std::max( size.height(), fontHeight ) );
41 }
42 
44 {
45  return mSizeHint;
46 }
47 
49 {
50  return mSizeHint;
51 }
52 
54 {
55  if ( mPreviewSymbol->type() != type )
56  {
57  switch ( type )
58  {
59  case QgsSymbol::Marker:
60  mPreviewSymbol.reset( QgsMarkerSymbol::createSimple( QgsStringMap() ) );
61  break;
62 
63  case QgsSymbol::Line:
64  mPreviewSymbol.reset( QgsLineSymbol::createSimple( QgsStringMap() ) );
65  break;
66 
67  case QgsSymbol::Fill:
68  mPreviewSymbol.reset( QgsFillSymbol::createSimple( QgsStringMap() ) );
69  break;
70 
71  case QgsSymbol::Hybrid:
72  break;
73  }
74  }
75 
76  if ( type != mType )
77  {
78  mType = type;
79  setToDefault();
80  }
81 
82  updatePreview();
83 }
84 
86 {
87  mPreviewSymbol.reset( symbol );
88  updatePreview();
89 }
90 
91 void QgsLegendPatchShapeButton::showSettingsDialog()
92 {
94  if ( panel && panel->dockMode() )
95  {
96  QgsLegendPatchShapeWidget *widget = new QgsLegendPatchShapeWidget( this, mShape );
97  connect( widget, &QgsLegendPatchShapeWidget::changed, this, [ = ]
98  {
99  setShape( widget->shape() );
100  } );
101  widget->setPanelTitle( mDialogTitle );
102  panel->openPanel( widget );
103  }
104 }
105 
107 {
108  switch ( mType )
109  {
110  case QgsSymbol::Marker:
111  mShape = QgsStyle::defaultStyle()->defaultPatch( QgsSymbol::Marker, QSizeF( 10, 5 ) );
112  break;
113 
114  case QgsSymbol::Line:
115  mShape = QgsStyle::defaultStyle()->defaultPatch( QgsSymbol::Line, QSizeF( 10, 5 ) );
116  break;
117 
118  case QgsSymbol::Fill:
119  mShape = QgsStyle::defaultStyle()->defaultPatch( QgsSymbol::Fill, QSizeF( 10, 5 ) );
120  break;
121 
122  case QgsSymbol::Hybrid:
123  break;
124  }
125  mIsDefault = true;
126  updatePreview();
127  emit changed();
128 }
129 
131 {
132  mMessageBar = bar;
133 }
134 
136 {
137  return mMessageBar;
138 }
139 
141 {
142  mShape = shape.symbolType() == mType ? shape : QgsLegendPatchShape();
143  mIsDefault = mShape.isNull();
144  if ( mIsDefault )
145  {
146  switch ( mType )
147  {
148  case QgsSymbol::Marker:
149  mShape = QgsStyle::defaultStyle()->defaultPatch( QgsSymbol::Marker, QSizeF( 10, 5 ) );
150  break;
151 
152  case QgsSymbol::Line:
153  mShape = QgsStyle::defaultStyle()->defaultPatch( QgsSymbol::Line, QSizeF( 10, 5 ) );
154  break;
155 
156  case QgsSymbol::Fill:
157  mShape = QgsStyle::defaultStyle()->defaultPatch( QgsSymbol::Fill, QSizeF( 10, 5 ) );
158  break;
159 
160  case QgsSymbol::Hybrid:
161  break;
162  }
163  }
164 
165  updatePreview();
166  emit changed();
167 }
168 
170 {
171  if ( e->button() == Qt::RightButton )
172  {
173  QToolButton::showMenu();
174  return;
175  }
176  QToolButton::mousePressEvent( e );
177 }
178 
179 void QgsLegendPatchShapeButton::prepareMenu()
180 {
181  mMenu->clear();
182 
183  QAction *configureAction = new QAction( tr( "Configure Patch…" ), this );
184  mMenu->addAction( configureAction );
185  connect( configureAction, &QAction::triggered, this, &QgsLegendPatchShapeButton::showSettingsDialog );
186 
187  QAction *defaultAction = new QAction( tr( "Reset to Default" ), this );
188  mMenu->addAction( defaultAction );
189  connect( defaultAction, &QAction::triggered, this, [ = ] { setToDefault(); emit changed(); } );
190 
191  mMenu->addSeparator();
192 
194  patchNames.sort();
195  const int iconSize = QgsGuiUtils::scaleIconSize( 16 );
196  for ( const QString &name : qgis::as_const( patchNames ) )
197  {
199  if ( shape.symbolType() == mType )
200  {
201  if ( const QgsSymbol *symbol = QgsStyle::defaultStyle()->previewSymbolForPatchShape( shape ) )
202  {
203  QIcon icon = QgsSymbolLayerUtils::symbolPreviewPixmap( symbol, QSize( iconSize, iconSize ), 1, nullptr, false, nullptr, &shape );
204  QAction *action = new QAction( name, this );
205  action->setIcon( icon );
206  connect( action, &QAction::triggered, this, [ = ] { loadPatchFromStyle( name ); } );
207  mMenu->addAction( action );
208  }
209  }
210  }
211 }
212 
213 void QgsLegendPatchShapeButton::loadPatchFromStyle( const QString &name )
214 {
215  if ( !QgsStyle::defaultStyle()->legendPatchShapeNames().contains( name ) )
216  return;
217 
218  const QgsLegendPatchShape newShape = QgsStyle::defaultStyle()->legendPatchShape( name );
219  setShape( newShape );
220 }
221 
223 {
224  if ( e->type() == QEvent::EnabledChange )
225  {
226  updatePreview();
227  }
228  QToolButton::changeEvent( e );
229 }
230 
232 {
233  updatePreview();
234  QToolButton::showEvent( e );
235 }
236 
237 void QgsLegendPatchShapeButton::resizeEvent( QResizeEvent *event )
238 {
239  QToolButton::resizeEvent( event );
240  //recalculate icon size and redraw icon
241  mIconSize = QSize();
242  updatePreview();
243 }
244 
245 void QgsLegendPatchShapeButton::updatePreview()
246 {
247  QSize currentIconSize;
248  //icon size is button size with a small margin
249  if ( menu() )
250  {
251  if ( !mIconSize.isValid() )
252  {
253  //calculate size of push button part of widget (ie, without the menu dropdown button part)
254  QStyleOptionToolButton opt;
255  initStyleOption( &opt );
256  QRect buttonSize = QApplication::style()->subControlRect( QStyle::CC_ToolButton, &opt, QStyle::SC_ToolButton,
257  this );
258  //make sure height of icon looks good under different platforms
259 #ifdef Q_OS_WIN
260  mIconSize = QSize( buttonSize.width() - 10, height() - 6 );
261 #else
262  mIconSize = QSize( buttonSize.width() - 10, height() - 12 );
263 #endif
264  }
265  currentIconSize = mIconSize;
266  }
267  else
268  {
269  //no menu
270 #ifdef Q_OS_WIN
271  currentIconSize = QSize( width() - 10, height() - 6 );
272 #else
273  currentIconSize = QSize( width() - 10, height() - 12 );
274 #endif
275  }
276 
277  if ( !currentIconSize.isValid() || currentIconSize.width() <= 0 || currentIconSize.height() <= 0 )
278  {
279  return;
280  }
281 
282  //create an icon pixmap
283  QIcon icon = QgsSymbolLayerUtils::symbolPreviewIcon( mPreviewSymbol.get(), currentIconSize, currentIconSize.height() / 10, &mShape );
284  setIconSize( currentIconSize );
285  setIcon( icon );
286 
287  // set tooltip
288  // create very large preview image
289 
290 #if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)
291  int width = static_cast< int >( Qgis::UI_SCALE_FACTOR * fontMetrics().width( 'X' ) * 23 );
292 #else
293  int width = static_cast< int >( Qgis::UI_SCALE_FACTOR * fontMetrics().horizontalAdvance( 'X' ) * 23 );
294 #endif
295  int height = static_cast< int >( width / 1.61803398875 ); // golden ratio
296 
297  QPixmap pm = QgsSymbolLayerUtils::symbolPreviewPixmap( mPreviewSymbol.get(), QSize( width, height ), height / 20, nullptr, false, nullptr, &mShape );
298  QByteArray data;
299  QBuffer buffer( &data );
300  pm.save( &buffer, "PNG", 100 );
301  setToolTip( QStringLiteral( "<img src='data:image/png;base64, %3'>" ).arg( QString( data.toBase64() ) ) );
302 }
303 
304 void QgsLegendPatchShapeButton::setDialogTitle( const QString &title )
305 {
306  mDialogTitle = title;
307 }
308 
310 {
311  return mDialogTitle;
312 }
313 
315 {
316  return mIsDefault ? QgsLegendPatchShape() : mShape;
317 }
QgsLegendPatchShapeButton::changed
void changed()
Emitted when the shape's settings are changed.
QgsLegendPatchShapeButton::QgsLegendPatchShapeButton
QgsLegendPatchShapeButton(QWidget *parent=nullptr, const QString &dialogTitle=QString())
Construct a new patch shape button with the specified parent widget.
Definition: qgslegendpatchshapebutton.cpp:22
QgsPanelWidget::findParentPanel
static QgsPanelWidget * findParentPanel(QWidget *widget)
Traces through the parents of a widget to find if it is contained within a QgsPanelWidget widget.
Definition: qgspanelwidget.cpp:49
QgsStyle::symbolsOfFavorite
QStringList symbolsOfFavorite(StyleEntity type) const
Returns the symbol names which are flagged as favorite.
Definition: qgsstyle.cpp:1085
QgsLegendPatchShapeButton::mousePressEvent
void mousePressEvent(QMouseEvent *e) override
Definition: qgslegendpatchshapebutton.cpp:169
QgsLegendPatchShapeButton::showEvent
void showEvent(QShowEvent *e) override
Definition: qgslegendpatchshapebutton.cpp:231
QgsMarkerSymbol::createSimple
static QgsMarkerSymbol * createSimple(const QgsStringMap &properties)
Create a marker symbol with one symbol layer: SimpleMarker with specified properties.
Definition: qgssymbol.cpp:1418
QgsPanelWidget::openPanel
void openPanel(QgsPanelWidget *panel)
Open a panel or dialog depending on dock mode setting If dock mode is true this method will emit the ...
Definition: qgspanelwidget.cpp:79
qgis.h
QgsSymbol
Definition: qgssymbol.h:63
QgsStyle::LegendPatchShapeEntity
@ LegendPatchShapeEntity
Legend patch shape (since QGIS 3.14)
Definition: qgsstyle.h:186
QgsStyle::defaultPatch
QgsLegendPatchShape defaultPatch(QgsSymbol::SymbolType type, QSizeF size) const
Returns the default legend patch shape for the given symbol type.
Definition: qgsstyle.cpp:1030
QgsPanelWidget::dockMode
bool dockMode()
Returns the dock mode state.
Definition: qgspanelwidget.h:83
QgsLegendPatchShape::symbolType
QgsSymbol::SymbolType symbolType() const
Returns the symbol type associated with this patch.
Definition: qgslegendpatchshape.cpp:207
QgsLegendPatchShapeButton::shape
QgsLegendPatchShape shape()
Returns the current shape defined by the button.
Definition: qgslegendpatchshapebutton.cpp:314
QgsLegendPatchShape
Definition: qgslegendpatchshape.h:30
QgsGuiUtils::iconSize
QSize iconSize(bool dockableToolbar)
Returns the user-preferred size of a window's toolbar icons.
Definition: qgsguiutils.cpp:264
QgsStyle::defaultStyle
static QgsStyle * defaultStyle()
Returns default application-wide style.
Definition: qgsstyle.cpp:111
QgsLegendPatchShapeButton::minimumSizeHint
QSize minimumSizeHint() const override
Definition: qgslegendpatchshapebutton.cpp:43
QgsSymbolLayerUtils::symbolPreviewIcon
static QIcon symbolPreviewIcon(const QgsSymbol *symbol, QSize size, int padding=0, QgsLegendPatchShape *shape=nullptr)
Returns an icon preview for a color ramp.
Definition: qgssymbollayerutils.cpp:762
QgsPanelWidget
Base class for any widget that can be shown as a inline panel.
Definition: qgspanelwidget.h:29
QgsSymbolLayerUtils::symbolPreviewPixmap
static QPixmap symbolPreviewPixmap(const QgsSymbol *symbol, QSize size, int padding=0, QgsRenderContext *customContext=nullptr, bool selected=false, const QgsExpressionContext *expressionContext=nullptr, const QgsLegendPatchShape *shape=nullptr)
Returns a pixmap preview for a color ramp.
Definition: qgssymbollayerutils.cpp:767
QgsLegendPatchShapeWidget
Widget for configuring a custom legend patch shape.
Definition: qgslegendpatchshapewidget.h:32
QgsLegendPatchShapeWidget::shape
QgsLegendPatchShape shape() const
Returns the legend patch shape as currently defined by the widget.
Definition: qgslegendpatchshapewidget.cpp:41
QgsMessageBar
Definition: qgsmessagebar.h:60
QgsLegendPatchShapeButton::messageBar
QgsMessageBar * messageBar() const
Returns the message bar associated with the widget.
Definition: qgslegendpatchshapebutton.cpp:135
Qgis::UI_SCALE_FACTOR
static const double UI_SCALE_FACTOR
UI scaling factor.
Definition: qgis.h:195
QgsSymbol::Fill
@ Fill
Fill symbol.
Definition: qgssymbol.h:89
QgsLegendPatchShapeButton::setSymbolType
void setSymbolType(QgsSymbol::SymbolType type)
Sets the symbol type which the button requires.
Definition: qgslegendpatchshapebutton.cpp:53
QgsPanelWidget::setPanelTitle
void setPanelTitle(const QString &panelTitle)
Set the title of the panel when shown in the interface.
Definition: qgspanelwidget.h:44
QgsLegendPatchShapeButton::setMessageBar
void setMessageBar(QgsMessageBar *bar)
Sets the message bar associated with the widget.
Definition: qgslegendpatchshapebutton.cpp:130
QgsLegendPatchShapeWidget::changed
void changed()
Emitted whenever the patch shape defined by the widget is changed.
QgsLegendPatchShapeButton::resizeEvent
void resizeEvent(QResizeEvent *event) override
Definition: qgslegendpatchshapebutton.cpp:237
QgsLegendPatchShapeButton::dialogTitle
QString dialogTitle() const
Returns the title for the symbol settings dialog window.
Definition: qgslegendpatchshapebutton.cpp:309
QgsStringMap
QMap< QString, QString > QgsStringMap
Definition: qgis.h:714
QgsStyle::legendPatchShape
QgsLegendPatchShape legendPatchShape(const QString &name) const
Returns the legend patch shape with the specified name.
Definition: qgsstyle.cpp:1920
QgsStyle
Definition: qgsstyle.h:159
QgsLineSymbol::createSimple
static QgsLineSymbol * createSimple(const QgsStringMap &properties)
Create a line symbol with one symbol layer: SimpleLine with specified properties.
Definition: qgssymbol.cpp:1429
QgsLegendPatchShapeButton::setToDefault
void setToDefault()
Resets the shape to the default shape.
Definition: qgslegendpatchshapebutton.cpp:106
QgsLegendPatchShapeButton::setPreviewSymbol
void setPreviewSymbol(QgsSymbol *symbol)
Sets the symbol to use for previewing the legend patch shape.
Definition: qgslegendpatchshapebutton.cpp:85
QgsLegendPatchShapeButton::sizeHint
QSize sizeHint() const override
Definition: qgslegendpatchshapebutton.cpp:48
QgsSymbol::Line
@ Line
Line symbol.
Definition: qgssymbol.h:88
QgsLegendPatchShapeButton::changeEvent
void changeEvent(QEvent *e) override
Definition: qgslegendpatchshapebutton.cpp:222
QgsSymbol::Marker
@ Marker
Marker symbol.
Definition: qgssymbol.h:87
QgsFillSymbol::createSimple
static QgsFillSymbol * createSimple(const QgsStringMap &properties)
Create a fill symbol with one symbol layer: SimpleFill with specified properties.
Definition: qgssymbol.cpp:1440
QgsSymbol::SymbolType
SymbolType
Type of the symbol.
Definition: qgssymbol.h:85
qgslegendpatchshapebutton.h
QgsSymbol::Hybrid
@ Hybrid
Hybrid symbol.
Definition: qgssymbol.h:90
qgsguiutils.h
QgsGuiUtils::scaleIconSize
int scaleIconSize(int standardSize)
Scales an icon size to compensate for display pixel density, making the icon size hi-dpi friendly,...
Definition: qgsguiutils.cpp:257
QgsLegendPatchShapeButton::setDialogTitle
void setDialogTitle(const QString &title)
Sets the title for the symbol settings dialog window.
Definition: qgslegendpatchshapebutton.cpp:304
QgsLegendPatchShapeButton::setShape
void setShape(const QgsLegendPatchShape &shape)
Sets the shape for the button.
Definition: qgslegendpatchshapebutton.cpp:140
qgslegendpatchshapewidget.h
QgsLegendPatchShape::isNull
bool isNull() const
Returns true if the patch shape is a null QgsLegendPatchShape, which indicates that the default legen...
Definition: qgslegendpatchshape.cpp:32