Skip to content

Commit

Permalink
Add option to control the plain text display method for fields using
Browse files Browse the repository at this point in the history
the checkbox form widget

In QGIS 2.x we always displayed the underlying raw value of a field
using the checkbox widget in the attribute table or in identify results
(i.e. the text display of the field would always match the value
the user had set for the checked or unchecked representation status,
e.g. "yes"/"no", "present"/"absent", etc)

This was changed in 3.0 so that the representation status ONLY affected
how the underlying stored values are mapped to a boolean true or false
value, and accordingly the attribute table/identify results started
only showing "true" or "false" strings.

This new setting allows users to control whether they want plain text
displays of the field to use the 3.x "true"/"false" behavior (the
default), or if they want to see the 2.x style actual field value.
  • Loading branch information
nyalldawson authored and github-actions[bot] committed Feb 16, 2021
1 parent eccd7f0 commit 16fed71
Show file tree
Hide file tree
Showing 6 changed files with 146 additions and 32 deletions.
Expand Up @@ -21,6 +21,12 @@ Field formatter for a checkbox field.
%End
public:

enum TextDisplayMethod
{
ShowTrueFalse,
ShowStoredValues,
};

QgsCheckBoxFieldFormatter();
%Docstring
Constructor for QgsCheckBoxFieldFormatter.
Expand Down
26 changes: 19 additions & 7 deletions src/core/fieldformatter/qgscheckboxfieldformatter.cpp
Expand Up @@ -52,6 +52,7 @@ QString QgsCheckBoxFieldFormatter::representValue( QgsVectorLayer *layer, int fi
if ( fieldType == QVariant::Bool )
{
boolValue = value.toBool();
textValue = boolValue ? QObject::tr( "true" ) : QObject::tr( "false" );
}
else
{
Expand All @@ -62,18 +63,19 @@ QString QgsCheckBoxFieldFormatter::representValue( QgsVectorLayer *layer, int fi
}
else
{
if ( config.contains( QStringLiteral( "CheckedState" ) ) && value.toString() == config[ QStringLiteral( "CheckedState" ) ].toString() )
textValue = value.toString();
if ( config.contains( QStringLiteral( "CheckedState" ) ) && textValue == config[ QStringLiteral( "CheckedState" ) ].toString() )
{
boolValue = true;
}
else if ( config.contains( QStringLiteral( "UncheckedState" ) ) && value.toString() == config[ QStringLiteral( "UncheckedState" ) ].toString() )
else if ( config.contains( QStringLiteral( "UncheckedState" ) ) && textValue == config[ QStringLiteral( "UncheckedState" ) ].toString() )
{
boolValue = false;
}
else
{
isNull = true;
textValue = QStringLiteral( "(%1)" ).arg( value.toString() );
textValue = QStringLiteral( "(%1)" ).arg( textValue );
}
}
}
Expand All @@ -82,8 +84,18 @@ QString QgsCheckBoxFieldFormatter::representValue( QgsVectorLayer *layer, int fi
{
return textValue;
}
if ( boolValue )
return QObject::tr( "true" );
else
return QObject::tr( "false" );

const TextDisplayMethod displayMethod = static_cast< TextDisplayMethod >( config.value( QStringLiteral( "TextDisplayMethod" ), QStringLiteral( "0" ) ).toInt() );
switch ( displayMethod )
{
case QgsCheckBoxFieldFormatter::ShowTrueFalse:
if ( boolValue )
return QObject::tr( "true" );
else
return QObject::tr( "false" );

case QgsCheckBoxFieldFormatter::ShowStoredValues:
return textValue;
}
return QString();
}
11 changes: 11 additions & 0 deletions src/core/fieldformatter/qgscheckboxfieldformatter.h
Expand Up @@ -30,6 +30,17 @@ class CORE_EXPORT QgsCheckBoxFieldFormatter : public QgsFieldFormatter
{
public:

/**
* Method to use when displaying the checkbox values as plain text.
*
* \since QGIS 3.18
*/
enum TextDisplayMethod
{
ShowTrueFalse, //!< Shows "True" or "False" strings
ShowStoredValues, //!< Shows actual stored field value
};

/**
* Constructor for QgsCheckBoxFieldFormatter.
*/
Expand Down
9 changes: 9 additions & 0 deletions src/gui/editorwidgets/qgscheckboxconfigdlg.cpp
Expand Up @@ -14,14 +14,21 @@
***************************************************************************/

#include "qgscheckboxconfigdlg.h"
#include "qgscheckboxwidgetwrapper.h"
#include "qgscheckboxfieldformatter.h"

QgsCheckBoxConfigDlg::QgsCheckBoxConfigDlg( QgsVectorLayer *vl, int fieldIdx, QWidget *parent )
: QgsEditorConfigWidget( vl, fieldIdx, parent )
{
setupUi( this );

mDisplayAsTextComboBox->addItem( tr( "\"True\" or \"False\"" ), QgsCheckBoxFieldFormatter::ShowTrueFalse );
mDisplayAsTextComboBox->addItem( tr( "Stored Values" ), QgsCheckBoxFieldFormatter::ShowStoredValues );
mDisplayAsTextComboBox->setCurrentIndex( 0 );

connect( leCheckedState, &QLineEdit::textEdited, this, &QgsEditorConfigWidget::changed );
connect( leUncheckedState, &QLineEdit::textEdited, this, &QgsEditorConfigWidget::changed );
connect( mDisplayAsTextComboBox, qgis::overload<int>::of( &QComboBox::currentIndexChanged ), this, &QgsEditorConfigWidget::changed );

if ( vl->fields().at( fieldIdx ).type() == QVariant::Bool )
{
Expand All @@ -39,6 +46,7 @@ QVariantMap QgsCheckBoxConfigDlg::config()

cfg.insert( QStringLiteral( "CheckedState" ), leCheckedState->text() );
cfg.insert( QStringLiteral( "UncheckedState" ), leUncheckedState->text() );
cfg.insert( QStringLiteral( "TextDisplayMethod" ), mDisplayAsTextComboBox->currentData().toInt() );

return cfg;
}
Expand All @@ -50,4 +58,5 @@ void QgsCheckBoxConfigDlg::setConfig( const QVariantMap &config )
leCheckedState->setText( config.value( QStringLiteral( "CheckedState" ) ).toString() );
leUncheckedState->setText( config.value( QStringLiteral( "UncheckedState" ) ).toString() );
}
mDisplayAsTextComboBox->setCurrentIndex( mDisplayAsTextComboBox->findData( config.value( QStringLiteral( "TextDisplayMethod" ), QString::number( static_cast< int >( QgsCheckBoxFieldFormatter::ShowTrueFalse ) ) ).toInt() ) );
}
87 changes: 70 additions & 17 deletions src/ui/editorwidgets/qgscheckboxconfigdlgbase.ui
Expand Up @@ -6,34 +6,87 @@
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
<width>395</width>
<height>351</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Representation for checked state</string>
<layout class="QGridLayout" name="gridLayout" columnstretch="0,0">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="1" column="0" colspan="2">
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Display Settings</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0" colspan="2">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Display the checkbox state as a plain text value (e.g. in an attribute table) using</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QComboBox" name="mDisplayAsTextComboBox"/>
</item>
</layout>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="leCheckedState"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Representation for unchecked state</string>
<item row="0" column="0" colspan="2">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Representation</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Unchecked state</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="leUncheckedState"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Checked state</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="leCheckedState"/>
</item>
<item row="0" column="0" colspan="2">
<widget class="QLabel" name="label_4">
<property name="text">
<string>The checked and unchecked representation state are the raw values stored in the layer when the checkbox is checked or unchecked.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="leUncheckedState"/>
</item>
<item row="2" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
Expand Down
39 changes: 31 additions & 8 deletions tests/src/python/test_qgsfieldformatters.py
Expand Up @@ -401,17 +401,40 @@ def test_representValue(self):

# test with integer
# normal case
self.assertEqual(field_formatter.representValue(layer, 0, {'UncheckedState': 0, 'CheckedState': 1}, None, 1), 'true')
self.assertEqual(field_formatter.representValue(layer, 0, {'UncheckedState': 0, 'CheckedState': 1}, None, 0), 'false')
self.assertEqual(field_formatter.representValue(layer, 0, {'UncheckedState': 0, 'CheckedState': 1}, None, 10), "(10)")

config = {'UncheckedState': 0, 'CheckedState': 1}
self.assertEqual(field_formatter.representValue(layer, 0, config, None, 1), 'true')
self.assertEqual(field_formatter.representValue(layer, 0, config, None, 0), 'false')
self.assertEqual(field_formatter.representValue(layer, 0, config, None, 10), "(10)")

# displaying stored values
config['TextDisplayMethod'] = QgsCheckBoxFieldFormatter.ShowStoredValues
self.assertEqual(field_formatter.representValue(layer, 0, config, None, 1), '1')
self.assertEqual(field_formatter.representValue(layer, 0, config, None, 0), '0')
self.assertEqual(field_formatter.representValue(layer, 0, config, None, 10), "(10)")

# invert true/false
self.assertEqual(field_formatter.representValue(layer, 0, {'UncheckedState': 1, 'CheckedState': 0}, None, 0), 'true')
self.assertEqual(field_formatter.representValue(layer, 0, {'UncheckedState': 1, 'CheckedState': 0}, None, 1), 'false')
config = {'UncheckedState': 1, 'CheckedState': 0}
self.assertEqual(field_formatter.representValue(layer, 0, config, None, 0), 'true')
self.assertEqual(field_formatter.representValue(layer, 0, config, None, 1), 'false')

# displaying stored values
config['TextDisplayMethod'] = QgsCheckBoxFieldFormatter.ShowStoredValues
self.assertEqual(field_formatter.representValue(layer, 0, config, None, 1), '1')
self.assertEqual(field_formatter.representValue(layer, 0, config, None, 0), '0')
self.assertEqual(field_formatter.representValue(layer, 0, config, None, 10), "(10)")

# test with string
self.assertEqual(field_formatter.representValue(layer, 1, {'UncheckedState': 'nooh', 'CheckedState': 'yeah'}, None, 'yeah'), 'true')
self.assertEqual(field_formatter.representValue(layer, 1, {'UncheckedState': 'nooh', 'CheckedState': 'yeah'}, None, 'nooh'), 'false')
self.assertEqual(field_formatter.representValue(layer, 1, {'UncheckedState': 'nooh', 'CheckedState': 'yeah'}, None, 'oops'), "(oops)")
config = {'UncheckedState': 'nooh', 'CheckedState': 'yeah'}
self.assertEqual(field_formatter.representValue(layer, 1, config, None, 'yeah'), 'true')
self.assertEqual(field_formatter.representValue(layer, 1, config, None, 'nooh'), 'false')
self.assertEqual(field_formatter.representValue(layer, 1, config, None, 'oops'), "(oops)")

# displaying stored values
config['TextDisplayMethod'] = QgsCheckBoxFieldFormatter.ShowStoredValues
self.assertEqual(field_formatter.representValue(layer, 0, config, None, 'yeah'), 'yeah')
self.assertEqual(field_formatter.representValue(layer, 0, config, None, 'nooh'), 'nooh')
self.assertEqual(field_formatter.representValue(layer, 0, config, None, 'oops'), "(oops)")


class TestQgsFallbackFieldFormatter(unittest.TestCase):
Expand Down

0 comments on commit 16fed71

Please sign in to comment.