From 46d9f47ef56c1982fcd3880d4b0b29067b69d333 Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 26 Feb 2014 01:49:06 +0000 Subject: [PATCH] Add qgraphicsembedscene.py file based on Qt C++ code --- Makefile | 3 +- source/widgets/qgraphicsembedscene.py | 236 ++++++++++++++++++++++++++ 2 files changed, 238 insertions(+), 1 deletion(-) create mode 100644 source/widgets/qgraphicsembedscene.py diff --git a/Makefile b/Makefile index 579f79dce..76dd5b9dd 100644 --- a/Makefile +++ b/Makefile @@ -145,7 +145,8 @@ WIDGETS = \ source/paramspinbox.py \ source/pixmapbutton.py \ source/pixmapdial.py \ - source/pixmapkeyboard.py + source/pixmapkeyboard.py \ + source/qgraphicsembedscene.py WIDGETS: $(WIDGETS) diff --git a/source/widgets/qgraphicsembedscene.py b/source/widgets/qgraphicsembedscene.py new file mode 100644 index 000000000..adfcc83fa --- /dev/null +++ b/source/widgets/qgraphicsembedscene.py @@ -0,0 +1,236 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# QGraphicsEmbedScene class, based on Qt3D C++ code +# Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +# Copyright (C) 2014 Filipe Coelho +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License, or any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# For a full copy of the GNU General Public License see the doc/GPL.txt file. + +# ------------------------------------------------------------------------------------------------------------ +# Imports (Config) + +from carla_config import * + +# ------------------------------------------------------------------------------------------------------------ +# Imports (Global) + +if config_UseQt5: + from PyQt5.QtCore import pyqtSignal, pyqtSlot, qRound, Qt, QPoint, QSize + from PyQt5.QtGui import QEvent, QPainter + from PyQt5.QtOpenGL import QGLFramebufferObject, QGLFramebufferObjectFormat + from PyQt5.QtWidgets import QApplication, QGraphicsScene +else: + from PyQt4.QtCore import pyqtSignal, pyqtSlot, qRound, Qt, QPoint, QSize + from PyQt4.QtOpenGL import QGLFramebufferObject, QGLFramebufferObjectFormat + from PyQt4.QtGui import QApplication, QEvent, QObject, QPainter, QGraphicsScene + +# ------------------------------------------------------------------------------------------------------------ + +# Returns the next power of two that is greater than or +# equal to @a value. The @a value must be positive or the +# result is undefined. +# +# This is a convenience function for use with GL texture +# handling code. + +def nextPowerOfTwo(value): + value -= 1 + value |= value >> 1 + value |= value >> 2 + value |= value >> 4 + value |= value >> 8 + value |= value >> 16 + value += 1 + return value + +# ------------------------------------------------------------------------------------------------------------ +# Widget Class + +class QGraphicsEmbedScene(QGraphicsScene): + def __init__(self, parent): + QGraphicsScene.__init__(self, parent) + + self.dirty = True + self.fbo = None + + self.format = QGLFramebufferObjectFormat() + self.format.setAttachment(QGLFramebufferObject.CombinedDepthStencil) + + self.pressedPos = QPoint() + + self.changed.connect(self.slot_update) + self.sceneRectChanged.connect(self.slot_update) + + def format(self): + return self.format + + def setFormat(self, format): + self.format = format + + def renderToTexture(self, levelOfDetail = 1.0): + # Determine the fbo size we will need. + size = (self.sceneRect().size() * levelOfDetail).toSize() + fboSize = nextPowerOfTwo(size) + if fboSize.isEmpty(): + fboSize = QSize(16, 16) + + # Create or re-create the fbo. + if self.fbo is None or self.fbo.size() != fboSize: + #del self.fbo + self.fbo = QGLFramebufferObject(fboSize, self.format) + if not self.fbo.isValid(): + #del self.fbo + self.fbo = None + return 0 + self.dirty = True + + # Return the previous texture contents if the scene hasn't changed. + if self.fbo is not None and not self.dirty: + return self.fbo.texture() + + # Render the scene into the fbo, scaling the QPainter's view + # transform up to the power-of-two fbo size. + painter = QPainter(self.fbo) + painter.setWindow(0, 0, size.width(), size.height()); + painter.setViewport(0, 0, fboSize.width(), fboSize.height()); + self.render(painter) + painter.end() + self.dirty = False + return self.fbo.texture(); + + def deliverEvent(self, event, texCoord): + # Map the texture co-ordinate into "screen" co-ordinates. + # Mouse move and release events can extend beyond the boundaries + # of the scene, for "click and drag off-screen" operations. + # Mouse press and double-click events need to be constrained. + bounds = self.sceneRect() + screenX = qRound(texCoord.x() * bounds.width()) + screenY = qRound((1.0 - texCoord.y()) * bounds.height()) + if event.type() in (QEvent.GraphicsSceneMousePress, + QEvent.GraphicsSceneMouseDoubleClick, + QEvent.MouseButtonPress, + QEvent.MouseButtonDblClick): + if screenX < 0: + screenX = 0 + elif screenX >= bounds.width(): + screenX = qRound(bounds.width() - 1) + if screenY < 0: + screenY = 0 + elif screenY >= bounds.height(): + screenY = qRound(bounds.height() - 1) + self.pressedPos = QPoint(screenX, screenY) + + # Convert the event and deliver it to the scene. + eventType = event.type() + + if eventType in (QEvent.GraphicsSceneMouseMove, + QEvent.GraphicsSceneMousePress, + QEvent.GraphicsSceneMouseRelease, + QEvent.GraphicsSceneMouseDoubleClick): + pass + #QGraphicsSceneMouseEvent *ev = + #static_cast(event); + #QGraphicsSceneMouseEvent e(ev->type()); + #e.setPos(QPointF(screenX, screenY)); + #e.setScenePos(QPointF(screenX + bounds.x(), screenY + bounds.y())); + #e.setScreenPos(QPoint(screenX, screenY)); + #e.setButtonDownScreenPos(ev->button(), d->pressedPos); + #e.setButtonDownScenePos + #(ev->button(), QPointF(d->pressedPos.x() + bounds.x(), + #d->pressedPos.y() + bounds.y())); + #e.setButtons(ev->buttons()); + #e.setButton(ev->button()); + #e.setModifiers(ev->modifiers()); + #e.setAccepted(false); + #QApplication::sendEvent(this, &e); + + elif eventType == QEvent.GraphicsSceneWheel: + pass + #QGraphicsSceneWheelEvent *ev = + #static_cast(event); + #QGraphicsSceneWheelEvent e(QEvent::GraphicsSceneWheel); + #e.setPos(QPointF(screenX, screenY)); + #e.setScenePos(QPointF(screenX + bounds.x(), screenY + bounds.y())); + #e.setScreenPos(QPoint(screenX, screenY)); + #e.setButtons(ev->buttons()); + #e.setModifiers(ev->modifiers()); + #e.setDelta(ev->delta()); + #e.setOrientation(ev->orientation()); + #e.setAccepted(false); + #QApplication::sendEvent(this, &e); + + elif eventType in (QEvent.MouseButtonPress, + QEvent.MouseButtonRelease, + QEvent.MouseButtonDblClick, + QEvent.MouseMove): + pass + #QMouseEvent *ev = static_cast(event); + #QEvent::Type type; + #if (ev->type() == QEvent::MouseButtonPress) + #type = QEvent::GraphicsSceneMousePress; + #else if (ev->type() == QEvent::MouseButtonRelease) + #type = QEvent::GraphicsSceneMouseRelease; + #else if (ev->type() == QEvent::MouseButtonDblClick) + #type = QEvent::GraphicsSceneMouseDoubleClick; + #else + #type = QEvent::GraphicsSceneMouseMove; + #QGraphicsSceneMouseEvent e(type); + #e.setPos(QPointF(screenX, screenY)); + #e.setScenePos(QPointF(screenX + bounds.x(), screenY + bounds.y())); + #e.setScreenPos(QPoint(screenX, screenY)); + #e.setButtonDownScreenPos(ev->button(), d->pressedPos); + #e.setButtonDownScenePos + #(ev->button(), QPointF(d->pressedPos.x() + bounds.x(), + #d->pressedPos.y() + bounds.y())); + #e.setButtons(ev->buttons()); + #e.setButton(ev->button()); + #e.setModifiers(ev->modifiers()); + #e.setAccepted(false); + #QApplication::sendEvent(this, &e); + + elif eventType == QEvent.Wheel: + pass + #QWheelEvent *ev = static_cast(event); + #QGraphicsSceneWheelEvent e(QEvent::GraphicsSceneWheel); + #e.setPos(QPointF(screenX, screenY)); + #e.setScenePos(QPointF(screenX + bounds.x(), screenY + bounds.y())); + #e.setScreenPos(QPoint(screenX, screenY)); + #e.setButtons(ev->buttons()); + #e.setModifiers(ev->modifiers()); + #e.setDelta(ev->delta()); + #e.setOrientation(ev->orientation()); + #e.setAccepted(false); + #QApplication::sendEvent(this, &e); + + #else: + # Send the event directly without any conversion. + # Typically used for keyboard, focus, and enter/leave events. + #QApplication.sendEvent(self, event) + + QApplication.sendEvent(self, event) + + def drawBackground(self, painter, rect): + if self.backgroundBrush().style() == Qt.NoBrush: + # Fill the fbo with the transparent color as there won't + # be a window or graphics item drawing a previous background. + painter.save() + painter.setCompositionMode(QPainter.CompositionMode_Source) + painter.fillRect(rect, Qt.transparent) + painter.restore() + else: + QGraphicsScene.drawBackground(painter, rect) + + @pyqtSlot() + def slot_update(self): + self.dirty = True