On 22/07/2019 01:11, Nyall Dawson wrote:
On Sun, 21 Jul 2019 at 21:16, Áron Gergely<[email protected]>  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?
No, for some strange reason the bad layer handler would not get assigned when I tried within initGui. I tried to find out why by looking at debug messages on qgis launch, reading the source code, but it is a lot to take in with my rusty C skills so I have not found the answer.

But I reckoned it may be due to the plugin being loaded before a QgsProject singleton would be initialized.
# 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.
Understood - if that part is not exposed to the python API then I will not pursue the above. Thanks for letting me know!
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

Thanks for your insight Nyall!
Sounds bad with that IT Department. :(

But yes, I think that would be helpful. (I wonder who else hit a wall with the broken paths? ) If there would be a way to fix broken paths in python on project init, I would use it now to improve on our plugin.

Although my problem (or rather, our problem with Raymond) is similar but not exactly the same:

We created a plugin that helps to achieve consistent map layouts: the plugin itself ships with the correct resources (logos, symbols, and a world map layer) for specific map layouting tasks and has a simple workflow via dialogs that creates the right layouts which the users would fill in or adapt to meet their needs.

The problem is that since we ship the resources with the plugin, the resource paths are on the plugin path which is different across machines. So when users create map layouts, save the project file and pass it to another user to work on they see missing items and broken layer path, even though they have the plugin installed.

On the longer term we believe it would be good to have a resource sharing system that is native to QGIS. That would make it very easy to share resources for plugins as well, without needing to instruct the users to get the resource sharing plugin, connect to resource repo X and pull resources a, b, c,... and so on.

I had discussions about this with Raymond and we are looking to write a QEP about 'native' resource sharing.

We might as well be able to live without a solution for now, as native resource sharing would answer our needs best.

Best regards,
Aron
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
[email protected]
List info:https://lists.osgeo.org/mailman/listinfo/qgis-developer
Unsubscribe:https://lists.osgeo.org/mailman/listinfo/qgis-developer

_______________________________________________
QGIS-Developer mailing list
[email protected]
List info: https://lists.osgeo.org/mailman/listinfo/qgis-developer
Unsubscribe: https://lists.osgeo.org/mailman/listinfo/qgis-developer

Reply via email to