[QGIS-Developer] Difficulties with custom bad layer handler in plugin code

Nyall Dawson nyall.dawson at gmail.com
Sun Jul 21 16:11:39 PDT 2019


On Sun, 21 Jul 2019 at 21:16, Áron Gergely <aron.gergely at rasterra.nl> wrote:

> This actually works, for the 1st time when I open a project file with bad layers. Then the 2nd time, QGIS always crashes.
> It seems once I replaced the bad layer handler during project start, QGIS is not able to open a subsequent new project properly.

Couldn't you achieve the same result by setting your bad layer handler
directly in initGui?

># TODO: after this, call the GUI file dialog as in the default bad layer handler, to allow the user to fix
># any other broken layers... not sure how to do this

The bad news is -- unfortunately you cannot. The inbuilt bad layer
handler isn't exposed to Python in any way, and as soon as you set a
new bad layer
handler method the existing inbuilt one is completely deleted.

I've been thinking about a similar situation recently, where a
client's IT department is continually replacing network paths and
moving things around (they do it roughly every 6 months, with no
notice in advance. yep.) This breaks many many paths in their qgis
projects, but the fix is actually super simple since we know what the
network path used to be and what it is now. So what I've been
pondering is whether we need a new hook to allow a "data source
pre-processor" function to be specified, much in the same way that
custom bad layer handlers would be. This would be called BEFORE
resolving any raw paths, and would simply take an input data source
string and return a processed version of it. (e.g. by replacing
outdated network paths with their new equivalent). In this way it
would be transparent to users -- the paths would be automatically
updated, and then they'd only see the bad layer handler dialog IF an
updated path still doesn't exist.

Does this sound like what you're seeking?

Nyall



>
> Here is my example in code:
>
> class MyBadLayerHandler(QgsProjectBadLayerHandler):
>     """
>     This is a custom bad layer handler that would work as the default one, except it would automatically fix
>     a specific layer that is broken.
>     """
>
>     def __init__(self, path):
>         QgsProjectBadLayerHandler.__init__(self)
>         self.validLayerPath = path
>
>     def handleBadLayers(self, domNodes):
>         # Some logic to look for a specific DomNode to fix, oversimplified here:
>         for dom in domNodes:
>             dataSource = self.dataSource(dom)
>
>             if dataSource is 'the broken path to the layer I want to fix':
>                 # set correct data source then force re-read
>                 self.setDataSource(domNodes, self.validLayerPath)
>                 QgsProject.instance().readLayer(dom)
>
>         # TODO: after this, call the GUI file dialog as in the default bad layer handler, to allow the user to fix
>         # any other broken layers... not sure how to do this
>
>
> class MyPlugin:
>     """My plugin"""
>
>     def __init__(self):
>         self.validPath = 'valid path to layer'
>         self.badLayerHandler = None
>
>     def hackyAndUglyReplacer(self, i, n):
>         """
>         This hacky ugly function is to replace the bad layer handler early on, before any layers would be loaded.
>         it is meant to be connected to the `layerLoaded` signal of `QgsProject`
>         """
>         # do not run further if there were other layers loaded before (e.g. the signal was emitted before)
>         if i != 0:
>             return
>
>         if not self.badLayerHandler:
>             self.badLayerHandler = MyBadLayerHandler(self.validPath)
>             QgsProject.instance().setBadLayerHandler(self.badLayerHandler)
>
>     def initGui(self):
>         # start-up code here...
>
>         #connect to signal
>         QgsProject.instance().layerLoaded.connect(self.hackyAndUglyReplacer)
>
>     def unload(self):
>         try:
>             QgsProject.instance().layerLoaded.disconnect()
>         except Exception:
>             pass
>
>
> Does anyone know what am I doing wrong here? Have I missed something?
> Why does QGIS crash every 2nd time?
>
> I would be happy to pass the bad layer handler a better way than with the current signal above. But the other signals I found are emitted 'too late' already.
> Perhaps here I am also doing something wrong?
>
> I tried looking for help but the web barely has any trace of documentation on this.
>
> I also tried to see how this is done in other plugins e.g. the changeDataSource plugin, but the authors  seem to have removed the code when they ported the plugin to QGIS 3.x
> (perhaps a bad sign)
>
> In general it seems to me this part was also overhauled in QGIS3 but there aren't many leads to follow on what is the current way of using custom bad layer handlers.
>
> Maybe if we could put the story together on how to do this correctly, I could document it and put it on the web for others to refer to.
>
> Hope you are having / had a great Sunday!
>
> Best regards,
> Aron
>
> _______________________________________________
> QGIS-Developer mailing list
> QGIS-Developer at lists.osgeo.org
> List info: https://lists.osgeo.org/mailman/listinfo/qgis-developer
> Unsubscribe: https://lists.osgeo.org/mailman/listinfo/qgis-developer


More information about the QGIS-Developer mailing list