mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-17 00:04:02 -04:00
Merge pull request #3467 from pvalsecc/arrays
[FEATURE] Add support for array attributes
This commit is contained in:
commit
8746932193
@ -24,13 +24,17 @@ class QgsField
|
||||
* @param prec Field precision. Usually decimal places but may also be
|
||||
* used in conjunction with other fields types (eg. variable character fields)
|
||||
* @param comment Comment for the field
|
||||
* @param subType If the field is a collection, its element's type. When
|
||||
* all the elements don't need to have the same type, leave
|
||||
* this to QVariant::Invalid.
|
||||
*/
|
||||
QgsField( const QString& name = QString(),
|
||||
QVariant::Type type = QVariant::Invalid,
|
||||
const QString& typeName = QString(),
|
||||
int len = 0,
|
||||
int prec = 0,
|
||||
const QString& comment = QString() );
|
||||
const QString& comment = QString(),
|
||||
QVariant::Type subType = QVariant::Invalid );
|
||||
|
||||
/** Copy constructor
|
||||
*/
|
||||
@ -59,6 +63,14 @@ class QgsField
|
||||
//! Gets variant type of the field as it will be retrieved from data source
|
||||
QVariant::Type type() const;
|
||||
|
||||
/**
|
||||
* If the field is a collection, gets its element's type.
|
||||
* When all the elements don't need to have the same type, this returns
|
||||
* QVariant::Invalid.
|
||||
* @note added in QGIS 3.0
|
||||
*/
|
||||
QVariant::Type subType() const;
|
||||
|
||||
/**
|
||||
* Gets the field type. Field types vary depending on the data source. Examples
|
||||
* are char, int, double, blob, geometry, etc. The type is stored exactly as
|
||||
@ -103,6 +115,14 @@ class QgsField
|
||||
*/
|
||||
void setType( QVariant::Type type );
|
||||
|
||||
/**
|
||||
* If the field is a collection, set its element's type.
|
||||
* When all the elements don't need to have the same type, set this to
|
||||
* QVariant::Invalid.
|
||||
* @note added in QGIS 3.0
|
||||
*/
|
||||
void setSubType( QVariant::Type subType );
|
||||
|
||||
/**
|
||||
* Set the field type.
|
||||
* @param typeName Field type
|
||||
|
@ -302,7 +302,7 @@ class QgsVectorDataProvider : QgsDataProvider
|
||||
|
||||
struct NativeType
|
||||
{
|
||||
NativeType( const QString& typeDesc, const QString& typeName, QVariant::Type type, int minLen = 0, int maxLen = 0, int minPrec = 0, int maxPrec = 0 );
|
||||
NativeType( const QString& typeDesc, const QString& typeName, QVariant::Type type, int minLen = 0, int maxLen = 0, int minPrec = 0, int maxPrec = 0, QVariant::Type subType = QVariant::Invalid );
|
||||
|
||||
QString mTypeDesc;
|
||||
QString mTypeName;
|
||||
@ -311,6 +311,7 @@ class QgsVectorDataProvider : QgsDataProvider
|
||||
int mMaxLen;
|
||||
int mMinPrec;
|
||||
int mMaxPrec;
|
||||
QVariant::Type mSubType;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -75,6 +75,13 @@ class QgsEditorWidgetWrapper : QgsWidgetWrapper
|
||||
*/
|
||||
static QgsEditorWidgetWrapper* fromWidget( QWidget* widget );
|
||||
|
||||
/**
|
||||
* Check if the given widget or one of its parent is a QTableView.
|
||||
* @param parent the widget to check
|
||||
* @return true if yes
|
||||
*/
|
||||
static bool isInTable( const QWidget* parent );
|
||||
|
||||
/**
|
||||
* Is used to enable or disable the edit functionality of the managed widget.
|
||||
* By default this will enable or disable the whole widget
|
||||
|
@ -96,6 +96,7 @@
|
||||
%Include qgshtmlannotationitem.sip
|
||||
%Include qgsidentifymenu.sip
|
||||
%Include qgskeyvaluewidget.sip
|
||||
%Include qgslistwidget.sip
|
||||
%Include qgslegendfilterbutton.sip
|
||||
%Include qgslegendinterface.sip
|
||||
%Include qgslimitedrandomcolorrampdialog.sip
|
||||
@ -161,6 +162,7 @@
|
||||
%Include qgssourceselectdialog.sip
|
||||
%Include qgssublayersdialog.sip
|
||||
%Include qgssvgannotationitem.sip
|
||||
%Include qgstablewidgetbase.sip
|
||||
%Include qgstabwidget.sip
|
||||
%Include qgstablewidgetitem.sip
|
||||
%Include qgstextannotationitem.sip
|
||||
|
@ -2,7 +2,7 @@
|
||||
* Widget allowing to edit a QVariantMap, using a table.
|
||||
* @note added in QGIS 3.0
|
||||
*/
|
||||
class QgsKeyValueWidget : public QWidget
|
||||
class QgsKeyValueWidget : public QgsTableWidgetBase
|
||||
{
|
||||
%TypeHeaderCode
|
||||
#include "qgskeyvaluewidget.h"
|
||||
|
32
python/gui/qgslistwidget.sip
Normal file
32
python/gui/qgslistwidget.sip
Normal file
@ -0,0 +1,32 @@
|
||||
/** \ingroup gui
|
||||
* Widget allowing to edit a QVariantList, using a table.
|
||||
* @note added in QGIS 3.0
|
||||
*/
|
||||
class QgsListWidget : public QgsTableWidgetBase
|
||||
{
|
||||
%TypeHeaderCode
|
||||
#include "qgslistwidget.h"
|
||||
%End
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
explicit QgsListWidget( QVariant::Type subType, QWidget* parent = nullptr );
|
||||
|
||||
/**
|
||||
* Set the initial value of the widget.
|
||||
*/
|
||||
void setList( const QVariantList& list );
|
||||
|
||||
/**
|
||||
* Get the edit value.
|
||||
* @return the QVariantList
|
||||
*/
|
||||
QVariantList list() const;
|
||||
|
||||
/**
|
||||
* Check the content is valid
|
||||
* @return true if valid
|
||||
*/
|
||||
bool valid() const;
|
||||
};
|
28
python/gui/qgstablewidgetbase.sip
Normal file
28
python/gui/qgstablewidgetbase.sip
Normal file
@ -0,0 +1,28 @@
|
||||
/** \ingroup gui
|
||||
* Base widget allowing to edit a collection, using a table.
|
||||
* @note added in QGIS 3.0
|
||||
*/
|
||||
class QgsTableWidgetBase: public QWidget
|
||||
{
|
||||
%TypeHeaderCode
|
||||
#include "qgstablewidgetbase.h"
|
||||
%End
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
explicit QgsTableWidgetBase( QWidget* parent );
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Initialise the table with the given model.
|
||||
* Must be called once in the child class' constructor.
|
||||
*/
|
||||
void init( QAbstractTableModel* model );
|
||||
|
||||
signals:
|
||||
/**
|
||||
* Emitted each time a key or a value is changed.
|
||||
*/
|
||||
void valueChanged();
|
||||
};
|
@ -346,6 +346,7 @@ void QgsFieldCalculator::populateOutputFieldTypes()
|
||||
mOutputFieldTypeComboBox->setItemData( i, typelist[i].mMaxLen, Qt::UserRole + 3 );
|
||||
mOutputFieldTypeComboBox->setItemData( i, typelist[i].mMinPrec, Qt::UserRole + 4 );
|
||||
mOutputFieldTypeComboBox->setItemData( i, typelist[i].mMaxPrec, Qt::UserRole + 5 );
|
||||
mOutputFieldTypeComboBox->setItemData( i, static_cast<int>( typelist[i].mSubType ), Qt::UserRole + 6 );
|
||||
}
|
||||
mOutputFieldTypeComboBox->blockSignals( false );
|
||||
mOutputFieldTypeComboBox->setCurrentIndex( 0 );
|
||||
|
@ -67,7 +67,10 @@ class APP_EXPORT QgsFieldCalculator: public QDialog, private Ui::QgsFieldCalcula
|
||||
static_cast< QVariant::Type >( mOutputFieldTypeComboBox->itemData( mOutputFieldTypeComboBox->currentIndex(), Qt::UserRole ).toInt() ),
|
||||
mOutputFieldTypeComboBox->itemData( mOutputFieldTypeComboBox->currentIndex(), Qt::UserRole + 1 ).toString(),
|
||||
mOutputFieldWidthSpinBox->value(),
|
||||
mOutputFieldPrecisionSpinBox->value() );
|
||||
mOutputFieldPrecisionSpinBox->value(),
|
||||
QString(),
|
||||
static_cast< QVariant::Type >( mOutputFieldTypeComboBox->itemData( mOutputFieldTypeComboBox->currentIndex(), Qt::UserRole + 6 ).toInt() )
|
||||
);
|
||||
}
|
||||
|
||||
/** Idx of changed attribute*/
|
||||
|
@ -5074,6 +5074,22 @@ QString QgsExpression::formatPreviewString( const QVariant& value )
|
||||
}
|
||||
return tr( "<i><map: %1></i>" ).arg( mapStr );
|
||||
}
|
||||
else if ( value.type() == QVariant::List || value.type() == QVariant::StringList )
|
||||
{
|
||||
QString listStr;
|
||||
const QVariantList list = value.toList();
|
||||
for ( QVariantList::const_iterator it = list.constBegin(); it != list.constEnd(); ++it )
|
||||
{
|
||||
if ( !listStr.isEmpty() ) listStr.append( ", " );
|
||||
listStr.append( formatPreviewString( *it ) );
|
||||
if ( listStr.length() > MAX_PREVIEW + 3 )
|
||||
{
|
||||
listStr = QString( tr( "%1..." ) ).arg( listStr.left( MAX_PREVIEW ) );
|
||||
break;
|
||||
}
|
||||
}
|
||||
return tr( "<i><list: %1></i>" ).arg( listStr );
|
||||
}
|
||||
else
|
||||
{
|
||||
return value.toString();
|
||||
|
@ -54,6 +54,7 @@ void QgsExpressionFieldBuffer::writeXml( QDomNode& layerNode, QDomDocument& docu
|
||||
fldElem.setAttribute( "comment", fld.field.comment() );
|
||||
fldElem.setAttribute( "length", fld.field.length() );
|
||||
fldElem.setAttribute( "type", fld.field.type() );
|
||||
fldElem.setAttribute( "subType", fld.field.subType() );
|
||||
fldElem.setAttribute( "typeName", fld.field.typeName() );
|
||||
|
||||
expressionFieldsElem.appendChild( fldElem );
|
||||
@ -79,9 +80,10 @@ void QgsExpressionFieldBuffer::readXml( const QDomNode& layerNode )
|
||||
int precision = field.attribute( "precision" ).toInt();
|
||||
int length = field.attribute( "length" ).toInt();
|
||||
QVariant::Type type = static_cast< QVariant::Type >( field.attribute( "type" ).toInt() );
|
||||
QVariant::Type subType = static_cast< QVariant::Type >( field.attribute( "subType", 0 ).toInt() );
|
||||
QString typeName = field.attribute( "typeName" );
|
||||
|
||||
mExpressions.append( ExpressionField( exp, QgsField( name, type, typeName, length, precision, comment ) ) );
|
||||
mExpressions.append( ExpressionField( exp, QgsField( name, type, typeName, length, precision, comment, subType ) ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -44,9 +44,10 @@ QgsField::QgsField( QString nam, QString typ, int len, int prec, bool num,
|
||||
}
|
||||
#endif
|
||||
QgsField::QgsField( const QString& name, QVariant::Type type,
|
||||
const QString& typeName, int len, int prec, const QString& comment )
|
||||
const QString& typeName, int len, int prec, const QString& comment,
|
||||
QVariant::Type subType )
|
||||
{
|
||||
d = new QgsFieldPrivate( name, type, typeName, len, prec, comment );
|
||||
d = new QgsFieldPrivate( name, type, subType, typeName, len, prec, comment );
|
||||
}
|
||||
|
||||
QgsField::QgsField( const QgsField &other )
|
||||
@ -99,6 +100,11 @@ QVariant::Type QgsField::type() const
|
||||
return d->type;
|
||||
}
|
||||
|
||||
QVariant::Type QgsField::subType() const
|
||||
{
|
||||
return d->subType;
|
||||
}
|
||||
|
||||
QString QgsField::typeName() const
|
||||
{
|
||||
return d->typeName;
|
||||
@ -140,6 +146,11 @@ void QgsField::setType( QVariant::Type type )
|
||||
d->type = type;
|
||||
}
|
||||
|
||||
void QgsField::setSubType( QVariant::Type subType )
|
||||
{
|
||||
d->subType = subType;
|
||||
}
|
||||
|
||||
void QgsField::setTypeName( const QString& typeName )
|
||||
{
|
||||
d->typeName = typeName;
|
||||
@ -292,14 +303,15 @@ QDataStream& operator<<( QDataStream& out, const QgsField& field )
|
||||
out << field.comment();
|
||||
out << field.alias();
|
||||
out << field.defaultValueExpression();
|
||||
out << static_cast< quint32 >( field.subType() );
|
||||
return out;
|
||||
}
|
||||
|
||||
QDataStream& operator>>( QDataStream& in, QgsField& field )
|
||||
{
|
||||
quint32 type, length, precision;
|
||||
quint32 type, subType, length, precision;
|
||||
QString name, typeName, comment, alias, defaultValueExpression;
|
||||
in >> name >> type >> typeName >> length >> precision >> comment >> alias >> defaultValueExpression;
|
||||
in >> name >> type >> typeName >> length >> precision >> comment >> alias >> defaultValueExpression >> subType;
|
||||
field.setName( name );
|
||||
field.setType( static_cast< QVariant::Type >( type ) );
|
||||
field.setTypeName( typeName );
|
||||
@ -308,6 +320,7 @@ QDataStream& operator>>( QDataStream& in, QgsField& field )
|
||||
field.setComment( comment );
|
||||
field.setAlias( alias );
|
||||
field.setDefaultValueExpression( defaultValueExpression );
|
||||
field.setSubType( static_cast< QVariant::Type >( subType ) );
|
||||
return in;
|
||||
}
|
||||
|
||||
|
@ -66,13 +66,17 @@ class CORE_EXPORT QgsField
|
||||
* @param prec Field precision. Usually decimal places but may also be
|
||||
* used in conjunction with other fields types (eg. variable character fields)
|
||||
* @param comment Comment for the field
|
||||
* @param subType If the field is a collection, its element's type. When
|
||||
* all the elements don't need to have the same type, leave
|
||||
* this to QVariant::Invalid.
|
||||
*/
|
||||
QgsField( const QString& name = QString(),
|
||||
QVariant::Type type = QVariant::Invalid,
|
||||
const QString& typeName = QString(),
|
||||
int len = 0,
|
||||
int prec = 0,
|
||||
const QString& comment = QString() );
|
||||
const QString& comment = QString(),
|
||||
QVariant::Type subType = QVariant::Invalid );
|
||||
|
||||
/** Copy constructor
|
||||
*/
|
||||
@ -105,6 +109,14 @@ class CORE_EXPORT QgsField
|
||||
//! Gets variant type of the field as it will be retrieved from data source
|
||||
QVariant::Type type() const;
|
||||
|
||||
/**
|
||||
* If the field is a collection, gets its element's type.
|
||||
* When all the elements don't need to have the same type, this returns
|
||||
* QVariant::Invalid.
|
||||
* @note added in QGIS 3.0
|
||||
*/
|
||||
QVariant::Type subType() const;
|
||||
|
||||
/**
|
||||
* Gets the field type. Field types vary depending on the data source. Examples
|
||||
* are char, int, double, blob, geometry, etc. The type is stored exactly as
|
||||
@ -149,6 +161,14 @@ class CORE_EXPORT QgsField
|
||||
*/
|
||||
void setType( QVariant::Type type );
|
||||
|
||||
/**
|
||||
* If the field is a collection, set its element's type.
|
||||
* When all the elements don't need to have the same type, set this to
|
||||
* QVariant::Invalid.
|
||||
* @note added in QGIS 3.0
|
||||
*/
|
||||
void setSubType( QVariant::Type subType );
|
||||
|
||||
/**
|
||||
* Set the field type.
|
||||
* @param typeName Field type
|
||||
|
@ -44,12 +44,14 @@ class QgsFieldPrivate : public QSharedData
|
||||
|
||||
QgsFieldPrivate( const QString& name = QString(),
|
||||
QVariant::Type type = QVariant::Invalid,
|
||||
QVariant::Type subType = QVariant::Invalid,
|
||||
const QString& typeName = QString(),
|
||||
int len = 0,
|
||||
int prec = 0,
|
||||
const QString& comment = QString() )
|
||||
: name( name )
|
||||
, type( type )
|
||||
, subType( subType )
|
||||
, typeName( typeName )
|
||||
, length( len )
|
||||
, precision( prec )
|
||||
@ -61,6 +63,7 @@ class QgsFieldPrivate : public QSharedData
|
||||
: QSharedData( other )
|
||||
, name( other.name )
|
||||
, type( other.type )
|
||||
, subType( other.subType )
|
||||
, typeName( other.typeName )
|
||||
, length( other.length )
|
||||
, precision( other.precision )
|
||||
@ -74,7 +77,7 @@ class QgsFieldPrivate : public QSharedData
|
||||
|
||||
bool operator==( const QgsFieldPrivate& other ) const
|
||||
{
|
||||
return (( name == other.name ) && ( type == other.type )
|
||||
return (( name == other.name ) && ( type == other.type ) && ( subType == other.subType )
|
||||
&& ( length == other.length ) && ( precision == other.precision )
|
||||
&& ( alias == other.alias ) && ( defaultValueExpression == other.defaultValueExpression ) );
|
||||
}
|
||||
@ -85,6 +88,9 @@ class QgsFieldPrivate : public QSharedData
|
||||
//! Variant type
|
||||
QVariant::Type type;
|
||||
|
||||
//! If the variant is a collection, its element's type
|
||||
QVariant::Type subType;
|
||||
|
||||
//! Type name from provider
|
||||
QString typeName;
|
||||
|
||||
|
@ -352,7 +352,7 @@ class CORE_EXPORT QgsVectorDataProvider : public QgsDataProvider
|
||||
|
||||
struct NativeType
|
||||
{
|
||||
NativeType( const QString& typeDesc, const QString& typeName, QVariant::Type type, int minLen = 0, int maxLen = 0, int minPrec = 0, int maxPrec = 0 )
|
||||
NativeType( const QString& typeDesc, const QString& typeName, QVariant::Type type, int minLen = 0, int maxLen = 0, int minPrec = 0, int maxPrec = 0, QVariant::Type subType = QVariant::Invalid )
|
||||
: mTypeDesc( typeDesc )
|
||||
, mTypeName( typeName )
|
||||
, mType( type )
|
||||
@ -360,6 +360,7 @@ class CORE_EXPORT QgsVectorDataProvider : public QgsDataProvider
|
||||
, mMaxLen( maxLen )
|
||||
, mMinPrec( minPrec )
|
||||
, mMaxPrec( maxPrec )
|
||||
, mSubType( subType )
|
||||
{}
|
||||
|
||||
QString mTypeDesc;
|
||||
@ -369,6 +370,7 @@ class CORE_EXPORT QgsVectorDataProvider : public QgsDataProvider
|
||||
int mMaxLen;
|
||||
int mMinPrec;
|
||||
int mMaxPrec;
|
||||
QVariant::Type mSubType;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -118,6 +118,8 @@ SET(QGIS_GUI_SRCS
|
||||
editorwidgets/qgshiddenwidgetfactory.cpp
|
||||
editorwidgets/qgskeyvaluewidgetfactory.cpp
|
||||
editorwidgets/qgskeyvaluewidgetwrapper.cpp
|
||||
editorwidgets/qgslistwidgetfactory.cpp
|
||||
editorwidgets/qgslistwidgetwrapper.cpp
|
||||
editorwidgets/qgsmultiedittoolbutton.cpp
|
||||
editorwidgets/qgsphotoconfigdlg.cpp
|
||||
editorwidgets/qgsphotowidgetwrapper.cpp
|
||||
@ -234,6 +236,7 @@ SET(QGIS_GUI_SRCS
|
||||
qgshtmlannotationitem.cpp
|
||||
qgsidentifymenu.cpp
|
||||
qgskeyvaluewidget.cpp
|
||||
qgslistwidget.cpp
|
||||
qgslegendfilterbutton.cpp
|
||||
qgslegendinterface.cpp
|
||||
qgslimitedrandomcolorrampdialog.cpp
|
||||
@ -299,6 +302,7 @@ SET(QGIS_GUI_SRCS
|
||||
qgssublayersdialog.cpp
|
||||
qgssqlcomposerdialog.cpp
|
||||
qgssvgannotationitem.cpp
|
||||
qgstablewidgetbase.cpp
|
||||
qgstabwidget.cpp
|
||||
qgstablewidgetitem.cpp
|
||||
qgstextannotationitem.cpp
|
||||
@ -393,6 +397,7 @@ SET(QGIS_GUI_MOC_HDRS
|
||||
qgshtmlannotationitem.h
|
||||
qgsidentifymenu.h
|
||||
qgskeyvaluewidget.h
|
||||
qgslistwidget.h
|
||||
qgslegendfilterbutton.h
|
||||
qgslegendinterface.h
|
||||
qgslimitedrandomcolorrampdialog.h
|
||||
@ -452,6 +457,7 @@ SET(QGIS_GUI_MOC_HDRS
|
||||
qgsslider.h
|
||||
qgssqlcomposerdialog.h
|
||||
qgssublayersdialog.h
|
||||
qgstablewidgetbase.h
|
||||
qgstabwidget.h
|
||||
qgstreewidgetitem.h
|
||||
qgsunitselectionwidget.h
|
||||
@ -568,6 +574,7 @@ SET(QGIS_GUI_MOC_HDRS
|
||||
editorwidgets/qgsfilenamewidgetwrapper.h
|
||||
editorwidgets/qgshiddenwidgetwrapper.h
|
||||
editorwidgets/qgskeyvaluewidgetwrapper.h
|
||||
editorwidgets/qgslistwidgetwrapper.h
|
||||
editorwidgets/qgsmultiedittoolbutton.h
|
||||
editorwidgets/qgsphotoconfigdlg.h
|
||||
editorwidgets/qgsphotowidgetwrapper.h
|
||||
@ -684,6 +691,7 @@ SET(QGIS_GUI_HDRS
|
||||
editorwidgets/qgsfilenamewidgetfactory.h
|
||||
editorwidgets/qgshiddenwidgetfactory.h
|
||||
editorwidgets/qgskeyvaluewidgetfactory.h
|
||||
editorwidgets/qgslistwidgetfactory.h
|
||||
editorwidgets/qgsphotowidgetfactory.h
|
||||
editorwidgets/qgsrangewidgetfactory.h
|
||||
editorwidgets/qgsrelationreferencefactory.h
|
||||
@ -739,7 +747,6 @@ SET(QGIS_GUI_UI_HDRS
|
||||
${CMAKE_CURRENT_BINARY_DIR}/../ui/ui_qgsexpressionbuilder.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/../ui/ui_qgsexpressionselectiondialogbase.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/../ui/ui_qgsgenericprojectionselectorbase.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/../ui/ui_qgskeyvaluewidgetbase.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/../ui/ui_qgsmessagelogviewer.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/../ui/ui_qgsmessageviewer.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/../ui/ui_qgsowssourceselectbase.h
|
||||
@ -747,6 +754,7 @@ SET(QGIS_GUI_UI_HDRS
|
||||
${CMAKE_CURRENT_BINARY_DIR}/../ui/ui_qgsquerybuilderbase.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/../ui/ui_qgssqlcomposerdialogbase.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/../ui/ui_qgssublayersdialogbase.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/../ui/ui_qgstablewidgetbase.h
|
||||
)
|
||||
|
||||
IF(ENABLE_MODELTEST)
|
||||
|
@ -33,6 +33,7 @@
|
||||
#include "qgsfilenamewidgetfactory.h"
|
||||
#include "qgshiddenwidgetfactory.h"
|
||||
#include "qgskeyvaluewidgetfactory.h"
|
||||
#include "qgslistwidgetfactory.h"
|
||||
#include "qgsphotowidgetfactory.h"
|
||||
#include "qgsrangewidgetfactory.h"
|
||||
#include "qgsrelationreferencefactory.h"
|
||||
@ -75,6 +76,7 @@ void QgsEditorWidgetRegistry::initEditors( QgsMapCanvas *mapCanvas, QgsMessageBa
|
||||
reg->registerWidget( "DateTime", new QgsDateTimeEditFactory( tr( "Date/Time" ) ) );
|
||||
reg->registerWidget( "ExternalResource", new QgsExternalResourceWidgetFactory( tr( "External Resource" ) ) );
|
||||
reg->registerWidget( "KeyValue", new QgsKeyValueWidgetFactory( tr( "Key/Value" ) ) );
|
||||
reg->registerWidget( "List", new QgsListWidgetFactory( tr( "List" ) ) );
|
||||
}
|
||||
|
||||
QgsEditorWidgetRegistry::QgsEditorWidgetRegistry()
|
||||
|
@ -18,7 +18,7 @@
|
||||
#include "qgsvectordataprovider.h"
|
||||
#include "qgsfield.h"
|
||||
|
||||
#include <QWidget>
|
||||
#include <QTableView>
|
||||
|
||||
QgsEditorWidgetWrapper::QgsEditorWidgetWrapper( QgsVectorLayer* vl, int fieldIdx, QWidget* editor, QWidget* parent )
|
||||
: QgsWidgetWrapper( vl, editor, parent )
|
||||
@ -169,3 +169,10 @@ bool QgsEditorWidgetWrapper::isValidConstraint() const
|
||||
{
|
||||
return mValidConstraint;
|
||||
}
|
||||
|
||||
bool QgsEditorWidgetWrapper::isInTable( const QWidget* parent )
|
||||
{
|
||||
if ( !parent ) return false;
|
||||
if ( qobject_cast<const QTableView*>( parent ) ) return true;
|
||||
return isInTable( parent->parentWidget() );
|
||||
}
|
@ -95,6 +95,13 @@ class GUI_EXPORT QgsEditorWidgetWrapper : public QgsWidgetWrapper
|
||||
*/
|
||||
static QgsEditorWidgetWrapper* fromWidget( QWidget* widget );
|
||||
|
||||
/**
|
||||
* Check if the given widget or one of its parent is a QTableView.
|
||||
* @param parent the widget to check
|
||||
* @return true if yes
|
||||
*/
|
||||
static bool isInTable( const QWidget* parent );
|
||||
|
||||
/**
|
||||
* Is used to enable or disable the edit functionality of the managed widget.
|
||||
* By default this will enable or disable the whole widget
|
||||
|
@ -33,13 +33,6 @@ void QgsKeyValueWidgetWrapper::showIndeterminateState()
|
||||
mWidget->setMap( QVariantMap() );
|
||||
}
|
||||
|
||||
static bool isInTable( const QWidget* parent )
|
||||
{
|
||||
if ( !parent ) return false;
|
||||
if ( qobject_cast<const QTableView*>( parent ) ) return true;
|
||||
return isInTable( parent->parentWidget() );
|
||||
}
|
||||
|
||||
QWidget* QgsKeyValueWidgetWrapper::createWidget( QWidget* parent )
|
||||
{
|
||||
if ( isInTable( parent ) )
|
||||
@ -66,7 +59,7 @@ void QgsKeyValueWidgetWrapper::initWidget( QWidget* editor )
|
||||
mWidget = editor->findChild<QgsKeyValueWidget*>();
|
||||
}
|
||||
|
||||
connect( mWidget, SIGNAL( valueChanged( const QVariant& ) ), this, SIGNAL( valueChanged( const QVariant& ) ) );
|
||||
connect( mWidget, SIGNAL( valueChanged() ), this, SLOT( onValueChanged() ) );
|
||||
}
|
||||
|
||||
bool QgsKeyValueWidgetWrapper::valid() const
|
||||
@ -82,4 +75,9 @@ void QgsKeyValueWidgetWrapper::setValue( const QVariant& value )
|
||||
void QgsKeyValueWidgetWrapper::updateConstraintWidgetStatus( bool /*constraintValid*/ )
|
||||
{
|
||||
// Nothing
|
||||
}
|
||||
}
|
||||
|
||||
void QgsKeyValueWidgetWrapper::onValueChanged()
|
||||
{
|
||||
emit valueChanged( value() );
|
||||
}
|
||||
|
@ -47,6 +47,9 @@ class GUI_EXPORT QgsKeyValueWidgetWrapper : public QgsEditorWidgetWrapper
|
||||
public slots:
|
||||
void setValue( const QVariant& value ) override;
|
||||
|
||||
private slots:
|
||||
void onValueChanged();
|
||||
|
||||
private:
|
||||
void updateConstraintWidgetStatus( bool constraintValid ) override;
|
||||
|
||||
|
97
src/gui/editorwidgets/qgslistwidgetfactory.cpp
Normal file
97
src/gui/editorwidgets/qgslistwidgetfactory.cpp
Normal file
@ -0,0 +1,97 @@
|
||||
/***************************************************************************
|
||||
qgslistwidgetfactory.cpp
|
||||
--------------------------------------
|
||||
Date : 09.2016
|
||||
Copyright : (C) 2016 Patrick Valsecchi
|
||||
Email : patrick.valsecchi@camptocamp.com
|
||||
***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#include "qgslistwidgetfactory.h"
|
||||
#include "qgslistwidgetwrapper.h"
|
||||
#include "qgsdummyconfigdlg.h"
|
||||
#include "qgsfield.h"
|
||||
#include "qgsvectorlayer.h"
|
||||
#include "qgseditorwidgetregistry.h"
|
||||
|
||||
#include <QVariant>
|
||||
#include <QSettings>
|
||||
|
||||
QgsListWidgetFactory::QgsListWidgetFactory( const QString& name ):
|
||||
QgsEditorWidgetFactory( name )
|
||||
{
|
||||
}
|
||||
|
||||
QgsEditorWidgetWrapper* QgsListWidgetFactory::create( QgsVectorLayer *vl, int fieldIdx, QWidget *editor, QWidget *parent ) const
|
||||
{
|
||||
return new QgsListWidgetWrapper( vl, fieldIdx, editor, parent );
|
||||
}
|
||||
|
||||
QgsEditorConfigWidget* QgsListWidgetFactory::configWidget( QgsVectorLayer *vl, int fieldIdx, QWidget *parent ) const
|
||||
{
|
||||
Q_UNUSED( vl );
|
||||
Q_UNUSED( fieldIdx );
|
||||
Q_UNUSED( parent );
|
||||
return new QgsDummyConfigDlg( vl, fieldIdx, parent, QObject::tr( "List field" ) );
|
||||
}
|
||||
|
||||
QgsEditorWidgetConfig QgsListWidgetFactory::readConfig( const QDomElement &configElement, QgsVectorLayer *layer, int fieldIdx )
|
||||
{
|
||||
Q_UNUSED( configElement );
|
||||
Q_UNUSED( layer );
|
||||
Q_UNUSED( fieldIdx );
|
||||
return QgsEditorWidgetConfig();
|
||||
}
|
||||
|
||||
void QgsListWidgetFactory::writeConfig( const QgsEditorWidgetConfig& config, QDomElement& configElement, QDomDocument& doc, const QgsVectorLayer* layer, int fieldIdx )
|
||||
{
|
||||
Q_UNUSED( config );
|
||||
Q_UNUSED( configElement );
|
||||
Q_UNUSED( doc );
|
||||
Q_UNUSED( layer );
|
||||
Q_UNUSED( fieldIdx );
|
||||
}
|
||||
|
||||
QString QgsListWidgetFactory::representValue( QgsVectorLayer* vl, int fieldIdx, const QgsEditorWidgetConfig& config, const QVariant& cache, const QVariant& value ) const
|
||||
{
|
||||
Q_UNUSED( vl );
|
||||
Q_UNUSED( fieldIdx );
|
||||
Q_UNUSED( config );
|
||||
Q_UNUSED( cache );
|
||||
|
||||
if ( value.isNull() )
|
||||
{
|
||||
QSettings settings;
|
||||
return settings.value( "qgis/nullValue", "NULL" ).toString();
|
||||
}
|
||||
|
||||
QString result;
|
||||
const QVariantList list = value.toList();
|
||||
for ( QVariantList::const_iterator i = list.constBegin(); i != list.constEnd(); ++i )
|
||||
{
|
||||
if ( !result.isEmpty() ) result.append( ", " );
|
||||
result.append( i->toString() );
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Qt::AlignmentFlag QgsListWidgetFactory::alignmentFlag( QgsVectorLayer *vl, int fieldIdx, const QgsEditorWidgetConfig &config ) const
|
||||
{
|
||||
Q_UNUSED( vl );
|
||||
Q_UNUSED( fieldIdx );
|
||||
Q_UNUSED( config );
|
||||
|
||||
return Qt::AlignLeft;
|
||||
}
|
||||
|
||||
unsigned int QgsListWidgetFactory::fieldScore( const QgsVectorLayer* vl, int fieldIdx ) const
|
||||
{
|
||||
const QgsField field = vl->fields().field( fieldIdx );
|
||||
return ( field.type() == QVariant::List || field.type() == QVariant::StringList ) && field.subType() != QVariant::Invalid ? 20 : 0;
|
||||
}
|
46
src/gui/editorwidgets/qgslistwidgetfactory.h
Normal file
46
src/gui/editorwidgets/qgslistwidgetfactory.h
Normal file
@ -0,0 +1,46 @@
|
||||
/***************************************************************************
|
||||
qgslistwidgetfactory.h
|
||||
--------------------------------------
|
||||
Date : 09.2016
|
||||
Copyright : (C) 2016 Patrick Valsecchi
|
||||
Email : patrick.valsecchi@camptocamp.com
|
||||
***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef QGSLISTWIDGETFACTORY_H
|
||||
#define QGSLISTWIDGETFACTORY_H
|
||||
|
||||
#include "qgseditorwidgetfactory.h"
|
||||
|
||||
/** @ingroup gui
|
||||
* Factory for widgets for editing a QVariantList or a QStringList
|
||||
* @note added in QGIS 3.0
|
||||
* @note not available in Python bindings
|
||||
*/
|
||||
class GUI_EXPORT QgsListWidgetFactory : public QgsEditorWidgetFactory
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
QgsListWidgetFactory( const QString& name );
|
||||
|
||||
// QgsEditorWidgetFactory interface
|
||||
public:
|
||||
QgsEditorWidgetWrapper* create( QgsVectorLayer *vl, int fieldIdx, QWidget *editor, QWidget *parent ) const override;
|
||||
//QgsSearchWidgetWrapper* createSearchWidget( QgsVectorLayer* vl, int fieldIdx, QWidget* parent ) const override;
|
||||
QgsEditorConfigWidget* configWidget( QgsVectorLayer *vl, int fieldIdx, QWidget *parent ) const override;
|
||||
QgsEditorWidgetConfig readConfig( const QDomElement &configElement, QgsVectorLayer *layer, int fieldIdx ) override;
|
||||
void writeConfig( const QgsEditorWidgetConfig& config, QDomElement& configElement, QDomDocument& doc, const QgsVectorLayer* layer, int fieldIdx ) override;
|
||||
QString representValue( QgsVectorLayer* vl, int fieldIdx, const QgsEditorWidgetConfig& config, const QVariant& cache, const QVariant& value ) const override;
|
||||
Qt::AlignmentFlag alignmentFlag( QgsVectorLayer *vl, int fieldIdx, const QgsEditorWidgetConfig &config ) const override;
|
||||
unsigned int fieldScore( const QgsVectorLayer* vl, int fieldIdx ) const override;
|
||||
};
|
||||
|
||||
#endif // QGSLISTWIDGETFACTORY_H
|
93
src/gui/editorwidgets/qgslistwidgetwrapper.cpp
Normal file
93
src/gui/editorwidgets/qgslistwidgetwrapper.cpp
Normal file
@ -0,0 +1,93 @@
|
||||
/***************************************************************************
|
||||
qgslistwidgetwrapper.cpp
|
||||
--------------------------------------
|
||||
Date : 09.2016
|
||||
Copyright : (C) 2016 Patrick Valsecchi
|
||||
Email : patrick.valsecchi@camptocamp.com
|
||||
***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#include "qgslistwidgetwrapper.h"
|
||||
#include "qgslistwidget.h"
|
||||
#include "qgsattributeform.h"
|
||||
|
||||
QgsListWidgetWrapper::QgsListWidgetWrapper( QgsVectorLayer* vl, int fieldIdx, QWidget* editor, QWidget* parent ):
|
||||
QgsEditorWidgetWrapper( vl, fieldIdx, editor, parent ), mWidget( nullptr )
|
||||
{
|
||||
}
|
||||
|
||||
void QgsListWidgetWrapper::showIndeterminateState()
|
||||
{
|
||||
mWidget->setList( QVariantList() );
|
||||
}
|
||||
|
||||
QWidget* QgsListWidgetWrapper::createWidget( QWidget* parent )
|
||||
{
|
||||
if ( isInTable( parent ) )
|
||||
{
|
||||
// if to be put in a table, draw a border and set a decent size
|
||||
QFrame* ret = new QFrame( parent );
|
||||
ret->setFrameShape( QFrame::StyledPanel );
|
||||
QHBoxLayout* layout = new QHBoxLayout( ret );
|
||||
layout->addWidget( new QgsListWidget( field().subType(), ret ) );
|
||||
ret->setMinimumSize( QSize( 320, 110 ) );
|
||||
return ret;
|
||||
}
|
||||
else
|
||||
{
|
||||
return new QgsListWidget( field().subType(), parent );
|
||||
}
|
||||
}
|
||||
|
||||
void QgsListWidgetWrapper::initWidget( QWidget* editor )
|
||||
{
|
||||
mWidget = qobject_cast<QgsListWidget*>( editor );
|
||||
if ( !mWidget )
|
||||
{
|
||||
mWidget = editor->findChild<QgsListWidget*>();
|
||||
}
|
||||
|
||||
connect( mWidget, SIGNAL( valueChanged() ), this, SLOT( onValueChanged() ) );
|
||||
}
|
||||
|
||||
bool QgsListWidgetWrapper::valid() const
|
||||
{
|
||||
return mWidget ? mWidget->valid() : true;
|
||||
}
|
||||
|
||||
void QgsListWidgetWrapper::setValue( const QVariant& value )
|
||||
{
|
||||
mWidget->setList( value.toList() );
|
||||
}
|
||||
|
||||
QVariant QgsListWidgetWrapper::value() const
|
||||
{
|
||||
QVariant::Type type = field().type();
|
||||
if ( !mWidget ) return QVariant( type );
|
||||
if ( type == QVariant::StringList )
|
||||
{
|
||||
QStringList result;
|
||||
const QVariantList list = mWidget->list();
|
||||
for ( QVariantList::const_iterator it = list.constBegin(); it != list.constEnd(); ++it )
|
||||
result.append( it->toString() );
|
||||
return result;
|
||||
}
|
||||
else
|
||||
return QVariant( mWidget->list() );
|
||||
}
|
||||
|
||||
void QgsListWidgetWrapper::onValueChanged()
|
||||
{
|
||||
emit valueChanged( value() );
|
||||
}
|
||||
|
||||
void QgsListWidgetWrapper::updateConstraintWidgetStatus( bool /*constraintValid*/ )
|
||||
{
|
||||
// Nothing
|
||||
}
|
59
src/gui/editorwidgets/qgslistwidgetwrapper.h
Normal file
59
src/gui/editorwidgets/qgslistwidgetwrapper.h
Normal file
@ -0,0 +1,59 @@
|
||||
/***************************************************************************
|
||||
qgslistwidgetwrapper.h
|
||||
--------------------------------------
|
||||
Date : 09.2016
|
||||
Copyright : (C) 2016 Patrick Valsecchi
|
||||
Email : patrick.valsecchi@camptocamp.com
|
||||
***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef QGSLISTWIDGETWRAPPER_H
|
||||
#define QGSLISTWIDGETWRAPPER_H
|
||||
|
||||
#include "qgseditorwidgetwrapper.h"
|
||||
|
||||
class QgsListWidget;
|
||||
|
||||
/** @ingroup gui
|
||||
* Wraps a list widget.
|
||||
* @note added in QGIS 3.0
|
||||
* @note not available in Python bindings
|
||||
*/
|
||||
class GUI_EXPORT QgsListWidgetWrapper : public QgsEditorWidgetWrapper
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
explicit QgsListWidgetWrapper( QgsVectorLayer* vl, int fieldIdx, QWidget* editor = nullptr, QWidget* parent = nullptr );
|
||||
|
||||
// QgsEditorWidgetWrapper interface
|
||||
public:
|
||||
QVariant value() const override;
|
||||
void showIndeterminateState() override;
|
||||
|
||||
protected:
|
||||
QWidget* createWidget( QWidget* parent ) override;
|
||||
void initWidget( QWidget* editor ) override;
|
||||
bool valid() const override;
|
||||
|
||||
public slots:
|
||||
void setValue( const QVariant& value ) override;
|
||||
|
||||
private slots:
|
||||
void onValueChanged();
|
||||
|
||||
private:
|
||||
void updateConstraintWidgetStatus( bool constraintValid ) override;
|
||||
|
||||
QgsListWidget* mWidget;
|
||||
};
|
||||
|
||||
#endif // QGSLISTWIDGETWRAPPER_H
|
@ -16,13 +16,10 @@
|
||||
#include "qgskeyvaluewidget.h"
|
||||
|
||||
QgsKeyValueWidget::QgsKeyValueWidget( QWidget* parent )
|
||||
: QWidget( parent )
|
||||
: QgsTableWidgetBase( parent )
|
||||
, mModel( this )
|
||||
{
|
||||
setupUi( this );
|
||||
tableView->setModel( &mModel );
|
||||
connect( tableView->selectionModel(), SIGNAL( selectionChanged( const QItemSelection &, const QItemSelection & ) ), this, SLOT( onSelectionChanged() ) );
|
||||
connect( &mModel, SIGNAL( dataChanged( const QModelIndex&, const QModelIndex& ) ), this, SLOT( onValueChanged() ) );
|
||||
init( &mModel );
|
||||
}
|
||||
|
||||
void QgsKeyValueWidget::setMap( const QVariantMap& map )
|
||||
@ -31,37 +28,6 @@ void QgsKeyValueWidget::setMap( const QVariantMap& map )
|
||||
mModel.setMap( map );
|
||||
}
|
||||
|
||||
void QgsKeyValueWidget::on_addButton_clicked()
|
||||
{
|
||||
const QItemSelectionModel *select = tableView->selectionModel();
|
||||
const int pos = select->hasSelection() ? select->selectedRows()[0].row() : 0;
|
||||
mModel.insertRows( pos, 1 );
|
||||
const QModelIndex index = mModel.index( pos, 0 );
|
||||
tableView->scrollTo( index );
|
||||
tableView->edit( index );
|
||||
tableView->selectRow( pos );
|
||||
}
|
||||
|
||||
void QgsKeyValueWidget::on_removeButton_clicked()
|
||||
{
|
||||
const QItemSelectionModel *select = tableView->selectionModel();
|
||||
// The UI is configured to have single row selection.
|
||||
if ( select->hasSelection() )
|
||||
{
|
||||
mModel.removeRows( select->selectedRows()[0].row(), 1 );
|
||||
}
|
||||
}
|
||||
|
||||
void QgsKeyValueWidget::onSelectionChanged()
|
||||
{
|
||||
removeButton->setEnabled( tableView->selectionModel()->hasSelection() );
|
||||
}
|
||||
|
||||
void QgsKeyValueWidget::onValueChanged()
|
||||
{
|
||||
emit valueChanged( QVariant( map() ) );
|
||||
}
|
||||
|
||||
///@cond PRIVATE
|
||||
void QgsKeyValueModel::setMap( const QVariantMap& map )
|
||||
{
|
||||
|
@ -16,7 +16,7 @@
|
||||
#ifndef QGSKEYVALUEWIDGET_H
|
||||
#define QGSKEYVALUEWIDGET_H
|
||||
|
||||
#include "ui_qgskeyvaluewidgetbase.h"
|
||||
#include "qgstablewidgetbase.h"
|
||||
#include <QAbstractTableModel>
|
||||
#include <QMap>
|
||||
|
||||
@ -56,7 +56,7 @@ class GUI_EXPORT QgsKeyValueModel : public QAbstractTableModel
|
||||
* Widget allowing to edit a QVariantMap, using a table.
|
||||
* @note added in QGIS 3.0
|
||||
*/
|
||||
class GUI_EXPORT QgsKeyValueWidget: public QWidget, public Ui::QgsKeyValueWidgetBase
|
||||
class GUI_EXPORT QgsKeyValueWidget: public QgsTableWidgetBase
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY( QVariantMap map READ map WRITE setMap )
|
||||
@ -77,24 +77,6 @@ class GUI_EXPORT QgsKeyValueWidget: public QWidget, public Ui::QgsKeyValueWidget
|
||||
*/
|
||||
QVariantMap map() const { return mModel.map(); }
|
||||
|
||||
signals:
|
||||
/**
|
||||
* Emitted each time a key or a value is changed.
|
||||
* @param value the new value
|
||||
*/
|
||||
void valueChanged( const QVariant& value );
|
||||
|
||||
private slots:
|
||||
void on_addButton_clicked();
|
||||
void on_removeButton_clicked();
|
||||
|
||||
/**
|
||||
* Called when the selection is changed to enable/disable the delete button.
|
||||
*/
|
||||
void onSelectionChanged();
|
||||
|
||||
void onValueChanged();
|
||||
|
||||
private:
|
||||
QgsKeyValueModel mModel;
|
||||
};
|
||||
|
140
src/gui/qgslistwidget.cpp
Normal file
140
src/gui/qgslistwidget.cpp
Normal file
@ -0,0 +1,140 @@
|
||||
/***************************************************************************
|
||||
qgslistwidget.cpp
|
||||
--------------------------------------
|
||||
Date : 08.2016
|
||||
Copyright : (C) 2016 Patrick Valsecchi
|
||||
Email : patrick.valsecchi@camptocamp.com
|
||||
***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#include "qgslistwidget.h"
|
||||
|
||||
QgsListWidget::QgsListWidget( QVariant::Type subType, QWidget* parent )
|
||||
: QgsTableWidgetBase( parent )
|
||||
, mModel( subType, this )
|
||||
, mSubType( subType )
|
||||
{
|
||||
init( &mModel );
|
||||
}
|
||||
|
||||
void QgsListWidget::setList( const QVariantList& list )
|
||||
{
|
||||
removeButton->setEnabled( false );
|
||||
mModel.setList( list );
|
||||
}
|
||||
|
||||
|
||||
///@cond PRIVATE
|
||||
QgsListModel::QgsListModel( QVariant::Type subType, QObject *parent ) :
|
||||
QAbstractTableModel( parent ),
|
||||
mSubType( subType )
|
||||
{
|
||||
}
|
||||
|
||||
void QgsListModel::setList( const QVariantList& list )
|
||||
{
|
||||
emit beginResetModel();
|
||||
mLines = list;
|
||||
emit endResetModel();
|
||||
}
|
||||
|
||||
QVariantList QgsListModel::list() const
|
||||
{
|
||||
QVariantList result;
|
||||
for ( QVariantList::const_iterator it = mLines.constBegin(); it != mLines.constEnd(); ++it )
|
||||
{
|
||||
QVariant cur = *it;
|
||||
if ( cur.convert( mSubType ) )
|
||||
result.append( cur );
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool QgsListModel::valid() const
|
||||
{
|
||||
for ( QVariantList::const_iterator it = mLines.constBegin(); it != mLines.constEnd(); ++it )
|
||||
{
|
||||
QVariant cur = *it;
|
||||
if ( !cur.convert( mSubType ) ) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int QgsListModel::rowCount( const QModelIndex& parent ) const
|
||||
{
|
||||
Q_UNUSED( parent );
|
||||
return mLines.count();
|
||||
}
|
||||
|
||||
int QgsListModel::columnCount( const QModelIndex & parent ) const
|
||||
{
|
||||
Q_UNUSED( parent );
|
||||
return 1;
|
||||
}
|
||||
|
||||
QVariant QgsListModel::headerData( int section, Qt::Orientation orientation, int role ) const
|
||||
{
|
||||
if ( orientation == Qt::Horizontal && role == Qt::DisplayRole && section == 0 )
|
||||
{
|
||||
return QObject::tr( "Value" );
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
QVariant QgsListModel::data( const QModelIndex& index, int role ) const
|
||||
{
|
||||
if ( index.row() < 0 ||
|
||||
index.row() >= mLines.count() ||
|
||||
( role != Qt::DisplayRole && role != Qt::EditRole ) ||
|
||||
index.column() != 0 )
|
||||
{
|
||||
return QVariant( mSubType );
|
||||
}
|
||||
return mLines.at( index.row() );
|
||||
}
|
||||
|
||||
bool QgsListModel::setData( const QModelIndex & index, const QVariant & value, int role )
|
||||
{
|
||||
if ( index.row() < 0 || index.row() >= mLines.count() ||
|
||||
index.column() != 0 || role != Qt::EditRole )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
mLines[index.row()] = value.toString();
|
||||
emit dataChanged( index, index );
|
||||
return true;
|
||||
}
|
||||
|
||||
Qt::ItemFlags QgsListModel::flags( const QModelIndex &index ) const
|
||||
{
|
||||
return QAbstractTableModel::flags( index ) | Qt::ItemIsEditable;
|
||||
}
|
||||
|
||||
bool QgsListModel::insertRows( int position, int rows, const QModelIndex & parent )
|
||||
{
|
||||
Q_UNUSED( parent );
|
||||
beginInsertRows( QModelIndex(), position, position + rows - 1 );
|
||||
for ( int i = 0; i < rows; ++i )
|
||||
{
|
||||
mLines.insert( position, QVariant( mSubType ) );
|
||||
}
|
||||
endInsertRows();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QgsListModel::removeRows( int position, int rows, const QModelIndex &parent )
|
||||
{
|
||||
Q_UNUSED( parent );
|
||||
beginRemoveRows( QModelIndex(), position, position + rows - 1 );
|
||||
for ( int i = 0; i < rows; ++i )
|
||||
mLines.removeAt( position );
|
||||
endRemoveRows();
|
||||
return true;
|
||||
}
|
||||
///@endcond
|
92
src/gui/qgslistwidget.h
Normal file
92
src/gui/qgslistwidget.h
Normal file
@ -0,0 +1,92 @@
|
||||
/***************************************************************************
|
||||
qgslistwidget.h
|
||||
--------------------------------------
|
||||
Date : 08.2016
|
||||
Copyright : (C) 2016 Patrick Valsecchi
|
||||
Email : patrick.valsecchi@camptocamp.com
|
||||
***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef QGSLISTWIDGET_H
|
||||
#define QGSLISTWIDGET_H
|
||||
|
||||
#include "qgstablewidgetbase.h"
|
||||
#include <QAbstractTableModel>
|
||||
#include <QVariant>
|
||||
|
||||
///@cond PRIVATE
|
||||
/** @ingroup gui
|
||||
* Table model to edit a QVariantList.
|
||||
* @note added in QGIS 3.0
|
||||
* @note not available in Python bindings
|
||||
*/
|
||||
class GUI_EXPORT QgsListModel : public QAbstractTableModel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
||||
explicit QgsListModel( QVariant::Type subType, QObject *parent = 0 );
|
||||
void setList( const QVariantList& list );
|
||||
QVariantList list() const;
|
||||
bool valid() const;
|
||||
|
||||
int rowCount( const QModelIndex& parent = QModelIndex() ) const override;
|
||||
int columnCount( const QModelIndex& parent = QModelIndex() ) const override;
|
||||
QVariant headerData( int section, Qt::Orientation orientation, int role ) const override;
|
||||
QVariant data( const QModelIndex& index, int role = Qt::DisplayRole ) const override;
|
||||
bool setData( const QModelIndex & index, const QVariant & value, int role = Qt::EditRole ) override;
|
||||
Qt::ItemFlags flags( const QModelIndex &index ) const override;
|
||||
bool insertRows( int position, int rows, const QModelIndex & parent = QModelIndex() ) override;
|
||||
bool removeRows( int position, int rows, const QModelIndex &parent = QModelIndex() ) override;
|
||||
|
||||
private:
|
||||
QVariantList mLines;
|
||||
QVariant::Type mSubType;
|
||||
};
|
||||
///@endcond
|
||||
|
||||
|
||||
/** \ingroup gui
|
||||
* Widget allowing to edit a QVariantList, using a table.
|
||||
* @note added in QGIS 3.0
|
||||
*/
|
||||
class GUI_EXPORT QgsListWidget: public QgsTableWidgetBase
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY( QVariantList list READ list WRITE setList )
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
explicit QgsListWidget( QVariant::Type subType, QWidget* parent = nullptr );
|
||||
|
||||
/**
|
||||
* Set the initial value of the widget.
|
||||
*/
|
||||
void setList( const QVariantList& list );
|
||||
|
||||
/**
|
||||
* Get the edit value.
|
||||
* @return the QVariantList
|
||||
*/
|
||||
QVariantList list() const { return mModel.list(); }
|
||||
|
||||
/**
|
||||
* Check the content is valid
|
||||
* @return true if valid
|
||||
*/
|
||||
bool valid() const { return mModel.valid(); }
|
||||
|
||||
private:
|
||||
QgsListModel mModel;
|
||||
QVariant::Type mSubType;
|
||||
};
|
||||
|
||||
|
||||
#endif // QGSKEYVALUEWIDGET_H
|
56
src/gui/qgstablewidgetbase.cpp
Normal file
56
src/gui/qgstablewidgetbase.cpp
Normal file
@ -0,0 +1,56 @@
|
||||
/***************************************************************************
|
||||
qgstablewidgetbase.cpp
|
||||
--------------------------------------
|
||||
Date : 08.2016
|
||||
Copyright : (C) 2016 Patrick Valsecchi
|
||||
Email : patrick.valsecchi@camptocamp.com
|
||||
***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#include "qgstablewidgetbase.h"
|
||||
|
||||
QgsTableWidgetBase::QgsTableWidgetBase( QWidget* parent )
|
||||
: QWidget( parent )
|
||||
{
|
||||
setupUi( this );
|
||||
}
|
||||
|
||||
void QgsTableWidgetBase::init( QAbstractTableModel* model )
|
||||
{
|
||||
tableView->setModel( model );
|
||||
connect( tableView->selectionModel(), SIGNAL( selectionChanged( const QItemSelection &, const QItemSelection & ) ), this, SLOT( onSelectionChanged() ) );
|
||||
connect( model, SIGNAL( dataChanged( const QModelIndex&, const QModelIndex& ) ), this, SIGNAL( valueChanged() ) );
|
||||
}
|
||||
|
||||
void QgsTableWidgetBase::on_addButton_clicked()
|
||||
{
|
||||
const QItemSelectionModel *select = tableView->selectionModel();
|
||||
const int pos = select->hasSelection() ? select->selectedRows()[0].row() : 0;
|
||||
QAbstractItemModel* model = tableView->model();
|
||||
model->insertRows( pos, 1 );
|
||||
const QModelIndex index = model->index( pos, 0 );
|
||||
tableView->scrollTo( index );
|
||||
tableView->edit( index );
|
||||
tableView->selectRow( pos );
|
||||
}
|
||||
|
||||
void QgsTableWidgetBase::on_removeButton_clicked()
|
||||
{
|
||||
const QItemSelectionModel *select = tableView->selectionModel();
|
||||
// The UI is configured to have single row selection.
|
||||
if ( select->hasSelection() )
|
||||
{
|
||||
tableView->model()->removeRows( select->selectedRows()[0].row(), 1 );
|
||||
}
|
||||
}
|
||||
|
||||
void QgsTableWidgetBase::onSelectionChanged()
|
||||
{
|
||||
removeButton->setEnabled( tableView->selectionModel()->hasSelection() );
|
||||
}
|
71
src/gui/qgstablewidgetbase.h
Normal file
71
src/gui/qgstablewidgetbase.h
Normal file
@ -0,0 +1,71 @@
|
||||
/***************************************************************************
|
||||
qgstablewidgetbase.h
|
||||
--------------------------------------
|
||||
Date : 09.2016
|
||||
Copyright : (C) 2016 Patrick Valsecchi
|
||||
Email : patrick.valsecchi@camptocamp.com
|
||||
***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef QGSTABLEWIDGETBASE_H
|
||||
#define QGSTABLEWIDGETBASE_H
|
||||
|
||||
#include "ui_qgstablewidgetbase.h"
|
||||
#include <QAbstractTableModel>
|
||||
#include <QVariant>
|
||||
|
||||
/** \ingroup gui
|
||||
* Base widget allowing to edit a collection, using a table.
|
||||
*
|
||||
* This widget includes buttons to add and remove rows.
|
||||
* Child classes must call init(QAbstractTableModel*) from their constructor.
|
||||
*
|
||||
* @note added in QGIS 3.0
|
||||
*/
|
||||
class GUI_EXPORT QgsTableWidgetBase: public QWidget, public Ui::QgsTableWidgetBase
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
explicit QgsTableWidgetBase( QWidget* parent );
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Initialise the table with the given model.
|
||||
* Must be called once in the child class' constructor.
|
||||
*/
|
||||
void init( QAbstractTableModel* model );
|
||||
|
||||
signals:
|
||||
/**
|
||||
* Emitted each time a key or a value is changed.
|
||||
*/
|
||||
void valueChanged();
|
||||
|
||||
private slots:
|
||||
/**
|
||||
* Called when the add button is clicked.
|
||||
*/
|
||||
void on_addButton_clicked();
|
||||
|
||||
/**
|
||||
* Called when the remove button is clicked.
|
||||
*/
|
||||
void on_removeButton_clicked();
|
||||
|
||||
/**
|
||||
* Called when the selection is changed to enable/disable the delete button.
|
||||
*/
|
||||
void onSelectionChanged();
|
||||
};
|
||||
|
||||
|
||||
#endif // QGSTABLEWIDGETBASE_H
|
@ -110,14 +110,15 @@ QgsMemoryProvider::QgsMemoryProvider( const QString& uri )
|
||||
QRegExp reFieldDef( "\\:"
|
||||
"(int|integer|long|int8|real|double|string|date|time|datetime)" // type
|
||||
"(?:\\((\\-?\\d+)" // length
|
||||
"(?:\\,(\\d+))?" // precision
|
||||
"\\))?"
|
||||
"(?:\\,(\\d+))?" // precision
|
||||
"\\))?(\\[\\])?" // array
|
||||
"$", Qt::CaseInsensitive );
|
||||
QStringList fields = url.allQueryItemValues( "field" );
|
||||
for ( int i = 0; i < fields.size(); i++ )
|
||||
{
|
||||
QString name = fields.at( i );
|
||||
QVariant::Type type = QVariant::String;
|
||||
QVariant::Type subType = QVariant::Invalid;
|
||||
QString typeName( "string" );
|
||||
int length = 255;
|
||||
int precision = 0;
|
||||
@ -173,9 +174,14 @@ QgsMemoryProvider::QgsMemoryProvider( const QString& uri )
|
||||
{
|
||||
precision = reFieldDef.cap( 3 ).toInt();
|
||||
}
|
||||
if ( reFieldDef.cap( 4 ) != "" )
|
||||
{ //array
|
||||
subType = type;
|
||||
type = ( subType == QVariant::String ? QVariant::StringList : QVariant::List );
|
||||
}
|
||||
}
|
||||
if ( name != "" )
|
||||
attributes.append( QgsField( name, type, typeName, length, precision ) );
|
||||
attributes.append( QgsField( name, type, typeName, length, precision, "", subType ) );
|
||||
}
|
||||
addAttributes( attributes );
|
||||
}
|
||||
@ -384,6 +390,8 @@ bool QgsMemoryProvider::addAttributes( const QList<QgsField> &attributes )
|
||||
case QVariant::Time:
|
||||
case QVariant::DateTime:
|
||||
case QVariant::LongLong:
|
||||
case QVariant::StringList:
|
||||
case QVariant::List:
|
||||
break;
|
||||
default:
|
||||
QgsDebugMsg( "Field type not supported: " + it->typeName() );
|
||||
|
@ -961,6 +961,20 @@ static QString quotedMap( const QVariantMap& map )
|
||||
return "E'" + ret + "'::hstore";
|
||||
}
|
||||
|
||||
static QString quotedList( const QVariantList& list )
|
||||
{
|
||||
QString ret;
|
||||
for ( QVariantList::const_iterator i = list.constBegin(); i != list.constEnd(); ++i )
|
||||
{
|
||||
if ( !ret.isEmpty() )
|
||||
{
|
||||
ret += ",";
|
||||
}
|
||||
ret.append( doubleQuotedMapValue( i->toString() ) );
|
||||
}
|
||||
return "E'{" + ret + "}'";
|
||||
}
|
||||
|
||||
QString QgsPostgresConn::quotedValue( const QVariant& value )
|
||||
{
|
||||
if ( value.isNull() )
|
||||
@ -979,6 +993,10 @@ QString QgsPostgresConn::quotedValue( const QVariant& value )
|
||||
case QVariant::Map:
|
||||
return quotedMap( value.toMap() );
|
||||
|
||||
case QVariant::StringList:
|
||||
case QVariant::List:
|
||||
return quotedList( value.toList() );
|
||||
|
||||
case QVariant::String:
|
||||
default:
|
||||
return quotedString( value.toString() );
|
||||
|
@ -739,7 +739,7 @@ bool QgsPostgresFeatureIterator::getFeature( QgsPostgresResult &queryResult, int
|
||||
{
|
||||
QgsField fld = mSource->mFields.at( idx );
|
||||
|
||||
QVariant v = QgsPostgresProvider::convertValue( fld.type(), queryResult.PQgetvalue( row, col ) );
|
||||
QVariant v = QgsPostgresProvider::convertValue( fld.type(), fld.subType(), queryResult.PQgetvalue( row, col ) );
|
||||
primaryKeyVals << v;
|
||||
|
||||
if ( !subsetOfAttributes || fetchAttributes.contains( idx ) )
|
||||
@ -781,7 +781,8 @@ void QgsPostgresFeatureIterator::getFeatureAttribute( int idx, QgsPostgresResult
|
||||
if ( mSource->mPrimaryKeyAttrs.contains( idx ) )
|
||||
return;
|
||||
|
||||
QVariant v = QgsPostgresProvider::convertValue( mSource->mFields.at( idx ).type(), queryResult.PQgetvalue( row, col ) );
|
||||
const QgsField fld = mSource->mFields.at( idx );
|
||||
QVariant v = QgsPostgresProvider::convertValue( fld.type(), fld.subType(), queryResult.PQgetvalue( row, col ) );
|
||||
feature.setAttribute( idx, v );
|
||||
|
||||
col++;
|
||||
|
@ -220,7 +220,11 @@ QgsPostgresProvider::QgsPostgresProvider( QString const & uri )
|
||||
<< QgsVectorDataProvider::NativeType( tr( "Date & Time" ), "timestamp without time zone", QVariant::DateTime, -1, -1, -1, -1 )
|
||||
|
||||
// complex types
|
||||
<< QgsVectorDataProvider::NativeType( tr( "Map" ), "hstore", QVariant::Map, -1, -1, -1, -1 )
|
||||
<< QgsVectorDataProvider::NativeType( tr( "Map" ), "hstore", QVariant::Map, -1, -1, -1, -1, QVariant::String )
|
||||
<< QgsVectorDataProvider::NativeType( tr( "Array of number (integer - 32bit)" ), "int4[]", QVariant::List, -1, -1, -1, -1, QVariant::Int )
|
||||
<< QgsVectorDataProvider::NativeType( tr( "Array of number (integer - 64bit)" ), "int8[]", QVariant::List, -1, -1, -1, -1, QVariant::LongLong )
|
||||
<< QgsVectorDataProvider::NativeType( tr( "Array of number (double)" ), "double precision[]", QVariant::List, -1, -1, -1, -1, QVariant::Double )
|
||||
<< QgsVectorDataProvider::NativeType( tr( "Array of text" ), "text[]", QVariant::StringList, -1, -1, -1, -1, QVariant::String )
|
||||
;
|
||||
|
||||
QString key;
|
||||
@ -356,9 +360,10 @@ static bool operator<( const QVariant &a, const QVariant &b )
|
||||
return a.toLongLong() < b.toLongLong();
|
||||
|
||||
case QVariant::List:
|
||||
case QVariant::StringList:
|
||||
{
|
||||
const QList<QVariant> &al = a.toList();
|
||||
const QList<QVariant> &bl = b.toList();
|
||||
const QList<QVariant> al = a.toList();
|
||||
const QList<QVariant> bl = b.toList();
|
||||
|
||||
int i, n = qMin( al.size(), bl.size() );
|
||||
for ( i = 0; i < n && al[i].type() == bl[i].type() && al[i].isNull() == bl[i].isNull() && al[i] == bl[i]; i++ )
|
||||
@ -370,21 +375,6 @@ static bool operator<( const QVariant &a, const QVariant &b )
|
||||
return al[i] < bl[i];
|
||||
}
|
||||
|
||||
case QVariant::StringList:
|
||||
{
|
||||
const QStringList &al = a.toStringList();
|
||||
const QStringList &bl = b.toStringList();
|
||||
|
||||
int i, n = qMin( al.size(), bl.size() );
|
||||
for ( i = 0; i < n && al[i] == bl[i]; i++ )
|
||||
;
|
||||
|
||||
if ( i == n )
|
||||
return al.size() < bl.size();
|
||||
else
|
||||
return al[i] < bl[i];
|
||||
}
|
||||
|
||||
case QVariant::Map:
|
||||
return a.toMap() < b.toMap();
|
||||
|
||||
@ -884,6 +874,7 @@ bool QgsPostgresProvider::loadFields()
|
||||
QString fieldComment = descrMap[tableoid][attnum];
|
||||
|
||||
QVariant::Type fieldType;
|
||||
QVariant::Type fieldSubType = QVariant::Invalid;
|
||||
|
||||
if ( fieldTType == "b" )
|
||||
{
|
||||
@ -1025,6 +1016,7 @@ bool QgsPostgresProvider::loadFields()
|
||||
else if ( fieldTypeName == "hstore" )
|
||||
{
|
||||
fieldType = QVariant::Map;
|
||||
fieldSubType = QVariant::String;
|
||||
fieldSize = -1;
|
||||
}
|
||||
else
|
||||
@ -1036,7 +1028,8 @@ bool QgsPostgresProvider::loadFields()
|
||||
if ( isArray )
|
||||
{
|
||||
fieldTypeName = '_' + fieldTypeName;
|
||||
fieldType = QVariant::String;
|
||||
fieldSubType = fieldType;
|
||||
fieldType = ( fieldType == QVariant::String ? QVariant::StringList : QVariant::List );
|
||||
fieldSize = -1;
|
||||
}
|
||||
}
|
||||
@ -1068,7 +1061,7 @@ bool QgsPostgresProvider::loadFields()
|
||||
|
||||
mAttrPalIndexName.insert( i, fieldName );
|
||||
mDefaultValues.insert( mAttributeFields.size(), defValMap[tableoid][attnum] );
|
||||
mAttributeFields.append( QgsField( fieldName, fieldType, fieldTypeName, fieldSize, fieldPrec, fieldComment ) );
|
||||
mAttributeFields.append( QgsField( fieldName, fieldType, fieldTypeName, fieldSize, fieldPrec, fieldComment, fieldSubType ) );
|
||||
}
|
||||
|
||||
setEditorWidgets();
|
||||
@ -1570,7 +1563,7 @@ QVariant QgsPostgresProvider::minimumValue( int index ) const
|
||||
sql = QString( "SELECT %1 FROM (%2) foo" ).arg( connectionRO()->fieldExpression( fld ), sql );
|
||||
|
||||
QgsPostgresResult rmin( connectionRO()->PQexec( sql ) );
|
||||
return convertValue( fld.type(), rmin.PQgetvalue( 0, 0 ) );
|
||||
return convertValue( fld.type(), fld.subType(), rmin.PQgetvalue( 0, 0 ) );
|
||||
}
|
||||
catch ( PGFieldNotFound )
|
||||
{
|
||||
@ -1609,7 +1602,7 @@ void QgsPostgresProvider::uniqueValues( int index, QList<QVariant> &uniqueValues
|
||||
if ( res.PQresultStatus() == PGRES_TUPLES_OK )
|
||||
{
|
||||
for ( int i = 0; i < res.PQntuples(); i++ )
|
||||
uniqueValues.append( convertValue( fld.type(), res.PQgetvalue( i, 0 ) ) );
|
||||
uniqueValues.append( convertValue( fld.type(), fld.subType(), res.PQgetvalue( i, 0 ) ) );
|
||||
}
|
||||
}
|
||||
catch ( PGFieldNotFound )
|
||||
@ -1746,7 +1739,7 @@ QVariant QgsPostgresProvider::maximumValue( int index ) const
|
||||
|
||||
QgsPostgresResult rmax( connectionRO()->PQexec( sql ) );
|
||||
|
||||
return convertValue( fld.type(), rmax.PQgetvalue( 0, 0 ) );
|
||||
return convertValue( fld.type(), fld.subType(), rmax.PQgetvalue( 0, 0 ) );
|
||||
}
|
||||
catch ( PGFieldNotFound )
|
||||
{
|
||||
@ -1770,7 +1763,7 @@ QVariant QgsPostgresProvider::defaultValue( int fieldId ) const
|
||||
|
||||
QgsPostgresResult res( connectionRO()->PQexec( QString( "SELECT %1" ).arg( defVal.toString() ) ) );
|
||||
|
||||
return convertValue( fld.type(), res.PQgetvalue( 0, 0 ) );
|
||||
return convertValue( fld.type(), fld.subType(), res.PQgetvalue( 0, 0 ) );
|
||||
}
|
||||
|
||||
return defVal;
|
||||
@ -2070,7 +2063,7 @@ bool QgsPostgresProvider::addFeatures( QgsFeatureList &flist )
|
||||
{
|
||||
QgsField fld = field( attrIdx );
|
||||
v = paramValue( defaultValues[ i ], defaultValues[ i ] );
|
||||
features->setAttribute( attrIdx, convertValue( fld.type(), v ) );
|
||||
features->setAttribute( attrIdx, convertValue( fld.type(), fld.subType(), v ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -2079,7 +2072,7 @@ bool QgsPostgresProvider::addFeatures( QgsFeatureList &flist )
|
||||
if ( v != value.toString() )
|
||||
{
|
||||
QgsField fld = field( attrIdx );
|
||||
features->setAttribute( attrIdx, convertValue( fld.type(), v ) );
|
||||
features->setAttribute( attrIdx, convertValue( fld.type(), fld.subType(), v ) );
|
||||
}
|
||||
}
|
||||
|
||||
@ -2092,8 +2085,9 @@ bool QgsPostgresProvider::addFeatures( QgsFeatureList &flist )
|
||||
{
|
||||
for ( int i = 0; i < mPrimaryKeyAttrs.size(); ++i )
|
||||
{
|
||||
int idx = mPrimaryKeyAttrs.at( i );
|
||||
features->setAttribute( idx, convertValue( mAttributeFields.at( idx ).type(), result.PQgetvalue( 0, i ) ) );
|
||||
const int idx = mPrimaryKeyAttrs.at( i );
|
||||
const QgsField fld = mAttributeFields.at( idx );
|
||||
features->setAttribute( idx, convertValue( fld.type(), fld.subType(), result.PQgetvalue( 0, i ) ) );
|
||||
}
|
||||
}
|
||||
else if ( result.PQresultStatus() != PGRES_COMMAND_OK )
|
||||
@ -3485,6 +3479,20 @@ bool QgsPostgresProvider::convertField( QgsField &field, const QMap<QString, QVa
|
||||
fieldPrec = -1;
|
||||
break;
|
||||
|
||||
case QVariant::StringList:
|
||||
fieldType = "_text";
|
||||
fieldPrec = -1;
|
||||
break;
|
||||
|
||||
case QVariant::List:
|
||||
{
|
||||
QgsField sub( "", field.subType(), "", fieldSize, fieldPrec );
|
||||
if ( !convertField( sub, nullptr ) ) return false;
|
||||
fieldType = "_" + sub.typeName();
|
||||
fieldPrec = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
case QVariant::Double:
|
||||
if ( fieldPrec > 0 )
|
||||
{
|
||||
@ -3847,42 +3855,130 @@ QString QgsPostgresProvider::description() const
|
||||
return tr( "PostgreSQL/PostGIS provider\n%1\nPostGIS %2" ).arg( pgVersion, postgisVersion );
|
||||
} // QgsPostgresProvider::description()
|
||||
|
||||
|
||||
static QVariant parseHstore( const QString& value )
|
||||
static void jumpSpace( const QString& txt, int& i )
|
||||
{
|
||||
QRegExp recordSep( "\\s*,\\s*" );
|
||||
QRegExp valueExtractor( "^(?:\"((?:\\.|.)*)\"|((?:\\.|.)*))\\s*=>\\s*(?:\"((?:\\.|.)*)\"|((?:\\.|.)*))$" );
|
||||
QVariantMap result;
|
||||
Q_FOREACH ( QString record, value.split( recordSep ) )
|
||||
while ( i < txt.length() && txt.at( i ).isSpace() ) ++i;
|
||||
}
|
||||
|
||||
static QString getNextString( const QString& txt, int& i, const QString& sep )
|
||||
{
|
||||
jumpSpace( txt, i );
|
||||
QString cur = txt.mid( i );
|
||||
if ( cur.startsWith( '"' ) )
|
||||
{
|
||||
if ( valueExtractor.exactMatch( record ) )
|
||||
QRegExp stringRe( "^\"((?:\\\\.|[^\"\\\\])*)\".*" );
|
||||
if ( !stringRe.exactMatch( cur ) )
|
||||
{
|
||||
QString key = valueExtractor.cap( 1 ) + valueExtractor.cap( 2 );
|
||||
key.replace( "\\\"", "\"" ).replace( "\\\\", "\\" );
|
||||
QString value = valueExtractor.cap( 3 ) + valueExtractor.cap( 4 );
|
||||
value.replace( "\\\"", "\"" ).replace( "\\\\", "\\" );
|
||||
result.insert( key, value );
|
||||
QgsLogger::warning( "Cannot find end of double quoted string: " + txt );
|
||||
return QString::null;
|
||||
}
|
||||
else
|
||||
i += stringRe.cap( 1 ).length() + 2;
|
||||
jumpSpace( txt, i );
|
||||
if ( !txt.mid( i ).startsWith( sep ) && i < txt.length() )
|
||||
{
|
||||
QgsLogger::warning( "Error parsing hstore record: " + record );
|
||||
QgsLogger::warning( "Cannot find separator: " + txt.mid( i ) );
|
||||
return QString::null;
|
||||
}
|
||||
i += sep.length();
|
||||
return stringRe.cap( 1 ).replace( "\\\"", "\"" ).replace( "\\\\", "\\" );
|
||||
}
|
||||
else
|
||||
{
|
||||
QString ret;
|
||||
int sepPos = cur.indexOf( sep );
|
||||
if ( sepPos < 0 )
|
||||
{
|
||||
i += cur.length();
|
||||
return cur.trimmed();
|
||||
}
|
||||
i += sepPos + sep.length();
|
||||
return cur.left( sepPos ).trimmed();
|
||||
}
|
||||
}
|
||||
|
||||
static QVariant parseHstore( const QString& txt )
|
||||
{
|
||||
QVariantMap result;
|
||||
int i = 0;
|
||||
while ( i < txt.length() )
|
||||
{
|
||||
QString key = getNextString( txt, i, "=>" );
|
||||
QString value = getNextString( txt, i, "," );
|
||||
if ( key.isNull() || value.isNull() )
|
||||
{
|
||||
QgsLogger::warning( "Error parsing hstore: " + txt );
|
||||
break;
|
||||
}
|
||||
result.insert( key, value );
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static QVariant parseOtherArray( const QString& txt, QVariant::Type subType )
|
||||
{
|
||||
int i = 0;
|
||||
QVariantList result;
|
||||
while ( i < txt.length() )
|
||||
{
|
||||
const QString value = getNextString( txt, i, "," );
|
||||
if ( value.isNull() )
|
||||
{
|
||||
QgsLogger::warning( "Error parsing array: " + txt );
|
||||
break;
|
||||
}
|
||||
result.append( QgsPostgresProvider::convertValue( subType, QVariant::Invalid, value ) );
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QVariant QgsPostgresProvider::convertValue( QVariant::Type type, const QString& value )
|
||||
static QVariant parseStringArray( const QString& txt )
|
||||
{
|
||||
if ( type == QVariant::Map )
|
||||
int i = 0;
|
||||
QStringList result;
|
||||
while ( i < txt.length() )
|
||||
{
|
||||
return parseHstore( value );
|
||||
const QString value = getNextString( txt, i, "," );
|
||||
if ( value.isNull() )
|
||||
{
|
||||
QgsLogger::warning( "Error parsing array: " + txt );
|
||||
break;
|
||||
}
|
||||
result.append( value );
|
||||
}
|
||||
QVariant v( value );
|
||||
return result;
|
||||
}
|
||||
|
||||
if ( !v.convert( type ) || value.isNull() )
|
||||
v = QVariant( type );
|
||||
static QVariant parseArray( const QString& txt, QVariant::Type type, QVariant::Type subType )
|
||||
{
|
||||
if ( !txt.startsWith( '{' ) || !txt.endsWith( '}' ) )
|
||||
{
|
||||
QgsLogger::warning( "Error parsing array, missing curly braces: " + txt );
|
||||
return QVariant( type );
|
||||
}
|
||||
QString inner = txt.mid( 1, txt.length() - 2 );
|
||||
if ( type == QVariant::StringList )
|
||||
return parseStringArray( inner );
|
||||
else
|
||||
return parseOtherArray( inner, subType );
|
||||
}
|
||||
|
||||
return v;
|
||||
QVariant QgsPostgresProvider::convertValue( QVariant::Type type, QVariant::Type subType, const QString& value )
|
||||
{
|
||||
switch ( type )
|
||||
{
|
||||
case QVariant::Map:
|
||||
return parseHstore( value );
|
||||
case QVariant::StringList:
|
||||
case QVariant::List:
|
||||
return parseArray( value, type, subType );
|
||||
default:
|
||||
{
|
||||
QVariant v( value );
|
||||
if ( !v.convert( type ) || value.isNull() ) return QVariant( type );
|
||||
return v;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -252,10 +252,11 @@ class QgsPostgresProvider : public QgsVectorDataProvider
|
||||
/**
|
||||
* Convert the postgres string representation into the given QVariant type.
|
||||
* @param type the wanted type
|
||||
* @param subType if type is a collection, the wanted element type
|
||||
* @param value the value to convert
|
||||
* @return a QVariant of the given type or a null QVariant
|
||||
*/
|
||||
static QVariant convertValue( QVariant::Type type, const QString& value );
|
||||
static QVariant convertValue( QVariant::Type type, QVariant::Type subType, const QString& value );
|
||||
|
||||
signals:
|
||||
/**
|
||||
|
@ -542,7 +542,8 @@ bool QgsWFSProvider::processSQL( const QString& sqlString, QString& errorMsg, QS
|
||||
tablePrefix = QgsWFSUtils::removeNamespacePrefix( tablePrefix );
|
||||
fieldName = tablePrefix + "." + fieldName;
|
||||
}
|
||||
QgsField field( fieldName, srcField.type(), srcField.typeName() );
|
||||
QgsField field( srcField );
|
||||
field.setName( fieldName );
|
||||
if ( mapFieldNameToSrcLayerNameFieldName.contains( fieldName ) )
|
||||
{
|
||||
errorMsg = tr( "Field '%1': a field with the same name already exists" ).arg( field.name() );
|
||||
@ -572,7 +573,8 @@ bool QgsWFSProvider::processSQL( const QString& sqlString, QString& errorMsg, QS
|
||||
tablePrefix = QgsWFSUtils::removeNamespacePrefix( tablePrefix );
|
||||
fieldName = tablePrefix + "." + fieldName;
|
||||
}
|
||||
QgsField field( fieldName, srcField.type(), srcField.typeName() );
|
||||
QgsField field( srcField );
|
||||
field.setName( fieldName );
|
||||
mapFieldNameToSrcLayerNameFieldName[ field.name()] =
|
||||
QPair<QString, QString>( typeName, srcField.name() );
|
||||
mShared->mFields.append( field );
|
||||
@ -618,9 +620,11 @@ bool QgsWFSProvider::processSQL( const QString& sqlString, QString& errorMsg, QS
|
||||
return false;
|
||||
}
|
||||
|
||||
QgsField field( fieldName, tableFields.at( idx ).type(), tableFields.at( idx ).typeName() );
|
||||
QgsField orig = tableFields.at( idx );
|
||||
QgsField field( orig );
|
||||
field.setName( fieldName );
|
||||
mapFieldNameToSrcLayerNameFieldName[ field.name()] =
|
||||
QPair<QString, QString>( columnTableTypename, tableFields.at( idx ).name() );
|
||||
QPair<QString, QString>( columnTableTypename, orig.name() );
|
||||
mShared->mFields.append( field );
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>QgsKeyValueWidgetBase</class>
|
||||
<class>QgsTableWidgetBase</class>
|
||||
<widget class="QWidget" name="QgsKeyValueWidgetBase">
|
||||
<property name="geometry">
|
||||
<rect>
|
@ -2343,6 +2343,15 @@ class TestQgsExpression: public QObject
|
||||
QCOMPARE( QgsExpression::formatPreviewString( QVariant( map ) ), QString( "<i><map: 1: 'One', 2: 'Two'></i>" ) );
|
||||
map["3"] = "A very long string that is going to be truncated";
|
||||
QCOMPARE( QgsExpression::formatPreviewString( QVariant( map ) ), QString( "<i><map: 1: 'One', 2: 'Two', 3: 'A very long string that is going to ...></i>" ) );
|
||||
|
||||
QVariantList list;
|
||||
list << 1 << 2 << 3;
|
||||
QCOMPARE( QgsExpression::formatPreviewString( QVariant( list ) ), QString( "<i><list: 1, 2, 3></i>" ) );
|
||||
|
||||
QStringList stringList;
|
||||
stringList << "One" << "Two" << "A very long string that is going to be truncated";
|
||||
QCOMPARE( QgsExpression::formatPreviewString( QVariant( stringList ) ),
|
||||
QString( "<i><list: 'One', 'Two', 'A very long string that is going to be trunca...></i>" ) );
|
||||
}
|
||||
|
||||
};
|
||||
|
@ -42,6 +42,7 @@ class TestQgsField: public QObject
|
||||
void dataStream();
|
||||
void displayName();
|
||||
void editorWidgetSetup();
|
||||
void collection();
|
||||
|
||||
private:
|
||||
};
|
||||
@ -411,5 +412,16 @@ void TestQgsField::editorWidgetSetup()
|
||||
QCOMPARE( field.editorWidgetSetup().config(), setup.config() );
|
||||
}
|
||||
|
||||
void TestQgsField::collection()
|
||||
{
|
||||
QgsField field( "collection", QVariant::List, "_int32", 0, 0, QString(), QVariant::Int );
|
||||
QCOMPARE( field.subType(), QVariant::Int );
|
||||
field.setSubType( QVariant::Double );
|
||||
QCOMPARE( field.subType(), QVariant::Double );
|
||||
|
||||
QVariant str( "hello" );
|
||||
QVERIFY( !field.convertCompatible( str ) );
|
||||
}
|
||||
|
||||
QTEST_MAIN( TestQgsField )
|
||||
#include "testqgsfield.moc"
|
||||
|
@ -142,4 +142,4 @@ ADD_QGIS_TEST(spinbox testqgsspinbox.cpp)
|
||||
ADD_QGIS_TEST(sqlcomposerdialog testqgssqlcomposerdialog.cpp)
|
||||
ADD_QGIS_TEST(editorwidgetregistrytest testqgseditorwidgetregistry.cpp)
|
||||
ADD_QGIS_TEST(keyvaluewidgettest testqgskeyvaluewidget.cpp)
|
||||
|
||||
ADD_QGIS_TEST(listwidgettest testqgslistwidget.cpp)
|
||||
|
@ -78,6 +78,13 @@ class TestQgsEditorWidgetRegistry: public QObject
|
||||
checkSimple( "double", "Range" );
|
||||
}
|
||||
|
||||
void arrayType()
|
||||
{
|
||||
checkSimple( "double[]", "List" );
|
||||
checkSimple( "int[]", "List" );
|
||||
checkSimple( "string[]", "List" );
|
||||
}
|
||||
|
||||
void configuredType()
|
||||
{
|
||||
QgsVectorLayer vl( "LineString?crs=epsg:3111&field=pk:int&field=col1:string", "vl", "memory" );
|
||||
|
126
tests/src/gui/testqgslistwidget.cpp
Normal file
126
tests/src/gui/testqgslistwidget.cpp
Normal file
@ -0,0 +1,126 @@
|
||||
/***************************************************************************
|
||||
testqgslistwidget.cpp
|
||||
--------------------------------------
|
||||
Date : 08 09 2016
|
||||
Copyright : (C) 2016 Patrick Valsecchi
|
||||
Email : patrick dot valsecchi at camptocamp dot com
|
||||
***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#include <QtTest/QtTest>
|
||||
|
||||
#include <editorwidgets/qgslistwidgetfactory.h>
|
||||
#include <qgslistwidget.h>
|
||||
#include <editorwidgets/core/qgseditorwidgetwrapper.h>
|
||||
#include <qgsapplication.h>
|
||||
|
||||
class TestQgsListWidget : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
private slots:
|
||||
void initTestCase() // will be called before the first testfunction is executed.
|
||||
{
|
||||
QgsApplication::init();
|
||||
QgsApplication::initQgis();
|
||||
}
|
||||
|
||||
void cleanupTestCase() // will be called after the last testfunction was executed.
|
||||
{
|
||||
QgsApplication::exitQgis();
|
||||
}
|
||||
|
||||
void testStringUpdate()
|
||||
{
|
||||
const QgsListWidgetFactory factory( "testList" );
|
||||
QgsVectorLayer vl( "Point?field=fld:string[]", "test", "memory" );
|
||||
QgsEditorWidgetWrapper* wrapper = factory.create( &vl, 0, nullptr, nullptr );
|
||||
QVERIFY( wrapper );
|
||||
QSignalSpy spy( wrapper, SIGNAL( valueChanged( const QVariant& ) ) );
|
||||
|
||||
QgsListWidget* widget = qobject_cast< QgsListWidget* >( wrapper->widget() );
|
||||
QVERIFY( widget );
|
||||
|
||||
QStringList initial;
|
||||
initial << "one" << "two";
|
||||
wrapper->setValue( initial );
|
||||
|
||||
const QVariant value = wrapper->value();
|
||||
QCOMPARE( int( value.type() ), int( QVariant::StringList ) );
|
||||
QCOMPARE( value.toStringList(), initial );
|
||||
QCOMPARE( spy.count(), 0 );
|
||||
|
||||
QAbstractItemModel* model = widget->tableView->model();
|
||||
model->setData( model->index( 0, 0 ), "hello" );
|
||||
QCOMPARE( spy.count(), 1 );
|
||||
QVERIFY( widget->valid() );
|
||||
|
||||
QStringList expected = initial;
|
||||
expected[0] = "hello";
|
||||
QVariant eventValue = spy.at( 0 ).at( 0 ).value<QVariant>();
|
||||
QCOMPARE( int( eventValue.type() ), int( QVariant::StringList ) );
|
||||
QCOMPARE( eventValue.toStringList(), expected );
|
||||
QCOMPARE( wrapper->value().toStringList(), expected );
|
||||
QCOMPARE( spy.count(), 1 );
|
||||
QVERIFY( widget->valid() );
|
||||
}
|
||||
|
||||
void testIntUpdate()
|
||||
{
|
||||
const QgsListWidgetFactory factory( "testList" );
|
||||
QgsVectorLayer vl( "Point?field=fld:int[]", "test", "memory" );
|
||||
QgsEditorWidgetWrapper* wrapper = factory.create( &vl, 0, nullptr, nullptr );
|
||||
QVERIFY( wrapper );
|
||||
QSignalSpy spy( wrapper, SIGNAL( valueChanged( const QVariant& ) ) );
|
||||
|
||||
QgsListWidget* widget = qobject_cast< QgsListWidget* >( wrapper->widget() );
|
||||
QVERIFY( widget );
|
||||
|
||||
QVariantList initial;
|
||||
initial << 1 << -2;
|
||||
wrapper->setValue( initial );
|
||||
|
||||
const QVariant value = wrapper->value();
|
||||
QCOMPARE( int( value.type() ), int( QVariant::List ) );
|
||||
QCOMPARE( value.toList(), initial );
|
||||
QCOMPARE( spy.count(), 0 );
|
||||
|
||||
QAbstractItemModel* model = widget->tableView->model();
|
||||
model->setData( model->index( 0, 0 ), 3 );
|
||||
QCOMPARE( spy.count(), 1 );
|
||||
|
||||
QVariantList expected = initial;
|
||||
expected[0] = 3;
|
||||
QCOMPARE( spy.count(), 1 );
|
||||
QVariant eventValue = spy.at( 0 ).at( 0 ).value<QVariant>();
|
||||
QCOMPARE( int( eventValue.type() ), int( QVariant::List ) );
|
||||
QCOMPARE( eventValue.toList(), expected );
|
||||
QCOMPARE( wrapper->value().toList(), expected );
|
||||
QVERIFY( widget->valid() );
|
||||
|
||||
model->setData( model->index( 0, 0 ), "a" );
|
||||
expected = initial;
|
||||
expected.removeAt( 0 );
|
||||
QVERIFY( !widget->valid() );
|
||||
QCOMPARE( wrapper->value().toList(), expected );
|
||||
|
||||
spy.clear();
|
||||
model->setData( model->index( 0, 0 ), 56 );
|
||||
expected = initial;
|
||||
expected[0] = 56;
|
||||
QCOMPARE( spy.count(), 1 );
|
||||
eventValue = spy.at( 0 ).at( 0 ).value<QVariant>();
|
||||
QCOMPARE( eventValue.toList(), expected );
|
||||
QCOMPARE( wrapper->value().toList(), expected );
|
||||
QVERIFY( widget->valid() );
|
||||
}
|
||||
};
|
||||
|
||||
QTEST_MAIN( TestQgsListWidget )
|
||||
#include "testqgslistwidget.moc"
|
@ -24,6 +24,22 @@ class TestQgsPostgresConn: public QObject
|
||||
QCOMPARE( QgsPostgresConn::quotedValue( "b \"c' \\x" ), QString( "E'b \"c'' \\\\x'" ) );
|
||||
}
|
||||
|
||||
void quotedValueStringArray()
|
||||
{
|
||||
QStringList list;
|
||||
list << "a" << "b \"c' \\x";
|
||||
const QString actual = QgsPostgresConn::quotedValue( list );
|
||||
QCOMPARE( actual, QString( "E'{\"a\",\"b \\\\\"c\\' \\\\\\\\x\"}'" ) );
|
||||
}
|
||||
|
||||
void quotedValueIntArray()
|
||||
{
|
||||
QVariantList list;
|
||||
list << 1 << -5;
|
||||
const QString actual = QgsPostgresConn::quotedValue( list );
|
||||
QCOMPARE( actual, QString( "E'{\"1\",\"-5\"}'" ) );
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
QTEST_MAIN( TestQgsPostgresConn )
|
||||
|
@ -9,27 +9,62 @@ class TestQgsPostgresProvider: public QObject
|
||||
private slots:
|
||||
void decodeHstore()
|
||||
{
|
||||
const QVariant decoded = QgsPostgresProvider::convertValue( QVariant::Map, "\"1\"=>\"2\", \"a\"=>\"b \\\"c'\"" );
|
||||
const QVariant decoded = QgsPostgresProvider::convertValue( QVariant::Map, QVariant::String, "\"1\"=>\"2\", \"a\"=>\"b, \\\"c'\", \"backslash\"=>\"\\\\\"" );
|
||||
QCOMPARE( decoded.type(), QVariant::Map );
|
||||
|
||||
QVariantMap expected;
|
||||
expected["1"] = "2";
|
||||
expected["a"] = "b \"c'";
|
||||
expected["a"] = "b, \"c'";
|
||||
expected["backslash"] = "\\";
|
||||
qDebug() << "actual: " << decoded;
|
||||
QCOMPARE( decoded.toMap(), expected );
|
||||
}
|
||||
|
||||
void decodeHstoreNoQuote()
|
||||
{
|
||||
const QVariant decoded = QgsPostgresProvider::convertValue( QVariant::Map, "1=>2, a=>b" );
|
||||
const QVariant decoded = QgsPostgresProvider::convertValue( QVariant::Map, QVariant::String, "1=>2, a=>b c" );
|
||||
QCOMPARE( decoded.type(), QVariant::Map );
|
||||
|
||||
QVariantMap expected;
|
||||
expected["1"] = "2";
|
||||
expected["a"] = "b";
|
||||
expected["a"] = "b c";
|
||||
qDebug() << "actual: " << decoded;
|
||||
QCOMPARE( decoded.toMap(), expected );
|
||||
}
|
||||
|
||||
void decodeArray2StringList()
|
||||
{
|
||||
const QVariant decoded = QgsPostgresProvider::convertValue( QVariant::StringList, QVariant::String, "{\"1\",\"2\", \"a\\\\1\" , \"\\\\\",\"b, \\\"c'\"}" );
|
||||
QCOMPARE( decoded.type(), QVariant::StringList );
|
||||
|
||||
QStringList expected;
|
||||
expected << "1" << "2" << "a\\1" << "\\" << "b, \"c'";
|
||||
qDebug() << "actual: " << decoded;
|
||||
QCOMPARE( decoded.toStringList(), expected );
|
||||
}
|
||||
|
||||
void decodeArray2StringListNoQuote()
|
||||
{
|
||||
const QVariant decoded = QgsPostgresProvider::convertValue( QVariant::StringList, QVariant::String, "{1,2, a ,b, c}" );
|
||||
QCOMPARE( decoded.type(), QVariant::StringList );
|
||||
|
||||
QStringList expected;
|
||||
expected << "1" << "2" << "a" << "b" << "c";
|
||||
qDebug() << "actual: " << decoded;
|
||||
QCOMPARE( decoded.toStringList(), expected );
|
||||
}
|
||||
|
||||
void decodeArray2IntList()
|
||||
{
|
||||
const QVariant decoded = QgsPostgresProvider::convertValue( QVariant::StringList, QVariant::String, "{1, 2, 3,-5,10}" );
|
||||
QCOMPARE( decoded.type(), QVariant::StringList );
|
||||
|
||||
QVariantList expected;
|
||||
expected << QVariant( 1 ) << QVariant( 2 ) << QVariant( 3 ) << QVariant( -5 ) << QVariant( 10 );
|
||||
qDebug() << "actual: " << decoded;
|
||||
QCOMPARE( decoded.toList(), expected );
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
QTEST_MAIN( TestQgsPostgresProvider )
|
||||
|
@ -143,7 +143,7 @@ class TestPyQgsMemoryProvider(unittest.TestCase, ProviderTestCase):
|
||||
layer = QgsVectorLayer("Point", "test", "memory")
|
||||
provider = layer.dataProvider()
|
||||
|
||||
res = provider.addAttributes([QgsField("name", QVariant.String, ),
|
||||
res = provider.addAttributes([QgsField("name", QVariant.String),
|
||||
QgsField("age", QVariant.Int),
|
||||
QgsField("size", QVariant.Double)])
|
||||
assert res, "Failed to add attributes"
|
||||
@ -193,7 +193,7 @@ class TestPyQgsMemoryProvider(unittest.TestCase, ProviderTestCase):
|
||||
layer = QgsVectorLayer("Point", "test", "memory")
|
||||
provider = layer.dataProvider()
|
||||
|
||||
provider.addAttributes([QgsField("name", QVariant.String, ),
|
||||
provider.addAttributes([QgsField("name", QVariant.String),
|
||||
QgsField("age", QVariant.Int),
|
||||
QgsField("size", QVariant.Double)])
|
||||
myMessage = ('Expected: %s\nGot: %s\n' %
|
||||
@ -267,7 +267,7 @@ class TestPyQgsMemoryProvider(unittest.TestCase, ProviderTestCase):
|
||||
layer = QgsVectorLayer("Point", "test", "memory")
|
||||
provider = layer.dataProvider()
|
||||
|
||||
res = provider.addAttributes([QgsField("name", QVariant.String, ),
|
||||
res = provider.addAttributes([QgsField("name", QVariant.String),
|
||||
QgsField("age", QVariant.Int),
|
||||
QgsField("size", QVariant.Double)])
|
||||
layer.updateFields()
|
||||
|
@ -350,7 +350,6 @@ class TestPyQgsPostgresProvider(unittest.TestCase, ProviderTestCase):
|
||||
|
||||
new_f = QgsFeature(vl.fields())
|
||||
new_f['pk'] = NULL
|
||||
#new_f['value'] = {'x': 'a\'s "y" \\', 'z': 'end'}
|
||||
new_f['value'] = {'simple': '1', 'doubleQuote': '"y"', 'quote': "'q'", 'backslash': '\\'}
|
||||
r, fs = vl.dataProvider().addFeatures([new_f])
|
||||
self.assertTrue(r)
|
||||
@ -366,6 +365,65 @@ class TestPyQgsPostgresProvider(unittest.TestCase, ProviderTestCase):
|
||||
self.assertTrue(vl.deleteFeatures([new_pk]))
|
||||
self.assertTrue(vl.commitChanges())
|
||||
|
||||
def testStringArray(self):
|
||||
vl = QgsVectorLayer('%s table="qgis_test"."string_array" sql=' % (self.dbconn), "teststringarray", "postgres")
|
||||
self.assertTrue(vl.isValid())
|
||||
|
||||
fields = vl.dataProvider().fields()
|
||||
self.assertEqual(fields.at(fields.indexFromName('value')).type(), QVariant.StringList)
|
||||
self.assertEqual(fields.at(fields.indexFromName('value')).subType(), QVariant.String)
|
||||
|
||||
f = next(vl.getFeatures(QgsFeatureRequest()))
|
||||
|
||||
value_idx = vl.fieldNameIndex('value')
|
||||
self.assertTrue(isinstance(f.attributes()[value_idx], list))
|
||||
self.assertEqual(f.attributes()[value_idx], ['a', 'b', 'c'])
|
||||
|
||||
new_f = QgsFeature(vl.fields())
|
||||
new_f['pk'] = NULL
|
||||
new_f['value'] = ['simple', '"doubleQuote"', "'quote'", 'back\\slash']
|
||||
r, fs = vl.dataProvider().addFeatures([new_f])
|
||||
self.assertTrue(r)
|
||||
new_pk = fs[0]['pk']
|
||||
self.assertNotEqual(new_pk, NULL, fs[0].attributes())
|
||||
|
||||
try:
|
||||
read_back = vl.getFeature(new_pk)
|
||||
self.assertEqual(read_back['pk'], new_pk)
|
||||
self.assertEqual(read_back['value'], new_f['value'])
|
||||
finally:
|
||||
self.assertTrue(vl.startEditing())
|
||||
self.assertTrue(vl.deleteFeatures([new_pk]))
|
||||
self.assertTrue(vl.commitChanges())
|
||||
|
||||
def testIntArray(self):
|
||||
vl = QgsVectorLayer('%s table="qgis_test"."int_array" sql=' % (self.dbconn), "testintarray", "postgres")
|
||||
self.assertTrue(vl.isValid())
|
||||
|
||||
fields = vl.dataProvider().fields()
|
||||
self.assertEqual(fields.at(fields.indexFromName('value')).type(), QVariant.List)
|
||||
self.assertEqual(fields.at(fields.indexFromName('value')).subType(), QVariant.Int)
|
||||
|
||||
f = next(vl.getFeatures(QgsFeatureRequest()))
|
||||
|
||||
value_idx = vl.fieldNameIndex('value')
|
||||
self.assertTrue(isinstance(f.attributes()[value_idx], list))
|
||||
self.assertEqual(f.attributes()[value_idx], [1, 2, -5])
|
||||
|
||||
def testDoubleArray(self):
|
||||
vl = QgsVectorLayer('%s table="qgis_test"."double_array" sql=' % (self.dbconn), "testdoublearray", "postgres")
|
||||
self.assertTrue(vl.isValid())
|
||||
|
||||
fields = vl.dataProvider().fields()
|
||||
self.assertEqual(fields.at(fields.indexFromName('value')).type(), QVariant.List)
|
||||
self.assertEqual(fields.at(fields.indexFromName('value')).subType(), QVariant.Double)
|
||||
|
||||
f = next(vl.getFeatures(QgsFeatureRequest()))
|
||||
|
||||
value_idx = vl.fieldNameIndex('value')
|
||||
self.assertTrue(isinstance(f.attributes()[value_idx], list))
|
||||
self.assertEqual(f.attributes()[value_idx], [1.1, 2, -5.12345])
|
||||
|
||||
|
||||
class TestPyQgsPostgresProviderCompoundKey(unittest.TestCase, ProviderTestCase):
|
||||
|
||||
|
1
tests/testdata/provider/testdata_pg.sh
vendored
1
tests/testdata/provider/testdata_pg.sh
vendored
@ -5,6 +5,7 @@ SCRIPTS="
|
||||
tests/testdata/provider/testdata_pg_reltests.sql
|
||||
tests/testdata/provider/testdata_pg_vectorjoin.sql
|
||||
tests/testdata/provider/testdata_pg_hstore.sql
|
||||
tests/testdata/provider/testdata_pg_array.sql
|
||||
"
|
||||
|
||||
dropdb qgis_test 2> /dev/null || true
|
||||
|
37
tests/testdata/provider/testdata_pg_array.sql
vendored
Normal file
37
tests/testdata/provider/testdata_pg_array.sql
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
DROP TABLE IF EXISTS qgis_test.string_array;
|
||||
|
||||
CREATE TABLE qgis_test.string_array
|
||||
(
|
||||
pk SERIAL NOT NULL PRIMARY KEY,
|
||||
value text[]
|
||||
);
|
||||
|
||||
INSERT INTO qgis_test.string_array(value)
|
||||
VALUES
|
||||
('{a,b,c}');
|
||||
|
||||
|
||||
DROP TABLE IF EXISTS qgis_test.int_array;
|
||||
|
||||
CREATE TABLE qgis_test.int_array
|
||||
(
|
||||
pk SERIAL NOT NULL PRIMARY KEY,
|
||||
value int4[]
|
||||
);
|
||||
|
||||
INSERT INTO qgis_test.int_array(value)
|
||||
VALUES
|
||||
('{1,2,-5}');
|
||||
|
||||
DROP TABLE IF EXISTS qgis_test.double_array;
|
||||
|
||||
CREATE TABLE qgis_test.double_array
|
||||
(
|
||||
pk SERIAL NOT NULL PRIMARY KEY,
|
||||
value float8[]
|
||||
);
|
||||
|
||||
INSERT INTO qgis_test.double_array(value)
|
||||
VALUES
|
||||
('{1.1,2,-5.12345}');
|
||||
|
Loading…
x
Reference in New Issue
Block a user