QGIS API Documentation  3.23.0-Master (22c16f2067)
qgsfieldconditionalformatwidget.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsfieldconditionalformatwidget.cpp
3  ---------------------
4  begin : August 2015
5  copyright : (C) 2015 by Nathan Woodrow
6  email : woodrow dot nathan 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  ***************************************************************************/
16 
18 #include "qgssymbol.h"
20 #include "qgssymbollayerutils.h"
21 #include "qgsstyle.h"
22 #include "qgsvectorlayer.h"
24 #include "qgsguiutils.h"
25 #include "qgsmarkersymbol.h"
26 
27 //
28 // QgsFieldConditionalFormatWidget
29 //
30 
32  : QgsPanelWidget( parent )
33 {
34  setupUi( this );
35  setPanelTitle( tr( "Conditional Styles" ) );
36  connect( mFieldCombo, &QgsFieldComboBox::fieldChanged, this, &QgsFieldConditionalFormatWidget::fieldChanged );
37  connect( fieldRadio, &QAbstractButton::clicked, this, &QgsFieldConditionalFormatWidget::reloadStyles );
38  connect( rowRadio, &QAbstractButton::clicked, this, &QgsFieldConditionalFormatWidget::reloadStyles );
39  connect( mNewButton, &QAbstractButton::clicked, this, &QgsFieldConditionalFormatWidget::addNewRule );
40  connect( listView, &QAbstractItemView::clicked, this, &QgsFieldConditionalFormatWidget::ruleClicked );
41  mModel = new QStandardItemModel( listView );
42  listView->setModel( mModel );
43 
44  connect( fieldRadio, &QRadioButton::toggled, mFieldCombo, &QWidget::setEnabled );
45 
46  mPresets = defaultPresets();
47 }
48 
50 {
51  mLayer = layer;
52  mFieldCombo->setLayer( layer );
53  mFieldCombo->setCurrentIndex( 0 );
54  fieldChanged( mFieldCombo->currentField() );
55 }
56 
57 void QgsFieldConditionalFormatWidget::ruleClicked( const QModelIndex &index )
58 {
59  const QList<QgsConditionalStyle> styles = getStyles();
60  const QgsConditionalStyle style = styles.at( index.row() );
61  editStyle( index.row(), style );
62 }
63 
65 {
66  mEditIndex = editIndex;
67  mEditing = editIndex >= 0;
68  mPanelHandled = false;
69 
71  ruleWidget->setLayer( mLayer );
72  ruleWidget->setPresets( mPresets );
73  ruleWidget->loadStyle( style );
74  ruleWidget->setDockMode( true );
75 
76  if ( fieldRadio->isChecked() && style.rule().isEmpty() )
77  {
78  ruleWidget->setRule( QStringLiteral( "@value " ) );
79  }
80 
81  connect( ruleWidget, &QgsEditConditionalFormatRuleWidget::panelAccepted, this, [ = ]
82  {
83  if ( mPanelHandled )
84  {
85  // already handled the result of the panel, and the panel is being dismissed as a result
86  // of an already dealt with action
87  return;
88  }
89 
90  QList<QgsConditionalStyle> styles = getStyles();
91  if ( mEditing )
92  {
93  styles.replace( mEditIndex, ruleWidget->currentStyle() );
94  }
95  else
96  {
97  styles.append( ruleWidget->currentStyle() );
98  }
99 
100  QString fieldName;
101  if ( fieldRadio->isChecked() )
102  {
103  fieldName = mFieldCombo->currentField();
104  mLayer->conditionalStyles()->setFieldStyles( fieldName, styles );
105  }
106  if ( rowRadio->isChecked() )
107  {
108  mLayer->conditionalStyles()->setRowStyles( styles );
109  }
110  reloadStyles();
111  emit rulesUpdated( fieldName );
112  } );
113 
114  connect( ruleWidget, &QgsEditConditionalFormatRuleWidget::ruleSaved, this, [ = ]
115  {
116  ruleWidget->acceptPanel();
117  } );
118 
119  connect( ruleWidget, &QgsEditConditionalFormatRuleWidget::canceled, this, [ = ]
120  {
121  mPanelHandled = true;
122  ruleWidget->acceptPanel();
123  } );
124 
125  connect( ruleWidget, &QgsEditConditionalFormatRuleWidget::ruleDeleted, this, [ = ]
126  {
127  deleteCurrentRule();
128  mPanelHandled = true;
129  ruleWidget->acceptPanel();
130  } );
131  showPanel( ruleWidget );
132 }
133 
135 {
136 }
137 
138 QList<QgsConditionalStyle> QgsFieldConditionalFormatWidget::getStyles()
139 {
140  QList<QgsConditionalStyle> styles;
141  if ( fieldRadio->isChecked() )
142  {
143  styles = mLayer->conditionalStyles()->fieldStyles( mFieldCombo->currentField() );
144  }
145  if ( rowRadio->isChecked() )
146  {
147  styles = mLayer->conditionalStyles()->rowStyles();
148  }
149  return styles;
150 }
151 
152 void QgsFieldConditionalFormatWidget::addNewRule()
153 {
155 }
156 
158 {
159 }
160 
161 void QgsFieldConditionalFormatWidget::setPresets( const QList<QgsConditionalStyle> &styles )
162 {
163  mPresets = styles;
164 }
165 
167 {
168  QList<QgsConditionalStyle> styles;
170  style.setBackgroundColor( QColor( 154, 216, 113 ) );
171  styles.append( style );
172  style = QgsConditionalStyle();
173  style.setBackgroundColor( QColor( 251, 193, 78 ) );
174  styles.append( style );
175  style = QgsConditionalStyle();
176  style.setBackgroundColor( QColor( 251, 154, 153 ) );
177  styles.append( style );
178  style = QgsConditionalStyle();
179  style.setTextColor( QColor( 154, 216, 113 ) );
180  styles.append( style );
181  style = QgsConditionalStyle();
182  style.setTextColor( QColor( 251, 193, 78 ) );
183  styles.append( style );
184  style = QgsConditionalStyle();
185  style.setTextColor( QColor( 251, 154, 153 ) );
186  styles.append( style );
187  return styles;
188 }
189 
190 void QgsFieldConditionalFormatWidget::reloadStyles()
191 {
192  mModel->clear();
193 
194  const auto constGetStyles = getStyles();
195 
196  const QSize size( Qgis::UI_SCALE_FACTOR * fontMetrics().horizontalAdvance( 'X' ) * 10, Qgis::UI_SCALE_FACTOR * fontMetrics().height() * 2 );
197 
198  listView->setIconSize( size );
199 
200  for ( const QgsConditionalStyle &style : constGetStyles )
201  {
202  QStandardItem *item = new QStandardItem( style.displayText() );
203  item->setIcon( QIcon( style.renderPreview( size ) ) );
204  mModel->appendRow( item );
205  }
206 }
207 
208 void QgsFieldConditionalFormatWidget::fieldChanged( const QString &fieldName )
209 {
210  Q_UNUSED( fieldName )
211  reloadStyles();
212 }
213 
214 void QgsFieldConditionalFormatWidget::deleteCurrentRule()
215 {
216  if ( !mEditing )
217  return;
218 
219  QList<QgsConditionalStyle> styles = getStyles();
220  styles.removeAt( mEditIndex );
221  QString fieldName;
222  if ( fieldRadio->isChecked() )
223  {
224  fieldName = mFieldCombo->currentField();
225  mLayer->conditionalStyles()->setFieldStyles( fieldName, styles );
226  }
227  if ( rowRadio->isChecked() )
228  {
229  mLayer->conditionalStyles()->setRowStyles( styles );
230  }
231 
232  reloadStyles();
233  emit rulesUpdated( fieldName );
234 }
235 
237 {
238 }
239 
240 
241 //
242 // QgsEditConditionalFormatRuleWidget
243 //
244 
246  : QgsPanelWidget( parent )
247 {
248  setupUi( this );
249 
250  setPanelTitle( tr( "Edit Rule" ) );
251 
252  btnBackgroundColor->setColor( QColor() );
253  btnTextColor->setColor( QColor() );
254  checkIcon->setChecked( false );
255  btnChangeIcon->setIcon( QIcon() );
256  btnBackgroundColor->setToNoColor();
257  btnTextColor->setToNoColor();
258 
259  mFontBoldBtn->setChecked( false );
260  mFontItalicBtn->setChecked( false );
261  mFontStrikethroughBtn->setChecked( false );
262  mFontUnderlineBtn->setChecked( false );
263 
264  const int buttonSize = QgsGuiUtils::scaleIconSize( 24 );
265  mFontUnderlineBtn->setMinimumSize( buttonSize, buttonSize );
266  mFontUnderlineBtn->setMaximumSize( buttonSize, buttonSize );
267  mFontStrikethroughBtn->setMinimumSize( buttonSize, buttonSize );
268  mFontStrikethroughBtn->setMaximumSize( buttonSize, buttonSize );
269  mFontBoldBtn->setMinimumSize( buttonSize, buttonSize );
270  mFontBoldBtn->setMaximumSize( buttonSize, buttonSize );
271  mFontItalicBtn->setMinimumSize( buttonSize, buttonSize );
272  mFontItalicBtn->setMaximumSize( buttonSize, buttonSize );
273 
274  connect( mSaveRule, &QAbstractButton::clicked, this, &QgsEditConditionalFormatRuleWidget::ruleSaved );
275  connect( mCancelButton, &QAbstractButton::clicked, this, &QgsEditConditionalFormatRuleWidget::canceled );
276  connect( mDeleteButton, &QAbstractButton::clicked, this, &QgsEditConditionalFormatRuleWidget::ruleDeleted );
277 
278  connect( btnBuildExpression, &QAbstractButton::clicked, this, &QgsEditConditionalFormatRuleWidget::setExpression );
279  connect( mPresetsList, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsEditConditionalFormatRuleWidget::presetSet );
280 
281  btnBackgroundColor->setAllowOpacity( true );
282  btnBackgroundColor->setShowNoColor( true );
283  btnTextColor->setAllowOpacity( true );
284  btnTextColor->setShowNoColor( true );
285  mPresetsModel = new QStandardItemModel( mPresetsList );
286  mPresetsList->setModel( mPresetsModel );
287 
288  btnChangeIcon->setSymbolType( Qgis::SymbolType::Marker );
289  btnChangeIcon->setSymbol( QgsSymbol::defaultSymbol( QgsWkbTypes::PointGeometry ) );
290  connect( checkIcon, &QCheckBox::toggled, btnChangeIcon, &QWidget::setEnabled );
291 }
292 
294 {
295  mLayer = layer;
296 }
297 
299 {
300  mRuleEdit->setText( style.rule() );
301  mNameEdit->setText( style.name() );
302  setFormattingFromStyle( style );
303 }
304 
306 {
307  QgsConditionalStyle style;
308 
309  style.setRule( mRuleEdit->text() );
310  style.setName( mNameEdit->text() );
311 
312  const QColor backColor = btnBackgroundColor->color();
313  const QColor fontColor = btnTextColor->color();
314 
315  QFont font = mFontFamilyCmbBx->currentFont();
316  font.setBold( mFontBoldBtn->isChecked() );
317  font.setItalic( mFontItalicBtn->isChecked() );
318  font.setStrikeOut( mFontStrikethroughBtn->isChecked() );
319  font.setUnderline( mFontUnderlineBtn->isChecked() );
320  style.setFont( font );
321  style.setBackgroundColor( backColor );
322  style.setTextColor( fontColor );
323  if ( checkIcon->isChecked() )
324  {
325  style.setSymbol( btnChangeIcon->clonedSymbol< QgsMarkerSymbol >() );
326  }
327  else
328  {
329  style.setSymbol( nullptr );
330  }
331  return style;
332 }
333 
334 void QgsEditConditionalFormatRuleWidget::setExpression()
335 {
337  context.lastScope()->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "value" ), 0, true ) );
338  context.setHighlightedVariables( QStringList() << QStringLiteral( "value" ) );
339 
340  QgsExpressionBuilderDialog dlg( mLayer, mRuleEdit->text(), this, QStringLiteral( "generic" ), context );
341  dlg.setWindowTitle( tr( "Conditional Style Rule Expression" ) );
342 
343  if ( dlg.exec() )
344  {
345  const QString expression = dlg.expressionBuilder()->expressionText();
346  mRuleEdit->setText( expression );
347  }
348 }
349 
350 void QgsEditConditionalFormatRuleWidget::presetSet( int index )
351 {
352  if ( index == -1 || mPresets.isEmpty() )
353  return;
354 
355  const int styleIndex = mPresetsList->currentData( Qt::UserRole + 1 ).toInt();
356  const QgsConditionalStyle style = mPresets.at( styleIndex );
357  setFormattingFromStyle( style );
358 }
359 
360 void QgsEditConditionalFormatRuleWidget::setFormattingFromStyle( const QgsConditionalStyle &style )
361 {
362  btnBackgroundColor->setColor( style.backgroundColor() );
363  btnTextColor->setColor( style.textColor() );
364  if ( auto *lSymbol = style.symbol() )
365  {
366  btnChangeIcon->setSymbol( lSymbol->clone() );
367  checkIcon->setChecked( true );
368  }
369  else
370  {
371  checkIcon->setChecked( false );
372  }
373  const QFont font = style.font();
374  mFontBoldBtn->setChecked( font.bold() );
375  mFontItalicBtn->setChecked( font.italic() );
376  mFontStrikethroughBtn->setChecked( font.strikeOut() );
377  mFontUnderlineBtn->setChecked( font.underline() );
378  mFontFamilyCmbBx->setCurrentFont( font );
379 }
380 
381 void QgsEditConditionalFormatRuleWidget::setPresets( const QList<QgsConditionalStyle> &styles )
382 {
383  mPresets.clear();
384  mPresetsModel->clear();
385  QStandardItem *item = new QStandardItem( QString() );
386  mPresetsModel->appendRow( item );
387  int i = 0;
388  for ( const QgsConditionalStyle &style : styles )
389  {
390  if ( style.isValid() )
391  {
392  QStandardItem *item = new QStandardItem( QStringLiteral( "abc - 123" ) );
393  if ( style.validBackgroundColor() )
394  item->setBackground( style.backgroundColor() );
395  if ( style.validTextColor() )
396  item->setForeground( style.textColor() );
397  if ( style.symbol() )
398  item->setIcon( style.icon() );
399  item->setFont( style.font() );
400  item->setData( i, Qt::UserRole + 1 );
401  mPresetsModel->appendRow( item );
402  mPresets.append( style );
403  i++;
404  }
405  }
406  mPresetsList->setCurrentIndex( 0 );
407 }
408 
410 {
411  mRuleEdit->setText( rule );
412 }
413 
414 bool QgsEditConditionalFormatRuleWidget::isCustomSet()
415 {
416  return ( btnBackgroundColor->color().isValid()
417  || btnTextColor->color().isValid()
418  || mFontButtons->checkedId() != -1 );
419 }
@ Marker
Marker symbol.
static const double UI_SCALE_FACTOR
UI scaling factor.
Definition: qgis.h:1295
QgsConditionalStyles rowStyles() const
Returns a list of row styles associated with the layer.
QList< QgsConditionalStyle > fieldStyles(const QString &fieldName) const
Returns the conditional styles set for the field with matching fieldName.
void setFieldStyles(const QString &fieldName, const QList< QgsConditionalStyle > &styles)
Set the conditional styles for a field, with the specified fieldName.
void setRowStyles(const QgsConditionalStyles &styles)
Sets the conditional styles that apply to full rows of data in the attribute table.
Conditional styling for a rule.
QString displayText() const
The name of the style.
QString name() const
The name of the style.
void setSymbol(QgsSymbol *value)
Set the icon for the style.
void setName(const QString &value)
Set the name of the style.
QPixmap renderPreview(const QSize &size=QSize()) const
Render a preview icon of the rule, at the specified size.
QgsSymbol * symbol() const
The symbol used to generate the icon for the style.
void setTextColor(const QColor &value)
Set the text color for the style.
void setRule(const QString &value)
Set the rule for the style.
void setBackgroundColor(const QColor &value)
Set the background color for the style.
void setFont(const QFont &value)
Set the font for the style.
QColor backgroundColor() const
The background color for style.
QColor textColor() const
The text color set for style.
QString rule() const
The condition rule set for the style.
QFont font() const
The font for the style.
bool validTextColor() const
Check if the text color is valid for render.
bool isValid() const
isValid Check if this rule is valid.
QPixmap icon() const
The icon set for style generated from the set symbol.
bool validBackgroundColor() const
Check if the background color is valid for render.
A widget for customizing an individual conditional formatting rule.
void setPresets(const QList< QgsConditionalStyle > &styles)
Sets the preset styles that can be used for quick pick.
void loadStyle(const QgsConditionalStyle &style)
Sets the widget to match the settings from the specified style.
QgsEditConditionalFormatRuleWidget(QWidget *parent SIP_TRANSFERTHIS=nullptr)
Constructor for QgsFieldConditionalFormatWidget, with the specified parent widget.
void setLayer(QgsVectorLayer *layer)
Sets the vector layer associated with the widget.
void setRule(const QString &rule)
Sets the current expression rule to show in the widget.
QgsConditionalStyle currentStyle() const
Returns the current style defined by the widget.
void canceled()
Emitted when a user has opted to cancel the rule modification.
void ruleDeleted()
Emitted when a user has opted to deleted the current rule.
void ruleSaved()
Emitted when a user has opted to save the current rule.
A generic dialog for building expression strings.
static QList< QgsExpressionContextScope * > globalProjectLayerScopes(const QgsMapLayer *layer)
Creates a list of three scopes: global, layer's project and layer.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void fieldChanged(const QString &fieldName)
Emitted when the currently selected field changes.
QgsFieldConditionalFormatWidget(QWidget *parent SIP_TRANSFERTHIS=nullptr)
Constructor for QgsFieldConditionalFormatWidget.
void rulesUpdated(const QString &fieldName)
Emitted when the conditional styling rules are updated.
static QList< QgsConditionalStyle > defaultPresets()
Returns a list of the default presets.
Q_DECL_DEPRECATED void reset() SIP_DEPRECATED
Resets the formatting options to their default state.
Q_DECL_DEPRECATED void viewRules() SIP_DEPRECATED
Switches the widget to the rules page.
void editStyle(int index, const QgsConditionalStyle &style)
Switches the widget to the edit style mode for the specified style, where index is the index of the c...
void setPresets(const QList< QgsConditionalStyle > &styles)
Sets the preset styles that can be used for quick pick.
Q_DECL_DEPRECATED void loadStyle(const QgsConditionalStyle &style) SIP_DEPRECATED
void setLayer(QgsVectorLayer *layer)
Sets the vector layer associated with the widget.
A marker symbol type, for rendering Point and MultiPoint geometries.
Base class for any widget that can be shown as a inline panel.
void showPanel(QgsPanelWidget *panel)
Emit when you require a panel to be show in the interface.
void panelAccepted(QgsPanelWidget *panel)
Emitted when the panel is accepted by the user.
void acceptPanel()
Accept the panel.
void setPanelTitle(const QString &panelTitle)
Set the title of the panel when shown in the interface.
virtual void setDockMode(bool dockMode)
Set the widget in dock mode which tells the widget to emit panel widgets and not open dialogs.
static QgsSymbol * defaultSymbol(QgsWkbTypes::GeometryType geomType)
Returns a new default symbol for the specified geometry type.
Definition: qgssymbol.cpp:355
Represents a vector layer which manages a vector based data sets.
QgsConditionalLayerStyles * conditionalStyles() const
Returns the conditional styles that are set for this layer.
int scaleIconSize(int standardSize)
Scales an icon size to compensate for display pixel density, making the icon size hi-dpi friendly,...
Single variable definition for use within a QgsExpressionContextScope.