|  | #!/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 <falktx@falktx.com>
#
# 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, QPointF, QRectF, QSize
    from PyQt5.QtGui import QPainter
    from PyQt5.QtOpenGL import QGLFramebufferObject, QGLFramebufferObjectFormat
    from PyQt5.QtWidgets import QApplication, QEvent, QGraphicsItem, QGraphicsScene
else:
    from PyQt4.QtCore import pyqtSignal, pyqtSlot, qRound, Qt, QEvent, QObject, QPoint, QPointF, QRectF, QSize
    from PyQt4.QtOpenGL import QGLFramebufferObject, QGLFramebufferObjectFormat
    from PyQt4.QtGui import QApplication, QPainter, QGraphicsItem, 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<QGraphicsSceneMouseEvent *>(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<QGraphicsSceneWheelEvent *>(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<QMouseEvent *>(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<QWheelEvent *>(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
# ------------------------------------------------------------------------------------------------------------
# PatchScene compatible class
# object types
CanvasBoxType           = QGraphicsItem.UserType + 1
CanvasIconType          = QGraphicsItem.UserType + 2
CanvasPortType          = QGraphicsItem.UserType + 3
CanvasLineType          = QGraphicsItem.UserType + 4
CanvasBezierLineType    = QGraphicsItem.UserType + 5
CanvasLineMovType       = QGraphicsItem.UserType + 6
CanvasBezierLineMovType = QGraphicsItem.UserType + 7
class PatchScene3D(QGraphicsEmbedScene):
    scaleChanged    = pyqtSignal(float)
    sceneGroupMoved = pyqtSignal(int, int, QPointF)
    pluginSelected  = pyqtSignal(list)
    def __init__(self, parent, view):
        QGraphicsEmbedScene.__init__(self, parent)
        self.m_ctrl_down = False
        self.m_mouse_down_init = False
        self.m_mouse_rubberband = False
        self.addRubberBand()
        self.m_view = view
        #if not self.m_view:
            #qFatal("PatchCanvas::PatchScene() - invalid view")
        self.selectionChanged.connect(self.slot_selectionChanged)
    def addRubberBand(self):
        self.m_rubberband = self.addRect(QRectF(0, 0, 0, 0))
        self.m_rubberband.setZValue(-1)
        self.m_rubberband.hide()
        self.m_rubberband_selection = False
        self.m_rubberband_orig_point = QPointF(0, 0)
    def clear(self):
        QGraphicsEmbedScene.clear(self)
        # Re-add rubberband, that just got deleted
        self.addRubberBand()
    def fixScaleFactor(self):
        pass
    def updateTheme(self):
        pass
    def zoom_fit(self):
        pass
    def zoom_in(self):
        pass
    def zoom_out(self):
        pass
    def zoom_reset(self):
        pass
    @pyqtSlot()
    def slot_selectionChanged(self):
        items_list = self.selectedItems()
        if len(items_list) == 0:
            self.pluginSelected.emit([])
            return
        plugin_list = []
        for item in items_list:
            if item and item.isVisible():
                group_item = None
                if item.type() == CanvasBoxType:
                    group_item = item
                elif item.type() == CanvasPortType:
                    group_item = item.parentItem()
                #elif item.type() in (CanvasLineType, CanvasBezierLineType, CanvasLineMovType, CanvasBezierLineMovType):
                    #plugin_list = []
                    #break
                if group_item is not None and group_item.m_plugin_id >= 0:
                    plugin_list.append(group_item.m_plugin_id)
        self.pluginSelected.emit(plugin_list)
 |