Visualize Postgres JSON data in QML widgets

As promised some time ago in “The new QML widgets in QGIS – When widgets get unbridled” we still owe you some fancy unicorns, but first let’s have a look at another nice feature that has been introduced in QGIS 3.4 LTR,  the reading of PostgreSQL JSON and JSONB types.
With JSON you have a lot of possibilities for storing unstructured data. In our case, it’s mainly interesting when the data are stored as an array or a JSON object. Let’s have a look at two examples.

Visualize Postgres JSON data with common widgets

With the usual QGIS widgets “List” and “Key/Value” you are able to display JSON arrays and simple JSON objects.

JSON array as List

[
    "European dark bee",
    "Carniolan honey bee",
    "Buckfast bee"
]

Simple JSON object as Key/Value

{
    "nomenclatura":"Apis mellifera mellifera",
    "name":"European dark bee",
    "link":"https://en.wikipedia.org/wiki/European_dark_bee"
}

Or of course both as plain text in the “Text Edit” widget:

Say hi to Postgres JSON in QML widget

Probably, your JSON data does not look really nice with the aforementioned widgets, luckily since QGIS 3.4, you are free to create your own QML widget. Since QGIS already loads the JSON data into structures that are supported by QML, we can use all the JSON data within the QML code.
Let’s assume you have the JSON array from above and you like the elegance of the blue of Jacques Majorelle. You create your personal list widget by adding the JSON field as an expression:

import QtQuick 2.0
Rectangle {
    width: 310; height: 250; color: "grey"
    Column {
        anchors.horizontalCenter: parent.horizontalCenter
        anchors.verticalCenter: parent.verticalCenter
        spacing: 5
        Repeater {
            model:expression.evaluate("\"jvalue\"")
            Rectangle {
                color: "#6050dc"
                width: 300; height: 50; radius: 10.0
                Text {
                    anchors.centerIn: parent
                    font.pointSize: 24
                    text: modelData
                }
            }
        }
    }
}

You will have your very personal list:

JSON also allows storing more complex data, like for example a list of objects. In that case, you will reach the limits of the common QGIS widgets.
Let’s assume you have a table looking like this:

nomenclatura name link
Apis mellifera mellifera European dark bee https://en.wikipedia.org/wiki/European_dark_bee
Apis mellifera carnica Carniolan honey bee https://en.wikipedia.org/wiki/Carniolan_honey_bee
Apis mellifera Buckfast bee https://en.wikipedia.org/wiki/Buckfast_bee

In JSON it would be stored like this:

[
    {"nomenclatura":"Apis mellifera mellifera","name":"European dark bee","link":"https://en.wikipedia.org/wiki/European_dark_bee"},
    {"nomenclatura":"Apis mellifera carnica","name":"Carniolan honey bee","link":"https://en.wikipedia.org/wiki/Carniolan_honey_bee"},
    {"nomenclatura":"Apis mellifera","name":"Buckfast bee","link":"https://en.wikipedia.org/wiki/Buckfast_bee"}
]

With the QML Widget you can use the QML TableView to visualize:

import QtQuick 2.0
import QtQuick.Controls 1.4
TableView {
    width: 600
    model: expression.evaluate("\"jvalue\"")
    TableViewColumn {
        role: "nomenclatura"
        title: "Nomenclature"
        width: 200
    }
    TableViewColumn {
        role: "name"
        title: "Name"
        width: 200
    }
    TableViewColumn {
        role: "link"
        title: "Wikipedia"
        width: 200
    }
}


Or, even more powerful, you can create your super individual table using the model and create each row by using a QML Repeater.
Additionally, you can use a lot of fancy stuff like:

  • mouse interaction
  • animation
  • opening an external link
  • … and so on


The QML code for that looks like this.

import QtQuick 2.0
Rectangle {
    width: 610; height: 500
    Column {
        anchors.horizontalCenter: parent.horizontalCenter
        anchors.verticalCenter: parent.verticalCenter
        Repeater {
            model: expression.evaluate("\"jvalue\"")
            Row {
                id: theRow
                height: mouseArea1.containsMouse || mouseArea2.containsMouse || mouseArea3.containsMouse ? 70 : 50;
                Rectangle { color: "lightblue";
                            border.width: 1
                            width: 150; height: parent.height
                            Text { anchors.centerIn: parent
                                   font.pointSize: 10; text: modelData.nomenclatura }
                            MouseArea {
                                id: mouseArea1
                                anchors.fill: parent
                                hoverEnabled: true
                            }
                }
                Rectangle { color: "lightgreen";
                            border.width: 1
                            width: 150; height: parent.height
                            Text { anchors.centerIn: parent
                                   font.pointSize: 10; text: modelData.name }
                            MouseArea {
                                id: mouseArea2
                                anchors.fill: parent
                                hoverEnabled: true
                            }
                }
                Rectangle {
                            id: linkField
                            color: "lightyellow";
                            border.width: 1
                            width: 300; height: parent.height
                            Text { anchors.centerIn: parent
                                   font.pointSize: 10; text: modelData.link }
                            MouseArea {
                                id: mouseArea3
                                anchors.fill: parent
                                hoverEnabled: true
                                onPressed: linkField.state = "PRESSED"
                                onReleased: linkField.state = "RELEASED"
                                onClicked: Qt.openUrlExternally(modelData.link)
                            }
                            states: [
                                State {
                                    name: "PRESSED"
                                    PropertyChanges { target: linkField; color: "green"}
                                },
                                State {
                                    name: "RELEASED"
                                    PropertyChanges { target: linkField; color: "lightyellow"}
                                }
                            ]
                            transitions: [
                                Transition {
                                    from: "PRESSED"
                                    to: "RELEASED"
                                    ColorAnimation { target: linkField; duration: 1000}
                                },
                                Transition {
                                    from: "RELEASED"
                                    to: "PRESSED"
                                    ColorAnimation { target: linkField; duration: 1000}
                                }
                            ]
                }
            }
        }
    }
}

And that’s it

I hope you liked reading and you will enjoy using it to make beautiful widgets and forms. If you have questions or inputs, feel free to add a comment.
… and in case you still asking where the promised unicorns are. Here’s is a super-fancy implementation 😉

Leave a Reply