1#!/usr/bin/env python 2 3 4############################################################################# 5## 6## Copyright (C) 2013 Riverbank Computing Limited. 7## Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). 8## 9## This file is part of the examples of PyQt. 10## 11## $QT_BEGIN_LICENSE:BSD$ 12## You may use this file under the terms of the BSD license as follows: 13## 14## "Redistribution and use in source and binary forms, with or without 15## modification, are permitted provided that the following conditions are 16## met: 17## * Redistributions of source code must retain the above copyright 18## notice, this list of conditions and the following disclaimer. 19## * Redistributions in binary form must reproduce the above copyright 20## notice, this list of conditions and the following disclaimer in 21## the documentation and/or other materials provided with the 22## distribution. 23## * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names 24## of its contributors may be used to endorse or promote products derived 25## from this software without specific prior written permission. 26## 27## 28## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 29## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 30## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 31## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 32## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 33## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 34## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 35## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 36## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 37## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 38## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." 39## $QT_END_LICENSE$ 40## 41############################################################################# 42 43 44from PyQt5.QtCore import pyqtProperty, pyqtSignal, QPointF, QUrl 45from PyQt5.QtGui import QColor, QGuiApplication 46from PyQt5.QtQml import qmlRegisterType 47from PyQt5.QtQuick import (QQuickItem, QQuickView, QSGFlatColorMaterial, 48 QSGGeometry, QSGGeometryNode, QSGNode) 49 50import customgeometry_rc 51 52 53class BezierCurve(QQuickItem): 54 55 p1Changed = pyqtSignal(QPointF) 56 57 @pyqtProperty(QPointF, notify=p1Changed) 58 def p1(self): 59 return self._p1 60 61 @p1.setter 62 def p1(self, p): 63 if self._p1 != p: 64 self._p1 = QPointF(p) 65 self.p1Changed.emit(p) 66 self.update() 67 68 p2Changed = pyqtSignal(QPointF) 69 70 @pyqtProperty(QPointF, notify=p2Changed) 71 def p2(self): 72 return self._p2 73 74 @p2.setter 75 def p2(self, p): 76 if self._p2 != p: 77 self._p2 = QPointF(p) 78 self.p2Changed.emit(p) 79 self.update() 80 81 p3Changed = pyqtSignal(QPointF) 82 83 @pyqtProperty(QPointF, notify=p3Changed) 84 def p3(self): 85 return self._p3 86 87 @p3.setter 88 def p3(self, p): 89 if self._p3 != p: 90 self._p3 = QPointF(p) 91 self.p3Changed.emit(p) 92 self.update() 93 94 p4Changed = pyqtSignal(QPointF) 95 96 @pyqtProperty(QPointF, notify=p4Changed) 97 def p4(self): 98 return self._p4 99 100 @p4.setter 101 def p4(self, p): 102 if self._p4 != p: 103 self._p4 = QPointF(p) 104 self.p4Changed.emit(p) 105 self.update() 106 107 segmentCountChanged = pyqtSignal(int) 108 109 @pyqtProperty(int, notify=segmentCountChanged) 110 def segmentCount(self): 111 return self._segmentCount 112 113 @segmentCount.setter 114 def segmentCount(self, count): 115 if self._segmentCount != count: 116 self._segmentCount = count 117 self.segmentCountChanged.emit(count) 118 self.update() 119 120 def __init__(self, parent=None): 121 super(BezierCurve, self).__init__(parent) 122 123 self._p1 = QPointF(0, 0) 124 self._p2 = QPointF(1, 0) 125 self._p3 = QPointF(0, 1) 126 self._p4 = QPointF(1, 1) 127 128 self._segmentCount = 32 129 130 self._root_node = None 131 132 self.setFlag(QQuickItem.ItemHasContents, True) 133 134 def updatePaintNode(self, oldNode, nodeData): 135 if self._root_node is None: 136 self._root_node = QSGGeometryNode() 137 138 geometry = QSGGeometry(QSGGeometry.defaultAttributes_Point2D(), 139 self._segmentCount) 140 geometry.setLineWidth(2) 141 geometry.setDrawingMode(QSGGeometry.GL_LINE_STRIP) 142 self._root_node.setGeometry(geometry) 143 self._root_node.setFlag(QSGNode.OwnsGeometry) 144 145 material = QSGFlatColorMaterial() 146 material.setColor(QColor(255, 0, 0)) 147 self._root_node.setMaterial(material) 148 self._root_node.setFlag(QSGNode.OwnsMaterial) 149 else: 150 geometry = self._root_node.geometry() 151 geometry.allocate(self._segmentCount) 152 153 w = self.width() 154 h = self.height() 155 vertices = geometry.vertexDataAsPoint2D() 156 157 for i in range(self._segmentCount): 158 t = i / float(self._segmentCount - 1) 159 invt = 1 - t 160 161 pos = invt * invt * invt * self._p1 \ 162 + 3 * invt * invt * t * self._p2 \ 163 + 3 * invt * t * t * self._p3 \ 164 + t * t * t * self._p4 165 166 vertices[i].set(pos.x() * w, pos.y() * h) 167 168 self._root_node.markDirty(QSGNode.DirtyGeometry) 169 170 return self._root_node 171 172 173if __name__ == '__main__': 174 import sys 175 176 app = QGuiApplication(sys.argv) 177 178 qmlRegisterType(BezierCurve, "CustomGeometry", 1, 0, "BezierCurve") 179 180 view = QQuickView() 181 format = view.format() 182 format.setSamples(16) 183 view.setFormat(format) 184 185 view.setSource(QUrl('qrc:///scenegraph/customgeometry/main.qml')) 186 view.show() 187 188 sys.exit(app.exec_()) 189