On Tue, 23 Jul 2019 at 00:58, Áron Gergely <aron.gerg...@rasterra.nl> wrote: > > > > On 22/07/2019 01:11, Nyall Dawson wrote: > > On Sun, 21 Jul 2019 at 21:16, Áron Gergely<aron.gerg...@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? > 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.
Ehhhh- right. The built-in handler is set AFTER plugins are loaded, so it wipes out any custom handler implemented by a plugin! I think this is a bug -- as it makes that API call rather useless. PR inbound. > 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! Well, it's possible we could work around this by NOT deleting the inbuilt one when a new one is set. But honestly, I think your use case is better server by my proposed API addition. > 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? ) I suspect lots of people. Personally, as a user, everytime I see that dialog popup a little bit dies inside of me... > Although my problem (or rather, our problem with Raymond) is similar but > not exactly the same: Nope - it IS the same ;) You can see it in https://github.com/qgis/QGIS/pull/30842 > 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. So what you would do here is implement a custom pre-processor which detects some hardcoded path to the plugin resource, and swaps it out with the actual path to the python plugin. E.g. def my_processor(path): if path.endswith('my_plugins_super_north_arrow.svg'): return os.path.join(os.path.dirname(__file__), 'resources/my_plugins_super_north_arrow.svg') return path QgsPathResolver.setPathPreprocessor(my_processor) Basically - whenever a path ending in 'my_plugins_super_north_arrow.svg' is requested, it gets (silently) modified to point to a subfolder off your plugin's actual runtime location. You'd just need to make sure the intercepted resources have a distinct enough name to not clash with other (non-plugin) paths! (e.g. don't just check for "north_arrow.svg"!). Nyall > 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 > >> QGIS-Developer@lists.osgeo.org > >> List info:https://lists.osgeo.org/mailman/listinfo/qgis-developer > >> Unsubscribe:https://lists.osgeo.org/mailman/listinfo/qgis-developer > _______________________________________________ QGIS-Developer mailing list QGIS-Developer@lists.osgeo.org List info: https://lists.osgeo.org/mailman/listinfo/qgis-developer Unsubscribe: https://lists.osgeo.org/mailman/listinfo/qgis-developer