QGIS API Documentation  3.25.0-Master (6b426f5f8a)
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 "qgsfillsymbol.h"
21 #include "qgsmarkersymbol.h"
22 #include "qgslinesymbol.h"
23 
24 #include <QMenu>
25 #include <QBuffer>
26 
27 QgsLegendPatchShapeButton::QgsLegendPatchShapeButton( QWidget *parent, const QString &dialogTitle )
28  : QToolButton( parent )
29  , mShape( QgsStyle::defaultStyle()->defaultPatch( Qgis::SymbolType::Fill, QSizeF( 10, 5 ) ) )
30  , mDialogTitle( dialogTitle.isEmpty() ? tr( "Legend Patch Shape" ) : dialogTitle )
31 {
32  mPreviewSymbol.reset( QgsFillSymbol::createSimple( QVariantMap() ) );
33 
34  connect( this, &QAbstractButton::clicked, this, &QgsLegendPatchShapeButton::showSettingsDialog );
35 
36  //setup dropdown menu
37  mMenu = new QMenu( this );
38  connect( mMenu, &QMenu::aboutToShow, this, &QgsLegendPatchShapeButton::prepareMenu );
39  setMenu( mMenu );
40  setPopupMode( QToolButton::MenuButtonPopup );
41 
42  //make sure height of button looks good under different platforms
43  QSize size = QToolButton::minimumSizeHint();
44  int fontHeight = static_cast< int >( Qgis::UI_SCALE_FACTOR * fontMetrics().height() * 2.0 );
45  mSizeHint = QSize( size.width(), std::max( size.height(), fontHeight ) );
46 }
47 
49 
51 {
52  return mSizeHint;
53 }
54 
56 {
57  return mSizeHint;
58 }
59 
61 {
62  if ( mPreviewSymbol->type() != type )
63  {
64  switch ( type )
65  {
67  mPreviewSymbol.reset( QgsMarkerSymbol::createSimple( QVariantMap() ) );
68  break;
69 
71  mPreviewSymbol.reset( QgsLineSymbol::createSimple( QVariantMap() ) );
72  break;
73 
75  mPreviewSymbol.reset( QgsFillSymbol::createSimple( QVariantMap() ) );
76  break;
77 
79  break;
80  }
81  }
82 
83  if ( type != mType )
84  {
85  mType = type;
86  setToDefault();
87  }
88 
89  updatePreview();
90 }
91 
93 {
94  mPreviewSymbol.reset( symbol );
95  updatePreview();
96 }
97 
98 void QgsLegendPatchShapeButton::showSettingsDialog()
99 {
101  if ( panel && panel->dockMode() )
102  {
103  QgsLegendPatchShapeWidget *widget = new QgsLegendPatchShapeWidget( this, mShape );
104  connect( widget, &QgsLegendPatchShapeWidget::changed, this, [ = ]
105  {
106  setShape( widget->shape() );
107  } );
108  widget->setPanelTitle( mDialogTitle );
109  panel->openPanel( widget );
110  }
111 }
112 
114 {
115  switch ( mType )
116  {
118  mShape = QgsStyle::defaultStyle()->defaultPatch( Qgis::SymbolType::Marker, QSizeF( 10, 5 ) );
119  break;
120 
122  mShape = QgsStyle::defaultStyle()->defaultPatch( Qgis::SymbolType::Line, QSizeF( 10, 5 ) );
123  break;
124 
126  mShape = QgsStyle::defaultStyle()->defaultPatch( Qgis::SymbolType::Fill, QSizeF( 10, 5 ) );
127  break;
128 
130  break;
131  }
132  mIsDefault = true;
133  updatePreview();
134  emit changed();
135 }
136 
138 {
139  mMessageBar = bar;
140 }
141 
143 {
144  return mMessageBar;
145 }
146 
148 {
149  mShape = shape.symbolType() == mType ? shape : QgsLegendPatchShape();
150  mIsDefault = mShape.isNull();
151  if ( mIsDefault )
152  {
153  switch ( mType )
154  {
156  mShape = QgsStyle::defaultStyle()->defaultPatch( Qgis::SymbolType::Marker, QSizeF( 10, 5 ) );
157  break;
158 
160  mShape = QgsStyle::defaultStyle()->defaultPatch( Qgis::SymbolType::Line, QSizeF( 10, 5 ) );
161  break;
162 
164  mShape = QgsStyle::defaultStyle()->defaultPatch( Qgis::SymbolType::Fill, QSizeF( 10, 5 ) );
165  break;
166 
168  break;
169  }
170  }
171 
172  updatePreview();
173  emit changed();
174 }
175 
177 {
178  if ( e->button() == Qt::RightButton )
179  {
180  QToolButton::showMenu();
181  return;
182  }
183  QToolButton::mousePressEvent( e );
184 }
185 
186 void QgsLegendPatchShapeButton::prepareMenu()
187 {
188  mMenu->clear();
189 
190  QAction *configureAction = new QAction( tr( "Configure Patch…" ), this );
191  mMenu->addAction( configureAction );
192  connect( configureAction, &QAction::triggered, this, &QgsLegendPatchShapeButton::showSettingsDialog );
193 
194  QAction *defaultAction = new QAction( tr( "Reset to Default" ), this );
195  mMenu->addAction( defaultAction );
196  connect( defaultAction, &QAction::triggered, this, [ = ] { setToDefault(); emit changed(); } );
197 
198  mMenu->addSeparator();
199 
201  patchNames.sort();
202  const int iconSize = QgsGuiUtils::scaleIconSize( 16 );
203  for ( const QString &name : std::as_const( patchNames ) )
204  {
206  if ( shape.symbolType() == mType )
207  {
208  if ( const QgsSymbol *symbol = QgsStyle::defaultStyle()->previewSymbolForPatchShape( shape ) )
209  {
210  QIcon icon = QgsSymbolLayerUtils::symbolPreviewPixmap( symbol, QSize( iconSize, iconSize ), 1, nullptr, false, nullptr, &shape );
211  QAction *action = new QAction( name, this );
212  action->setIcon( icon );
213  connect( action, &QAction::triggered, this, [ = ] { loadPatchFromStyle( name ); } );
214  mMenu->addAction( action );
215  }
216  }
217  }
218 }
219 
220 void QgsLegendPatchShapeButton::loadPatchFromStyle( const QString &name )
221 {
222  if ( !QgsStyle::defaultStyle()->legendPatchShapeNames().contains( name ) )
223  return;
224 
225  const QgsLegendPatchShape newShape = QgsStyle::defaultStyle()->legendPatchShape( name );
226  setShape( newShape );
227 }
228 
230 {
231  if ( e->type() == QEvent::EnabledChange )
232  {
233  updatePreview();
234  }
235  QToolButton::changeEvent( e );
236 }
237 
239 {
240  updatePreview();
241  QToolButton::showEvent( e );
242 }
243 
244 void QgsLegendPatchShapeButton::resizeEvent( QResizeEvent *event )
245 {
246  QToolButton::resizeEvent( event );
247  //recalculate icon size and redraw icon
248  mIconSize = QSize();
249  updatePreview();
250 }
251 
252 void QgsLegendPatchShapeButton::updatePreview()
253 {
254  QSize currentIconSize;
255  //icon size is button size with a small margin
256  if ( menu() )
257  {
258  if ( !mIconSize.isValid() )
259  {
260  //calculate size of push button part of widget (ie, without the menu dropdown button part)
261  QStyleOptionToolButton opt;
262  initStyleOption( &opt );
263  QRect buttonSize = QApplication::style()->subControlRect( QStyle::CC_ToolButton, &opt, QStyle::SC_ToolButton,
264  this );
265  //make sure height of icon looks good under different platforms
266 #ifdef Q_OS_WIN
267  mIconSize = QSize( buttonSize.width() - 10, height() - 6 );
268 #else
269  mIconSize = QSize( buttonSize.width() - 10, height() - 12 );
270 #endif
271  }
272  currentIconSize = mIconSize;
273  }
274  else
275  {
276  //no menu
277 #ifdef Q_OS_WIN
278  currentIconSize = QSize( width() - 10, height() - 6 );
279 #else
280  currentIconSize = QSize( width() - 10, height() - 12 );
281 #endif
282  }
283 
284  if ( !currentIconSize.isValid() || currentIconSize.width() <= 0 || currentIconSize.height() <= 0 )
285  {
286  return;
287  }
288 
289  //create an icon pixmap
290  QIcon icon = QgsSymbolLayerUtils::symbolPreviewIcon( mPreviewSymbol.get(), currentIconSize, currentIconSize.height() / 10, &mShape );
291  setIconSize( currentIconSize );
292  setIcon( icon );
293 
294  // set tooltip
295  // create very large preview image
296 
297  int width = static_cast< int >( Qgis::UI_SCALE_FACTOR * fontMetrics().horizontalAdvance( 'X' ) * 23 );
298  int height = static_cast< int >( width / 1.61803398875 ); // golden ratio
299 
300  QPixmap pm = QgsSymbolLayerUtils::symbolPreviewPixmap( mPreviewSymbol.get(), QSize( width, height ), height / 20, nullptr, false, nullptr, &mShape );
301  QByteArray data;
302  QBuffer buffer( &data );
303  pm.save( &buffer, "PNG", 100 );
304  setToolTip( QStringLiteral( "<img src='data:image/png;base64, %3'>" ).arg( QString( data.toBase64() ) ) );
305 }
306 
307 void QgsLegendPatchShapeButton::setDialogTitle( const QString &title )
308 {
309  mDialogTitle = title;
310 }
311 
313 {
314  return mDialogTitle;
315 }
316 
318 {
319  return mIsDefault ? QgsLegendPatchShape() : mShape;
320 }
The Qgis class provides global constants for use throughout the application.
Definition: qgis.h:72
SymbolType
Symbol types.
Definition: qgis.h:205
@ Marker
Marker symbol.
@ Line
Line symbol.
@ Fill
Fill symbol.
@ Hybrid
Hybrid symbol.
static const double UI_SCALE_FACTOR
UI scaling factor.
Definition: qgis.h:1773
static QgsFillSymbol * createSimple(const QVariantMap &properties)
Create a fill symbol with one symbol layer: SimpleFill with specified properties.
void setSymbolType(Qgis::SymbolType type)
Sets the symbol type which the button requires.
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.
~QgsLegendPatchShapeButton() override
void setMessageBar(QgsMessageBar *bar)
Sets the message bar associated with the widget.
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...
Qgis::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.
static QgsMarkerSymbol * createSimple(const QVariantMap &properties)
Create a marker symbol with one symbol layer: SimpleMarker with specified properties.
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.
QgsLegendPatchShape defaultPatch(Qgis::SymbolType type, QSizeF size) const
Returns the default legend patch shape for the given symbol type.
Definition: qgsstyle.cpp:1176
@ LegendPatchShapeEntity
Legend patch shape (since QGIS 3.14)
Definition: qgsstyle.h:186
static QgsStyle * defaultStyle()
Returns default application-wide style.
Definition: qgsstyle.cpp:131
QStringList symbolsOfFavorite(StyleEntity type) const
Returns the symbol names which are flagged as favorite.
Definition: qgsstyle.cpp:1306
QgsLegendPatchShape legendPatchShape(const QString &name) const
Returns the legend patch shape with the specified name.
Definition: qgsstyle.cpp:2150
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:93
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,...