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

"""
Code for exporting OpenGL stripDisplay to pdf and svg files.
"""
#=========================================================================================
# Licence, Reference and Credits
#=========================================================================================
__copyright__ = "Copyright (C) CCPN project (http://www.ccpn.ac.uk) 2014 - 2021"
__credits__ = ("Ed Brooksbank, Joanna Fox, Victoria A Higman, Luca Mureddu, Eliza Płoskoń",
               "Timothy J Ragan, Brian O Smith, Gary S Thompson & Geerten W Vuister")
__licence__ = ("CCPN licence. See http://www.ccpn.ac.uk/v3-software/downloads/license")
__reference__ = ("Skinner, S.P., Fogh, R.H., Boucher, W., Ragan, T.J., Mureddu, L.G., & Vuister, G.W.",
                 "CcpNmr AnalysisAssign: a flexible platform for integrated NMR analysis",
                 "J.Biomol.Nmr (2016), 66, 111-124, http://doi.org/10.1007/s10858-016-0060-y")
#=========================================================================================
# Last code modification
#=========================================================================================
__modifiedBy__ = "$modifiedBy: Ed Brooksbank $"
__dateModified__ = "$dateModified: 2021-12-20 18:47:15 +0000 (Mon, December 20, 2021) $"
__version__ = "$Revision: 3.0.4 $"
#=========================================================================================
# Created
#=========================================================================================
__author__ = "$Author: Ed Brooksbank $"
__date__ = "$Date: 2018-12-20 13:28:13 +0000 (Thu, December 20, 2018) $"
#=========================================================================================
# Start of code
#=========================================================================================

# import sys
import os
import io
import numpy as np
import math
from PyQt5 import QtGui
from PyQt5.QtCore import QStandardPaths
from PyQt5.QtGui import QFontDatabase
from PyQt5.QtWidgets import QApplication
from dataclasses import dataclass
from collections import OrderedDict
from collections.abc import Iterable
from reportlab.platypus import SimpleDocTemplate, Paragraph, Flowable
from reportlab.lib.styles import getSampleStyleSheet
from reportlab.lib.units import inch, cm
from reportlab.lib import colors
from reportlab.graphics import renderSVG, renderPS, renderPM
from reportlab.graphics.shapes import Drawing, Rect, String, PolyLine, Group, Path
# from reportlab.graphics.shapes import definePath
# from reportlab.graphics.renderSVG import draw, renderScaledDrawing, SVGCanvas
from reportlab.lib.units import mm
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import portrait, landscape, letter, A0, A1, A2, A3, A4, A5, A6
from reportlab.platypus.tables import Table, TableStyle
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
from ccpn.ui.gui.lib.OpenGL.CcpnOpenGLViewports import viewportDimensions
from ccpn.ui.gui.lib.OpenGL.CcpnOpenGLDefs import SPECTRUM_STACKEDMATRIX, SPECTRUM_MATRIX, \
    GLLINE_STYLES_ARRAY, SPECTRUM_XLIMITS, SPECTRUM_AF, SPECTRUM_ALIASINGINDEX, SPECTRUM_FOLDINGMODE, \
    SPECTRUM_YLIMITS, SPECTRUM_SCALE, SPECTRUM_STACKEDMATRIXOFFSET

from ccpn.ui.gui.lib.OpenGL import GL
from ccpn.ui.gui.lib.OpenGL.CcpnOpenGLDefs import GLGRIDLINES, GLAXISLABELS, GLAXISMARKS, \
    GLINTEGRALLABELS, GLINTEGRALSYMBOLS, GLMARKLABELS, GLMARKLINES, GLMULTIPLETLABELS, GLREGIONS, \
    GLMULTIPLETSYMBOLS, GLOTHERLINES, GLPEAKLABELS, GLPEAKSYMBOLS, GLPRINTTYPE, GLSELECTEDPIDS, \
    GLSPECTRUMBORDERS, GLSPECTRUMCONTOURS, GLSPECTRUMLABELS, \
    GLSTRIP, GLSTRIPLABELLING, GLTRACES, GLACTIVETRACES, GLPLOTBORDER, \
    GLPAGETYPE, GLPAGESIZE, GLSPECTRUMDISPLAY, GLBACKGROUND, GLBASETHICKNESS, GLSYMBOLTHICKNESS, \
    GLCONTOURTHICKNESS, GLFOREGROUND, GLSHOWSPECTRAONPHASE, \
    GLAXISTITLES, GLAXISUNITS, GLSTRIPDIRECTION, GLSTRIPPADDING, GLEXPORTDPI, \
    GLCURSORS, GLDIAGONALLINE, GLDIAGONALSIDEBANDS, \
    MAINVIEW, MAINVIEWFULLHEIGHT, MAINVIEWFULLWIDTH, \
    RIGHTAXIS, RIGHTAXISBAR, FULLRIGHTAXIS, FULLRIGHTAXISBAR, \
    BOTTOMAXIS, BOTTOMAXISBAR, FULLBOTTOMAXIS, FULLBOTTOMAXISBAR, FULLVIEW, BLANKVIEW, \
    GLALIASSHADE, GLSTRIPREGIONS, \
    GLSCALINGMODE, GLSCALINGOPTIONS, GLSCALINGPERCENT, GLSCALINGBYUNITS, \
    GLPRINTFONT, GLUSEPRINTFONT, GLSCALINGAXIS
# from ccpn.ui.gui.lib.OpenGL.CcpnOpenGLDefs import GLFILENAME, GLWIDGET, GLAXISLINES, GLAXISMARKSINSIDE, \
#     GLFULLLIST, GLEXTENDEDLIST, GLALIASENABLED, GLALIASLABELSENABLED
from ccpn.ui.gui.lib.OpenGL.CcpnOpenGLGlobal import getAliasSetting
from ccpn.ui.gui.popups.ExportStripToFile import PAGEPORTRAIT, DEFAULT_FONT, PAGESIZEA6, PAGESIZEA5, \
    PAGESIZEA4, PAGESIZEA3, PAGESIZEA2, PAGESIZEA1, PAGESIZEA0, PAGESIZELETTER, PAGESIZES
# from ccpn.ui.gui.popups.ExportStripToFile import EXPORTPDF, EXPORTSVG, EXPORTTYPES, \
#     PAGELANDSCAPE, PAGETYPES
from ccpn.ui.gui.popups.ExportStripToFile import EXPORTPNG
# from ccpn.util.Colour import colorSchemeTable
from ccpn.core.lib.ContextManagers import catchExceptions
from ccpn.util.Report import Report
from ccpn.util.Constants import SCALE_PERCENT, SCALE_UNIT_CM, SCALE_UNIT_INCH, SCALE_INCH_UNIT, SCALE_CM_UNIT, SCALING_MODES


PLOTLEFT = 'plotLeft'
PLOTBOTTOM = 'plotBottom'
PLOTWIDTH = 'plotWidth'
PLOTHEIGHT = 'plotHeight'

PDFSTROKEWIDTH = 'strokeWidth'
PDFSTROKECOLOR = 'strokeColor'
PDFSTROKELINECAP = 'strokeLineCap'
PDFFILLCOLOR = 'fillColor'
PDFFILL = 'fill'
PDFFILLMODE = 'fillMode'
PDFSTROKE = 'stroke'
PDFSTROKEDASHARRAY = 'strokeDashArray'
PDFCLOSEPATH = 'closePath'
PDFLINES = 'lines'
FRAMEPADDING = 13
PAGEREFERENCE = {PAGESIZEA0    : A0,
                 PAGESIZEA1    : A1,
                 PAGESIZEA2    : A2,
                 PAGESIZEA3    : A3,
                 PAGESIZEA4    : A4,
                 PAGESIZEA5    : A5,
                 PAGESIZEA6    : A6,
                 PAGESIZELETTER: letter}


[docs]def alphaClip(value): # return np.clip(float(value), 0.0, 1.0) return float(value)
[docs]class GLExporter(): """ Class container for exporting OpenGL stripDisplay to a file or object """ def __init__(self, parent, strip, filename, params): """ Initialise the exporter :param filename - not required :param params - parameter dict from the exporter dialog Need to have different settingsif the output is to a .png file This needs a multiplier based on (output dpi / 72) and scale = (output dpi / 72) - a thickness modifier on all drawing output Fonts need a 0.5 scaling for .png """ self._parent = parent self.strip = strip self.project = self.strip.project self.filename = filename self.params = params # set the page orientation if self.params[GLPAGETYPE] == PAGEPORTRAIT: pageType = portrait else: pageType = landscape _pageSize = PAGEREFERENCE.get(self.params[GLPAGESIZE]) or A4 self._report = Report(self, self.project, filename, pagesize=pageType(_pageSize), leftMargin=1, rightMargin=1, topMargin=1, bottomMargin=1) self._ordering = [] self._importFonts() self._printType = self.params[GLPRINTTYPE] if self._printType == EXPORTPNG: # need to set the scaling for a PNG file and alter baseThickness/font size self._dpiScale = self.params[GLEXPORTDPI] / 72 self.baseThickness = self.params[GLBASETHICKNESS] * self._dpiScale self.symbolThickness = self.params[GLSYMBOLTHICKNESS] self.contourThickness = self.params[GLCONTOURTHICKNESS] self._pngScale = 0.5 else: self.baseThickness = self.params[GLBASETHICKNESS] self.symbolThickness = self.params[GLSYMBOLTHICKNESS] self.contourThickness = self.params[GLCONTOURTHICKNESS] self._pngScale = 1.0 self._dpiScale = 1.0 # set default colours self.backgroundColour = colors.Color(*self.params[GLBACKGROUND], alpha=alphaClip(1.0)) self.foregroundColour = colors.Color(*self.params[GLFOREGROUND], alpha=alphaClip(1.0)) # build all the sections of the pdf self.stripReports = [] self.stripWidths = [] self.stripHeights = [] self.stripSpacing = 0 if self.params[GLSPECTRUMDISPLAY]: # print the whole spectrumDisplay spectrumDisplay = self.params[GLSPECTRUMDISPLAY] self._selectedStrip = self.strip self.numStrips = len(spectrumDisplay.strips) self._buildPageDimensions(spectrumDisplay.strips) for strNum, strip in enumerate(spectrumDisplay.orderedStrips): self.stripNumber = strNum self._linkedAxisStrip = None self._createStrip(strip, singleStrip=False, axesOnly=False) self._lastMainView = self.mainView # build strip for the floating axis self.stripNumber = self.numStrips self._linkedAxisStrip = spectrumDisplay.orderedStrips[-1] # self._parent still points to the last strip - check not to double up the last axis if self.params[GLSTRIPDIRECTION] == 'Y': if self._parent and not self._parent._drawRightAxis: self._createStrip(spectrumDisplay.orderedStrips[0], spectrumDisplay._rightGLAxis, singleStrip=False, axesOnly=True) else: if self._parent and not self._parent._drawBottomAxis: self._createStrip(spectrumDisplay.orderedStrips[0], spectrumDisplay._bottomGLAxis, singleStrip=False, axesOnly=True) else: # print a single strip strip = self.params[GLSTRIP] spectrumDisplay = strip.spectrumDisplay self._selectedStrip = strip self.numStrips = 1 self._buildPageDimensions([strip]) self.stripNumber = 0 self._linkedAxisStrip = None self._createStrip(strip, singleStrip=True, axesOnly=False) self._lastMainView = self.mainView self._linkedAxisStrip = strip if self.params[GLSTRIPDIRECTION] == 'Y': if self._parent and not self._parent._drawRightAxis: self._createStrip(spectrumDisplay.orderedStrips[0], spectrumDisplay._rightGLAxis, singleStrip=True, axesOnly=True) else: if self._parent and not self._parent._drawBottomAxis: self._createStrip(spectrumDisplay.orderedStrips[0], spectrumDisplay._bottomGLAxis, singleStrip=True, axesOnly=True) self._addTableToStory() # this generates the buffer to write to the file self._report.buildDocument() def _createStrip(self, strip, _parent=None, singleStrip=False, axesOnly=False): # point to the correct strip self.strip = strip self._parent = self.strip._CcpnGLWidget if _parent is None else _parent self._buildPage(singleStrip=singleStrip) self._setStripAxes() self._modifyScaling() self._buildStrip(axesOnly=axesOnly) self._resetStripAxes() self._addDrawingToStory() def _getFontPaths(self): font_paths = QStandardPaths.standardLocations(QStandardPaths.FontsLocation) unloadable = [] familyPath = {} db = QFontDatabase() for fpath in font_paths: # go through all font paths for filename in os.listdir(fpath): # go through all files at each path path = os.path.join(fpath, filename) idx = db.addApplicationFont(path) # add font path if idx < 0: unloadable.append(path) # font wasn't loaded if idx is -1 else: names = db.applicationFontFamilies(idx) # load back font family name for n in names: _paths = familyPath.setdefault(n, set()) _paths.add(path) return unloadable, familyPath def _importFonts(self): from ccpn.framework.PathsAndUrls import fontsPath from ccpn.ui.gui.lib.OpenGL.CcpnOpenGLGlobal import GLFONT_SUBSTITUTE # load all system fonts to find matches with OpenGl fonts for glFonts in self._parent.globalGL.fonts.values(): pdfmetrics.registerFont(TTFont(glFonts.fontName, fontsPath / 'open-sans' / GLFONT_SUBSTITUTE + '.ttf')) self._printFont = None if self.params[GLUSEPRINTFONT]: _fontName, _fontSize = self.params[GLPRINTFONT] unloadable, familyPath = self._getFontPaths() _paths = familyPath.get(_fontName, []) for _path in _paths: try: pdfmetrics.registerFont(TTFont(_fontName, _path)) except Exception as es: pass # set a default fontName self.fontName = self._parent.getSmallFont().fontName # load a .pfb/.afm font for the png exporter afmdir = fontsPath / 'open-sans' pfbdir = fontsPath / 'open-sans' afmFile = afmdir / 'OpenSans-Regular.afm' pfbFile = pfbdir / 'OpenSans-Regular.pfb' justFace = pdfmetrics.EmbeddedType1Face(afmFile, pfbFile) faceName = 'OpenSans' # pulled from AFM file pdfmetrics.registerTypeFace(justFace) # this needs to have a space justFont = pdfmetrics.Font('Open Sans', faceName, 'WinAnsiEncoding') pdfmetrics.registerFont(justFont) def _buildPageDimensions(self, strips): """Calculate the scaling required for the whole page """ st = strips[0]._CcpnGLWidget if self.params[GLSTRIPDIRECTION] == 'Y': fh = st.height() _widths = [st._CcpnGLWidget.width() for st in strips] if self.numStrips > 1: _widths.append((self.numStrips - 1) * self.params[GLSTRIPPADDING]) if st and not st._drawRightAxis: _widths.append(strips[0].spectrumDisplay._rightGLAxis.width()) fw = sum(_widths) else: fw = st.width() _heights = [st._CcpnGLWidget.height() for st in strips] if self.numStrips > 1: _heights.append((self.numStrips - 1) * self.params[GLSTRIPPADDING]) if st and not st._drawBottomAxis: _heights.append(self.strip.spectrumDisplay._bottomGLAxis.height()) fh = sum(_heights) # dimensions of the strip list - should be ints _parentH = self.fh = fh _parentW = self.fw = fw _doc = self._report.doc self.docWidth = docWidth = _doc.width # _doc.pagesize[0] # - FRAMEPADDING self.docHeight = docHeight = _doc.height # pagesize[1] # - (2 * FRAMEPADDING) # translate to size of drawing Flowable self.modRatio = 1.0 _pageRatio = docHeight / docWidth _stripRatio = fh / fw if _stripRatio > _pageRatio: # strips are taller than relative page - fit to height _scale = fh / docHeight fh = docHeight fw /= _scale else: _scale = fw / docWidth fw = docWidth fh /= _scale self.modRatio = 1.0 self._displayScale = fh / _parentH self._fontScale = self._pngScale / self._parent.viewports.devicePixelRatio # read the strip spacing from the params self._stripSpacing = self.params[GLSTRIPPADDING] #* self._displayScale def _buildPage(self, singleStrip=True): """Build the main sections of the pdf file from a drawing object and add the drawing object to a reportlab document """ dpi = 72 # drawing object and documents are hard-coded to this # keep aspect ratio of the original screen self.margin = 2.0 * cm self.main = True self.rAxis = self._parent._drawRightAxis self.bAxis = self._parent._drawBottomAxis # get the method for retrieving the viewport sizes getView = self._parent.viewports.getViewportFromWH _parentH = self._parent.h _parentW = self._parent.w if not self.rAxis and not self.bAxis: # no axes visible self.mainView = viewportDimensions(*getView(FULLVIEW, _parentW, _parentH)) self.rAxisMarkView = viewportDimensions(*getView(BLANKVIEW, _parentW, _parentH)) self.rAxisBarView = viewportDimensions(*getView(BLANKVIEW, _parentW, _parentH)) self.bAxisMarkView = viewportDimensions(*getView(BLANKVIEW, _parentW, _parentH)) self.bAxisBarView = viewportDimensions(*getView(BLANKVIEW, _parentW, _parentH)) elif self.rAxis and not self.bAxis: # right axis visible self.mainView = viewportDimensions(*getView(MAINVIEWFULLHEIGHT, _parentW, _parentH)) if self._parent._fullHeightRightAxis: self.rAxisMarkView = viewportDimensions(*getView(FULLRIGHTAXIS, _parentW, _parentH)) self.rAxisBarView = viewportDimensions(*getView(FULLRIGHTAXISBAR, _parentW, _parentH)) else: self.rAxisMarkView = viewportDimensions(*getView(RIGHTAXIS, _parentW, _parentH)) self.rAxisBarView = viewportDimensions(*getView(RIGHTAXISBAR, _parentW, _parentH)) self.bAxisMarkView = viewportDimensions(*getView(BLANKVIEW, _parentW, _parentH)) self.bAxisBarView = viewportDimensions(*getView(BLANKVIEW, _parentW, _parentH)) elif not self.rAxis and self.bAxis: # bottom axis visible self.mainView = viewportDimensions(*getView(MAINVIEWFULLWIDTH, _parentW, _parentH)) self.rAxisMarkView = viewportDimensions(*getView(BLANKVIEW, _parentW, _parentH)) self.rAxisBarView = viewportDimensions(*getView(BLANKVIEW, _parentW, _parentH)) if self._parent._fullWidthBottomAxis: self.bAxisMarkView = viewportDimensions(*getView(FULLBOTTOMAXIS, _parentW, _parentH)) self.bAxisBarView = viewportDimensions(*getView(FULLBOTTOMAXISBAR, _parentW, _parentH)) else: self.bAxisMarkView = viewportDimensions(*getView(BOTTOMAXIS, _parentW, _parentH)) self.bAxisBarView = viewportDimensions(*getView(BOTTOMAXISBAR, _parentW, _parentH)) else: # both axes visible self.mainView = viewportDimensions(*getView(MAINVIEW, _parentW, _parentH)) self.rAxisMarkView = viewportDimensions(*getView(RIGHTAXIS, _parentW, _parentH)) self.rAxisBarView = viewportDimensions(*getView(RIGHTAXISBAR, _parentW, _parentH)) self.bAxisMarkView = viewportDimensions(*getView(BOTTOMAXIS, _parentW, _parentH)) self.bAxisBarView = viewportDimensions(*getView(BOTTOMAXISBAR, _parentW, _parentH)) # self.pixWidth = math.floor(_parentW * self.displayScale) # self.pixHeight = math.floor(_parentH * self.displayScale) # self.fontScale = self._pngScale * self.pixWidth * self.displayScale / _parentW self._pixWidth = _parentW self._pixHeight = _parentH self.fontXOffset = 0.75 self.fontYOffset = 3.0 def _modifyScaling(self): # modify by the print dialog scaling factor _scaleMode = self.params[GLSCALINGMODE] _scalePercent = self.params[GLSCALINGPERCENT] if _scaleMode == SCALING_MODES.index(SCALE_PERCENT): # modify the displayScale self.displayScale = (self._displayScale * (_scalePercent / 100.0)) if (0 <= _scalePercent <= 100) else self._displayScale self.pixWidth = self._pixWidth * self.displayScale self.pixHeight = self._pixHeight * self.displayScale self.fontScale = self._fontScale * self.displayScale self.stripSpacing = self._stripSpacing * self.displayScale else: _newScale = 1.0 _scale = self.params[GLSCALINGBYUNITS] try: # scales are ratios # based on self.mainView dimensions and ranges if self._linkedAxisStrip: _axisL = self._linkedAxisStrip._CcpnGLWidget.axisL _axisR = self._linkedAxisStrip._CcpnGLWidget.axisR _axisT = self._linkedAxisStrip._CcpnGLWidget.axisT _axisB = self._linkedAxisStrip._CcpnGLWidget.axisB _width = self._lastMainView.width _height = self._lastMainView.height else: _axisL = self._axisL _axisR = self._axisR _axisT = self._axisT _axisB = self._axisB _width = self.mainView.width _height = self.mainView.height if _scaleMode == SCALING_MODES.index(SCALE_CM_UNIT): # this is scaled to 72dpi if self.params[GLSCALINGAXIS] == 0: _cms = (self._displayScale * _width * 2.54) / 72.0 _axisScale = abs(_axisL - _axisR) else: _cms = (self._displayScale * _height * 2.54) / 72.0 _axisScale = abs(_axisT - _axisB) _newScale = _scale / (_cms / _axisScale) elif _scaleMode == SCALING_MODES.index(SCALE_UNIT_CM): if self.params[GLSCALINGAXIS] == 0: _cms = (self._displayScale * _width * 2.54) / 72.0 _axisScale = abs(_axisL - _axisR) else: _cms = (self._displayScale * _height * 2.54) / 72.0 _axisScale = abs(_axisT - _axisB) _newScale = (_axisScale / _cms) / _scale elif _scaleMode == SCALING_MODES.index(SCALE_INCH_UNIT): if self.params[GLSCALINGAXIS] == 0: _cms = (self._displayScale * _width) / 72.0 _axisScale = abs(_axisL - _axisR) else: _cms = (self._displayScale * _height) / 72.0 _axisScale = abs(_axisT - _axisB) _newScale = _scale / (_cms / _axisScale) else: if self.params[GLSCALINGAXIS] == 0: _cms = (self._displayScale * _width) / 72.0 _axisScale = abs(_axisL - _axisR) else: _cms = (self._displayScale * _height) / 72.0 _axisScale = abs(_axisT - _axisB) _newScale = (_axisScale / _cms) / _scale except Exception as es: # default to full page _newScale = 1.0 finally: # clip to the percentage range _newScale = max(0.01, min(_newScale, 1.0)) self.displayScale = self._displayScale * _newScale self.pixWidth = self._pixWidth * self.displayScale self.pixHeight = self._pixHeight * self.displayScale self.fontScale = self._fontScale * self.displayScale self.stripSpacing = self._stripSpacing * self.displayScale def _addBackgroundBox(self, thisPlot): """Make a background box to cover the plot area """ gr = Group() # paint a background box ll = [0.0, 0.0, 0.0, self.pixHeight, self.pixWidth, self.pixHeight, self.pixWidth, 0.0] if ll: pl = Path(fillColor=self.backgroundColour, stroke=None, strokeColor=None) pl.moveTo(ll[0], ll[1]) for vv in range(2, len(ll), 2): pl.lineTo(ll[vv], ll[vv + 1]) pl.closePath() gr.add(pl) # add to the drawing object thisPlot.add(gr, name='mainPlotBox') # gr = Group() # # paint a background box # ll = [0.0, 0.0, # 0.0, self.pixHeight, # self.pixWidth, self.pixHeight, # self.pixWidth, 0.0] # if ll: # pl = Path(fillColor=self.backgroundColour, stroke=None, strokeColor=None) # pl.moveTo(ll[0], ll[1]) # for vv in range(2, len(ll), 2): # pl.lineTo(ll[vv], ll[vv + 1]) # pl.closePath() # gr.add(pl) # # frame the top-left of the main plot area # ll = [self.displayScale * self.mainView.left, self.displayScale * self.mainView.bottom, # self.displayScale * self.mainView.left, self.pixHeight, # self.displayScale * self.mainView.width, self.pixHeight] # # ll = [self.displayScale * self.mainL, self.displayScale * self.mainB, # # self.displayScale * self.mainL, self.pixHeight, # # self.displayScale * self.mainW, self.pixHeight] # # if ll and self.params[GLPLOTBORDER]: # pl = Path(strokeColor=self.foregroundColour, strokeWidth=0.5) # pl.moveTo(ll[0], ll[1]) # for vv in range(2, len(ll), 2): # pl.lineTo(ll[vv], ll[vv + 1]) # gr.add(pl) # # add to the drawing object # self._mainPlot.add(gr, name='mainPlotBox') def _addPlotBorders(self, thisPlot): """Add requires borders to the plot area """ # frame the top-left of the main plot area - after other plotting gr = Group() ll = [self.displayScale * self.mainView.left, self.displayScale * self.mainView.bottom, self.displayScale * self.mainView.left, self.pixHeight, self.displayScale * self.mainView.width, self.pixHeight, self.displayScale * self.mainView.width, self.displayScale * self.mainView.bottom, self.displayScale * self.mainView.left, self.displayScale * self.mainView.bottom] if ll: pl = Path(fillColor=None, strokeColor=self.foregroundColour if self.params[GLPLOTBORDER] else self.backgroundColour, strokeWidth=0.5 * self.baseThickness) pl.moveTo(ll[0], ll[1]) for vv in range(2, len(ll), 2): pl.lineTo(ll[vv], ll[vv + 1]) gr.add(pl) thisPlot.add(gr, name='mainPlotBox') def _setStripAxes(self): # set the range for the display self._oldValues = (self.strip._CcpnGLWidget.axisL, self.strip._CcpnGLWidget.axisR, self.strip._CcpnGLWidget.axisT, self.strip._CcpnGLWidget.axisB) try: self._updateAxes = False _dd = self.params[GLSTRIPREGIONS][self.strip.id] self._updateAxes = _dd.useRegion if self._updateAxes: for ii, ddAxis in enumerate(_dd.axes): if _dd.minMaxMode == 0: self.strip.setAxisRegion(ii, (ddAxis['Min'], ddAxis['Max']), rescale=False, update=False) else: self.strip.setAxisPosition(ii, ddAxis['Centre'], rescale=False, update=False) self.strip.setAxisWidth(ii, ddAxis['Width'], rescale=False, update=False) self._axisL = self.strip._CcpnGLWidget.axisL self._axisR = self.strip._CcpnGLWidget.axisR self._axisT = self.strip._CcpnGLWidget.axisT self._axisB = self.strip._CcpnGLWidget.axisB self.strip._CcpnGLWidget._rescaleAllAxes(update=False) self.strip._CcpnGLWidget._buildGL() self.strip._CcpnGLWidget.buildAxisLabels() else: self._axisL = self.strip._CcpnGLWidget.axisL self._axisR = self.strip._CcpnGLWidget.axisR self._axisT = self.strip._CcpnGLWidget.axisT self._axisB = self.strip._CcpnGLWidget.axisB except Exception as es: pass def _buildStrip(self, axesOnly=False): # create an object that can be added to a report self._mainPlot = Drawing(self.pixWidth, self.pixHeight) self._addBackgroundBox(self._mainPlot) # get the list of required spectra self._ordering = self.strip.getSpectrumViews() # print the grid objects if self.params[GLGRIDLINES]: self._addGridLines() if not axesOnly: if self.params[GLDIAGONALLINE]: self._addDiagonalLine() if self.params[GLDIAGONALSIDEBANDS]: self._addDiagonalSideBands() # check parameters to decide what to print if not self._parent.spectrumDisplay.is1D or \ not self._parent.spectrumDisplay.phasingFrame.isVisible() or \ self.params[GLSHOWSPECTRAONPHASE]: if self.params[GLSPECTRUMCONTOURS]: self._addSpectrumContours() if self.params[GLSPECTRUMBORDERS]: self._addSpectrumBoundaries() if not self._parent._stackingMode: if self.params[GLINTEGRALSYMBOLS]: self._addIntegralAreas() if self.params[GLINTEGRALSYMBOLS]: self._addIntegralLines() if self.params[GLPEAKSYMBOLS]: self._addPeakSymbols() if self.params[GLMULTIPLETSYMBOLS]: self._addMultipletSymbols() if self.params[GLMARKLINES]: self._addMarkLines() if self.params[GLREGIONS]: self._addRegions() if self.params[GLPEAKLABELS]: self._addPeakLabels() if self.params[GLINTEGRALLABELS]: self._addIntegralLabels() if self.params[GLMULTIPLETLABELS]: self._addMultipletLabels() if self.params[GLMARKLABELS]: self._addMarkLabels() else: if self.params[GLPEAKSYMBOLS]: self._addPeakSymbols() if self.params[GLMULTIPLETSYMBOLS]: self._addMultipletSymbols() if self.params[GLPEAKLABELS]: self._addPeakLabels() if self.params[GLMULTIPLETLABELS]: self._addMultipletLabels() if self.params[GLSPECTRUMLABELS]: self._addSpectrumLabels() if self.params[GLTRACES]: self._addTraces() if self._selectedStrip == self.strip: if self.params[GLACTIVETRACES]: self._addLiveTraces() if not self._parent._stackingMode: if self.params[GLOTHERLINES]: self._addInfiniteLines() if self.params[GLSTRIPLABELLING]: self._addOverlayText() # frame the top-left of the main plot area - after other plotting self._addPlotBorders(self._mainPlot) # add the axis labels which requires a mask to clean the edges self._addAxisMask() self._addGridTickMarks() if not axesOnly: if self.params[GLCURSORS]: self._addCursors() if self._selectedStrip == self.strip: self._addCursorText() if self.params[GLAXISLABELS] or self.params[GLAXISUNITS] or self.params[GLAXISTITLES]: self._addGridLabels() def _resetStripAxes(self): try: if self._updateAxes: # reset the strip to the original values self.strip._CcpnGLWidget.axisL, self.strip._CcpnGLWidget.axisR, self.strip._CcpnGLWidget.axisT, self.strip._CcpnGLWidget.axisB = self._oldValues self.strip._CcpnGLWidget._rescaleAllZoom() self.strip._CcpnGLWidget._buildGL() self.strip._CcpnGLWidget.buildAxisLabels() except Exception as es: pass def _addGridLines(self): """ Add grid lines to the main drawing area. """ if self._parent._gridVisible and self._parent.gridList[0]: colourGroups = OrderedDict() self._appendIndexLineGroup(indArray=self._parent.gridList[0], colourGroups=colourGroups, plotDim={PLOTLEFT : self.displayScale * self.mainView.left, PLOTBOTTOM: self.displayScale * self.mainView.bottom, PLOTWIDTH : self.displayScale * self.mainView.width, PLOTHEIGHT: self.displayScale * self.mainView.height}, name='grid', ratioLine=True, lineWidth=0.5 * self.baseThickness) self._appendGroup(drawing=self._mainPlot, colourGroups=colourGroups, name='grid') def _addDiagonalSideBands(self): """ Add the diagonal sideBand lines to the main drawing area. """ if self._parent.diagonalSideBandsGLList and self._parent._matchingIsotopeCodes: colourGroups = OrderedDict() self._appendIndexLineGroup(indArray=self._parent.diagonalSideBandsGLList, colourGroups=colourGroups, plotDim={PLOTLEFT : self.displayScale * self.mainView.left, PLOTBOTTOM: self.displayScale * self.mainView.bottom, PLOTWIDTH : self.displayScale * self.mainView.width, PLOTHEIGHT: self.displayScale * self.mainView.height}, name='diagonal', ratioLine=True, lineWidth=0.5 * self.baseThickness) self._appendGroup(drawing=self._mainPlot, colourGroups=colourGroups, name='diagonal') def _addDiagonalLine(self): """ Add the diagonal line to the main drawing area. """ if self._parent.diagonalGLList and self._parent._matchingIsotopeCodes: colourGroups = OrderedDict() self._appendIndexLineGroup(indArray=self._parent.diagonalGLList, colourGroups=colourGroups, plotDim={PLOTLEFT : self.displayScale * self.mainView.left, PLOTBOTTOM: self.displayScale * self.mainView.bottom, PLOTWIDTH : self.displayScale * self.mainView.width, PLOTHEIGHT: self.displayScale * self.mainView.height}, name='diagonal', ratioLine=True, lineWidth=0.5 * self.baseThickness) self._appendGroup(drawing=self._mainPlot, colourGroups=colourGroups, name='diagonal') def _addCursors(self): """ Add cursors/double cursor to the main drawing area. """ if not (self._parent._glCursorQueue and self._parent._glCursorHead < len(self._parent._glCursorQueue)): return _drawList = self._parent._glCursorQueue[self._parent._glCursorHead] if _drawList: colourGroups = OrderedDict() self._appendIndexLineGroup(indArray=_drawList, colourGroups=colourGroups, plotDim={PLOTLEFT : self.displayScale * self.mainView.left, PLOTBOTTOM: self.displayScale * self.mainView.bottom, PLOTWIDTH : self.displayScale * self.mainView.width, PLOTHEIGHT: self.displayScale * self.mainView.height}, name='cursors', ratioLine=True, lineWidth=0.5 * self.baseThickness) self._appendGroup(drawing=self._mainPlot, colourGroups=colourGroups, name='cursors') def _addCursorText(self): """ Add the cursor text. """ colourGroups = OrderedDict() if not self._parent.mouseString: return drawString = self._parent.mouseString if drawString.vertices is None or drawString.vertices.size == 0: return col = drawString.colors[0] if not isinstance(col, Iterable): col = drawString.colors[0:4] colour = colors.Color(*col[0:3], alpha=alphaClip(col[3])) colourPath = 'cursorText%s%s%s%s' % (colour.red, colour.green, colour.blue, colour.alpha) newLine = self._scaleRatioToWindow([drawString.attribs[0] + (self.fontXOffset * self._parent.deltaX), drawString.attribs[1] + (self.fontYOffset * self._parent.deltaY)]) if self.pointVisible(self._parent, newLine, x=self.displayScale * self.mainView.left, y=self.displayScale * self.mainView.bottom, width=self.displayScale * self.mainView.width, height=self.displayScale * self.mainView.height): if colourPath not in colourGroups: colourGroups[colourPath] = Group() textGroup = drawString.text.split('\n') textLine = len(textGroup) - 1 for text in textGroup: self._addString(colourGroups, colourPath, drawString, (newLine[0], newLine[1]), colour, text=text, offset=textLine ) textLine -= 1 for colourGroup in colourGroups.values(): self._mainPlot.add(colourGroup) def _addSpectrumViewManager(self, groupName): """ Add the spectrum objects to the main drawing area. Generator function to iterate over all the aliasing regions of all spectrumViews in the strip and execute user code each iteration e.g. >>> for data in self._addSpectrumViewManager('spectrumContours'): >>> print(data.spectrum) """ # simple class to export variables from the generator function @dataclass class _editValues: colourGroups = OrderedDict() GLObject = None specSettings = None spectrum = None dimensionCount = 0 matrix = None matrixSymbols = None spectrumView = None x = 0 y = 0 width = 0 height = 0 index = 0 alias = None _data = _editValues() # set the display parameters _data.x = _x = self.displayScale * self.mainView.left _data.y = _y = self.displayScale * self.mainView.bottom _data.width = _width = self.displayScale * self.mainView.width _data.height = _height = self.displayScale * self.mainView.height _data.index = 0 for spectrumView in self._ordering: if spectrumView.isDeleted: continue if spectrumView.spectrum.pid in self.params[GLSELECTEDPIDS]: # get the contour list _data.GLObject = self._parent._contourList[spectrumView] if spectrumView in self._parent._contourList else None if spectrumView in self._parent._spectrumSettings.keys(): # get the spectrum settings for the spectrumView _data.specSettings = specSettings = self._parent._spectrumSettings[spectrumView] _data.spectrumView = spectrumView _data.spectrum = spectrumView.spectrum _data.dimensionCount = spectrumView.spectrum.dimensionCount if spectrumView.spectrum.dimensionCount > 1: # draw nD spectra # self.globalGL._shaderProgram1.setGLUniformMatrix4fv('mvMatrix', # 1, GL.GL_FALSE, # self._spectrumSettings[spectrumView][SPECTRUM_MATRIX]) _, fxMax = specSettings[SPECTRUM_XLIMITS] _, fyMax = specSettings[SPECTRUM_YLIMITS] dxAF, dyAF = specSettings[SPECTRUM_AF] xScale, yScale = specSettings[SPECTRUM_SCALE] alias = specSettings[SPECTRUM_ALIASINGINDEX] folding = specSettings[SPECTRUM_FOLDINGMODE] for ii in range(alias[0][0], alias[0][1] + 1, 1): for jj in range(alias[1][0], alias[1][1] + 1, 1): foldX = foldY = 1.0 foldXOffset = foldYOffset = 0 if folding[0] == 'mirror': foldX = pow(-1, ii) foldXOffset = -dxAF if foldX < 0 else 0 if folding[1] == 'mirror': foldY = pow(-1, jj) foldYOffset = -dyAF if foldY < 0 else 0 # build the spectrum transformation matrix specMatrix = np.array([xScale * foldX, 0.0, 0.0, 0.0, 0.0, yScale * foldY, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, fxMax + (ii * dxAF) + foldXOffset, fyMax + (jj * dyAF) + foldYOffset, 0.0, 1.0], dtype=np.float32) _data.matrix = np.transpose(specMatrix.reshape((4, 4))) _data.matrixSymbols = np.transpose(specMatrix.reshape((4, 4))) _data.alias = getAliasSetting(ii, jj) # get the transformation matrix from the spectrumView # mat = np.transpose(self._parent._spectrumSettings[spectrumView][SPECTRUM_MATRIX].reshape((4, 4))) # # clip all colours first - not sure if needed now, but was causing overflow error in the past # _colors = np.clip(thisSpec.colors, 0.0, 0.9999) yield _data # pass object _data.index += 1 else: # draw 1D spectra # assume that the vertexArray is a GL_LINE_STRIP _, fxMax = specSettings[SPECTRUM_XLIMITS] dxAF, _ = specSettings[SPECTRUM_AF] xScale, _ = specSettings[SPECTRUM_SCALE] alias = specSettings[SPECTRUM_ALIASINGINDEX] folding = specSettings[SPECTRUM_FOLDINGMODE] stackX, stackY = specSettings[SPECTRUM_STACKEDMATRIXOFFSET] for ii in range(alias[0][0], alias[0][1] + 1, 1): foldX = 1.0 foldXOffsetSym = foldXOffset = 0 if folding[0] == 'mirror': foldX = pow(-1, ii) foldXOffset = (2 * fxMax - dxAF) if foldX < 0 else 0 foldXOffsetSym = -dxAF if foldX < 0 else 0 if self._parent._stackingMode: _matrix = np.array(specSettings[SPECTRUM_STACKEDMATRIX]) else: _matrix = np.array(self._parent._IMatrix) # build the spectrum transformation matrices _matrixSym = np.array([xScale * foldX, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, fxMax + (ii * dxAF) + foldXOffsetSym + stackX, stackY, 0.0, 1.0], dtype=np.float32) # take the stacking matrix and insert the correct x-scaling to map the pointPositions to the screen _matrix[0] = foldX _matrix[12] += (ii * dxAF) + foldXOffset # add to the stacked offset _data.matrix = np.transpose(_matrix.reshape((4, 4))) _data.matrixSymbols = np.transpose(_matrixSym.reshape((4, 4))) _data.alias = getAliasSetting(ii, 0) yield _data # pass object back to the calling method _data.index += 1 if _data.colourGroups: self._appendGroup(drawing=self._mainPlot, colourGroups=_data.colourGroups, name=groupName) def _addSpectrumContours(self): """ Add the spectrum contours to the main drawing area. """ for data in self._addSpectrumViewManager('spectrumContours'): if data.dimensionCount > 1: for ppInd in range(0, len(data.GLObject.indices), 2): ppInd0 = int(data.GLObject.indices[ppInd]) ppInd1 = int(data.GLObject.indices[ppInd + 1]) vectStart = [data.GLObject.vertices[ppInd0 * 2], data.GLObject.vertices[ppInd0 * 2 + 1], 0.0, 1.0] vectStart = data.matrix.dot(vectStart) vectEnd = [data.GLObject.vertices[ppInd1 * 2], data.GLObject.vertices[ppInd1 * 2 + 1], 0.0, 1.0] vectEnd = data.matrix.dot(vectEnd) newLine = [vectStart[0], vectStart[1], vectEnd[0], vectEnd[1]] colour = colors.Color(*data.GLObject.colors[ppInd0 * 4:ppInd0 * 4 + 3], alpha=alphaClip(data.GLObject.colors[ppInd0 * 4 + 3])) colourPath = 'spectrumContours%s%s%s%s%s%s' % (data.spectrumView.pid, data.index, colour.red, colour.green, colour.blue, colour.alpha) newLine = self.lineVisible(self._parent, newLine, x=data.x, y=data.y, width=data.width, height=data.height) if newLine: if colourPath not in data.colourGroups: data.colourGroups[colourPath] = {PDFLINES : [], PDFSTROKEWIDTH: 0.5 * self.baseThickness * self.contourThickness, PDFSTROKECOLOR: colour, PDFSTROKELINECAP: 1} data.colourGroups[colourPath][PDFLINES].append(newLine) else: # drawVertexColor self._appendVertexLineGroup(indArray=data.GLObject, colourGroups=data.colourGroups, plotDim={PLOTLEFT : data.x, PLOTBOTTOM: data.y, PLOTWIDTH : data.width, PLOTHEIGHT: data.height}, name='spectrumContours%s%s' % (data.spectrumView.pid, data.index), mat=data.matrix, lineWidth=0.5 * self.baseThickness * self.contourThickness ) def _addSpectrumBoundaries(self): """ Add the spectrum boundaries to the main drawing area. """ colourGroups = OrderedDict() self._appendIndexLineGroup(indArray=self._parent.boundingBoxes, colourGroups=colourGroups, plotDim={PLOTLEFT : self.displayScale * self.mainView.left, PLOTBOTTOM: self.displayScale * self.mainView.bottom, PLOTWIDTH : self.displayScale * self.mainView.width, PLOTHEIGHT: self.displayScale * self.mainView.height}, name='boundary', lineWidth=0.5 * self.baseThickness) self._appendGroup(drawing=self._mainPlot, colourGroups=colourGroups, name='boundaries') def _addPeakSymbols(self): """ Add the peak symbols to the main drawing area. """ _symbols = self._parent._GLPeaks._GLSymbols # iterate through the visible regions with the viewManager for data in self._addSpectrumViewManager('peakSymbols'): attribList = data.spectrumView.peakListViews validListViews = [_symbols[pp] for pp in attribList if pp in _symbols.keys() and pp.isDisplayed and data.spectrumView.isDisplayed and pp.peakList.pid in self.params[GLSELECTEDPIDS] ] for GLObject in validListViews: self._appendIndexLineGroup(indArray=GLObject, colourGroups=data.colourGroups, plotDim={PLOTLEFT : data.x, PLOTBOTTOM: data.y, PLOTWIDTH : data.width, PLOTHEIGHT: data.height}, name='spectrumView%s%s%s' % ('peakSymbols', data.index, data.spectrumView.pid), mat=data.matrixSymbols, fillMode=None, splitGroups=False, lineWidth=0.5 * self.baseThickness * self.symbolThickness, alias=data.alias) def _addMultipletSymbols(self): """ Add the multiplet symbols to the main drawing area. """ _symbols = self._parent._GLMultiplets._GLSymbols # iterate through the visible regions with the viewManager for data in self._addSpectrumViewManager('multipletSymbols'): attribList = data.spectrumView.multipletListViews validListViews = [_symbols[pp] for pp in attribList if pp in _symbols.keys() and pp.isDisplayed and data.spectrumView.isDisplayed and pp.multipletList.pid in self.params[GLSELECTEDPIDS]] for GLObject in validListViews: self._appendIndexLineGroup(indArray=GLObject, colourGroups=data.colourGroups, plotDim={PLOTLEFT : data.x, PLOTBOTTOM: data.y, PLOTWIDTH : data.width, PLOTHEIGHT: data.height}, name='spectrumView%s%s%s' % ('multipletSymbols', data.index, data.spectrumView.pid), mat=data.matrixSymbols, fillMode=None, splitGroups=False, lineWidth=0.5 * self.baseThickness * self.symbolThickness, alias=data.alias) def _addMarkLines(self): """ Add the mark lines to the main drawing area. """ colourGroups = OrderedDict() self._appendIndexLineGroup(indArray=self._parent._marksList, colourGroups=colourGroups, plotDim={PLOTLEFT : self.displayScale * self.mainView.left, PLOTBOTTOM: self.displayScale * self.mainView.bottom, PLOTWIDTH : self.displayScale * self.mainView.width, PLOTHEIGHT: self.displayScale * self.mainView.height}, name='marks', lineWidth=0.5 * self.baseThickness) self._appendGroup(drawing=self._mainPlot, colourGroups=colourGroups, name='marks') def _addIntegralLines(self): """ Add the integral lines to the main drawing area. """ colourGroups = OrderedDict() self._appendIndexLineGroupFill(indArray=self._parent._GLIntegrals._GLSymbols, listView='integralList', colourGroups=colourGroups, plotDim={PLOTLEFT : self.displayScale * self.mainView.left, PLOTBOTTOM: self.displayScale * self.mainView.bottom, PLOTWIDTH : self.displayScale * self.mainView.width, PLOTHEIGHT: self.displayScale * self.mainView.height}, name='IntegralListsFill', fillMode=GL.GL_FILL, splitGroups=True, lineWidth=0.5 * self.baseThickness) self._appendGroup(drawing=self._mainPlot, colourGroups=colourGroups, name='integralLists') def _addIntegralAreas(self): """ Add the integral filled areas to the main drawing area. """ colourGroups = OrderedDict() for spectrumView in self._ordering: if spectrumView.isDeleted: continue validIntegralListViews = [pp for pp in spectrumView.integralListViews if pp.isDisplayed and spectrumView.isDisplayed and pp in self._parent._GLIntegrals._GLSymbols.keys() and pp.integralList.pid in self.params[GLSELECTEDPIDS]] _x = self.displayScale * self.mainView.left _y = self.displayScale * self.mainView.bottom _width = self.displayScale * self.mainView.width _height = self.displayScale * self.mainView.height for integralListView in validIntegralListViews: # spectrumView.integralListViews: mat = None if spectrumView.spectrum.dimensionCount > 1: if spectrumView in self._parent._spectrumSettings.keys(): # draw pass else: # assume that the vertexArray is a GL_LINE_STRIP if spectrumView in self._parent._contourList.keys(): if self._parent._stackingMode: mat = np.transpose(self._parent._spectrumSettings[spectrumView][SPECTRUM_STACKEDMATRIX].reshape((4, 4))) else: mat = None # draw the integralAreas if they exist for integralArea in self._parent._GLIntegrals._GLSymbols[integralListView]._regions: if hasattr(integralArea, '_integralArea'): thisSpec = integralArea._integralArea for vv in range(0, len(thisSpec.vertices) - 4, 2): if mat is not None: vectStart = [thisSpec.vertices[vv], thisSpec.vertices[vv + 1], 0.0, 1.0] vectStart = mat.dot(vectStart) vectMid = [thisSpec.vertices[vv + 2], thisSpec.vertices[vv + 3], 0.0, 1.0] vectMid = mat.dot(vectMid) vectEnd = [thisSpec.vertices[vv + 4], thisSpec.vertices[vv + 5], 0.0, 1.0] vectEnd = mat.dot(vectEnd) newLine = [vectStart[0], vectStart[1], vectMid[0], vectMid[1], vectEnd[0], vectEnd[1]] else: newLine = list(thisSpec.vertices[vv:vv + 6]) colour = colors.Color(*thisSpec.colors[vv * 2:vv * 2 + 3], alpha=alphaClip(thisSpec.colors[vv * 2 + 3])) colourPath = 'spectrumViewIntegralFill%s%s%s%s%s' % ( spectrumView.pid, colour.red, colour.green, colour.blue, colour.alpha) newLine = self.lineVisible(self._parent, newLine, x=_x, y=_y, width=_width, height=_height) if newLine: if colourPath not in colourGroups: colourGroups[colourPath] = {PDFLINES: [], PDFFILLCOLOR: colour, PDFSTROKE: None, PDFSTROKECOLOR: None} colourGroups[colourPath][PDFLINES].append(newLine) self._appendGroup(drawing=self._mainPlot, colourGroups=colourGroups, name='integralListsAreaFill') def _addRegions(self): """ Add the regions to the main drawing area. """ colourGroups = OrderedDict() self._appendIndexLineGroup(indArray=self._parent._externalRegions, colourGroups=colourGroups, plotDim={PLOTLEFT : self.displayScale * self.mainView.left, PLOTBOTTOM: self.displayScale * self.mainView.bottom, PLOTWIDTH : self.displayScale * self.mainView.width, PLOTHEIGHT: self.displayScale * self.mainView.height}, name='regions', lineWidth=0.5 * self.baseThickness) self._appendGroup(drawing=self._mainPlot, colourGroups=colourGroups, name='regions') def _addPeakLabels(self): """ Add the peak labels to the main drawing area. """ colourGroups = OrderedDict() # iterate through the visible regions with the viewManager for data in self._addSpectrumViewManager('peakLabels'): validPeakListViews = [pp for pp in data.spectrumView.peakListViews if pp.isDisplayed and data.spectrumView.isDisplayed and pp in self._parent._GLPeaks._GLLabels.keys() and pp.peakList.pid in self.params[GLSELECTEDPIDS]] for peakListView in validPeakListViews: # spectrumView.peakListViews: for drawString in self._parent._GLPeaks._GLLabels[peakListView].stringList: if drawString.vertices is None or drawString.vertices.size == 0: continue col = drawString.colors[0] if not isinstance(col, Iterable): col = drawString.colors[0:4] _alias = 1.0 if data.alias is not None and drawString._alias is not None: if abs(data.alias - drawString._alias) > 0.5: _alias = self.params[GLALIASSHADE] / 100.0 colour = colors.Color(*col[0:3], alpha=_alias * alphaClip(col[3])) colourPath = 'spectrumViewPeakLabels%s%s%s%s%s%s' % ( data.spectrumView.pid, data.index, colour.red, colour.green, colour.blue, colour.alpha) if data.matrixSymbols is not None: newLine = [drawString.attribs[0], drawString.attribs[1], 0.0, 1.0] newLine = data.matrixSymbols.dot(newLine)[0:2] else: newLine = [drawString.attribs[0], drawString.attribs[1]] if self.pointVisible(self._parent, newLine, x=data.x, y=data.y, width=data.width, height=data.height): if colourPath not in colourGroups: colourGroups[colourPath] = Group() textGroup = drawString.text.split('\n') textLine = len(textGroup) - 1 for text in textGroup: self._addString(colourGroups, colourPath, drawString, (newLine[0], newLine[1]), # + (textLine * drawString.font.fontSize * self.fontScale)), colour, text=text, offset=textLine ) textLine -= 1 for colourGroup in colourGroups.values(): self._mainPlot.add(colourGroup) def _addIntegralLabels(self): """ Add the integral labels to the main drawing area. """ colourGroups = OrderedDict() for spectrumView in self._ordering: if spectrumView.isDeleted: continue validIntegralListViews = [pp for pp in spectrumView.integralListViews if pp.isDisplayed and spectrumView.isDisplayed and pp in self._parent._GLIntegrals._GLLabels.keys() and pp.integralList.pid in self.params[GLSELECTEDPIDS]] for integralListView in validIntegralListViews: # spectrumView.integralListViews: for drawString in self._parent._GLIntegrals._GLLabels[integralListView].stringList: if drawString.vertices is None or drawString.vertices.size == 0: continue col = drawString.colors[0] if not isinstance(col, Iterable): col = drawString.colors[0:4] colour = colors.Color(*col[0:3], alpha=alphaClip(col[3])) colourPath = 'spectrumViewIntegralLabels%s%s%s%s%s' % ( spectrumView.pid, colour.red, colour.green, colour.blue, colour.alpha) newLine = [drawString.attribs[0], drawString.attribs[1]] if self.pointVisible(self._parent, newLine, x=self.displayScale * self.mainView.left, y=self.displayScale * self.mainView.bottom, width=self.displayScale * self.mainView.width, height=self.displayScale * self.mainView.height): if colourPath not in colourGroups: colourGroups[colourPath] = Group() textGroup = drawString.text.split('\n') textLine = len(textGroup) - 1 for text in textGroup: self._addString(colourGroups, colourPath, drawString, (newLine[0], newLine[1]), # + (textLine * drawString.font.fontSize * self.fontScale)), colour, text=text, offset=textLine ) textLine -= 1 for colourGroup in colourGroups.values(): self._mainPlot.add(colourGroup) def _addMultipletLabels(self): """ Add the multiplet labels to the main drawing area. """ colourGroups = OrderedDict() # iterate through the visible regions with the viewManager for data in self._addSpectrumViewManager('multipletLabels'): validMultipletListViews = [pp for pp in data.spectrumView.multipletListViews if pp.isDisplayed and data.spectrumView.isDisplayed and pp in self._parent._GLMultiplets._GLLabels.keys() and pp.multipletList.pid in self.params[GLSELECTEDPIDS]] for multipletListView in validMultipletListViews: # spectrumView.multipletListViews: for drawString in self._parent._GLMultiplets._GLLabels[multipletListView].stringList: if drawString.vertices is None or drawString.vertices.size == 0: continue col = drawString.colors[0] if not isinstance(col, Iterable): col = drawString.colors[0:4] _alias = 1.0 if data.alias is not None and drawString._alias is not None: if abs(data.alias - drawString._alias) > 0.5: _alias = self.params[GLALIASSHADE] / 100.0 colour = colors.Color(*col[0:3], alpha=_alias * alphaClip(col[3])) colourPath = 'spectrumViewMultipletLabels%s%s%s%s%s%s' % ( data.spectrumView.pid, data.index, colour.red, colour.green, colour.blue, colour.alpha) if data.matrixSymbols is not None: newLine = [drawString.attribs[0], drawString.attribs[1], 0.0, 1.0] newLine = data.matrixSymbols.dot(newLine)[0:2] else: newLine = [drawString.attribs[0], drawString.attribs[1]] if self.pointVisible(self._parent, newLine, x=data.x, y=data.y, width=data.width, height=data.height): if colourPath not in colourGroups: colourGroups[colourPath] = Group() textGroup = drawString.text.split('\n') textLine = len(textGroup) - 1 for text in textGroup: self._addString(colourGroups, colourPath, drawString, (newLine[0], newLine[1]), # + (textLine * drawString.font.fontSize * self.fontScale)), colour, text=text, offset=textLine ) textLine -= 1 for colourGroup in colourGroups.values(): self._mainPlot.add(colourGroup) def _addMarkLabels(self): """ Add the mark labels to the main drawing area. """ colourGroups = OrderedDict() for drawString in self._parent._marksAxisCodes: if drawString.vertices is None or drawString.vertices.size == 0: continue col = drawString.colors[0] if not isinstance(col, Iterable): col = drawString.colors[0:4] colour = colors.Color(*col[0:3], alpha=alphaClip(col[3])) colourPath = 'projectMarks%s%s%s%s' % (colour.red, colour.green, colour.blue, colour.alpha) newLine = [drawString.attribs[0], drawString.attribs[1]] if self.pointVisible(self._parent, newLine, x=self.displayScale * self.mainView.left, y=self.displayScale * self.mainView.bottom, width=self.displayScale * self.mainView.width, height=self.displayScale * self.mainView.height): if colourPath not in colourGroups: colourGroups[colourPath] = Group() self._addString(colourGroups, colourPath, drawString, newLine, colour, boxed=False) for colourGroup in colourGroups.values(): self._mainPlot.add(colourGroup) def _addSpectrumLabels(self): """ Add the (stacked) spectrum labels to the main drawing area. """ colourGroups = OrderedDict() if not (self._parent._spectrumLabelling and self._parent._spectrumLabelling.strings): return for drawString in self._parent._spectrumLabelling.strings.values(): if drawString.vertices is None or drawString.vertices.size == 0: continue col = drawString.colors[0] if not isinstance(col, Iterable): col = drawString.colors[0:4] colour = colors.Color(*col[0:3], alpha=alphaClip(col[3])) colourPath = 'projectSpectrumLabels%s%s%s%s' % (colour.red, colour.green, colour.blue, colour.alpha) newLine = [drawString.attribs[0], drawString.attribs[1]] if self.pointVisible(self._parent, newLine, x=self.displayScale * self.mainView.left, y=self.displayScale * self.mainView.bottom, width=self.displayScale * self.mainView.width, height=self.displayScale * self.mainView.height): if colourPath not in colourGroups: colourGroups[colourPath] = Group() self._addString(colourGroups, colourPath, drawString, newLine, colour, boxed=True) for colourGroup in colourGroups.values(): self._mainPlot.add(colourGroup) def _addSingleTrace(self, traceName, trace, spectrumView, colourGroups): if spectrumView and not spectrumView.isDeleted and spectrumView.isDisplayed: # drawVertexColor if self._parent._stackingMode: mat = np.transpose(self._parent._spectrumSettings[spectrumView][SPECTRUM_STACKEDMATRIX].reshape((4, 4))) else: mat = None self._appendVertexLineGroup(indArray=trace, colourGroups=colourGroups, plotDim={PLOTLEFT : self.displayScale * self.mainView.left, PLOTBOTTOM: self.displayScale * self.mainView.bottom, PLOTWIDTH : self.displayScale * self.mainView.width, PLOTHEIGHT: self.displayScale * self.mainView.height}, name='%s%s' % (traceName, spectrumView.pid), includeLastVertex=not self._parent.is1D, mat=mat, lineWidth=0.5 * self.baseThickness * self.contourThickness) def _addTraces(self): """ Add the traces to the main drawing area. """ colourGroups = OrderedDict() for hTrace in self._parent._staticHTraces: self._addSingleTrace('hTrace', hTrace, hTrace.spectrumView, colourGroups) for vTrace in self._parent._staticVTraces: self._addSingleTrace('vTrace', vTrace, vTrace.spectrumView, colourGroups) self._appendGroup(drawing=self._mainPlot, colourGroups=colourGroups, name='traces') def _addLiveTraces(self): """ Add the live traces to the main drawing area. """ colourGroups = OrderedDict() if self._parent.showActivePhaseTrace or not self._parent.spectrumDisplay.phasingFrame.isVisible(): if self._parent._updateHTrace: for spectrumView, hTrace in self._parent._hTraces.items(): self._addSingleTrace('hTrace', hTrace, spectrumView, colourGroups) if self._parent._updateVTrace: for spectrumView, vTrace in self._parent._vTraces.items(): self._addSingleTrace('vTrace', vTrace, spectrumView, colourGroups) self._appendGroup(drawing=self._mainPlot, colourGroups=colourGroups, name='traces') def _addInfiniteLines(self): """ Add the infinite lines to the main drawing area. """ colourGroups = OrderedDict() _x = self.displayScale * self.mainView.left _y = self.displayScale * self.mainView.bottom _width = self.displayScale * self.mainView.width _height = self.displayScale * self.mainView.height for infLine in self._parent._infiniteLines: if infLine.visible: colour = colors.Color(*infLine.brush[0:3], alpha=alphaClip(infLine.brush[3])) colourPath = 'infiniteLines%s%s%s%s%s' % (colour.red, colour.green, colour.blue, colour.alpha, infLine.lineStyle) if infLine.orientation == 'h': newLine = [self._axisL, infLine.values, self._axisR, infLine.values] else: newLine = [infLine.values, self._axisT, infLine.values, self._axisB] newLine = self.lineVisible(self._parent, newLine, x=_x, y=_y, width=_width, height=_height) if newLine: if colourPath not in colourGroups: colourGroups[colourPath] = {PDFLINES : [], PDFSTROKEWIDTH : 0.5 * infLine.lineWidth * self.baseThickness, PDFSTROKECOLOR : colour, PDFSTROKELINECAP : 1, PDFCLOSEPATH: False, PDFSTROKEDASHARRAY: GLLINE_STYLES_ARRAY[infLine.lineStyle]} colourGroups[colourPath][PDFLINES].append(newLine) self._appendGroup(drawing=self._mainPlot, colourGroups=colourGroups, name='infiniteLines') def _scaleRatioToWindow(self, values): return [values[0] * (self._axisR - self._axisL) + self._axisL, values[1] * (self._axisT - self._axisB) + self._axisB] def _addOverlayText(self): """ Add the overlay text to the main drawing area. """ colourGroups = OrderedDict() drawString = self._parent.stripIDString if drawString.vertices is None or drawString.vertices.size == 0: return colour = self.foregroundColour colourPath = 'overlayText%s%s%s%s' % (colour.red, colour.green, colour.blue, colour.alpha) newLine = self._scaleRatioToWindow([drawString.attribs[0] + (self.fontXOffset * self._parent.deltaX), drawString.attribs[1] + (self.fontYOffset * self._parent.deltaY)]) if self.pointVisible(self._parent, newLine, x=self.displayScale * self.mainView.left, y=self.displayScale * self.mainView.bottom, width=self.displayScale * self.mainView.width, height=self.displayScale * self.mainView.height): pass if colourPath not in colourGroups: colourGroups[colourPath] = Group() self._addString(colourGroups, colourPath, drawString, newLine, colour, boxed=True) for colourGroup in colourGroups.values(): self._mainPlot.add(colourGroup) def _addAxisMask(self): """ Add a mask to clean the right/bottom axis areas. """ ll1 = None ll2 = None if self.rAxis and self.bAxis: ll1 = [0.0, 0.0, 0.0, self.displayScale * self.mainView.bottom, self.displayScale * self.mainView.width, self.displayScale * self.mainView.bottom, self.displayScale * self.mainView.width, self.pixHeight, self.pixWidth, self.pixHeight, self.pixWidth, 0.0] ll2 = [0.0, 0.0, self.pixWidth, 0.0, self.pixWidth, self.pixHeight] elif self.rAxis: ll1 = [self.displayScale * self.mainView.width, 0.0, self.displayScale * self.mainView.width, self.pixHeight, self.pixWidth, self.pixHeight, self.pixWidth, 0.0] ll2 = [self.pixWidth, 0.0, self.pixWidth, self.pixHeight] elif self.bAxis: ll1 = [0.0, 0.0, 0.0, self.displayScale * self.mainView.bottom, self.pixWidth, self.displayScale * self.mainView.bottom, self.pixWidth, 0.0] ll2 = [0.0, 0.0, self.pixWidth, 0.0] if ll1: pl = Path(fillColor=self.backgroundColour, stroke=None, strokeColor=None) pl.moveTo(ll1[0], ll1[1]) for vv in range(2, len(ll1), 2): pl.lineTo(ll1[vv], ll1[vv + 1]) pl.closePath() self._mainPlot.add(pl) pl = Path(fillColor=None, strokeColor=self.backgroundColour, strokeWidth=1.0) pl.moveTo(ll2[0], ll2[1]) for vv in range(2, len(ll2), 2): pl.lineTo(ll2[vv], ll2[vv + 1]) self._mainPlot.add(pl) def _addGridTickMarks(self): """ Add tick marks to the main drawing area. """ if self.rAxis or self.bAxis: colourGroups = OrderedDict() # add the right axis if visible if self.rAxis and self.params[GLAXISMARKS]: indArray = self._parent.gridList[1] if indArray.indices is not None and indArray.indices.size != 0: # add the vertices for the grid lines self._appendIndexLineGroup(indArray=indArray, colourGroups=colourGroups, plotDim={PLOTLEFT : self.displayScale * self.rAxisMarkView.left, PLOTBOTTOM: self.displayScale * self.rAxisMarkView.bottom, PLOTWIDTH : self.displayScale * self.rAxisMarkView.width, PLOTHEIGHT: self.displayScale * self.rAxisMarkView.height}, name='gridAxes', setColour=self.foregroundColour, ratioLine=True, lineWidth=0.5 * self.baseThickness) # # add the right axis border line if needed # if self.params[GLPLOTBORDER] or (self.rAxis and self.params[GLAXISLINES]): # from ccpn.ui.gui.lib.OpenGL.CcpnOpenGLArrays import GLVertexArray # # # dummy list with non-visible line # tempVertexArray = GLVertexArray(numLists=1, drawMode=GL.GL_LINE, dimension=2) # tempVertexArray.indices = [0, 1] # tempVertexArray.vertices = [0.0, 0.0, 0.0, 0.0] # # # return the colour list defined by self.foreColour - needs setColour to be defined in the function call # setGroup = self._appendIndexLineGroup(indArray=tempVertexArray, # colourGroups=colourGroups, # plotDim={PLOTLEFT : self.displayScale * self.rAxisMarkView.left, # PLOTBOTTOM: self.displayScale * self.rAxisMarkView.bottom, # PLOTWIDTH : self.displayScale * self.rAxisMarkView.width, # PLOTHEIGHT: self.displayScale * self.rAxisMarkView.height}, # name='gridAxes', # setColour=self.foregroundColour, # ratioLine=True, # lineWidth=0.5 * self.baseThickness) # if setGroup in colourGroups: # colourGroups[setGroup][PDFLINES].append([self.displayScale * self.mainView.width, self.displayScale * self.mainView.bottom, # self.displayScale * self.mainView.width, self.pixHeight]) # add the bottom axis if visible if self.bAxis and self.params[GLAXISMARKS]: indArray = self._parent.gridList[2] if indArray.indices is not None and indArray.indices.size != 0: # add the vertices for the grid lines self._appendIndexLineGroup(indArray=indArray, colourGroups=colourGroups, plotDim={PLOTLEFT : self.displayScale * self.bAxisMarkView.left, PLOTBOTTOM: self.displayScale * self.bAxisMarkView.bottom, PLOTWIDTH : self.displayScale * self.bAxisMarkView.width, PLOTHEIGHT: self.displayScale * self.bAxisMarkView.height}, name='gridAxes', setColour=self.foregroundColour, ratioLine=True, lineWidth=0.5 * self.baseThickness) # # add the bottom axis border line if needed # if self.params[GLPLOTBORDER] or (self.bAxis and self.params[GLAXISLINES]): # from ccpn.ui.gui.lib.OpenGL.CcpnOpenGLArrays import GLVertexArray # # tempVertexArray = GLVertexArray(numLists=1, drawMode=GL.GL_LINE, dimension=2) # tempVertexArray.indices = [0, 1] # tempVertexArray.vertices = [0.0, 0.0, 0.0, 0.0] # # # dummy list with non-visible line # setGroup = self._appendIndexLineGroup(indArray=tempVertexArray, # colourGroups=colourGroups, # plotDim={PLOTLEFT : self.displayScale * self.bAxisMarkView.left, # PLOTBOTTOM: self.displayScale * self.bAxisMarkView.bottom, # PLOTWIDTH : self.displayScale * self.bAxisMarkView.width, # PLOTHEIGHT: self.displayScale * self.bAxisMarkView.height}, # name='gridAxes', # setColour=self.foregroundColour, # ratioLine=True, # lineWidth=0.5 * self.baseThickness) # # if setGroup in colourGroups: # colourGroups[setGroup][PDFLINES].append([self.displayScale * self.mainView.left, self.displayScale * self.mainView.bottom, # self.displayScale * self.mainView.width, self.displayScale * self.mainView.bottom]) self._appendGroup(drawing=self._mainPlot, colourGroups=colourGroups, name='gridAxes') def _addString(self, colourGroups, colourPath, drawString, position, colour, boxed=False, text=None, offset=0): if self.params[GLUSEPRINTFONT]: _fontName, _fontSize = self.params[GLPRINTFONT] # allows the user to set the font size if _fontName == DEFAULT_FONT: _fontName = drawString.font.fontName # _fontSize = self._printFont.pointSize() # if _fontSize < 0: # _fontSize = self._printFont.pixelSize() # _fontName = self._printFont.family() else: _fontSize = drawString.font.fontSize * self.fontScale _fontName = drawString.font.fontName newStr = String(position[0], position[1] + (offset * _fontSize), text or drawString.text, fontSize=_fontSize, fontName=_fontName, fillColor=colour) if boxed: bounds = newStr.getBounds() # arbitrary scaling dx = _fontSize * 0.11 dy = _fontSize * 0.125 colourGroups[colourPath].add(Rect(bounds[0] - dx, bounds[1] - dy, (bounds[2] - bounds[0]) + 5 * dx, (bounds[3] - bounds[1]) + 2.0 * dy, strokeColor=None, fillColor=self.backgroundColour)) colourGroups[colourPath].add(newStr) def _addGridLabels(self): """ Add labels/titles/units to the right/bottom axis areas. """ if self.rAxis or self.bAxis: colourGroups = OrderedDict() if self.rAxis: numStrs = len(self._parent._axisYLabelling) for strNum, drawString in enumerate(self._parent._axisYLabelling): # skip empty strings if not drawString.text: continue # drawTextArray colour = self.foregroundColour colourPath = 'axisLabels%s%s%s%s' % (colour.red, colour.green, colour.blue, colour.alpha) # add (0, 3) to mid-point # mid = self._axisL + (0 + drawString.attribs[0]) * (self._axisR - self._axisL) / self._parent.AXIS_MARGINRIGHT # newLine = [mid, drawString.attribs[1] + (3 * self._parent.pixelY)] # mid = self._axisL + drawString.attribs[0] * (self._axisR - self._axisL) * self._parent.pixelX # newLine = [mid, drawString.attribs[1] + (3 * self._parent.deltaY)] attribPos = (0.0, 0.0) if drawString.attribs.size < 2 else drawString.attribs[0:2] newLine = self._scaleRatioToWindow([(self.fontXOffset + attribPos[0]) / self._parent.AXIS_MARGINRIGHT, attribPos[1] + (self.fontYOffset * self._parent.deltaY)]) if self.pointVisible(self._parent, newLine, x=self.displayScale * self.rAxisBarView.left, y=self.displayScale * self.rAxisBarView.bottom, width=self.displayScale * self.rAxisBarView.width, height=self.displayScale * self.rAxisBarView.height): if colourPath not in colourGroups: colourGroups[colourPath] = Group() # set box around the last 2 elements (axis title and units), and skip if not needed if strNum == numStrs - 1 and self.params[GLAXISUNITS]: # draw units self._addString(colourGroups, colourPath, drawString, newLine, colour, boxed=True) elif strNum == numStrs - 2 and self.params[GLAXISTITLES]: # draw axis title self._addString(colourGroups, colourPath, drawString, newLine, colour, boxed=True) elif strNum < numStrs - 2 and self.params[GLAXISLABELS]: # draw labels self._addString(colourGroups, colourPath, drawString, newLine, colour, boxed=False) if self.bAxis: numStrs = len(self._parent._axisXLabelling) for strNum, drawString in enumerate(self._parent._axisXLabelling): # skip empty strings if not drawString.text: continue # drawTextArray colour = self.foregroundColour colourPath = 'axisLabels%s%s%s%s' % (colour.red, colour.green, colour.blue, colour.alpha) # add (0, 3) to mid # mid = self._axisB + (3 + drawString.attribs[1]) * (self._axisT - self._axisB) / self._parent.AXIS_MARGINBOTTOM # newLine = [drawString.attribs[0] + (0 * self._parent.pixelX), mid] # mid = self._axisB + drawString.attribs[1] * (self._axisT - self._axisB) # newLine = [drawString.attribs[0] + (0 * self._parent.deltaX), mid] attribPos = (0.0, 0.0) if drawString.attribs.size < 2 else drawString.attribs[0:2] newLine = self._scaleRatioToWindow([attribPos[0] + (self.fontXOffset * self._parent.deltaX), (self.fontYOffset + attribPos[1]) / self._parent.AXIS_MARGINBOTTOM]) if self.pointVisible(self._parent, newLine, x=self.displayScale * self.bAxisBarView.left, y=self.displayScale * self.bAxisBarView.bottom, width=self.displayScale * self.bAxisBarView.width, height=self.displayScale * self.bAxisBarView.height): if colourPath not in colourGroups: colourGroups[colourPath] = Group() # set box around the last 2 elements (axis title and units), and skip if not needed if strNum == numStrs - 1 and self.params[GLAXISUNITS]: # draw units self._addString(colourGroups, colourPath, drawString, newLine, colour, boxed=True) elif strNum == numStrs - 2 and self.params[GLAXISTITLES]: # draw axis title self._addString(colourGroups, colourPath, drawString, newLine, colour, boxed=True) elif strNum < numStrs - 2 and self.params[GLAXISLABELS]: # draw labels self._addString(colourGroups, colourPath, drawString, newLine, colour, boxed=False) for colourGroup in colourGroups.values(): self._mainPlot.add(colourGroup)
[docs] def report(self): """ Return the current report for the GL widget. This is the vector image for the current strip containing the GL widget, it is a reportlab Flowable type object that can be added to reportlab documents. :return reportlab.platypus.Flowable: """ scale = self.displayScale return Clipped_Flowable(width=self.pixWidth, height=self.pixHeight, mainPlot=self._mainPlot, mainDim={PLOTLEFT : 0, #scale*view.left, PLOTBOTTOM: 0, #scale*view.bottom, PLOTWIDTH : self.pixWidth, #scale*self.mainView.width, PLOTHEIGHT: self.pixHeight #scale*self.mainView.height } )
def _addDrawingToStory(self): """ Add the current drawing the story of a document """ report = self.report() self.stripReports.append(report) self._appendStripSize(report) def _appendStripSize(self, report): _val = 1.0 if self.params[GLSTRIPDIRECTION] == 'Y': if self.stripNumber < (self.numStrips - 1): self.stripWidths.append((report.width + self.stripSpacing) * _val) self.stripHeights.append(report.height * _val) else: self.stripWidths.append(report.width * _val) self.stripHeights.append(report.height * _val) self.stripHeights.append(report.height * _val) else: if self.stripNumber < (self.numStrips - 1): self.stripWidths.append(report.width * _val) self.stripHeights.append((report.height + self.stripSpacing) * _val) else: self.stripWidths.append(report.width * _val) self.stripHeights.append(report.height * _val) self.stripWidths.append(report.width * _val) def _addTableToStory(self): _style = TableStyle([('ALIGN', (0, 0), (-1, -1), 'LEFT'), ('VALIGN', (0, 0), (-1, -1), 'TOP'), ('TOPPADDING', (0, 0), (-1, -1), 0), ('BOTTOMPADDING', (0, 0), (-1, -1), 0), ('LEFTPADDING', (0, 0), (-1, -1), 0), ('RIGHTPADDING', (0, 0), (-1, -1), 0), ('LEADING', (0, 0), (-1, -1), 0), ] ) _minSpace = 2.0 * cm # self.docHeight / 2.0 if self.params[GLSTRIPDIRECTION] == 'Y': # arrange as a row table = (self.stripReports,) _spacing = min(_minSpace, max(0.0, (self.docHeight - self.stripHeights[0]) / 2.0)) _table = Table(table, colWidths=self.stripWidths, rowHeights=self.stripHeights[0], ) else: # arrange as a column table = tuple((rep,) for rep in self.stripReports) _spacing = min(_minSpace, max(0.0, (self.docHeight - sum(self.stripHeights)) / 2.0)) _table = Table(table, rowHeights=self.stripHeights, colWidths=self.stripWidths[0], ) self._report.doc.topMargin = math.floor(_spacing) # NOTE:ED - same for left/bottom margin? _table.setStyle(_style) self._report.addItemToStory(_table)
[docs] def writePNGFile(self): """ Output a PNG file for the GL widget. """ with catchExceptions(errorStringTemplate='Error writing PNG file: "%s"', printTraceBack=False): dpi = self.params[GLEXPORTDPI] # NOTE:ED - need to look at this as only saves the last created drawing, how to concatenate? self._mainPlot.scale(dpi / 72, dpi / 72) renderPM.drawToFile(self._mainPlot, self.filename, fmt='PNG', dpi=dpi, showBoundary=False)
[docs] def writeSVGFile(self): """ Output an SVG file for the GL widget. """ with catchExceptions(errorStringTemplate='Error writing SVG file: "%s"', printTraceBack=False): renderSVG.drawToFile(self._mainPlot, self.filename, showBoundary=False)
[docs] def writePDFFile(self): """ Output a PDF file for the GL widget. """ with catchExceptions(errorStringTemplate='Error writing PDF document: "%s"', printTraceBack=False): self._report.writeDocument()
[docs] def writePSFile(self): """ Output a PS file for the GL widget. """ with catchExceptions(errorStringTemplate='Error writing PS file: "%s"', printTraceBack=False): renderPS.drawToFile(self._mainPlot, self.filename, showBoundary=False)
def _appendVertexLineGroup(self, indArray, colourGroups, plotDim, name, mat=None, includeLastVertex=False, lineWidth=0.5): _x = plotDim[PLOTLEFT] _y = plotDim[PLOTBOTTOM] _width = plotDim[PLOTWIDTH] _height = plotDim[PLOTHEIGHT] for vv in range(0, len(indArray.vertices) - 2, 2): if mat is not None: vectStart = [indArray.vertices[vv], indArray.vertices[vv + 1], 0.0, 1.0] vectStart = mat.dot(vectStart) vectEnd = [indArray.vertices[vv + 2], indArray.vertices[vv + 3], 0.0, 1.0] vectEnd = mat.dot(vectEnd) newLine = [vectStart[0], vectStart[1], vectEnd[0], vectEnd[1]] else: newLine = list(indArray.vertices[vv:vv + 4]) colour = colors.Color(*indArray.colors[vv * 2:vv * 2 + 3], alpha=alphaClip(indArray.colors[vv * 2 + 3])) colourPath = '%s%s%s%s%s' % (name, colour.red, colour.green, colour.blue, colour.alpha) if colourPath not in colourGroups: cc = colourGroups[colourPath] = {} if (indArray.fillMode or GL.GL_LINE) == GL.GL_LINE: cc[PDFLINES] = [] cc[PDFSTROKEWIDTH] = lineWidth cc[PDFSTROKECOLOR] = colour cc[PDFSTROKELINECAP] = 1 else: # assume that it is GL.GL_FILL cc[PDFLINES] = [] cc[PDFFILLCOLOR] = colour cc[PDFSTROKE] = None cc[PDFSTROKECOLOR] = None newLine = self.lineVisible(self._parent, newLine, x=_x, y=_y, width=_width, height=_height) if newLine: colourGroups[colourPath][PDFLINES].append(newLine) def _colourID(self, name, colour): return 'spectrumView%s%s%s%s%s' % (name, colour.red, colour.green, colour.blue, colour.alpha) def _appendIndexLineGroup(self, indArray, colourGroups, plotDim, name, mat=None, fillMode=None, splitGroups=False, setColour=None, lineWidth=0.5, ratioLine=False, alias=None): if indArray.drawMode == GL.GL_TRIANGLES: indexLen = 3 elif indArray.drawMode == GL.GL_QUADS: indexLen = 4 else: indexLen = 2 # override so that each element is a new group if splitGroups: colourGroups = OrderedDict() _x = plotDim[PLOTLEFT] _y = plotDim[PLOTBOTTOM] _width = plotDim[PLOTWIDTH] _height = plotDim[PLOTHEIGHT] for ii in range(0, len(indArray.indices), indexLen): ii0 = [int(ind) for ind in indArray.indices[ii:ii + indexLen]] newLine = [] for vv in ii0: if mat is not None: _vec = [indArray.vertices[vv * 2], indArray.vertices[vv * 2 + 1], 0.0, 1.0] _vec = mat.dot(_vec) if ratioLine: newLine.extend(self._scaleRatioToWindow(_vec[0:2])) else: newLine.extend(_vec[0:2]) else: if ratioLine: # convert ratio to axis coordinates # newLine.extend([self._scaleRatioToWindow(indArray.vertices[vv * 2], (self._axisR - self._axisL), self._axisL), # self._scaleRatioToWindow(indArray.vertices[vv * 2 + 1], (self._axisT - self._axisB), self._axisB)]) newLine.extend(self._scaleRatioToWindow(indArray.vertices[vv * 2:vv * 2 + 2])) else: newLine.extend([indArray.vertices[vv * 2], indArray.vertices[vv * 2 + 1]]) _alias = 1.0 if alias is not None and indArray.attribs is not None and indArray.attribs.size != 0: if abs(indArray.attribs[ii0[0]] - alias) > 0.5: _alias = self.params[GLALIASSHADE] / 100.0 colour = (setColour or colors.Color(*indArray.colors[ii0[0] * 4:ii0[0] * 4 + 3], alpha=_alias * alphaClip(indArray.colors[ii0[0] * 4 + 3]))) colourPath = self._colourID(name, colour) # 'spectrumView%s%s%s%s%s' % (name, # colour.red, colour.green, colour.blue, colour.alpha) # # override so that each element is a new group # if splitGroups: # colourGroups = OrderedDict() if colourPath not in colourGroups: cc = colourGroups[colourPath] = {} if (fillMode or indArray.fillMode or GL.GL_LINE) == GL.GL_LINE: cc[PDFLINES] = [] cc[PDFSTROKEWIDTH] = lineWidth cc[PDFSTROKECOLOR] = colour cc[PDFSTROKELINECAP] = 1 else: # assume that it is GL.GL_FILL cc[PDFLINES] = [] cc[PDFFILLCOLOR] = colour cc[PDFSTROKE] = None cc[PDFSTROKECOLOR] = None newLine = self.lineVisible(self._parent, newLine, x=_x, y=_y, width=_width, height=_height) if newLine: colourGroups[colourPath][PDFLINES].append(newLine) # override so that each element is a new group if splitGroups: self._appendGroup(drawing=self._mainPlot, colourGroups=colourGroups, name=name) if setColour is not None: return self._colourID(name, setColour) def _appendIndexLineGroupFill(self, indArray=None, listView=None, colourGroups=None, plotDim=None, name=None, mat=None, fillMode=None, splitGroups=False, lineWidth=0.5): for spectrumView in self._ordering: if spectrumView.isDeleted: continue specSettings = self._parent._spectrumSettings[spectrumView] # get the transformation matrix from the spectrumView mat = np.transpose(self._parent._spectrumSettings[spectrumView][SPECTRUM_MATRIX].reshape((4, 4))) attribList = getattr(spectrumView, listView + 'Views') validListViews = [pp for pp in attribList if pp.isDisplayed and spectrumView.isDisplayed and getattr(pp, listView).pid in self.params[GLSELECTEDPIDS]] for thisListView in validListViews: if thisListView in indArray.keys(): thisSpec = indArray[thisListView] self._appendIndexLineGroup(indArray=thisSpec, colourGroups=colourGroups, plotDim=plotDim, name='spectrumView%s%s' % (name, spectrumView.pid), mat=mat, fillMode=fillMode, splitGroups=splitGroups, lineWidth=lineWidth) def _appendGroup(self, drawing: Drawing = None, colourGroups: dict = None, name: str = None): """ Append a group of polylines to the current drawing object :param drawing - drawing to append groups to :param colourGroups - OrderedDict of polylines :param name - name for the group """ gr = Group() for colourItem in colourGroups.values(): # pl = PolyLine(ll[PDFLINES], strokeWidth=ll[PDFSTROKEWIDTH], strokeColor=ll[PDFSTROKECOLOR], strokeLineCap=ll[PDFSTROKELINECAP]) wanted_keys = [PDFSTROKEWIDTH, PDFSTROKECOLOR, PDFSTROKELINECAP, PDFFILLCOLOR, PDFFILL, PDFFILLMODE, PDFSTROKE, PDFSTROKEDASHARRAY] newColour = dict((k, colourItem[k]) for k in wanted_keys if k in colourItem) pl = Path(**newColour) # strokeWidth=colourItem[PDFSTROKEWIDTH], strokeColor=colourItem[PDFSTROKECOLOR], strokeLineCap=colourItem[PDFSTROKELINECAP]) for ll in colourItem[PDFLINES]: if len(ll) == 4: pl.moveTo(ll[0], ll[1]) pl.lineTo(ll[2], ll[3]) elif len(ll) > 4: pl.moveTo(ll[0], ll[1]) for vv in range(2, len(ll), 2): try: pl.lineTo(ll[vv], ll[vv + 1]) except Exception as es: pass if PDFCLOSEPATH not in colourItem or (PDFCLOSEPATH in colourItem and colourItem[PDFCLOSEPATH] == True): pl.closePath() gr.add(pl) drawing.add(gr, name=name)
[docs] def between(self, val, l, r): return (l - val) * (r - val) <= 0
[docs] def pointVisible(self, _parent, lineList, x=0.0, y=0.0, width=0.0, height=0.0): """return true if the line has visible endpoints """ axisL, axisR, axisT, axisB = self._axisL, self._axisR, self._axisT, self._axisB if (self.between(lineList[0], axisL, axisR) and (self.between(lineList[1], axisT, axisB))): lineList[0] = x + width * (lineList[0] - axisL) / (axisR - axisL) lineList[1] = y + height * (lineList[1] - axisB) / (axisT - axisB) return True
[docs] def lineVisible(self, _parent, lineList, x=0.0, y=0.0, width=0.0, height=0.0, checkIntegral=False): """return the list of visible lines """ # make into a list of tuples newList = [] newLine = [[lineList[ll], lineList[ll + 1]] for ll in range(0, len(lineList), 2)] if len(newLine) > 2: newList = self.clipPoly(_parent, newLine) elif len(newLine) == 2: newList = self.clipLine(_parent, newLine) try: if newList: axisL, axisR, axisT, axisB = self._axisL, self._axisR, self._axisT, self._axisB newList = [pp for outPoint in newList for pp in (x + width * (outPoint[0] - axisL) / (axisR - axisL), y + height * (outPoint[1] - axisB) / (axisT - axisB))] except Exception as es: pass return newList
[docs] def clipPoly(self, _parent, subjectPolygon): """Apply Sutherland-Hodgman algorithm for clipping polygons """ axisL, axisR, axisT, axisB = self._axisL, self._axisR, self._axisT, self._axisB if self._parent.INVERTXAXIS != self._parent.INVERTYAXIS: clipPolygon = [[axisL, axisB], [axisL, axisT], [axisR, axisT], [axisR, axisB]] else: clipPolygon = [[axisL, axisB], [axisR, axisB], [axisR, axisT], [axisL, axisT]] def inside(p): return (cp2[0] - cp1[0]) * (p[1] - cp1[1]) > (cp2[1] - cp1[1]) * (p[0] - cp1[0]) def get_intersect(): """Returns the point of intersection of the lines passing through a2,a1 and b2,b1. """ pp = np.vstack([s, e, cp1, cp2]) # s for stacked h = np.hstack((pp, np.ones((4, 1)))) # h for homogeneous l1 = np.cross(h[0], h[1]) # get first line l2 = np.cross(h[2], h[3]) # get second line x, y, z = np.cross(l1, l2) # point of intersection if z == 0: # lines are parallel return (float('inf'), float('inf')) return (x / z, y / z) outputList = subjectPolygon cLen = len(clipPolygon) cp1 = clipPolygon[cLen - 1] for clipVertex in clipPolygon: cp2 = clipVertex inputList = outputList outputList = [] if not inputList: break ilLen = len(inputList) s = inputList[ilLen - 1] for e in inputList: if inside(e): if not inside(s): outputList.append(get_intersect()) outputList.append(e) elif inside(s): outputList.append(get_intersect()) s = e cp1 = cp2 return outputList
[docs] def clipLine(self, _parent, subjectPolygon): """Apply Sutherland-Hodgman algorithm for clipping polygons """ axisL, axisR, axisT, axisB = self._axisL, self._axisR, self._axisT, self._axisB if self._parent.INVERTXAXIS != self._parent.INVERTYAXIS: clipPolygon = [[axisL, axisB], [axisL, axisT], [axisR, axisT], [axisR, axisB]] else: clipPolygon = [[axisL, axisB], [axisR, axisB], [axisR, axisT], [axisL, axisT]] def inside(p): return (cp2[0] - cp1[0]) * (p[1] - cp1[1]) > (cp2[1] - cp1[1]) * (p[0] - cp1[0]) def get_intersect(): """Returns the point of intersection of the lines passing through a2,a1 and b2,b1. """ pp = np.vstack([s, e, cp1, cp2]) # s for stacked h = np.hstack((pp, np.ones((4, 1)))) # h for homogeneous l1 = np.cross(h[0], h[1]) # get first line l2 = np.cross(h[2], h[3]) # get second line x, y, z = np.cross(l1, l2) # point of intersection if z == 0: # lines are parallel return (float('inf'), float('inf')) return (x / z, y / z) outputList = subjectPolygon cLen = len(clipPolygon) cp1 = clipPolygon[cLen - 1] for clipVertex in clipPolygon: cp2 = clipVertex inputList = outputList outputList = [] if not inputList: break ilLen = len(inputList) s = inputList[ilLen - 1] for e in inputList: if inside(e): if not inside(s): outputList.append(get_intersect()) outputList.append(e) elif inside(s): outputList.append(get_intersect()) s = e cp1 = cp2 return outputList
[docs] def lineFit(self, _parent, lineList, x=0.0, y=0.0, width=0.0, height=0.0, checkIntegral=False): axisL, axisR, axisT, axisB = self._axisL, self._axisR, self._axisT, self._axisB for pp in range(0, len(lineList), 2): if (self.between(lineList[pp], axisL, axisR) and (self.between(lineList[pp + 1], axisT, axisB) or checkIntegral)): fit = True break else: fit = False for pp in range(0, len(lineList), 2): lineList[pp] = x + width * (lineList[pp] - axisL) / (axisR - axisL) lineList[pp + 1] = y + height * (lineList[pp + 1] - axisB) / (axisT - axisB) return fit
[docs]class Clipped_Flowable(Flowable): def __init__(self, width=0.0, height=0.0, mainPlot=None, mainDim=None): Flowable.__init__(self) self.mainPlot = mainPlot self.mainDim = mainDim self.width = width self.height = height
[docs] def draw(self): if self.mainPlot: self.canv.saveState() # make a clippath for the mainPlot pl = self.canv.beginPath() pl.moveTo(self.mainDim[PLOTLEFT], self.mainDim[PLOTBOTTOM]) pl.lineTo(self.mainDim[PLOTLEFT], self.mainDim[PLOTHEIGHT] + self.mainDim[PLOTBOTTOM]) pl.lineTo(self.mainDim[PLOTLEFT] + self.mainDim[PLOTWIDTH], self.mainDim[PLOTHEIGHT] + self.mainDim[PLOTBOTTOM]) pl.lineTo(self.mainDim[PLOTLEFT] + self.mainDim[PLOTWIDTH], self.mainDim[PLOTBOTTOM]) pl.close() self.canv.clipPath(pl, fill=0, stroke=0) # draw the drawing into the canvas self.mainPlot.drawOn(self.canv, self.mainDim[PLOTLEFT], self.mainDim[PLOTBOTTOM]) # restore preclipping state self.canv.restoreState()
if __name__ == '__main__': buf = io.BytesIO() # Setup the document with paper size and margins doc = SimpleDocTemplate( buf, rightMargin=2 * cm, leftMargin=2 * cm, topMargin=2 * cm, bottomMargin=2 * cm, pagesize=A4, ) # Styling paragraphs styles = getSampleStyleSheet() # Write things on the document paragraphs = [] paragraphs.append( Paragraph('This is a paragraph testing CCPN pdf generation', styles['Normal'])) paragraphs.append( Paragraph('This is another paragraph', styles['Normal'])) dpi = 72 mmwidth = 150 mmheight = 150 pixWidth = int(mmwidth * mm) pixHeight = int(mmheight * mm) # the width doesn't mean anything, but the height defines how much space is added to the story # co-ordinates are origin bottom-left d = Drawing(pixWidth, pixHeight, ) d.add(Rect(0.0, 0.0, pixWidth, pixHeight, fillColor=colors.yellow, stroke=0, fill=0)) d.add(String(150.0, 100.0, 'Hello World', fontSize=18, fillColor=colors.red)) d.add(String(180.0, 86.0, 'Special characters \ \xc2\xa2\xc2\xa9\xc2\xae\xc2\xa3\xce\xb1\xce\xb2', fillColor=colors.red)) pl = PolyLine([120, 110, 130, 150], strokeWidth=2, strokeColor=colors.red) d.add(pl) # pl = definePath(isClipPath=1) # pl.moveTo(30.0, 30.0) # pl.lineTo(30.0, pixHeight/2) # pl.lineTo(pixWidth/2, pixHeight/2) # pl.lineTo(pixWidth/2, 30.0) # pl.closePath() # d.add(pl) gr = Group() gr.add(Rect(0.0, 0.0, 20.0, 20.0, fillColor=colors.yellow)) gr.add(Rect(30.0, 30.0, 20.0, 20.0, fillColor=colors.blue)) d.add(gr) # d.add(Rect(0.0, 0.0, 20.0, 20.0, fillColor=colors.yellow)) # d.add(Rect(30.0, 30.0, 20.0, 20.0, fillColor=colors.blue)) # paragraphs.append(d) fred = Clipped_Flowable() paragraphs.append(fred) doc.pageCompression = None # this generates the buffer to write to the file doc.build(paragraphs) c = canvas.Canvas(filename='/Users/ejb66/Desktop/testCCPNpdf3.pdf', pagesize=A4) # make a clippath pl = c.beginPath() pl.moveTo(0, 0) pl.lineTo(0, 100) pl.lineTo(100, 100) pl.lineTo(100, 0) pl.close() c.clipPath(pl, fill=0, stroke=0) # draw the drawing to the canvas after clipping defined d.drawOn(c, 0, 0) c.save() # Write the PDF to a file with open('/Users/ejb66/Desktop/testCCPNpdf.pdf', 'wb') as fd: fd.write(buf.getvalue()) c = canvas.Canvas(filename='/Users/ejb66/Desktop/testCCPNpdf2.pdf', pagesize=A4) # define a clipping path pageWidth = A4[0] pageHeight = A4[1] p = c.beginPath() p.moveTo(0, 0) p.lineTo(0, 200) p.lineTo(200, 200) p.lineTo(200, 0) p.close() c.clipPath(p, fill=0, stroke=0) red50transparent = colors.Color(100, 0, 0, alpha=alphaClip(0.5)) c.setFillColor(colors.black) c.setFont('Helvetica', 10) c.drawString(25, 180, 'solid') c.setFillColor(colors.blue) c.rect(25, 25, 100, 100, fill=True, stroke=False) c.setFillColor(colors.red) c.rect(100, 75, 100, 100, fill=True, stroke=False) c.setFillColor(colors.black) c.drawString(225, 180, 'transparent') c.setFillColor(colors.blue) c.rect(225, 25, 100, 100, fill=True, stroke=False) c.setFillColor(red50transparent) c.rect(300, 75, 100, 100, fill=True, stroke=False) c.rect(0, 0, 100, 100, fill=True, stroke=False) # this is much better as it remembers the transparency and object grouping h = inch / 3.0 k = inch / 2.0 c.setStrokeColorRGB(0.2, 0.3, 0.5) c.setFillColorRGB(0.8, 0.6, 0.2) c.setLineWidth(4) p = c.beginPath() for i in (1, 2, 3, 4): for j in (1, 2): xc, yc = inch * i, inch * j p.moveTo(xc, yc) p.arcTo(xc - h, yc - k, xc + h, yc + k, startAng=0, extent=60 * i) # close only the first one, not the second one if j == 1: p.close() c.drawPath(p, fill=1, stroke=1) c.save()