QGIS API Documentation  3.0.2-Girona (307d082)
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  setStyleSheet( QStringLiteral( "QToolButton{ background: none; border: 1px solid rgba(0, 0, 0, 0%);} QToolButton:focus { border: 1px solid palette(highlight); }" ) );
47 
48  setIconSize( QSize( iconSize, iconSize ) );
49  setPopupMode( QToolButton::InstantPopup );
50 
51  connect( this, &QgsPropertyOverrideButton::activated, this, &QgsPropertyOverrideButton::updateSiblingWidgets );
52 
53  mDefineMenu = new QMenu( this );
54  connect( mDefineMenu, &QMenu::aboutToShow, this, &QgsPropertyOverrideButton::aboutToShowMenu );
55  connect( mDefineMenu, &QMenu::triggered, this, &QgsPropertyOverrideButton::menuActionTriggered );
56  setMenu( mDefineMenu );
57 
58  mFieldsMenu = new QMenu( this );
59  mActionDataTypes = new QAction( this );
60  // list fields and types in submenu, since there may be many
61  mActionDataTypes->setMenu( mFieldsMenu );
62 
63  mActionVariables = new QAction( tr( "Variable" ), this );
64  mVariablesMenu = new QMenu( this );
65  mActionVariables->setMenu( mVariablesMenu );
66 
67  mActionActive = new QAction( this );
68  QFont f = mActionActive->font();
69  f.setBold( true );
70  mActionActive->setFont( f );
71 
72  mActionDescription = new QAction( tr( "Description…" ), this );
73 
74  mActionCreateAuxiliaryField = new QAction( tr( "Store Data in the Project" ), this );
75  mActionCreateAuxiliaryField->setCheckable( true );
76 
77  mActionExpDialog = new QAction( tr( "Edit…" ), this );
78  mActionExpression = nullptr;
79  mActionPasteExpr = new QAction( tr( "Paste" ), this );
80  mActionCopyExpr = new QAction( tr( "Copy" ), this );
81  mActionClearExpr = new QAction( tr( "Clear" ), this );
82  mActionAssistant = new QAction( tr( "Assistant…" ), this );
83  QFont assistantFont = mActionAssistant->font();
84  assistantFont.setBold( true );
85  mActionAssistant->setFont( assistantFont );
86  mDefineMenu->addAction( mActionAssistant );
87 }
88 
89 
90 void QgsPropertyOverrideButton::init( int propertyKey, const QgsProperty &property, const QgsPropertiesDefinition &definitions, const QgsVectorLayer *layer, bool auxiliaryStorageEnabled )
91 {
92  init( propertyKey, property, definitions.value( propertyKey ), layer, auxiliaryStorageEnabled );
93 }
94 
95 void QgsPropertyOverrideButton::init( int propertyKey, const QgsProperty &property, const QgsPropertyDefinition &definition, const QgsVectorLayer *layer, bool auxiliaryStorageEnabled )
96 {
97  mVectorLayer = layer;
98  mAuxiliaryStorageEnabled = auxiliaryStorageEnabled;
99  setToProperty( property );
100  mPropertyKey = propertyKey;
101 
102  mDefinition = definition;
103  mDataTypes = mDefinition.dataType();
104 
105  mInputDescription = mDefinition.helpText();
106  mFullDescription.clear();
107  mUsageInfo.clear();
108 
109  // set up data types string
110  mDataTypesString.clear();
111 
112  QStringList ts;
113  switch ( mDataTypes )
114  {
116  ts << tr( "boolean" );
117  FALLTHROUGH;
118 
120  ts << tr( "int" );
121  ts << tr( "double" );
122  FALLTHROUGH;
123 
125  ts << tr( "string" );
126  break;
127  }
128 
129  if ( !ts.isEmpty() )
130  {
131  mDataTypesString = ts.join( QStringLiteral( ", " ) );
132  mActionDataTypes->setText( tr( "Field type: " ) + mDataTypesString );
133  }
134 
136  updateGui();
137 }
138 
139 void QgsPropertyOverrideButton::init( int propertyKey, const QgsAbstractPropertyCollection &collection, const QgsPropertiesDefinition &definitions, const QgsVectorLayer *layer, bool auxiliaryStorageEnabled )
140 {
141  init( propertyKey, collection.property( propertyKey ), definitions, layer, auxiliaryStorageEnabled );
142 }
143 
144 
146 {
147  mFieldNameList.clear();
148  mFieldTypeList.clear();
149 
150  if ( mVectorLayer )
151  {
152  // store just a list of fields of unknown type or those that match the expected type
153  Q_FOREACH ( const QgsField &f, mVectorLayer->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  mDefineMenu->addAction( mActionClearExpr );
473  }
474  else
475  {
476  mDefineMenu->addAction( mActionExpDialog );
477  mDefineMenu->addAction( mActionPasteExpr );
478  }
479 
480  if ( !mDefinition.name().isEmpty() && mDefinition.supportsAssistant() )
481  {
482  mDefineMenu->addSeparator();
483  mActionAssistant->setCheckable( mProperty.transformer() );
484  mActionAssistant->setChecked( mProperty.transformer() );
485  mDefineMenu->addAction( mActionAssistant );
486  }
487 }
488 
489 void QgsPropertyOverrideButton::menuActionTriggered( QAction *action )
490 {
491  if ( action == mActionActive )
492  {
493  setActivePrivate( mActionActive->data().toBool() );
494  updateGui();
495  emit changed();
496  }
497  else if ( action == mActionDescription )
498  {
499  showDescriptionDialog();
500  }
501  else if ( action == mActionExpDialog )
502  {
503  showExpressionDialog();
504  }
505  else if ( action == mActionExpression )
506  {
507  mProperty.setExpressionString( mExpressionString );
508  mProperty.setTransformer( nullptr );
509  setActivePrivate( true );
510  updateSiblingWidgets( isActive() );
511  updateGui();
512  emit changed();
513  }
514  else if ( action == mActionCopyExpr )
515  {
516  QApplication::clipboard()->setText( mExpressionString );
517  }
518  else if ( action == mActionPasteExpr )
519  {
520  QString exprString = QApplication::clipboard()->text();
521  if ( !exprString.isEmpty() )
522  {
523  mExpressionString = exprString;
524  mProperty.setExpressionString( mExpressionString );
525  mProperty.setTransformer( nullptr );
526  setActivePrivate( true );
527  updateSiblingWidgets( isActive() );
528  updateGui();
529  emit changed();
530  }
531  }
532  else if ( action == mActionClearExpr )
533  {
534  setActivePrivate( false );
535  mProperty.setStaticValue( QVariant() );
536  mProperty.setTransformer( nullptr );
537  mExpressionString.clear();
538  updateSiblingWidgets( isActive() );
539  updateGui();
540  emit changed();
541  }
542  else if ( action == mActionAssistant )
543  {
544  showAssistant();
545  }
546  else if ( action == mActionCreateAuxiliaryField )
547  {
548  emit createAuxiliaryField();
549  }
550  else if ( mFieldsMenu->actions().contains( action ) ) // a field name clicked
551  {
552  if ( action->isEnabled() )
553  {
554  if ( mFieldName != action->text() )
555  {
556  mFieldName = action->data().toString();
557  }
558  mProperty.setField( mFieldName );
559  mProperty.setTransformer( nullptr );
560  setActivePrivate( true );
561  updateSiblingWidgets( isActive() );
562  updateGui();
563  emit changed();
564  }
565  }
566  else if ( mVariablesMenu->actions().contains( action ) ) // a variable name clicked
567  {
568  if ( mExpressionString != action->text().prepend( "@" ) )
569  {
570  mExpressionString = action->data().toString().prepend( "@" );
571  }
572  mProperty.setExpressionString( mExpressionString );
573  mProperty.setTransformer( nullptr );
574  setActivePrivate( true );
575  updateSiblingWidgets( isActive() );
576  updateGui();
577  emit changed();
578  }
579 }
580 
581 void QgsPropertyOverrideButton::showDescriptionDialog()
582 {
583  QgsMessageViewer *mv = new QgsMessageViewer( this );
584  mv->setWindowTitle( tr( "Data Definition Description" ) );
585  mv->setMessageAsHtml( mFullDescription );
586  mv->exec();
587 }
588 
589 
590 void QgsPropertyOverrideButton::showExpressionDialog()
591 {
592  QgsExpressionContext context = mExpressionContextGenerator ? mExpressionContextGenerator->createExpressionContext() : QgsExpressionContext();
593 
594  // build sensible initial expression text - see https://issues.qgis.org/issues/18638
595  QString currentExpression = ( mProperty.propertyType() == QgsProperty::StaticProperty && !mProperty.staticValue().isValid() ) ? QString()
596  : mProperty.asExpression();
597 
598  QgsExpressionBuilderDialog d( const_cast<QgsVectorLayer *>( mVectorLayer ), currentExpression, this, QStringLiteral( "generic" ), context );
599  if ( d.exec() == QDialog::Accepted )
600  {
601  mExpressionString = d.expressionText().trimmed();
602  mProperty.setExpressionString( mExpressionString );
603  mProperty.setTransformer( nullptr );
604  setActivePrivate( !mExpressionString.isEmpty() );
605  updateGui();
606  emit changed();
607  }
608  activateWindow(); // reset focus to parent window
609 }
610 
611 void QgsPropertyOverrideButton::showAssistant()
612 {
613  //first step - try to convert any existing expression to a transformer if one doesn't
614  //already exist
615  if ( !mProperty.transformer() )
616  {
617  ( void )mProperty.convertToTransformer();
618  }
619 
621  QgsPropertyAssistantWidget *widget = new QgsPropertyAssistantWidget( panel, mDefinition, mProperty, mVectorLayer );
622  widget->registerExpressionContextGenerator( mExpressionContextGenerator );
623  widget->setSymbol( mSymbol ); // we only show legend preview in dialog version
624 
625  if ( panel && panel->dockMode() )
626  {
627  connect( widget, &QgsPropertyAssistantWidget::widgetChanged, this, [this, widget]
628  {
629  widget->updateProperty( this->mProperty );
630  mExpressionString = this->mProperty.asExpression();
631  mFieldName = this->mProperty.field();
632  this->emit changed();
633  } );
634 
635  connect( widget, &QgsPropertyAssistantWidget::panelAccepted, this, [ = ] { updateGui(); } );
636 
637  panel->openPanel( widget );
638  return;
639  }
640  else
641  {
642  // Show the dialog version if not in a panel
643  QDialog *dlg = new QDialog( this );
644  QString key = QStringLiteral( "/UI/paneldialog/%1" ).arg( widget->panelTitle() );
645  QgsSettings settings;
646  dlg->restoreGeometry( settings.value( key ).toByteArray() );
647  dlg->setWindowTitle( widget->panelTitle() );
648  dlg->setLayout( new QVBoxLayout() );
649  dlg->layout()->addWidget( widget );
650  QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Cancel | QDialogButtonBox::Help | QDialogButtonBox::Ok );
651  connect( buttonBox, &QDialogButtonBox::accepted, dlg, &QDialog::accept );
652  connect( buttonBox, &QDialogButtonBox::rejected, dlg, &QDialog::reject );
653  connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsPropertyOverrideButton::showHelp );
654  dlg->layout()->addWidget( buttonBox );
655 
656  if ( dlg->exec() == QDialog::Accepted )
657  {
658  widget->updateProperty( mProperty );
659  mExpressionString = mProperty.asExpression();
660  mFieldName = mProperty.field();
661  widget->acceptPanel();
662  updateGui();
663 
664  emit changed();
665  }
666  settings.setValue( key, dlg->saveGeometry() );
667  }
668 }
669 
670 void QgsPropertyOverrideButton::updateGui()
671 {
672  bool hasExp = !mExpressionString.isEmpty();
673  bool hasField = !mFieldName.isEmpty();
674 
675  QIcon icon = QgsApplication::getThemeIcon( QStringLiteral( "/mIconDataDefine.svg" ) );
676  QString deftip = tr( "undefined" );
677  if ( mProperty.propertyType() == QgsProperty::ExpressionBasedProperty && hasExp )
678  {
679  icon = mProperty.isActive() ? QgsApplication::getThemeIcon( QStringLiteral( "/mIconDataDefineExpressionOn.svg" ) ) : QgsApplication::getThemeIcon( QStringLiteral( "/mIconDataDefineExpression.svg" ) );
680 
681  QgsExpression exp( mExpressionString );
682  if ( exp.hasParserError() )
683  {
684  icon = QgsApplication::getThemeIcon( QStringLiteral( "/mIconDataDefineExpressionError.svg" ) );
685  deftip = tr( "Parse error: %1" ).arg( exp.parserErrorString() );
686  }
687  }
688  else if ( mProperty.propertyType() != QgsProperty::ExpressionBasedProperty && hasField )
689  {
690  icon = mProperty.isActive() ? QgsApplication::getThemeIcon( QStringLiteral( "/mIconDataDefineOn.svg" ) ) : QgsApplication::getThemeIcon( QStringLiteral( "/mIconDataDefine.svg" ) );
691 
692  if ( !mFieldNameList.contains( mFieldName ) && !mProperty.transformer() )
693  {
694  icon = QgsApplication::getThemeIcon( QStringLiteral( "/mIconDataDefineError.svg" ) );
695  deftip = tr( "'%1' field missing" ).arg( mFieldName );
696  }
697  }
698 
699  setIcon( icon );
700 
701  // build full description for tool tip and popup dialog
702  mFullDescription = tr( "<b><u>Data defined override</u></b><br>" );
703 
704  mFullDescription += tr( "<b>Active: </b>%1&nbsp;&nbsp;&nbsp;<i>(ctrl|right-click toggles)</i><br>" ).arg( mProperty.isActive() ? tr( "yes" ) : tr( "no" ) );
705 
706  if ( !mUsageInfo.isEmpty() )
707  {
708  mFullDescription += tr( "<b>Usage:</b><br>%1<br>" ).arg( mUsageInfo );
709  }
710 
711  if ( !mInputDescription.isEmpty() )
712  {
713  mFullDescription += tr( "<b>Expected input:</b><br>%1<br>" ).arg( mInputDescription );
714  }
715 
716  if ( !mDataTypesString.isEmpty() )
717  {
718  mFullDescription += tr( "<b>Valid input types:</b><br>%1<br>" ).arg( mDataTypesString );
719  }
720 
721  QString deftype( QLatin1String( "" ) );
722  if ( deftip != tr( "undefined" ) )
723  {
724  deftype = QStringLiteral( " (%1)" ).arg( mProperty.propertyType() == QgsProperty::ExpressionBasedProperty ? tr( "expression" ) : tr( "field" ) );
725  }
726 
727  // truncate long expressions, or tool tip may be too wide for screen
728  if ( deftip.length() > 75 )
729  {
730  deftip.truncate( 75 );
731  deftip.append( QChar( 0x2026 ) );
732  }
733 
734  mFullDescription += tr( "<b>Current definition %1:</b><br>%2" ).arg( deftype, deftip );
735 
736  setToolTip( mFullDescription );
737 
738 }
739 
740 void QgsPropertyOverrideButton::setActivePrivate( bool active )
741 {
742  if ( mProperty.isActive() != active )
743  {
744  mProperty.setActive( active );
745  emit activated( mProperty.isActive() );
746  }
747 }
748 
749 void QgsPropertyOverrideButton::updateSiblingWidgets( bool state )
750 {
751 
752  Q_FOREACH ( const SiblingWidget &sw, mSiblingWidgets )
753  {
754  switch ( sw.mSiblingType )
755  {
756 
757  case SiblingCheckState:
758  {
759  // don't uncheck, only set to checked
760  if ( state )
761  {
762  QAbstractButton *btn = qobject_cast< QAbstractButton * >( sw.mWidgetPointer.data() );
763  if ( btn && btn->isCheckable() )
764  {
765  btn->setChecked( sw.mNatural ? state : !state );
766  }
767  else
768  {
769  QGroupBox *grpbx = qobject_cast< QGroupBox * >( sw.mWidgetPointer.data() );
770  if ( grpbx && grpbx->isCheckable() )
771  {
772  grpbx->setChecked( sw.mNatural ? state : !state );
773  }
774  }
775  }
776  break;
777  }
778 
779  case SiblingEnableState:
780  {
781  QLineEdit *le = qobject_cast< QLineEdit * >( sw.mWidgetPointer.data() );
782  if ( le )
783  le->setReadOnly( sw.mNatural ? !state : state );
784  else
785  sw.mWidgetPointer.data()->setEnabled( sw.mNatural ? state : !state );
786  break;
787  }
788 
789  case SiblingVisibility:
790  {
791  sw.mWidgetPointer.data()->setVisible( sw.mNatural ? state : !state );
792  break;
793  }
794 
795  case SiblingExpressionText:
796  {
797  QLineEdit *le = qobject_cast<QLineEdit *>( sw.mWidgetPointer.data() );
798  if ( le )
799  {
800  le->setText( mProperty.asExpression() );
801  }
802  else
803  {
804  QTextEdit *te = qobject_cast<QTextEdit *>( sw.mWidgetPointer.data() );
805  if ( te )
806  {
807  te->setText( mProperty.asExpression() );
808  }
809  }
810  break;
811  }
812 
813  default:
814  break;
815  }
816 
817 
818  }
819 }
820 
821 
822 
824 {
825  if ( mProperty.isActive() != active )
826  {
827  mProperty.setActive( active );
828  emit changed();
829  emit activated( mProperty.isActive() );
830  }
831 }
832 
834 {
835  mExpressionContextGenerator = generator;
836 }
837 
838 void QgsPropertyOverrideButton::showHelp()
839 {
840  QgsHelp::openHelp( QStringLiteral( "introduction/general_tools.html#data-defined" ) );
841 }
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 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()
Return the dock mode state.
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:57
Expression based property (QgsExpressionBasedProperty)
Definition: qgsproperty.h:239
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...
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.
void setValue(const QString &key, const QVariant &value, const QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
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
Get 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:548
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
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), const Section section=NoSection) const
Returns the value for setting key.
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...