Esempio n. 1
0
    def __init__(self, parent, cropModel, imagescene2d):
        """
        Constructs a view upon a ImageScene2D

        imagescene2d -- a ImgeScene2D instance
        """

        QGraphicsView.__init__(self, parent)
        self.setScene(imagescene2d)
        self.mousePos = QPointF(0,0)
        # FIXME: These int members shadow QWidget.x() and QWidget.y(), which can lead to confusion when debugging...
        self.x, self.y = (0,0)

        self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)

        self._isRubberBandZoom = False
        self._cursorBackup = None

        #these attributes are exposed as public properties above
        self._sliceShape  = None #2D shape of this view's shown image
        self._slices = None #number of slices that are stacked
        self._hud    = None

        self._crossHairCursor         = None
        self._sliceIntersectionMarker = None

        self._ticker = QTimer(self)
        self._ticker.timeout.connect(self._tickerEvent)
        
        #
        # Setup the Viewport for fast painting
        #
        #With these flags turned on we could handle the drawing of the
        #white background ourselves thus removing the flicker
        #when scrolling fast through the slices
        #self.viewport().setAttribute(Qt.WA_OpaquePaintEvent)
        #self.viewport().setAttribute(Qt.WA_NoSystemBackground)
        #self.viewport().setAttribute(Qt.WA_PaintOnScreen)
        #self.viewport().setAutoFillBackground(False)

        self.setViewportUpdateMode(QGraphicsView.MinimalViewportUpdate)
        #as rescaling images is slow if done in software,
        #we use Qt's built-in background caching mode so that the cached
        #image need only be blitted on the screen when we only move
        #the cursor
        self.setCacheMode(QGraphicsView.CacheBackground)
        self.setRenderHint(QPainter.Antialiasing, False)

        self._crossHairCursor = CrossHairCursor(self.scene())
        self._crossHairCursor.setZValue(99)

        self.posModel = self.scene()._posModel
        self.axis = self.scene()._along[1] - 1 # axis is 0,1,2 for X,Y,Z
        self._sliceIntersectionMarker = SliceIntersectionMarker(self.scene(), self.axis, self.posModel)
        self._sliceIntersectionMarker.setZValue(100)

        self._sliceIntersectionMarker.setVisible(True)

        self._croppingMarkers = CroppingMarkers( self.scene(), self.axis, cropModel )

        #FIXME: this should be private, but is currently used from
        #       within the image scene renderer
        self.tempImageItems = []

        self._zoomFactor = 1.0

        #for panning
        self._lastPanPoint = QPoint()
        self._dragMode = False
        self._deltaPan = QPointF(0,0)

        #FIXME: Is there are more elegant way to handle this?

        self.setMouseTracking(True)

        # invisible cursor to enable custom cursor
        self._hiddenCursor = QCursor(Qt.BlankCursor)
Esempio n. 2
0
    def __init__(self, parent, cropModel, imagescene2d):
        """
        Constructs a view upon a ImageScene2D

        imagescene2d -- a ImgeScene2D instance
        """

        QGraphicsView.__init__(self, parent)

        # We can't use OpenGL because the HUD doesn't render properly on top.
        # Maybe this will be fixed in Qt5?
        if False:
            self.setViewport(QGLWidget())
        
        self.setScene(imagescene2d)
        self.mousePos = QPointF(0,0)
        # FIXME: These int members shadow QWidget.x() and QWidget.y(), which can lead to confusion when debugging...
        self.x, self.y = (0,0)

        self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)

        self._isRubberBandZoom = False
        self._cursorBackup = None

        #these attributes are exposed as public properties above
        self._sliceShape  = None #2D shape of this view's shown image
        self._slices = None #number of slices that are stacked
        self._hud    = None

        self._crossHairCursor         = None
        self._sliceIntersectionMarker = None

        self._ticker = QTimer(self)
        self._ticker.timeout.connect(self._tickerEvent)
        
        #
        # Setup the Viewport for fast painting
        #
        #With these flags turned on we could handle the drawing of the
        #white background ourselves thus removing the flicker
        #when scrolling fast through the slices
        #self.viewport().setAttribute(Qt.WA_OpaquePaintEvent)
        #self.viewport().setAttribute(Qt.WA_NoSystemBackground)
        #self.viewport().setAttribute(Qt.WA_PaintOnScreen)
        #self.viewport().setAutoFillBackground(False)

        self.setViewportUpdateMode(QGraphicsView.MinimalViewportUpdate)
        #as rescaling images is slow if done in software,
        #we use Qt's built-in background caching mode so that the cached
        #image need only be blitted on the screen when we only move
        #the cursor
        self.setCacheMode(QGraphicsView.CacheBackground)
        self.setRenderHint(QPainter.Antialiasing, False)

        self._crossHairCursor = CrossHairCursor(self.scene())
        self._crossHairCursor.setZValue(99)

        self.posModel = self.scene()._posModel
        self.axis = self.scene()._along[1] - 1 # axis is 0,1,2 for X,Y,Z
        self._sliceIntersectionMarker = SliceIntersectionMarker(self.scene(), self.axis, self.posModel)
        self._sliceIntersectionMarker.setZValue(100)

        self._sliceIntersectionMarker.setVisible(True)

        self._croppingMarkers = CroppingMarkers( self.scene(), self.axis, cropModel )

        #FIXME: this should be private, but is currently used from
        #       within the image scene renderer
        self.tempImageItems = []

        self._zoomFactor = 1.0

        #for panning
        self._lastPanPoint = QPoint()
        self._dragMode = False
        self._deltaPan = QPointF(0,0)

        #FIXME: Is there are more elegant way to handle this?

        self.setMouseTracking(True)

        # invisible cursor to enable custom cursor
        self._hiddenCursor = QCursor(Qt.BlankCursor)
Esempio n. 3
0
class ImageView2D(QGraphicsView):
    focusChanged = pyqtSignal()

    """
    Shows a ImageScene2D to the user and allows for interactive
    scrolling, panning, zooming etc.

    """
    @property
    def sliceShape(self):
        """
        (width, height) of the scene.
        Specifying the shape is necessary to allow for correct
        scrollbars
        """
        return self._sliceShape

    @sliceShape.setter
    def sliceShape(self, s):
        self._sliceShape = s
        self.scene().dataShape = s
        self._crossHairCursor.dataShape = s
        self._sliceIntersectionMarker.dataShape = s
        self._croppingMarkers.dataShape = s

    @property
    def hud(self):
        return self._hud
    @hud.setter
    def hud(self, hud):
        """
        Sets up a heads up display at the upper left corner of the view

        hud -- a QWidget
        """
        self._hud = hud
        self.setLayout(QVBoxLayout())
        self.layout().setContentsMargins(0,0,0,0)
        self.layout().addWidget(self._hud)
        self.layout().addStretch()

        scene = self.scene()
        hud.zoomToFitButtonClicked.connect(self.fitImage)
        hud.resetZoomButtonClicked.connect(self.doScaleTo)
        hud.rotLeftButtonClicked.connect(scene._onRotateLeft)
        hud.rotRightButtonClicked.connect(scene._onRotateRight)
        hud.swapAxesButtonClicked.connect(scene._onSwapAxes)
        hud.exportButtonClicked.connect(self.exportImages)

        scene.axesChanged.connect(hud.setAxes)

    def __init__(self, parent, cropModel, imagescene2d):
        """
        Constructs a view upon a ImageScene2D

        imagescene2d -- a ImgeScene2D instance
        """

        QGraphicsView.__init__(self, parent)
        self.setScene(imagescene2d)
        self.mousePos = QPointF(0,0)
        # FIXME: These int members shadow QWidget.x() and QWidget.y(), which can lead to confusion when debugging...
        self.x, self.y = (0,0)

        self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)

        self._isRubberBandZoom = False
        self._cursorBackup = None

        #these attributes are exposed as public properties above
        self._sliceShape  = None #2D shape of this view's shown image
        self._slices = None #number of slices that are stacked
        self._hud    = None

        self._crossHairCursor         = None
        self._sliceIntersectionMarker = None

        self._ticker = QTimer(self)
        self._ticker.timeout.connect(self._tickerEvent)
        
        #
        # Setup the Viewport for fast painting
        #
        #With these flags turned on we could handle the drawing of the
        #white background ourselves thus removing the flicker
        #when scrolling fast through the slices
        #self.viewport().setAttribute(Qt.WA_OpaquePaintEvent)
        #self.viewport().setAttribute(Qt.WA_NoSystemBackground)
        #self.viewport().setAttribute(Qt.WA_PaintOnScreen)
        #self.viewport().setAutoFillBackground(False)

        self.setViewportUpdateMode(QGraphicsView.MinimalViewportUpdate)
        #as rescaling images is slow if done in software,
        #we use Qt's built-in background caching mode so that the cached
        #image need only be blitted on the screen when we only move
        #the cursor
        self.setCacheMode(QGraphicsView.CacheBackground)
        self.setRenderHint(QPainter.Antialiasing, False)

        self._crossHairCursor = CrossHairCursor(self.scene())
        self._crossHairCursor.setZValue(99)

        self.posModel = self.scene()._posModel
        self.axis = self.scene()._along[1] - 1 # axis is 0,1,2 for X,Y,Z
        self._sliceIntersectionMarker = SliceIntersectionMarker(self.scene(), self.axis, self.posModel)
        self._sliceIntersectionMarker.setZValue(100)

        self._sliceIntersectionMarker.setVisible(True)

        self._croppingMarkers = CroppingMarkers( self.scene(), self.axis, cropModel )

        #FIXME: this should be private, but is currently used from
        #       within the image scene renderer
        self.tempImageItems = []

        self._zoomFactor = 1.0

        #for panning
        self._lastPanPoint = QPoint()
        self._dragMode = False
        self._deltaPan = QPointF(0,0)

        #FIXME: Is there are more elegant way to handle this?

        self.setMouseTracking(True)

        # invisible cursor to enable custom cursor
        self._hiddenCursor = QCursor(Qt.BlankCursor)
        # For screen recording BlankCursor doesn't work
        #self.hiddenCursor = QCursor(Qt.ArrowCursor)

    def showCropLines(self, visible):
        self._croppingMarkers.setVisible(visible)

    def _cleanUp(self):
        self._ticker.stop()
        del self._ticker

    def setZoomFactor(self,zoom):
        if self._hud is not None:
            self._hud.zoomLevelIndicator.updateLevel(zoom)
        self._zoomFactor = zoom

    def indicateSlicingPositionSettled(self, settled):
        self.scene().indicateSlicingPositionSettled(settled)

    def viewportRect(self):
        """
        Return a QRectF giving the part of the scene currently displayed in this
        widget's viewport in the scene's coordinates
        """
        r =  self.mapToScene(self.viewport().geometry()).boundingRect()
        return r

    def mapScene2Data(self, pos):
        return self.scene().scene2data.map(pos)

    def mapMouseCoordinates2Data(self, pos):
        return self.mapScene2Data(self.mapToScene(pos))

    def _panning(self):
        hBar = self.horizontalScrollBar()
        vBar = self.verticalScrollBar()
        vBar.setValue(vBar.value() - self._deltaPan.y())
        if self.isRightToLeft():
            hBar.setValue(hBar.value() + self._deltaPan.x())
        else:
            hBar.setValue(hBar.value() - self._deltaPan.x())

    def _deaccelerate(self, speed, a=1, maxVal=64):
        x = self._qBound(-maxVal, speed.x(), maxVal)
        y = self._qBound(-maxVal, speed.y(), maxVal)
        ax ,ay = self._setdeaccelerateAxAy(speed.x(), speed.y(), a)
        if x > 0:
            x = max(0.0, x - a*ax)
        elif x < 0:
            x = min(0.0, x + a*ax)
        if y > 0:
            y = max(0.0, y - a*ay)
        elif y < 0:
            y = min(0.0, y + a*ay)
        return QPointF(x, y)

    def _qBound(self, minVal, current, maxVal):
        """PyQt4 does not wrap the qBound function from Qt's global namespace
           This is equivalent."""
        return max(min(current, maxVal), minVal)

    def _setdeaccelerateAxAy(self, x, y, a):
        x = abs(x)
        y = abs(y)
        if x > y:
            if y > 0:
                ax = int(x / y)
                if ax != 0:
                    return ax, 1
            else:
                return x/a, 1
        if y > x:
            if x > 0:
                ay = int(y/x)
                if ay != 0:
                    return 1, ay
            else:
                return 1, y/a
        return 1, 1

    def _tickerEvent(self):
        if self._deltaPan.x() == 0.0 and self._deltaPan.y() == 0.0 or self._dragMode == True:
            self._ticker.stop()
        else:
            self._deltaPan = self._deaccelerate(self._deltaPan)
            self._panning()
        
    def zoomOut(self):
        self.doScale(0.9)

    def zoomIn(self):
        self.doScale(1.1)

    def fitImage(self):
        self.fitInView(self.sceneRect(), Qt.KeepAspectRatio)
        width, height = self.size().width() / self.sceneRect().width(), self.height() / self.sceneRect().height()
        self.setZoomFactor(min(width, height))
    
    def exportImages(self):
        settingsDlg = WysiwygExportOptionsDlg(self)

        if (settingsDlg.exec_() == WysiwygExportOptionsDlg.Accepted):
            from volumina.widgets.wysiwygExportOptionsDlg import WysiwygExportHelper
            exportHelper = WysiwygExportHelper(self, settingsDlg)
            exportHelper.prepareExport()
            exportHelper.run()
        else:
            # user didn't click OK button -> do nothing
            return

    def centerImage(self):
        self.centerOn(self.sceneRect().width()/2 + self.sceneRect().x(), self.sceneRect().height()/2 + self.sceneRect().y())

    def toggleHud(self):
        if self._hud is not None:
            self._hud.setVisible(not self._hud.isVisible())

    def setHudVisible(self, visible):
        if self._hud is not None:
            self._hud.setVisible(visible)
            
    def hudVisible(self):
        return self._hud.isVisible()

    def focusInEvent(self, event):
        self.setStyleSheet(".QFrame {border: 2px solid white; border-radius: 4px;}")
        self.focusChanged.emit()

    def focusOutEvent(self, event):
        self.setStyleSheet(".QFrame {}")

    def changeViewPort(self,qRectf):
        self.fitInView(qRectf,mode = Qt.KeepAspectRatio)
        width, height = self.size().width() / qRectf.width(), self.height() / qRectf.height()
        self.setZoomFactor(min(width, height))

    def doScale(self, factor):
        self.setZoomFactor(self._zoomFactor * factor)
        self.scale(factor, factor)

    def doScaleTo(self, zoom=1):
        factor = ( 1 / self._zoomFactor ) * zoom
        self.setZoomFactor(zoom)
        self.scale(factor, factor)
Esempio n. 4
0
class ImageView2D(QGraphicsView):
    focusChanged = pyqtSignal()

    """
    Shows a ImageScene2D to the user and allows for interactive
    scrolling, panning, zooming etc.

    """
    @property
    def sliceShape(self):
        """
        (width, height) of the scene.
        Specifying the shape is necessary to allow for correct
        scrollbars
        """
        return self._sliceShape

    @sliceShape.setter
    def sliceShape(self, s):
        self._sliceShape = s
        self.scene().dataShape = s
        self._crossHairCursor.dataShape = s
        self._sliceIntersectionMarker.dataShape = s
        self._croppingMarkers.dataShape = s

    @property
    def hud(self):
        return self._hud
    @hud.setter
    def hud(self, hud):
        """
        Sets up a heads up display at the upper left corner of the view

        hud -- a QWidget
        """
        self._hud = hud
        self.setLayout(QVBoxLayout())
        self.layout().setContentsMargins(0,0,0,0)
        self.layout().addWidget(self._hud)
        self.layout().addStretch()

        scene = self.scene()
        hud.zoomToFitButtonClicked.connect(self.fitImage)
        hud.resetZoomButtonClicked.connect(self.doScaleTo)
        hud.rotLeftButtonClicked.connect(scene._onRotateLeft)
        hud.rotRightButtonClicked.connect(scene._onRotateRight)
        hud.swapAxesButtonClicked.connect(scene._onSwapAxes)
        hud.exportButtonClicked.connect(self.exportImages)

        scene.axesChanged.connect(hud.setAxes)

    def __init__(self, parent, cropModel, imagescene2d):
        """
        Constructs a view upon a ImageScene2D

        imagescene2d -- a ImgeScene2D instance
        """

        QGraphicsView.__init__(self, parent)

        # We can't use OpenGL because the HUD doesn't render properly on top.
        # Maybe this will be fixed in Qt5?
        if False:
            self.setViewport(QGLWidget())
        
        self.setScene(imagescene2d)
        self.mousePos = QPointF(0,0)
        # FIXME: These int members shadow QWidget.x() and QWidget.y(), which can lead to confusion when debugging...
        self.x, self.y = (0,0)

        self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)

        self._isRubberBandZoom = False
        self._cursorBackup = None

        #these attributes are exposed as public properties above
        self._sliceShape  = None #2D shape of this view's shown image
        self._slices = None #number of slices that are stacked
        self._hud    = None

        self._crossHairCursor         = None
        self._sliceIntersectionMarker = None

        self._ticker = QTimer(self)
        self._ticker.timeout.connect(self._tickerEvent)
        
        #
        # Setup the Viewport for fast painting
        #
        #With these flags turned on we could handle the drawing of the
        #white background ourselves thus removing the flicker
        #when scrolling fast through the slices
        #self.viewport().setAttribute(Qt.WA_OpaquePaintEvent)
        #self.viewport().setAttribute(Qt.WA_NoSystemBackground)
        #self.viewport().setAttribute(Qt.WA_PaintOnScreen)
        #self.viewport().setAutoFillBackground(False)

        self.setViewportUpdateMode(QGraphicsView.MinimalViewportUpdate)
        #as rescaling images is slow if done in software,
        #we use Qt's built-in background caching mode so that the cached
        #image need only be blitted on the screen when we only move
        #the cursor
        self.setCacheMode(QGraphicsView.CacheBackground)
        self.setRenderHint(QPainter.Antialiasing, False)

        self._crossHairCursor = CrossHairCursor(self.scene())
        self._crossHairCursor.setZValue(99)

        self.posModel = self.scene()._posModel
        self.axis = self.scene()._along[1] - 1 # axis is 0,1,2 for X,Y,Z
        self._sliceIntersectionMarker = SliceIntersectionMarker(self.scene(), self.axis, self.posModel)
        self._sliceIntersectionMarker.setZValue(100)

        self._sliceIntersectionMarker.setVisible(True)

        self._croppingMarkers = CroppingMarkers( self.scene(), self.axis, cropModel )

        #FIXME: this should be private, but is currently used from
        #       within the image scene renderer
        self.tempImageItems = []

        self._zoomFactor = 1.0

        #for panning
        self._lastPanPoint = QPoint()
        self._dragMode = False
        self._deltaPan = QPointF(0,0)

        #FIXME: Is there are more elegant way to handle this?

        self.setMouseTracking(True)

        # invisible cursor to enable custom cursor
        self._hiddenCursor = QCursor(Qt.BlankCursor)
        # For screen recording BlankCursor doesn't work
        #self.hiddenCursor = QCursor(Qt.ArrowCursor)

#     def paintEvent(self, event):
#         start = time.time()
#         super( ImageView2D, self ).paintEvent(event)
#         print "painting took {} ms".format( int((time.time() - start)*1000) )


    def showCropLines(self, visible):
        self._croppingMarkers.setVisible(visible)

    def _cleanUp(self):
        self._ticker.stop()
        del self._ticker

    def setZoomFactor(self,zoom):
        if self._hud is not None:
            self._hud.zoomLevelIndicator.updateLevel(zoom)
        self._zoomFactor = zoom

    def indicateSlicingPositionSettled(self, settled):
        self.scene().indicateSlicingPositionSettled(settled)

    def viewportRect(self):
        """
        Return a QRectF giving the part of the scene currently displayed in this
        widget's viewport in the scene's coordinates
        """
        r =  self.mapToScene(self.viewport().geometry()).boundingRect()
        return r

    def mapScene2Data(self, pos):
        return self.scene().scene2data.map(pos)

    def mapMouseCoordinates2Data(self, pos):
        return self.mapScene2Data(self.mapToScene(pos))

    def _panning(self):
        hBar = self.horizontalScrollBar()
        vBar = self.verticalScrollBar()
        vBar.setValue(vBar.value() - self._deltaPan.y())
        if self.isRightToLeft():
            hBar.setValue(hBar.value() + self._deltaPan.x())
        else:
            hBar.setValue(hBar.value() - self._deltaPan.x())

    def _deaccelerate(self, speed, a=1, maxVal=64):
        x = self._qBound(-maxVal, speed.x(), maxVal)
        y = self._qBound(-maxVal, speed.y(), maxVal)
        ax ,ay = self._setdeaccelerateAxAy(speed.x(), speed.y(), a)
        if x > 0:
            x = max(0.0, x - a*ax)
        elif x < 0:
            x = min(0.0, x + a*ax)
        if y > 0:
            y = max(0.0, y - a*ay)
        elif y < 0:
            y = min(0.0, y + a*ay)
        return QPointF(x, y)

    def _qBound(self, minVal, current, maxVal):
        """PyQt4 does not wrap the qBound function from Qt's global namespace
           This is equivalent."""
        return max(min(current, maxVal), minVal)

    def _setdeaccelerateAxAy(self, x, y, a):
        x = abs(x)
        y = abs(y)
        if x > y:
            if y > 0:
                ax = x // y
                if ax != 0:
                    return ax, 1
            else:
                return x/a, 1
        if y > x:
            if x > 0:
                ay = y//x
                if ay != 0:
                    return 1, ay
            else:
                return 1, y/a
        return 1, 1

    def _tickerEvent(self):
        if self._deltaPan.x() == 0.0 and self._deltaPan.y() == 0.0 or self._dragMode == True:
            self._ticker.stop()
        else:
            self._deltaPan = self._deaccelerate(self._deltaPan)
            self._panning()
        
    def zoomOut(self):
        self.doScale(0.9)

    def zoomIn(self):
        self.doScale(1.1)

    def fitImage(self):
        self.fitInView(self.sceneRect(), Qt.KeepAspectRatio)
        width, height = self.size().width() / self.sceneRect().width(), self.height() / self.sceneRect().height()
        self.setZoomFactor(min(width, height))
    
    def exportImages(self):
        settingsDlg = WysiwygExportOptionsDlg(self)

        if (settingsDlg.exec_() == WysiwygExportOptionsDlg.Accepted):
            from volumina.widgets.wysiwygExportOptionsDlg import WysiwygExportHelper
            exportHelper = WysiwygExportHelper(self, settingsDlg)
            exportHelper.prepareExport()
            exportHelper.run()
        else:
            # user didn't click OK button -> do nothing
            return

    def centerImage(self):
        self.centerOn(self.sceneRect().width()/2 + self.sceneRect().x(), self.sceneRect().height()/2 + self.sceneRect().y())

    def toggleHud(self):
        if self._hud is not None:
            self._hud.setVisible(not self._hud.isVisible())

    def setHudVisible(self, visible):
        if self._hud is not None:
            self._hud.setVisible(visible)
            
    def hudVisible(self):
        return self._hud.isVisible()

    def focusInEvent(self, event):
        self.setStyleSheet(".QFrame {border: 2px solid white; border-radius: 4px;}")
        self.focusChanged.emit()

    def focusOutEvent(self, event):
        self.setStyleSheet(".QFrame {}")

    def changeViewPort(self,qRectf):
        self.fitInView(qRectf,mode = Qt.KeepAspectRatio)
        width, height = self.size().width() / qRectf.width(), self.height() / qRectf.height()
        self.setZoomFactor(min(width, height))

    def doScale(self, factor):
        self.setZoomFactor(self._zoomFactor * factor)
        self.scale(factor, factor)

    def doScaleTo(self, zoom=1):
        factor = ( 1 / self._zoomFactor ) * zoom
        self.setZoomFactor(zoom)
        self.scale(factor, factor)