Source code for ccpn.ui.gui.widgets.ViewBox

"""
By Functionality:

Zoom and pan:
    Left-drag:                          pans the spectrum.

    shift-left-drag:                    draws a zooming box and zooms the viewbox.
    shift-middle-drag:                  draws a zooming box and zooms the viewbox.
    shift-right-drag:                   draws a zooming box and zooms the viewbox.
    Two successive shift-right-clicks:  define zoombox
    control-right click:                reset the zoom

Peaks:
    Left-click:                         select peak near cursor in a spectrum display, deselecting others
    Control(Cmd)-left-click:            (de)select peak near cursor in a spectrum display, adding/removing to selection.
    Control(Cmd)-left-drag:             selects peaks in an area specified by the dragged region.
    Middle-drag:                        Moves a selected peak.
    Control(Cmd)-Shift-Left-click:      picks a peak at the cursor position, adding to selection
    Control(Cmd)-shift-left-drag:       picks peaks in an area specified by the dragged region.

Others:
    Right-click:                        raises the context menu.


By Mouse button:

    Left-click:                         select peak near cursor in a spectrum display, deselecting others
    Control(Cmd)-left-click:            (de)select peak near cursor in a spectrum display, adding/removing to selection.
    Control(Cmd)-Shift-Left-click:      picks a peak at the cursor position, adding to selection

    Left-drag:                          pans the spectrum.
    shift-left-drag:                    draws a zooming box and zooms the viewbox.
    Control(Cmd)-left-drag:             selects peaks in an area specified by the dragged region.
    Control(Cmd)-shift-left-drag:       picks peaks in an area specified by the dragged region.


    shift-middle-drag:                  draws a zooming box and zooms the viewbox.

    Right-click:                        raises the context menu.
    control-right click:                reset the zoom
    Two successive shift-right-clicks:  define zoombox

    shift-right-drag:                   draws a zooming box and zooms the viewbox.
"""
#=========================================================================================
# Licence, Reference and Credits
#=========================================================================================
__copyright__ = "Copyright (C) CCPN project (http://www.ccpn.ac.uk) 2014 - 2021"
__credits__ = ("Ed Brooksbank, Joanna Fox, Victoria A Higman, Luca Mureddu, Eliza Płoskoń",
               "Timothy J Ragan, Brian O Smith, Gary S Thompson & Geerten W Vuister")
__licence__ = ("CCPN licence. See http://www.ccpn.ac.uk/v3-software/downloads/license")
__reference__ = ("Skinner, S.P., Fogh, R.H., Boucher, W., Ragan, T.J., Mureddu, L.G., & Vuister, G.W.",
                 "CcpNmr AnalysisAssign: a flexible platform for integrated NMR analysis",
                 "J.Biomol.Nmr (2016), 66, 111-124, http://doi.org/10.1007/s10858-016-0060-y")
#=========================================================================================
# Last code modification
#=========================================================================================
__modifiedBy__ = "$modifiedBy: Geerten Vuister $"
__dateModified__ = "$dateModified: 2021-12-23 11:27:19 +0000 (Thu, December 23, 2021) $"
__version__ = "$Revision: 3.0.4 $"
#=========================================================================================
# Created
#=========================================================================================
__author__ = "$Author: Wayne Boucher $"
__date__ = "$Date: 2017-03-22 15:13:45 +0000 (Wed, March 22, 2017) $"
#=========================================================================================
# Start of code
#=========================================================================================

import sys
import pyqtgraph as pg
from PyQt5 import QtCore, QtGui, QtWidgets
from pyqtgraph.Point import Point
from ccpn.util import Common as commonUtil
import numpy as np

from ccpn.core.PeakList import PeakList
from ccpn.ui.gui.widgets.Menu import Menu

from ccpn.util.Logging import getLogger

# GWV: moved to ccpn.ui.gui.lib.mouseEvents on 17/04/2017
from ccpn.ui.gui.lib.mouseEvents import \
    leftMouse, shiftLeftMouse, controlLeftMouse, controlShiftLeftMouse, \
    middleMouse, shiftMiddleMouse, controlMiddleMouse, controlShiftMiddleMouse, \
    rightMouse, shiftRightMouse, controlRightMouse, controlShiftRightMouse, PICK, SELECT
from ccpn.ui.gui.widgets.LinearRegionsPlot import LinearRegionsPlot
from ccpn.ui.gui.lib.mouseEvents import MouseModes, getCurrentMouseMode
from ccpn.core.lib.ContextManagers import undoBlockWithoutSideBar


[docs]class CrossHair: "class to implement a cross-hair" def __init__(self, plotWidget, position=Point(0, 0), show=True, rgb=None, colour=None, moveable=False, **kwds): """CrossHair init, stetting color, using rgb or colour, which-ever is not None (default to grey) + optional **kwds setting visibility it adds CrossHair hLine and vLine plot items to parent using addItem method """ self.plotWidget = plotWidget if rgb: pen = pg.functions.mkPen(color=rgb, **kwds) elif colour: pen = pg.functions.mkPen(color=rgb, **kwds) else: pen = pg.functions.mkPen(color=(129, 129, 129), **kwds) self.vLine = pg.InfiniteLine(angle=90, movable=moveable, pen=pen) self.hLine = pg.InfiniteLine(angle=0, movable=moveable, pen=pen) plotWidget.addItem(self.vLine, ignoreBounds=True) plotWidget.addItem(self.hLine, ignoreBounds=True) # ejb - set the Z value to 1.0 to bring to the top self.vLine.setZValue(1.0) self.hLine.setZValue(1.0) self.setPointPosition(position) if show: self.show() else: self.hide()
[docs] def setPosition(self, xPos: float, yPos: float): "Set position in world xPos, yPos coordinates" self.setVline(xPos) self.setHline(yPos)
[docs] def setVline(self, xPos: float): "Set vertical line in world xPos coordinates" if xPos is not None: self.vLine.setPos(xPos)
[docs] def setHline(self, yPos: float): "Set horizontal in world xPos coordinates" if yPos is not None: self.hLine.setPos(yPos)
[docs] def setPointPosition(self, position: QtCore.QPointF): "Set position in Point syntax" self.setPosition(position.x(), position.y())
[docs] def show(self): #print(">> show") self.vLine.show() self.hLine.show()
[docs] def hide(self): #print(">> hide") self.vLine.hide() self.hLine.hide()
[docs] def isVisible(self): return self.vLine.isVisible()
[docs] def toggle(self): if self.isVisible(): self.hide() else: self.show()
[docs]class ViewBox(pg.ViewBox): """ Base-class to implement mouse and drag events in PlotWidget.py; it will inherit the same parent as PlotWidget """ def __init__(self, strip): pg.ViewBox.__init__(self) # Override pyqtgraph ViewBoxMenu self.menu = self._getMenu() # built in GuiStrip, GuiStripNd, GuiStrip1D self.strip = strip self.application = self.current = strip.spectrumDisplay.mainWindow.application self.current = self.application.current self.preferences = self.application.preferences self.mainWindow = strip.spectrumDisplay.mainWindow self._setMouseCursor() # self.rbScaleBox: Native PyQtGraph; used for Zoom self.selectionBox = QtWidgets.QGraphicsRectItem(0, 0, 1, 1) self.selectionBox.setPen(pg.functions.mkPen((255, 0, 255), width=1)) self.selectionBox.setBrush(pg.functions.mkBrush(255, 100, 255, 100)) self.selectionBox.setZValue(1e9) self.selectionBox.hide() self.addItem(self.selectionBox, ignoreBounds=True) self.pickBox = QtWidgets.QGraphicsRectItem(0, 0, 1, 1) self.pickBox.setPen(pg.functions.mkPen((0, 255, 255), width=1)) self.pickBox.setBrush(pg.functions.mkBrush(100, 255, 255, 100)) self.pickBox.setZValue(1e9) self.pickBox.hide() self.addItem(self.pickBox, ignoreBounds=True) self.mouseClickEvent = self._mouseClickEvent self.mouseDragEvent = self._mouseDragEvent self.hoverEvent = self._hoverEvent self.state['wheelScaleFactor'] = -1.0 / 20.0 #speed of the wheel event. the larger the denominator the slower the zoom self._successiveClicks = None # GWV: Store successive click events for zooming; None means first click not set self.crossHair = CrossHair(self, show=False, rgb=(255, 255, 0), dash=[20.0, 7.0]) # dashes in pixels, [on, off] # not needed anymore # self.integralRegions = LinearRegionsPlot(values=[0, 0], orientation='v', bounds=None, # brush=None, colour='purple', movable=True) # for line in self.integralRegions.lines: # line.sigPositionChanged.connect(self._integralRegionsMoved) # TODO:ED smaller cross for now, need to scale for ppm self.peakWidthPixels = 16 # for ND peaks self.contextMenuPosition = None #we need this because current.position is not always the best choice for everything! def _raiseContextMenu(self, event: QtGui.QMouseEvent): """ Raise the context menu """ position = event.screenPos() self.menu.popup(QtCore.QPoint(position.x(), position.y())) self.contextMenuPosition = self.current.cursorPosition def _getMenu(self): if self.menu is None: self.menu = Menu('', self.parent(), isFloatWidget=True) return self.menu def _setMouseCursor(self): # if self.application.ui.mainWindow.mouseMode == PICK: if getCurrentMouseMode() == PICK: cursor = QtGui.QCursor(QtCore.Qt.CrossCursor) self.setCursor(cursor) else: self.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor)) def _pickAtMousePosition(self, event): '' event.accept() self._resetBoxes() mousePosition = self.mapSceneToView(event.pos()) position = [mousePosition.x(), mousePosition.y()] orderedAxes = self.current.strip.orderedAxes for orderedAxis in orderedAxes[2:]: position.append(orderedAxis.position) newPeaks = self.current.strip.createPeak(position) self.current.peaks = newPeaks def _mouseClickEvent(self, event: QtGui.QMouseEvent, axis=None): """ Re-implementation of PyQtGraph mouse click event to allow custom actions for different mouse click events. """ self.current.strip = self.strip xPosition = self.mapSceneToView(event.pos()).x() yPosition = self.mapSceneToView(event.pos()).y() self.current.positions = [xPosition, yPosition] # This is the correct future style for cursorPosition handling self.current.cursorPosition = (xPosition, yPosition) # if self.application.ui.mainWindow.mouseMode == PICK: if getCurrentMouseMode() == PICK: self._pickAtMousePosition(event) if controlShiftLeftMouse(event): # Control-Shift-left-click: pick peak self._pickAtMousePosition(event) # peaks = list(self.current.peaks) # peakLists = [] # # for spectrumView in self.current.strip.spectrumViews: # if not spectrumView.peakListViews: # continue # peakListView = spectrumView.peakListViews[0] # TODO: is there some way of specifying which peakListView # if not peakListView.isVisible(): # continue # peakList = peakListView.peakList # peak = peakList.newPeak(ppmPositions=position) # # note, the height below is not derived from any fitting # # but is a weighted average of the values at the neighbouring grid points # peak.height = spectrumView.spectrum.getPositionValue(peak.pointPositions) # #self.current.addPeak(peak) # # peak.isSelected = True # peaks.append(peak) # peakLists.append(peakList) # # self.current.peaks = peaks # for peakList in peakLists: # self.current.strip.showPeaks(peakList) elif controlLeftMouse(event): # Control-left-click; (de-)select peak and add/remove to selection event.accept() self._resetBoxes() # self._deselectPeaks() #self.current.clearPeaks() self._selectPeak(xPosition, yPosition) elif leftMouse(event): # Left-click; select peak, deselecting others event.accept() self._resetBoxes() # self._deselectPeaks() self.current.clearPeaks() self.current.clearIntegrals() self._clearIntegralRegions() self._selectPeak(xPosition, yPosition) elif shiftRightMouse(event): # Two successive shift-right-clicks: define zoombox event.accept() if self._successiveClicks is None: self._resetBoxes() self._successiveClicks = Point(event.pos()) position = self.mapSceneToView(event.pos()) self.crossHair.setPointPosition(position) self.crossHair.show() else: self._setView(Point(self._successiveClicks), Point(event.pos())) self._resetBoxes() self._successiveClicks = None elif rightMouse(event) and axis is None: # right click on canvas, not the axes event.accept() self._resetBoxes() self._raiseContextMenu(event) elif controlRightMouse(event) and axis is None: # control-right-mouse click: reset the zoom event.accept() self._resetBoxes() self.current.strip.resetZoom() else: # reset and hide all for all other clicks self._resetBoxes() event.ignore() # def _deselectPeaks(self): # "Deselected all current peaks" # # NBNB TBD FIXME. We need to deselect ALL peaks # for spectrumView in self.current.strip.spectrumViews: # for peakList in spectrumView.spectrum.peakLists: # for peak in peakList.peaks: # peak.isSelected = False # # # NBNB TBD FIXME this isSelected stuff is a dogs breakfast and needs refactoring. # # Probably we should replace 'peak.isSelected' with 'peak in current.peaks' # # Meanwhile at least deselect current peaks # for peak in self.current.peaks: # peak.isSelected = False # # self.current.clearPeaks() def _selectPeak(self, xPosition, yPosition): """(de-)Select first peak near cursor xPosition, yPosition if peak already was selected, de-select it """ xPeakWidth = abs(self.mapSceneToView(QtCore.QPoint(self.peakWidthPixels, 0)).x() - self.mapSceneToView(QtCore.QPoint(0, 0)).x()) yPeakWidth = abs(self.mapSceneToView(QtCore.QPoint(0, self.peakWidthPixels)).y() - self.mapSceneToView(QtCore.QPoint(0, 0)).y()) xPositions = [xPosition - 0.5 * xPeakWidth, xPosition + 0.5 * xPeakWidth] yPositions = [yPosition - 0.5 * yPeakWidth, yPosition + 0.5 * yPeakWidth] if len(self.current.strip.orderedAxes) > 2: # NBNB TBD FIXME what about 4D peaks? zPositions = self.current.strip.orderedAxes[2].region else: zPositions = None # now select (take first one within range) for spectrumView in self.current.strip.spectrumViews: if spectrumView.spectrum.dimensionCount == 1: continue for peakListView in spectrumView.peakListViews: if peakListView.isDisplayed: # ejb - strange error AttributeError: 'IntegralList' object has no attribute 'peaks' if peakListView.peakList and hasattr(peakListView.peakList, 'peaks'): for peak in peakListView.peakList.peaks: if (xPositions[0] < float(peak.position[0]) < xPositions[1] and yPositions[0] < float(peak.position[1]) < yPositions[1]): if zPositions is None or (zPositions[0] < float(peak.position[2]) < zPositions[1]): #print(">>found peak", peak, peak.isSelected, peak in self.current.peaks) if peak in self.current.peaks: self.current._peaks.remove(peak) else: self.current.addPeak(peak) break def _resetBoxes(self): "Reset/Hide the boxes " self._successiveClicks = None self.selectionBox.hide() self.pickBox.hide() self.rbScaleBox.hide() self.crossHair.hide() def _hoverEvent(self, event): if hasattr(event, '_scenePos'): position = self.mapSceneToView(event.pos()) if self.strip and hasattr(self.strip, 'spectrumDisplay'): self.mainWindow._mousePositionMoved(self.strip, position) def _updateSelectionBox(self, p1: float, p2: float): """ Updates drawing of selection box as mouse is moved. """ r = QtCore.QRectF(p1, p2) r = self.childGroup.mapRectFromParent(r) self.selectionBox.setPos(r.topLeft()) self.selectionBox.resetTransform() self.selectionBox.scale(r.width(), r.height()) self.selectionBox.show() def _updatePickBox(self, p1: float, p2: float): """ Updates drawing of selection box as mouse is moved. """ r = QtCore.QRectF(p1, p2) r = self.childGroup.mapRectFromParent(r) self.pickBox.setPos(r.topLeft()) self.pickBox.resetTransform() self.pickBox.scale(r.width(), r.height()) self.pickBox.show() def _mouseDragEvent(self, event: QtGui.QMouseEvent, axis=None): """ Re-implementation of PyQtGraph mouse drag event to allow custom actions off of different mouse drag events. """ self.current.strip = self.strip if leftMouse(event): # Left-drag: Panning of the spectrum pg.ViewBox.mouseDragEvent(self, event) elif controlShiftLeftMouse(event): # Control(Cmd)+shift+left drag: Peak-picking event.accept() if not event.isFinish(): # not isFinish() is not the same as isStart() in behavior; The latter will fire for every move, the former only # at the end of the move self._resetBoxes() self._updatePickBox(event.buttonDownPos(), event.pos()) else: self._resetBoxes() startPosition = self.mapSceneToView(event.buttonDownPos()) endPosition = self.mapSceneToView(event.pos()) orderedAxes = self.current.strip.orderedAxes selectedRegion = [[round(startPosition.x(), 3), round(endPosition.x(), 3)], [round(startPosition.y(), 3), round(endPosition.y(), 3)]] if len(orderedAxes) > 2: for n in orderedAxes[2:]: selectedRegion.append((n.region[0], n.region[1])) # TBD: Should be using onceOnly=True notifiers and suspend/resumeNotification but that is not working. # So instead turn off notifications (so that they all get ignored) with blankNotification, and then # at the end turn them back on again. This means have to update the relevant parts of the code which # needs to know about new peaks. This is not a good way to do it. project = self.current.strip.project project.blankNotification() try: peaks = self.current.strip.pickPeaks(selectedRegion) finally: project.unblankNotification() # hide all the messages from the peak annotation generation # project._startCommandEchoBlock('mousePeakPicking') # update strips which have the above peaks in them # (could check for visibility...) with undoBlockWithoutSideBar(): peakLists = set([peak.peakList for peak in peaks]) for peakList in peakLists: for peakListView in peakList.peakListViews: peakListView.spectrumView.strip.showPeaks(peakList) # project._endCommandEchoBlock() # update peak table # limitation: this will only update the first peak table # if hasattr(self.current.strip.spectrumDisplay.mainWindow.application, 'peakTableModule'): # self.current.strip.spectrumDisplay.mainWindow.application.peakTableModule.peakListTable._updateTable() self.current.peaks = peaks elif controlLeftMouse(event): # Control(Cmd)+left drag: selects peaks event.accept() if not event.isFinish(): # not isFinish() is not the same as isStart() in behavior; The latter will fire for every move, the former only # at the end of the move self._resetBoxes() self._updateSelectionBox(event.buttonDownPos(), event.pos()) else: self._deselectPeaksFromOtherDisplays() self._resetBoxes() endPosition = self.mapSceneToView(event.pos()) startPosition = self.mapSceneToView(event.buttonDownPos()) xPositions = sorted(list([startPosition.x(), endPosition.x()])) yPositions = sorted(list([startPosition.y(), endPosition.y()])) if len(self.current.strip.orderedAxes) > 2: zPositions = self.current.strip.orderedAxes[2].region else: zPositions = None # selectedPeaks = [] #self.current.clearPeaks() peaks = list(self.current.peaks) for spectrumView in self.current.strip.spectrumViews: for peakListView in spectrumView.peakListViews: if not peakListView.isDisplayed: continue peakList = peakListView.peakList if not isinstance(peakList, PeakList): # it could be an IntegralList continue stripAxisCodes = self.current.strip.axisOrder # TODO: Special casing 1D here, seems like a hack. if len(spectrumView.spectrum.axisCodes) == 1: y0 = startPosition.y() y1 = endPosition.y() y0, y1 = min(y0, y1), max(y0, y1) xAxis = 0 # scale = peakList.spectrum.scale # peak height now contains scale in it (so no scaling below) for peak in peakList.peaks: height = peak.height # * scale # TBD: is the scale already taken into account in peak.height??? if xPositions[0] < float(peak.position[xAxis]) < xPositions[1] and y0 < height < y1: # peak.isSelected = True #self.current.addPeak(peak) peaks.append(peak) else: # print('***', stripAxisCodes, spectrumView.spectrum.axisCodes) # Fixed 13/3/2016 Rasmus Fogh # Avoid comparing spectrum AxisCodes to display axisCodes - they are not identical spectrumIndices = spectrumView.dimensionIndices xAxis = spectrumIndices[0] yAxis = spectrumIndices[1] # axisMapping = axisCodeMapping(stripAxisCodes, spectrumView.spectrum.axisCodes) # xAxis = spectrumView.spectrum.axisCodes.index(axisMapping[self.current.strip.orderedAxes[0].code]) # yAxis = spectrumView.spectrum.axisCodes.index(axisMapping[self.current.strip.orderedAxes[1].code]) for peak in peakList.peaks: if (xPositions[0] < float(peak.position[xAxis]) < xPositions[1] and yPositions[0] < float(peak.position[yAxis]) < yPositions[1]): if zPositions is not None: zAxis = spectrumIndices[2] # zAxis = spectrumView.spectrum.axisCodes.index(axisMapping[self.current.strip.orderedAxes[2].code]) if zPositions[0] < float(peak.position[zAxis]) < zPositions[1]: # peak.isSelected = True #self.current.addPeak(peak) peaks.append(peak) else: # peak.isSelected = True #self.current.addPeak(peak) peaks.append(peak) self.current.peaks = peaks elif middleMouse(event): # middle drag: moves a selected peak event.accept() self.setMouseEnabled(False, False) refPosition = (self.mapSceneToView(event.buttonDownPos()).x(), self.mapSceneToView(event.buttonDownPos()).y()) peaks = self.current.peaks if not peaks: return deltaPosition = np.subtract(self.current.cursorPosition, refPosition) for peak in peaks: peak.startPosition = peak.position if event.isFinish(): for peak in peaks: oldPosition = peak.position peak.position = oldPosition + deltaPosition peak._finaliseAction('change') self.setMouseEnabled(True, True) app = self.strip.spectrumDisplay.mainWindow.application app.ui.echoCommands(("project.getByPid(%s).position = %s" % (peak.pid, peak.position),)) self.current.peaks = peaks else: #this is when is being dragged pass # for peak in peaks: # # print(peak.position, deltaPosition) # peak.position = (peak.position[0] + deltaPosition[0],peak.position[1] + deltaPosition[1] ) # startPoint = Point(event.buttonDownPos()) # endPoint = Point(event.pos()) # startPosition = self.childGroup.mapFromParent(startPoint) # endPosition = self.childGroup.mapFromParent(endPoint) # deltaPosition = endPosition - startPosition # deltaPosition = deltaPosition.x(), deltaPosition.y() # # project = peak.project # undo = project._undo # # if not hasattr(peak, 'startPosition'): # # start of move # project.newUndoPoint() # undo.increaseBlocking() # project.blankNotification() # self.setMouseEnabled(False,False) # # try: # self.pointer.show() # self.pointer.setPos(endPosition) # if not hasattr(peak, 'startPosition'): # peak.startPosition = peak.position # indices = peakListToIndicesDict[peak.peakList] # position = list(peak.startPosition) # for n, index in enumerate(indices): # position[index] += deltaPosition[n] # peak.position = self.current.cursorPosition # project.newUndoPoint() # # except: # undo.decreaseBlocking() # project.unblankNotification() # self.setMouseEnabled(True, True) # self.pointer.hide() # else: # if event.isFinish(): # self.pointer.hide() # undo.decreaseBlocking() # project.unblankNotification() # self.setMouseEnabled(True, True) # if hasattr(peak, 'startPosition'): # undo.newItem(setattr, setattr, undoArgs=[peak, 'position', peak.startPosition], # redoArgs=[peak, 'position', peak.position]) # delattr(peak, 'startPosition') # peak._finaliseAction('change') # self.strip.spectrumDisplay.mainWindow.application.ui.echoCommands( # ("project.getByPid(%s).position = %s" % (peak.pid, peak.position),) # ) elif shiftLeftMouse(event) or shiftMiddleMouse(event) or shiftRightMouse(event): # Middle-drag, shift-left-drag, shift-middle-drag, shift-right-drag: draws a zooming box and zooms the viewbox. event.accept() if not event.isFinish(): # not isFinish() is not the same as isStart() in behavior; The latter will fire for every move, the former only # at the end of the move self._resetBoxes() self.updateScaleBox(event.buttonDownPos(), event.pos()) else: # This is the final move in the drag; change the view scale now self._resetBoxes() #self._setView(Point(event.buttonDownPos(event.button())), Point(event.pos())) self._setView(Point(event.buttonDownPos()), Point(event.pos())) ## above events remove pan abilities from plot window, ## need to re-implement them without changing mouseMode else: self._resetBoxes() event.ignore() def _deselectPeaksFromOtherDisplays(self): if self.current.peak: if self.current.strip.spectrumViews: if self.current.peak.peakList.spectrum.spectrumViews: if self.current.strip.spectrumViews[0].strip != self.current.peak.peakList.spectrum.spectrumViews[0].strip: # self._deselectPeaks() self.current.clearPeaks() getLogger().warning('Can only multi select from current strip') def _setView(self, point1, point2): ax = QtCore.QRectF(point1, point2) ax = self.childGroup.mapRectFromParent(ax) self.showAxRect(ax) # GWV: This was copied from pyqtgraph viewbox, but appears a oddly # implemented zoom stack, which could amount to a memory leak #self.axHistoryPointer += 1 #self.axHistory = self.axHistory[:self.axHistoryPointer] + [ax] ##### Action callback: Lines on plot def _showIntegralLines(self): integral = self.current.integral if integral is not None: self.integralRegions.setLines(integral.limits[0]) self.addItem(self.integralRegions) def _clearIntegralRegions(self): try: if self.integralRegions in self.addedItems: self.removeItem(self.integralRegions) except: pass def _integralRegionsMoved(self): integrals = self.current.integrals values = [] for line in self.integralRegions.lines: values.append(line.pos().x()) for integral in integrals: if integral is not None: integral.limits = [[min(values), max(values)], ]
def _peaksVisibleInStrip(peaks, strip): peakListToIndicesDict = {} for spectrumView in strip.spectrumViews: if spectrumView.spectrum.dimensionCount == 1: # skip 1D peakLists continue indices = spectrumView.dimensionIndices[:2] peakLists = [peakListView.peakList for peakListView in spectrumView.peakListViews if peakListView.isDisplayed] for peakList in peakLists: peakListToIndicesDict[peakList] = indices # TBD: strip.positions and strip.widths not kept up to sync with plotWidget, # so need to go via plotWidget for now in x, y positions = strip.positions widths = strip.widths viewRange = strip.plotWidget.viewRange() positions0 = [viewRange[0][0], viewRange[1][0]] positions1 = [viewRange[0][1], viewRange[1][1]] for n, position in enumerate(positions): if n >= 2: positions0.append(positions[n] - 0.5 * widths[n]) positions1.append(positions[n] + 0.5 * widths[n]) peaksVisible = [] for peak in peaks: if peak.peakList in peakListToIndicesDict.keys(): for n, index in enumerate(peakListToIndicesDict[peak.peakList]): if hasattr(peak, 'startPosition'): startPosition = peak.startPosition else: startPosition = peak.position peakPosition = startPosition[index] if peakPosition < positions0[n] or peakPosition > positions1[n]: break else: peaksVisible.append(peak) return peaksVisible, peakListToIndicesDict