QGIS style dock – Part 2 – Plugin panels


In part 1 I talked about the new style dock and the in built functions it has in this post I would like to cover how you can extend that dock using plugin panels.

While building the dock it became evident that being able to add your own panels with be super neat, why lock the user to what styling controls we have built into core.

After one, or two, API iterations it is now possible to add your own panels to the style dock.

First we need to make a new widget using QgsLayerStylingPanel

class CustomPanel(QgsLayerStylingPanel):
    def __init__(self, layer, canvas, parent):
        super(CustomPanel, self).__init__(layer, canvas, parent)
        self.layer = layer

    def apply(self):
        pass

then we need to create a factory object (QgsLayerStylingPanelFactory) which will hold the metadata for the panel widget, things like icon, title, and the widget it self as required. The factory object looks like this:

class PanelFactory(QgsLayerStylingPanelFactory):
    def icon(self):
        return QIcon(r"F:\icons\SVG\book.svg")

    def title(self):
        return ""

    def supportsLayer( self, layer):
        return layer.type() == QgsMapLayer.VectorLayer

    def createPanel(self, layer, canvas, parent):
        return CustomPanel(layer, canvas, parent)

# Also make a instance and tell the style dock about it
# make sure you keep a instance of the the factory around
factory = PanelFactory()
iface.registerMapStylePanelFactory(factory)

You can also see we have a supportsLayer() method. This must return True if the layer can be supported for the widget, if the layer isn’t support it’s not shown in the layer style dock. We also have the createPanel method which just returns the widget, you could also use this method to return different widgets based on layer type if you needed. This createPanel method will be called any time the item is selected in the left panel of the style dock. Using the above code we will see a new icon on the side panel

style

Remember, the create method is called any time this new item is selected.    If your widget is expensive to create you can create a cache here.

Now lets add some logic.  As an example I’m going to load the style xml and show it in the panel. Adding some logic to the __init__ method of the CustomPanel class

class CustomPanel(QgsLayerStylingPanel):
    def __init__(self, layer, canvas, parent):
        super(CustomPanel, self).__init__(layer, canvas, parent)
        self.layer = layer
        self.setLayout(QVBoxLayout())

        # Create the editor and set the xml from the layer.
        self.editor = QgsCodeEditorHTML()
        self.editor.setLexer(QsciLexerXML())
        doc = QDomDocument( "style" ) 
        rootNode = doc.createElement( "qgis" )
        doc.appendChild( rootNode )
        iface.activeLayer().writeStyle( rootNode, doc, "")
        xml = doc.toString()
        self.editor.setText(xml)
        self.layout().addWidget(self.editor)

xml

Nifty.  Now can we 1 UP it and make it live edit?  Sure can!  The style dock will call apply() whenever we raise the widgetChanged signal.  We can just connect this to the text changed event and add some logic to the apply method.

class CustomPanel(QgsLayerStylingPanel):
    def __init__(self, layer, canvas, parent):
        super(CustomPanel, self).__init__(layer, canvas, parent)
        .....
        self.editor.textChanged.connect(self.widgetChanged.emit)

    def apply(self):
        # Read the xml and set it back to the style.
        doc = QDomDocument( "style" ) 
        doc.setContent(self.editor.text())
        node = doc.documentElement()
        self.layer.readStyle(node, "")
        self.layer.triggerRepaint()

live

WINNING!

Pro Tip: Raise widgetChanged whenever you need to tell the style dock to trigger the apply method. The style state of the layer will also be saved to the undo stack so you don’t have to worry about that.

This is just an example, hand editing xml is well gross, but I’m sure you can see great uses for being able to expand the style dock.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s