Compare commits

...

16 Commits

Author SHA1 Message Date
Dietrich Göttlicher
4350c6c9b6
Merge be69d80f16e817fb9e18eac759377d4304f0c1f2 into 74549aad26c3358101e88477d9dfa1caae013d72 2025-07-01 10:32:40 +00:00
Dietrich Göttlicher
be69d80f16
Merge branch 'qgis:master' into testing 2025-07-01 10:32:36 +00:00
Juergen E. Fischer
74549aad26 Reapply "Allow free naming of project properties (#60855)"
This reverts commit fb11239112adfc321b3bbacbb20da888a7a37c23.
2025-07-01 09:08:44 +00:00
Juergen E. Fischer
eac401c009 if a plugin supports qt6 it should also support QGIS4 (fixes #62359) 2025-07-01 09:07:26 +00:00
goettlic
6486f3b081 adjusting decoration scalebar fixing #15647 2025-07-01 07:49:10 +00:00
goettlic
2459a9f1db adjusting decoration scalebar fixing #15647 2025-07-01 07:49:10 +00:00
goettlic
f712a870c5 adjusting decoration scalebar fixing #15647 2025-07-01 07:49:10 +00:00
goettlic
e170670b48 adjusting decoration scalebar fixing #15647 2025-07-01 07:49:10 +00:00
goettlic
6fa70808a2 adjusting decoration scalebar fixing #15647 2025-07-01 07:49:10 +00:00
Loïc Bartoletti
1f0166d35e
Merge pull request #62365 from benoitdm-oslandia/fix/cgibin_dir
fix: update default cgi-bin installation paths according to OS
2025-07-01 07:26:25 +02:00
Loïc Bartoletti
ada589bb1d
Merge pull request #62052 from martin-s42/visitPointsByRegularDistance-segfault-fix
fix segfault due to calling qgsDoubleNear with tollerance that can ne…
2025-07-01 06:59:03 +02:00
bdm-oslandia
b1c8ef3265 fixup! fix: update default cgi-bin installation paths according to OS 2025-06-27 09:35:48 +02:00
bdm-oslandia
f4cf09d4b0 fixup! fix: update default cgi-bin installation paths according to OS 2025-06-25 08:29:52 +02:00
bdm-oslandia
9db58e3726 fixup! fix: update default cgi-bin installation paths according to OS 2025-06-24 17:37:57 +02:00
bdm-oslandia
551aa20f20 fix: update default cgi-bin installation paths according to OS 2025-06-23 10:44:49 +02:00
Martin Siegert
a156c43f7b
fix segfault due to calling qgsDoubleNear with tollerance that can never been reached
see issue #60772: function interpolatePoint in qgsabstractprofilesurfacegenerator.cpp:157 can return a nullpointer causing a segfault because qgsDoubleNear in visitPointsByRegularDistance in qgslinestring.cpp is called with a tolerance that is too small so that qgsDoubleNear always returns false. Solution: scale epsilon by the size of the numbers to be compared.
2025-05-29 13:37:03 -07:00
12 changed files with 507 additions and 371 deletions

View File

@ -979,7 +979,31 @@ if (WITH_CORE)
else()
# UNIX
set (DEFAULT_BIN_SUBDIR bin)
set (DEFAULT_CGIBIN_SUBDIR bin)
# From https://www.cyberciti.biz/faq/how-do-i-find-the-url-for-my-cgi-bin/
execute_process(COMMAND lsb_release -a OUTPUT_VARIABLE LSB_RELEASE_A)
if(EXISTS "/etc/fedora-release")
# in /var/www/cgi-bin
set (DEFAULT_CGIBIN_SUBDIR www/cgi-bin)
elseif (${CMAKE_HOST_SYSTEM_NAME} MATCHES "FreeBSD")
# in /usr/local/www/cgi-bin/
set (DEFAULT_CGIBIN_SUBDIR www/cgi-bin)
elseif (${CMAKE_HOST_SYSTEM_NAME} MATCHES "BSD")
# in /usr/local/libexec/cgi-bin/
set (DEFAULT_CGIBIN_SUBDIR libexec/cgi-bin)
elseif ("${LSB_RELEASE_A}" MATCHES "Ubuntu" OR "${LSB_RELEASE_A}" MATCHES "Debian" OR "${LSB_RELEASE_A}" MATCHES "Mint")
# in /usr/lib/cgi-bin/
set (DEFAULT_CGIBIN_SUBDIR lib/cgi-bin)
else()
# others: Red Hat/CentOS/Rocky/Alma Linux
# in /var/www/cgi-bin/
set (DEFAULT_CGIBIN_SUBDIR www/cgi-bin)
endif()
set (DEFAULT_LIB_SUBDIR lib${LIB_SUFFIX})
set (DEFAULT_DATA_SUBDIR share/qgis)
set (DEFAULT_LIBEXEC_SUBDIR lib${LIB_SUFFIX}/qgis)

View File

@ -659,7 +659,10 @@ class Repositories(QObject):
.strip()
)
if not qgisMaximumVersion:
qgisMaximumVersion = qgisMinimumVersion[0] + ".99"
if qgisMinimumVersion[0] == "3" and supports_qt6:
qgisMaximumVersion = "4.99"
else:
qgisMaximumVersion = qgisMinimumVersion[0] + ".99"
# if compatible, add the plugin to the list
if not pluginNodes.item(i).firstChildElement(
"disabled"
@ -845,7 +848,10 @@ class Plugins(QObject):
qgisMinimumVersion = "0"
qgisMaximumVersion = pluginMetadata("qgisMaximumVersion").strip()
if not qgisMaximumVersion:
qgisMaximumVersion = qgisMinimumVersion[0] + ".99"
if qgisMinimumVersion[0] == "3" and supports_qt6:
qgisMaximumVersion = "4.99"
else:
qgisMaximumVersion = qgisMinimumVersion[0] + ".99"
# if compatible, add the plugin to the list
if not isCompatible(
pyQgisVersion(), qgisMinimumVersion, qgisMaximumVersion

View File

@ -33,7 +33,6 @@ email : sbr00pwb@users.sourceforge.net
#include "qgspointxy.h"
#include "qgsproject.h"
#include "qgsunittypes.h"
#include "qgssettings.h"
#include "qgscolorutils.h"
#include "qgsfillsymbollayer.h"
#include "qgsfillsymbol.h"
@ -60,7 +59,7 @@ email : sbr00pwb@users.sourceforge.net
QgsDecorationScaleBar::QgsDecorationScaleBar( QObject *parent )
: QgsDecorationItem( parent )
{
mPlacement = TopLeft;
mPlacement = BottomLeft;
mMarginUnit = Qgis::RenderUnit::Millimeters;
mStyleLabels << tr( "Tick Down" ) << tr( "Tick Up" )
<< tr( "Bar" ) << tr( "Box" );
@ -77,7 +76,8 @@ void QgsDecorationScaleBar::projectRead()
{
QgsDecorationItem::projectRead();
mPreferredSize = QgsProject::instance()->readNumEntry( mConfigurationName, QStringLiteral( "/PreferredSize" ), 30 );
mStyleIndex = QgsProject::instance()->readNumEntry( mConfigurationName, QStringLiteral( "/Style" ), 0 );
mMaximumWidth = QgsProject::instance()->readNumEntry( mConfigurationName, QStringLiteral( "/MaxWidth" ), 33 );
mStyleIndex = QgsProject::instance()->readNumEntry( mConfigurationName, QStringLiteral( "/Style" ), 1 );
mSnapping = QgsProject::instance()->readBoolEntry( mConfigurationName, QStringLiteral( "/Snapping" ), true );
mColor = QgsColorUtils::colorFromString( QgsProject::instance()->readEntry( mConfigurationName, QStringLiteral( "/Color" ), QStringLiteral( "#000000" ) ) );
mOutlineColor = QgsColorUtils::colorFromString( QgsProject::instance()->readEntry( mConfigurationName, QStringLiteral( "/OutlineColor" ), QStringLiteral( "#FFFFFF" ) ) );
@ -120,6 +120,7 @@ void QgsDecorationScaleBar::saveToProject()
{
QgsDecorationItem::saveToProject();
QgsProject::instance()->writeEntry( mConfigurationName, QStringLiteral( "/PreferredSize" ), mPreferredSize );
QgsProject::instance()->writeEntry( mConfigurationName, QStringLiteral( "/MaxWidth" ), mMaximumWidth );
QgsProject::instance()->writeEntry( mConfigurationName, QStringLiteral( "/Snapping" ), mSnapping );
QgsProject::instance()->writeEntry( mConfigurationName, QStringLiteral( "/Style" ), mStyleIndex );
QgsProject::instance()->writeEntry( mConfigurationName, QStringLiteral( "/Color" ), QgsColorUtils::colorToString( mColor ) );
@ -139,7 +140,7 @@ void QgsDecorationScaleBar::saveToProject()
void QgsDecorationScaleBar::run()
{
QgsDecorationScaleBarDialog dlg( *this, QgisApp::instance()->mapCanvas()->mapUnits(), QgisApp::instance() );
QgsDecorationScaleBarDialog dlg( *this, QgisApp::instance() );
dlg.exec();
}
@ -252,34 +253,34 @@ void QgsDecorationScaleBar::render( const QgsMapSettings &mapSettings, QgsRender
const float deviceHeight = static_cast<float>( device->height() ) / context.devicePixelRatio();
const float deviceWidth = static_cast<float>( device->width() ) / context.devicePixelRatio();
const Qgis::DistanceUnit preferredUnits = QgsProject::instance()->distanceUnits();
Qgis::DistanceUnit scaleBarUnits = mapSettings.mapUnits();
//Get map units per pixel
const double scaleBarUnitsPerPixel = ( mapWidth( mapSettings ) / mapSettings.outputSize().width() ) * QgsUnitTypes::fromUnitToUnitFactor( mSettings.units(), preferredUnits );
scaleBarUnits = preferredUnits;
Qgis::DistanceUnit scaleBarUnits = preferredUnits;
// Exit if the canvas width is 0 or layercount is 0 or QGIS will freeze
if ( mapSettings.layers().isEmpty() || !deviceWidth || !scaleBarUnitsPerPixel )
return;
//Set length of scale bar to desired length in map units
double unitsPerSegment = mPreferredSize;
//Calculate size of scale bar for preferred number of map units
//Calculate size of scale bar in pixels for preferred number of map units
double scaleBarWidth = mPreferredSize / scaleBarUnitsPerPixel;
//If scale bar is very small reset to 1/4 of the canvas wide
//If scale bar is very small (< 30 pixels) reset to max percentage of map canvas
if ( scaleBarWidth < 30 )
{
scaleBarWidth = deviceWidth / 4.0; // value in pixels
scaleBarWidth = deviceWidth * mMaximumWidth / 100; // value in pixels
unitsPerSegment = scaleBarWidth * scaleBarUnitsPerPixel; // value in map units
}
//if scale bar is more than half the canvas wide keep halving until not
while ( scaleBarWidth > deviceWidth / 3.0 )
//if scale bar is more than the maximum percentage of the width of the map canvas resize to maxWidth
if ( scaleBarWidth > deviceWidth * mMaximumWidth / 100 )
{
scaleBarWidth = scaleBarWidth / 3;
scaleBarWidth = deviceWidth * mMaximumWidth / 100;
unitsPerSegment = scaleBarWidth * scaleBarUnitsPerPixel;
}
unitsPerSegment = scaleBarWidth * scaleBarUnitsPerPixel;
// Work out the exponent for the number - e.g, 1234 will give 3,
// and .001234 will give -3

View File

@ -53,6 +53,8 @@ class APP_EXPORT QgsDecorationScaleBar : public QgsDecorationItem
private:
//! The size preferred size of the scale bar
int mPreferredSize;
//! The maximum width of the scale bar in percentage of width of map canvas
int mMaximumWidth;
//! Should we snap to integer times power of 10?
bool mSnapping;
//! Style of scale bar. An index and the translated text

View File

@ -13,7 +13,6 @@
#include "qgsdecorationscalebardialog.h"
#include "moc_qgsdecorationscalebardialog.cpp"
#include "qgsdecorationscalebar.h"
#include "qgslogger.h"
#include "qgshelp.h"
#include "qgsgui.h"
@ -21,7 +20,7 @@
#include <QDialogButtonBox>
#include <QPushButton>
QgsDecorationScaleBarDialog::QgsDecorationScaleBarDialog( QgsDecorationScaleBar &deco, Qgis::DistanceUnit units, QWidget *parent )
QgsDecorationScaleBarDialog::QgsDecorationScaleBarDialog( QgsDecorationScaleBar &deco, QWidget *parent )
: QDialog( parent )
, mDeco( deco )
{
@ -36,25 +35,10 @@ QgsDecorationScaleBarDialog::QgsDecorationScaleBarDialog( QgsDecorationScaleBar
QPushButton *applyButton = buttonBox->button( QDialogButtonBox::Apply );
connect( applyButton, &QAbstractButton::clicked, this, &QgsDecorationScaleBarDialog::apply );
// set the map units in the spin box
spnSize->setShowClearButton( false );
switch ( units )
{
case Qgis::DistanceUnit::Meters:
spnSize->setSuffix( tr( " meters/km" ) );
break;
case Qgis::DistanceUnit::Feet:
case Qgis::DistanceUnit::Miles:
spnSize->setSuffix( tr( " feet/miles" ) );
break;
case Qgis::DistanceUnit::Degrees:
spnSize->setSuffix( tr( " degrees" ) );
break;
default:
QgsDebugError( QStringLiteral( "Error: not picked up map units - actual value = %1" ).arg( qgsEnumValueToKey( units ) ) );
}
spnSize->setValue( mDeco.mPreferredSize );
spnMaxWidth->setValue( mDeco.mMaximumWidth );
chkSnapping->setChecked( mDeco.mSnapping );
// placement
@ -114,6 +98,7 @@ void QgsDecorationScaleBarDialog::apply()
mDeco.mMarginHorizontal = spnHorizontal->value();
mDeco.mMarginVertical = spnVertical->value();
mDeco.mPreferredSize = spnSize->value();
mDeco.mMaximumWidth = spnMaxWidth->value();
mDeco.mSnapping = chkSnapping->isChecked();
mDeco.setEnabled( grpEnable->isChecked() );
mDeco.mStyleIndex = cboStyle->currentIndex();

View File

@ -24,7 +24,7 @@ class APP_EXPORT QgsDecorationScaleBarDialog : public QDialog, private Ui::QgsDe
Q_OBJECT
public:
QgsDecorationScaleBarDialog( QgsDecorationScaleBar &deco, Qgis::DistanceUnit units, QWidget *parent = nullptr );
QgsDecorationScaleBarDialog( QgsDecorationScaleBar &deco, QWidget *parent = nullptr );
private slots:
void buttonBox_accepted();

View File

@ -727,19 +727,25 @@ bool QgsPluginRegistry::checkPythonPlugin( const QString &packageName )
bool QgsPluginRegistry::isPythonPluginCompatible( const QString &packageName ) const
{
#ifdef WITH_BINDINGS
#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 )
bool supportsQgis4 = true;
const QString supportsQt6 = mPythonUtils->getPluginMetadata( packageName, QStringLiteral( "supportsQt6" ) ).trimmed();
if ( supportsQt6.compare( QLatin1String( "YES" ), Qt::CaseInsensitive ) != 0 && supportsQt6.compare( QLatin1String( "TRUE" ), Qt::CaseInsensitive ) != 0 )
{
#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 )
if ( !getenv( "QGIS_DISABLE_SUPPORTS_QT6_CHECK" ) )
{
return false;
}
}
#endif
supportsQgis4 = false;
}
const QString minVersion = mPythonUtils->getPluginMetadata( packageName, QStringLiteral( "qgisMinimumVersion" ) );
// try to read qgisMaximumVersion. Note checkQgisVersion can cope with "__error__" value.
const QString maxVersion = mPythonUtils->getPluginMetadata( packageName, QStringLiteral( "qgisMaximumVersion" ) );
QString maxVersion = mPythonUtils->getPluginMetadata( packageName, QStringLiteral( "qgisMaximumVersion" ) );
if ( maxVersion == QLatin1String( "__error__" ) && minVersion.startsWith( QLatin1String( "3." ) ) && supportsQgis4 )
{
maxVersion = QLatin1String( "4.99.0" );
}
return minVersion != QLatin1String( "__error__" ) && checkQgisVersion( minVersion, maxVersion );
#else
Q_UNUSED( packageName )

View File

@ -1532,6 +1532,7 @@ void QgsLineString::visitPointsByRegularDistance( const double distance, const s
double pZ = std::numeric_limits<double>::quiet_NaN();
double pM = std::numeric_limits<double>::quiet_NaN();
double nextPointDistance = distance;
const double eps = 4 * nextPointDistance * std::numeric_limits<double>::epsilon ();
for ( int i = 1; i < totalPoints; ++i )
{
double thisX = *x++;
@ -1540,7 +1541,7 @@ void QgsLineString::visitPointsByRegularDistance( const double distance, const s
double thisM = m ? *m++ : 0.0;
const double segmentLength = QgsGeometryUtilsBase::distance2D( thisX, thisY, prevX, prevY );
while ( nextPointDistance < distanceTraversed + segmentLength || qgsDoubleNear( nextPointDistance, distanceTraversed + segmentLength ) )
while ( nextPointDistance < distanceTraversed + segmentLength || qgsDoubleNear( nextPointDistance, distanceTraversed + segmentLength, eps ) )
{
// point falls on this segment - truncate to segment length if qgsDoubleNear test was actually > segment length
const double distanceToPoint = std::min( nextPointDistance - distanceTraversed, segmentLength );

View File

@ -116,21 +116,6 @@ QStringList makeKeyTokens_( const QString &scope, const QString &key )
// be sure to include the canonical root node
keyTokens.push_front( QStringLiteral( "properties" ) );
//check validy of keys since an invalid xml name will will be dropped upon saving the xml file. If not valid, we print a message to the console.
for ( int i = 0; i < keyTokens.size(); ++i )
{
const QString keyToken = keyTokens.at( i );
//invalid chars in XML are found at http://www.w3.org/TR/REC-xml/#NT-NameChar
//note : it seems \x10000-\xEFFFF is valid, but it when added to the regexp, a lot of unwanted characters remain
const thread_local QRegularExpression sInvalidRegexp = QRegularExpression( QStringLiteral( "([^:A-Z_a-z\\x{C0}-\\x{D6}\\x{D8}-\\x{F6}\\x{F8}-\\x{2FF}\\x{370}-\\x{37D}\\x{37F}-\\x{1FFF}\\x{200C}-\\x{200D}\\x{2070}-\\x{218F}\\x{2C00}-\\x{2FEF}\\x{3001}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFFD}\\-\\.0-9\\x{B7}\\x{0300}-\\x{036F}\\x{203F}-\\x{2040}]|^[^:A-Z_a-z\\x{C0}-\\x{D6}\\x{D8}-\\x{F6}\\x{F8}-\\x{2FF}\\x{370}-\\x{37D}\\x{37F}-\\x{1FFF}\\x{200C}-\\x{200D}\\x{2070}-\\x{218F}\\x{2C00}-\\x{2FEF}\\x{3001}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFFD}])" ) );
if ( keyToken.contains( sInvalidRegexp ) )
{
const QString errorString = QObject::tr( "Entry token invalid : '%1'. The token will not be saved to file." ).arg( keyToken );
QgsMessageLog::logMessage( errorString, QString(), Qgis::MessageLevel::Critical );
}
}
return keyTokens;
}
@ -1322,20 +1307,20 @@ void dump_( const QgsProjectPropertyKey &topQgsPropertyKey )
* scope. "layers" is a list containing three string values.
*
* \code{.xml}
* <properties>
* <fsplugin>
* <foo type="int" >42</foo>
* <baz type="int" >1</baz>
* <layers type="QStringList" >
* <properties name="properties">
* <properties name="fsplugin">
* <properties name="foo" type="int" >42</properties>
* <properties name="baz" type="int" >1</properties>
* <properties name="layers" type="QStringList">
* <value>railroad</value>
* <value>airport</value>
* </layers>
* <xyqzzy type="int" >1</xyqzzy>
* <bar type="double" >123.456</bar>
* <feature_types type="QStringList" >
* </properties>
* <properties name="xyqzzy" type="int" >1</properties>
* <properties name="bar" type="double" >123.456</properties>
* <properties name="feature_types" type="QStringList">
* <value>type</value>
* </feature_types>
* </fsplugin>
* </properties>
* </properties>
* </properties>
* \endcode
*
@ -3992,10 +3977,25 @@ bool QgsProject::createEmbeddedLayer( const QString &layerId, const QString &pro
const QDomElement propertiesElem = sProjectDocument.documentElement().firstChildElement( QStringLiteral( "properties" ) );
if ( !propertiesElem.isNull() )
{
const QDomElement absElem = propertiesElem.firstChildElement( QStringLiteral( "Paths" ) ).firstChildElement( QStringLiteral( "Absolute" ) );
if ( !absElem.isNull() )
QDomElement e = propertiesElem.firstChildElement( QStringLiteral( "Paths" ) );
if ( e.isNull() )
{
useAbsolutePaths = absElem.text().compare( QLatin1String( "true" ), Qt::CaseInsensitive ) == 0;
e = propertiesElem.firstChildElement( QStringLiteral( "properties" ) );
while ( !e.isNull() && e.attribute( QStringLiteral( "name" ) ) != QStringLiteral( "Paths" ) )
e = e.nextSiblingElement( QStringLiteral( "properties" ) );
e = e.firstChildElement( QStringLiteral( "properties" ) );
while ( !e.isNull() && e.attribute( QStringLiteral( "name" ) ) != QStringLiteral( "Absolute" ) )
e = e.nextSiblingElement( QStringLiteral( "properties" ) );
}
else
{
e = e.firstChildElement( QStringLiteral( "Absolute" ) );
}
if ( !e.isNull() )
{
useAbsolutePaths = e.text().compare( QLatin1String( "true" ), Qt::CaseInsensitive ) == 0;
}
}

View File

@ -233,15 +233,15 @@ bool QgsProjectPropertyValue::readXml( const QDomNode &keyNode )
// keyElement is created by parent QgsProjectPropertyKey
bool QgsProjectPropertyValue::writeXml( QString const &nodeName,
QDomElement &keyElement,
QDomDocument &document )
QDomElement &keyElement,
QDomDocument &document )
{
QDomElement valueElement = document.createElement( nodeName );
QDomElement valueElement = document.createElement( QStringLiteral( "properties" ) );
// remember the type so that we can rebuild it when the project is read in
valueElement.setAttribute( QStringLiteral( "name" ), nodeName );
valueElement.setAttribute( QStringLiteral( "type" ), mValue.typeName() );
// we handle string lists differently from other types in that we
// create a sequence of repeated elements to cover all the string list
// members; each value will be in a <value></value> tag.
@ -362,33 +362,41 @@ bool QgsProjectPropertyKey::readXml( const QDomNode &keyNode )
while ( i < subkeys.count() )
{
const QDomNode subkey = subkeys.item( i );
QString name;
if ( subkey.nodeName() == QStringLiteral( "properties" ) &&
subkey.hasAttributes() && // if we have attributes
subkey.isElement() && // and we're an element
subkey.toElement().hasAttribute( QStringLiteral( "name" ) ) ) // and we have a "name" attribute
name = subkey.toElement().attribute( QStringLiteral( "name" ) );
else
name = subkey.nodeName();
// if the current node is an element that has a "type" attribute,
// then we know it's a leaf node; i.e., a subkey _value_, and not
// a subkey
if ( subkeys.item( i ).hasAttributes() && // if we have attributes
subkeys.item( i ).isElement() && // and we're an element
subkeys.item( i ).toElement().hasAttribute( QStringLiteral( "type" ) ) ) // and we have a "type" attribute
if ( subkey.hasAttributes() && // if we have attributes
subkey.isElement() && // and we're an element
subkey.toElement().hasAttribute( QStringLiteral( "type" ) ) ) // and we have a "type" attribute
{
// then we're a key value
delete mProperties.take( subkeys.item( i ).nodeName() );
mProperties.insert( subkeys.item( i ).nodeName(), new QgsProjectPropertyValue );
//
delete mProperties.take( name );
mProperties.insert( name, new QgsProjectPropertyValue );
QDomNode subkey = subkeys.item( i );
if ( !mProperties[subkeys.item( i ).nodeName()]->readXml( subkey ) )
if ( !mProperties[name]->readXml( subkey ) )
{
QgsDebugError( QStringLiteral( "unable to parse key value %1" ).arg( subkeys.item( i ).nodeName() ) );
QgsDebugError( QStringLiteral( "unable to parse key value %1" ).arg( name ) );
}
}
else // otherwise it's a subkey, so just recurse on down the remaining keys
{
addKey( subkeys.item( i ).nodeName() );
addKey( name );
QDomNode subkey = subkeys.item( i );
if ( !mProperties[subkeys.item( i ).nodeName()]->readXml( subkey ) )
if ( !mProperties[name]->readXml( subkey ) )
{
QgsDebugError( QStringLiteral( "unable to parse subkey %1" ).arg( subkeys.item( i ).nodeName() ) );
QgsDebugError( QStringLiteral( "unable to parse subkey %1" ).arg( name ) );
}
}
@ -408,7 +416,8 @@ bool QgsProjectPropertyKey::writeXml( QString const &nodeName, QDomElement &elem
// If it's an _empty_ node (i.e., one with no properties) we need to emit
// an empty place holder; else create new Dom elements as necessary.
QDomElement keyElement = document.createElement( nodeName ); // Dom element for this property key
QDomElement keyElement = document.createElement( "properties" ); // Dom element for this property key
keyElement.toElement().setAttribute( QStringLiteral( "name" ), nodeName );
if ( ! mProperties.isEmpty() )
{

View File

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>596</width>
<height>337</height>
<width>600</width>
<height>320</height>
</rect>
</property>
<property name="windowTitle">
@ -17,21 +17,11 @@
<iconset>
<normaloff>../../../../../.designer/backup</normaloff>../../../../../.designer/backup</iconset>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="0">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Help|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="grpEnable">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
@ -39,7 +29,7 @@
<property name="minimumSize">
<size>
<width>0</width>
<height>220</height>
<height>0</height>
</size>
</property>
<property name="title">
@ -51,14 +41,85 @@
<property name="checked">
<bool>false</bool>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="3" column="1">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="textLabel1">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Scale bar style</string>
</property>
<property name="buddy">
<cstring>cboStyle</cstring>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="cboStyle">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Select the style of the scale bar</string>
</property>
<property name="currentText">
<string>Tick Up</string>
</property>
<property name="currentIndex">
<number>1</number>
</property>
<item>
<property name="text">
<string>Tick Down</string>
</property>
</item>
<item>
<property name="text">
<string>Tick Up</string>
</property>
</item>
<item>
<property name="text">
<string>Box</string>
</property>
</item>
<item>
<property name="text">
<string>Bar</string>
</property>
</item>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="textLabel1_3_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Color of bar</string>
</property>
</widget>
</item>
<item row="1" column="1" colspan="2">
<layout class="QHBoxLayout" name="colorLayout">
<item>
<widget class="QLabel" name="fillLabel">
<property name="text">
<string>Fill</string>
</property>
<property name="buddy">
<cstring>pbnChangeColor</cstring>
</property>
</widget>
</item>
<item>
@ -85,6 +146,9 @@
<property name="text">
<string>Outline</string>
</property>
<property name="buddy">
<cstring>pbnChangeOutlineColor</cstring>
</property>
</widget>
</item>
<item>
@ -106,130 +170,6 @@
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="0">
<widget class="QLabel" name="textLabel1">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Scale bar style</string>
</property>
<property name="buddy">
<cstring>cboStyle</cstring>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="textLabel1_3">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Size of bar</string>
</property>
<property name="buddy">
<cstring>spnSize</cstring>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="cboStyle">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Select the style of the scale bar</string>
</property>
<item>
<property name="text">
<string>Tick Down</string>
</property>
</item>
<item>
<property name="text">
<string>Tick Up</string>
</property>
</item>
<item>
<property name="text">
<string>Box</string>
</property>
</item>
<item>
<property name="text">
<string>Bar</string>
</property>
</item>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="textLabel1_3_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Color of bar</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="textLabel1_3_22">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Font of bar</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QgsFontButton" name="mButtonFontStyle">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Font</string>
</property>
</widget>
</item>
<item row="5" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QgsSpinBox" name="spnSize">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>150</width>
<height>0</height>
</size>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
@ -245,8 +185,234 @@
</item>
</layout>
</item>
<item row="8" column="1">
<layout class="QHBoxLayout" name="hlytMargin" stretch="0,0,0,0,0">
<item row="2" column="0">
<widget class="QLabel" name="textLabel1_3_22">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Font of bar</string>
</property>
<property name="buddy">
<cstring>mButtonFontStyle</cstring>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QgsFontButton" name="mButtonFontStyle">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Font</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="lblSize">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Size of bar</string>
</property>
</widget>
</item>
<item row="3" column="1" colspan="2">
<layout class="QHBoxLayout" name="hlytSize">
<item>
<widget class="QLabel" name="lblmapunits">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>70</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>60</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Map Units</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
<property name="buddy">
<cstring>spnSize</cstring>
</property>
</widget>
</item>
<item>
<widget class="QgsSpinBox" name="spnSize">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>90</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Desired length of scale bar in measuring map units</string>
</property>
<property name="maximum">
<number>10000</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lblMaxSize">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>70</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>60</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Max Width</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
<property name="buddy">
<cstring>spnMaxWidth</cstring>
</property>
</widget>
</item>
<item>
<widget class="QgsSpinBox" name="spnMaxWidth">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>90</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Maximum width of scale bar as percentage of map canvas (1-80%)</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>80</number>
</property>
<property name="value">
<number>50</number>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="4" column="0" colspan="3">
<widget class="QCheckBox" name="chkSnapping">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Automatically snap to round number on resize</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="lblLocation">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Placement</string>
</property>
<property name="buddy">
<cstring>cboPlacement</cstring>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QComboBox" name="cboPlacement">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="lblMargin">
<property name="minimumSize">
<size>
<width>155</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Margin from edge</string>
</property>
</widget>
</item>
<item row="6" column="1" colspan="2">
<layout class="QHBoxLayout" name="hlytMargin" stretch="0,0,0,0,0,0">
<property name="spacing">
<number>10</number>
</property>
@ -279,6 +445,9 @@
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
<property name="buddy">
<cstring>spnHorizontal</cstring>
</property>
</widget>
</item>
<item>
@ -301,15 +470,15 @@
<height>16777215</height>
</size>
</property>
<property name="toolTip">
<string>Horizontal offset on the map canvas from the placement anchor</string>
</property>
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="toolTip">
<string>Horizontal offset on the map canvas from the placement anchor</string>
</property>
</widget>
</item>
<item>
@ -329,6 +498,9 @@
<property name="text">
<string>Vertical</string>
</property>
<property name="buddy">
<cstring>spnVertical</cstring>
</property>
</widget>
</item>
<item>
@ -351,12 +523,12 @@
<height>16777215</height>
</size>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="toolTip">
<string>Vertical offset on the map canvas from the placement anchor</string>
</property>
<property name="maximum">
<number>100</number>
</property>
</widget>
</item>
<item>
@ -366,64 +538,22 @@
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="7" column="1">
<widget class="QComboBox" name="cboPlacement">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="8" column="0">
<widget class="QLabel" name="lblMargin">
<property name="minimumSize">
<size>
<width>155</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Margin from edge</string>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QLabel" name="lblLocation">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Placement</string>
</property>
<property name="buddy">
<cstring>cboPlacement</cstring>
</property>
</widget>
</item>
<item row="6" column="0" colspan="2">
<widget class="QCheckBox" name="chkSnapping">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Automatically snap to round number on resize</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="9" column="0" colspan="2">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
@ -436,9 +566,58 @@
</property>
</spacer>
</item>
<item row="5" column="2">
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="2" column="2">
<spacer name="horizontalSpacer_5">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="2">
<spacer name="horizontalSpacer_6">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Help|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<layoutdefault spacing="6" margin="11"/>
@ -473,6 +652,7 @@
<tabstop>pbnChangeOutlineColor</tabstop>
<tabstop>mButtonFontStyle</tabstop>
<tabstop>spnSize</tabstop>
<tabstop>spnMaxWidth</tabstop>
<tabstop>chkSnapping</tabstop>
<tabstop>cboPlacement</tabstop>
<tabstop>spnHorizontal</tabstop>

View File

@ -65,84 +65,6 @@ class TestQgsProject(QgisTestCase):
QgisTestCase.__init__(self, methodName)
self.messageCaught = False
def test_makeKeyTokens_(self):
# see http://www.w3.org/TR/REC-xml/#d0e804 for a list of valid characters
invalidTokens = []
validTokens = []
# all test tokens will be generated by prepending or inserting characters to this token
validBase = "valid"
# some invalid characters, not allowed anywhere in a token
# note that '/' must not be added here because it is taken as a separator by makeKeyTokens_()
invalidChars = "+*,;<>|!$%()=?#\x01"
# generate the characters that are allowed at the start of a token (and at every other position)
validStartChars = ":_"
charRanges = [
(ord("a"), ord("z")),
(ord("A"), ord("Z")),
(0x00F8, 0x02FF),
(0x0370, 0x037D),
(0x037F, 0x1FFF),
(0x200C, 0x200D),
(0x2070, 0x218F),
(0x2C00, 0x2FEF),
(0x3001, 0xD7FF),
(0xF900, 0xFDCF),
(0xFDF0, 0xFFFD),
# (0x10000, 0xEFFFF), while actually valid, these are not yet accepted by makeKeyTokens_()
]
for r in charRanges:
for c in range(r[0], r[1]):
validStartChars += chr(c)
# generate the characters that are only allowed inside a token, not at the start
validInlineChars = "-.\xB7"
charRanges = [
(ord("0"), ord("9")),
(0x0300, 0x036F),
(0x203F, 0x2040),
]
for r in charRanges:
for c in range(r[0], r[1]):
validInlineChars += chr(c)
# test forbidden start characters
for c in invalidChars + validInlineChars:
invalidTokens.append(c + validBase)
# test forbidden inline characters
for c in invalidChars:
invalidTokens.append(validBase[:4] + c + validBase[4:])
# test each allowed start character
for c in validStartChars:
validTokens.append(c + validBase)
# test each allowed inline character
for c in validInlineChars:
validTokens.append(validBase[:4] + c + validBase[4:])
logger = QgsApplication.messageLog()
logger.messageReceived.connect(self.catchMessage)
prj = QgsProject.instance()
for token in validTokens:
self.messageCaught = False
prj.readEntry("test", token)
myMessage = f"valid token '{token}' not accepted"
assert not self.messageCaught, myMessage
for token in invalidTokens:
self.messageCaught = False
prj.readEntry("test", token)
myMessage = f"invalid token '{token}' accepted"
assert self.messageCaught, myMessage
logger.messageReceived.disconnect(self.catchMessage)
def catchMessage(self):
self.messageCaught = True