Skip to content

Commit 9beebf9

Browse files
uclaroswonder-sk
authored andcommitted
Change the combobox logic
Now the combobox is populated with all classes from the Classification renderer, which may have a modified name, and all available classes in the dataset, based on layer statistics. The combobox is editable so that the rest classes can be typed-in.
1 parent af2dc31 commit 9beebf9

File tree

2 files changed

+113
-19
lines changed

2 files changed

+113
-19
lines changed

src/app/3d/qgs3dmapcanvaswidget.cpp

+97-19
Original file line numberDiff line numberDiff line change
@@ -110,14 +110,31 @@ Qgs3DMapCanvasWidget::Qgs3DMapCanvasWidget( const QString &name, bool isDocked )
110110
mSpinChangeAttributeValue->setShowClearButton( false );
111111
mPointCloudEditingToolbar->addWidget( new QLabel( tr( "Value" ) ) );
112112
mSpinChangeAttributeValueAction = mPointCloudEditingToolbar->addWidget( mSpinChangeAttributeValue );
113+
mSpinChangeAttributeValueAction->setVisible( false );
113114
mCboChangeAttributeValue = new QComboBox();
114-
mCboChangeAttributeValue->setMaxVisibleItems( 15 );
115+
mCboChangeAttributeValue->setEditable( true );
116+
mClassValidator = new ClassValidator( this );
115117
mCboChangeAttributeValueAction = mPointCloudEditingToolbar->addWidget( mCboChangeAttributeValue );
116118

117119
QAction *actionEditingToolbar = toolBar->addAction( QIcon( QgsApplication::iconPath( "mIconPointCloudLayer.svg" ) ), tr( "Show Editing Toolbar" ), this, [this] { mEditingToolBar->setVisible( !mEditingToolBar->isVisible() ); } );
118120
actionEditingToolbar->setCheckable( true );
119121
connect( mCboChangeAttribute, qOverload<int>( &QComboBox::currentIndexChanged ), this, [this]( int ) { onPointCloudChangeAttributeSettingsChanged(); } );
120-
connect( mCboChangeAttributeValue, qOverload<int>( &QComboBox::currentIndexChanged ), this, [this]( int ) { mMapToolPointCloudChangeAttribute->setNewValue( mCboChangeAttributeValue->currentData().toDouble() ); } );
122+
connect( mCboChangeAttributeValue, qOverload<const QString &>( &QComboBox::currentTextChanged ), this, [this]( const QString &text ) {
123+
double newValue = 0;
124+
if ( mCboChangeAttributeValue->isEditable() )
125+
{
126+
const QStringList split = text.split( ' ' );
127+
if ( !split.isEmpty() )
128+
{
129+
newValue = split.constFirst().toDouble();
130+
}
131+
}
132+
else
133+
{
134+
newValue = mCboChangeAttributeValue->currentData().toDouble();
135+
}
136+
mMapToolPointCloudChangeAttribute->setNewValue( newValue );
137+
} );
121138
connect( mSpinChangeAttributeValue, qOverload<double>( &QgsDoubleSpinBox::valueChanged ), this, [this]( double ) { mMapToolPointCloudChangeAttribute->setNewValue( mSpinChangeAttributeValue->value() ); } );
122139

123140
QAction *toggleOnScreenNavigation = toolBar->addAction(
@@ -903,10 +920,13 @@ void Qgs3DMapCanvasWidget::onPointCloudChangeAttributeSettingsChanged()
903920
else if ( attributeName == QLatin1String( "Synthetic" ) || attributeName == QLatin1String( "KeyPoint" ) || attributeName == QLatin1String( "Withheld" ) || attributeName == QLatin1String( "Overlap" ) || attributeName == QLatin1String( "ScanDirectionFlag" ) || attributeName == QLatin1String( "EdgeOfFlightLine" ) )
904921
{
905922
useComboBox = true;
923+
const int oldIndex = mCboChangeAttributeValue->currentIndex();
906924
QgsSignalBlocker< QComboBox > blocker( mCboChangeAttributeValue );
907925
mCboChangeAttributeValue->clear();
908926
mCboChangeAttributeValue->addItem( tr( "False" ), 0 );
909927
mCboChangeAttributeValue->addItem( tr( "True" ), 1 );
928+
mCboChangeAttributeValue->setEditable( false );
929+
mCboChangeAttributeValue->setCurrentIndex( std::min( oldIndex, 1 ) );
910930
}
911931
else if ( attributeName == QLatin1String( "ScannerChannel" ) )
912932
{
@@ -917,13 +937,13 @@ void Qgs3DMapCanvasWidget::onPointCloudChangeAttributeSettingsChanged()
917937
else if ( attributeName == QLatin1String( "Classification" ) )
918938
{
919939
useComboBox = true;
920-
const double oldValue = mCboChangeAttributeValue->currentData().toDouble();
940+
const QStringList split = mCboChangeAttributeValue->currentText().split( ' ' );
941+
const int oldValue = split.isEmpty() ? 0 : split.constFirst().toInt();
921942

922943
whileBlocking( mCboChangeAttributeValue )->clear();
923-
// Instead of showing a list of all available las codes, we are going to build a list of "most popular" and display it on top,
924-
// consisting of Classification renderer classes and used classes in the data from the layer's stats
925-
// Then the full list will go on after a separator.
926-
const QMap<int, QString> lasCodes = QgsPointCloudDataProvider::translatedLasClassificationCodes();
944+
// We will fill the combobox with all available classes from the Classification renderer (may have changed names) and the layer statistics
945+
// Users will be able to manually type in any other class number too.
946+
QMap<int, QString> lasCodes = QgsPointCloudDataProvider::translatedLasClassificationCodes();
927947
QMap<int, QString> classes;
928948

929949
QgsPointCloudLayer *layer = qobject_cast<QgsPointCloudLayer *>( QgisApp::instance()->activeLayer() );
@@ -955,21 +975,23 @@ void Qgs3DMapCanvasWidget::onPointCloudChangeAttributeSettingsChanged()
955975
}
956976
for ( auto it = classes.constBegin(); it != classes.constEnd(); ++it )
957977
{
978+
// populate the combobox
958979
whileBlocking( mCboChangeAttributeValue )->addItem( QStringLiteral( "%1 (%2)" ).arg( it.key() ).arg( it.value() ), it.key() );
980+
// and also update the labels in the full list of classes, which will be used in the editable combobox validator.
981+
lasCodes[it.key()] = it.value();
959982
}
960983
}
961-
// after a separator, we add all the standard las classification codes 0-255 but we are keeping the classification renderer's labels
984+
// new values (manually edited) will be added after a separator
962985
mCboChangeAttributeValue->insertSeparator( mCboChangeAttributeValue->count() );
963-
for ( auto it = lasCodes.constBegin(); it != lasCodes.constEnd(); ++it )
964-
{
965-
whileBlocking( mCboChangeAttributeValue )->addItem( QStringLiteral( "%1 (%2)" ).arg( it.key() ).arg( classes.value( it.key(), it.value() ) ), it.key() );
966-
}
986+
mClassValidator->setClasses( lasCodes );
987+
mCboChangeAttributeValue->setEditable( true );
988+
mCboChangeAttributeValue->setValidator( mClassValidator );
989+
mCboChangeAttributeValue->setCompleter( nullptr );
967990

968991
// Try to reselect last selected value
969992
for ( int i = 0; i < mCboChangeAttributeValue->count(); ++i )
970993
{
971-
bool ok = false;
972-
if ( mCboChangeAttributeValue->itemData( i ).toDouble( &ok ) == oldValue && ok )
994+
if ( mCboChangeAttributeValue->itemText( i ).startsWith( QStringLiteral( "%1 " ).arg( oldValue ) ) )
973995
{
974996
mCboChangeAttributeValue->setCurrentIndex( i );
975997
break;
@@ -987,7 +1009,7 @@ void Qgs3DMapCanvasWidget::onPointCloudChangeAttributeSettingsChanged()
9871009
mSpinChangeAttributeValue->setMinimum( -180 );
9881010
mSpinChangeAttributeValue->setMaximum( 180 );
9891011
mSpinChangeAttributeValue->setDecimals( 3 );
990-
mSpinChangeAttributeValue->setSuffix( QStringLiteral( " (%1)" ).arg( tr( "degrees" ) ) );
1012+
mSpinChangeAttributeValue->setSuffix( QStringLiteral( " %1" ).arg( tr( "degrees" ) ) );
9911013
}
9921014
else if ( attributeName == QLatin1String( "GpsTime" ) )
9931015
{
@@ -997,13 +1019,28 @@ void Qgs3DMapCanvasWidget::onPointCloudChangeAttributeSettingsChanged()
9971019
}
9981020

9991021
mMapToolPointCloudChangeAttribute->setAttribute( attributeName );
1000-
mMapToolPointCloudChangeAttribute->setNewValue( useComboBox ? mCboChangeAttributeValue->currentData().toDouble() : mSpinChangeAttributeValue->value() );
1022+
double newValue = 0;
1023+
if ( useComboBox && mCboChangeAttributeValue->isEditable() )
1024+
{
1025+
// read class integer
1026+
const QStringList split = mCboChangeAttributeValue->currentText().split( ' ' );
1027+
if ( !split.isEmpty() )
1028+
newValue = split.constFirst().toDouble();
1029+
}
1030+
else if ( useComboBox )
1031+
{
1032+
// read true/false combo box
1033+
newValue = mCboChangeAttributeValue->currentData().toDouble();
1034+
}
1035+
else
1036+
{
1037+
// read the spinbox value
1038+
newValue = mSpinChangeAttributeValue->value();
1039+
}
1040+
mMapToolPointCloudChangeAttribute->setNewValue( newValue );
10011041

10021042
mCboChangeAttributeValueAction->setVisible( useComboBox );
10031043
mSpinChangeAttributeValueAction->setVisible( !useComboBox );
1004-
1005-
mCboChangeAttributeValue->setEditable( true );
1006-
mCboChangeAttributeValue->lineEdit()->setReadOnly( true );
10071044
}
10081045

10091046
void Qgs3DMapCanvasWidget::setSceneExtentOn2DCanvas()
@@ -1030,3 +1067,44 @@ void Qgs3DMapCanvasWidget::setSceneExtent( const QgsRectangle &extent )
10301067
else
10311068
mMainCanvas->unsetMapTool( mMapToolExtent.get() );
10321069
}
1070+
1071+
ClassValidator::ClassValidator( QWidget *parent )
1072+
: QValidator( parent )
1073+
{
1074+
mRx = QRegularExpression( QStringLiteral( "([0-9]{1,3})" ) );
1075+
}
1076+
1077+
QValidator::State ClassValidator::validate( QString &input, int &pos ) const
1078+
{
1079+
QRegularExpressionMatch match = mRx.match( input );
1080+
const QString number = match.captured();
1081+
bool ok;
1082+
const int n = number.toInt( &ok );
1083+
1084+
if ( !ok && pos == 0 )
1085+
{
1086+
input.clear();
1087+
return QValidator::State::Intermediate;
1088+
}
1089+
1090+
if ( !ok )
1091+
return QValidator::State::Invalid;
1092+
if ( n < 0 || n > 255 )
1093+
return QValidator::State::Invalid;
1094+
if ( mClasses.contains( n ) )
1095+
{
1096+
input = QStringLiteral( "%1 (%2)" ).arg( n ).arg( mClasses[n] );
1097+
pos = std::min( pos, number.size() );
1098+
return QValidator::State::Acceptable;
1099+
}
1100+
return QValidator::State::Intermediate;
1101+
}
1102+
1103+
void ClassValidator::fixup( QString &input ) const
1104+
{
1105+
QRegularExpressionMatch match = mRx.match( input );
1106+
const QString number = match.captured();
1107+
bool ok;
1108+
const int n = number.toInt( &ok );
1109+
input = QStringLiteral( "%1 (%2)" ).arg( n ).arg( mClasses[n] );
1110+
}

src/app/3d/qgs3dmapcanvaswidget.h

+16
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,24 @@ class QgsMessageBar;
4949
class QgsRubberBand;
5050
class QgsDoubleSpinBox;
5151

52+
//! Helper validator for classification classes
53+
class ClassValidator : public QValidator
54+
{
55+
public:
56+
ClassValidator( QWidget *parent );
57+
QValidator::State validate( QString &input, int &pos ) const override;
58+
void fixup( QString &input ) const override;
59+
void setClasses( const QMap<int, QString> &classes ) { mClasses = classes; }
60+
61+
private:
62+
QMap<int, QString> mClasses;
63+
QRegularExpression mRx;
64+
};
65+
5266
class APP_EXPORT Qgs3DMapCanvasWidget : public QWidget
5367
{
5468
Q_OBJECT
69+
5570
public:
5671
Qgs3DMapCanvasWidget( const QString &name, bool isDocked );
5772
~Qgs3DMapCanvasWidget();
@@ -166,6 +181,7 @@ class APP_EXPORT Qgs3DMapCanvasWidget : public QWidget
166181
QToolBar *mEditingToolBar = nullptr;
167182
QComboBox *mCboChangeAttribute = nullptr;
168183
QComboBox *mCboChangeAttributeValue = nullptr;
184+
ClassValidator *mClassValidator = nullptr;
169185
QgsDoubleSpinBox *mSpinChangeAttributeValue = nullptr;
170186
QAction *mCboChangeAttributeValueAction = nullptr;
171187
QAction *mSpinChangeAttributeValueAction = nullptr;

0 commit comments

Comments
 (0)