Skip to content

Instantly share code, notes, and snippets.

@mottosso
Last active April 12, 2023 03:23
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save mottosso/fc0149224b21c22061fe to your computer and use it in GitHub Desktop.
Save mottosso/fc0149224b21c22061fe to your computer and use it in GitHub Desktop.
Minimal QML SceneGraph

untitled

Usage

python main.py --amount 100
  • Panning by left-click + drag
  • Scale by rotating the mouse wheel

Notable things

  • The model is populated randomly via Python and a QAbstractListModel
  • Increase number of nodes until you are convinced QML can handle it (mine chokes at around 300, not if Python or QML is to blame)
  • The blurriness at the end is due to the DropShadow, which produces a rasterised version of each node, as opposed to a pure rectangle. You can tell by how the Text is updated correctly.
  • The visible lag in the above .gif is not present prior to recording.
import sys
import random
import contextlib
from PyQt5 import QtQml, QtGui, QtCore
@contextlib.contextmanager
def application():
app = QtGui.QGuiApplication(sys.argv)
yield
app.exec_()
class Model(QtCore.QAbstractListModel):
def __init__(self, items, parent=None):
super(Model, self).__init__(parent)
self._items = items
def rowCount(self, parent=None):
return len(self._items)
def data(self, index, role=QtCore.QModelIndex()):
name = self.roleNames()[role]
try:
return self._items[index.row()][name]
except KeyError:
pass
def roleNames(self):
return {
QtCore.Qt.UserRole + 0: "name",
QtCore.Qt.UserRole + 1: "x",
QtCore.Qt.UserRole + 2: "y",
}
if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--amount", type=int, default=100)
amount = parser.parse_args().amount
print("Creating %i nodes" % amount)
with application():
engine = QtQml.QQmlApplicationEngine()
# Generate lots of nodes
canvas_width = 600
canvas_height = 500
node_width = 100
node_height = 50
scale = 10
nodes = list()
for i in xrange(amount):
x = random.randint(0, canvas_width - node_width)
y = random.randint(0, canvas_height - node_height)
nodes.append({
"name": "Node %s" % i,
"x": x * scale,
"y": y * scale
})
model = Model(nodes)
context = engine.rootContext()
context.setContextProperty("sgModel", model)
engine.load(QtCore.QUrl.fromLocalFile("main.qml"))
import QtQuick 2.4
import QtQuick.Controls 1.2
import QtGraphicalEffects 1.0
ApplicationWindow {
title: "QML SceneGraph"
width: 600
height: 500
visible: true
/* Make canvas draggable */
MouseArea {
anchors.fill: parent
drag.target: canvas
acceptedButtons: Qt.LeftButton | Qt.Wheel
/* And zoomable */
onWheel: canvas.scale += wheel.angleDelta.y / 1500
}
Item {
id: canvas
/* Draw nodes from model */
Repeater {
model: sgModel
delegate: Item {
width: 100
height: 50
x: model.x
y: model.y
Item {
id: shadow
anchors.fill: parent
visible: false
Rectangle {
anchors.fill: parent
anchors.margins: 5
radius: 5
border.width: 1
border.color: Qt.darker("brown", 1.2)
color: "brown"
}
}
DropShadow {
anchors.fill: parent
horizontalOffset: 1
verticalOffset: 1
radius: 3.0
samples: 16
color: "#80000000"
source: shadow
}
Text {
anchors.centerIn: parent
text: model.name
color: "#ddd"
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment