<html>
  <head>

    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
  </head>
  <body text="#000000" bgcolor="#FFFFFF">
    Hello to all contributors,<br>
    <br>
    I am trying to implement a custom bad layer handler that I would
    ship with a plugin (for QGIS 3.4 and above).<br>
    <br>
    The plugin creates map layouts and places a default world map layer
    in them, which we ship with the plugin.<br>
    However when users shave the project file and share it among each
    other, many find this layer to be broken.<br>
    The layer points to a data in the plugin path, which is not the same
    across machines. <br>
    <br>
    The custom bad layer handler would try to see if this specific layer
    is broken and fix it for the user,<br>
    then call the regular bad layer handling "file dialog" to allow them
    to fix any eventual broken layers that are unrelated to the plugin.
    <br>
    <br>
    I am creating my bad layer handler by subclassing
    QgsProjectBadLayerHandler,<br>
    thin I pass it to QgsProject by calling its setBadLayerHandler()
    method.<br>
    Since I want to pass it before any layer is actually loaded (so when
    launching qgis to open a project file, it would work right away) <br>
    I had to resort to a sort of ugly way of using the
    QgsProject.layerLoaded signal to make this happen.<br>
    <br>
    This actually works, for the 1st time when I open a project file
    with bad layers. Then the 2nd time, QGIS always crashes.<br>
    It seems once I replaced the bad layer handler during project start,
    QGIS is not able to open a subsequent new project properly.<br>
    <br>
    Here is my example in code:<br>
    <pre style="background-color:#2b2b2b;color:#a9b7c6;font-family:'DejaVu Sans Mono';font-size:9.0pt;"><span style="color:#cc7832;">class </span>MyBadLayerHandler(QgsProjectBadLayerHandler):
    <span style="color:#629755;font-style:italic;">"""
</span><span style="color:#629755;font-style:italic;">    This is a custom bad layer handler that would work as the default one, except it would automatically fix
</span><span style="color:#629755;font-style:italic;">    a specific layer that is broken.
</span><span style="color:#629755;font-style:italic;">    """
</span><span style="color:#629755;font-style:italic;">
</span><span style="color:#629755;font-style:italic;">    </span><span style="color:#cc7832;">def </span><span style="color:#b200b2;">__init__</span>(<span style="color:#94558d;">self</span><span style="color:#cc7832;">, </span>path):
        QgsProjectBadLayerHandler.<span style="color:#b200b2;">__init__</span>(<span style="color:#94558d;">self</span>)
        <span style="color:#94558d;">self</span>.validLayerPath = path

    <span style="color:#cc7832;">def </span><span style="color:#ffc66d;">handleBadLayers</span>(<span style="color:#94558d;">self</span><span style="color:#cc7832;">, </span>domNodes):
        <span style="color:#808080;"># Some logic to look for a specific DomNode to fix, oversimplified here:
</span><span style="color:#808080;">        </span><span style="color:#cc7832;">for </span>dom <span style="color:#cc7832;">in </span>domNodes:
            dataSource = <span style="color:#94558d;">self</span>.dataSource(dom)

            <span style="color:#cc7832;">if </span>dataSource <span style="color:#cc7832;">is </span><span style="color:#6a8759;">'the broken path to the layer I want to fix'</span>:
                <span style="color:#808080;"># set correct data source then force re-read
</span><span style="color:#808080;">                </span><span style="color:#94558d;">self</span>.setDataSource(domNodes<span style="color:#cc7832;">, </span><span style="color:#94558d;">self</span>.validLayerPath)
                QgsProject.instance().readLayer(dom)

        <span style="color:#808080;"># TODO: after this, call the GUI file dialog as in the default bad layer handler, to allow the user to fix
</span><span style="color:#808080;">        # any other broken layers... not sure how to do this
</span><span style="color:#808080;">
</span><span style="color:#808080;">
</span><span style="color:#cc7832;">class </span>MyPlugin:
    <span style="color:#629755;font-style:italic;">"""My plugin"""
</span><span style="color:#629755;font-style:italic;">
</span><span style="color:#629755;font-style:italic;">    </span><span style="color:#cc7832;">def </span><span style="color:#b200b2;">__init__</span>(<span style="color:#94558d;">self</span>):
        <span style="color:#94558d;">self</span>.validPath = <span style="color:#6a8759;">'valid path to layer'
</span><span style="color:#6a8759;">        </span><span style="color:#94558d;">self</span>.badLayerHandler = <span style="color:#cc7832;">None
</span><span style="color:#cc7832;">
</span><span style="color:#cc7832;">    def </span><span style="color:#ffc66d;">hackyAndUglyReplacer</span>(<span style="color:#94558d;">self</span><span style="color:#cc7832;">, </span>i<span style="color:#cc7832;">, </span><span style="color:#808080;">n</span>):
        <span style="color:#629755;font-style:italic;">"""
</span><span style="color:#629755;font-style:italic;">        This hacky ugly function is to replace the bad layer handler early on, before any layers would be loaded.
</span><span style="color:#629755;font-style:italic;">        it is meant to be connected to the `layerLoaded` signal of `QgsProject`
</span><span style="color:#629755;font-style:italic;">        """
</span><span style="color:#629755;font-style:italic;">        </span><span style="color:#808080;"># do not run further if there were other layers loaded before (e.g. the signal was emitted before)
</span><span style="color:#808080;">        </span><span style="color:#cc7832;">if </span>i != <span style="color:#6897bb;">0</span>:
            <span style="color:#cc7832;">return
</span><span style="color:#cc7832;">
</span><span style="color:#cc7832;">        if not </span><span style="color:#94558d;">self</span>.badLayerHandler:
            <span style="color:#94558d;">self</span>.badLayerHandler = MyBadLayerHandler(<span style="color:#94558d;">self</span>.validPath)
            QgsProject.instance().setBadLayerHandler(<span style="color:#94558d;">self</span>.badLayerHandler)

    <span style="color:#cc7832;">def </span><span style="color:#ffc66d;">initGui</span>(<span style="color:#94558d;">self</span>):
        <span style="color:#808080;"># start-up code here...
</span><span style="color:#808080;">
</span><span style="color:#808080;">        #connect to signal
</span><span style="color:#808080;">        </span>QgsProject.instance().layerLoaded.connect(<span style="color:#94558d;">self</span>.hackyAndUglyReplacer)

    <span style="color:#cc7832;">def </span><span style="color:#ffc66d;">unload</span>(<span style="color:#94558d;">self</span>):
        <span style="color:#cc7832;">try</span>:
            QgsProject.instance().layerLoaded.disconnect()
        <span style="color:#cc7832;">except </span><span style="color:#8888c6;">Exception</span>:
            <span style="color:#cc7832;">pass</span></pre>
    <br>
    Does anyone know what am I doing wrong here? Have I missed
    something?<br>
    Why does QGIS crash every 2nd time? <br>
    <br>
    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. <br>
    Perhaps here I am also doing something wrong?<br>
    <br>
    I tried looking for help but the web barely has any trace of
    documentation on this.<br>
    <br>
    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<br>
    (perhaps a bad sign)<br>
    <br>
    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.<br>
    <br>
    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.<br>
    <br>
    Hope you are having / had a great Sunday!<br>
    <br>
    Best regards,<br>
    Aron<br>
    <br>
  </body>
</html>