-
-
Notifications
You must be signed in to change notification settings - Fork 3.4k
Description
What is the bug or the crash?
The QgsFieldCalculator uses an expression context based on QgsProject.instance(). If the QgsVectorLayer to calculate field values for belongs to another QgsProject, the QgsFieldCalculator will show a wrong project context scope (see example below).
Using different QgsProject instances eases the handling of different layer sets, e.g. to focus on layers which are relevant for a plugin application, instead of showing all layers existing in a QgsProject.instance().
Classes like QgsMapLayerCombox or QgsVectorLayerTools implement ::setProject(QgsProject * project) which allows to benefit from specialized QgsProject instances.
This behaviour is caused by:
QgsFieldCalculator::QgsFieldCalculator( QgsVectorLayer *vl, QWidget *parent )
...
QgsExpressionContext expContext( QgsExpressionContextUtils::globalProjectLayerScopes( mVectorLayer ) );
...and:
QList<QgsExpressionContextScope *> QgsExpressionContextUtils::globalProjectLayerScopes( const QgsMapLayer *layer )
{
QList<QgsExpressionContextScope *> scopes;
scopes << globalScope();
QgsProject *project = QgsProject::instance(); // TODO: use project associated with layer skip-keyword-check
if ( project )
scopes << projectScope( project );
if ( layer )
scopes << layerScope( layer );
return scopes;
}
Not being a C++ developer, I recommend to replace the //TODO line
with:
QgsProject *project = layer && layer->project() ? layer->project() : QgsProject::instance();Steps to reproduce the issue
In the following example I may want to use layers from QgsProject pB, but the QgsFieldCalculator lists only map layer from QgsProject.instance().
from qgis.core import QgsProject, QgsVectorLayer
from qgis.gui import QgsFieldCalculator
from qgis.testing import start_app
start_app()
pA = QgsProject.instance()
pB = QgsProject()
lyrA = QgsVectorLayer('Point?crs=epsg:4326&field=foo:string(20)', 'A', 'memory')
lyrB1 = QgsVectorLayer('Point?crs=epsg:4326&field=bar:string(20)', 'B1', 'memory')
lyrB2 = QgsVectorLayer('Point?crs=epsg:4326&field=bar:string(20)', 'B2', 'memory')
pA.addMapLayer(lyrA)
pB.addMapLayers([lyrB1, lyrB2])
assert pA != pB
assert lyrA.project() == pA
assert lyrB1.project() == pB
assert lyrB2.project() == pB
calc = QgsFieldCalculator(lyrB1)
calc.exec_()
Versions
| QGIS version | 3.44.7-Solothurn |
| QGIS code branch | Release 3.44 |
| Libraries | |
| Qt version | 5.15.15 |
| Python version | 3.13.11 |
| GDAL version | 3.12.0 (Compiled) 3.12.1 (Running) — Chicoutimi |
| PROJ version | 9.7.1 |
| EPSG Registry database version | v12.029 (2025-10-03) |
| GEOS version | 3.14.1-CAPI-1.20.5 |
| SQLite version | 3.51.2 |
| PDAL version | 2.9.2 (Compiled) 2.9.3 (Running) |
| PostgreSQL client version | 18.1 |
| SpatiaLite version | 5.1.0 |
| QWT version | 6.2.0 |
| QScintilla2 version | 2.14.1 |
| OS version | Fedora Linux 43 (Workstation Edition) |
| Active Python plugins | |
| processing | 2.12.99 |
Supported QGIS version
- I'm running a supported QGIS version according to the roadmap.
New profile
- I tried with a new QGIS profile
Additional context
No response