Skip to content

QgsFieldCalculator misses project layers / shows wrong layer context #64969

@jakimowb

Description

@jakimowb

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_()
Image

Versions

QGIS version3.44.7-Solothurn
QGIS code branchRelease 3.44
 
Libraries
Qt version5.15.15
Python version3.13.11
GDAL version3.12.0 (Compiled)
3.12.1 (Running) — Chicoutimi
PROJ version9.7.1
EPSG Registry database versionv12.029 (2025-10-03)
GEOS version3.14.1-CAPI-1.20.5
SQLite version3.51.2
PDAL version2.9.2 (Compiled)
2.9.3 (Running)
PostgreSQL client version18.1
SpatiaLite version5.1.0
QWT version6.2.0
QScintilla2 version2.14.1
OS versionFedora Linux 43 (Workstation Edition)
 
Active Python plugins
processing2.12.99

Supported QGIS version

  • I'm running a supported QGIS version according to the roadmap.

New profile

Additional context

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    BugEither a bug report, or a bug fix. Let's hope for the latter!

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions