7

I'm trying to change the style of all my DXF layers using the QgsProject layersAdded signal while adding the layers to QGIS 3.28.1 (Windows).

For this purpose I wrote some lines of Python macro code and inserted it into one of my projects:

from qgis.utils import iface
from qgis.core import QgsProject

def loadDxfStyle(layers):
    style = 'C:/OSGeo4W/styles/dxf.qml'
    for lay in layers:
        if '.dxf' in lay.source():
            print(lay.source())
            lay.loadNamedStyle(style)
            lay.triggerRepaint()
            iface.layerTreeView().refreshLayerSymbology(lay.id())

def openProject():
    QgsProject.instance().layersAdded.connect(loadDxfStyle)


def saveProject():
    pass

def closeProject():
    QgsProject.instance().layersAdded.disconnect(loadDxfStyle)

The layer sources are always printed to the console, but the style is never changed.

What am I doing wrong here?


UPDATE: I changed the code to use the processing algorithm native:setlayerstyle, but this doesn't change anything. When I pause the script after setting the style, I can see that the style was changed. But after the timer ends the style changes are automatically overwritten.

from qgis.utils import iface
from qgis.core import QgsProject
from qgis.PyQt.QtCore import QTimer,QEventLoop
import processing

def wait():
    loop = QEventLoop()
    QTimer.singleShot(5000,loop.quit)
    loop.exec_()

def loadDxfStyle(layers):
    style = 'C:/OSGeo4W/styles/dxf.qml'
    for lay in layers:
        if '.dxf' in lay.source():
            processing.run("native:setlayerstyle",{"INPUT":lay.id(),"STYLE":style})
            wait()

def openProject():
    QgsProject.instance().layersAdded.connect(loadDxfStyle)


def saveProject():
    pass

def closeProject():
    QgsProject.instance().layersAdded.disconnect(loadDxfStyle)

Is it perhaps possible to run the processing alg in another thread using asyncio after the layer import is finished?

4
  • My code works perfectly with GEOJSON data sources, but unfortunately not with DXF. QGIS uses "Embedded Symbols" as default style for DXF layers. Perhaps that is the reason.
    – christoph
    Commented Dec 4, 2022 at 18:31
  • It looks like you are not saving the project file at the end of the script. See this: qgis.org/pyqgis/master/core/… Commented Jan 27, 2023 at 9:15
  • @ComradeChe good idea, but what if the file does not exist yet. I guess QGIS uses a special event routine with DXF files, that removes my style changes after the macro code was called.
    – christoph
    Commented Jan 27, 2023 at 9:38
  • The layersAdded signal is a bit weird as it triggers not really after the layers are "fully" added to QGIS but at some earlier stage. Commented Jan 29, 2023 at 20:31

1 Answer 1

4
+200

This feels a bit like a poor workaround but what you can do is use a single-shot timer to schedule the style update with a slight delay. In my tests, values as small as 100ms work fine and are barely noticeable.

from qgis.utils import iface
from qgis.core import QgsProject
from qgis.PyQt.QtCore import QTimer

def loadDxfStyle(layers):
    for lay in layers:
        if '.dxf' in lay.source():
            print(lay.source())
            QTimer.singleShot(100, lambda: applyStyle(lay))
            
def applyStyle(layer):
    style = 'C:/OSGeo4W/styles/dxf.qml'
    print('applying style')
    layer.loadNamedStyle(style)
    layer.triggerRepaint()
    iface.layerTreeView().refreshLayerSymbology(layer.id())

def openProject():
    QgsProject.instance().layersAdded.connect(loadDxfStyle)


def saveProject():
    pass

def closeProject():
    QgsProject.instance().layersAdded.disconnect(loadDxfStyle)

And now an attempt to explain this issue:

It seems that the layersAdded signal is emitted, before the initial default style is loaded. Therefore, changing the style in the slot function will immediately be overridden by the default style again.

In the source code we can find that loadDefaultStyle is called in a function named postProcessAddedLayer which itself is called on each added layer here. That is about 10 lines of code after the call to QgsProject::instance()->addMapLayers( layersToAdd ) which emits the layersAdded signal.

1
  • thanks a lot, it's definitely what I was looking for... works like a charm!
    – christoph
    Commented Jan 31, 2023 at 11:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.