QGIS API Documentation  3.4.15-Madeira (e83d02e274)
qgspropertyoverridebutton.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgspropertyoverridebutton.cpp
3  -----------------------------
4  Date : January 2017
5  Copyright : (C) 2017 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 
17 
18 #include "qgsapplication.h"
20 #include "qgsexpression.h"
21 #include "qgsmessageviewer.h"
22 #include "qgsvectorlayer.h"
23 #include "qgspanelwidget.h"
25 #include "qgsauxiliarystorage.h"
26 #include "qgsguiutils.h"
27 
28 #include <QClipboard>
29 #include <QMenu>
30 #include <QMouseEvent>
31 #include <QPointer>
32 #include <QGroupBox>
33 
35  const QgsVectorLayer *layer )
36  : QToolButton( parent )
37  , mVectorLayer( layer )
38 
39 {
40  setFocusPolicy( Qt::StrongFocus );
41 
42  int iconSize = QgsGuiUtils::scaleIconSize( 24 );
43 
44  // button width is 1.25 * icon size, height 1.1 * icon size. But we round to ensure even pixel sizes for equal margins
45  setFixedSize( 2 * static_cast< int >( 1.25 * iconSize / 2.0 ), 2 * static_cast< int >( iconSize * 1.1 / 2.0 ) );
46 
47  setIconSize( QSize( iconSize, iconSize ) );
48  setPopupMode( QToolButton::InstantPopup );
49 
50  connect( this, &QgsPropertyOverrideButton::activated, this, &QgsPropertyOverrideButton::updateSiblingWidgets );
51 
52  mDefineMenu = new QMenu( this );
53  connect( mDefineMenu, &QMenu::aboutToShow, this, &QgsPropertyOverrideButton::aboutToShowMenu );
54  connect( mDefineMenu, &QMenu::triggered, this, &QgsPropertyOverrideButton::menuActionTriggered );
55  setMenu( mDefineMenu );
56 
57  mFieldsMenu = new QMenu( this );
58  mActionDataTypes = new QAction( this );
59  // list fields and types in submenu, since there may be many
60  mActionDataTypes->setMenu( mFieldsMenu );
61 
62  mActionVariables = new QAction( tr( "Variable" ), this );
63  mVariablesMenu = new QMenu( this );
64  mActionVariables->setMenu( mVariablesMenu );
65 
66  mActionActive = new QAction( this );
67  QFont f = mActionActive->font();
68  f.setBold( true );
69  mActionActive->setFont( f );
70 
71  mActionDescription = new QAction( tr( "Description…" ), this );
72 
73  mActionCreateAuxiliaryField = new QAction( tr( "Store Data in the Project" ), this );
74  mActionCreateAuxiliaryField->setCheckable( true );
75 
76  mActionExpDialog = new QAction( tr( "Edit…" ), this );
77  mActionExpression = nullptr;
78  mActionPasteExpr = new QAction( tr( "Paste" ), this );
79  mActionCopyExpr = new QAction( tr( "Copy" ), this );
80  mActionClearExpr = new QAction( tr( "Clear" ), this );
81  mActionAssistant = new QAction( tr( "Assistant…" ), this );
82  QFont assistantFont = mActionAssistant->font();
83  assistantFont.setBold( true );
84  mActionAssistant->setFont( assistantFont );
85  mDefineMenu->addAction( mActionAssistant );
86 }
87 
88 
89 void QgsPropertyOverrideButton::init( int propertyKey, const QgsProperty &property, const QgsPropertiesDefinition &definitions, const QgsVectorLayer *layer, bool auxiliaryStorageEnabled )
90 {
91  init( propertyKey, property, definitions.value( propertyKey ), layer, auxiliaryStorageEnabled );
92 }
93 
94 void QgsPropertyOverrideButton::init( int propertyKey, const QgsProperty &property, const QgsPropertyDefinition &definition, const QgsVectorLayer *layer, bool auxiliaryStorageEnabled )
95 {
96  mVectorLayer = layer;
97  mAuxiliaryStorageEnabled = auxiliaryStorageEnabled;
98  setToProperty( property );
99  mPropertyKey = propertyKey;
100 
101  mDefinition = definition;
102  mDataTypes = mDefinition.dataType();
103 
104  mInputDescription = mDefinition.helpText();
105  mFullDescription.clear();
106  mUsageInfo.clear();
107 
108  // set up data types string
109  mDataTypesString.clear();
110 
111  QStringList ts;
112  switch ( mDataTypes )
113  {
115  ts << tr( "boolean" );
117 
119  ts << tr( "int" );
120  ts << tr( "double" );
122 
124  ts << tr( "string" );
125  break;
126  }
127 
128  if ( !ts.isEmpty() )
129  {
130  mDataTypesString = ts.join( QStringLiteral( ", " ) );
131  mActionDataTypes->setText( tr( "Field type: " ) + mDataTypesString );
132  }
133 
135  updateGui();
136 }
137 
138 void QgsPropertyOverrideButton::init( int propertyKey, const QgsAbstractPropertyCollection &collection, const QgsPropertiesDefinition &definitions, const QgsVectorLayer *layer, bool auxiliaryStorageEnabled )
139 {
140  init( propertyKey, collection.property( propertyKey ), definitions, layer, auxiliaryStorageEnabled );
141 }
142 
143 
145 {
146  mFieldNameList.clear();
147  mFieldTypeList.clear();
148 
149  if ( mVectorLayer )
150  {
151  // store just a list of fields of unknown type or those that match the expected type
152  const QgsFields fields = mVectorLayer->fields();
153  for ( const QgsField &f : fields )
154  {
155  bool fieldMatch = false;
156  QString fieldType;
157 
158  switch ( mDataTypes )
159  {
161  fieldMatch = true;
162  break;
163 
165  fieldMatch = f.isNumeric() || f.type() == QVariant::String;
166  break;
167 
169  fieldMatch = f.type() == QVariant::String;
170  break;
171  }
172 
173  switch ( f.type() )
174  {
175  case QVariant::String:
176  fieldType = tr( "string" );
177  break;
178  case QVariant::Int:
179  fieldType = tr( "integer" );
180  break;
181  case QVariant::LongLong:
182  fieldType = tr( "integer64" );
183  break;
184  case QVariant::Double:
185  fieldType = tr( "double" );
186  break;
187  case QVariant::Bool:
188  fieldType = tr( "boolean" );
189  break;
190  default:
191  fieldType = tr( "unknown type" );
192  }
193  if ( fieldMatch )
194  {
195  mFieldNameList << f.name();
196  mFieldTypeList << fieldType;
197  }
198  }
199  }
200 }
201 
203 {
204  return mProperty;
205 }
206 
208 {
209  mVectorLayer = layer;
210 }
211 
212 void QgsPropertyOverrideButton::registerCheckedWidget( QWidget *widget, bool natural )
213 {
214  Q_FOREACH ( const SiblingWidget &sw, mSiblingWidgets )
215  {
216  if ( widget == sw.mWidgetPointer.data() && sw.mSiblingType == SiblingCheckState )
217  return;
218  }
219  mSiblingWidgets.append( SiblingWidget( QPointer<QWidget>( widget ), SiblingCheckState, natural ) );
220  updateSiblingWidgets( isActive() );
221 }
222 
223 void QgsPropertyOverrideButton::registerEnabledWidget( QWidget *widget, bool natural )
224 {
225  Q_FOREACH ( const SiblingWidget &sw, mSiblingWidgets )
226  {
227  if ( widget == sw.mWidgetPointer.data() && sw.mSiblingType == SiblingEnableState )
228  return;
229  }
230  mSiblingWidgets.append( SiblingWidget( QPointer<QWidget>( widget ), SiblingEnableState, natural ) );
231  updateSiblingWidgets( isActive() );
232 }
233 
234 void QgsPropertyOverrideButton::registerVisibleWidget( QWidget *widget, bool natural )
235 {
236  Q_FOREACH ( const SiblingWidget &sw, mSiblingWidgets )
237  {
238  if ( widget == sw.mWidgetPointer.data() && sw.mSiblingType == SiblingVisibility )
239  return;
240  }
241  mSiblingWidgets.append( SiblingWidget( QPointer<QWidget>( widget ), SiblingVisibility, natural ) );
242  updateSiblingWidgets( isActive() );
243 }
244 
246 {
247  Q_FOREACH ( const SiblingWidget &sw, mSiblingWidgets )
248  {
249  if ( widget == sw.mWidgetPointer.data() && sw.mSiblingType == SiblingExpressionText )
250  return;
251  }
252  mSiblingWidgets.append( SiblingWidget( QPointer<QWidget>( widget ), SiblingExpressionText ) );
253  updateSiblingWidgets( isActive() );
254 }
255 
256 
258 {
259  // Ctrl-click to toggle activated state
260  if ( ( event->modifiers() & ( Qt::ControlModifier ) )
261  || event->button() == Qt::RightButton )
262  {
263  setActivePrivate( !mProperty.isActive() );
264  updateGui();
265  emit changed();
266  event->ignore();
267  return;
268  }
269 
270  // pass to default behavior
271  QToolButton::mousePressEvent( event );
272 }
273 
275 {
276  if ( property )
277  {
278  switch ( property.propertyType() )
279  {
282  break;
284  {
285  mFieldName = property.field();
286  break;
287  }
289  {
290  mExpressionString = property.expressionString();
291  break;
292  }
293  }
294  }
295  else
296  {
297  mFieldName.clear();
298  mExpressionString.clear();
299  }
300  mProperty = property;
301  setActive( mProperty && mProperty.isActive() );
302  updateSiblingWidgets( isActive() );
303  updateGui();
304 }
305 
306 void QgsPropertyOverrideButton::aboutToShowMenu()
307 {
308  mDefineMenu->clear();
309  // update fields so that changes made to layer's fields are reflected
311 
312  bool hasExp = !mExpressionString.isEmpty();
313  QString ddTitle = tr( "Data defined override" );
314 
315  QAction *ddTitleAct = mDefineMenu->addAction( ddTitle );
316  QFont titlefont = ddTitleAct->font();
317  titlefont.setItalic( true );
318  ddTitleAct->setFont( titlefont );
319  ddTitleAct->setEnabled( false );
320 
321  bool addActiveAction = false;
322  if ( mProperty.propertyType() == QgsProperty::ExpressionBasedProperty && hasExp )
323  {
324  QgsExpression exp( mExpressionString );
325  // whether expression is parse-able
326  addActiveAction = !exp.hasParserError();
327  }
328  else if ( mProperty.propertyType() == QgsProperty::FieldBasedProperty )
329  {
330  // whether field exists
331  addActiveAction = mFieldNameList.contains( mFieldName );
332  }
333 
334  if ( addActiveAction )
335  {
336  ddTitleAct->setText( ddTitle + " (" + ( mProperty.propertyType() == QgsProperty::ExpressionBasedProperty ? tr( "expression" ) : tr( "field" ) ) + ')' );
337  mDefineMenu->addAction( mActionActive );
338  mActionActive->setText( mProperty.isActive() ? tr( "Deactivate" ) : tr( "Activate" ) );
339  mActionActive->setData( QVariant( !mProperty.isActive() ) );
340  }
341 
342  if ( !mFullDescription.isEmpty() )
343  {
344  mDefineMenu->addAction( mActionDescription );
345  }
346 
347  mDefineMenu->addSeparator();
348 
349  // deactivate button if field already exists
350  if ( mAuxiliaryStorageEnabled && mVectorLayer )
351  {
352  mDefineMenu->addAction( mActionCreateAuxiliaryField );
353 
354  const QgsAuxiliaryLayer *alayer = mVectorLayer->auxiliaryLayer();
355 
356  mActionCreateAuxiliaryField->setEnabled( true );
357  mActionCreateAuxiliaryField->setChecked( false );
358 
359  int index = mVectorLayer->fields().indexFromName( mFieldName );
360  int srcIndex;
361  if ( index >= 0 && alayer && mVectorLayer->isAuxiliaryField( index, srcIndex ) )
362  {
363  mActionCreateAuxiliaryField->setEnabled( false );
364  mActionCreateAuxiliaryField->setChecked( true );
365  }
366  }
367 
368  bool fieldActive = false;
369  if ( !mDataTypesString.isEmpty() )
370  {
371  QAction *fieldTitleAct = mDefineMenu->addAction( tr( "Attribute Field" ) );
372  fieldTitleAct->setFont( titlefont );
373  fieldTitleAct->setEnabled( false );
374 
375  mDefineMenu->addAction( mActionDataTypes );
376 
377  mFieldsMenu->clear();
378 
379  if ( !mFieldNameList.isEmpty() )
380  {
381 
382  for ( int j = 0; j < mFieldNameList.count(); ++j )
383  {
384  QString fldname = mFieldNameList.at( j );
385  QAction *act = mFieldsMenu->addAction( fldname + " (" + mFieldTypeList.at( j ) + ')' );
386  act->setData( QVariant( fldname ) );
387  if ( mFieldName == fldname )
388  {
389  act->setCheckable( true );
390  act->setChecked( mProperty.propertyType() == QgsProperty::FieldBasedProperty );
391  fieldActive = mProperty.propertyType() == QgsProperty::FieldBasedProperty;
392  }
393  }
394  }
395  else
396  {
397  QAction *act = mFieldsMenu->addAction( tr( "No matching field types found" ) );
398  act->setEnabled( false );
399  }
400 
401  mDefineMenu->addSeparator();
402  }
403 
404  mFieldsMenu->menuAction()->setCheckable( true );
405  mFieldsMenu->menuAction()->setChecked( fieldActive && mProperty.propertyType() == QgsProperty::FieldBasedProperty && !mProperty.transformer() );
406 
407  QAction *exprTitleAct = mDefineMenu->addAction( tr( "Expression" ) );
408  exprTitleAct->setFont( titlefont );
409  exprTitleAct->setEnabled( false );
410 
411  mVariablesMenu->clear();
412  bool variableActive = false;
413  if ( mExpressionContextGenerator )
414  {
415  QgsExpressionContext context = mExpressionContextGenerator->createExpressionContext();
416  QStringList variables = context.variableNames();
417  Q_FOREACH ( const QString &variable, variables )
418  {
419  if ( context.isReadOnly( variable ) ) //only want to show user-set variables
420  continue;
421  if ( variable.startsWith( '_' ) ) //no hidden variables
422  continue;
423 
424  QAction *act = mVariablesMenu->addAction( variable );
425  act->setData( QVariant( variable ) );
426 
427  if ( mProperty.propertyType() == QgsProperty::ExpressionBasedProperty && hasExp && mExpressionString == '@' + variable )
428  {
429  act->setCheckable( true );
430  act->setChecked( true );
431  variableActive = true;
432  }
433  }
434  }
435 
436  if ( mVariablesMenu->actions().isEmpty() )
437  {
438  QAction *act = mVariablesMenu->addAction( tr( "No variables set" ) );
439  act->setEnabled( false );
440  }
441 
442  mDefineMenu->addAction( mActionVariables );
443  mVariablesMenu->menuAction()->setCheckable( true );
444  mVariablesMenu->menuAction()->setChecked( variableActive && !mProperty.transformer() );
445 
446  if ( hasExp )
447  {
448  QString expString = mExpressionString;
449  if ( expString.length() > 35 )
450  {
451  expString.truncate( 35 );
452  expString.append( QChar( 0x2026 ) );
453  }
454 
455  expString.prepend( tr( "Current: " ) );
456 
457  if ( !mActionExpression )
458  {
459  mActionExpression = new QAction( expString, this );
460  mActionExpression->setCheckable( true );
461  }
462  else
463  {
464  mActionExpression->setText( expString );
465  }
466  mDefineMenu->addAction( mActionExpression );
467  mActionExpression->setChecked( mProperty.propertyType() == QgsProperty::ExpressionBasedProperty && !variableActive && !mProperty.transformer() );
468 
469  mDefineMenu->addAction( mActionExpDialog );
470  mDefineMenu->addAction( mActionCopyExpr );
471  mDefineMenu->addAction( mActionPasteExpr );
472  }
473  else
474  {
475  mDefineMenu->addAction( mActionExpDialog );
476  mDefineMenu->addAction( mActionPasteExpr );
477  }
478 
479  if ( hasExp || !mFieldName.isEmpty() )
480  {
481  mDefineMenu->addSeparator();
482  mDefineMenu->addAction( mActionClearExpr );
483  }
484 
485  if ( !mDefinition.name().isEmpty() && mDefinition.supportsAssistant() )
486  {
487  mDefineMenu->addSeparator();
488  mActionAssistant->setCheckable( mProperty.transformer() );
489  mActionAssistant->setChecked( mProperty.transformer() );
490  mDefineMenu->addAction( mActionAssistant );
491  }
492 }
493 
494 void QgsPropertyOverrideButton::menuActionTriggered( QAction *action )
495 {
496  if ( action == mActionActive )
497  {
498  setActivePrivate( mActionActive->data().toBool() );
499  updateGui();
500  emit changed();
501  }
502  else if ( action == mActionDescription )
503  {
504  showDescriptionDialog();
505  }
506  else if ( action == mActionExpDialog )
507  {
508  showExpressionDialog();
509  }
510  else if ( action == mActionExpression )
511  {
512  mProperty.setExpressionString( mExpressionString );
513  mProperty.setTransformer( nullptr );
514  setActivePrivate( true );
515  updateSiblingWidgets( isActive() );
516  updateGui();
517  emit changed();
518  }
519  else if ( action == mActionCopyExpr )
520  {
521  QApplication::clipboard()->setText( mExpressionString );
522  }
523  else if ( action == mActionPasteExpr )
524  {
525  QString exprString = QApplication::clipboard()->text();
526  if ( !exprString.isEmpty() )
527  {
528  mExpressionString = exprString;
529  mProperty.setExpressionString( mExpressionString );
530  mProperty.setTransformer( nullptr );
531  setActivePrivate( true );
532  updateSiblingWidgets( isActive() );
533  updateGui();
534  emit changed();
535  }
536  }
537  else if ( action == mActionClearExpr )
538  {
539  setActivePrivate( false );
540  mProperty.setStaticValue( QVariant() );
541  mProperty.setTransformer( nullptr );
542  mExpressionString.clear();
543  updateSiblingWidgets( isActive() );
544  updateGui();
545  emit changed();
546  }
547  else if ( action == mActionAssistant )
548  {
549  showAssistant();
550  }
551  else if ( action == mActionCreateAuxiliaryField )
552  {
553  emit createAuxiliaryField();
554  }
555  else if ( mFieldsMenu->actions().contains( action ) ) // a field name clicked
556  {
557  if ( action->isEnabled() )
558  {
559  if ( mFieldName != action->text() )
560  {
561  mFieldName = action->data().toString();
562  }
563  mProperty.setField( mFieldName );
564  mProperty.setTransformer( nullptr );
565  setActivePrivate( true );
566  updateSiblingWidgets( isActive() );
567  updateGui();
568  emit changed();
569  }
570  }
571  else if ( mVariablesMenu->actions().contains( action ) ) // a variable name clicked
572  {
573  if ( mExpressionString != action->text().prepend( "@" ) )
574  {
575  mExpressionString = action->data().toString().prepend( "@" );
576  }
577  mProperty.setExpressionString( mExpressionString );
578  mProperty.setTransformer( nullptr );
579  setActivePrivate( true );
580  updateSiblingWidgets( isActive() );
581  updateGui();
582  emit changed();
583  }
584 }
585 
586 void QgsPropertyOverrideButton::showDescriptionDialog()
587 {
588  QgsMessageViewer *mv = new QgsMessageViewer( this );
589  mv->setWindowTitle( tr( "Data Definition Description" ) );
590  mv->setMessageAsHtml( mFullDescription );
591  mv->exec();
592 }
593 
594 
595 void QgsPropertyOverrideButton::showExpressionDialog()
596 {
597  QgsExpressionContext context = mExpressionContextGenerator ? mExpressionContextGenerator->createExpressionContext() : QgsExpressionContext();
598 
599  // build sensible initial expression text - see https://issues.qgis.org/issues/18638
600  QString currentExpression = ( mProperty.propertyType() == QgsProperty::StaticProperty && !mProperty.staticValue().isValid() ) ? QString()
601  : mProperty.asExpression();
602 
603  QgsExpressionBuilderDialog d( const_cast<QgsVectorLayer *>( mVectorLayer ), currentExpression, this, QStringLiteral( "generic" ), context );
604  d.setExpectedOutputFormat( mInputDescription );
605  if ( d.exec() == QDialog::Accepted )
606  {
607  mExpressionString = d.expressionText().trimmed();
608  mProperty.setExpressionString( mExpressionString );
609  mProperty.setTransformer( nullptr );
610  setActivePrivate( !mExpressionString.isEmpty() );
611  updateSiblingWidgets( isActive() );
612  updateGui();
613  emit changed();
614  }
615  activateWindow(); // reset focus to parent window
616 }
617 
618 void QgsPropertyOverrideButton::showAssistant()
619 {
620  //first step - try to convert any existing expression to a transformer if one doesn't
621  //already exist
622  if ( !mProperty.transformer() )
623  {
624  ( void )mProperty.convertToTransformer();
625  }
626 
628  QgsPropertyAssistantWidget *widget = new QgsPropertyAssistantWidget( panel, mDefinition, mProperty, mVectorLayer );
629  widget->registerExpressionContextGenerator( mExpressionContextGenerator );
630  widget->setSymbol( mSymbol ); // we only show legend preview in dialog version
631 
632  if ( panel && panel->dockMode() )
633  {
634  connect( widget, &QgsPropertyAssistantWidget::widgetChanged, this, [this, widget]
635  {
636  widget->updateProperty( this->mProperty );
637  mExpressionString = this->mProperty.asExpression();
638  mFieldName = this->mProperty.field();
639  updateSiblingWidgets( isActive() );
640  this->emit changed();
641  } );
642 
643  connect( widget, &QgsPropertyAssistantWidget::panelAccepted, this, [ = ] { updateGui(); } );
644 
645  panel->openPanel( widget );
646  return;
647  }
648  else
649  {
650  // Show the dialog version if not in a panel
651  QDialog *dlg = new QDialog( this );
652  QString key = QStringLiteral( "/UI/paneldialog/%1" ).arg( widget->panelTitle() );
653  QgsSettings settings;
654  dlg->restoreGeometry( settings.value( key ).toByteArray() );
655  dlg->setWindowTitle( widget->panelTitle() );
656  dlg->setLayout( new QVBoxLayout() );
657  dlg->layout()->addWidget( widget );
658  QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Cancel | QDialogButtonBox::Help | QDialogButtonBox::Ok );
659  connect( buttonBox, &QDialogButtonBox::accepted, dlg, &QDialog::accept );
660  connect( buttonBox, &QDialogButtonBox::rejected, dlg, &QDialog::reject );
661  connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsPropertyOverrideButton::showHelp );
662  dlg->layout()->addWidget( buttonBox );
663 
664  if ( dlg->exec() == QDialog::Accepted )
665  {
666  widget->updateProperty( mProperty );
667  mExpressionString = mProperty.asExpression();
668  mFieldName = mProperty.field();
669  widget->acceptPanel();
670  updateSiblingWidgets( isActive() );
671  updateGui();
672 
673  emit changed();
674  }
675  settings.setValue( key, dlg->saveGeometry() );
676  }
677 }
678 
679 void QgsPropertyOverrideButton::updateGui()
680 {
681  bool hasExp = !mExpressionString.isEmpty();
682  bool hasField = !mFieldName.isEmpty();
683 
684  QIcon icon = QgsApplication::getThemeIcon( QStringLiteral( "/mIconDataDefine.svg" ) );
685  QString deftip = tr( "undefined" );
686  if ( mProperty.propertyType() == QgsProperty::ExpressionBasedProperty && hasExp )
687  {
688  icon = mProperty.isActive() ? QgsApplication::getThemeIcon( QStringLiteral( "/mIconDataDefineExpressionOn.svg" ) ) : QgsApplication::getThemeIcon( QStringLiteral( "/mIconDataDefineExpression.svg" ) );
689 
690  QgsExpression exp( mExpressionString );
691  if ( exp.hasParserError() )
692  {
693  icon = QgsApplication::getThemeIcon( QStringLiteral( "/mIconDataDefineExpressionError.svg" ) );
694  deftip = tr( "Parse error: %1" ).arg( exp.parserErrorString() );
695  }
696  else
697  {
698  deftip = mExpressionString;
699  }
700  }
701  else if ( mProperty.propertyType() != QgsProperty::ExpressionBasedProperty && hasField )
702  {
703  icon = mProperty.isActive() ? QgsApplication::getThemeIcon( QStringLiteral( "/mIconDataDefineOn.svg" ) ) : QgsApplication::getThemeIcon( QStringLiteral( "/mIconDataDefine.svg" ) );
704 
705  if ( !mFieldNameList.contains( mFieldName ) && !mProperty.transformer() )
706  {
707  icon = QgsApplication::getThemeIcon( QStringLiteral( "/mIconDataDefineError.svg" ) );
708  deftip = tr( "'%1' field missing" ).arg( mFieldName );
709  }
710  else
711  {
712  deftip = mFieldName;
713  }
714  }
715 
716  setIcon( icon );
717 
718  // build full description for tool tip and popup dialog
719  mFullDescription = tr( "<b><u>Data defined override</u></b><br>" );
720 
721  mFullDescription += tr( "<b>Active: </b>%1&nbsp;&nbsp;&nbsp;<i>(ctrl|right-click toggles)</i><br>" ).arg( mProperty.isActive() ? tr( "yes" ) : tr( "no" ) );
722 
723  if ( !mUsageInfo.isEmpty() )
724  {
725  mFullDescription += tr( "<b>Usage:</b><br>%1<br>" ).arg( mUsageInfo );
726  }
727 
728  if ( !mInputDescription.isEmpty() )
729  {
730  mFullDescription += tr( "<b>Expected input:</b><br>%1<br>" ).arg( mInputDescription );
731  }
732 
733  if ( !mDataTypesString.isEmpty() )
734  {
735  mFullDescription += tr( "<b>Valid input types:</b><br>%1<br>" ).arg( mDataTypesString );
736  }
737 
738  QString deftype;
739  if ( deftip != tr( "undefined" ) )
740  {
741  deftype = QStringLiteral( " (%1)" ).arg( mProperty.propertyType() == QgsProperty::ExpressionBasedProperty ? tr( "expression" ) : tr( "field" ) );
742  }
743 
744  // truncate long expressions, or tool tip may be too wide for screen
745  if ( deftip.length() > 75 )
746  {
747  deftip.truncate( 75 );
748  deftip.append( QChar( 0x2026 ) );
749  }
750 
751  mFullDescription += tr( "<b>Current definition %1:</b><br>%2" ).arg( deftype, deftip );
752 
753  setToolTip( mFullDescription );
754 
755 }
756 
757 void QgsPropertyOverrideButton::setActivePrivate( bool active )
758 {
759  if ( mProperty.isActive() != active )
760  {
761  mProperty.setActive( active );
762  emit activated( mProperty.isActive() );
763  }
764 }
765 
766 void QgsPropertyOverrideButton::updateSiblingWidgets( bool state )
767 {
768 
769  Q_FOREACH ( const SiblingWidget &sw, mSiblingWidgets )
770  {
771  switch ( sw.mSiblingType )
772  {
773 
774  case SiblingCheckState:
775  {
776  // don't uncheck, only set to checked
777  if ( state )
778  {
779  QAbstractButton *btn = qobject_cast< QAbstractButton * >( sw.mWidgetPointer.data() );
780  if ( btn && btn->isCheckable() )
781  {
782  btn->setChecked( sw.mNatural ? state : !state );
783  }
784  else
785  {
786  QGroupBox *grpbx = qobject_cast< QGroupBox * >( sw.mWidgetPointer.data() );
787  if ( grpbx && grpbx->isCheckable() )
788  {
789  grpbx->setChecked( sw.mNatural ? state : !state );
790  }
791  }
792  }
793  break;
794  }
795 
796  case SiblingEnableState:
797  {
798  QLineEdit *le = qobject_cast< QLineEdit * >( sw.mWidgetPointer.data() );
799  if ( le )
800  le->setReadOnly( sw.mNatural ? !state : state );
801  else
802  sw.mWidgetPointer.data()->setEnabled( sw.mNatural ? state : !state );
803  break;
804  }
805 
806  case SiblingVisibility:
807  {
808  sw.mWidgetPointer.data()->setVisible( sw.mNatural ? state : !state );
809  break;
810  }
811 
812  case SiblingExpressionText:
813  {
814  QLineEdit *le = qobject_cast<QLineEdit *>( sw.mWidgetPointer.data() );
815  if ( le )
816  {
817  le->setText( mProperty.asExpression() );
818  }
819  else
820  {
821  QTextEdit *te = qobject_cast<QTextEdit *>( sw.mWidgetPointer.data() );
822  if ( te )
823  {
824  te->setText( mProperty.asExpression() );
825  }
826  }
827  break;
828  }
829 
830  default:
831  break;
832  }
833 
834 
835  }
836 }
837 
838 
839 
841 {
842  if ( mProperty.isActive() != active )
843  {
844  mProperty.setActive( active );
845  emit changed();
846  emit activated( mProperty.isActive() );
847  }
848 }
849 
851 {
852  mExpressionContextGenerator = generator;
853 }
854 
855 void QgsPropertyOverrideButton::showHelp()
856 {
857  QgsHelp::openHelp( QStringLiteral( "introduction/general_tools.html#data-defined" ) );
858 }
bool isAuxiliaryField(int index, int &srcIndex) const
Returns true if the field comes from the auxiliary layer, false otherwise.
Class for parsing and evaluation of expressions (formerly called "search strings").
void openPanel(QgsPanelWidget *panel)
Open a panel or dialog depending on dock mode setting If dock mode is true this method will emit the ...
QString helpText() const
Helper text for using the property, including a description of the valid values for the property...
Definition: qgsproperty.h:177
Field based property (QgsFieldBasedProperty)
Definition: qgsproperty.h:238
bool hasParserError() const
Returns true if an error occurred when parsing the input expression.
bool dockMode()
Returns the dock mode state.
void setExpectedOutputFormat(const QString &expected)
Set the expected format string, which is shown in the dialog.
This class is a composition of two QSettings instances:
Definition: qgssettings.h:58
int propertyKey() const
Returns the property key linked to the button.
Expression based property (QgsExpressionBasedProperty)
Definition: qgsproperty.h:239
int scaleIconSize(int standardSize)
Scales an icon size to compensate for display pixel density, making the icon size hi-dpi friendly...
void setSymbol(std::shared_ptr< QgsSymbol > symbol)
Sets a symbol which can be used for previews inside the widget.
const QgsPropertyTransformer * transformer() const
Returns the existing transformer used for manipulating the calculated values for the property...
Class allowing to manage the auxiliary storage for a vector layer.
void registerEnabledWidget(QWidget *widget, bool natural=true)
Register a sibling widget that gets enabled when the property is active, and disabled when the proper...
void setVectorLayer(const QgsVectorLayer *layer)
Sets the vector layer associated with the button.
bool isActive() const
Returns whether the property is currently active.
void mouseReleaseEvent(QMouseEvent *event) override
void registerExpressionContextGenerator(QgsExpressionContextGenerator *generator)
Register an expression context generator class that will be used to retrieve an expression context fo...
DataType dataType() const
Returns the allowable field/value data type for the property.
Definition: qgsproperty.h:187
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
Container of fields for a vector layer.
Definition: qgsfields.h:42
static QIcon getThemeIcon(const QString &name)
Helper to get a theme icon.
Base class for any widget that can be shown as a inline panel.
void activated(bool isActive)
Emitted when the activated status of the widget changes.
QVariant staticValue() const
Returns the current static value for the property.
void panelAccepted(QgsPanelWidget *panel)
Emitted when the panel is accepted by the user.
void registerVisibleWidget(QWidget *widget, bool natural=true)
Register a sibling widget that gets visible when the property is active, and hidden when the property...
void createAuxiliaryField()
Emitted when creating a new auxiliary field.
void setMessageAsHtml(const QString &msg)
void setField(const QString &field)
Sets the field name the property references.
QStringList variableNames() const
Returns a list of variables names set by all scopes in the context.
virtual QgsExpressionContext createExpressionContext() const =0
This method needs to be reimplemented in all classes which implement this interface and return an exp...
QgsFields fields() const FINAL
Returns the list of fields of this layer.
#define FALLTHROUGH
Definition: qgis.h:646
QString name() const
Returns the name of the property.
Definition: qgsproperty.h:138
Property requires a boolean value.
Definition: qgsproperty.h:105
bool convertToTransformer()
Attempts to convert an existing expression based property to a base expression with corresponding tra...
static QgsPanelWidget * findParentPanel(QWidget *widget)
Traces through the parents of a widget to find if it is contained within a QgsPanelWidget widget...
void setToProperty(const QgsProperty &property)
Sets the widget to reflect the current state of a QgsProperty.
void acceptPanel()
Accept the panel.
void registerExpressionContextGenerator(QgsExpressionContextGenerator *generator)
Register an expression context generator class that will be used to retrieve an expression context fo...
Shows a user-friendly assistant guiding users through the creation of QgsProperty overrides...
void setActive(bool active)
Sets whether the property is currently active.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
Type propertyType() const
Returns the property type.
Abstract base class for QgsPropertyCollection like objects.
Property requires a numeric value.
Definition: qgsproperty.h:98
bool isReadOnly(const QString &name) const
Returns whether a variable is read only, and should not be modifiable by users.
QString panelTitle()
The title of the panel.
virtual QgsProperty property(int key) const =0
Returns a matching property from the collection, if one exists.
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:48
A store for object properties.
Definition: qgsproperty.h:229
QgsAuxiliaryLayer * auxiliaryLayer()
Returns the current auxiliary layer.
void widgetChanged()
Emitted when the widget state changes.
void setExpressionString(const QString &expression)
Sets the expression to use for the property value.
Definition for a property.
Definition: qgsproperty.h:46
int indexFromName(const QString &fieldName) const
Gets the field index from the field name.
Definition: qgsfields.cpp:202
QMap< int, QgsPropertyDefinition > QgsPropertiesDefinition
Definition of available properties.
Abstract interface for generating an expression context.
QString field() const
Returns the current field name the property references.
void changed()
Emitted when property definition changes.
QgsProperty toProperty() const
Returns a QgsProperty object encapsulating the current state of the widget.
void registerCheckedWidget(QWidget *widget, bool natural=true)
Register a sibling widget that gets checked when the property is active.
QVariant value(const QgsExpressionContext &context, const QVariant &defaultValue=QVariant(), bool *ok=nullptr) const
Calculates the current value of the property, including any transforms which are set for the property...
bool isActive() const
Returns true if the button has an active property.
Property requires a string value.
Definition: qgsproperty.h:91
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
void registerExpressionWidget(QWidget *widget)
Register a sibling widget (line edit, text edit) that will receive the property as an expression...
QString asExpression() const
Returns an expression string representing the state of the property, or an empty string if the proper...
QgsPropertyOverrideButton(QWidget *parent=nullptr, const QgsVectorLayer *layer=nullptr)
Constructor for QgsPropertyOverrideButton.
A generic message view for displaying QGIS messages.
static void openHelp(const QString &key)
Opens help topic for the given help key using default system web browser.
Definition: qgshelp.cpp:35
Represents a vector layer which manages a vector based data sets.
void updateFieldLists()
Updates list of fields.
Invalid (not set) property.
Definition: qgsproperty.h:237
QString parserErrorString() const
Returns parser error.
void init(int propertyKey, const QgsProperty &property, const QgsPropertiesDefinition &definitions, const QgsVectorLayer *layer=nullptr, bool auxiliaryStorageEnabled=false)
Initialize a newly constructed property button (useful if button was included in a UI layout)...
void setStaticValue(const QVariant &value)
Sets the static value for the property.
A generic dialog for building expression strings.
void setActive(bool active)
Set whether the current property override definition is to be used.
void setTransformer(QgsPropertyTransformer *transformer)
Sets an optional transformer to use for manipulating the calculated values for the property...
void updateProperty(QgsProperty &property)
Updates a property in place to corresponding to the current settings shown in the widget...
bool supportsAssistant() const
Returns true if the property is of a type which is compatible with property override assistants...