qgis / QGIS

QGIS is a free, open source, cross platform (lin/win/mac) geographical information system (GIS)
https://qgis.org
GNU General Public License v2.0
10.13k stars 2.93k forks source link

Custom processing algorithm crashes when dynamically updating items in a layout legend #56074

Open matster1994 opened 6 months ago

matster1994 commented 6 months ago

What is the bug or the crash?

User Feedback

Within a custom processing algorithm script, each iteration in the loop will update the visible content of the print layout, update the legend content and export the layout to PDF. Everything works except for the legend updating. I can execute the exact same lines in the QGIS python console but when it's run from my script, this crash happens. This script should take the visible layers in the main canvas window and add those to an existing legend box in the layout.

Here is the function to create the legend:

def updateLegendItems(): iface.mapCanvas().refresh() legend_tree = QgsLayerTree() for layer in iface.mapCanvas().layers(): if layer.providerType() != 'wms': legend_tree.addLayer(layer) return legend_tree

layout = QgsProject.instance().layoutManager().layoutByName("<MY_MAP_LAYOUT") legend = layout.itemById('legend')

exporter = QgsLayoutExporter(self.layout) pdf_export_settings = QgsLayoutExporter.PdfExportSettings()

This is the line that seems to cause issues

legend.model().setRootGroup(updateLegendItems())

exporter.exportToPdf(output_file_path, pdf_export_settings)

Report Details

Python Stack Trace

Windows fatal exception: access violation

Current thread 0x00004f98 (most recent call first):

Thread 0x00002338 (most recent call first):
<no Python frame>

Stack Trace


pdal::Utils::split<<lambda_af3baea9e60965f7a4bf52f259885e1c> > :
QgsLegendRenderer::createComponentGroupList :
QgsLegendRenderer::paintAndDetermineSize :
QgsLegendRenderer::minimumSize :
QgsLayoutItemLegend::paint :
QGraphicsScene::dragMoveEvent :
QGraphicsItem::dragMoveEvent :
QGraphicsEffectSource::draw :
QgsLayoutEffect::draw :
QGraphicsScene::drawItems :
QGraphicsScene::drawItems :
QGraphicsScene::render :
QgsLayoutExporter::renderRegion :
QgsLayoutExporter::renderPage :
QgsLayoutExporter::printPrivate :
QgsLayoutExporter::exportToPdf :
pdal::MetadataNode::value :
PyArg_ParseTuple_SizeT :
PyEval_EvalFrameDefault :
PyFunction_Vectorcall :
PyFloat_FromDouble :
PyVectorcall_Call :
PyObject_Call :
pdal::StreamPointTable::finalize :
pdal::Option::getName :
QgsProcessingAlgorithm::runPrepared :
QgsProcessingAlgRunnerTask::run :
pdal::Option::getName :
QgsTask::start :
QThreadPoolPrivate::reset :
QThread::start :
BaseThreadInitThunk :
RtlUserThreadStart :

QGIS Info QGIS Version: 3.32.2-Lima QGIS code revision: c0b8833964 Compiled against Qt: 5.15.3 Running against Qt: 5.15.3 Compiled against GDAL: 3.7.1 Running against GDAL: 3.7.1

System Info CPU Type: x86_64 Kernel Type: winnt Kernel Version: 10.0.22631

Steps to reproduce the issue

The above contains the code snippets. This executed by running my script from the processing toolbox --> open existing script.

Versions

3.32.2

Supported QGIS version

New profile

Additional context

No response

matster1994 commented 5 months ago

UPDATE:

Happens every time I try to update a legend item in a layout to display only specific layers in the legend. I've tried varies different ways of editing the legend: creating a brand new legend item for each PDF export, setting a new rootGroup with legend.model().setRootGroup(). Any time I work with the legend in PyQGIS, I get different crashes. Below is another one produced when trying to execute the following code within my custom processingAlgorithm. Because this is a processingAlgorithm, all of my code is stored within a class, hence the references to "self".

def updateLegendItems(self): iface.mapCanvas().refresh() legend_tree = QgsLayerTree() for layer in iface.mapCanvas().layers(): if layer.providerType() != 'wms': legend_tree.addLayer(layer)

return legend_tree

legend_tree = self.updateLegendItems() legend.model().setRootGroup(legend_tree) self.layout.refresh() legend.updateLegend() legend.refresh()

Report Details

Python Stack Trace

Windows fatal exception: access violation

Current thread 0x000012fc (most recent call first):

Thread 0x00002dfc (most recent call first):
<no Python frame>

Stack Trace


QgsLayerTreeModel::disconnectFromLayer :
QgsLayerTreeModel::disconnectFromLayers :
QgsLayerTreeModel::setRootGroup :
pdal::MetadataNode::value :
PyObject_Str :
PyEval_EvalFrameDefault :
PyFunction_Vectorcall :
PyObject_GC_Del :
PyVectorcall_Call :
PyObject_Call :
pdal::PointContainer::freeTemp :
pdal::Option::getName :
QgsProcessingAlgorithm::runPrepared :
QgsProcessingAlgRunnerTask::run :
pdal::Option::getName :
QgsTask::start :
QThreadPoolPrivate::reset :
QThread::start :
BaseThreadInitThunk :
RtlUserThreadStart :

QGIS Info QGIS Version: 3.34.3-Prizren QGIS code revision: 47373234ac Compiled against Qt: 5.15.3 Running against Qt: 5.15.3 Compiled against GDAL: 3.8.3 Running against GDAL: 3.8.3

System Info CPU Type: x86_64 Kernel Type: winnt Kernel Version: 10.0.22631

benwirf commented 5 months ago

Hi @matster1994, If you are calling that method or running that code block inside the processAlgorithm() method of your custom processing algorithm, crashes would not be unexpected. Because processing algorithms are run by default in a separate thread, interacting with or modifying GUI elements which live in the main thread (canvas, layer tree etc) can certainly cause crashes or, at the least, unpredictable behaviour.

See: The Tip about algorithm flags in the docs

Try overriding the flags() method and returning the NoThreading flag.

    def flags(self):
        return super().flags() | QgsProcessingAlgorithm.FlagNoThreading
matster1994 commented 5 months ago

Hey Ben,

Thanks for replying to my report! I will try your suggested solution and let you know if it solves the problem.

Cheers, Matthew Pepper - RF Coverage SME & Traffic SME T8 Presales *Email: @. @.> Phone: (312) 909-6270*

On Sun, Feb 11, 2024 at 10:31 PM Ben Wirf @.***> wrote:

Hi @matster1994 https://urldefense.proofpoint.com/v2/url?u=https-3A__github.com_matster1994&d=DwMCaQ&c=q3cDpHe1hF8lXU5EFjNM_C93KOmcBXCBnhee2v6PYlc&r=fH_huYL_IcgKgkd27_hK8PKX3vTmrT7tjX9A7b9JcCo2QYZ9qRop4YvhJF0v9eY1&m=Etv8wiBi1MA7tn0AqcLJB9k0NihRXaw_PYCjS8rv_lV-QBoFswNC5KTd-xeXZgw5&s=aUhq6j-dJfxWLowA2BLXQi4-ViVGnf91liqlQc5cXFU&e= , If you are calling that method or running that code block inside the processAlgorithm() method of your custom processing algorithm, crashes would not be unexpected. Because processing algorithms are run by default in a separate thread, interacting with or modifying GUI elements (canvas, layer tree etc) can certainly cause crashes or, at the least, unpredictable behavior.

See: The Tip about algorithm flags in the docs https://urldefense.proofpoint.com/v2/url?u=https-3A__docs.qgis.org_3.28_en_docs_user-5Fmanual_processing_scripts.html-23flags&d=DwMCaQ&c=q3cDpHe1hF8lXU5EFjNM_C93KOmcBXCBnhee2v6PYlc&r=fH_huYL_IcgKgkd27_hK8PKX3vTmrT7tjX9A7b9JcCo2QYZ9qRop4YvhJF0v9eY1&m=Etv8wiBi1MA7tn0AqcLJB9k0NihRXaw_PYCjS8rv_lV-QBoFswNC5KTd-xeXZgw5&s=yI5tiEpYbtVqhE6RmrdzkN7uF8KF9FB68YZmbU9YVhc&e=

Try overriding the flags() https://urldefense.proofpoint.com/v2/url?u=https-3A__api.qgis.org_api_classQgsProcessingAlgorithm.html-23a3f052a9a0f0ee438afdf6eacb48795cb&d=DwMCaQ&c=q3cDpHe1hF8lXU5EFjNM_C93KOmcBXCBnhee2v6PYlc&r=fH_huYL_IcgKgkd27_hK8PKX3vTmrT7tjX9A7b9JcCo2QYZ9qRop4YvhJF0v9eY1&m=Etv8wiBi1MA7tn0AqcLJB9k0NihRXaw_PYCjS8rv_lV-QBoFswNC5KTd-xeXZgw5&s=5igQMuTV9j9v-eihXEmmORrV3UWNiBGzKLNj-ArNJ80&e= method and returning the NoThreading flag.

def flags(self):
    return super().flags() | QgsProcessingAlgorithm.FlagNoThreading

— Reply to this email directly, view it on GitHub https://urldefense.proofpoint.com/v2/url?u=https-3A__github.com_qgis_QGIS_issues_56074-23issuecomment-2D1938123723&d=DwMCaQ&c=q3cDpHe1hF8lXU5EFjNM_C93KOmcBXCBnhee2v6PYlc&r=fH_huYL_IcgKgkd27_hK8PKX3vTmrT7tjX9A7b9JcCo2QYZ9qRop4YvhJF0v9eY1&m=Etv8wiBi1MA7tn0AqcLJB9k0NihRXaw_PYCjS8rv_lV-QBoFswNC5KTd-xeXZgw5&s=EXllfNRaHzne32Ffrj9onW8glN79NnSIEPUxGcOdMeo&e=, or unsubscribe https://urldefense.proofpoint.com/v2/url?u=https-3A__github.com_notifications_unsubscribe-2Dauth_A5SXUO7I62KQZGTPB6WW4LDYTGZK7AVCNFSM6AAAAABCQFI5PWVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMYTSMZYGEZDGNZSGM&d=DwMCaQ&c=q3cDpHe1hF8lXU5EFjNM_C93KOmcBXCBnhee2v6PYlc&r=fH_huYL_IcgKgkd27_hK8PKX3vTmrT7tjX9A7b9JcCo2QYZ9qRop4YvhJF0v9eY1&m=Etv8wiBi1MA7tn0AqcLJB9k0NihRXaw_PYCjS8rv_lV-QBoFswNC5KTd-xeXZgw5&s=T2NxWGNqpV7hWpmZ_1eaLY1pWfLHU_j0V5D1jvN1yeI&e= . You are receiving this because you were mentioned.Message ID: @.***>

--

For more information on how and why we collect your personal information, please visit our Privacy Policy https://www.motorolasolutions.com/en_us/about/privacy-policy.html?elqTrackId=8980d888905940e39a2613a7a3dcb0a7&elqaid=2786&elqat=2#privacystatement.

matster1994 commented 5 months ago

Hi Ben, I finally got around to trying your suggestion. I added the flags() function to my processingAlgorithm class definition but I still get the same crash errors. I'm a bit new to the QGIS API, so maybe there's something I'm missing here with setting the flags? Do I need to call self.flags() somewhere?