QGIS API Documentation  3.2.0-Bonn (bc43194)
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 
27 #include <QClipboard>
28 #include <QMenu>
29 #include <QMouseEvent>
30 #include <QPointer>
31 #include <QGroupBox>
32 
34  const QgsVectorLayer *layer )
35  : QToolButton( parent )
36  , mVectorLayer( layer )
37 
38 {
39  setFocusPolicy( Qt::StrongFocus );
40 
41  // icon size is a bit bigger than text, but minimum size of 24 so that we get pixel-aligned rendering on low-dpi screens
42  int iconSize = std::floor( std::max( Qgis::UI_SCALE_FACTOR * fontMetrics().height() * 1.1, 24.0 ) );
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  QString ss;
47  ss += QStringLiteral( "QToolButton{ background: none; border: 1px solid rgba(0, 0, 0, 0%); } QToolButton:focus { border: 1px solid palette(highlight); }" );
48 #ifdef Q_OS_MACX
49  ss += QStringLiteral( "QToolButton::menu-indicator{ width: 5px; }" );
50 #endif
51  setStyleSheet( ss );
52 
53  setIconSize( QSize( iconSize, iconSize ) );
54  setPopupMode( QToolButton::InstantPopup );
55 
56  connect( this, &QgsPropertyOverrideButton::activated, this, &QgsPropertyOverrideButton::updateSiblingWidgets );
57 
58  mDefineMenu = new QMenu( this );
59  connect( mDefineMenu, &QMenu::aboutToShow, this, &QgsPropertyOverrideButton::aboutToShowMenu );
60  connect( mDefineMenu, &QMenu::triggered, this, &QgsPropertyOverrideButton::menuActionTriggered );
61  setMenu( mDefineMenu );
62 
63  mFieldsMenu = new QMenu( this );
64  mActionDataTypes = new QAction( this );
65  // list fields and types in submenu, since there may be many
66  mActionDataTypes->setMenu( mFieldsMenu );
67 
68  mActionVariables = new QAction( tr( "Variable" ), this );
69  mVariablesMenu = new QMenu( this );
70  mActionVariables->setMenu( mVariablesMenu );
71 
72  mActionActive = new QAction( this );
73  QFont f = mActionActive->font();
74  f.setBold( true );
75  mActionActive->setFont( f );
76 
77  mActionDescription = new QAction( tr( "Description…" ), this );
78 
79  mActionCreateAuxiliaryField = new QAction( tr( "Store Data in the Project" ), this );
80  mActionCreateAuxiliaryField->setCheckable( true );
81 
82  mActionExpDialog = new QAction( tr( "Edit…" ), this );
83  mActionExpression = nullptr;
84  mActionPasteExpr = new QAction( tr( "Paste" ), this );
85  mActionCopyExpr = new QAction( tr( "Copy" ), this );
86  mActionClearExpr = new QAction( tr( "Clear" ), this );
87  mActionAssistant = new QAction( tr( "Assistant…" ), this );
88  QFont assistantFont = mActionAssistant->font();
89  assistantFont.setBold( true );
90  mActionAssistant->setFont( assistantFont );
91  mDefineMenu->addAction( mActionAssistant );
92 }
93 
94 
95 void QgsPropertyOverrideButton::init( int propertyKey, const QgsProperty &property, const QgsPropertiesDefinition &definitions, const QgsVectorLayer *layer, bool auxiliaryStorageEnabled )
96 {
97  init( propertyKey, property, definitions.value( propertyKey ), layer, auxiliaryStorageEnabled );
98 }
99 
100 void QgsPropertyOverrideButton::init( int propertyKey, const QgsProperty &property, const QgsPropertyDefinition &definition, const QgsVectorLayer *layer, bool auxiliaryStorageEnabled )
101 {
102  mVectorLayer = layer;
103  mAuxiliaryStorageEnabled = auxiliaryStorageEnabled;
104  setToProperty( property );
105  mPropertyKey = propertyKey;
106 
107  mDefinition = definition;
108  mDataTypes = mDefinition.dataType();
109 
110  mInputDescription = mDefinition.helpText();
111  mFullDescription.clear();
112  mUsageInfo.clear();
113 
114  // set up data types string
115  mDataTypesString.clear();
116 
117  QStringList ts;
118  switch ( mDataTypes )
119  {
121  ts << tr( "boolean" );
122  FALLTHROUGH;
123 
125  ts << tr( "int" );
126  ts << tr( "double" );
127  FALLTHROUGH;
128 
130  ts << tr( "string" );
131  break;
132  }
133 
134  if ( !ts.isEmpty() )
135  {
136  mDataTypesString = ts.join( QStringLiteral( ", " ) );
137  mActionDataTypes->setText( tr( "Field type: " ) + mDataTypesString );
138  }
139 
141  updateGui();
142 }
143 
144 void QgsPropertyOverrideButton::init( int propertyKey, const QgsAbstractPropertyCollection &collection, const QgsPropertiesDefinition &definitions, const QgsVectorLayer *layer, bool auxiliaryStorageEnabled )
145 {
146  init( propertyKey, collection.property( propertyKey ), definitions, layer, auxiliaryStorageEnabled );
147 }
148 
149 
151 {
152  mFieldNameList.clear();
153  mFieldTypeList.clear();
154 
155  if ( mVectorLayer )
156  {
157  // store just a list of fields of unknown type or those that match the expected type
158  Q_FOREACH ( const QgsField &f, mVectorLayer->fields() )
159  {
160  bool fieldMatch = false;
161  QString fieldType;
162 
163  switch ( mDataTypes )
164  {
166  fieldMatch = true;
167  break;
168 
170  fieldMatch = f.isNumeric() || f.type() == QVariant::String;
171  break;
172 
174  fieldMatch = f.type() == QVariant::String;
175  break;
176  }
177 
178  switch ( f.type() )
179  {
180  case QVariant::String:
181  fieldType = tr( "string" );
182  break;
183  case QVariant::Int:
184  fieldType = tr( "integer" );
185  break;
186  case QVariant::LongLong:
187  fieldType = tr( "integer64" );
188  break;
189  case QVariant::Double:
190  fieldType = tr( "double" );
191  break;
192  case QVariant::Bool:
193  fieldType = tr( "boolean" );
194  break;
195  default:
196  fieldType = tr( "unknown type" );
197  }
198  if ( fieldMatch )
199  {
200  mFieldNameList << f.name();
201  mFieldTypeList << fieldType;
202  }
203  }
204  }
205 }
206 
208 {
209  return mProperty;
210 }
211 
213 {
214  mVectorLayer = layer;
215 }
216 
217 void QgsPropertyOverrideButton::registerCheckedWidget( QWidget *widget, bool natural )
218 {
219  Q_FOREACH ( const SiblingWidget &sw, mSiblingWidgets )
220  {
221  if ( widget == sw.mWidgetPointer.data() && sw.mSiblingType == SiblingCheckState )
222  return;
223  }
224  mSiblingWidgets.append( SiblingWidget( QPointer<QWidget>( widget ), SiblingCheckState, natural ) );
225  updateSiblingWidgets( isActive() );
226 }
227 
228 void QgsPropertyOverrideButton::registerEnabledWidget( QWidget *widget, bool natural )
229 {
230  Q_FOREACH ( const SiblingWidget &sw, mSiblingWidgets )
231  {
232  if ( widget == sw.mWidgetPointer.data() && sw.mSiblingType == SiblingEnableState )
233  return;
234  }
235  mSiblingWidgets.append( SiblingWidget( QPointer<QWidget>( widget ), SiblingEnableState, natural ) );
236  updateSiblingWidgets( isActive() );
237 }
238 
239 void QgsPropertyOverrideButton::registerVisibleWidget( QWidget *widget, bool natural )
240 {
241  Q_FOREACH ( const SiblingWidget &sw, mSiblingWidgets )
242  {
243  if ( widget == sw.mWidgetPointer.data() && sw.mSiblingType == SiblingVisibility )
244  return;
245  }
246  mSiblingWidgets.append( SiblingWidget( QPointer<QWidget>( widget ), SiblingVisibility, natural ) );
247  updateSiblingWidgets( isActive() );
248 }
249 
251 {
252  Q_FOREACH ( const SiblingWidget &sw, mSiblingWidgets )
253  {
254  if ( widget == sw.mWidgetPointer.data() && sw.mSiblingType == SiblingExpressionText )
255  return;
256  }
257  mSiblingWidgets.append( SiblingWidget( QPointer<QWidget>( widget ), SiblingExpressionText ) );
258  updateSiblingWidgets( isActive() );
259 }
260 
261 
263 {
264  // Ctrl-click to toggle activated state
265  if ( ( event->modifiers() & ( Qt::ControlModifier ) )
266  || event->button() == Qt::RightButton )
267  {
268  setActivePrivate( !mProperty.isActive() );
269  updateGui();
270  emit changed();
271  event->ignore();
272  return;
273  }
274 
275  // pass to default behavior
276  QToolButton::mousePressEvent( event );
277 }
278 
280 {
281  if ( property )
282  {
283  switch ( property.propertyType() )
284  {
287  break;
289  {
290  mFieldName = property.field();
291  break;
292  }
294  {
295  mExpressionString = property.expressionString();
296  break;
297  }
298  }
299  }
300  else
301  {
302  mFieldName.clear();
303  mExpressionString.clear();
304  }
305  mProperty = property;
306  setActive( mProperty && mProperty.isActive() );
307  updateSiblingWidgets( isActive() );
308  updateGui();
309 }
310 
311 void QgsPropertyOverrideButton::aboutToShowMenu()
312 {
313  mDefineMenu->clear();
314  // update fields so that changes made to layer's fields are reflected
316 
317  bool hasExp = !mExpressionString.isEmpty();
318  QString ddTitle = tr( "Data defined override" );
319 
320  QAction *ddTitleAct = mDefineMenu->addAction( ddTitle );
321  QFont titlefont = ddTitleAct->font();
322  titlefont.setItalic( true );
323  ddTitleAct->setFont( titlefont );
324  ddTitleAct->setEnabled( false );
325 
326  bool addActiveAction = false;
327  if ( mProperty.propertyType() == QgsProperty::ExpressionBasedProperty && hasExp )
328  {
329  QgsExpression exp( mExpressionString );
330  // whether expression is parse-able
331  addActiveAction = !exp.hasParserError();
332  }
333  else if ( mProperty.propertyType() == QgsProperty::FieldBasedProperty )
334  {
335  // whether field exists
336  addActiveAction = mFieldNameList.contains( mFieldName );
337  }
338 
339  if ( addActiveAction )
340  {
341  ddTitleAct->setText( ddTitle + " (" + ( mProperty.propertyType() == QgsProperty::ExpressionBasedProperty ? tr( "expression" ) : tr( "field" ) ) + ')' );
342  mDefineMenu->addAction( mActionActive );
343  mActionActive->setText( mProperty.isActive() ? tr( "Deactivate" ) : tr( "Activate" ) );
344  mActionActive->setData( QVariant( !mProperty.isActive() ) );
345  }
346 
347  if ( !mFullDescription.isEmpty() )
348  {
349  mDefineMenu->addAction( mActionDescription );
350  }
351 
352  mDefineMenu->addSeparator();
353 
354  // deactivate button if field already exists
355  if ( mAuxiliaryStorageEnabled && mVectorLayer )
356  {
357  mDefineMenu->addAction( mActionCreateAuxiliaryField );
358 
359  const QgsAuxiliaryLayer *alayer = mVectorLayer->auxiliaryLayer();
360 
361  mActionCreateAuxiliaryField->setEnabled( true );
362  mActionCreateAuxiliaryField->setChecked( false );
363 
364  int index = mVectorLayer->fields().indexFromName( mFieldName );
365  int srcIndex;
366  if ( index >= 0 && alayer && mVectorLayer->isAuxiliaryField( index, srcIndex ) )
367  {
368  mActionCreateAuxiliaryField->setEnabled( false );
369  mActionCreateAuxiliaryField->setChecked( true );
370  }
371  }
372 
373  bool fieldActive = false;
374  if ( !mDataTypesString.isEmpty() )
375  {
376  QAction *fieldTitleAct = mDefineMenu->addAction( tr( "Attribute Field" ) );
377  fieldTitleAct->setFont( titlefont );
378  fieldTitleAct->setEnabled( false );
379 
380  mDefineMenu->addAction( mActionDataTypes );
381 
382  mFieldsMenu->clear();
383 
384  if ( !mFieldNameList.isEmpty() )
385  {
386 
387  for ( int j = 0; j < mFieldNameList.count(); ++j )
388  {
389  QString fldname = mFieldNameList.at( j );
390  QAction *act = mFieldsMenu->addAction( fldname + " (" + mFieldTypeList.at( j ) + ')' );
391  act->setData( QVariant( fldname ) );
392  if ( mFieldName == fldname )
393  {
394  act->setCheckable( true );
395  act->setChecked( mProperty.propertyType() == QgsProperty::FieldBasedProperty );
396  fieldActive = mProperty.propertyType() == QgsProperty::FieldBasedProperty;
397  }
398  }
399  }
400  else
401  {
402  QAction *act = mFieldsMenu->addAction( tr( "No matching field types found" ) );
403  act->setEnabled( false );
404  }
405 
406  mDefineMenu->addSeparator();
407  }
408 
409  mFieldsMenu->menuAction()->setCheckable( true );
410  mFieldsMenu->menuAction()->setChecked( fieldActive && mProperty.propertyType() == QgsProperty::FieldBasedProperty && !mProperty.transformer() );
411 
412  QAction *exprTitleAct = mDefineMenu->addAction( tr( "Expression" ) );
413  exprTitleAct->setFont( titlefont );
414  exprTitleAct->setEnabled( false );
415 
416  mVariablesMenu->clear();
417  bool variableActive = false;
418  if ( mExpressionContextGenerator )
419  {
420  QgsExpressionContext context = mExpressionContextGenerator->createExpressionContext();
421  QStringList variables = context.variableNames();
422  Q_FOREACH ( const QString &variable, variables )
423  {
424  if ( context.isReadOnly( variable ) ) //only want to show user-set variables
425  continue;
426  if ( variable.startsWith( '_' ) ) //no hidden variables
427  continue;
428 
429  QAction *act = mVariablesMenu->addAction( variable );
430  act->setData( QVariant( variable ) );
431 
432  if ( mProperty.propertyType() == QgsProperty::ExpressionBasedProperty && hasExp && mExpressionString == '@' + variable )
433  {
434  act->setCheckable( true );
435  act->setChecked( true );
436  variableActive = true;
437  }
438  }
439  }
440 
441  if ( mVariablesMenu->actions().isEmpty() )
442  {
443  QAction *act = mVariablesMenu->addAction( tr( "No variables set" ) );
444  act->setEnabled( false );
445  }
446 
447  mDefineMenu->addAction( mActionVariables );
448  mVariablesMenu->menuAction()->setCheckable( true );
449  mVariablesMenu->menuAction()->setChecked( variableActive && !mProperty.transformer() );
450 
451  if ( hasExp )
452  {
453  QString expString = mExpressionString;
454  if ( expString.length() > 35 )
455  {
456  expString.truncate( 35 );
457  expString.append( QChar( 0x2026 ) );
458  }
459 
460  expString.prepend( tr( "Current: " ) );
461 
462  if ( !mActionExpression )
463  {
464  mActionExpression = new QAction( expString, this );
465  mActionExpression->setCheckable( true );
466  }
467  else
468  {
469  mActionExpression->setText( expString );
470  }
471  mDefineMenu->addAction( mActionExpression );
472  mActionExpression->setChecked( mProperty.propertyType() == QgsProperty::ExpressionBasedProperty && !variableActive && !mProperty.transformer() );
473 
474  mDefineMenu->addAction( mActionExpDialog );
475  mDefineMenu->addAction( mActionCopyExpr );
476  mDefineMenu->addAction( mActionPasteExpr );
477  mDefineMenu->addAction( mActionClearExpr );
478  }
479  else
480  {
481  mDefineMenu->addAction( mActionExpDialog );
482  mDefineMenu->addAction( mActionPasteExpr );
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  updateGui();
612  emit changed();
613  }
614  activateWindow(); // reset focus to parent window
615 }
616 
617 void QgsPropertyOverrideButton::showAssistant()
618 {
619  //first step - try to convert any existing expression to a transformer if one doesn't
620  //already exist
621  if ( !mProperty.transformer() )
622  {
623  ( void )mProperty.convertToTransformer();
624  }
625 
627  QgsPropertyAssistantWidget *widget = new QgsPropertyAssistantWidget( panel, mDefinition, mProperty, mVectorLayer );
628  widget->registerExpressionContextGenerator( mExpressionContextGenerator );
629  widget->setSymbol( mSymbol ); // we only show legend preview in dialog version
630 
631  if ( panel && panel->dockMode() )
632  {
633  connect( widget, &QgsPropertyAssistantWidget::widgetChanged, this, [this, widget]
634  {
635  widget->updateProperty( this->mProperty );
636  mExpressionString = this->mProperty.asExpression();
637  mFieldName = this->mProperty.field();
638  this->emit changed();
639  } );
640 
641  connect( widget, &QgsPropertyAssistantWidget::panelAccepted, this, [ = ] { updateGui(); } );
642 
643  panel->openPanel( widget );
644  return;
645  }
646  else
647  {
648  // Show the dialog version if not in a panel
649  QDialog *dlg = new QDialog( this );
650  QString key = QStringLiteral( "/UI/paneldialog/%1" ).arg( widget->panelTitle() );
651  QgsSettings settings;
652  dlg->restoreGeometry( settings.value( key ).toByteArray() );
653  dlg->setWindowTitle( widget->panelTitle() );
654  dlg->setLayout( new QVBoxLayout() );
655  dlg->layout()->addWidget( widget );
656  QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Cancel | QDialogButtonBox::Help | QDialogButtonBox::Ok );
657  connect( buttonBox, &QDialogButtonBox::accepted, dlg, &QDialog::accept );
658  connect( buttonBox, &QDialogButtonBox::rejected, dlg, &QDialog::reject );
659  connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsPropertyOverrideButton::showHelp );
660  dlg->layout()->addWidget( buttonBox );
661 
662  if ( dlg->exec() == QDialog::Accepted )
663  {
664  widget->updateProperty( mProperty );
665  mExpressionString = mProperty.asExpression();
666  mFieldName = mProperty.field();
667  widget->acceptPanel();
668  updateGui();
669 
670  emit changed();
671  }
672  settings.setValue( key, dlg->saveGeometry() );
673  }
674 }
675 
676 void QgsPropertyOverrideButton::updateGui()
677 {
678  bool hasExp = !mExpressionString.isEmpty();
679  bool hasField = !mFieldName.isEmpty();
680 
681  QIcon icon = QgsApplication::getThemeIcon( QStringLiteral( "/mIconDataDefine.svg" ) );
682  QString deftip = tr( "undefined" );
683  if ( mProperty.propertyType() == QgsProperty::ExpressionBasedProperty && hasExp )
684  {
685  icon = mProperty.isActive() ? QgsApplication::getThemeIcon( QStringLiteral( "/mIconDataDefineExpressionOn.svg" ) ) : QgsApplication::getThemeIcon( QStringLiteral( "/mIconDataDefineExpression.svg" ) );
686 
687  QgsExpression exp( mExpressionString );
688  if ( exp.hasParserError() )
689  {
690  icon = QgsApplication::getThemeIcon( QStringLiteral( "/mIconDataDefineExpressionError.svg" ) );
691  deftip = tr( "Parse error: %1" ).arg( exp.parserErrorString() );
692  }
693  else
694  {
695  deftip = mExpressionString;
696  }
697  }
698  else if ( mProperty.propertyType() != QgsProperty::ExpressionBasedProperty && hasField )
699  {
700  icon = mProperty.isActive() ? QgsApplication::getThemeIcon( QStringLiteral( "/mIconDataDefineOn.svg" ) ) : QgsApplication::getThemeIcon( QStringLiteral( "/mIconDataDefine.svg" ) );
701 
702  if ( !mFieldNameList.contains( mFieldName ) && !mProperty.transformer() )
703  {
704  icon = QgsApplication::getThemeIcon( QStringLiteral( "/mIconDataDefineError.svg" ) );
705  deftip = tr( "'%1' field missing" ).arg( mFieldName );
706  }
707  else
708  {
709  deftip = mFieldName;
710  }
711  }
712 
713  setIcon( icon );
714 
715  // build full description for tool tip and popup dialog
716  mFullDescription = tr( "<b><u>Data defined override</u></b><br>" );
717 
718  mFullDescription += tr( "<b>Active: </b>%1&nbsp;&nbsp;&nbsp;<i>(ctrl|right-click toggles)</i><br>" ).arg( mProperty.isActive() ? tr( "yes" ) : tr( "no" ) );
719 
720  if ( !mUsageInfo.isEmpty() )
721  {
722  mFullDescription += tr( "<b>Usage:</b><br>%1<br>" ).arg( mUsageInfo );
723  }
724 
725  if ( !mInputDescription.isEmpty() )
726  {
727  mFullDescription += tr( "<b>Expected input:</b><br>%1<br>" ).arg( mInputDescription );
728  }
729 
730  if ( !mDataTypesString.isEmpty() )
731  {
732  mFullDescription += tr( "<b>Valid input types:</b><br>%1<br>" ).arg( mDataTypesString );
733  }
734 
735  QString deftype;
736  if ( deftip != tr( "undefined" ) )
737  {
738  deftype = QStringLiteral( " (%1)" ).arg( mProperty.propertyType() == QgsProperty::ExpressionBasedProperty ? tr( "expression" ) : tr( "field" ) );
739  }
740 
741  // truncate long expressions, or tool tip may be too wide for screen
742  if ( deftip.length() > 75 )
743  {
744  deftip.truncate( 75 );
745  deftip.append( QChar( 0x2026 ) );
746  }
747 
748  mFullDescription += tr( "<b>Current definition %1:</b><br>%2" ).arg( deftype, deftip );
749 
750  setToolTip( mFullDescription );
751 
752 }
753 
754 void QgsPropertyOverrideButton::setActivePrivate( bool active )
755 {
756  if ( mProperty.isActive() != active )
757  {
758  mProperty.setActive( active );
759  emit activated( mProperty.isActive() );
760  }
761 }
762 
763 void QgsPropertyOverrideButton::updateSiblingWidgets( bool state )
764 {
765 
766  Q_FOREACH ( const SiblingWidget &sw, mSiblingWidgets )
767  {
768  switch ( sw.mSiblingType )
769  {
770 
771  case SiblingCheckState:
772  {
773  // don't uncheck, only set to checked
774  if ( state )
775  {
776  QAbstractButton *btn = qobject_cast< QAbstractButton * >( sw.mWidgetPointer.data() );
777  if ( btn && btn->isCheckable() )
778  {
779  btn->setChecked( sw.mNatural ? state : !state );
780  }
781  else
782  {
783  QGroupBox *grpbx = qobject_cast< QGroupBox * >( sw.mWidgetPointer.data() );
784  if ( grpbx && grpbx->isCheckable() )
785  {
786  grpbx->setChecked( sw.mNatural ? state : !state );
787  }
788  }
789  }
790  break;
791  }
792 
793  case SiblingEnableState:
794  {
795  QLineEdit *le = qobject_cast< QLineEdit * >( sw.mWidgetPointer.data() );
796  if ( le )
797  le->setReadOnly( sw.mNatural ? !state : state );
798  else
799  sw.mWidgetPointer.data()->setEnabled( sw.mNatural ? state : !state );
800  break;
801  }
802 
803  case SiblingVisibility:
804  {
805  sw.mWidgetPointer.data()->setVisible( sw.mNatural ? state : !state );
806  break;
807  }
808 
809  case SiblingExpressionText:
810  {
811  QLineEdit *le = qobject_cast<QLineEdit *>( sw.mWidgetPointer.data() );
812  if ( le )
813  {
814  le->setText( mProperty.asExpression() );
815  }
816  else
817  {
818  QTextEdit *te = qobject_cast<QTextEdit *>( sw.mWidgetPointer.data() );
819  if ( te )
820  {
821  te->setText( mProperty.asExpression() );
822  }
823  }
824  break;
825  }
826 
827  default:
828  break;
829  }
830 
831 
832  }
833 }
834 
835 
836 
838 {
839  if ( mProperty.isActive() != active )
840  {
841  mProperty.setActive( active );
842  emit changed();
843  emit activated( mProperty.isActive() );
844  }
845 }
846 
848 {
849  mExpressionContextGenerator = generator;
850 }
851 
852 void QgsPropertyOverrideButton::showHelp()
853 {
854  QgsHelp::openHelp( QStringLiteral( "introduction/general_tools.html#data-defined" ) );
855 }
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 ...
bool hasParserError() const
Returns true if an error occurred when parsing the input expression.
bool supportsAssistant() const
Returns true if the property is of a type which is compatible with property override assistants...
QString asExpression() const
Returns an expression string representing the state of the property, or an empty string if the proper...
Field based property (QgsFieldBasedProperty)
Definition: qgsproperty.h:238
bool isReadOnly(const QString &name) const
Returns whether a variable is read only, and should not be modifiable by users.
bool dockMode()
Returns the dock mode state.
void setExpectedOutputFormat(const QString &expected)
The set expected format string.
QString name
Definition: qgsfield.h:57
static const double UI_SCALE_FACTOR
UI scaling factor.
Definition: qgis.h:151
This class is a composition of two QSettings instances:
Definition: qgssettings.h:58
Expression based property (QgsExpressionBasedProperty)
Definition: qgsproperty.h:239
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
void setSymbol(std::shared_ptr< QgsSymbol > symbol)
Sets a symbol which can be used for previews inside the widget.
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.
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...
bool isActive() const
Returns true if the button has an active property.
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 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 isAuxiliaryField(int index, int &srcIndex) const
Returns true if the field comes from the auxiliary layer, false otherwise.
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...
QString parserErrorString() const
Returns parser error.
const QgsPropertyTransformer * transformer() const
Returns the existing transformer used for manipulating the calculated values for 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.
virtual QgsExpressionContext createExpressionContext() const =0
This method needs to be reimplemented in all classes which implement this interface and return an exp...
int indexFromName(const QString &fieldName) const
Gets the field index from the field name.
Definition: qgsfields.cpp:184
DataType dataType() const
Returns the allowable field/value data type for the property.
Definition: qgsproperty.h:187
#define FALLTHROUGH
Definition: qgis.h:570
Type propertyType() const
Returns the property type.
QgsFields fields() const override
Returns the list of fields of this layer.
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...
QgsProperty toProperty() const
Returns a QgsProperty object encapsulating the current state of the 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...
Abstract base class for QgsPropertyCollection like objects.
Property requires a numeric value.
Definition: qgsproperty.h:98
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
QStringList variableNames() const
Returns a list of variables names set by all scopes in the context.
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
QString field() const
Returns the current field name the property references.
QMap< int, QgsPropertyDefinition > QgsPropertiesDefinition
Definition of available properties.
QString helpText() const
Helper text for using the property, including a description of the valid values for the property...
Definition: qgsproperty.h:177
Abstract interface for generating an expression context.
void changed()
Emitted when property definition changes.
void registerCheckedWidget(QWidget *widget, bool natural=true)
Register a sibling widget that gets checked when the property is active.
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...
int propertyKey() const
Returns the property key linked to the button.
QgsPropertyOverrideButton(QWidget *parent=nullptr, const QgsVectorLayer *layer=nullptr)
Constructor for QgsPropertyOverrideButton.
A generic message view for displaying QGIS messages.
QVariant staticValue() const
Returns the current static value for the property.
static void openHelp(const QString &key)
Opens help topic for the given help key using default system web browser.
Definition: qgshelp.cpp:35
bool isNumeric
Definition: qgsfield.h:52
QString name() const
Returns the name of the property.
Definition: qgsproperty.h:138
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
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.
QVariant::Type type
Definition: qgsfield.h:55
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...
bool isActive() const
Returns whether the property is currently active.
void updateProperty(QgsProperty &property)
Updates a property in place to corresponding to the current settings shown in the widget...