1############################################################################# 2## 3## Copyright (C) 2013 Riverbank Computing Limited. 4## Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). 5## All rights reserved. 6## 7## This file is part of the examples of PyQt. 8## 9## $QT_BEGIN_LICENSE:LGPL$ 10## Commercial Usage 11## Licensees holding valid Qt Commercial licenses may use this file in 12## accordance with the Qt Commercial License Agreement provided with the 13## Software or, alternatively, in accordance with the terms contained in 14## a written agreement between you and Nokia. 15## 16## GNU Lesser General Public License Usage 17## Alternatively, this file may be used under the terms of the GNU Lesser 18## General Public License version 2.1 as published by the Free Software 19## Foundation and appearing in the file LICENSE.LGPL included in the 20## packaging of this file. Please review the following information to 21## ensure the GNU Lesser General Public License version 2.1 requirements 22## will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. 23## 24## In addition, as a special exception, Nokia gives you certain additional 25## rights. These rights are described in the Nokia Qt LGPL Exception 26## version 1.1, included in the file LGPL_EXCEPTION.txt in this package. 27## 28## GNU General Public License Usage 29## Alternatively, this file may be used under the terms of the GNU 30## General Public License version 3.0 as published by the Free Software 31## Foundation and appearing in the file LICENSE.GPL included in the 32## packaging of this file. Please review the following information to 33## ensure the GNU General Public License version 3.0 requirements will be 34## met: http://www.gnu.org/copyleft/gpl.html. 35## 36## If you have questions regarding the use of this file, please contact 37## Nokia at qt-info@nokia.com. 38## $QT_END_LICENSE$ 39## 40############################################################################# 41 42 43from PyQt5.QtCore import QPointF, QRectF, qRound 44from PyQt5.QtGui import QColor, QPainter, QPixmap, QTransform 45from PyQt5.QtWidgets import QGraphicsObject 46 47from colors import Colors 48 49 50class SharedImage(object): 51 def __init__(self): 52 self.refCount = 0 53 self.image = None 54 self.pixmap = None 55 self.transform = QTransform() 56 self.unscaledBoundingRect = QRectF() 57 58 59class DemoItem(QGraphicsObject): 60 _sharedImageHash = {} 61 62 _transform = QTransform() 63 64 def __init__(self, parent=None): 65 super(DemoItem, self).__init__(parent) 66 67 self.noSubPixeling = False 68 self.currentAnimation = None 69 self.currGuide = None 70 self.guideFrame = 0.0 71 72 self._sharedImage = SharedImage() 73 self._sharedImage.refCount += 1 74 self._hashKey = '' 75 76 def __del__(self): 77 self._sharedImage.refCount -= 1 78 if self._sharedImage.refCount == 0: 79 if self._hashKey: 80 del DemoItem._sharedImageHash[self._hashKey] 81 82 def animationStarted(self, id=0): 83 pass 84 85 def animationStopped(self, id=0): 86 pass 87 88 def setRecursiveVisible(self, visible): 89 self.setVisible(visible) 90 for c in self.childItems(): 91 c.setVisible(visible) 92 93 def useGuide(self, guide, startFrame): 94 self.guideFrame = startFrame 95 while self.guideFrame > guide.startLength + guide.length(): 96 if guide.nextGuide == guide.firstGuide: 97 break 98 99 guide = guide.nextGuide 100 101 self.currGuide = guide 102 103 def guideAdvance(self, distance): 104 self.guideFrame += distance 105 while self.guideFrame > self.currGuide.startLength + self.currGuide.length(): 106 self.currGuide = self.currGuide.nextGuide 107 if self.currGuide == self.currGuide.firstGuide: 108 self.guideFrame -= self.currGuide.lengthAll() 109 110 def guideMove(self, moveSpeed): 111 self.currGuide.guide(self, moveSpeed) 112 113 def setPosUsingSheepDog(self, dest, sceneFence): 114 self.setPos(dest) 115 if sceneFence.isNull(): 116 return 117 118 itemWidth = self.boundingRect().width() 119 itemHeight = self.boundingRect().height() 120 fenceRight = sceneFence.x() + sceneFence.width() 121 fenceBottom = sceneFence.y() + sceneFence.height() 122 123 if self.scenePos().x() < sceneFence.x(): 124 self.moveBy(self.mapFromScene(QPointF(sceneFence.x(), 0)).x(), 0) 125 126 if self.scenePos().x() > fenceRight - itemWidth: 127 self.moveBy(self.mapFromScene(QPointF(fenceRight - itemWidth, 0)).x(), 0) 128 129 if self.scenePos().y() < sceneFence.y(): 130 self.moveBy(0, self.mapFromScene(QPointF(0, sceneFence.y())).y()) 131 132 if self.scenePos().y() > fenceBottom - itemHeight: 133 self.moveBy(0, self.mapFromScene(QPointF(0, fenceBottom - itemHeight)).y()) 134 135 def setGuidedPos(self, pos): 136 # Make sure we have a copy. 137 self.guidedPos = QPointF(pos) 138 139 def getGuidedPos(self): 140 # Return a copy so that it can be changed. 141 return QPointF(self.guidedPos) 142 143 @staticmethod 144 def setTransform(transform): 145 DemoItem._transform = transform 146 147 def useSharedImage(self, hashKey): 148 self._hashKey = hashKey 149 if hashKey not in DemoItem._sharedImageHash: 150 DemoItem._sharedImageHash[hashKey] = self._sharedImage 151 else: 152 self._sharedImage.refCount -= 1 153 self._sharedImage = DemoItem._sharedImageHash[hashKey] 154 self._sharedImage.refCount += 1 155 156 def createImage(self, transform): 157 return None 158 159 def _validateImage(self): 160 if (self._sharedImage.transform != DemoItem._transform and not Colors.noRescale) or (self._sharedImage.image is None and self._sharedImage.pixmap is None): 161 # (Re)create image according to new transform. 162 self._sharedImage.image = None 163 self._sharedImage.pixmap = None 164 self._sharedImage.transform = DemoItem._transform 165 166 # Let subclass create and draw a new image according to the new 167 # transform. 168 if Colors.noRescale: 169 transform = QTransform() 170 else: 171 transform = DemoItem._transform 172 image = self.createImage(transform) 173 if image is not None: 174 if Colors.showBoundingRect: 175 # Draw red transparent rect. 176 painter = QPainter(image) 177 painter.fillRect(image.rect(), QColor(255, 0, 0, 50)) 178 painter.end() 179 180 self._sharedImage.unscaledBoundingRect = self._sharedImage.transform.inverted()[0].mapRect(QRectF(image.rect())) 181 182 if Colors.usePixmaps: 183 if image.isNull(): 184 self._sharedImage.pixmap = QPixmap(1, 1) 185 else: 186 self._sharedImage.pixmap = QPixmap(image.size()) 187 188 self._sharedImage.pixmap.fill(QColor(0, 0, 0, 0)) 189 painter = QPainter(self._sharedImage.pixmap) 190 painter.drawImage(0, 0, image) 191 else: 192 self._sharedImage.image = image 193 194 return True 195 else: 196 return False 197 198 return True 199 200 def boundingRect(self): 201 self._validateImage() 202 return self._sharedImage.unscaledBoundingRect 203 204 def paint(self, painter, option=None, widget=None): 205 if self._validateImage(): 206 wasSmoothPixmapTransform = painter.testRenderHint(QPainter.SmoothPixmapTransform) 207 painter.setRenderHint(QPainter.SmoothPixmapTransform) 208 209 if Colors.noRescale: 210 # Let the painter scale the image for us. This may degrade 211 # both quality and performance. 212 if self._sharedImage.image is not None: 213 painter.drawImage(self.pos(), self._sharedImage.image) 214 else: 215 painter.drawPixmap(self.pos(), self._sharedImage.pixmap) 216 else: 217 m = painter.worldTransform() 218 painter.setWorldTransform(QTransform()) 219 220 x = m.dx() 221 y = m.dy() 222 if self.noSubPixeling: 223 x = qRound(x) 224 y = qRound(y) 225 226 if self._sharedImage.image is not None: 227 painter.drawImage(QPointF(x, y), self._sharedImage.image) 228 else: 229 painter.drawPixmap(QPointF(x, y), self._sharedImage.pixmap) 230 231 if not wasSmoothPixmapTransform: 232 painter.setRenderHint(QPainter.SmoothPixmapTransform, 233 False) 234 235 def collidesWithItem(self, item, mode): 236 return False 237