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