Source code for ccpn.ui.gui.lib.OpenGL.CcpnOpenGL

"""
By Functionality:

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

    shift-left-drag:                    draws a zooming box and zooms the viewing window.
    shift-middle-drag:                  draws a zooming box and zooms the viewing window.
    shift-right-drag:                   draws a zooming box and zooms the viewing window.
    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 viewing window.
    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 viewing window.

    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 viewing window.
"""
#=========================================================================================
# Licence, Reference and Credits
#=========================================================================================
__copyright__ = "Copyright (C) CCPN project (https://www.ccpn.ac.uk) 2014 - 2022"
__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 https://ccpn.ac.uk/software/licensing/")
__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: Luca Mureddu $"
__dateModified__ = "$dateModified: 2022-04-04 14:35:52 +0100 (Mon, April 04, 2022) $"
__version__ = "$Revision: 3.1.0 $"
#=========================================================================================
# Created
#=========================================================================================
__author__ = "$Author: Ed Brooksbank $"
__date__ = "$Date: 2018-12-20 13:28:13 +0000 (Thu, December 20, 2018) $"
#=========================================================================================
# Start of code
#=========================================================================================

import sys
import math
import re
import time
import numpy as np
from contextlib import contextmanager
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import QPoint, QSize, Qt, pyqtSlot
from PyQt5.QtWidgets import QApplication, QOpenGLWidget
from PyQt5.QtGui import QSurfaceFormat
from ccpn.ui.gui.lib.OpenGL.CcpnOpenGLDefs import PaintModes
from ccpn.util.Logging import getLogger
from pyqtgraph import functions as fn
from ccpn.core.PeakList import PeakList
from ccpn.core.Peak import Peak
from ccpn.core.Integral import Integral
from ccpn.core.Multiplet import Multiplet
from ccpn.ui.gui.lib.mouseEvents import getCurrentMouseMode
from ccpn.ui.gui.lib.GuiStrip import DefaultMenu, PeakMenu, IntegralMenu, \
    MultipletMenu, PhasingMenu, AxisMenu
from ccpn.ui.gui.guiSettings import CCPNGLWIDGET_BACKGROUND, CCPNGLWIDGET_FOREGROUND, CCPNGLWIDGET_PICKCOLOUR, \
    CCPNGLWIDGET_GRID, CCPNGLWIDGET_HIGHLIGHT, CCPNGLWIDGET_LABELLING, CCPNGLWIDGET_PHASETRACE, getColours, \
    CCPNGLWIDGET_HEXBACKGROUND, CCPNGLWIDGET_ZOOMAREA, CCPNGLWIDGET_PICKAREA, \
    CCPNGLWIDGET_SELECTAREA, CCPNGLWIDGET_ZOOMLINE, CCPNGLWIDGET_MOUSEMOVELINE, \
    CCPNGLWIDGET_HARDSHADE, CCPNGLWIDGET_BADAREA
import ccpn.util.Phasing as Phasing
from ccpn.ui.gui.lib.mouseEvents import \
    leftMouse, shiftLeftMouse, controlLeftMouse, controlShiftLeftMouse, controlShiftRightMouse, \
    middleMouse, shiftMiddleMouse, rightMouse, shiftRightMouse, controlRightMouse, PICK, \
    makeDragEvent

from ccpn.ui.gui.lib.OpenGL import GL, GLU, GLUT, VBO
from ccpn.ui.gui.lib.OpenGL.CcpnOpenGLNotifier import GLNotifier
from ccpn.ui.gui.lib.OpenGL.CcpnOpenGLGlobal import GLGlobalData
from ccpn.ui.gui.lib.OpenGL.CcpnOpenGLFonts import GLString
from ccpn.ui.gui.lib.OpenGL.CcpnOpenGLSimpleLabels import GLSimpleStrings
from ccpn.ui.gui.lib.OpenGL.CcpnOpenGLArrays import GLRENDERMODE_DRAW, \
    GLRENDERMODE_RESCALE, GLRENDERMODE_REBUILD, \
    GLREFRESHMODE_REBUILD, GLVertexArray
from ccpn.ui.gui.lib.OpenGL.CcpnOpenGLViewports import GLViewports
from ccpn.ui.gui.lib.OpenGL.CcpnOpenGLWidgets import GLExternalRegion, \
    GLRegion, REGION_COLOURS, GLInfiniteLine
from ccpn.ui.gui.lib.OpenGL.CcpnOpenGLMultiplet import GLmultipletNdLabelling, GLmultiplet1dLabelling
from ccpn.ui.gui.lib.OpenGL.CcpnOpenGLPeak import GLpeakNdLabelling, GLpeak1dLabelling
from ccpn.ui.gui.lib.OpenGL.CcpnOpenGLIntegral import GLintegralNdLabelling, GLintegral1dLabelling
from ccpn.ui.gui.lib.OpenGL.CcpnOpenGLExport import GLExporter
import ccpn.ui.gui.lib.OpenGL.CcpnOpenGLDefs as GLDefs
from ccpn.util.Common import makeIterableList
from ccpn.core.lib.AxisCodeLib import getAxisCodeMatchIndices
from typing import Tuple
from ccpn.util.Constants import AXIS_FULLATOMNAME, AXIS_MATCHATOMTYPE, AXIS_ACTIVEAXES, \
    DOUBLEAXIS_ACTIVEAXES, DOUBLEAXIS_FULLATOMNAME, DOUBLEAXIS_MATCHATOMTYPE, MOUSEDICTSTRIP
from ccpn.ui.gui.widgets.DropBase import DropBase
from ccpn.ui.gui.lib.mouseEvents import getMouseEventDict
from ccpn.core.lib.ContextManagers import undoBlockWithoutSideBar, notificationEchoBlocking
from ccpn.core.lib.Notifiers import Notifier
from ccpn.core.lib import Pid
from ccpn.ui.gui.lib.GuiStripContextMenus import _hidePeaksSingleActionItems, _setEnabledAllItems


UNITS_PPM = 'ppm'
UNITS_HZ = 'Hz'
UNITS_POINT = 'point'
UNITS = [UNITS_PPM, UNITS_HZ, UNITS_POINT]

ZOOMTIMERDELAY = 1
ZOOMMAXSTORE = 1
ZOOMHISTORYSTORE = 10

STRINGOFFSET = -2
removeTrailingZero = re.compile(r'^(\d*[\d.]*?)\.?0*$')

PEAKSELECT = Peak._pluralLinkName
INTEGRALSELECT = Integral._pluralLinkName
MULTIPLETSELECT = Multiplet._pluralLinkName
SELECTOBJECTS = [PEAKSELECT, INTEGRALSELECT, MULTIPLETSELECT]

CURSOR_SOURCE_NONE = None
CURSOR_SOURCE_SELF = 'self'
CURSOR_SOURCE_OTHER = 'other'


[docs]class CcpnGLWidget(QOpenGLWidget): """Widget to handle all visible spectra/peaks/integrals/multiplets """ AXIS_MARGINRIGHT = 50 AXIS_MARGINBOTTOM = 25 AXIS_LINE = 7 AXIS_OFFSET = 3 AXIS_INSIDE = False YAXISUSEEFORMAT = False INVERTXAXIS = True INVERTYAXIS = True AXISLOCKEDBUTTON = True AXISLOCKEDBUTTONALLSTRIPS = True SPECTRUMXZOOM = 1.0e1 SPECTRUMYZOOM = 1.0e1 SHOWSPECTRUMONPHASING = True XAXES = GLDefs.XAXISUNITS YAXES = GLDefs.YAXISUNITS AXIS_MOUSEYOFFSET = AXIS_MARGINBOTTOM + (0 if AXIS_INSIDE else AXIS_LINE) def __init__(self, strip=None, mainWindow=None, stripIDLabel=None, antiAlias=4): # add a flag so that scaling cannot be done until the gl attributes are initialised self.glReady = False super().__init__(strip) # trying to get VAOs to work on MacOS - might just give up :| # GLUT.glutInitContextVersion(3, 0) # GLUT.glutInitContextProfile(GLUT.GLUT_CORE_PROFILE) # GST add antiAliasing, no perceptible speed impact on my mac (intel iris graphics!) # samples = 4 is good enough but 8 also works well in terms of speed... try: fmt = QSurfaceFormat() fmt.setSamples(antiAlias) self.setFormat(fmt) self.setUpdateBehavior(QtWidgets.QOpenGLWidget.PartialUpdate) samples = self.format().samples() # GST a use for the walrus if samples != antiAlias: getLogger().warning('hardware changed antialias level, expected %i got %i...' % (samples, antiAlias)) except Exception as es: getLogger().warning('error during anti aliasing setup %s, anti aliasing disabled...' % str(es)) # flag to display paintGL but keep an empty screen self._blankDisplay = False self.setAutoFillBackground(False) if not strip: # don't initialise if nothing there return self.strip = strip self.spectrumDisplay = strip.spectrumDisplay self.mainWindow = mainWindow if mainWindow: self.application = mainWindow.application self.project = mainWindow.application.project self.current = mainWindow.application.current else: self.application = None self.project = None self.current = None self._preferences = self.application.preferences.general self.globalGL = None self.stripIDLabel = stripIDLabel if stripIDLabel else '' self.setMouseTracking(True) # generate mouse events when button not pressed # always respond to mouse events self.setFocusPolicy(Qt.StrongFocus) # initialise all attributes self._initialiseAll() # set a minimum size so that the strips resize nicely self.setMinimumSize(self.AXIS_MARGINRIGHT + 10, self.AXIS_MARGINBOTTOM + 10) # initialise the pyqtsignal notifier self.GLSignals = GLNotifier(parent=self, strip=strip) self.lastPixelRatio = None def _initialiseAll(self): """Initialise all attributes for the display """ # if self.glReady: return self.w = self.width() self.h = self.height() self._threads = {} self._threadUpdate = False self.lastPos = QPoint() self._mouseX = 0 self._mouseY = 0 self._mouseStart = (0.0, 0.0) self._mouseEnd = (0.0, 0.0) self.pixelX = 1.0 self.pixelY = 1.0 self.deltaX = 1.0 self.deltaY = 1.0 self.symbolX = 1.0 self.symbolY = 1.0 self.peakWidthPixels = 16 # set initial axis limits - should be changed by strip.display.. self.axisL = -1.0 self.axisR = 1.0 self.axisT = 1.0 self.axisB = -1.0 self.storedZooms = [] self._currentZoom = 0 self._zoomHistory = [None] * ZOOMHISTORYSTORE self._zoomHistoryCurrent = 0 self._zoomHistoryHead = 0 self._zoomTimerLast = time.time() self.base = None self.spectrumValues = [] self.highlighted = False self._drawSelectionBox = False self._drawMouseMoveLine = False self._drawDeltaOffset = False self._selectionMode = 0 self._startCoordinate = None self._endCoordinate = None self.cursorSource = CURSOR_SOURCE_NONE # can be CURSOR_SOURCE_NONE / CURSOR_SOURCE_SELF / CURSOR_SOURCE_OTHER self.cursorCoordinate = np.zeros((4,), dtype=np.float32) self.doubleCursorCoordinate = np.zeros((4,), dtype=np.float32) self._shift = False self._command = False self._lastClick = None self._mousePressed = False self._draggingLabel = False self._lastTimeClicked = time.time_ns() // 1e6 self._clickInterval = QtWidgets.QApplication.instance().doubleClickInterval() self.buildMarks = True self._marksList = None self._infiniteLines = [] self._regionList = None self._orderedAxes = None self._axisOrder = None self._axisCodes = None self._refreshMouse = False self._successiveClicks = None # GWV: Store successive click events for zooming; None means first click not set self._dottedCursorCoordinate = None self._dottedCursorVisible = None self._spectrumBordersVisible = True self.gridList = [] self._gridVisible = True #self._preferences.showGrid self._crosshairVisible = True #self._preferences.showCrosshair self._doubleCrosshairVisible = True #self._preferences.showDoubleCrosshair self._sideBandsVisible = True self.diagonalGLList = None self.diagonalSideBandsGLList = None self.boundingBoxes = None self._updateAxes = True self.axesChanged = False self.axisLabelling = {'0': [], '1': []} self._axesVisible = True self._aspectRatios = {} self._lockedAspectRatios = {} self._fixedAspectX = 1.0 self._fixedAspectY = 1.0 self._showSpectraOnPhasing = False self._xUnits = 0 self._yUnits = 0 self.modeDecimal = [False, False] # here for completeness, although they should be updated in rescale self._currentView = GLDefs.MAINVIEW self._currentRightAxisView = GLDefs.RIGHTAXIS self._currentRightAxisBarView = GLDefs.RIGHTAXISBAR self._currentBottomAxisView = GLDefs.BOTTOMAXIS self._currentBottomAxisBarView = GLDefs.BOTTOMAXISBAR self._oldStripIDLabel = None self.stripIDString = None self._spectrumSettings = {} self._newStripID = False self._lockedStringFalse = None self._lockedStringTrue = None self._fixedStringFalse = None self._fixedStringTrue = None self._setColourScheme() self._updateHTrace = False self._updateVTrace = False self._lastTracePoint = {} # [-1, -1] self.showActivePhaseTrace = True self._applyXLimit = True #.zoomXLimitApply self._applyYLimit = True #self._preferences.zoomYLimitApply self._intensityLimit = True #self._preferences.intensityLimit self._GLIntegralLists = {} self._GLIntegralLabels = {} self._marksAxisCodes = [] self._regions = [] self._infiniteLines = [] self._buildTextFlag = True # define a new class holding the entire peaklist symbols and labelling if self.is1D: self._drawRightAxis = True self._drawBottomAxis = True self._fullHeightRightAxis = True self._fullWidthBottomAxis = True self._GLPeaks = GLpeak1dLabelling(parent=self, strip=self.strip, name='peaks', enableResize=True) self._GLIntegrals = GLintegral1dLabelling(parent=self, strip=self.strip, name='integrals', enableResize=True) self._GLMultiplets = GLmultiplet1dLabelling(parent=self, strip=self.strip, name='multiplets', enableResize=True) else: self._drawRightAxis = True self._drawBottomAxis = True self._fullHeightRightAxis = True self._fullWidthBottomAxis = True self._GLPeaks = GLpeakNdLabelling(parent=self, strip=self.strip, name='peaks', enableResize=True) self._GLIntegrals = GLintegralNdLabelling(parent=self, strip=self.strip, name='integrals', enableResize=True) self._GLMultiplets = GLmultipletNdLabelling(parent=self, strip=self.strip, name='multiplets', enableResize=True) self._buildMouse = True self._mouseCoords = [-1.0, -1.0] self.mouseString = None # self.diffMouseString = None self._symbolLabelling = 0 self._symbolType = 0 self._symbolSize = 0 self._symbolThickness = 0 self._contourThickness = 0 self._aliasEnabled = True self._aliasShade = 0.0 self._aliasLabelsEnabled = True self._peakLabelsEnabled = True self._multipletLabelsEnabled = True self._contourList = {} self._hTraces = {} self._vTraces = {} self._staticHTraces = [] self._staticVTraces = [] self._currentTraces = [] self._axisXLabelling = [] self._axisYLabelling = [] self._axisScaleLabelling = [] self._stackingValue = (0.0, 0.0) self._stackingMode = False self._hTraceVisible = False self._vTraceVisible = False self.w = 0 self.h = 0 self._uPMatrix = np.zeros((16,), dtype=np.float32) self._uMVMatrix = np.zeros((16,), dtype=np.float32) self._uVMatrix = np.zeros((16,), dtype=np.float32) self._dataMatrix = np.zeros((16,), dtype=np.float32) self._aMatrix = np.zeros((16,), dtype=np.float32) self._IMatrix = np.zeros((16,), dtype=np.float32) self._IMatrix[0:16] = [1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0] self.vInv = None self.mouseTransform = None self._useTexture = np.zeros((1,), dtype=np.int) self._axisScale = np.zeros((4,), dtype=np.float32) self._background = np.zeros((4,), dtype=np.float32) self._parameterList = np.zeros((4,), dtype=np.int32) # self._view = np.zeros((4,), dtype=np.float32) self._updateBackgroundColour = True # get information from the parent class (strip) self.orderedAxes = self.strip.orderedAxes self.axisOrder = self.strip.axisOrder self.axisCodes = self.strip.axisCodes self._dragRegions = set() self.resetRangeLimits() self._ordering = [] self._visibleOrdering = [] self._firstVisible = None self.visiblePlaneList = {} self.visiblePlaneListPointValues = {} self.visiblePlaneDimIndices = {} self._visibleSpectrumViewsChange = False self._matchingIsotopeCodes = False self._visibleOrderingDict = {} self._visibleOrderingAxisCodes = () self._tilePosition = (0, 0) self.viewports = None self._cursorFrameCounter = GLDefs.CursorFrameCounterModes.CURSOR_DEFAULT self._menuActive = False self._disableCursorUpdate = False
[docs] def close(self): self.GLSignals.glXAxisChanged.disconnect() self.GLSignals.glYAxisChanged.disconnect() self.GLSignals.glAllAxesChanged.disconnect() self.GLSignals.glMouseMoved.disconnect() self.GLSignals.glEvent.disconnect() self.GLSignals.glAxisLockChanged.disconnect() self.GLSignals.glAxisUnitsChanged.disconnect() self.GLSignals.glKeyEvent.disconnect()
[docs] def threadUpdate(self): self.update()
[docs] def update(self, mode=PaintModes.PAINT_ALL): """Update the glWidget with the correct refresh mode """ self._paintMode = mode super().update()
[docs] def rescale(self, rescaleOverlayText=True, rescaleMarksRulers=True, rescaleIntegralLists=True, rescaleRegions=True, rescaleSpectra=True, rescaleStaticHTraces=True, rescaleStaticVTraces=True, rescaleSpectrumLabels=True, rescaleLegend=True): """Change to axes of the view, axis visibility, scale and rebuild matrices when necessary to improve display speed """ if self.strip.isDeleted or not self.globalGL: return # update the shader settings - assume axis limits have changed self._resizeGL() # calculate the aspect ratios for the current screen self._lockedAspectRatios = self._aspectRatios.copy() kx = self._getValidAspectRatioKey(self.axisCodes[0]) ky = self._getValidAspectRatioKey(self.axisCodes[1]) base = self._preferences._baseAspectRatioAxisCode if kx == base: if ky != base: self._lockedAspectRatios[ky] = abs(self._lockedAspectRatios[kx] * self.pixelY / self.pixelX) elif ky == base: if kx != base: self._lockedAspectRatios[kx] = abs(self._lockedAspectRatios[ky] * self.pixelX / self.pixelY) # rescale all the items in the scene if rescaleOverlayText: self._rescaleOverlayText() if rescaleMarksRulers: self.rescaleMarksRulers() if rescaleIntegralLists: self._GLIntegrals.rescaleIntegralLists() self._GLIntegrals.rescale() if rescaleRegions: self._rescaleRegions() if rescaleSpectra: self.rescaleSpectra() if rescaleSpectrumLabels: self._spectrumLabelling.rescale() # if rescaleLegend: # self._legend.rescale() if rescaleStaticHTraces: self.rescaleStaticHTraces() if rescaleStaticVTraces: self.rescaleStaticVTraces()
[docs] def mainViewHeight(self): if self.viewports: vp = self.viewports.getViewportFromWH(self._currentView, self.w, self.h) return vp.height
[docs] def setStackingValue(self, val): self._stackingValue = val
[docs] def setStackingMode(self, value): self._stackingMode = value self.rescaleSpectra() self._spectrumLabelling.rescale() self.update()
# def setLegendMode(self, value): # self._legendMode = value # self._legend.rescale() # self.update()
[docs] def resetRangeLimits(self, allLimits=True): # reset zoom limits for the display self._minXRange, self._maxXRange = GLDefs.RANGELIMITS self._minYRange, self._maxYRange = GLDefs.RANGELIMITS self._maxX, self._minX = GLDefs.AXISLIMITS self._maxY, self._minY = GLDefs.AXISLIMITS if allLimits: self._rangeXDefined = False self._rangeYDefined = False self._minXReached = False self._minYReached = False self._maxXReached = False self._maxYReached = False self._minReached = False self._maxReached = False
[docs] def rescaleSpectra(self): if self.strip.isDeleted: return self.updateVisibleSpectrumViews() self.resetRangeLimits(allLimits=False) for stackCount, spectrumView in enumerate(self._ordering): if spectrumView.isDeleted: self._spectrumSettings[spectrumView] = {} continue self._buildSpectrumSetting(spectrumView=spectrumView, stackCount=stackCount)
[docs] def setXRegion(self, axisL=None, axisR=None): if axisL is not None: self.axisL = axisL if axisR is not None: self.axisR = axisR self._setRegion(self._orderedAxes[0], (self.axisL, self.axisR))
[docs] def setYRegion(self, axisT=None, axisB=None): if axisT is not None: self.axisT = axisT if axisB is not None: self.axisB = axisB self._setRegion(self._orderedAxes[1], (self.axisT, self.axisB))
def _setRegion(self, axisObject, value): """Set the region attribute in the axis object""" # self.strip.project._undo.increaseBlocking() undo = self.mainWindow.application._getUndo() undo.increaseBlocking() if axisObject: axisObject.region = value # self.strip.project._undo.decreaseBlocking() undo.decreaseBlocking()
[docs] def autoRange(self): self._updateVisibleSpectrumViews() for spectrumView in self._ordering: if spectrumView.isDeleted: self._spectrumSettings[spectrumView] = {} continue self._buildSpectrumSetting(spectrumView) if self.INVERTXAXIS: self.setXRegion(float(self._maxX), float(self._minX)) else: self.setXRegion(float(self._minX), float(self._maxX)) if self.INVERTYAXIS: self.setYRegion(float(self._minY), float(self._maxY)) else: self.setYRegion(float(self._maxY), float(self._minY)) self.update()
[docs] def refreshDevicePixelRatio(self): """refresh the devicePixelRatio for the viewports """ # control for changing screens has now been moved to mainWindow so only one signal is needed # GST this most probably ought to be deferred until the drag completes... # possibly via an event... newPixelRatio = self.devicePixelRatioF() if newPixelRatio != self.lastPixelRatio: self.lastPixelRatio = newPixelRatio if hasattr(self, GLDefs.VIEWPORTSATTRIB) and self.viewports: self.viewports.devicePixelRatio = newPixelRatio self.buildOverlayStrings() for spectrumView in self._ordering: for listView in spectrumView.peakListViews: listView.buildLabels = True for listView in spectrumView.integralListViews: listView.buildLabels = True for listView in spectrumView.multipletListViews: listView.buildLabels = True self.buildMarks = True self.update()
def _getValidAspectRatio(self, axisCode): va = [ax for ax in self._aspectRatios.keys() if ax.upper()[0] == axisCode.upper()[0]] if va and len(va) > 0: return self._aspectRatios[va[0]] else: return 1.0 def _getValidAspectRatioKey(self, axisCode): """Get the valid key from the axis ratios dict - valid for _aspectRatios and _lockedAspectRatios """ va = [ax for ax in self._aspectRatios.keys() if ax.upper()[0] == axisCode.upper()[0]] if va and len(va) > 0: return va[0] def _getValidLockedAspectRatio(self, axisCode): va = [ax for ax in self._lockedAspectRatios.keys() if ax.upper()[0] == axisCode.upper()[0]] if va and len(va) > 0: return abs(self._lockedAspectRatios[va[0]]) else: return 1.0
[docs] def resizeGL(self, w, h): """Resize event from the openGL architecture """ # must be set here to catch the change of screen - possibly when unplugging a monitor self.refreshDevicePixelRatio() self.w, self.h = w, h self._rescaleAllZoom()
def _resizeGL(self): """Resize - update the GL settings update viewports shader settings pixel ratios """ if not self.viewports: getLogger().debug(f'viewport not defined: {self}') return currentShader = self.globalGL._shaderProgram1.makeCurrent() # set projection to axis coordinates currentShader.setProjectionAxes(self._uPMatrix, self.axisL, self.axisR, self.axisB, self.axisT, -1.0, 1.0) currentShader.setPMatrix(self._uPMatrix) currentShader.setMVMatrix(self._IMatrix) # needs to be offset from (0, 0) for mouse scaling if self._drawRightAxis and self._drawBottomAxis: self._currentView = GLDefs.MAINVIEW self._currentRightAxisView = GLDefs.RIGHTAXIS self._currentRightAxisBarView = GLDefs.RIGHTAXISBAR self._currentBottomAxisView = GLDefs.BOTTOMAXIS self._currentBottomAxisBarView = GLDefs.BOTTOMAXISBAR elif self._drawRightAxis and not self._drawBottomAxis: self._currentView = GLDefs.MAINVIEWFULLHEIGHT self._currentRightAxisView = GLDefs.FULLRIGHTAXIS self._currentRightAxisBarView = GLDefs.FULLRIGHTAXISBAR elif not self._drawRightAxis and self._drawBottomAxis: self._currentView = GLDefs.MAINVIEWFULLWIDTH self._currentBottomAxisView = GLDefs.FULLBOTTOMAXIS self._currentBottomAxisBarView = GLDefs.FULLBOTTOMAXISBAR else: self._currentView = GLDefs.FULLVIEW vp = self.viewports.getViewportFromWH(self._currentView, self.w, self.h) vpwidth, vpheight = vp.width, vp.height currentShader.setViewportMatrix(self._uVMatrix, 0, vpwidth, 0, vpheight, -1.0, 1.0) self.pixelX = (self.axisR - self.axisL) / vpwidth self.pixelY = (self.axisT - self.axisB) / vpheight self.deltaX = 1.0 / vpwidth self.deltaY = 1.0 / vpheight self.symbolX = abs(self._symbolSize * self.pixelX) self.symbolY = abs(self._symbolSize * self.pixelY) currentShader.setMVMatrix(self._IMatrix) # map mouse coordinates to world coordinates - only needs to change on resize, move soon currentShader.setViewportMatrix(self._aMatrix, self.axisL, self.axisR, self.axisB, self.axisT, -1.0, 1.0) # calculate the screen to axes transform self.vInv = np.linalg.inv(self._uVMatrix.reshape((4, 4))) self.mouseTransform = np.matmul(self._aMatrix.reshape((4, 4)), self.vInv) self.modelViewMatrix = (GL.GLdouble * 16)() self.projectionMatrix = (GL.GLdouble * 16)() self.viewport = (GL.GLint * 4)() # change to the text shader currentShader = self.globalGL._shaderProgramTex.makeCurrent() currentShader.setProjectionAxes(self._uPMatrix, self.axisL, self.axisR, self.axisB, self.axisT, -1.0, 1.0) currentShader.setPTexMatrix(self._uPMatrix) self._axisScale[0:4] = [self.pixelX, self.pixelY, 1.0, 1.0] currentShader.setAxisScale(self._axisScale)
[docs] def viewRange(self): return ((self.axisL, self.axisR), (self.axisT, self.axisB))
[docs] def mainViewSize(self): """Return the width/height for the mainView of the OpenGL widget """ if self.viewports: mw = self.viewports.getViewportFromWH(self._currentView, self.w, self.h) return (mw.width, mw.height) else: return (self.w, self.h)
[docs] def wheelEvent(self, event): if self.strip and not self._ordering: # strip.spectrumViews: event.accept() return # check the movement of the wheel first numPixels = event.pixelDelta() numDegrees = event.angleDelta() zoomCentre = self._preferences.zoomCentreType # get the keyboard state keyModifiers = QApplication.keyboardModifiers() zoomScale = 0.0 scrollDirection = 0 if numPixels: # always seems to be numPixels - check with Linux # the Shift key automatically returns the x-axis scrollDirection = numPixels.x() if (keyModifiers & Qt.ShiftModifier) else numPixels.y() zoomScale = 8.0 # stop the very sensitive movements if abs(scrollDirection) < 1: event.ignore() return elif numDegrees: # this may work when using Linux scrollDirection = (numDegrees.x() / 4) if (keyModifiers & Qt.ShiftModifier) else (numDegrees.y() / 4) zoomScale = 8.0 # stop the very sensitive movements if abs(scrollDirection) < 1: event.ignore() return else: event.ignore() return if (keyModifiers & (Qt.ShiftModifier | Qt.ControlModifier)): # process wheel with buttons here # transfer event to the correct widget for changing the plane OR raising base contour level... if (keyModifiers & Qt.ShiftModifier): # raise/lower base contour level - should be strip I think if scrollDirection > 0: self.strip.spectrumDisplay.raiseContourBase() else: self.strip.spectrumDisplay.lowerContourBase() elif (keyModifiers & Qt.ControlModifier): # scroll through planes pT = self.strip.planeAxisBars if hasattr(self.strip, 'planeAxisBars') else None activePlaneAxis = self.strip.activePlaneAxis if pT and activePlaneAxis is not None and (activePlaneAxis - 2) < len(pT): # pass the event to the correct double spinbox pT[activePlaneAxis - 2].scrollPpmPosition(event) event.accept() return # test whether the limits have been reached in either axis if (scrollDirection > 0 and self._minReached and self._aspectRatioMode) or \ (scrollDirection < 0 and self._maxReached and self._aspectRatioMode): event.accept() return zoomIn = (100.0 + zoomScale) / 100.0 zoomOut = 100.0 / (100.0 + zoomScale) h = self.h w = self.w # find the correct viewport if (self._drawRightAxis and self._drawBottomAxis): mw = self.viewports.getViewportFromWH(self._currentView, w, h) ba = self.viewports.getViewportFromWH(self._currentBottomAxisBarView, w, h) ra = self.viewports.getViewportFromWH(self._currentRightAxisBarView, w, h) elif (self._drawBottomAxis): mw = self.viewports.getViewportFromWH(self._currentView, w, h) ba = self.viewports.getViewportFromWH(self._currentBottomAxisBarView, w, h) ra = (0, 0, 0, 0) elif (self._drawRightAxis): mw = self.viewports.getViewportFromWH(self._currentView, w, h) ba = (0, 0, 0, 0) ra = self.viewports.getViewportFromWH(self._currentRightAxisBarView, w, h) else: # no axes visible mw = self.viewports.getViewportFromWH(self._currentView, w, h) ba = (0, 0, 0, 0) ra = (0, 0, 0, 0) mx = event.pos().x() my = self.height() - event.pos().y() tilePos = self.strip.tilePosition if self.strip else self.tilePosition if self.between(mx, mw[0], mw[0] + mw[2]) and self.between(my, mw[1], mw[1] + mw[3]): # if in the mainView if (scrollDirection > 0 and self._minReached) or \ (scrollDirection < 0 and self._maxReached): event.accept() return if zoomCentre == 0: # centre on mouse mb0 = (mx - mw[0]) / (mw[2] - mw[0]) mb1 = (my - mw[1]) / (mw[3] - mw[1]) else: # centre on the screen mb0 = 0.5 mb1 = 0.5 mbx = self.axisL + mb0 * (self.axisR - self.axisL) mby = self.axisB + mb1 * (self.axisT - self.axisB) if scrollDirection < 0: self.axisL = mbx + zoomIn * (self.axisL - mbx) self.axisR = mbx - zoomIn * (mbx - self.axisR) self.axisB = mby + zoomIn * (self.axisB - mby) self.axisT = mby - zoomIn * (mby - self.axisT) else: self.axisL = mbx + zoomOut * (self.axisL - mbx) self.axisR = mbx - zoomOut * (mbx - self.axisR) self.axisB = mby + zoomOut * (self.axisB - mby) self.axisT = mby - zoomOut * (mby - self.axisT) self.GLSignals._emitAllAxesChanged(source=self, strip=self.strip, axisB=self.axisB, axisT=self.axisT, axisL=self.axisL, axisR=self.axisR, row=tilePos[0], column=tilePos[1], zoomAll=True) self._rescaleAllAxes() self._storeZoomHistory() elif self.between(mx, ba[0], ba[0] + ba[2]) and self.between(my, ba[1], ba[1] + ba[3]): # in the bottomAxisBar, so zoom in the X axis # check the X limits if (scrollDirection > 0 and self._minXReached) or (scrollDirection < 0 and self._maxXReached): event.accept() return if zoomCentre == 0: # centre on mouse mb = (mx - ba[0]) / (ba[2] - ba[0]) else: # centre on the screen mb = 0.5 mbx = self.axisL + mb * (self.axisR - self.axisL) if scrollDirection < 0: self.axisL = mbx + zoomIn * (self.axisL - mbx) self.axisR = mbx - zoomIn * (mbx - self.axisR) else: self.axisL = mbx + zoomOut * (self.axisL - mbx) self.axisR = mbx - zoomOut * (mbx - self.axisR) if not self._aspectRatioMode: self._rescaleXAxis() self.GLSignals._emitXAxisChanged(source=self, strip=self.strip, axisB=self.axisB, axisT=self.axisT, axisL=self.axisL, axisR=self.axisR, row=tilePos[0], column=tilePos[1], aspectRatios=self._lockedAspectRatios) self._storeZoomHistory() else: self._scaleToXAxis() self.GLSignals._emitAllAxesChanged(source=self, strip=self.strip, axisB=self.axisB, axisT=self.axisT, axisL=self.axisL, axisR=self.axisR, row=tilePos[0], column=tilePos[1]) self._storeZoomHistory() elif self.between(mx, ra[0], ra[0] + ra[2]) and self.between(my, ra[1], ra[1] + ra[3]): # in the rightAxisBar, so zoom in the Y axis # check the Y limits if (scrollDirection > 0 and self._minYReached) or (scrollDirection < 0 and self._maxYReached): event.accept() return if zoomCentre == 0: # centre on mouse mb = (my - ra[1]) / (ra[3] - ra[1]) else: # centre on the screen mb = 0.5 mby = self.axisB + mb * (self.axisT - self.axisB) if scrollDirection < 0: self.axisB = mby + zoomIn * (self.axisB - mby) self.axisT = mby - zoomIn * (mby - self.axisT) else: self.axisB = mby + zoomOut * (self.axisB - mby) self.axisT = mby - zoomOut * (mby - self.axisT) if not self._aspectRatioMode: self._rescaleYAxis() self.GLSignals._emitYAxisChanged(source=self, strip=self.strip, axisB=self.axisB, axisT=self.axisT, axisL=self.axisL, axisR=self.axisR, row=tilePos[0], column=tilePos[1], aspectRatios=self._lockedAspectRatios) self._storeZoomHistory() else: self._scaleToYAxis() self.GLSignals._emitAllAxesChanged(source=self, strip=self.strip, axisB=self.axisB, axisT=self.axisT, axisL=self.axisL, axisR=self.axisR, row=tilePos[0], column=tilePos[1]) self._storeZoomHistory() event.accept()
[docs] def emitAllAxesChanged(self, allStrips=False): """Signal all strips in the spectrumDisplay to refresh Strips will be scaled to the Y-Axis if aspect ratio is set to Locked/Fixed :param allStrips: True/False, if true, apply scaling to all strips; False, ignore the current strip in spectrumDisplay """ tilePos = self.strip.tilePosition if self.strip else self.tilePosition self.GLSignals._emitAllAxesChanged(source=None if allStrips else self, strip=None, spectrumDisplay=self.spectrumDisplay, axisB=self.axisB, axisT=self.axisT, axisL=self.axisL, axisR=self.axisR, row=tilePos[0], column=tilePos[1])
[docs] def emitYAxisChanged(self, allStrips=False, aspectRatios=None): """Signal all strips in the spectrumDisplay to refresh Strips will be scaled to the Y-Axis if aspect ratio is set to Locked/Fixed :param allStrips: True/False, if true, apply scaling to all strips; False, ignore the current strip in spectrumDisplay """ tilePos = self.strip.tilePosition if self.strip else self.tilePosition self.GLSignals._emitYAxisChanged(source=None if allStrips else self, strip=None, spectrumDisplay=self.spectrumDisplay, axisB=self.axisB, axisT=self.axisT, axisL=self.axisL, axisR=self.axisR, row=tilePos[0], column=tilePos[1], aspectRatios=aspectRatios.copy() if aspectRatios else None)
[docs] def emitXAxisChanged(self, allStrips=False, aspectRatios=None): """Signal all strips in the spectrumDisplay to refresh Strips will be scaled to the X-Axis if aspect ratio is set to Locked/Fixed :param allStrips: True/False, if true, apply scaling to all strips; False, ignore the current strip in spectrumDisplay """ tilePos = self.strip.tilePosition if self.strip else self.tilePosition self.GLSignals._emitXAxisChanged(source=None if allStrips else self, strip=None, spectrumDisplay=self.spectrumDisplay, axisB=self.axisB, axisT=self.axisT, axisL=self.axisL, axisR=self.axisR, row=tilePos[0], column=tilePos[1], aspectRatios=aspectRatios.copy() if aspectRatios else None)
def _scaleToXAxis(self, rescale=True, update=False): _useFirstDefault = getattr(self.strip.spectrumDisplay, '_useFirstDefault', False) if (self._aspectRatioMode or _useFirstDefault): if (self._aspectRatioMode == 2) or _useFirstDefault: ax0 = self._getValidAspectRatio(self._axisCodes[0]) ax1 = self._getValidAspectRatio(self._axisCodes[1]) else: try: ax0, ax1 = self.spectrumDisplay._stripAddMode except: ax0 = self.pixelX ax1 = self.pixelY if self.viewports: vp = self.viewports.getViewportFromWH(self._currentView, self.w, self.h) width = vp.width height = vp.height else: width = (self.w - self.AXIS_MARGINRIGHT) if self._drawRightAxis else self.w height = (self.h - self.AXIS_MOUSEYOFFSET) if self._drawBottomAxis else self.h ratio = (height / width) * 0.5 * abs((self.axisL - self.axisR) * ax1 / ax0) mby = 0.5 * (self.axisT + self.axisB) self.axisB = mby + ratio * self.sign(self.axisB - mby) self.axisT = mby - ratio * self.sign(mby - self.axisT) if rescale: self._rescaleAllAxes(update) else: if rescale: self._rescaleXAxis(update) def _scaleToYAxis(self, rescale=True, update=False): _useFirstDefault = getattr(self.strip.spectrumDisplay, '_useFirstDefault', False) if (self._aspectRatioMode or _useFirstDefault): if (self._aspectRatioMode == 2) or _useFirstDefault: ax0 = self._getValidAspectRatio(self._axisCodes[0]) ax1 = self._getValidAspectRatio(self._axisCodes[1]) else: # must be 1 try: ax0, ax1 = self.spectrumDisplay._stripAddMode except: ax0 = self.pixelX ax1 = self.pixelY if self.viewports: vp = self.viewports.getViewportFromWH(self._currentView, self.w, self.h) width = vp.width height = vp.height else: width = (self.w - self.AXIS_MARGINRIGHT) if self._drawRightAxis else self.w height = (self.h - self.AXIS_MOUSEYOFFSET) if self._drawBottomAxis else self.h ratio = (width / height) * 0.5 * abs((self.axisT - self.axisB) * ax0 / ax1) mbx = 0.5 * (self.axisR + self.axisL) self.axisL = mbx + ratio * self.sign(self.axisL - mbx) self.axisR = mbx - ratio * self.sign(mbx - self.axisR) if rescale: self._rescaleAllAxes() else: if rescale: self._rescaleYAxis(update) def _getSelectionBoxRatio(self, delta=(0.0, 0.0)): """Get the current deltas for the selection box and restrict to the aspectRatio if locked/fixed """ if (self._aspectRatioMode == 2): ax0 = self._getValidAspectRatio(self._axisCodes[0]) ax1 = self._getValidAspectRatio(self._axisCodes[1]) else: # must be 1 ax0 = self.pixelX ax1 = self.pixelY if self.viewports: vp = self.viewports.getViewportFromWH(self._currentView, self.w, self.h) width = vp.width height = vp.height else: width = (self.w - self.AXIS_MARGINRIGHT) if self._drawRightAxis else self.w height = (self.h - self.AXIS_MOUSEYOFFSET) if self._drawBottomAxis else self.h if width > height: dy = abs(height * delta[0] * ax1 / (ax0 * width)) * self.sign(delta[1]) return (delta[0], dy) else: dx = abs(width * delta[1] * ax0 / (ax1 * height)) * self.sign(delta[0]) return (dx, delta[1]) def _rescaleXAxis(self, rescale=True, update=True): if rescale: self._testAxisLimits() self.rescale(rescaleStaticHTraces=False) # spawn rebuild event for the grid self._updateAxes = True if self.gridList: for gr in self.gridList: gr.renderMode = GLRENDERMODE_REBUILD # ratios have changed so rescale the peak/multiplet symbols self._GLPeaks.rescale() self._GLMultiplets.rescale() self._rescaleOverlayText() self.setXRegion() if update: self.update() def _rescaleYAxis(self, rescale=True, update=True): if rescale: self._testAxisLimits() self.rescale(rescaleStaticVTraces=False) # spawn rebuild event for the grid self._updateAxes = True if self.gridList: for gr in self.gridList: gr.renderMode = GLRENDERMODE_REBUILD # ratios have changed so rescale the peak/multiplet symbols self._GLPeaks.rescale() self._GLMultiplets.rescale() self._rescaleOverlayText() self.setYRegion() if update: self.update() def _testAxisLimits(self, setLimits=False): xRange = abs(self.axisL - self.axisR) / 3.0 yRange = abs(self.axisT - self.axisB) / 3.0 self._minXReached = False self._minYReached = False self._maxXReached = False self._maxYReached = False if xRange < self._minXRange and self._rangeXDefined and self._applyXLimit: if setLimits: xMid = (self.axisR + self.axisL) / 2.0 self.axisL = xMid - self._minXRange * self.sign(self.pixelX) self.axisR = xMid + self._minXRange * self.sign(self.pixelX) self._minXReached = True if yRange < self._minYRange and self._rangeYDefined and self._applyYLimit: if setLimits: yMid = (self.axisT + self.axisB) / 2.0 self.axisT = yMid + self._minYRange * self.sign(self.pixelY) self.axisB = yMid - self._minYRange * self.sign(self.pixelY) self._minYReached = True if xRange > self._maxXRange and self._rangeXDefined and self._applyXLimit: if setLimits: xMid = (self.axisR + self.axisL) / 2.0 self.axisL = xMid - self._maxXRange * self.sign(self.pixelX) self.axisR = xMid + self._maxXRange * self.sign(self.pixelX) self._maxXReached = True if yRange > self._maxYRange and self._rangeYDefined and self._applyYLimit: if setLimits: yMid = (self.axisT + self.axisB) / 2.0 self.axisT = yMid + self._maxYRange * self.sign(self.pixelY) self.axisB = yMid - self._maxYRange * self.sign(self.pixelY) self._maxYReached = True self._minReached = self._minXReached or self._minYReached self._maxReached = self._maxXReached or self._maxYReached def _rescaleAllZoom(self, rescale=True): """Reset the zoom to fit the spectra, including aspect checking """ if self.strip.isDeleted or self.strip._flaggedForDelete: return _useFirstDefault = getattr(self.strip.spectrumDisplay, '_useFirstDefault', False) if (self._aspectRatioMode or _useFirstDefault): # check which is the primary axis and update the opposite axis - similar to wheelEvent if self.spectrumDisplay.stripArrangement == 'Y': # strips are arranged in a row self._scaleToYAxis(rescale=rescale) elif self.spectrumDisplay.stripArrangement == 'X': # strips are arranged in a column self._scaleToXAxis(rescale=rescale) elif self.spectrumDisplay.stripArrangement == 'T': # NOTE:ED - Tiled plots not fully implemented yet getLogger().warning('Tiled plots not implemented for spectrumDisplay: %s' % str(self.spectrumDisplay.pid)) else: getLogger().warning('Strip direction is not defined for spectrumDisplay: %s' % str(self.spectrumDisplay.pid)) else: self.rescale() # put stuff in here that will change on a resize self._updateAxes = True for gr in self.gridList: gr.renderMode = GLRENDERMODE_REBUILD self._GLPeaks.rescale() self._GLMultiplets.rescale() self._clearAndUpdate() self.update() def _rescaleAllAxes(self, mouseMoveOnly=False, update=True): self._testAxisLimits() self.rescale(rescaleStaticHTraces=True, rescaleStaticVTraces=True, rescaleSpectra=not mouseMoveOnly) # spawn rebuild event for the grid self._updateAxes = True if self.gridList: for gr in self.gridList: gr.renderMode = GLRENDERMODE_REBUILD if not mouseMoveOnly: # if (self._useLockedAspect or self._useDefaultAspect): # ratios have changed so rescale the peak/multiplet symbols self._GLPeaks.rescale() self._GLMultiplets.rescale() self._rescaleOverlayText() self.setXRegion() self.setYRegion() if update: self.update() def _movePeaks(self, direction: str = 'up'): """Move the peaks with the cursor keys """ # this is a bit convoluted if len(self.current.peaks) < 1: return moveFactor = 5 moveDict = { 'left' : (-self.pixelX * moveFactor, 0), 'right': (self.pixelX * moveFactor, 0), 'up' : (0, self.pixelX * moveFactor), 'down' : (0, -self.pixelX * moveFactor) } if direction in moveDict: with undoBlockWithoutSideBar(): for peak in self.current.peaks: self._movePeak(peak, moveDict.get(direction)) def _panSpectrum(self, direction, movePercent=20): """Implements Arrows up,down, left, right to pan the spectrum """ # percentage of the view to set as single step moveFactor = movePercent / 100.0 dx = (self.axisR - self.axisL) / 2.0 dy = (self.axisT - self.axisB) / 2.0 if direction == 'left': self.axisL -= moveFactor * dx self.axisR -= moveFactor * dx elif direction == 'up': self.axisT += moveFactor * dy self.axisB += moveFactor * dy elif direction == 'right': self.axisL += moveFactor * dx self.axisR += moveFactor * dx elif direction == 'down': self.axisT -= moveFactor * dy self.axisB -= moveFactor * dy elif direction == 'plus': self._testAxisLimits() if self._minReached: return self.zoomIn() elif direction == 'minus': self._testAxisLimits() if self._maxReached: return self.zoomOut() else: # not a movement key return tilePos = self.strip.tilePosition if self.strip else self.tilePosition self.GLSignals._emitAllAxesChanged(source=self, strip=self.strip, axisB=self.axisB, axisT=self.axisT, axisL=self.axisL, axisR=self.axisR, row=tilePos[0], column=tilePos[1]) self._rescaleAllAxes() self._storeZoomHistory() def _panGLSpectrum(self, key, movePercent=20): """Implements Arrows up,down, left, right to pan the spectrum """ # percentage of the view to set as single step moveFactor = movePercent / 100.0 dx = (self.axisR - self.axisL) / 2.0 dy = (self.axisT - self.axisB) / 2.0 if key == QtCore.Qt.Key_Left: self.axisL -= moveFactor * dx self.axisR -= moveFactor * dx elif key == QtCore.Qt.Key_Up: self.axisT += moveFactor * dy self.axisB += moveFactor * dy elif key == QtCore.Qt.Key_Right: self.axisL += moveFactor * dx self.axisR += moveFactor * dx elif key == QtCore.Qt.Key_Down: self.axisT -= moveFactor * dy self.axisB -= moveFactor * dy elif key == QtCore.Qt.Key_Plus or key == QtCore.Qt.Key_Equal: # Plus: self._testAxisLimits() if self._minReached: return self.zoomIn() elif key == QtCore.Qt.Key_Minus: self._testAxisLimits() if self._maxReached: return self.zoomOut() else: # not a movement key return tilePos = self.strip.tilePosition if self.strip else self.tilePosition self.GLSignals._emitAllAxesChanged(source=self, strip=self.strip, axisB=self.axisB, axisT=self.axisT, axisL=self.axisL, axisR=self.axisR, row=tilePos[0], column=tilePos[1]) self._rescaleAllAxes() self._storeZoomHistory() def _moveAxes(self, delta=(0.0, 0.0)): """Implements Arrows up,down, left, right to pan the spectrum """ # percentage of the view to set as single step self.axisL += delta[0] self.axisR += delta[0] self.axisT += delta[1] self.axisB += delta[1] tilePos = self.strip.tilePosition if self.strip else self.tilePosition self.GLSignals._emitAllAxesChanged(source=self, strip=self.strip, axisB=self.axisB, axisT=self.axisT, axisL=self.axisL, axisR=self.axisR, row=tilePos[0], column=tilePos[1]) self._rescaleAllAxes()
[docs] def initialiseAxes(self, strip=None): """setup the correct axis range and padding """ self.orderedAxes = strip.orderedAxes self._axisCodes = strip.axisCodes self._axisOrder = strip.axisOrder axis = self.orderedAxes[0] region = axis.region if self.INVERTXAXIS: self.axisL = max(region[0], region[1]) self.axisR = min(region[0], region[1]) else: self.axisL = min(region[0], region[1]) self.axisR = max(region[0], region[1]) self._xUnits = axis._unitIndex axis = self.orderedAxes[1] region = axis.region if self.INVERTYAXIS: self.axisB = max(region[0], region[1]) self.axisT = min(region[0], region[1]) else: self.axisB = min(region[0], region[1]) self.axisT = max(region[0], region[1]) if not self.spectrumDisplay.is1D: self._yUnits = axis._unitIndex self.update()
[docs] def zoom(self, xRegion: Tuple[float, float], yRegion: Tuple[float, float]): """Zooms strip to the specified region """ if self.INVERTXAXIS: self.axisL = max(xRegion[0], xRegion[1]) self.axisR = min(xRegion[0], xRegion[1]) else: self.axisL = min(xRegion[0], xRegion[1]) self.axisR = max(xRegion[0], xRegion[1]) if self.INVERTYAXIS: self.axisB = max(yRegion[0], yRegion[1]) self.axisT = min(yRegion[0], yRegion[1]) else: self.axisB = min(yRegion[0], yRegion[1]) self.axisT = max(yRegion[0], yRegion[1]) self._rescaleAllAxes()
[docs] def zoomX(self, x1: float, x2: float): """Zooms x axis of strip to the specified region """ if self.INVERTXAXIS: self.axisL = max(x1, x2) self.axisR = min(x1, x2) else: self.axisL = min(x1, x2) self.axisR = max(x1, x2) self._rescaleXAxis()
[docs] def zoomY(self, y1: float, y2: float): """Zooms y axis of strip to the specified region """ if self.INVERTYAXIS: self.axisB = max(y1, y2) self.axisT = min(y1, y2) else: self.axisB = min(y1, y2) self.axisT = max(y1, y2) self._rescaleYAxis()
[docs] def resetXZoom(self): self._resetAxisRange(xAxis=True, yAxis=False) self._zoomHistoryCurrent = self._zoomHistoryHead self._storeZoomHistory() self._rescaleXAxis()
[docs] def resetYZoom(self): self._resetAxisRange(xAxis=False, yAxis=True) self._zoomHistoryCurrent = self._zoomHistoryHead self._storeZoomHistory() self._rescaleYAxis()
[docs] def resetAllZoom(self): self._resetAxisRange(xAxis=True, yAxis=True) self._zoomHistoryCurrent = self._zoomHistoryHead self._storeZoomHistory() self._rescaleAllAxes()
def _storeZoomHistory(self): """Store the current axis state to the zoom history """ currentAxis = (self.axisL, self.axisR, self.axisB, self.axisT) # store the current value if current zoom has not been set if self._zoomHistory[self._zoomHistoryHead] is None: self._zoomHistory[self._zoomHistoryHead] = currentAxis if self._widthsChangedEnough(currentAxis, self._zoomHistory[self._zoomHistoryHead], tol=1e-8): for stored in self.storedZooms: if not self._widthsChangedEnough(currentAxis, self._zoomHistory[self._zoomHistoryHead], tol=1e-8): break else: currentTime = time.time() if currentTime - self._zoomTimerLast < ZOOMTIMERDELAY: # still on the current zoom item - write new value self._zoomHistory[self._zoomHistoryHead] = currentAxis else: # increment the head of the zoom history self._zoomHistoryHead = (self._zoomHistoryHead + 1) % len(self._zoomHistory) self._zoomHistory[self._zoomHistoryHead] = currentAxis self._zoomHistoryCurrent = self._zoomHistoryHead # reset the timer so you have to wait another 5 seconds self._zoomTimerLast = currentTime
[docs] def previousZoom(self): """Move to the previous stored zoom """ previousZoomPtr = (self._zoomHistoryCurrent - 1) % len(self._zoomHistory) if self._zoomHistoryHead != previousZoomPtr and self._zoomHistory[previousZoomPtr] is not None: self._zoomHistoryCurrent = previousZoomPtr restoredZooms = self._zoomHistory[self._zoomHistoryCurrent] self.axisL, self.axisR, self.axisB, self.axisT = restoredZooms[0], restoredZooms[1], restoredZooms[2], \ restoredZooms[3] # use this because it rescales all the symbols self._rescaleXAxis()
[docs] def nextZoom(self): """Move to the next stored zoom """ if self._zoomHistoryHead != self._zoomHistoryCurrent: self._zoomHistoryCurrent = (self._zoomHistoryCurrent + 1) % len(self._zoomHistory) restoredZooms = self._zoomHistory[self._zoomHistoryCurrent] self.axisL, self.axisR, self.axisB, self.axisT = restoredZooms[0], restoredZooms[1], restoredZooms[2], \ restoredZooms[3] # use this because it rescales all the symbols self._rescaleXAxis()
[docs] def storeZoom(self): """Store the current axis values to the zoom stack Sets this to the top of the stack, removing everything after """ if self._currentZoom < ZOOMMAXSTORE: self._currentZoom += 1 self.storedZooms = self.storedZooms[:self._currentZoom - 1] self.storedZooms.append((self.axisL, self.axisR, self.axisB, self.axisT))
@property def zoomState(self): return (self.axisL, self.axisR, self.axisB, self.axisT)
[docs] def restoreZoom(self, zoomState=None): """Restore zoom to the last stored zoom zoomState = (axisL, axisR, axisB, axisT) """ if zoomState and len(zoomState) == 4: self.storedZooms.append(zoomState) if self.storedZooms: # get the top of the stack self._currentZoom = len(self.storedZooms) restoredZooms = self.storedZooms[self._currentZoom - 1] self.axisL, self.axisR, self.axisB, self.axisT = restoredZooms[0], restoredZooms[1], restoredZooms[2], \ restoredZooms[3] else: self._resetAxisRange() self._zoomHistoryCurrent = self._zoomHistoryHead self._storeZoomHistory() # use this because it rescales all the symbols self._rescaleXAxis()
[docs] def resetZoom(self): self._resetAxisRange() self._rescaleAllAxes()
[docs] def zoomIn(self): zoomPercent = -self._preferences.zoomPercent / 100.0 dx = (self.axisR - self.axisL) / 2.0 dy = (self.axisT - self.axisB) / 2.0 self.axisL -= zoomPercent * dx self.axisR += zoomPercent * dx self.axisT += zoomPercent * dy self.axisB -= zoomPercent * dy self._rescaleAllAxes()
[docs] def zoomOut(self): zoomPercent = self._preferences.zoomPercent / 100.0 dx = (self.axisR - self.axisL) / 2.0 dy = (self.axisT - self.axisB) / 2.0 self.axisL -= zoomPercent * dx self.axisR += zoomPercent * dx self.axisT += zoomPercent * dy self.axisB -= zoomPercent * dy self._rescaleAllAxes()
def _resetAxisRange(self, xAxis=True, yAxis=True): """ reset the axes to the limits of the spectra in this view """ # set a default empty axisRange axisLimits = [] # iterate over spectrumViews for spectrumView in self._ordering: # strip.spectrumViews: if spectrumView.isDeleted: continue fxMin, fxMax = self._spectrumSettings[spectrumView][GLDefs.SPECTRUM_XLIMITS] fyMin, fyMax = self._spectrumSettings[spectrumView][GLDefs.SPECTRUM_YLIMITS] if not axisLimits: axisLimits = [fxMax, fxMin, fyMax, fyMin] else: axisLimits[0] = max(axisLimits[0], fxMax) axisLimits[1] = min(axisLimits[1], fxMin) axisLimits[2] = max(axisLimits[2], fyMax) axisLimits[3] = min(axisLimits[3], fyMin) if axisLimits: if xAxis: if self.INVERTXAXIS: self.axisL, self.axisR = axisLimits[0:2] else: self.axisR, self.axisL = axisLimits[0:2] if yAxis: if self.INVERTYAXIS: self.axisB, self.axisT = axisLimits[2:4] else: self.axisT, self.axisB = axisLimits[2:4] self._rescaleAllZoom(rescale=False)
[docs] def initializeGL(self): # GLversionFunctions = self.context().versionFunctions() # GLversionFunctions.initializeOpenGLFunctions() # self._GLVersion = GLversionFunctions.glGetString(GL.GL_VERSION) # initialise a common to all OpenGL windows self.globalGL = GLGlobalData(parent=self, mainWindow=self.mainWindow) # move outside of GLGlobalData to check threading on windows self.globalGL.bindFonts() # initialise the arrays for the grid and axes self.gridList = [] for li in range(3): self.gridList.append(GLVertexArray(numLists=1, renderMode=GLRENDERMODE_REBUILD, blendMode=False, drawMode=GL.GL_LINES, dimension=2, GLContext=self)) self.diagonalGLList = GLVertexArray(numLists=1, renderMode=GLRENDERMODE_REBUILD, blendMode=False, drawMode=GL.GL_LINES, dimension=2, GLContext=self) self.diagonalSideBandsGLList = GLVertexArray(numLists=1, renderMode=GLRENDERMODE_REBUILD, blendMode=False, drawMode=GL.GL_LINES, dimension=2, GLContext=self) self.boundingBoxes = GLVertexArray(numLists=1, renderMode=GLRENDERMODE_REBUILD, blendMode=False, drawMode=GL.GL_LINES, dimension=2, GLContext=self) # get the current buffering mode and set the required length to the number of buffers format = self.format() self._numBuffers = int(format.swapBehavior()) or 2 self._glCursorQueue = () for buf in range(self._numBuffers): self._glCursorQueue += (GLVertexArray(numLists=1, renderMode=GLRENDERMODE_REBUILD, blendMode=False, drawMode=GL.GL_LINES, dimension=2, GLContext=self),) self._clearGLCursorQueue() self._glCursor = GLVertexArray(numLists=1, renderMode=GLRENDERMODE_REBUILD, blendMode=False, drawMode=GL.GL_LINES, dimension=2, GLContext=self) self._externalRegions = GLExternalRegion(project=self.project, GLContext=self, spectrumView=None, integralListView=None) self._selectionBox = GLVertexArray(numLists=1, renderMode=GLRENDERMODE_REBUILD, blendMode=True, drawMode=GL.GL_QUADS, dimension=3, GLContext=self) self._selectionOutline = GLVertexArray(numLists=1, renderMode=GLRENDERMODE_REBUILD, blendMode=True, drawMode=GL.GL_LINES, dimension=3, GLContext=self) self._marksList = GLVertexArray(numLists=1, renderMode=GLRENDERMODE_REBUILD, blendMode=False, drawMode=GL.GL_LINES, dimension=2, GLContext=self) self._regionList = GLVertexArray(numLists=1, renderMode=GLRENDERMODE_REBUILD, blendMode=True, drawMode=GL.GL_QUADS, dimension=2, GLContext=self) self._testSpectrum = GLVertexArray(numLists=1, renderMode=GLRENDERMODE_REBUILD, blendMode=True, drawMode=GL.GL_TRIANGLES, dimension=4, GLContext=self) self._spectrumLabelling = GLSimpleStrings(parent=self, strip=self.strip, name='spectrumLabelling') # self._legend = GLSimpleLegend(parent=self, strip=self.strip, name='legend') # for ii, spectrum in enumerate(self.project.spectra): # # add some test strings # self._legend.addString(spectrum, (ii*15,ii*15), # colour="#FE64C6", alpha=0.75) self.viewports = GLViewports() self._initialiseViewPorts() # set strings for the overlay text self.buildOverlayStrings() # This is the correct blend function to ignore stray surface blending functions GL.glBlendFuncSeparate(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA, GL.GL_ONE, GL.GL_ONE) self._setColourScheme() self.setBackgroundColour(self.background, silent=True) _shader = self.globalGL._shaderProgramTex _shader.makeCurrent() _shader.setBlendEnabled(False) _shader.setAlpha(1.0) _shader = self.globalGL._shaderProgramTexAlias _shader.makeCurrent() _shader.setBlendEnabled(True) _shader.setAlpha(1.0) if self.strip: self.updateVisibleSpectrumViews() self.initialiseAxes(self.strip) self.initialiseTraces() # set the painting mode self._paintMode = PaintModes.PAINT_ALL self._paintLastFrame = True self._leavingWidget = False # check that the screen device pixel ratio is correct self.refreshDevicePixelRatio() # set the pyqtsignal responders self.GLSignals.glXAxisChanged.connect(self._glXAxisChanged) self.GLSignals.glYAxisChanged.connect(self._glYAxisChanged) self.GLSignals.glAllAxesChanged.connect(self._glAllAxesChanged) self.GLSignals.glMouseMoved.connect(self._glMouseMoved) self.GLSignals.glEvent.connect(self._glEvent) self.GLSignals.glAxisLockChanged.connect(self._glAxisLockChanged) self.GLSignals.glAxisUnitsChanged.connect(self._glAxisUnitsChanged) self.GLSignals.glKeyEvent.connect(self._glKeyEvent) self.glReady = True # make sure that the shaders are initialised self._resizeGL()
def _clearGLCursorQueue(self): """Clear the cursor glLists """ if not self._disableCursorUpdate: for glBuf in self._glCursorQueue: glBuf.clearArrays() self._glCursorHead = 0 self._glCursorTail = (self._glCursorHead - 1) % self._numBuffers def _advanceGLCursor(self): """Advance the pointers for the cursor glLists """ if not self._disableCursorUpdate: self._glCursorHead = (self._glCursorHead + 1) % self._numBuffers self._glCursorTail = (self._glCursorHead - 1) % self._numBuffers def _initialiseViewPorts(self): """Initialise all the viewports for the widget """ self.viewports.clearViewports() # define the main viewports if self.AXIS_INSIDE: self.viewports.addViewport(GLDefs.MAINVIEW, self, (0, 'a'), (self.AXIS_MARGINBOTTOM, 'a'), (-self.AXIS_MARGINRIGHT, 'w'), (-self.AXIS_MARGINBOTTOM, 'h')) self.viewports.addViewport(GLDefs.MAINVIEWFULLWIDTH, self, (0, 'a'), (self.AXIS_MARGINBOTTOM, 'a'), (0, 'w'), (-self.AXIS_MARGINBOTTOM, 'h')) self.viewports.addViewport(GLDefs.MAINVIEWFULLHEIGHT, self, (0, 'a'), (0, 'a'), (-self.AXIS_MARGINRIGHT, 'w'), (0, 'h')) else: self.viewports.addViewport(GLDefs.MAINVIEW, self, (0, 'a'), (self.AXIS_MARGINBOTTOM + self.AXIS_LINE, 'a'), (-(self.AXIS_MARGINRIGHT + self.AXIS_LINE), 'w'), (-(self.AXIS_MARGINBOTTOM + self.AXIS_LINE), 'h')) self.viewports.addViewport(GLDefs.MAINVIEWFULLWIDTH, self, (0, 'a'), (self.AXIS_MARGINBOTTOM + self.AXIS_LINE, 'a'), (0, 'w'), (-(self.AXIS_MARGINBOTTOM + self.AXIS_LINE), 'h')) self.viewports.addViewport(GLDefs.MAINVIEWFULLHEIGHT, self, (0, 'a'), (0, 'a'), (-(self.AXIS_MARGINRIGHT + self.AXIS_LINE), 'w'), (0, 'h')) # define the viewports for the right axis bar if self.AXIS_INSIDE: self.viewports.addViewport(GLDefs.RIGHTAXIS, self, (-(self.AXIS_MARGINRIGHT + self.AXIS_LINE), 'w'), (self.AXIS_MARGINBOTTOM, 'a'), (self.AXIS_LINE, 'a'), (-self.AXIS_MARGINBOTTOM, 'h')) self.viewports.addViewport(GLDefs.RIGHTAXISBAR, self, (-self.AXIS_MARGINRIGHT, 'w'), (self.AXIS_MARGINBOTTOM, 'a'), (self.AXIS_MARGINRIGHT, 'a'), (-self.AXIS_MARGINBOTTOM, 'h')) else: self.viewports.addViewport(GLDefs.RIGHTAXIS, self, (-(self.AXIS_MARGINRIGHT + self.AXIS_LINE), 'w'), (self.AXIS_MARGINBOTTOM + self.AXIS_LINE, 'a'), (self.AXIS_LINE, 'a'), (-(self.AXIS_MARGINBOTTOM + self.AXIS_LINE), 'h')) self.viewports.addViewport(GLDefs.RIGHTAXISBAR, self, (-self.AXIS_MARGINRIGHT, 'w'), (self.AXIS_MARGINBOTTOM + self.AXIS_LINE, 'a'), (self.AXIS_MARGINRIGHT, 'a'), (-(self.AXIS_MARGINBOTTOM + self.AXIS_LINE), 'h')) self.viewports.addViewport(GLDefs.FULLRIGHTAXIS, self, (-(self.AXIS_MARGINRIGHT + self.AXIS_LINE), 'w'), (0, 'a'), (self.AXIS_LINE, 'a'), (0, 'h')) self.viewports.addViewport(GLDefs.FULLRIGHTAXISBAR, self, (-self.AXIS_MARGINRIGHT, 'w'), (0, 'a'), (self.AXIS_MARGINRIGHT, 'a'), (0, 'h')) # define the viewports for the bottom axis bar if self.AXIS_INSIDE: self.viewports.addViewport(GLDefs.BOTTOMAXIS, self, (0, 'a'), (self.AXIS_MARGINBOTTOM, 'a'), (-self.AXIS_MARGINRIGHT, 'w'), (self.AXIS_LINE, 'a')) self.viewports.addViewport(GLDefs.BOTTOMAXISBAR, self, (0, 'a'), (0, 'a'), (-self.AXIS_MARGINRIGHT, 'w'), (self.AXIS_MARGINBOTTOM, 'a')) else: self.viewports.addViewport(GLDefs.BOTTOMAXIS, self, (0, 'a'), (self.AXIS_MARGINBOTTOM, 'a'), (-(self.AXIS_MARGINRIGHT + self.AXIS_LINE), 'w'), (self.AXIS_LINE, 'a')) self.viewports.addViewport(GLDefs.BOTTOMAXISBAR, self, (0, 'a'), (0, 'a'), (-(self.AXIS_MARGINRIGHT + self.AXIS_LINE), 'w'), (self.AXIS_MARGINBOTTOM, 'a')) self.viewports.addViewport(GLDefs.FULLBOTTOMAXIS, self, (0, 'a'), (self.AXIS_MARGINBOTTOM, 'a'), (0, 'w'), (self.AXIS_LINE, 'a')) self.viewports.addViewport(GLDefs.FULLBOTTOMAXISBAR, self, (0, 'a'), (0, 'a'), (0, 'w'), (self.AXIS_MARGINBOTTOM, 'a')) # define the full viewport self.viewports.addViewport(GLDefs.FULLVIEW, self, (0, 'a'), (0, 'a'), (0, 'w'), (0, 'h')) # define the remaining corner self.viewports.addViewport(GLDefs.AXISCORNER, self, (-self.AXIS_MARGINRIGHT, 'w'), (0, 'a'), (self.AXIS_MARGINRIGHT, 'a'), (self.AXIS_MARGINBOTTOM, 'a')) # define an empty view (for printing mainly) self.viewports.addViewport(GLDefs.BLANKVIEW, self, (0, 'a'), (0, 'a'), (0, 'a'), (0, 'a'))
[docs] def buildOverlayStrings(self): smallFont = self.getSmallFont() dy = STRINGOFFSET * self.deltaY self._lockedStringFalse = GLString(text=GLDefs.LOCKEDSTRING, font=smallFont, x=0, y=dy, colour=(0.5, 0.5, 0.5, 1.0), GLContext=self) self._lockedStringTrue = GLString(text=GLDefs.LOCKEDSTRING, font=smallFont, x=0, y=dy, colour=self.highlightColour, GLContext=self) dx = self._lockedStringTrue.width * self.deltaX self._fixedStringFalse = GLString(text=GLDefs.FIXEDSTRING, font=smallFont, x=dx, y=dy, colour=(0.5, 0.5, 0.5, 1.0), GLContext=self) self._fixedStringTrue = GLString(text=GLDefs.FIXEDSTRING, font=smallFont, x=dx, y=dy, colour=self.highlightColour, GLContext=self) cornerButtons = ((self._lockedStringTrue, self._toggleAxisLocked), (self._fixedStringTrue, self._toggleAxisFixed)) self._buttonCentres = () buttonOffset = 0 for button, callBack in cornerButtons: w = (button.width / 2) h = (button.height / 2) # define a slightly wider, lower box self._buttonCentres += ((w + 2 + buttonOffset, h - 2, w, h - 3, callBack),) buttonOffset += button.width self.stripIDString = GLString(text='', font=smallFont, x=0, y=0, GLContext=self, obj=None)
[docs] def getSmallFont(self, transparent=False): """Get the current active font """ scale = self.viewports.devicePixelRatio size = self.globalGL.glSmallFontSize # get the correct font depending on the scaling and set the scaled height/width _font = list(self.globalGL.fonts.values())[0].closestFont(size * scale) if not (0.9999 < scale < 1.0001): _font.charHeight = _font.height / scale _font.charWidth = _font.width / scale return _font
def _setColourScheme(self): """Update colours from colourScheme """ self.colours = getColours() self.hexBackground = self.colours[CCPNGLWIDGET_HEXBACKGROUND] self.background = self.colours[CCPNGLWIDGET_BACKGROUND] self.foreground = self.colours[CCPNGLWIDGET_FOREGROUND] self.mousePickColour = self.colours[CCPNGLWIDGET_PICKCOLOUR] self.gridColour = self.colours[CCPNGLWIDGET_GRID] self.highlightColour = self.colours[CCPNGLWIDGET_HIGHLIGHT] self._labellingColour = self.colours[CCPNGLWIDGET_LABELLING] self._phasingTraceColour = self.colours[CCPNGLWIDGET_PHASETRACE] self.zoomAreaColour = self.colours[CCPNGLWIDGET_ZOOMAREA] self.pickAreaColour = self.colours[CCPNGLWIDGET_PICKAREA] self.selectAreaColour = self.colours[CCPNGLWIDGET_SELECTAREA] self.badAreaColour = self.colours[CCPNGLWIDGET_BADAREA] self.zoomLineColour = self.colours[CCPNGLWIDGET_ZOOMLINE] self.mouseMoveLineColour = self.colours[CCPNGLWIDGET_MOUSEMOVELINE] self.zoomAreaColourHard = (*self.colours[CCPNGLWIDGET_ZOOMAREA][0:3], CCPNGLWIDGET_HARDSHADE) self.pickAreaColourHard = (*self.colours[CCPNGLWIDGET_PICKAREA][0:3], CCPNGLWIDGET_HARDSHADE) self.selectAreaColourHard = (*self.colours[CCPNGLWIDGET_SELECTAREA][0:3], CCPNGLWIDGET_HARDSHADE) self.badAreaColourHard = (*self.colours[CCPNGLWIDGET_BADAREA][0:3], CCPNGLWIDGET_HARDSHADE) def _preferencesUpdate(self): """update GL values after the preferences have changed """ self._preferences = self.application.preferences.general self._setColourScheme() # set the new limits self._applyXLimit = self._preferences.zoomXLimitApply self._applyYLimit = self._preferences.zoomYLimitApply self._intensityLimit = self._preferences.intensityLimit # set the flag to update the background in the paint event self._updateBackgroundColour = True self.stripIDString.renderMode = GLRENDERMODE_REBUILD # rebuild all the strings if the fontSize has changed _size = self.application.preferences.appearance.spectrumDisplayFontSize if _size != self.globalGL.glSmallFontSize: self.globalGL.glSmallFontSize = _size self.refreshDevicePixelRatio() # get the updated font smallFont = self.getSmallFont() # change the colour of the selected 'Lock' string self._lockedStringTrue = GLString(text=GLDefs.LOCKEDSTRING, font=smallFont, x=0, y=0, colour=self.highlightColour, GLContext=self) # change the colour of the selected 'Fixed' string self._fixedStringTrue = GLString(text=GLDefs.FIXEDSTRING, font=smallFont, x=0, y=0, colour=self.highlightColour, GLContext=self)
[docs] def setBackgroundColour(self, col, silent=False): """ set all background colours in the shaders :param col - vec4, 4 element list e.g.: [0.05, 0.05, 0.05, 1.0], very dark gray """ GL.glClearColor(*col) self.background = np.array(col, dtype=np.float32) self.globalGL._shaderProgramTex.makeCurrent() self.globalGL._shaderProgramTex.setBackground(self.background) self.globalGL._shaderProgramAlias.makeCurrent() self.globalGL._shaderProgramAlias.setBackground(self.background) self.globalGL._shaderProgramTexAlias.makeCurrent() self.globalGL._shaderProgramTexAlias.setBackground(self.background) if not silent: self.update()
[docs] def mapMouseToAxis(self, pnt): if isinstance(pnt, QPoint): mx = pnt.x() if self._drawBottomAxis: my = self.height() - pnt.y() - self.AXIS_MOUSEYOFFSET else: my = self.height() - pnt.y() return tuple(self.mouseTransform.dot([mx, my, 0.0, 1.0])[:2]) else: return None
def _toggleAxisLocked(self): """Toggle the axis locked button """ self._aspectRatioMode = 0 if self._aspectRatioMode == 1 else 1 # create a dict and event to update this strip first aDict = {GLNotifier.GLSOURCE : None, GLNotifier.GLSPECTRUMDISPLAY: self.spectrumDisplay, GLNotifier.GLVALUES : (self._aspectRatioMode,) } self._glAxisLockChanged(aDict) self.GLSignals._emitAxisLockChanged(source=self, strip=self.strip, lockValues=(self._aspectRatioMode,)) def _toggleAxisFixed(self): """Toggle the use fixed aspect button """ self._aspectRatioMode = 0 if self._aspectRatioMode == 2 else 2 self._emitAxisFixed() def _emitAxisFixed(self): # create a dict and event to update this strip first aDict = {GLNotifier.GLSOURCE : None, GLNotifier.GLSPECTRUMDISPLAY: self.spectrumDisplay, GLNotifier.GLVALUES : (self._aspectRatioMode,) } self._glAxisLockChanged(aDict) self.GLSignals._emitAxisLockChanged(source=self, strip=self.strip, lockValues=(self._aspectRatioMode,)) def _getAxisDict(self): # create a dict and event to update this strip first aDict = {GLNotifier.GLSOURCE : None, GLNotifier.GLSPECTRUMDISPLAY: self.spectrumDisplay, GLNotifier.GLVALUES : (self._aspectRatioMode,) } return aDict
[docs] def mousePressInCornerButtons(self, mx, my): """Check if the mouse has been pressed in the lock button """ if self.AXISLOCKEDBUTTON and (self.AXISLOCKEDBUTTONALLSTRIPS or self.strip == self.strip.spectrumDisplay.strips[0]): for button in self._buttonCentres: minDiff = abs(mx - button[0]) maxDiff = abs(my - button[1]) if (minDiff < button[2]) and (maxDiff < button[3]): button[4]() return True
[docs] def mousePressInLabel(self, mx, my, ty): """Check if the mouse has been pressed in the stripIDlabel """ smallFont = self.getSmallFont() buttons = (((GLDefs.TITLEXOFFSET + 0.5 * len(self.stripIDLabel)) * smallFont.charWidth, ty - ((GLDefs.TITLEYOFFSET - 0.5) * smallFont.charHeight), 0.5 * len(self.stripIDLabel) * smallFont.charWidth, 0.4 * smallFont.charHeight),) for button in buttons: minDiff = abs(mx - button[0]) maxDiff = abs(my - button[1]) if (minDiff < button[2]) and (maxDiff < button[3]): return True
def _dragStrip(self, mouseDict): #, event: QtGui.QMouseEvent): """ Re-implementation of the mouse press event to enable a NmrResidue label to be dragged as a json object containing its id and a modifier key to encode the direction to drop the strip. """ # create the dataDict dataDict = {DropBase.PIDS: [self.strip.pid]} # update the dataDict with all mouseEvents{"controlRightMouse": false, "text": "NR:@-.@27.", "leftMouse": true, "controlShiftMiddleMouse": false, "middleMouse": false, "controlMiddleMouse": false, "controlShiftLeftMouse": false, "controlShiftRightMouse": false, "shiftMiddleMouse": false, "_connectDir": "isRight", "controlLeftMouse": false, "rightMouse": false, "shiftLeftMouse": false, "shiftRightMouse": false} dataDict.update(mouseDict) makeDragEvent(self, dataDict, [self.stripIDLabel], self.stripIDLabel)
[docs] def mousePressIn1DArea(self, regions): cursorCoordinate = self.getCurrentCursorCoordinate() for region in regions: if region._objectView and not region._objectView.isDisplayed: continue if isinstance(region._object, Integral): thisRegion = region._object._1Dregions if thisRegion: mid = np.median(thisRegion[1]) delta = (np.max(thisRegion[1]) - np.min(thisRegion[1])) / 2.0 inX = self._widthsChangedEnough((mid, 0.0), (cursorCoordinate[0], 0.0), tol=delta) mx = np.max([thisRegion[0], np.max(thisRegion[2])]) mn = np.min([thisRegion[0], np.min(thisRegion[2])]) mid = (mx + mn) / 2.0 delta = (mx - mn) / 2.0 inY = self._widthsChangedEnough((0.0, mid), (0.0, cursorCoordinate[1]), tol=delta) if not inX and not inY: self._dragRegions.add((region, 'v', 3)) return self._dragRegions
[docs] def mousePressInRegion(self, regions): cursorCoordinate = self.getCurrentCursorCoordinate() for region in regions: if region._objectView and not region._objectView.isDisplayed: continue if region.visible and region.movable: if region.orientation == 'h': if not self._widthsChangedEnough((0.0, region.values[0]), (0.0, cursorCoordinate[1]), tol=abs(3 * self.pixelY)): self._dragRegions.add((region, 'h', 0)) # line 0 of h-region # break elif not self._widthsChangedEnough((0.0, region.values[1]), (0.0, cursorCoordinate[1]), tol=abs(3 * self.pixelY)): self._dragRegions.add((region, 'h', 1)) # line 1 of h-region # break else: mid = (region.values[0] + region.values[1]) / 2.0 delta = abs(region.values[0] - region.values[1]) / 2.0 if not self._widthsChangedEnough((0.0, mid), (0.0, cursorCoordinate[1]), tol=delta): self._dragRegions.add((region, 'h', 3)) # both lines of h-region # break elif region.orientation == 'v': if not self._widthsChangedEnough((region.values[0], 0.0), (cursorCoordinate[0], 0.0), tol=abs(3 * self.pixelX)): self._dragRegions.add((region, 'v', 0)) # line 0 of v-region # break elif not self._widthsChangedEnough((region.values[1], 0.0), (cursorCoordinate[0], 0.0), tol=abs(3 * self.pixelX)): self._dragRegions.add((region, 'v', 1)) # line 1 of v-region # break else: mid = (region.values[0] + region.values[1]) / 2.0 delta = abs(region.values[0] - region.values[1]) / 2.0 if not self._widthsChangedEnough((mid, 0.0), (cursorCoordinate[0], 0.0), tol=delta): self._dragRegions.add((region, 'v', 3)) # both lines of v-region # break return self._dragRegions
[docs] def mousePressInfiniteLine(self, regions): cursorCoordinate = self.getCurrentCursorCoordinate() for region in regions: if region._objectView and not region._objectView.isDisplayed: continue if region.visible and region.movable and region.values == region.values: # nan/inf check if region.orientation == 'h': if not self._widthsChangedEnough((0.0, region.values), (0.0, cursorCoordinate[1]), tol=abs(3 * self.pixelY)): self._dragRegions.add((region, 'h', 4)) # line 0 of h-region elif region.orientation == 'v': if not self._widthsChangedEnough((region.values, 0.0), (cursorCoordinate[0], 0.0), tol=abs(3 * self.pixelX)): self._dragRegions.add((region, 'v', 4)) # line 0 of v-region return self._dragRegions
[docs] def mousePressInIntegralLists(self): """Check whether the mouse has been pressed in an integral """ # check moved to 1D class # if not self._stackingMode and not(self.is1D and self.strip._isPhasingOn): # for reg in self._GLIntegralLists.values(): for reg in self._GLIntegrals._GLSymbols.values(): if not reg.integralListView.isDisplayed or \ not reg.spectrumView.isDisplayed: continue integralPressed = self.mousePressInRegion(reg._regions)
# if integralPressed: # break def _checkMousePressAllowed(self): """Check whether a mouse click is allowed """ # get delta between now and last mouse click _lastTime = self._lastTimeClicked _thisTime = time.time_ns() // 1e6 delta = _thisTime - _lastTime # if interval large enough then reset timer and return True if delta > self._clickInterval: self._lastTimeClicked = _thisTime return True def _handleLabelDrag(self, event): """handle a mouse drag event of a label """ if self._mouseButton == QtCore.Qt.LeftButton and self._pids: if (event.pos() - self._dragStartPosition).manhattanLength() >= QtWidgets.QApplication.startDragDistance(): mouseDict = getMouseEventDict(event) self._dragStrip(mouseDict) self._draggingLabel = False
[docs] def mousePressEvent(self, ev): # disable mouse presses in the double-click interval if not self._checkMousePressAllowed(): return # get the keyboard state keyModifiers = QApplication.keyboardModifiers() cursorCoordinate = self.getCurrentCursorCoordinate() self._mousePressed = True self.lastPos = ev.pos() mx = ev.pos().x() if self._drawBottomAxis: my = self.height() - ev.pos().y() - self.AXIS_MOUSEYOFFSET top = self.height() - self.AXIS_MOUSEYOFFSET else: my = self.height() - ev.pos().y() top = self.height() self._mouseStart = (mx, my) self._startCoordinate = self.mouseTransform.dot([mx, my, 0.0, 1.0]) self._startMiddleDrag = False self._validRegionPick = False self._endCoordinate = self._startCoordinate if int(ev.buttons() & (Qt.MiddleButton | Qt.RightButton)): # no modifiers pressed if not (keyModifiers & (Qt.ShiftModifier | Qt.ControlModifier | Qt.AltModifier | Qt.MetaModifier)): # drag a peak xPosition = cursorCoordinate[0] # self.mapSceneToView(event.pos()).x() yPosition = cursorCoordinate[1] # objs = self._mouseInPeak(xPosition, yPosition, firstOnly=True) if objs: # move from the centre of the clicked peak self.getPeakPositionFromMouse(objs[0], self._startCoordinate, cursorCoordinate) # set the flags for middle mouse dragging self._startMiddleDrag = True self._drawMouseMoveLine = True self._drawDeltaOffset = True if self.mousePressInLabel(mx, my, top): self._draggingLabel = False # True to enable dragging of label else: # check if the corner buttons have been pressed self.mousePressInCornerButtons(mx, my) # check for dragging of infinite lines, region boundaries, integrals self.mousePressInfiniteLine(self._infiniteLines) while len(self._dragRegions) > 1: self._dragRegions.pop() if not self._dragRegions: if not self.mousePressInRegion(self._externalRegions._regions): self.mousePressInIntegralLists() if int(ev.buttons() & (Qt.LeftButton | Qt.RightButton)): # find the bounds for the region that has currently been clicked # if (keyModifiers & (Qt.ShiftModifier | Qt.ControlModifier)): if self.is1D: bounds = ([],) self._minBounds = [None, ] self._maxBounds = [None, ] else: bounds = ([], []) self._minBounds = [None, None] self._maxBounds = [None, None] # get the list of visible spectrumViews, or the first in the list visibleSpectrumViews = [specView for specView in self._ordering if not specView.isDeleted and specView.isDisplayed] for specView in visibleSpectrumViews: specSettings = self._spectrumSettings[specView] if not self.is1D: pIndex = specSettings[GLDefs.SPECTRUM_POINTINDEX] if None in pIndex: continue for ii in range(len(bounds)): _rb = list(specSettings[GLDefs.SPECTRUM_REGIONBOUNDS][ii]) bounds[ii].extend(_rb[1:-1]) # skip the outer ppm values bounds = [sorted(set([round(b, 12) for b in bnd])) for bnd in bounds] mn = min(self.axisL, self.axisR) mx = max(self.axisL, self.axisR) bounds[0] = [mn] + [bnd for bnd in bounds[0] if mn < bnd < mx] + [mx] if len(bounds) > 1: mn = min(self.axisB, self.axisT) mx = max(self.axisB, self.axisT) bounds[1] = [mn] + [bnd for bnd in bounds[1] if mn < bnd < mx] + [mx] for jj in range(len(bounds)): for minB, maxB in zip(bounds[jj], bounds[jj][1:]): if minB < self._startCoordinate[jj] < maxB: self._minBounds[jj] = minB self._maxBounds[jj] = maxB break if None not in self._minBounds and None not in self._maxBounds: # not currently used, but available to set the region to red if required self._validRegionPick = True self.current.strip = self.strip self.update()
[docs] def mouseDoubleClickEvent(self, ev): self._mouseDoubleClickEvent(ev)
[docs] def mouseReleaseEvent(self, ev): # if no self.current then strip is not defined correctly if not getattr(self.current, 'mouseMovedDict', None): return if not self._mousePressed: return self._mousePressed = False self._draggingLabel = False # here or at the end of release? self._clearAndUpdate() mx = ev.pos().x() if self._drawBottomAxis: my = self.height() - ev.pos().y() - self.AXIS_MOUSEYOFFSET else: my = self.height() - ev.pos().y() self._mouseEnd = (mx, my) # add a 2-pixel tolerance to the click event - in case of a small wiggle on coordinates if not self._widthsChangedEnough(self._mouseStart, self._mouseEnd, tol=2): # perform click action self._mouseClickEvent(ev) else: # end of drag event - perform action self._mouseDragEvent(ev) self._startMiddleDrag = False
def _movePeakFromGLKeys(self, key): if len(self.current.peaks) < 1: return # move by 5 pixels moveFactor = 5 moveDict = { QtCore.Qt.Key_Left : (-self.pixelX * moveFactor, 0), QtCore.Qt.Key_Right: (self.pixelX * moveFactor, 0), QtCore.Qt.Key_Up : (0, self.pixelY * moveFactor), QtCore.Qt.Key_Down : (0, -self.pixelY * moveFactor) } if key in moveDict: with undoBlockWithoutSideBar(): for peak in self.current.peaks: self._movePeak(peak, moveDict.get(key)) def _singleKeyAction(self, key, isShift): """ :return: Actions for single key press. If current peaks, moves the peaks when using directional arrow otherwise pans the spectrum. """ if not isShift: self._panGLSpectrum(key) if isShift: self._movePeakFromGLKeys(key) def _KeyModifiersAction(self, key): keyModifiers = QApplication.keyboardModifiers() if keyModifiers & (Qt.MetaModifier | Qt.ControlModifier) and key == Qt.Key_A: self.mainWindow.selectAllPeaks(self.strip)
[docs] def glKeyPressEvent(self, aDict): """Process the key events from GLsignals """ if (self.strip and not self.strip.isDeleted): # this may not be the current strip if self.strip == self.current.strip: self._singleKeyAction(aDict[GLNotifier.GLKEY], aDict[GLNotifier.GLMODIFIER]) self.update()
[docs] def keyPressEvent(self, event: QtGui.QKeyEvent): """Process key events or pass to current strip of required """ if (self.strip and not self.strip.isDeleted): _key = event.key() keyModifiers = QApplication.keyboardModifiers() if self.strip == self.current.strip: self._singleKeyAction(_key, True if (keyModifiers & Qt.ShiftModifier) else False) self._KeyModifiersAction(_key) elif not self._preferences.currentStripFollowsMouse: self.GLSignals._emitKeyEvent(strip=self.strip, key=event.key(), modifier=True if (keyModifiers & Qt.ShiftModifier) else False)
def _clearAndUpdate(self): self._drawSelectionBox = False self._drawDeltaOffset = False self._drawMouseMoveLine = False self._dragRegions = set() self.update()
[docs] def enterEvent(self, ev: QtCore.QEvent): self.GLSignals._mouseInGLWidget = True if self.strip and not self.strip.isDeleted: if self._preferences.currentStripFollowsMouse and self.current.strip != self.strip: self.current.strip = self.strip self.application._focusStrip = self.strip self.setFocus() elif self._preferences.focusFollowsMouse and not self.hasFocus(): if getattr(self.application, '_focusStrip', None) != self.strip: self.application._focusStrip = self.strip self.setFocus() super().enterEvent(ev) self._clearAndUpdate()
[docs] def focusInEvent(self, ev: QtGui.QFocusEvent): super().focusInEvent(ev) self._clearAndUpdate()
[docs] def focusOutEvent(self, ev: QtGui.QFocusEvent): super().focusOutEvent(ev) self._clearAndUpdate()
[docs] def leaveEvent(self, ev: QtCore.QEvent): # set a flag for leaving this widget self._leavingWidget = True self._cursorFrameCounter = GLDefs.CursorFrameCounterModes.CURSOR_DRAWLAST self.GLSignals._mouseInGLWidget = False super().leaveEvent(ev) self._clearAndUpdate()
[docs] def getMousePosition(self): """Get the coordinates of the mouse in the window (in ppm) based on the current axes """ point = self.mapFromGlobal(QtGui.QCursor.pos()) # calculate mouse coordinate within the mainView _mouseX = point.x() if self._drawBottomAxis: _mouseY = self.height() - point.y() - self.AXIS_MOUSEYOFFSET _top = self.height() - self.AXIS_MOUSEYOFFSET else: _mouseY = self.height() - point.y() _top = self.height() # translate from screen (0..w, 0..h) to NDC (-1..1, -1..1) to axes (axisL, axisR, axisT, axisB) return self.mouseTransform.dot([_mouseX, _mouseY, 0.0, 1.0])
[docs] def mouseMoveEvent(self, event): self.cursorSource = CURSOR_SOURCE_SELF if self.strip.isDeleted: return if not self._ordering: # strip.spectrumViews: return if self._draggingLabel: self._handleLabelDrag(event) return if abs(self.axisL - self.axisR) < 1.0e-6 or abs(self.axisT - self.axisB) < 1.0e-6: return # reset on the first mouseMove - frees the locked/default axis setattr(self.strip.spectrumDisplay, '_useFirstDefault', False) keyModifiers = QApplication.keyboardModifiers() currentPos = self.mapFromGlobal(QtGui.QCursor.pos()) dx = currentPos.x() - self.lastPos.x() dy = currentPos.y() - self.lastPos.y() self.lastPos = currentPos cursorCoordinate = self.getCurrentCursorCoordinate() mouseMovedDict = self._updateMouseDict(cursorCoordinate) if int(event.buttons() & (Qt.LeftButton | Qt.RightButton)): # do the complicated keypresses first # other keys are: Key_Alt, Key_Meta, and _isALT, _isMETA # NOTE:ED I think that Linux is doing a strange button switch when you press shift/ctrl if (keyModifiers & Qt.ShiftModifier) and (keyModifiers & Qt.ControlModifier): if self.is1D: # self._endCoordinate = cursorCoordinate #[event.pos().x(), self.height() - event.pos().y()] # self._selectionMode = 3 # check for a valid region pick if self._validRegionPick: self._endCoordinate = [np.clip(cursorCoordinate[0], self._minBounds[0], self._maxBounds[0]), cursorCoordinate[1]] self._selectionMode = 3 else: # in case bad picking needs to be shown to the user, shows a red box # awkward for overlaid spectra with different aliasing regions specified self._endCoordinate = [np.clip(cursorCoordinate[0], self._minBounds[0], self._maxBounds[0]), cursorCoordinate[1]] self._selectionMode = 4 self._drawSelectionBox = True self._drawDeltaOffset = True else: # check for a valid region pick if self._validRegionPick: self._endCoordinate = [np.clip(pos, mn, mx) for pos, mn, mx in zip(cursorCoordinate, self._minBounds, self._maxBounds)] self._selectionMode = 3 else: # in case bad picking needs to be shown to the user, shows a red box # awkward for overlaid spectra with different aliasing regions specified self._endCoordinate = [np.clip(pos, mn, mx) for pos, mn, mx in zip(cursorCoordinate, self._minBounds, self._maxBounds)] #cursorCoordinate #[event.pos().x(), self.height() - event.pos().y()] self._selectionMode = 4 self._drawSelectionBox = True self._drawDeltaOffset = True elif (keyModifiers & Qt.ShiftModifier) and int(event.buttons() & Qt.LeftButton): # fix the box to the screen ratio if self.aspectRatioMode: self._endCoordinate = cursorCoordinate dx = self._startCoordinate[0] - self._endCoordinate[0] # deltaX dy = self._startCoordinate[1] - self._endCoordinate[1] # deltaY _delta = self._getSelectionBoxRatio((dx, dy)) dx = _delta[0] #* self.sign(dx) dy = _delta[1] #* self.sign(dy) # dx = abs(dy * self.pixelX / self.pixelY) * self.sign(dx) self._endCoordinate[0] = self._startCoordinate[0] - dx self._endCoordinate[1] = self._startCoordinate[1] - dy else: self._endCoordinate = cursorCoordinate #[event.pos().x(), self.height() - event.pos().y()] self._selectionMode = 1 self._drawSelectionBox = True self._drawDeltaOffset = True elif (keyModifiers & Qt.ControlModifier) and int(event.buttons() & Qt.LeftButton): self._endCoordinate = cursorCoordinate #[event.pos().x(), self.height() - event.pos().y()] self._selectionMode = 2 self._drawSelectionBox = True self._drawDeltaOffset = True elif int(event.buttons() & Qt.LeftButton): if self._dragRegions: for reg in self._dragRegions: values = reg[0].values if reg[1] == 'v': if reg[2] == 3: # moving the mouse in a region values[0] += dx * self.pixelX values[1] += dx * self.pixelX elif reg[2] == 4: # moving an infinite line values += dx * self.pixelX else: # moving one edge of a region values[reg[2]] += dx * self.pixelX elif reg[1] == 'h': if reg[2] == 3: # moving the mouse in a region values[0] -= dy * self.pixelY values[1] -= dy * self.pixelY elif reg[2] == 4: # moving an infinite line values -= dy * self.pixelY else: # moving one edge of a region values[reg[2]] -= dy * self.pixelY reg[0].values = values # # NOTE:ED check moving of baseline # if hasattr(reg[0], '_integralArea'): # # reg[0].renderMode = GLRENDERMODE_REBUILD # reg[0]._rebuildIntegral() else: # Main mouse drag event - handle moving the axes with the mouse self.axisL -= dx * self.pixelX self.axisR -= dx * self.pixelX self.axisT += dy * self.pixelY self.axisB += dy * self.pixelY tilePos = self.strip.tilePosition if self.strip else self.tilePosition self.GLSignals._emitAllAxesChanged(source=self, strip=self.strip, axisB=self.axisB, axisT=self.axisT, axisL=self.axisL, axisR=self.axisR, row=tilePos[0], column=tilePos[1]) self._selectionMode = 0 self._rescaleAllAxes(mouseMoveOnly=True) self._storeZoomHistory() elif event.buttons() & Qt.MiddleButton: if self._startMiddleDrag and not (keyModifiers & (Qt.ShiftModifier | Qt.ControlModifier | Qt.AltModifier | Qt.MetaModifier)): # drag a peak self._endCoordinate = cursorCoordinate self._drawMouseMoveLine = True self._drawDeltaOffset = True if not event.buttons(): self.GLSignals._emitMouseMoved(source=self, coords=cursorCoordinate, mouseMovedDict=mouseMovedDict, mainWindow=self.mainWindow) # spawn rebuild/paint of traces if self._updateHTrace or self._updateVTrace: self.updateTraces() self.update()
def _updateMouseDict(self, cursorCoordinate): try: mouseMovedDict = self.current.mouseMovedDict except: # initialise a new mouse moved dict mouseMovedDict = {MOUSEDICTSTRIP : self.strip, AXIS_MATCHATOMTYPE : {}, AXIS_FULLATOMNAME : {}, AXIS_ACTIVEAXES : (), DOUBLEAXIS_MATCHATOMTYPE: {}, DOUBLEAXIS_FULLATOMNAME : {}, DOUBLEAXIS_ACTIVEAXES : () } xPos = yPos = 0 activeOther = [] activeX = activeY = '<None>' for n, axisCode in enumerate(self._axisCodes): if n == 0: xPos = pos = cursorCoordinate[0] activeX = axisCode #[0] # double cursor dPos = cursorCoordinate[1] elif n == 1: yPos = pos = cursorCoordinate[1] activeY = axisCode #[0] # double cursor dPos = cursorCoordinate[0] else: # for other Nd dimensions dPos = pos = self._orderedAxes[n].position # if n in self._orderedAxes else 0 activeOther.append(axisCode) #[0]) # populate the mouse moved dict mouseMovedDict[AXIS_MATCHATOMTYPE][axisCode[0]] = pos mouseMovedDict[AXIS_FULLATOMNAME][axisCode] = pos mouseMovedDict[DOUBLEAXIS_MATCHATOMTYPE][axisCode[0]] = dPos mouseMovedDict[DOUBLEAXIS_FULLATOMNAME][axisCode] = dPos mouseMovedDict[AXIS_ACTIVEAXES] = (activeX, activeY) + tuple(activeOther) # changed to full axisCodes mouseMovedDict[DOUBLEAXIS_ACTIVEAXES] = (activeY, activeX) + tuple(activeOther) self.current.cursorPosition = (xPos, yPos) self.current.mouseMovedDict = mouseMovedDict return mouseMovedDict
[docs] def sign(self, x): return 1.0 if x >= 0 else -1.0
def _rescaleOverlayText(self): if self.stripIDString: smallFont = self.getSmallFont() offsets = [GLDefs.TITLEXOFFSET * smallFont.charWidth * self.deltaX, 1.0 - (GLDefs.TITLEYOFFSET * smallFont.charHeight * self.deltaY), 0.0] self.stripIDString.attribs[:] = offsets * self.stripIDString.numVertices self.stripIDString.updateTextArrayVBOAttribs() if self._lockedStringTrue: dy = STRINGOFFSET * self.deltaY offsets = [0.0, dy, 0.0] self._lockedStringTrue.attribs[:] = offsets * self._lockedStringTrue.numVertices self._lockedStringTrue.updateTextArrayVBOAttribs() if self._lockedStringTrue: self._lockedStringFalse.attribs[:] = offsets * self._lockedStringFalse.numVertices self._lockedStringFalse.updateTextArrayVBOAttribs() dx = self._lockedStringTrue.width * self.deltaX offsets = [dx, dy, 0.0] if self._fixedStringFalse: self._fixedStringFalse.attribs[:] = offsets * self._fixedStringFalse.numVertices self._fixedStringFalse.updateTextArrayVBOAttribs() if self._fixedStringTrue: self._fixedStringTrue.attribs[:] = offsets * self._fixedStringTrue.numVertices self._fixedStringTrue.updateTextArrayVBOAttribs() def _updateHighlightedIntegrals(self, spectrumView, integralListView): drawList = self._GLIntegralLists[integralListView] drawList._rebuild() def _processSpectrumNotifier(self, data): trigger = data[Notifier.TRIGGER] if trigger in [Notifier.RENAME]: obj = data[Notifier.OBJECT] self._spectrumLabelling.renameString(obj) self.update() def _processPeakNotifier(self, data): self._updateVisibleSpectrumViews() self._GLPeaks._processNotifier(data) self.update() def _processNmrAtomNotifier(self, data): self._updateVisibleSpectrumViews() self._GLPeaks._processNotifier(data) self._nmrAtomsNotifier(data) self.update() def _processIntegralNotifier(self, data): self._updateVisibleSpectrumViews() self._GLIntegrals._processNotifier(data) self.update() def _processMultipletNotifier(self, data): self._updateVisibleSpectrumViews() self._GLMultiplets._processNotifier(data) self.update() def _nmrAtomsNotifier(self, data): """respond to a rename notifier on an nmrAtom, and update marks """ trigger = data[Notifier.TRIGGER] if trigger in [Notifier.RENAME]: nmrAtom = data[Notifier.OBJECT] oldPid = Pid.Pid(data[Notifier.OLDPID]) oldId = oldPid.id # search for the old name in the strings and remake for mark in self._marksAxisCodes: if mark.text == oldId: mark.text = nmrAtom.id # rebuild string mark.buildString() self._rescaleMarksAxisCode(mark) self.update() def _round_sig(self, x, sig=6, small_value=1.0e-9): return 0 if x == 0 else round(x, sig - int(math.floor(math.log10(max(abs(x), abs(small_value))))) - 1)
[docs] def between(self, val, l, r): return (l - val) * (r - val) <= 0
def _setViewPortFontScale(self): # set the scale for drawing the overlay text correctly self._axisScale[0:4] = [self.deltaX, self.deltaY, 1.0, 1.0] self.globalGL._shaderProgramTex.setAxisScale(self._axisScale) self.globalGL._shaderProgramTex.setProjectionAxes(self._uPMatrix, 0.0, 1.0, 0, 1.0, -1.0, 1.0) self.globalGL._shaderProgramTex.setPTexMatrix(self._uPMatrix)
[docs] def updateVisibleSpectrumViews(self): self._visibleSpectrumViewsChange = True self.update()
[docs] @contextmanager def glBlocking(self): try: # stop notifiers and logging interfering with paint event self.project.blankNotification() self.application._increaseNotificationBlocking() yield finally: # re-enable notifiers self.application._decreaseNotificationBlocking() self.project.unblankNotification()
def _buildGL(self): """Separate the building of the display from the paint event; not sure that this is required """ # if abs(self.axisL - self.axisR) < 1e-9 or abs(self.axisT - self.axisB) < 1e-9: # return self.buildCursors() if self.underMouse(): self.buildMouseCoords() # build spectrumSettings, spectrumView visibility self.buildSpectra() # only call if the axes have changed, and after spectra if self._updateAxes: self.buildGrid() self.buildDiagonals() self._updateAxes = False # self.buildBoundingBoxes() self._GLPeaks._spectrumSettings = self._spectrumSettings self._GLMultiplets._spectrumSettings = self._spectrumSettings self._GLIntegrals._spectrumSettings = self._spectrumSettings self._GLPeaks.buildSymbols() self._GLMultiplets.buildSymbols() if not self._stackingMode: self._GLIntegrals.buildSymbols() self.buildRegions() if self.buildMarks: self._marksList.renderMode = GLRENDERMODE_REBUILD self.buildMarks = False self.buildMarksRulers() self._GLPeaks.buildLabels() self._GLMultiplets.buildLabels() if not self._stackingMode: self._GLIntegrals.buildLabels() phasingFrame = self.spectrumDisplay.phasingFrame if phasingFrame.isVisible(): self.buildStaticTraces() from ccpn.util.decorators import profile @profile() def _buildGLWithProfile(self): """A new test method for profiling the _buildGL """ self._buildGL()
[docs] def paintGL(self): """Handle the GL painting """ if self._blankDisplay: return if self.strip.isDeleted: return # NOTE:ED - testing, remove later # self._paintMode = PaintModes.PAINT_ALL if self._paintMode == PaintModes.PAINT_NONE: # do nothing pass elif self._paintMode == PaintModes.PAINT_ALL or self._leavingWidget: # NOTE:ED - paint all content to the GL widget - need to work on this self._clearGLCursorQueue() # check whether the visible spectra list needs updating if self._visibleSpectrumViewsChange: self._visibleSpectrumViewsChange = False self._updateVisibleSpectrumViews() # if there are no spectra then skip the paintGL event if not self._ordering: return with self.glBlocking(): # simple profile of building all if hasattr(self.project, '_buildWithProfile') and self.project._buildWithProfile is True: self.project._buildWithProfile = False self._buildGLWithProfile() else: self._buildGL() self._paintGL() # make all following paint events into mouse only # so only paints a single frame from an update event self._paintMode = PaintModes.PAINT_MOUSEONLY self._paintLastFrame = True self._leavingWidget = False elif self._paintMode == PaintModes.PAINT_MOUSEONLY: self._paintLastFrame = False self._leavingWidget = False # only need to paint the mouse cursor self._paintGLMouseOnly()
@contextmanager def _disableGLAliasing(self): """Disable aliasing for the contained routines """ try: GL.glDisable(GL.GL_MULTISAMPLE) yield finally: GL.glEnable(GL.GL_MULTISAMPLE) @contextmanager def _enableGLAliasing(self): """Enable aliasing for the contained routines """ try: GL.glEnable(GL.GL_MULTISAMPLE) yield finally: GL.glDisable(GL.GL_MULTISAMPLE) @contextmanager def _enableLogicOp(self, logicOp=GL.GL_COPY): """Enable logic operation for the contained routines """ # valid values are: GL_CLEAR, GL_SET, GL_COPY, GL_COPY_INVERTED, GL_NOOP, # GL_INVERT, GL_AND, GL_NAND, GL_OR, GL_NOR, # GL_XOR, GL_EQUIV, GL_AND_REVERSE, GL_AND_INVERTED, # GL_OR_REVERSE, and GL_OR_INVERTED.The # initial value is GL_COPY try: GL.glEnable(GL.GL_COLOR_LOGIC_OP) GL.glLogicOp(logicOp) # NOTE:ED needed to cure that strange pyqt5 window-mask GL.glColorMask(GL.GL_TRUE, GL.GL_TRUE, GL.GL_TRUE, GL.GL_FALSE) yield # pass control to the calling function finally: GL.glColorMask(GL.GL_TRUE, GL.GL_TRUE, GL.GL_TRUE, GL.GL_TRUE) GL.glLogicOp(GL.GL_COPY) GL.glDisable(GL.GL_COLOR_LOGIC_OP) def _paintGLMouseOnly(self): """paintGL event - paint only the mouse in Xor mode """ # reset the paint mode - need to check the logic here # self._paintMode = PaintModes.PAINT_ALL currentShader = self.globalGL._shaderProgram1.makeCurrent() currentShader.setMVMatrix(self._IMatrix) # draw the spectra, need to reset the viewport self.viewports.setViewport(self._currentView) with self._disableGLAliasing(): currentShader.setProjectionAxes(self._uPMatrix, 0.0, 1.0, 0.0, 1.0, -1.0, 1.0) currentShader.setPMatrix(self._uPMatrix) self.buildCursors() with self._enableLogicOp(GL.GL_INVERT): # enable invert mode so that only the cursor needs to be refreshed in the other viewports self.drawLastCursors() self.drawCursors() def _paintGL(self): if self._updateBackgroundColour: self._updateBackgroundColour = False self.setBackgroundColour(self.background, silent=True) GL.glClear(GL.GL_COLOR_BUFFER_BIT) GL.glEnable(GL.GL_MULTISAMPLE) GL.glColorMask(GL.GL_TRUE, GL.GL_TRUE, GL.GL_TRUE, GL.GL_TRUE) currentShader = self.globalGL._shaderProgram1.makeCurrent() # start with the grid mapped to (0..1, 0..1) to remove zoom errors here currentShader.setProjectionAxes(self._uPMatrix, 0.0, 1.0, 0.0, 1.0, -1.0, 1.0) currentShader.setPMatrix(self._uPMatrix) currentShader.setMVMatrix(self._IMatrix) with self._disableGLAliasing(): # draw the grid components self.drawGrid() # set the scale to the axis limits, needs addressing correctly, possibly same as grid currentShader.setProjectionAxes(self._uPMatrix, self.axisL, self.axisR, self.axisB, self.axisT, -1.0, 1.0) currentShader.setPMatrix(self._uPMatrix) # draw the spectra, need to reset the viewport self.viewports.setViewport(self._currentView) self.drawSpectra() self.drawBoundingBoxes() # draw all the aliased symbols self.drawAliasedSymbols() self.globalGL._shaderProgram1.makeCurrent() if not self._stackingMode: if not (self.is1D and self.strip._isPhasingOn): # other mouse buttons checks needed here self._GLIntegrals.drawSymbols(self._spectrumSettings) with self._disableGLAliasing(): self._GLIntegrals.drawSymbolRegions(self._spectrumSettings) self.drawRegions() with self._disableGLAliasing(): self.drawMarksRulers() # draw the text to the screen self.enableTexture() self.enableTextClientState() if self._peakLabelsEnabled or self._multipletLabelsEnabled: self.drawAliasedLabels() # change to the text shader currentShader = self.globalGL._shaderProgramTex.makeCurrent() currentShader.setProjectionAxes(self._uPMatrix, self.axisL, self.axisR, self.axisB, self.axisT, -1.0, 1.0) currentShader.setPTexMatrix(self._uPMatrix) self._axisScale[0:4] = [self.pixelX, self.pixelY, 0.0, 1.0] currentShader.setAxisScale(self._axisScale) currentShader.setStackOffset(np.array((0.0, 0.0), dtype=np.float32)) if not self._stackingMode: if not (self.is1D and self.strip._isPhasingOn): self.drawIntegralLabels() self.drawMarksAxisCodes() else: # make the overlay/axis solid currentShader.setBlendEnabled(False) self._spectrumLabelling.drawStrings() # not fully implemented yet # self._legend.drawStrings() currentShader.setBlendEnabled(True) self.disableTextClientState() currentShader = self.globalGL._shaderProgram1.makeCurrent() self.drawTraces() currentShader.setMVMatrix(self._IMatrix) with self._disableGLAliasing(): self.drawInfiniteLines() currentShader.setProjectionAxes(self._uPMatrix, 0.0, 1.0, 0.0, 1.0, -1.0, 1.0) currentShader.setPMatrix(self._uPMatrix) self.drawSelectionBox() self.drawMouseMoveLine() if self._successiveClicks: self.drawDottedCursor() with self._enableLogicOp(GL.GL_INVERT): # enable invert mode so that only the cursor needs to be refreshed in the other viewports self.drawCursors() currentShader = self.globalGL._shaderProgramTex.makeCurrent() self.enableTextClientState() self._setViewPortFontScale() self.drawMouseCoords() # make the overlay/axis solid currentShader.setBlendEnabled(False) self.drawOverlayText() self.drawAxisLabels() currentShader.setBlendEnabled(True) self.disableTextClientState() self.disableTexture()
[docs] def enableTexture(self): GL.glEnable(GL.GL_BLEND) # GL.glEnable(GL.GL_TEXTURE_2D) # GL.glBindTexture(GL.GL_TEXTURE_2D, self.globalGL.glSmallFont.textureId) GL.glActiveTexture(GL.GL_TEXTURE0) GL.glBindTexture(GL.GL_TEXTURE_2D, self.getSmallFont()._parent.textureId)
# GL.glActiveTexture(GL.GL_TEXTURE1) # GL.glBindTexture(GL.GL_TEXTURE_2D, self.getSmallFont(transparent=True).textureId)
[docs] def disableTexture(self): GL.glDisable(GL.GL_BLEND)
[docs] def buildAllContours(self): for spectrumView in self._ordering: # strip.spectrumViews: if not spectrumView.isDeleted: spectrumView.buildContours = True
[docs] def buildSpectra(self): if self.strip.isDeleted: return # self._spectrumSettings = {} rebuildFlag = False for spectrumView in self._ordering: # strip.spectrumViews: if spectrumView.isDeleted: continue if spectrumView.buildContours or spectrumView.buildContoursOnly: # flag the peaks for rebuilding if not spectrumView.buildContoursOnly: for peakListView in spectrumView.peakListViews: peakListView.buildSymbols = True peakListView.buildLabels = True for integralListView in spectrumView.integralListViews: integralListView.buildSymbols = True integralListView.buildLabels = True for multipletListView in spectrumView.multipletListViews: multipletListView.buildSymbols = True multipletListView.buildLabels = True spectrumView.buildContours = False spectrumView.buildContoursOnly = False # rebuild the contours if spectrumView not in self._contourList.keys(): self._contourList[spectrumView] = GLVertexArray(numLists=1, renderMode=GLRENDERMODE_DRAW, blendMode=False, drawMode=GL.GL_LINES, dimension=2, GLContext=self) self._buildSpectrumSetting(spectrumView=spectrumView) spectrumView._buildGLContours(self._contourList[spectrumView]) rebuildFlag = True # define the VBOs to pass to the graphics card self._contourList[spectrumView].defineIndexVBO() # rebuild the traces as the spectrum/plane may have changed if rebuildFlag: self.rebuildTraces()
[docs] def enableTextClientState(self): _attribArrayIndex = 1 GL.glEnableClientState(GL.GL_VERTEX_ARRAY) GL.glEnableClientState(GL.GL_COLOR_ARRAY) GL.glEnableClientState(GL.GL_TEXTURE_COORD_ARRAY) GL.glEnableVertexAttribArray(_attribArrayIndex)
[docs] def disableTextClientState(self): _attribArrayIndex = 1 GL.glDisableClientState(GL.GL_TEXTURE_COORD_ARRAY) GL.glDisableClientState(GL.GL_VERTEX_ARRAY) GL.glDisableClientState(GL.GL_COLOR_ARRAY) GL.glDisableVertexAttribArray(_attribArrayIndex)
[docs] def buildGrid(self): """Build the grids for the mainGrid and the bottom/right axes """ # build the axes self.axisLabelling, self.axesChanged = self._buildAxes(self.gridList[0], axisList=[0, 1], scaleGrid=[1, 0], r=self.foreground[0], g=self.foreground[1], b=self.foreground[2], transparency=300.0, _includeDiagonal=self._matchingIsotopeCodes, _diagonalList=None, _includeAxis=False) #self.diagonalGLList) if self.axesChanged: if self.highlighted: self._buildAxes(self.gridList[1], axisList=[1], scaleGrid=[1, 0], r=self.highlightColour[0], g=self.highlightColour[1], b=self.highlightColour[2], transparency=32.0) self._buildAxes(self.gridList[2], axisList=[0], scaleGrid=[1, 0], r=self.highlightColour[0], g=self.highlightColour[1], b=self.highlightColour[2], transparency=32.0) else: self._buildAxes(self.gridList[1], axisList=[1], scaleGrid=[1, 0], r=self.foreground[0], g=self.foreground[1], b=self.foreground[2], transparency=32.0) self._buildAxes(self.gridList[2], axisList=[0], scaleGrid=[1, 0], r=self.foreground[0], g=self.foreground[1], b=self.foreground[2], transparency=32.0) # buffer the lists to VBOs for gr in self.gridList: gr.defineIndexVBO()
# # buffer the diagonal GL line # self.diagonalGLList.defineIndexVBO()
[docs] def drawGrid(self): # set to the mainView and draw the grid # self.buildGrid() GL.glEnable(GL.GL_BLEND) GL.glLineWidth(GLDefs.GLDEFAULTLINETHICKNESS * self.viewports.devicePixelRatio) # draw the main grid if self._gridVisible: self.viewports.setViewport(self._currentView) self.gridList[0].drawIndexVBO() # draw the diagonal line - independent of viewing the grid if self._matchingIsotopeCodes: # and self.diagonalGLList: # viewport above may not be set if not self._gridVisible: self.viewports.setViewport(self._currentView) if self.diagonalGLList: self.diagonalGLList.drawIndexVBO() if self.diagonalSideBandsGLList and self._sideBandsVisible: self.diagonalSideBandsGLList.drawIndexVBO() # draw the axes tick marks (effectively the same grid in smaller viewport) if self._axesVisible: if self._drawRightAxis: # draw the grid marks for the right axis self.viewports.setViewport(self._currentRightAxisView) self.gridList[1].drawIndexVBO() if self._drawBottomAxis: # draw the grid marks for the bottom axis self.viewports.setViewport(self._currentBottomAxisView) self.gridList[2].drawIndexVBO()
def _floatFormat(self, f=0.0, prec=3): """return a float string, remove trailing zeros after decimal """ return (('%.' + str(prec) + 'f') % f).rstrip('0').rstrip('.') def _intFormat(self, ii=0, prec=0): """return an integer string """ return self._floatFormat(ii, 1) # return '%i' % ii def _eFormat(self, f=0.0, prec=4): """return an exponential with trailing zeroes removed """ s = '%.*e' % (prec, f) if 'e' in s: mantissa, exp = s.split('e') mantissa = mantissa.rstrip('0') if mantissa.endswith('.'): mantissa += '0' exp = exp.lstrip('0+') if exp: if exp.startswith('-'): return '%se%d' % (mantissa, int(exp)) else: return '%se+%d' % (mantissa, int(exp)) else: return '%s' % mantissa else: return '' def _buildSingleWildCard(self, _axisCodes): """Buld the axisCode appending wildcard as required """ _code = '' if _axisCodes: _maxLen = max(len(ax) for ax in _axisCodes) _chs = [a for a in zip(*_axisCodes)] for ch in _chs: chSet = set(ch) if len(chSet) == 1: _code += ch[0] else: _code += '*' break else: if len(_code) < _maxLen: _code += '*' _code = _code or '*' return _code def _buildAxisCodesWithWildCards(self): """Build the visible axis codes from the visible spectra appending wildcard as required """ _visibleSpec = [(specView, self._spectrumSettings[specView]) for specView in self._ordering if not specView.isDeleted and specView.isDisplayed and specView in self._spectrumSettings] _firstVisible = ((self._ordering[0], self._spectrumSettings[self._ordering[0]]),) if self._ordering and not self._ordering[0].isDeleted and self._ordering[0] in self._spectrumSettings else () self._visibleOrderingDict = _visibleSpec or _firstVisible # quick fix to take the set of matching letters from the spectrum axisCodes - append a '*' to denote trailing differences if self.spectrumDisplay.is1D: # get the x-axis codes for 1d _axisCodes = [spec.spectrum.axisCodes[0] for spec, settings in self._visibleOrderingDict] _axisWildCards = (self._buildSingleWildCard(_axisCodes), self.axisCodes[1] or '*') else: dim = len(self.spectrumDisplay.axisCodes) _axisWildCards = [] for axis in range(dim): # get the correct x-axis mapped axis codes for Nd _axisCodes = [] for spec, settings in self._visibleOrderingDict: try: _axisCodes.append(spec.spectrum.axisCodes[settings[GLDefs.SPECTRUM_POINTINDEX][axis]]) except Exception as es: # can skip for now pass _code = self._buildSingleWildCard(_axisCodes) _axisWildCards.append(_code) self._visibleOrderingAxisCodes = _axisWildCards
[docs] def buildAxisLabels(self, refresh=False): # build axes labelling if refresh or self.axesChanged: self._axisXLabelling = [] self._axisScaleLabelling = [] if self.highlighted: labelColour = self.highlightColour else: labelColour = self.foreground smallFont = self.getSmallFont() if self._drawBottomAxis: # create the X axis labelling for axLabel in self.axisLabelling['0'].values(): axisX = axLabel[2] axisXLabel = axLabel[3] axisXText = self._intFormat(axisXLabel) if axLabel[4] >= 1 else self.XMode(axisXLabel) self._axisXLabelling.append(GLString(text=axisXText, font=smallFont, x=axisX - (0.4 * smallFont.charWidth * self.deltaX * len( axisXText)), y=self.AXIS_MARGINBOTTOM - GLDefs.TITLEYOFFSET * smallFont.charHeight, colour=labelColour, GLContext=self, obj=None)) # append the axisCode self._axisXLabelling.append(GLString(text=self._visibleOrderingAxisCodes[0] if self._visibleOrderingAxisCodes else '*', font=smallFont, x=GLDefs.AXISTEXTXOFFSET * self.deltaX, y=self.AXIS_MARGINBOTTOM - GLDefs.TITLEYOFFSET * smallFont.charHeight, colour=labelColour, GLContext=self, obj=None)) # and the axis dimensions xUnitsLabels = self.XAXES[self._xUnits] self._axisXLabelling.append(GLString(text=xUnitsLabels, font=smallFont, x=1.0 - (self.deltaX * len(xUnitsLabels) * smallFont.charWidth), y=self.AXIS_MARGINBOTTOM - GLDefs.TITLEYOFFSET * smallFont.charHeight, colour=labelColour, GLContext=self, obj=None)) self._axisYLabelling = [] if self._drawRightAxis: # create the Y axis labelling for xx, ayLabel in enumerate(self.axisLabelling['1'].values()): axisY = ayLabel[2] axisYLabel = ayLabel[3] if self.YAXISUSEEFORMAT: axisYText = self.YMode(axisYLabel) else: axisYText = self._intFormat(axisYLabel) if ayLabel[4] >= 1 else self.YMode(axisYLabel) self._axisYLabelling.append(GLString(text=axisYText, font=smallFont, x=self.AXIS_OFFSET, y=axisY - (GLDefs.AXISTEXTYOFFSET * self.deltaY), colour=labelColour, GLContext=self, obj=None)) # append the axisCode self._axisYLabelling.append(GLString(text=self._visibleOrderingAxisCodes[1] if self._visibleOrderingAxisCodes and len(self._visibleOrderingAxisCodes) > 1 else '*', font=smallFont, x=self.AXIS_OFFSET, y=1.0 - (GLDefs.TITLEYOFFSET * smallFont.charHeight * self.deltaY), colour=labelColour, GLContext=self, obj=None)) # and the axis dimensions yUnitsLabels = self.YAXES[self._yUnits] self._axisYLabelling.append(GLString(text=yUnitsLabels, font=smallFont, x=self.AXIS_OFFSET, y=1.0 * self.deltaY, colour=labelColour, GLContext=self, obj=None))
[docs] def drawAxisLabels(self): # draw axes labelling if self._axesVisible: self.buildAxisLabels() if self._drawBottomAxis: # put the axis labels into the bottom bar self.viewports.setViewport(self._currentBottomAxisBarView) self._axisScale[0:4] = [self.deltaX, 1.0, 1.0, 1.0] self.globalGL._shaderProgramTex.setAxisScale(self._axisScale) self.globalGL._shaderProgramTex.setProjectionAxes(self._uPMatrix, 0.0, 1.0, 0, self.AXIS_MARGINBOTTOM, -1.0, 1.0) self.globalGL._shaderProgramTex.setPTexMatrix(self._uPMatrix) for lb in self._axisXLabelling: lb.drawTextArrayVBO() if self._drawRightAxis: # put the axis labels into the right bar self.viewports.setViewport(self._currentRightAxisBarView) self._axisScale[0:4] = [1.0, self.deltaY, 1.0, 1.0] self.globalGL._shaderProgramTex.setAxisScale(self._axisScale) self.globalGL._shaderProgramTex.setProjectionAxes(self._uPMatrix, 0, self.AXIS_MARGINRIGHT, 0.0, 1.0, -1.0, 1.0) self.globalGL._shaderProgramTex.setPTexMatrix(self._uPMatrix) for lb in self._axisYLabelling: lb.drawTextArrayVBO()
[docs] def removeInfiniteLine(self, line): if line in self._infiniteLines: self._infiniteLines.remove(line) self.update()
[docs] def addInfiniteLine(self, values=None, axisCode=None, orientation=None, brush=None, colour='blue', movable=True, visible=True, bounds=None, obj=None, lineStyle='dashed', lineWidth=1.0): if colour in REGION_COLOURS.keys(): if colour == 'highlight': brush = self.highlightColour else: brush = REGION_COLOURS[colour] else: brush = colour if orientation == 'h': axisCode = self._axisCodes[1] elif orientation == 'v': axisCode = self._axisCodes[0] else: if axisCode: axisIndex = None for ps, psCode in enumerate(self._axisCodes[0:2]): if self._preferences.matchAxisCode == 0: # default - match atom type if axisCode[0] == psCode[0]: axisIndex = ps elif self._preferences.matchAxisCode == 1: # match full code if axisCode == psCode: axisIndex = ps if axisIndex == 0: orientation = 'v' elif axisIndex == 1: orientation = 'h' if not axisIndex: getLogger().warning('Axis code %s not found in current strip' % axisCode) return None else: axisCode = self._axisCodes[0] orientation = 'v' newInfiniteLine = GLInfiniteLine(self.strip, self._regionList, values=values, axisCode=axisCode, orientation=orientation, brush=brush, colour=colour, movable=movable, visible=visible, bounds=bounds, obj=obj, lineStyle=lineStyle, lineWidth=lineWidth) self._infiniteLines.append(newInfiniteLine) self.update() return newInfiniteLine
[docs] def removeExternalRegion(self, region): pass self._externalRegions._removeRegion(region) self._externalRegions.renderMode = GLRENDERMODE_REBUILD # if self._dragRegions[0] == region: # self._dragRegions = set() for reg in self._dragRegions: if reg[0] == region: self._dragRegions.remove(reg) break self.update()
[docs] def addExternalRegion(self, values=None, axisCode=None, orientation=None, brush=None, colour='blue', movable=True, visible=True, bounds=None, obj=None, **kwds): newRegion = self._externalRegions._addRegion(values=values, axisCode=axisCode, orientation=orientation, brush=brush, colour=colour, movable=movable, visible=visible, bounds=bounds, obj=obj, **kwds) self._externalRegions.renderMode = GLRENDERMODE_REBUILD self.update() return newRegion
[docs] def addRegion(self, values=None, axisCode=None, orientation=None, brush=None, colour='blue', movable=True, visible=True, bounds=None, obj=None): if colour in REGION_COLOURS.keys(): brush = REGION_COLOURS[colour] if orientation == 'h': axisCode = self._axisCodes[1] elif orientation == 'v': axisCode = self._axisCodes[0] else: if axisCode: axisIndex = None for ps, psCode in enumerate(self._axisCodes[0:2]): if self._preferences.matchAxisCode == 0: # default - match atom type if axisCode[0] == psCode[0]: axisIndex = ps elif self._preferences.matchAxisCode == 1: # match full code if axisCode == psCode: axisIndex = ps if axisIndex == 0: orientation = 'v' elif axisIndex == 1: orientation = 'h' if not axisIndex: getLogger().warning('Axis code %s not found in current strip' % axisCode) return None else: axisCode = self._axisCodes[0] orientation = 'v' newRegion = GLRegion(self.strip, self._regionList, values=values, axisCode=axisCode, orientation=orientation, brush=brush, colour=colour, movable=movable, visible=visible, bounds=bounds, obj=obj) self._regions.append(newRegion) self._regionList.renderMode = GLRENDERMODE_REBUILD self.update() return newRegion
[docs] def buildRegions(self): drawList = self._externalRegions if drawList.renderMode == GLRENDERMODE_REBUILD: drawList.renderMode = GLRENDERMODE_DRAW # back to draw mode drawList._rebuild() drawList.defineIndexVBO() elif drawList.renderMode == GLRENDERMODE_RESCALE: drawList.renderMode = GLRENDERMODE_DRAW # back to draw mode drawList._resize() drawList.defineIndexVBO()
[docs] def buildMarksRulers(self): drawList = self._marksList if drawList.renderMode == GLRENDERMODE_REBUILD: drawList.renderMode = GLRENDERMODE_DRAW # back to draw mode drawList.refreshMode = GLREFRESHMODE_REBUILD drawList.clearArrays() # clear the attached strings self._marksAxisCodes = [] # build the marks VBO index = 0 for mark in self.project.marks: # find the matching axisCodes to the display exactMatch = (self._preferences.matchAxisCode == AXIS_FULLATOMNAME) indices = getAxisCodeMatchIndices(mark.axisCodes, self._axisCodes[:2], exactMatch=exactMatch, allMatches=not exactMatch) for ii, rr in enumerate(mark.rulerData): axisIndices = makeIterableList(indices[ii]) for axisIndex in axisIndices: if axisIndex is not None and axisIndex < 2: # NOTE:ED check axis units - assume 'ppm' for the minute if axisIndex == 0: # vertical ruler pos = x0 = x1 = rr.position y0 = self.axisT y1 = self.axisB textX = pos + (3.0 * self.pixelX) textY = self.axisB + (3.0 * self.pixelY) else: # horizontal ruler pos = y0 = y1 = rr.position x0 = self.axisL x1 = self.axisR textX = self.axisL + (3.0 * self.pixelX) textY = pos + (3.0 * self.pixelY) colour = mark.colour colR = int(colour.strip('# ')[0:2], 16) / 255.0 colG = int(colour.strip('# ')[2:4], 16) / 255.0 colB = int(colour.strip('# ')[4:6], 16) / 255.0 drawList.indices = np.append(drawList.indices, np.array((index, index + 1), dtype=np.uint32)) drawList.vertices = np.append(drawList.vertices, np.array((x0, y0, x1, y1), dtype=np.float32)) drawList.colors = np.append(drawList.colors, np.array((colR, colG, colB, 1.0) * 2, dtype=np.float32)) drawList.attribs = np.append(drawList.attribs, (axisIndex, pos, axisIndex, pos)) # build the string and add the extra axis code label = rr.label if rr.label else rr.axisCode newMarkString = GLString(text=label, font=self.getSmallFont(), x=textX, y=textY, colour=(colR, colG, colB, 1.0), GLContext=self, obj=None) # this is in the attribs newMarkString.axisIndex = axisIndex newMarkString.axisPosition = pos self._marksAxisCodes.append(newMarkString) index += 2 drawList.numVertices += 2 drawList.defineIndexVBO() elif drawList.renderMode == GLRENDERMODE_RESCALE: drawList.renderMode = GLRENDERMODE_DRAW # back to draw mode drawList.defineIndexVBO()
[docs] def drawMarksRulers(self): if self.strip.isDeleted: return self._marksList.drawIndexVBO()
[docs] def drawRegions(self): if self.strip.isDeleted: return self._externalRegions.drawIndexVBO()
[docs] def drawMarksAxisCodes(self): if self.strip.isDeleted: return # strings are generated when the marksRulers are modified for mark in self._marksAxisCodes: mark.drawTextArrayVBO()
def _scaleAxisToRatio(self, values): return [((values[0] - self.axisL) / (self.axisR - self.axisL)) if values[0] is not None and abs(self.axisR - self.axisL) > 1e-9 else 0.0, ((values[1] - self.axisB) / (self.axisT - self.axisB)) if values[1] is not None and abs(self.axisT - self.axisB) > 1e-9 else 0.0]
[docs] def buildCursors(self): """Build and draw the cursors/doubleCursors """ if not self._disableCursorUpdate and self._crosshairVisible: # get the next cursor drawList self._advanceGLCursor() drawList = self._glCursorQueue[self._glCursorHead] vertices = [] indices = [] index = 0 # map the cursor to the ratio coordinates - double cursor is flipped about the line x=y cursorCoordinate = self.getCurrentCursorCoordinate() newCoords = self._scaleAxisToRatio(cursorCoordinate[0:2]) doubleCoords = self._scaleAxisToRatio(self.getCurrentDoubleCursorCoordinates()[0:2]) if getCurrentMouseMode() == PICK and self.underMouse(): x = self.deltaX * 8 y = self.deltaY * 8 vertices = [newCoords[0] - x, newCoords[1] - y, newCoords[0] + x, newCoords[1] - y, newCoords[0] + x, newCoords[1] - y, newCoords[0] + x, newCoords[1] + y, newCoords[0] + x, newCoords[1] + y, newCoords[0] - x, newCoords[1] + y, newCoords[0] - x, newCoords[1] + y, newCoords[0] - x, newCoords[1] - y ] indices = [0, 1, 2, 3, 4, 5, 6, 7] col = self.mousePickColour index = 8 else: col = self.foreground phasingFrame = self.spectrumDisplay.phasingFrame if not phasingFrame.isVisible(): _drawDouble = self._doubleCrosshairVisible # any([specView.spectrum.showDoubleCrosshair == True for specView in self._ordering]) if not self._updateVTrace and newCoords[0] is not None: vertices.extend([newCoords[0], 1.0, newCoords[0], 0.0]) indices.extend([index, index + 1]) index += 2 # add the double cursor if _drawDouble and self._matchingIsotopeCodes and doubleCoords[0] is not None and abs(doubleCoords[0] - newCoords[0]) > self.deltaX: vertices.extend([doubleCoords[0], 1.0, doubleCoords[0], 0.0]) indices.extend([index, index + 1]) index += 2 if not self._updateHTrace and newCoords[1] is not None: vertices.extend([0.0, newCoords[1], 1.0, newCoords[1]]) indices.extend([index, index + 1]) index += 2 # add the double cursor if _drawDouble and self._matchingIsotopeCodes and doubleCoords[1] is not None and abs(doubleCoords[1] - newCoords[1]) > self.deltaY: vertices.extend([0.0, doubleCoords[1], 1.0, doubleCoords[1]]) indices.extend([index, index + 1]) index += 2 drawList.vertices = np.array(vertices, dtype=np.float32) drawList.indices = np.array(indices, dtype=np.int32) drawList.numVertices = len(vertices) // 2 drawList.colors = np.array(col * drawList.numVertices, dtype=np.float32) # build and draw the VBO drawList.defineIndexVBO()
[docs] def drawLastCursors(self): """Draw the cursors/doubleCursors """ cursor = self._glCursorQueue[self._glCursorTail] if cursor.indices.size: cursor.drawIndexVBO()
[docs] def drawCursors(self): """Draw the cursors/doubleCursors """ cursor = self._glCursorQueue[self._glCursorHead] if cursor.indices.size: cursor.drawIndexVBO()
[docs] def getCurrentCursorCoordinate(self): if self.cursorSource == None or self.cursorSource == 'self': currentPos = self.mapFromGlobal(QtGui.QCursor.pos()) # calculate mouse coordinate within the mainView _mouseX = currentPos.x() if self._drawBottomAxis: _mouseY = self.height() - currentPos.y() - self.AXIS_MOUSEYOFFSET _top = self.height() - self.AXIS_MOUSEYOFFSET else: _mouseY = self.height() - currentPos.y() _top = self.height() # translate from screen (0..w, 0..h) to NDC (-1..1, -1..1) to axes (axisL, axisR, axisT, axisB) result = self.mouseTransform.dot([_mouseX, _mouseY, 0.0, 1.0]) else: result = self.cursorCoordinate return result
[docs] def getCurrentDoubleCursorCoordinates(self): if self.cursorSource in (CURSOR_SOURCE_NONE, CURSOR_SOURCE_SELF): cursorCoordinate = self.getCurrentCursorCoordinate() # flip cursor about x=y to get double cursor result = [cursorCoordinate[1], cursorCoordinate[0], cursorCoordinate[2], cursorCoordinate[3]] else: result = self.doubleCursorCoordinate return result
[docs] def drawDottedCursor(self): # draw the cursors # need to change to VBOs GL.glColor4f(*self.zoomLineColour) GL.glLineStipple(1, 0xF0F0) GL.glEnable(GL.GL_LINE_STIPPLE) succClick = self._scaleAxisToRatio(self._successiveClicks[0:2]) GL.glBegin(GL.GL_LINES) GL.glVertex2d(succClick[0], 1.0) GL.glVertex2d(succClick[0], 0.0) GL.glVertex2d(0.0, succClick[1]) GL.glVertex2d(1.0, succClick[1]) GL.glEnd() GL.glDisable(GL.GL_LINE_STIPPLE)
[docs] def setInfiniteLineColour(self, infLine, colour): for reg in self._infiniteLines: if reg == infLine: colR = int(colour.strip('# ')[0:2], 16) / 255.0 colG = int(colour.strip('# ')[2:4], 16) / 255.0 colB = int(colour.strip('# ')[4:6], 16) / 255.0 reg.brush = (colR, colG, colB, 1.0)
[docs] def drawInfiniteLines(self): # draw the simulated infinite lines - using deprecated GL :) GL.glDisable(GL.GL_BLEND) GL.glEnable(GL.GL_LINE_STIPPLE) for infLine in self._infiniteLines: if infLine.visible: GL.glColor4f(*infLine.brush) GL.glLineStipple(1, GLDefs.GLLINE_STYLES[infLine.lineStyle]) GL.glLineWidth(infLine.lineWidth * self.viewports.devicePixelRatio) GL.glBegin(GL.GL_LINES) if infLine.orientation == 'h': GL.glVertex2d(self.axisL, infLine.values) GL.glVertex2d(self.axisR, infLine.values) else: GL.glVertex2d(infLine.values, self.axisT) GL.glVertex2d(infLine.values, self.axisB) GL.glEnd() GL.glDisable(GL.GL_LINE_STIPPLE) GL.glLineWidth(GLDefs.GLDEFAULTLINETHICKNESS * self.viewports.devicePixelRatio)
[docs] def setStripID(self, name): self.stripIDLabel = name self._newStripID = True
[docs] def drawOverlayText(self, refresh=False): """Draw extra information to the screen """ # cheat for the moment if self._newStripID or self.stripIDString.renderMode == GLRENDERMODE_REBUILD: self.stripIDString.renderMode = GLRENDERMODE_DRAW self._newStripID = False if self.highlighted: colour = self.highlightColour else: colour = self.foreground smallFont = self.getSmallFont() self.stripIDString = GLString(text=self.stripIDLabel, font=smallFont, x=GLDefs.TITLEXOFFSET * smallFont.charWidth * self.deltaX, y=1.0 - (GLDefs.TITLEYOFFSET * smallFont.charHeight * self.deltaY), colour=colour, GLContext=self, obj=None, blendMode=False) self._oldStripIDLabel = self.stripIDLabel # Don't draw for the minute, but keep for print strip # # draw the strip ID to the screen # self.stripIDString.drawTextArrayVBO() # only display buttons in the first strip (not required in others) if self.AXISLOCKEDBUTTON and (self.AXISLOCKEDBUTTONALLSTRIPS or self.strip == self.strip.spectrumDisplay.strips[0]): if self._aspectRatioMode == 2: self._fixedStringTrue.drawTextArrayVBO() else: self._fixedStringFalse.drawTextArrayVBO() if self._aspectRatioMode == 1: self._lockedStringTrue.drawTextArrayVBO() else: self._lockedStringFalse.drawTextArrayVBO()
def _rescaleRegions(self): self._externalRegions._rescale() def _rescaleMarksRulers(self): vertices = self._marksList.numVertices if vertices: for pp in range(0, 2 * vertices, 4): axisIndex = int(self._marksList.attribs[pp]) axisPosition = self._marksList.attribs[pp + 1] if axisIndex == 0: offsets = [axisPosition, self.axisT, axisPosition, self.axisB] else: offsets = [self.axisL, axisPosition, self.axisR, axisPosition] self._marksList.vertices[pp:pp + 4] = offsets self._marksList.defineIndexVBO() def _rescaleMarksAxisCode(self, mark): vertices = mark.numVertices # mark.attribs[0][0] = axisIndex # mark.attribs[0][1] = axisPosition if vertices: if mark.axisIndex == 0: offsets = [mark.axisPosition + (GLDefs.MARKTEXTXOFFSET * self.pixelX), self.axisB + (GLDefs.MARKTEXTYOFFSET * self.pixelY)] else: offsets = [self.axisL + (GLDefs.MARKTEXTXOFFSET * self.pixelX), mark.axisPosition + (GLDefs.MARKTEXTYOFFSET * self.pixelY)] for pp in range(0, 3 * vertices, 3): mark.attribs[pp:pp + 2] = offsets # redefine the mark's VBOs mark.updateTextArrayVBOAttribs()
[docs] def rescaleMarksRulers(self): """rescale the marks """ self._rescaleMarksRulers() for mark in self._marksAxisCodes: self._rescaleMarksAxisCode(mark)
[docs] def setRightAxisVisible(self, axisVisible=True): """Set the visibility of the right axis """ self._drawRightAxis = axisVisible if self._drawRightAxis and self._drawBottomAxis: self._fullHeightRightAxis = self._fullWidthBottomAxis = False else: self._fullHeightRightAxis = self._fullWidthBottomAxis = True self.rescale(rescaleStaticHTraces=False) self.update()
[docs] def setBottomAxisVisible(self, axisVisible=True): """Set the visibility of the bottom axis """ self._drawBottomAxis = axisVisible if self._drawRightAxis and self._drawBottomAxis: self._fullHeightRightAxis = self._fullWidthBottomAxis = False else: self._fullHeightRightAxis = self._fullWidthBottomAxis = True self.rescale(rescaleStaticVTraces=False) self.update()
[docs] def getAxesVisible(self): """Get the visibility of the axes """ return (self._drawRightAxis, self._drawBottomAxis)
[docs] def setAxesVisible(self, rightAxisVisible=True, bottomAxisVisible=False): """Set the visibility of the axes """ self._drawRightAxis = rightAxisVisible self._drawBottomAxis = bottomAxisVisible
@property def axesVisible(self): return self._axesVisible @axesVisible.setter def axesVisible(self, visible): self._axesVisible = visible self.update()
[docs] def toggleAxes(self): self._axesVisible = not self._axesVisible self.update()
@property def showSpectraOnPhasing(self): return self._showSpectraOnPhasing @showSpectraOnPhasing.setter def showSpectraOnPhasing(self, visible): self._showSpectraOnPhasing = visible self.update()
[docs] def toggleShowSpectraOnPhasing(self): self._showSpectraOnPhasing = not self._showSpectraOnPhasing self.update()
@property def axisOrder(self): return self._axisOrder @axisOrder.setter def axisOrder(self, axisOrder): self._axisOrder = axisOrder @property def axisCodes(self): return self._axisCodes @axisCodes.setter def axisCodes(self, axisCodes): self._axisCodes = axisCodes @property def xUnits(self): return self._xUnits @xUnits.setter def xUnits(self, xUnits): self._xUnits = xUnits self._rescaleAllAxes() @property def yUnits(self): return self._yUnits @yUnits.setter def yUnits(self, yUnits): self._yUnits = yUnits self._rescaleAllAxes() @property def aspectRatioMode(self): return self._aspectRatioMode @aspectRatioMode.setter def aspectRatioMode(self, value): self._aspectRatioMode = value self._rescaleAllAxes() @property def aspectRatios(self): return self._aspectRatios @aspectRatios.setter def aspectRatios(self, value): self._aspectRatios = value self._rescaleAllAxes() @property def orderedAxes(self): return self._orderedAxes @orderedAxes.setter def orderedAxes(self, axes): self._orderedAxes = axes try: if self._orderedAxes[1] and self._orderedAxes[1].code == 'intensity': self.mouseFormat = " %s: %.3f\n %s: %.6g" self.diffMouseFormat = " d%s: %.3f\n d%s: %.6g" else: self.mouseFormat = " %s: %.3f\n %s: %.3f" self.diffMouseFormat = " d%s: %.3f\n d%s: %.3f" except: self.mouseFormat = " %s: %.3f %s: %.4g" self.diffMouseFormat = " d%s: %.3f d%s: %.4g" @property def updateHTrace(self): return self._updateHTrace @updateHTrace.setter def updateHTrace(self, visible): self._updateHTrace = visible @property def updateVTrace(self): return self._updateVTrace @updateVTrace.setter def updateVTrace(self, visible): self._updateVTrace = visible
[docs] def buildMouseCoords(self, refresh=False): def valueToRatio(val, x0, x1): if abs(x1 - x0) > 1e-9: return (val - x0) / (x1 - x0) else: return 0.0 cursorCoordinate = self.getCurrentCursorCoordinate() if refresh or self._widthsChangedEnough(cursorCoordinate[:2], self._mouseCoords[:2], tol=1e-8): if not self._drawDeltaOffset: self._startCoordinate = cursorCoordinate # get the list of visible spectrumViews, or the first in the list visibleSpectrumViews = [specView for specView in self._ordering if not specView.isDeleted and specView.isDisplayed] thisSpecView = visibleSpectrumViews[0] if visibleSpectrumViews else self._ordering[0] if self._ordering and not self._ordering[ 0].isDeleted else None if thisSpecView: thisSpec = thisSpecView.spectrum # generate different axes depending on units - X Axis if self.XAXES[self._xUnits] == GLDefs.AXISUNITSPPM: cursorX = cursorCoordinate[0] startX = self._startCoordinate[0] # XMode = '%.3f' elif self.XAXES[self._xUnits] == GLDefs.AXISUNITSHZ: if self._ordering: if self.is1D: cursorX = cursorCoordinate[0] * thisSpec.spectrometerFrequencies[0] startX = cursorCoordinate[0] * thisSpec.spectrometerFrequencies[0] else: # get the axis ordering from the spectrumDisplay and map to the strip indices = self._spectrumSettings[thisSpecView][GLDefs.SPECTRUM_POINTINDEX] cursorX = cursorCoordinate[0] * thisSpec.spectrometerFrequencies[indices[0]] startX = self._startCoordinate[0] * thisSpec.spectrometerFrequencies[indices[0]] else: # error trap all spectra deleted cursorX = cursorCoordinate[0] startX = self._startCoordinate[0] else: if self._ordering: if self.is1D: cursorX = thisSpec.spectrumDimensions[0].valueToPoint(cursorCoordinate[0]) startX = thisSpec.spectrumDimensions[0].valueToPoint(cursorCoordinate[0]) else: # get the axis ordering from the spectrumDisplay and map to the strip indices = self._spectrumSettings[thisSpecView][GLDefs.SPECTRUM_POINTINDEX] # map to a point cursorX = thisSpec.spectrumDimensions[indices[0]].valueToPoint(cursorCoordinate[0]) startX = thisSpec.spectrumDimensions[indices[0]].valueToPoint(self._startCoordinate[0]) else: # error trap all spectra deleted cursorX = cursorCoordinate[0] startX = self._startCoordinate[0] # generate different axes depending on units - Y Axis, always use first option for 1d if self.is1D: cursorY = cursorCoordinate[1] startY = self._startCoordinate[1] elif self.YAXES[self._yUnits] == GLDefs.AXISUNITSPPM: cursorY = cursorCoordinate[1] startY = self._startCoordinate[1] elif self.YAXES[self._yUnits] == GLDefs.AXISUNITSHZ: if self._ordering: # get the axis ordering from the spectrumDisplay and map to the strip indices = self._spectrumSettings[thisSpecView][GLDefs.SPECTRUM_POINTINDEX] cursorY = cursorCoordinate[1] * thisSpec.spectrometerFrequencies[indices[1]] startY = self._startCoordinate[1] * thisSpec.spectrometerFrequencies[indices[1]] else: # error trap all spectra deleted cursorY = cursorCoordinate[1] startY = self._startCoordinate[1] else: if self._ordering: # get the axis ordering from the spectrumDisplay and map to the strip indices = self._spectrumSettings[thisSpecView][GLDefs.SPECTRUM_POINTINDEX] # map to a point cursorY = thisSpec.spectrumDimensions[indices[1]].valueToPoint(cursorCoordinate[1]) startY = thisSpec.spectrumDimensions[indices[1]].valueToPoint(self._startCoordinate[1]) else: # error trap all spectra deleted cursorY = cursorCoordinate[1] startY = self._startCoordinate[1] else: # no visible spectra return deltaOffset = 0 newCoords = ' %s: %s\n %s: %s' % (self._visibleOrderingAxisCodes[0], self.XMode(cursorX), self._visibleOrderingAxisCodes[1], self.YMode(cursorY)) smallFont = self.getSmallFont() if self._drawDeltaOffset: newCoords += '\n d%s: %s\n d%s: %s' % (self._visibleOrderingAxisCodes[0], self.XMode(cursorX - startX), self._visibleOrderingAxisCodes[1], self.YMode(cursorY - startY)) deltaOffset = smallFont.charHeight * 2.0 * self.pixelY mx, my = cursorCoordinate[0], cursorCoordinate[1] - deltaOffset self.mouseString = GLString(text=newCoords, font=smallFont, x=valueToRatio(mx, self.axisL, self.axisR), y=valueToRatio(my, self.axisB, self.axisT), colour=self.foreground, GLContext=self, obj=None) self._mouseCoords = (cursorCoordinate[0], cursorCoordinate[1]) # check that the string is actually visible, or constraint to the bounds of the strip _offset = self.pixelX * 80.0 _mouseOffsetR = valueToRatio(mx + _offset, self.axisL, self.axisR) _mouseOffsetL = valueToRatio(mx, self.axisL, self.axisR) ox = -min(max(_mouseOffsetR - 1.0, 0.0), _mouseOffsetL) _offset = self.pixelY * self.mouseString.height _mouseOffsetT = valueToRatio(my + _offset, self.axisB, self.axisT) _mouseOffsetB = valueToRatio(my, self.axisB, self.axisT) oy = -min(max(_mouseOffsetT - 1.0, 0.0), _mouseOffsetB) self.mouseString.setStringOffset((ox, oy)) self.mouseString.updateTextArrayVBOAttribs()
[docs] def drawMouseCoords(self): # if self.underMouse() or self._disableCursorUpdate: # and self.mouseString: # crosshairVisible # self.buildMouseCoords() if self.underMouse(): if self.mouseString is not None: # draw the mouse coordinates to the screen self.mouseString.drawTextArrayVBO()
[docs] def drawSelectionBox(self): # should really use the proper VBOs for this if self._drawSelectionBox: GL.glEnable(GL.GL_BLEND) self._dragStart = self._scaleAxisToRatio(self._startCoordinate[0:2]) self._dragEnd = self._scaleAxisToRatio(self._endCoordinate[0:2]) if self._selectionMode == 1: # yellow GL.glColor4f(*self.zoomAreaColour) elif self._selectionMode == 2: # purple GL.glColor4f(*self.selectAreaColour) elif self._selectionMode == 3: # cyan GL.glColor4f(*self.pickAreaColour) else: # red GL.glColor4f(*self.badAreaColour) GL.glBegin(GL.GL_QUADS) GL.glVertex2d(self._dragStart[0], self._dragStart[1]) GL.glVertex2d(self._dragEnd[0], self._dragStart[1]) GL.glVertex2d(self._dragEnd[0], self._dragEnd[1]) GL.glVertex2d(self._dragStart[0], self._dragEnd[1]) GL.glEnd() if self._selectionMode == 1: # yellow GL.glColor4f(*self.zoomAreaColourHard) elif self._selectionMode == 2: # purple GL.glColor4f(*self.selectAreaColourHard) elif self._selectionMode == 3: # cyan GL.glColor4f(*self.pickAreaColourHard) else: # red GL.glColor4f(*self.badAreaColourHard) GL.glBegin(GL.GL_LINE_STRIP) GL.glVertex2d(self._dragStart[0], self._dragStart[1]) GL.glVertex2d(self._dragEnd[0], self._dragStart[1]) GL.glVertex2d(self._dragEnd[0], self._dragEnd[1]) GL.glVertex2d(self._dragStart[0], self._dragEnd[1]) GL.glVertex2d(self._dragStart[0], self._dragStart[1]) GL.glEnd() GL.glDisable(GL.GL_BLEND)
[docs] def drawMouseMoveLine(self): """Draw the line for the middleMouse dragging of peaks """ if self._drawMouseMoveLine: GL.glColor4f(*self.mouseMoveLineColour) GL.glBegin(GL.GL_LINES) cursorCoordinate = self.getCurrentCursorCoordinate() startCoord = self._scaleAxisToRatio(self._startCoordinate[0:2]) cursCoord = self._scaleAxisToRatio(cursorCoordinate[0:2]) GL.glVertex2d(startCoord[0], startCoord[1]) GL.glVertex2d(cursCoord[0], cursCoord[1]) GL.glEnd()
def _getSliceData(self, spectrumView, points, sliceDim): """Get the slice along sliceDim, using spectrumView to get to spectrum Separate routine to allow for caching, uses Spectrum._getSliceDataFromPlane for efficient extraction of slices points as integer list, with points[sliceDim-1] set to 1, as this allows the cached _getSliceFromPlane to work best return sliceData numpy array """ #TODO: Move functionality to SpectrumView class # need to block logging with notificationEchoBlocking(self.application): axisCodes = [a.code for a in spectrumView.strip.axes][0:2] # planeDims = spectrumView.spectrum.getByAxisCodes('dimensions', axisCodes) pointCounts = spectrumView.spectrum.pointCounts pointInt = [int(round(pnt) % pointCounts[ii]) + 1 for ii, pnt in enumerate(points)] pointInt[sliceDim - 1] = 1 # To improve caching; points, dimensions are 1-based # GWV: why copy again into numpy array? the routine already returns this # data = np.array(spectrumView.spectrum._getSliceDataFromPlane(position=pointInt, # xDim=planeDims[0], yDim=planeDims[1], sliceDim=sliceDim)) # GWV reverted to getSliceData data = spectrumView.spectrum.getSliceData(position=pointInt, sliceDim=sliceDim) if spectrumView._traceScale is None: spectrumView._traceScale = 1.0 / max(data) * 0.5 return data def _getSliceDataTest(self, spectrumView, points, sliceDim, ppmPositions, range): # quick method for testing # need to block logging with notificationEchoBlocking(self.application): code = spectrumView.spectrum.axisCodes[0] vpps = spectrumView.spectrum.ppmPerPoints axisCodes = spectrumView.spectrum.axisCodes _region = {axis: (ppmPosition + 0.5 * vpp, ppmPosition + 0.5 * vpp) for axis, ppmPosition, vpp, in zip(axisCodes, ppmPositions, vpps)} _region[code] = sorted(range) data = spectrumView.spectrum.getRegion(**_region) if spectrumView._traceScale is None: spectrumView._traceScale = 1.0 / max(data) * 0.5 return data #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Code for traces #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ def _newStaticHTraceData(self, spectrumView, tracesDict, point, dim, positionPixel): try: data = self._getSliceData(spectrumView=spectrumView, points=point, sliceDim=dim) x = spectrumView.spectrum.getPpmArray(dimension=dim) _posColour = spectrumView.posColours[0] colR, colG, colB = _posColour[0:3] hSpectrum = GLVertexArray(numLists=1, renderMode=GLRENDERMODE_RESCALE, blendMode=False, drawMode=GL.GL_LINE_STRIP, dimension=2, GLContext=self) tracesDict.append(hSpectrum) # add extra vertices to give a horizontal line across the trace xLen = x.size x = np.append(x, (x[xLen - 1], x[0])) numVertices = len(x) hSpectrum.indices = numVertices hSpectrum.numVertices = numVertices hSpectrum.indices = np.arange(numVertices, dtype=np.uint32) hSpectrum.vertices = np.empty(hSpectrum.numVertices * 2, dtype=np.float32) hSpectrum.vertices[::2] = x hSpectrum.colors = np.array(self._phasingTraceColour * numVertices, dtype=np.float32) # change to colour of the last 2 points to the spectrum colour colLen = hSpectrum.colors.size hSpectrum.colors[colLen - 8:colLen] = (colR, colG, colB, 1.0, colR, colG, colB, 1.0) # store the pre-phase data hSpectrum.data = data hSpectrum.positionPixel = positionPixel hSpectrum.spectrumView = spectrumView except Exception as es: tracesDict = [] def _newStaticVTraceData(self, spectrumView, tracesDict, point, dim, positionPixel): try: data = self._getSliceData(spectrumView=spectrumView, points=point, sliceDim=dim) y = spectrumView.spectrum.getPpmArray(dimension=dim) _posColour = spectrumView.posColours[0] colR, colG, colB = _posColour[0:3] vSpectrum = GLVertexArray(numLists=1, renderMode=GLRENDERMODE_RESCALE, blendMode=False, drawMode=GL.GL_LINE_STRIP, dimension=2, GLContext=self) tracesDict.append(vSpectrum) # add extra vertices to give a horizontal line across the trace yLen = y.size y = np.append(y, (y[yLen - 1], y[0])) numVertices = len(y) vSpectrum.indices = numVertices vSpectrum.numVertices = numVertices vSpectrum.indices = np.arange(numVertices, dtype=np.uint32) vSpectrum.vertices = np.empty(vSpectrum.numVertices * 2, dtype=np.float32) vSpectrum.vertices[1::2] = y vSpectrum.colors = np.array(self._phasingTraceColour * numVertices, dtype=np.float32) # change to colour of the last 2 points to the spectrum colour colLen = vSpectrum.colors.size vSpectrum.colors[colLen - 8:colLen] = (colR, colG, colB, 1.0, colR, colG, colB, 1.0) # store the pre-phase data vSpectrum.data = data vSpectrum.positionPixel = positionPixel vSpectrum.spectrumView = spectrumView except Exception as es: tracesDict = [] def _updateHTraceData(self, spectrumView, tracesDict, point, dim, positionPixel, ph0=None, ph1=None, pivot=None): try: data = self._getSliceData(spectrumView=spectrumView, points=point, sliceDim=dim) if ph0 is not None and ph1 is not None and pivot is not None: data = Phasing.phaseRealData(data, ph0, ph1, pivot) x = spectrumView.spectrum.getPpmArray(dimension=dim) # x = spectrumView.spectrum.getPpmAliasingLimitsArray(dimension=dim) # this seems okay - need to check _getSliceData # data = self._getSliceDataTest(spectrumView=spectrumView, points=point, sliceDim=dim, # ppmPositions=positionPixel, range=[x[0], x[-1]]) # data = data[0] # if ph0 is not None and ph1 is not None and pivot is not None: # data = Phasing.phaseRealData(data, ph0, ph1, pivot) y = positionPixel[1] + spectrumView._traceScale * (self.axisT - self.axisB) * data _posColour = spectrumView.posColours[0] colR, colG, colB = _posColour[0:3] if spectrumView not in tracesDict.keys(): tracesDict[spectrumView] = GLVertexArray(numLists=1, renderMode=GLRENDERMODE_REBUILD, blendMode=False, drawMode=GL.GL_LINE_STRIP, dimension=2, GLContext=self) # add extra vertices to give a horizontal line across the trace xLen = x.size x = np.append(x, (x[xLen - 1], x[0])) y = np.append(y, (positionPixel[1], positionPixel[1])) numVertices = len(x) hSpectrum = tracesDict[spectrumView] hSpectrum.indices = numVertices hSpectrum.numVertices = numVertices hSpectrum.indices = np.arange(numVertices, dtype=np.uint32) hSpectrum.vertices = np.empty(numVertices * 2, dtype=np.float32) hSpectrum.vertices[::2] = x hSpectrum.vertices[1::2] = y hSpectrum.colors = np.array([colR, colG, colB, 1.0] * numVertices, dtype=np.float32) # colour the negative points - gives a nice fade if self._preferences.traceIncludeNegative: _negColour = spectrumView.negColours[0] _negCol = [*_negColour[0:3], 1.0] mask = np.nonzero(data < 0.0) for mm in mask[0]: m4 = mm * 4 hSpectrum.colors[m4:m4 + 4] = _negCol except Exception as es: tracesDict[spectrumView].clearArrays() def _updateVTraceData(self, spectrumView, tracesDict, point, dim, positionPixel, ph0=None, ph1=None, pivot=None): try: data = self._getSliceData(spectrumView=spectrumView, points=point, sliceDim=dim) if ph0 is not None and ph1 is not None and pivot is not None: data = Phasing.phaseRealData(data, ph0, ph1, pivot) y = spectrumView.spectrum.getPpmArray(dimension=dim) x = positionPixel[0] + spectrumView._traceScale * (self.axisL - self.axisR) * data _posColour = spectrumView.posColours[0] colR, colG, colB = _posColour[0:3] if spectrumView not in tracesDict.keys(): tracesDict[spectrumView] = GLVertexArray(numLists=1, renderMode=GLRENDERMODE_REBUILD, blendMode=False, drawMode=GL.GL_LINE_STRIP, dimension=2, GLContext=self) # add extra vertices to give a vertical line across the trace yLen = y.size y = np.append(y, (y[yLen - 1], y[0])) x = np.append(x, (positionPixel[0], positionPixel[0])) numVertices = len(x) vSpectrum = tracesDict[spectrumView] vSpectrum.indices = numVertices vSpectrum.numVertices = numVertices vSpectrum.indices = np.arange(numVertices, dtype=np.uint32) vSpectrum.vertices = np.zeros(numVertices * 2, dtype=np.float32) vSpectrum.vertices[::2] = x vSpectrum.vertices[1::2] = y vSpectrum.colors = np.array([colR, colG, colB, 1.0] * numVertices, dtype=np.float32) # colour the negative points - gives a nice fade if self._preferences.traceIncludeNegative: _negColour = spectrumView.negColours[0] _negCol = [*_negColour[0:3], 1.0] mask = np.nonzero(data < 0.0) for mm in mask[0]: m4 = mm * 4 vSpectrum.colors[m4:m4 + 4] = _negCol except Exception as es: tracesDict[spectrumView].clearArrays() # NOTE:ED - remember these for later, may create larger vertex arrays for symbols, but should be quicker # -- # x = np.array([1, 2, 3, -1, 5, 0, 3, 4, 4, 7, 3, 5, 9, 0, 5, 4, 3], dtype=np.uint32) # seems to be the fastest way of getting masked values # SKIPINDEX = np.uint32(-1) = 4294967295 # i.e. max index number, use as fill # timeit.timeit('import numpy as np; x = np.array([1, 2, 3, -1, 5, 0, 3, 4, 4, 7, 3, 5, 9, 0, 5, 4, 3], dtype=np.uint32); x[np.where(x != 3)]', number=200000) # fastest way to create filled arrays # *** timeit.timeit('import numpy as np; x = np.array([1, 2, 3, -1, 5, 0, 3, 4, 4, 7, 3, 5, 9, 0, 5, 4, 3], dtype=np.uint32); a = x[x != SKIPINDEX]', number=200000) # timeit.timeit('import numpy as np; x = np.array([1, 2, 3, -1, 5, 0, 3, 4, 4, 7, 3, 5, 9, 0, 5, 4, 3], dtype=np.uint32); mx = np.full(200000, SKIPINDEX, dtype=np.uint32)', number=20000) # -- # np.take(x, np.where(x != 3)) # mx = np.ma.masked_values(x, 3) # a = x[np.where(x != 3)] # *** a = x[x != SKIPINDEX] # np.where(data < 0)[0] * 4
[docs] def updateTraces(self): if self.strip.isDeleted: return cursorCoordinate = self.getCurrentCursorCoordinate() position = [cursorCoordinate[0], cursorCoordinate[1]] #list(cursorPosition) for axis in self._orderedAxes[2:]: position.append(axis.position) for spectrumView in self._ordering: # strip.spectrumViews: if spectrumView.isDeleted: continue if self.showActivePhaseTrace and self._tracesNeedUpdating(spectrumView): phasingFrame = self.spectrumDisplay.phasingFrame dimension = spectrumView.dimensionIndices ppm2point = spectrumView.spectrum.ppm2point point2ppm = spectrumView.spectrum.point2ppm if phasingFrame.isVisible(): ph0 = phasingFrame.slider0.value() ph1 = phasingFrame.slider1.value() pivotPpm = phasingFrame.pivotEntry.get() direction = phasingFrame.getDirection() axisIndex = dimension[direction] pivot = ppm2point(pivotPpm, dimension=axisIndex + 1) - 1 else: direction = 0 ph0 = ph1 = pivot = None # map to the spectrum pointPositions point = [ppm2point(position[dimension.index(n)], dimension=n + 1) - 1 for n in range(len(position))] intPositionPixel = [point2ppm(round(point[n]) + 1, dimension=n + 1) for n in dimension] if direction == 0: if self._updateHTrace: self._updateHTraceData(spectrumView, self._hTraces, point, dimension[0] + 1, intPositionPixel, ph0, ph1, pivot) if self._updateVTrace: self._updateVTraceData(spectrumView, self._vTraces, point, dimension[1] + 1, intPositionPixel) else: # pivots are on the opposite axis if self._updateHTrace: self._updateHTraceData(spectrumView, self._hTraces, point, dimension[0] + 1, intPositionPixel) if self._updateVTrace: self._updateVTraceData(spectrumView, self._vTraces, point, dimension[1] + 1, intPositionPixel, ph0, ph1, pivot)
[docs] def newTrace(self, position=None): # cursorCoordinate = self.getCurrentCursorCoordinate(self.cursorCoordinate) cursorCoordinate = self.current.cursorPosition position = position if position else [cursorCoordinate[0], cursorCoordinate[1]] # add to the list of traces self._currentTraces.append(position) for axis in self._orderedAxes[2:]: position.append(axis.position) for spectrumView in self._ordering: # strip.spectrumViews: if spectrumView.isDeleted: continue # only add phasing trace for the visible spectra if spectrumView.isDisplayed: phasingFrame = self.spectrumDisplay.phasingFrame ph0 = phasingFrame.slider0.value() ph1 = phasingFrame.slider1.value() pivotPpm = phasingFrame.pivotEntry.get() direction = phasingFrame.getDirection() dimension = spectrumView.dimensionIndices ppm2point = spectrumView.spectrum.ppm2point point2ppm = spectrumView.spectrum.point2ppm if self.is1D: pivot = spectrumView.spectrum.ppm2point(pivotPpm, dimension=1) - 1 self._newStatic1DTraceData(spectrumView, self._staticHTraces, position, ph0, ph1, pivot) else: # map to the spectrum pointPositions point = [ppm2point(position[dimension.index(n)], dimension=n + 1) - 1 for n in range(len(position))] intPositionPixel = [point2ppm(round(point[n]) + 1, dimension=n + 1) for n in dimension] if direction == 0: self._newStaticHTraceData(spectrumView, self._staticHTraces, point, dimension[0] + 1, intPositionPixel) else: self._newStaticVTraceData(spectrumView, self._staticVTraces, point, dimension[1] + 1, intPositionPixel)
[docs] def clearStaticTraces(self): self._staticVTraces = [] self._staticHTraces = [] self._currentTraces = [] self.update()
[docs] def rescaleStaticTraces(self): for hTrace in self._staticHTraces: hTrace.renderMode = GLRENDERMODE_RESCALE for vTrace in self._staticVTraces: vTrace.renderMode = GLRENDERMODE_RESCALE # need to update the current active trace - force reset of lastVisible trace for spectrumView in self._ordering: numDim = len(spectrumView.strip.axes) self._lastTracePoint[spectrumView] = [-1] * numDim # rebuild traces self.updateTraces() self.update()
[docs] def rescaleStaticHTraces(self): for hTrace in self._staticHTraces: hTrace.renderMode = GLRENDERMODE_RESCALE
[docs] def rescaleStaticVTraces(self): for vTrace in self._staticVTraces: vTrace.renderMode = GLRENDERMODE_RESCALE
[docs] def rebuildTraces(self): traces = self._currentTraces self.clearStaticTraces() for trace in traces: self.newTrace(trace[:2])
[docs] def buildStaticTraces(self): phasingFrame = self.spectrumDisplay.phasingFrame if phasingFrame.isVisible(): ph0 = phasingFrame.slider0.value() ph1 = phasingFrame.slider1.value() pivotPpm = phasingFrame.pivotEntry.get() direction = phasingFrame.getDirection() deleteHList = [] for hTrace in self._staticHTraces: specView = hTrace.spectrumView if specView and specView.isDeleted: deleteHList.append(hTrace) continue if hTrace.renderMode == GLRENDERMODE_RESCALE: hTrace.renderMode = GLRENDERMODE_DRAW axisIndex = specView.dimensionIndices[direction] pivot = specView.spectrum.ppm2point(pivotPpm, dimension=axisIndex + 1) positionPixel = hTrace.positionPixel preData = Phasing.phaseRealData(hTrace.data, ph0, ph1, pivot) if self.is1D: hTrace.vertices[1::2] = preData else: y = positionPixel[1] + specView._traceScale * (self.axisT - self.axisB) * preData y = np.append(y, (positionPixel[1], positionPixel[1])) hTrace.vertices[1::2] = y # build the VBOs here hTrace.defineVertexColorVBO() for dd in deleteHList: self._staticHTraces.remove(dd) deleteVList = [] for vTrace in self._staticVTraces: specView = vTrace.spectrumView if specView and specView.isDeleted: deleteVList.append(vTrace) continue if vTrace.renderMode == GLRENDERMODE_RESCALE: vTrace.renderMode = GLRENDERMODE_DRAW axisIndex = specView.dimensionIndices[direction] pivot = specView.spectrum.ppm2point(pivotPpm, dimension=axisIndex + 1) positionPixel = vTrace.positionPixel preData = Phasing.phaseRealData(vTrace.data, ph0, ph1, pivot) x = positionPixel[0] + specView._traceScale * (self.axisL - self.axisR) * preData x = np.append(x, (positionPixel[0], positionPixel[0])) vTrace.vertices[::2] = x # build the VBOs here vTrace.defineVertexColorVBO() for dd in deleteVList: self._staticVTraces.remove(dd)
[docs] def drawTraces(self): if self.strip.isDeleted: return phasingFrame = self.spectrumDisplay.phasingFrame if phasingFrame.isVisible(): for hTrace in self._staticHTraces: if hTrace.spectrumView and not hTrace.spectrumView.isDeleted and hTrace.spectrumView.isDisplayed: if self._stackingMode: # use the stacking matrix to offset the 1D spectra self.globalGL._shaderProgram1.setMVMatrix(self._spectrumSettings[hTrace.spectrumView][ GLDefs.SPECTRUM_STACKEDMATRIX]) hTrace.drawVertexColorVBO() for vTrace in self._staticVTraces: if vTrace.spectrumView and not vTrace.spectrumView.isDeleted and vTrace.spectrumView.isDisplayed: # vTrace.drawVertexColor() if self._stackingMode: # use the stacking matrix to offset the 1D spectra self.globalGL._shaderProgram1.setMVMatrix(self._spectrumSettings[vTrace.spectrumView][ GLDefs.SPECTRUM_STACKEDMATRIX]) vTrace.drawVertexColorVBO() # only paint if mouse is in the window, or menu has been raised in this strip if self.underMouse() or self._menuActive: deleteHList = [] if self._updateHTrace and (self.showActivePhaseTrace or not phasingFrame.isVisible()): for hTrace in self._hTraces.keys(): trace = self._hTraces[hTrace] if hTrace and hTrace.isDeleted: deleteHList.append(hTrace) continue if hTrace and not hTrace.isDeleted and hTrace.isVisible(): # trace.drawVertexColor() trace.defineVertexColorVBO() trace.drawVertexColorVBO() for dd in deleteHList: del self._hTraces[dd] deleteVList = [] if self._updateVTrace and (self.showActivePhaseTrace or not phasingFrame.isVisible()): for vTrace in self._vTraces.keys(): trace = self._vTraces[vTrace] if vTrace and vTrace.isDeleted: deleteVList.append(vTrace) continue if vTrace and not vTrace.isDeleted and vTrace.isVisible(): # trace.drawVertexColor() trace.defineVertexColorVBO() trace.drawVertexColorVBO() for dd in deleteVList: del self._vTraces[dd]
# NOTE:ED - don't delete this yet # def _makeSpectrumArray(self, spectrumView, drawList): # drawList.renderMode = GLRENDERMODE_DRAW # drawList.clearArrays() # # for position, dataArray in spectrumView._getPlaneData(): # ma = np.amax(dataArray) # mi = np.amin(dataArray) # # min( abs( fract(P.z * gsize) - 0.5), 0.2); # # newData = np.clip(np.absolute(np.remainder((50.0*dataArray/ma), 1.0)-0.5), 0.2, 50.0) # dataScale = 15.0 # newData = dataScale * dataArray / ma # npX = dataArray.shape[0] # npY = dataArray.shape[1] # # indexing = (npX - 1) * (npY - 1) # elements = npX * npY # drawList.indices = np.zeros(int(indexing * 6), dtype=np.uint32) # drawList.vertices = np.zeros(int(elements * 4), dtype=np.float32) # drawList.colors = np.zeros(int(elements * 4), dtype=np.float32) # # ii = 0 # for y0 in range(0, npY): # for x0 in range(0, npX): # vertex = [y0, x0, newData[x0, y0], 0.5 + newData[x0, y0] / (2.0 * dataScale)] # color = [0.5, 0.5, 0.5, 1.0] # drawList.vertices[ii * 4:ii * 4 + 4] = vertex # drawList.colors[ii * 4:ii * 4 + 4] = color # ii += 1 # drawList.numVertices = elements # # ii = 0 # for y0 in range(0, npY - 1): # for x0 in range(0, npX - 1): # corner = x0 + (y0 * npX) # indices = [corner, corner + 1, corner + npX, # corner + 1, corner + npX, corner + 1 + npX] # drawList.indices[ii * 6:ii * 6 + 6] = indices # ii += 1 # break # def sizeHint(self): # return QSize(self.w, self.h)
[docs] def set3DProjection(self): GL.glMatrixMode(GL.GL_PROJECTION) GL.glLoadIdentity() GL.glOrtho(-0.5, +0.5, +0.5, -0.5, 4.0, 15.0) GL.glMatrixMode(GL.GL_MODELVIEW) GL.glLoadIdentity()
[docs] def setClearColor(self, c): GL.glClearColor(c.redF(), c.greenF(), c.blueF(), c.alphaF())
[docs] def setColor(self, c): GL.glColor4f(c.redF(), c.greenF(), c.blueF(), c.alphaF())
[docs] def highlightCurrentStrip(self, current): if current: self.highlighted = True self._updateAxes = True for gr in self.gridList: gr.renderMode = GLRENDERMODE_REBUILD if self.stripIDString: self.stripIDString.renderMode = GLRENDERMODE_REBUILD else: self.highlighted = False self._updateAxes = True for gr in self.gridList: gr.renderMode = GLRENDERMODE_REBUILD if self.stripIDString: self.stripIDString.renderMode = GLRENDERMODE_REBUILD self.update()
def _buildAxes(self, gridGLList, axisList=None, scaleGrid=None, r=0.0, g=0.0, b=0.0, transparency=256.0, _includeDiagonal=False, _diagonalList=None, _includeAxis=True): """Build the grid """ def getDigit(ll, order): ss = '{0:.1f}'.format(ll[3] / order) valLen = len(ss) return ss[valLen - 3] def getDigitLen(ll, order): ss = '{0:.1f}'.format(ll[3] / order) return len(ss) def check(ll): # check if a number ends in an even digit val = '%.0f' % (ll[3] / ll[4]) valLen = len(val) if val[valLen - 1] in '02468': return True def valueToRatio(val, x0, x1): return (val - x0) / (x1 - x0) labelling = {'0': {}, '1': {}} axesChanged = False _minPow = None _minOrder = None _minSet = False _labelRestrictList = ('0123456789', '0258', '05', '0') # check if the width is too small to draw too many grid levels boundX = (self.w - self.AXIS_MARGINRIGHT) if self._drawRightAxis else self.w boundY = (self.h - self.AXIS_MOUSEYOFFSET) if self._drawBottomAxis else self.h scaleBounds = (boundX, boundY) if gridGLList.renderMode == GLRENDERMODE_REBUILD: # get the list of visible spectrumViews, or the first in the list visibleSpectrumViews = [specView for specView in self._ordering if not specView.isDeleted and specView.isDisplayed] thisSpecView = visibleSpectrumViews[0] if visibleSpectrumViews else self._ordering[0] if self._ordering and not self._ordering[ 0].isDeleted else None if thisSpecView: thisSpec = thisSpecView.spectrum # generate different axes depending on units - X Axis if self.XAXES[self._xUnits] == GLDefs.AXISUNITSPPM: axisLimitL = self.axisL axisLimitR = self.axisR self.XMode = self._floatFormat elif self.XAXES[self._xUnits] == GLDefs.AXISUNITSHZ: if self._ordering: if self.is1D: axisLimitL = self.axisL * thisSpec.spectrometerFrequencies[0] axisLimitR = self.axisR * thisSpec.spectrometerFrequencies[0] else: # get the axis ordering from the spectrumDisplay and map to the strip indices = self._spectrumSettings[thisSpecView][GLDefs.SPECTRUM_POINTINDEX] axisLimitL = self.axisL * thisSpec.spectrometerFrequencies[indices[0]] axisLimitR = self.axisR * thisSpec.spectrometerFrequencies[indices[0]] else: # error trap all spectra deleted axisLimitL = self.axisL axisLimitR = self.axisR self.XMode = self._floatFormat else: if self._ordering: if self.is1D: axisLimitL = thisSpec.spectrumDimensions[0].valueToPoint(self.axisL) axisLimitR = thisSpec.spectrumDimensions[0].valueToPoint(self.axisR) else: # get the axis ordering from the spectrumDisplay and map to the strip indices = self._spectrumSettings[thisSpecView][GLDefs.SPECTRUM_POINTINDEX] # map to a point axisLimitL = thisSpec.spectrumDimensions[indices[0]].valueToPoint(self.axisL) axisLimitR = thisSpec.spectrumDimensions[indices[0]].valueToPoint(self.axisR) else: # error trap all spectra deleted axisLimitL = self.axisL axisLimitR = self.axisR self.XMode = self._intFormat # generate different axes depending on units - Y Axis, always use first option for 1d if self.is1D: axisLimitT = self.axisT axisLimitB = self.axisB self.YMode = self._eFormat # '%.6g' elif self.YAXES[self._yUnits] == GLDefs.AXISUNITSPPM: axisLimitT = self.axisT axisLimitB = self.axisB self.YMode = self._floatFormat # '%.3f' elif self.YAXES[self._yUnits] == GLDefs.AXISUNITSHZ: if self._ordering: # get the axis ordering from the spectrumDisplay and map to the strip indices = self._spectrumSettings[thisSpecView][GLDefs.SPECTRUM_POINTINDEX] axisLimitT = self.axisT * thisSpec.spectrometerFrequencies[indices[1]] axisLimitB = self.axisB * thisSpec.spectrometerFrequencies[indices[1]] else: # error trap all spectra deleted axisLimitT = self.axisT axisLimitB = self.axisB self.YMode = self._floatFormat # '%.3f' else: if self._ordering: # get the axis ordering from the spectrumDisplay and map to the strip indices = self._spectrumSettings[thisSpecView][GLDefs.SPECTRUM_POINTINDEX] # map to a point axisLimitT = thisSpec.spectrumDimensions[indices[1]].valueToPoint(self.axisT) axisLimitB = thisSpec.spectrumDimensions[indices[1]].valueToPoint(self.axisB) else: # error trap all spectra deleted axisLimitT = self.axisT axisLimitB = self.axisB self.YMode = self._intFormat # '%i' # ul = np.array([min(self.axisL, self.axisR), min(self.axisT, self.axisB)]) # br = np.array([max(self.axisL, self.axisR), max(self.axisT, self.axisB)]) minX = min(axisLimitL, axisLimitR) maxX = max(axisLimitL, axisLimitR) minY = min(axisLimitT, axisLimitB) maxY = max(axisLimitT, axisLimitB) ul = np.array([minX, minY]) br = np.array([maxX, maxY]) gridGLList.renderMode = GLRENDERMODE_DRAW axesChanged = True gridGLList.clearArrays() vertexList = () indexList = () colorList = () index = 0 for scaleOrder, i in enumerate(scaleGrid): # [2,1,0]: ## Draw three different scales of grid dist = br - ul if 0 in dist: # skip if one of the axes is zero continue nlTarget = 10. ** i _pow = np.log10(abs(dist / nlTarget)) + 0.5 d = 10. ** np.floor(_pow) ul1 = np.floor(ul / d) * d br1 = np.ceil(br / d) * d dist = br1 - ul1 nl = (dist / d) + 0.5 _minPow = np.floor(_pow) if _minPow is None else np.minimum(_minPow, np.floor(_pow)) _minOrder = d if _minOrder is None else np.minimum(_minOrder, d) for ax in axisList: # range(0,2): ## Draw grid for both axes c = 30.0 + (scaleOrder * 20) bx = (ax + 1) % 2 for x in range(0, int(nl[ax])): p1 = np.array([0., 0.]) p2 = np.array([0., 0.]) p1[ax] = ul1[ax] + x * d[ax] p2[ax] = p1[ax] p1[bx] = ul[bx] p2[bx] = br[bx] if p1[ax] < min(ul[ax], br[ax]) or p1[ax] > max(ul[ax], br[ax]): continue if i == 1: # should be largest scale grid d[0] = self._round_sig(d[0], sig=4) d[1] = self._round_sig(d[1], sig=4) if ax == 0: includeGrid = not (self.XMode == self._intFormat and d[0] < 1 and abs(p1[0] - int(p1[0])) > d[0] / 2.0) else: includeGrid = not (self.YMode == self._intFormat and d[1] < 1 and abs(p1[1] - int(p1[1])) > d[1] / 2.0) if includeGrid: if '%.5f' % p1[0] == '%.5f' % p2[0]: # check whether a vertical line - x axis pp = self._round_sig(p1[0], sig=10) # xLabel = str(int(p1[0])) if d[0] >=1 else self.XMode % p1[0] labelling[str(ax)][pp] = ((i, ax, valueToRatio(p1[0], axisLimitL, axisLimitR), pp, d[0])) else: pp = self._round_sig(p1[1], sig=10) # num = int(p1[1]) if d[1] >=1 else self.XMode % p1[1] labelling[str(ax)][pp] = ((i, ax, valueToRatio(p1[1], axisLimitB, axisLimitT), pp, d[1])) # append the new points to the end of nparray, ignoring narrow grids if scaleBounds[ax] * (scaleOrder + 1) > 225: indexList += (index, index + 1) vertexList += (valueToRatio(p1[0], axisLimitL, axisLimitR), valueToRatio(p1[1], axisLimitB, axisLimitT), valueToRatio(p2[0], axisLimitL, axisLimitR), valueToRatio(p2[1], axisLimitB, axisLimitT)) alpha = min([1.0, c / transparency]) colorList += (r, g, b, alpha, r, g, b, alpha) gridGLList.numVertices += 2 index += 2 # add the extra axis lines if _includeAxis: for ax in axisList: offset = GLDefs.AXISDRAWOFFSET if self.AXIS_INSIDE else (1 - GLDefs.AXISDRAWOFFSET) if ax == 0: # add the x axis line indexList += (index, index + 1) vertexList += (0.0, offset, 1.0, offset) colorList += (r, g, b, 1.0, r, g, b, 1.0) gridGLList.numVertices += 2 index += 2 elif ax == 1: # add the y axis line indexList += (index, index + 1) vertexList += (1.0 - offset, 0.0, 1.0 - offset, 1.0) colorList += (r, g, b, 1.0, r, g, b, 1.0) gridGLList.numVertices += 2 index += 2 # copy the arrays the the GLstore gridGLList.vertices = np.array(vertexList, dtype=np.float32) gridGLList.indices = np.array(indexList, dtype=np.uint32) gridGLList.colors = np.array(colorList, dtype=np.float32) # restrict the labelling to the maximum without overlap based on width # should be dependent on font size though _maxChars = 1 for k, val in list(labelling['0'].items()): _maxChars = max(_maxChars, getDigitLen(val, _minOrder[0])) _width = self.w / (7.0 * _maxChars) # num of columns restrictList = 0 ll = len(labelling['0']) while ll > _width and restrictList < 3: ll /= 2 restrictList += 1 for k, val in list(labelling['0'].items()): if getDigit(val, _minOrder[0]) not in _labelRestrictList[restrictList]: # remove the item del labelling['0'][k] _height = self.h / 15.0 # num of rows restrictList = 0 ll = len(labelling['1']) while ll > _height and restrictList < 3: ll /= 2 restrictList += 1 for k, val in list(labelling['1'].items()): if getDigit(val, _minOrder[1]) not in _labelRestrictList[restrictList]: # remove the item del labelling['1'][k] return labelling, axesChanged def _widthsChangedEnough(self, r1, r2, tol=1e-5): if len(r1) != len(r2): raise ValueError('WidthsChanged must be the same length') for ii in zip(r1, r2): if abs(ii[0] - ii[1]) > tol: return True
[docs] def getAxisPosition(self, axisIndex): position = None if axisIndex == 0: position = (self.axisR + self.axisL) / 2.0 elif axisIndex == 1: position = (self.axisT + self.axisB) / 2.0 return position
[docs] def setAxisPosition(self, axisIndex, position, rescale=True, update=True): if axisIndex == 0: diff = (self.axisR - self.axisL) / 2.0 self.axisL = position - diff self.axisR = position + diff if rescale: self._rescaleXAxis(rescale=rescale, update=update) elif axisIndex == 1: diff = (self.axisT - self.axisB) / 2.0 self.axisB = position - diff self.axisT = position + diff if rescale: self._rescaleYAxis(rescale=rescale, update=update)
[docs] def getAxisWidth(self, axisIndex): width = None if axisIndex == 0: width = abs(self.axisR - self.axisL) elif axisIndex == 1: width = abs(self.axisT - self.axisB) return width
[docs] def setAxisWidth(self, axisIndex, width, rescale=True, update=True): if axisIndex == 0: diff = self.sign(self.axisR - self.axisL) * abs(width) / 2.0 mid = (self.axisR + self.axisL) / 2.0 self.axisL = mid - diff self.axisR = mid + diff self._scaleToXAxis(rescale=rescale, update=update) elif axisIndex == 1: diff = self.sign(self.axisT - self.axisB) * abs(width) / 2.0 mid = (self.axisT + self.axisB) / 2.0 self.axisB = mid - diff self.axisT = mid + diff self._scaleToYAxis(rescale=rescale, update=update)
[docs] def getAxisRegion(self, axisIndex): """Return the region for visible axisIndex 0/1 (for X/Y) if axis is reversed, the region will be returned as (max, min) """ if axisIndex == 0: return self.axisL, self.axisR elif axisIndex == 1: return self.axisB, self.axisT
[docs] def setAxisRegion(self, axisIndex, range, rescale=True, update=True): if axisIndex == 0: if self.INVERTXAXIS: self.axisL = max(range) self.axisR = min(range) else: self.axisL = min(range) self.axisR = max(range) if rescale: self._rescaleXAxis(rescale=rescale, update=update) elif axisIndex == 1: if self.INVERTXAXIS: self.axisB = max(range) self.axisT = min(range) else: self.axisB = min(range) self.axisT = max(range) if rescale: self._rescaleYAxis(rescale=rescale, update=update)
@pyqtSlot(dict) def _glXAxisChanged(self, aDict): if self._aspectRatioMode: self._glAllAxesChanged(aDict) return if self.strip.isDeleted: return if aDict[GLNotifier.GLSOURCE] != self and aDict[GLNotifier.GLSPECTRUMDISPLAY] == self.spectrumDisplay: # match only the scale for the X axis _dict = aDict[GLNotifier.GLAXISVALUES] axisL = _dict[GLNotifier.GLLEFTAXISVALUE] axisR = _dict[GLNotifier.GLRIGHTAXISVALUE] row = _dict[GLNotifier.GLSTRIPROW] col = _dict[GLNotifier.GLSTRIPCOLUMN] if any(val is None for val in (axisL, axisR, row, col)): return if self._widthsChangedEnough([axisL, self.axisL], [axisR, self.axisR]): if self.spectrumDisplay.stripArrangement == 'Y': if self.strip.tilePosition[1] == col: self.axisL = axisL self.axisR = axisR else: diff = (axisR - axisL) / 2.0 mid = (self.axisR + self.axisL) / 2.0 self.axisL = mid - diff self.axisR = mid + diff elif self.spectrumDisplay.stripArrangement == 'X': if self.strip.tilePosition[0] == row: self.axisL = axisL self.axisR = axisR else: diff = (axisR - axisL) / 2.0 mid = (self.axisR + self.axisL) / 2.0 self.axisL = mid - diff self.axisR = mid + diff else: raise self._rescaleXAxis() self._storeZoomHistory() @pyqtSlot(dict) def _glAxisLockChanged(self, aDict): if self.strip.isDeleted: return if aDict[GLNotifier.GLSOURCE] != self and aDict[GLNotifier.GLSPECTRUMDISPLAY] == self.spectrumDisplay: self._aspectRatioMode = aDict[GLNotifier.GLVALUES][0] if self._aspectRatioMode: # check which is the primary axis and update the opposite axis - similar to wheelEvent if self.spectrumDisplay.stripArrangement == 'Y': # strips are arranged in a row self._scaleToYAxis() elif self.spectrumDisplay.stripArrangement == 'X': # strips are arranged in a column self._scaleToXAxis() elif self.spectrumDisplay.stripArrangement == 'T': # NOTE:ED - Tiled plots not fully implemented yet getLogger().warning('Tiled plots not implemented for spectrumDisplay: %s' % str(self.spectrumDisplay.pid)) else: getLogger().warning('Strip direction is not defined for spectrumDisplay: %s' % str(self.spectrumDisplay.pid)) else: # paint to update lock button colours self.update() @pyqtSlot(dict) def _glAxisUnitsChanged(self, aDict): if self.strip.isDeleted: return # update the list of visible spectra self._updateVisibleSpectrumViews() if aDict[GLNotifier.GLSOURCE] != self and aDict[GLNotifier.GLSPECTRUMDISPLAY] == self.spectrumDisplay: # read values from dataDict and set units if aDict[GLNotifier.GLVALUES]: # and aDict[GLNotifier.GLVALUES][GLDefs.AXISLOCKASPECTRATIO]: self._xUnits = aDict[GLNotifier.GLVALUES][GLDefs.AXISXUNITS] self._yUnits = aDict[GLNotifier.GLVALUES][GLDefs.AXISYUNITS] if GLDefs.AXISASPECTRATIOMODE in aDict[GLNotifier.GLVALUES]: aRM = aDict[GLNotifier.GLVALUES][GLDefs.AXISASPECTRATIOMODE] if self._aspectRatioMode != aRM: self._aspectRatioMode = aRM changeDict = {GLNotifier.GLSOURCE : None, GLNotifier.GLSPECTRUMDISPLAY: self.spectrumDisplay, GLNotifier.GLVALUES : (aRM,) } self._glAxisLockChanged(changeDict) if GLDefs.AXISASPECTRATIOS in aDict[GLNotifier.GLVALUES]: self._aspectRatios.update(aDict[GLNotifier.GLVALUES][GLDefs.AXISASPECTRATIOS]) if aRM == 2: self._rescaleAllZoom(rescale=True) # spawn rebuild event for the grid self._updateAxes = True if self.gridList: for gr in self.gridList: gr.renderMode = GLRENDERMODE_REBUILD self.update() @pyqtSlot(dict) def _glYAxisChanged(self, aDict): if self._aspectRatioMode: self._glAllAxesChanged(aDict) return if self.strip.isDeleted: return if aDict[GLNotifier.GLSOURCE] != self and aDict[GLNotifier.GLSPECTRUMDISPLAY] == self.spectrumDisplay: # match the Y axis _dict = aDict[GLNotifier.GLAXISVALUES] axisB = _dict[GLNotifier.GLBOTTOMAXISVALUE] axisT = _dict[GLNotifier.GLTOPAXISVALUE] row = _dict[GLNotifier.GLSTRIPROW] col = _dict[GLNotifier.GLSTRIPCOLUMN] if any(val is None for val in (axisB, axisT, row, col)): return if self._widthsChangedEnough([axisB, self.axisB], [axisT, self.axisT]): if self.spectrumDisplay.stripArrangement == 'Y': if self.strip.tilePosition[0] == row: self.axisB = axisB self.axisT = axisT else: diff = (axisT - axisB) / 2.0 mid = (self.axisT + self.axisB) / 2.0 self.axisB = mid - diff self.axisT = mid + diff elif self.spectrumDisplay.stripArrangement == 'X': if self.strip.tilePosition[1] == col: self.axisB = axisB self.axisT = axisT else: diff = (axisT - axisB) / 2.0 mid = (self.axisT + self.axisB) / 2.0 self.axisB = mid - diff self.axisT = mid + diff else: raise self._rescaleYAxis() self._storeZoomHistory() @pyqtSlot(dict) def _glAllAxesChanged(self, aDict): if self.strip.isDeleted: return sDisplay = aDict[GLNotifier.GLSPECTRUMDISPLAY] source = aDict[GLNotifier.GLSOURCE] if source != self and sDisplay == self.spectrumDisplay: # match the values for the Y axis, and scale for the X axis _dict = aDict[GLNotifier.GLAXISVALUES] axisB = _dict[GLNotifier.GLBOTTOMAXISVALUE] axisT = _dict[GLNotifier.GLTOPAXISVALUE] axisL = _dict[GLNotifier.GLLEFTAXISVALUE] axisR = _dict[GLNotifier.GLRIGHTAXISVALUE] row = _dict[GLNotifier.GLSTRIPROW] col = _dict[GLNotifier.GLSTRIPCOLUMN] # zoomAll = _dict[GLNotifier.GLSTRIPZOOMALL] if any(val is None for val in (axisB, axisT, axisL, axisR, row, col)): return if self._widthsChangedEnough([axisB, self.axisB], [axisT, self.axisT]) and \ self._widthsChangedEnough([axisL, self.axisL], [axisR, self.axisR]): # # do the matching row and column only unless _useLockedAspect or self._useDefaultAspect are set # if not (self.strip.tilePosition[0] == row or self.strip.tilePosition[1] == col) and \ # not (self._useLockedAspect or self._useDefaultAspect) and zoomAll: # return if self.spectrumDisplay.stripArrangement == 'Y': # strips are arranged in a row if self.strip.tilePosition[1] == col: self.axisL = axisL self.axisR = axisR else: #if self._useLockedAspect or self._useDefaultAspect: diff = (axisR - axisL) / 2.0 mid = (self.axisR + self.axisL) / 2.0 self.axisL = mid - diff self.axisR = mid + diff if self.strip.tilePosition[0] == row: self.axisB = axisB self.axisT = axisT else: #if self._useLockedAspect or self._useDefaultAspect: diff = (axisT - axisB) / 2.0 mid = (self.axisT + self.axisB) / 2.0 self.axisB = mid - diff self.axisT = mid + diff elif self.spectrumDisplay.stripArrangement == 'X': # strips are arranged in a column if self.strip.tilePosition[1] == col: self.axisB = axisB self.axisT = axisT else: #if self._useLockedAspect or self._useDefaultAspect: diff = (axisT - axisB) / 2.0 mid = (self.axisT + self.axisB) / 2.0 self.axisB = mid - diff self.axisT = mid + diff if self.strip.tilePosition[0] == row: self.axisL = axisL self.axisR = axisR else: #if self._useLockedAspect or self._useDefaultAspect: diff = (axisR - axisL) / 2.0 mid = (self.axisR + self.axisL) / 2.0 self.axisL = mid - diff self.axisR = mid + diff elif self.spectrumDisplay.stripArrangement == 'T': # NOTE:ED - Tiled plots not fully implemented yet pass else: # currently ignore - warnings will be logged elsewhere pass self._rescaleAllAxes() self._storeZoomHistory() @pyqtSlot(dict) def _glMouseMoved(self, aDict): if self.strip.isDeleted: return if aDict[GLNotifier.GLSOURCE] != self: mouseMovedDict = aDict[GLNotifier.GLMOUSEMOVEDDICT] if self._crosshairVisible: exactMatch = (self._preferences.matchAxisCode == AXIS_FULLATOMNAME) indices = getAxisCodeMatchIndices(self._axisCodes[:2], mouseMovedDict[AXIS_ACTIVEAXES], exactMatch=exactMatch) if indices and len(indices) > 1: for n in range(2): if indices[n] is not None: axis = mouseMovedDict[AXIS_ACTIVEAXES][indices[n]] self.cursorSource = CURSOR_SOURCE_OTHER self.cursorCoordinate[n] = mouseMovedDict[AXIS_FULLATOMNAME][axis] # coordinates have already been flipped self.doubleCursorCoordinate[1 - n] = self.cursorCoordinate[n] else: self.cursorSource = CURSOR_SOURCE_OTHER self.cursorCoordinate[n] = None self.doubleCursorCoordinate[1 - n] = None self.update(mode=PaintModes.PAINT_MOUSEONLY) @pyqtSlot(dict) def _glKeyEvent(self, aDict): """Process Key events from the application/popups and other strips :param aDict - dictionary containing event flags: """ if self.strip.isDeleted: return if not self.globalGL: return self.glKeyPressEvent(aDict) @pyqtSlot(dict) def _glEvent(self, aDict): """Process events from the application/popups and other strips :param aDict - dictionary containing event flags: """ if self.strip.isDeleted: return if not self.globalGL: return if aDict: if aDict[GLNotifier.GLSOURCE] != self: # check the params for actions and update the display triggers = aDict[GLNotifier.GLTRIGGERS] targets = aDict[GLNotifier.GLTARGETS] if triggers or targets: if GLNotifier.GLRESCALE in triggers: self._rescaleXAxis(update=False) if GLNotifier.GLPREFERENCES in triggers: self._preferencesUpdate() self._rescaleXAxis(update=False) self.stripIDString.renderMode = GLRENDERMODE_REBUILD if GLNotifier.GLPEAKLISTS in triggers: for spectrumView in self._ordering: # strip.spectrumViews: if spectrumView.isDeleted: continue for peakListView in spectrumView.peakListViews: for peakList in targets: if peakList == peakListView.peakList: peakListView.buildSymbols = True # self.buildPeakLists() if GLNotifier.GLPEAKLISTLABELS in triggers: for spectrumView in self._ordering: # strip.spectrumViews: if spectrumView.isDeleted: continue for peakListView in spectrumView.peakListViews: for peakList in targets: if peakList == peakListView.peakList: peakListView.buildLabels = True # self.buildPeakListLabels() if GLNotifier.GLMARKS in triggers: self.buildMarks = True if GLNotifier.GLHIGHLIGHTPEAKS in triggers: self._GLPeaks.updateHighlightSymbols() if GLNotifier.GLHIGHLIGHTMULTIPLETS in triggers: self._GLMultiplets.updateHighlightSymbols() if GLNotifier.GLHIGHLIGHTINTEGRALS in triggers: self._GLIntegrals.updateHighlightSymbols() if GLNotifier.GLALLCONTOURS in triggers: self.buildAllContours() if GLNotifier.GLALLPEAKS in triggers: self._GLPeaks.updateAllSymbols() if GLNotifier.GLALLMULTIPLETS in triggers: self._GLMultiplets.updateAllSymbols() if GLNotifier.GLANY in targets: self._rescaleXAxis(update=False) if GLNotifier.GLPEAKNOTIFY in targets: self._GLPeaks.updateHighlightSymbols() if GLNotifier.GLINTEGRALLISTS in triggers: for spectrumView in self._ordering: # strip.spectrumViews: if spectrumView.isDeleted: continue for integralListView in spectrumView.integralListViews: for integralList in targets: if integralList == integralListView.integralList: integralListView.buildSymbols = True if GLNotifier.GLINTEGRALLISTLABELS in triggers: for spectrumView in self._ordering: # strip.spectrumViews: if spectrumView.isDeleted: continue for integralListView in spectrumView.integralListViews: for integralList in targets: if integralList == integralListView.integralList: integralListView.buildLabels = True if GLNotifier.GLMULTIPLETLISTS in triggers: for spectrumView in self._ordering: # strip.spectrumViews: if spectrumView.isDeleted: continue for multipletListView in spectrumView.multipletListViews: for multipletList in targets: if multipletList == multipletListView.multipletList: multipletListView.buildSymbols = True if GLNotifier.GLMULTIPLETLISTLABELS in triggers: for spectrumView in self._ordering: # strip.spectrumViews: if spectrumView.isDeleted: continue for multipletListView in spectrumView.multipletListViews: for multipletList in targets: if multipletList == multipletListView.multipletList: multipletListView.buildLabels = True if GLNotifier.GLCLEARPHASING in triggers: if self.spectrumDisplay == aDict[GLNotifier.GLSPECTRUMDISPLAY]: self.clearStaticTraces() if GLNotifier.GLADD1DPHASING in triggers: if self.spectrumDisplay == aDict[GLNotifier.GLSPECTRUMDISPLAY]: self.clearStaticTraces() self.newTrace() # repaint self.update() def _renderCursorOnly(self): # NOTE:ED - use partialUpdate # when the mouse is moving somewhere else then only render the mouse using XOR # without clearing the screen # normal paintGL should include GL.glClear(GL.GL_COLOR_BUFFER_BIT) # draw original mouse in Xor - OR replace vertical/horizontal columns as bitmap # update mouse coordinates # OR grab vertical/horizontal columns as bitmap # draw new mouse in Xor self.makeCurrent() if self._crosshairVisible: drawList = self._glCursor vertices = [] indices = [] index = 0 # map the cursor to the ratio coordinates - double cursor is flipped about the line x=y newCoords = self._scaleAxisToRatio(self.cursorCoordinate[0:2]) if getCurrentMouseMode() == PICK and self.underMouse(): x = self.deltaX * 8 y = self.deltaY * 8 vertices = [newCoords[0] - x, newCoords[1] - y, newCoords[0] + x, newCoords[1] - y, newCoords[0] + x, newCoords[1] - y, newCoords[0] + x, newCoords[1] + y, newCoords[0] + x, newCoords[1] + y, newCoords[0] - x, newCoords[1] + y, newCoords[0] - x, newCoords[1] + y, newCoords[0] - x, newCoords[1] - y ] indices = [0, 1, 2, 3, 4, 5, 6, 7] col = self.mousePickColour index = 8 else: col = self.foreground