Article Outline
Python pyqt (gui) example 'demoitem'
demoitem
Python pyqt example: demoitem
#############################################################################
##
## Copyright (C) 2013 Riverbank Computing Limited.
## Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
## All rights reserved.
##
## This file is part of the examples of PyQt.
##
## $QT_BEGIN_LICENSE:LGPL$
## Commercial Usage
## Licensees holding valid Qt Commercial licenses may use this file in
## accordance with the Qt Commercial License Agreement provided with the
## Software or, alternatively, in accordance with the terms contained in
## a written agreement between you and Nokia.
##
## GNU Lesser General Public License Usage
## Alternatively, this file may be used under the terms of the GNU Lesser
## General Public License version 2.1 as published by the Free Software
## Foundation and appearing in the file LICENSE.LGPL included in the
## packaging of this file. Please review the following information to
## ensure the GNU Lesser General Public License version 2.1 requirements
## will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
##
## In addition, as a special exception, Nokia gives you certain additional
## rights. These rights are described in the Nokia Qt LGPL Exception
## version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
##
## GNU General Public License Usage
## Alternatively, this file may be used under the terms of the GNU
## General Public License version 3.0 as published by the Free Software
## Foundation and appearing in the file LICENSE.GPL included in the
## packaging of this file. Please review the following information to
## ensure the GNU General Public License version 3.0 requirements will be
## met: http://www.gnu.org/copyleft/gpl.html.
##
## If you have questions regarding the use of this file, please contact
## Nokia at [email protected].
## $QT_END_LICENSE$
##
#############################################################################
from PyQt5.QtCore import QPointF, QRectF, qRound
from PyQt5.QtGui import QColor, QPainter, QPixmap, QTransform
from PyQt5.QtWidgets import QGraphicsObject
from colors import Colors
class SharedImage(object):
def __init__(self):
self.refCount = 0
self.image = None
self.pixmap = None
self.transform = QTransform()
self.unscaledBoundingRect = QRectF()
class DemoItem(QGraphicsObject):
_sharedImageHash = {}
_transform = QTransform()
def __init__(self, parent=None):
super(DemoItem, self).__init__(parent)
self.noSubPixeling = False
self.currentAnimation = None
self.currGuide = None
self.guideFrame = 0.0
self._sharedImage = SharedImage()
self._sharedImage.refCount += 1
self._hashKey = ''
def __del__(self):
self._sharedImage.refCount -= 1
if self._sharedImage.refCount == 0:
if self._hashKey:
del DemoItem._sharedImageHash[self._hashKey]
def animationStarted(self, id=0):
pass
def animationStopped(self, id=0):
pass
def setRecursiveVisible(self, visible):
self.setVisible(visible)
for c in self.childItems():
c.setVisible(visible)
def useGuide(self, guide, startFrame):
self.guideFrame = startFrame
while self.guideFrame > guide.startLength + guide.length():
if guide.nextGuide == guide.firstGuide:
break
guide = guide.nextGuide
self.currGuide = guide
def guideAdvance(self, distance):
self.guideFrame += distance
while self.guideFrame > self.currGuide.startLength + self.currGuide.length():
self.currGuide = self.currGuide.nextGuide
if self.currGuide == self.currGuide.firstGuide:
self.guideFrame -= self.currGuide.lengthAll()
def guideMove(self, moveSpeed):
self.currGuide.guide(self, moveSpeed)
def setPosUsingSheepDog(self, dest, sceneFence):
self.setPos(dest)
if sceneFence.isNull():
return
itemWidth = self.boundingRect().width()
itemHeight = self.boundingRect().height()
fenceRight = sceneFence.x() + sceneFence.width()
fenceBottom = sceneFence.y() + sceneFence.height()
if self.scenePos().x() < sceneFence.x():
self.moveBy(self.mapFromScene(QPointF(sceneFence.x(), 0)).x(), 0)
if self.scenePos().x() > fenceRight - itemWidth:
self.moveBy(self.mapFromScene(QPointF(fenceRight - itemWidth, 0)).x(), 0)
if self.scenePos().y() < sceneFence.y():
self.moveBy(0, self.mapFromScene(QPointF(0, sceneFence.y())).y())
if self.scenePos().y() > fenceBottom - itemHeight:
self.moveBy(0, self.mapFromScene(QPointF(0, fenceBottom - itemHeight)).y())
def setGuidedPos(self, pos):
# Make sure we have a copy.
self.guidedPos = QPointF(pos)
def getGuidedPos(self):
# Return a copy so that it can be changed.
return QPointF(self.guidedPos)
@staticmethod
def setTransform(transform):
DemoItem._transform = transform
def useSharedImage(self, hashKey):
self._hashKey = hashKey
if hashKey not in DemoItem._sharedImageHash:
DemoItem._sharedImageHash[hashKey] = self._sharedImage
else:
self._sharedImage.refCount -= 1
self._sharedImage = DemoItem._sharedImageHash[hashKey]
self._sharedImage.refCount += 1
def createImage(self, transform):
return None
def _validateImage(self):
if (self._sharedImage.transform != DemoItem._transform and not Colors.noRescale) or (self._sharedImage.image is None and self._sharedImage.pixmap is None):
# (Re)create image according to new transform.
self._sharedImage.image = None
self._sharedImage.pixmap = None
self._sharedImage.transform = DemoItem._transform
# Let subclass create and draw a new image according to the new
# transform.
if Colors.noRescale:
transform = QTransform()
else:
transform = DemoItem._transform
image = self.createImage(transform)
if image is not None:
if Colors.showBoundingRect:
# Draw red transparent rect.
painter = QPainter(image)
painter.fillRect(image.rect(), QColor(255, 0, 0, 50))
painter.end()
self._sharedImage.unscaledBoundingRect = self._sharedImage.transform.inverted()[0].mapRect(QRectF(image.rect()))
if Colors.usePixmaps:
if image.isNull():
self._sharedImage.pixmap = QPixmap(1, 1)
else:
self._sharedImage.pixmap = QPixmap(image.size())
self._sharedImage.pixmap.fill(QColor(0, 0, 0, 0))
painter = QPainter(self._sharedImage.pixmap)
painter.drawImage(0, 0, image)
else:
self._sharedImage.image = image
return True
else:
return False
return True
def boundingRect(self):
self._validateImage()
return self._sharedImage.unscaledBoundingRect
def paint(self, painter, option=None, widget=None):
if self._validateImage():
wasSmoothPixmapTransform = painter.testRenderHint(QPainter.SmoothPixmapTransform)
painter.setRenderHint(QPainter.SmoothPixmapTransform)
if Colors.noRescale:
# Let the painter scale the image for us. This may degrade
# both quality and performance.
if self._sharedImage.image is not None:
painter.drawImage(self.pos(), self._sharedImage.image)
else:
painter.drawPixmap(self.pos(), self._sharedImage.pixmap)
else:
m = painter.worldTransform()
painter.setWorldTransform(QTransform())
x = m.dx()
y = m.dy()
if self.noSubPixeling:
x = qRound(x)
y = qRound(y)
if self._sharedImage.image is not None:
painter.drawImage(QPointF(x, y), self._sharedImage.image)
else:
painter.drawPixmap(QPointF(x, y), self._sharedImage.pixmap)
if not wasSmoothPixmapTransform:
painter.setRenderHint(QPainter.SmoothPixmapTransform,
False)
def collidesWithItem(self, item, mode):
return False
Useful links
- Learn PyQt: https://pythonbasics.org/pyqt-hello-world/
- Install PyQt: https://pythonbasics.org/install-pyqt/