Source code for ccpn.ui.gui.popups.ExportStripToFile

"""
Module Documentation here
"""
#=========================================================================================
# Licence, Reference and Credits
#=========================================================================================
__copyright__ = "Copyright (C) CCPN project (http://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 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: 2022-01-17 16:13:53 +0000 (Mon, January 17, 2022) $"
__version__ = "$Revision: 3.0.4 $"
#=========================================================================================
# Created
#=========================================================================================
__author__ = "$Author: Ed Brooksbank $"
__date__ = "$Date: 2017-07-06 15:51:11 +0000 (Thu, July 06, 2017) $"
#=========================================================================================
# Start of code
#=========================================================================================

import os
import numpy as np
from collections import OrderedDict as OD
from PyQt5 import QtWidgets, QtCore, QtGui
from PyQt5.QtCore import QStandardPaths
from PyQt5.QtGui import QFontDatabase
from dataclasses import dataclass
from functools import partial
from typing import Optional

from ccpn.core.lib.ContextManagers import catchExceptions, queueStateChange
from ccpn.ui.gui.guiSettings import getColours, DIVIDER, BORDERFOCUS
from ccpn.ui.gui.popups.ExportDialog import ExportDialogABC
from ccpn.ui.gui.popups.Dialog import handleDialogApply, _verifyPopupApply
# from ccpn.ui.gui.widgets.Spacer import Spacer
from ccpn.ui.gui.widgets.Label import Label
from ccpn.ui.gui.widgets.HLine import HLine
from ccpn.ui.gui.widgets.Menu import Menu
from ccpn.ui.gui.widgets.ProjectTreeCheckBoxes import PrintTreeCheckBoxes
from ccpn.ui.gui.widgets.RadioButtons import RadioButtons
from ccpn.ui.gui.widgets.ButtonList import ButtonList
from ccpn.ui.gui.widgets.Button import Button
from ccpn.ui.gui.widgets.PulldownList import PulldownList
from ccpn.ui.gui.widgets.ColourDialog import ColourDialog
from ccpn.ui.gui.widgets.ListWidget import ListWidget
from ccpn.ui.gui.widgets.CompoundWidgets import PulldownListCompoundWidget, CheckBoxCompoundWidget, DoubleSpinBoxCompoundWidget
from ccpn.ui.gui.widgets.Frame import Frame
from ccpn.ui.gui.widgets.DoubleSpinbox import DoubleSpinbox, ScientificDoubleSpinBox
from ccpn.ui.gui.widgets.MessageDialog import showYesNoWarning, showWarning
from ccpn.ui.gui.widgets.HighlightBox import HighlightBox
from ccpn.ui.gui.widgets.Font import DEFAULTFONTNAME, DEFAULTFONTSIZE, DEFAULTFONTREGULAR, getFontHeight
# from ccpn.ui.gui.widgets.MessageDialog import progressManager
from ccpn.ui.gui.lib.OpenGL.CcpnOpenGLDefs import GLFILENAME, GLGRIDLINES, \
    GLINTEGRALLABELS, GLINTEGRALSYMBOLS, GLMULTIPLETLABELS, \
    GLMULTIPLETSYMBOLS, GLPEAKLABELS, GLPEAKSYMBOLS, GLPRINTTYPE, GLPAGETYPE, GLPAGESIZE, GLSELECTEDPIDS, \
    GLSPECTRUMBORDERS, GLSPECTRUMCONTOURS, \
    GLSPECTRUMDISPLAY, GLSTRIP, \
    GLWIDGET, GLBACKGROUND, GLBASETHICKNESS, GLSYMBOLTHICKNESS, GLFOREGROUND, \
    GLCONTOURTHICKNESS, GLSHOWSPECTRAONPHASE, \
    GLSTRIPDIRECTION, GLSTRIPPADDING, GLEXPORTDPI, \
    GLFULLLIST, GLEXTENDEDLIST, GLDIAGONALLINE, GLCURSORS, GLDIAGONALSIDEBANDS, \
    GLALIASENABLED, GLALIASSHADE, GLALIASLABELSENABLED, GLSTRIPREGIONS, \
    GLSCALINGMODE, GLSCALINGOPTIONS, GLSCALINGPERCENT, GLSCALINGBYUNITS, \
    GLPRINTFONT, GLUSEPRINTFONT, GLSCALINGAXIS, GLPEAKLABELSENABLED, GLMULTIPLETLABELSENABLED
from ccpn.ui.gui.lib.ChangeStateHandler import changeState
from ccpn.util.Colour import spectrumColours, addNewColour, fillColourPulldown, addNewColourString, hexToRgbRatio, colourNameNoSpace
from ccpn.util.Constants import SCALING_MODES, POSINFINITY
from ccpn.util.Logging import getLogger


# from ccpn.ui.gui.lib.OpenGL.CcpnOpenGLDefs import GLAXISLABELS, GLAXISMARKS, \
#     GLMARKLABELS, GLMARKLINES, GLREGIONS, GLOTHERLINES, GLSPECTRUMLABELS, GLSTRIPLABELLING, GLTRACES, \
#     GLPLOTBORDER, GLAXISLINES, GLAXISTITLES, GLAXISUNITS, GLAXISMARKSINSIDE


EXPORTEXT = 'EXT'
EXPORTFILTER = 'FILTER'
EXPORTPDF = 'PDF'
EXPORTPDFEXTENSION = '.pdf'
EXPORTPDFFILTER = 'pdf files (*.pdf)'
EXPORTSVG = 'SVG'
EXPORTSVGEXTENSION = '.svg'
EXPORTSVGFILTER = 'svg files (*.svg)'
EXPORTPNG = 'PNG'
EXPORTPNGEXTENSION = '.png'
EXPORTPNGFILTER = 'png files (*.png)'
EXPORTPS = 'PS'
EXPORTPSEXTENSION = '.ps'
EXPORTPSFILTER = 'ps files (*.ps)'
EXPORTTYPES = OD(((EXPORTPDF, {EXPORTEXT   : EXPORTPDFEXTENSION,
                               EXPORTFILTER: EXPORTPDFFILTER}),
                  (EXPORTSVG, {EXPORTEXT   : EXPORTSVGEXTENSION,
                               EXPORTFILTER: EXPORTSVGFILTER}),
                  (EXPORTPNG, {EXPORTEXT   : EXPORTPNGEXTENSION,
                               EXPORTFILTER: EXPORTPNGFILTER}),
                  (EXPORTPS, {EXPORTEXT   : EXPORTPSEXTENSION,
                              EXPORTFILTER: EXPORTPSFILTER}),
                  ))
EXPORTFILTERS = EXPORTPDFFILTER
PAGEPORTRAIT = 'portrait'
PAGELANDSCAPE = 'landscape'
PAGETYPES = [PAGEPORTRAIT, PAGELANDSCAPE]

STRIPAXIS = 'Axis'
STRIPMIN = 'Min'
STRIPMAX = 'Max'
STRIPCENTRE = 'Centre'
STRIPWIDTH = 'Width'
STRIPAXISINVERTED = 'AxisInverted'
STRIPBUTTONS = [STRIPAXIS, STRIPMIN, STRIPMAX, STRIPCENTRE, STRIPWIDTH]
STRIPAXES = ['X', 'Y']
DEFAULT_FONT = 'Default Font'

PAGESIZEA0 = 'A0'
PAGESIZEA1 = 'A1'
PAGESIZEA2 = 'A2'
PAGESIZEA3 = 'A3'
PAGESIZEA4 = 'A4'
PAGESIZEA5 = 'A5'
PAGESIZEA6 = 'A6'
PAGESIZELETTER = 'letter'
PAGESIZES = [PAGESIZEA0, PAGESIZEA1, PAGESIZEA2, PAGESIZEA3, PAGESIZEA4, PAGESIZEA5, PAGESIZEA6, PAGESIZELETTER]
PAGESIZE = 'pageSize'
OPTIONSPECTRA = 'Spectra'
OPTIONPEAKLISTS = 'Peak Lists'
OPTIONPRINT = 'Print Options'
OPTIONLIST = (OPTIONSPECTRA, OPTIONPEAKLISTS, OPTIONPRINT)


@dataclass
class _StripData:
    """Simple class to store strip widget state
    """
    strip = None
    useRegion = False
    minMaxMode = 0
    axes = None
    _widget = None

    _USEREGION = 'useRegion'
    _MINMAXMODE = 'minMaxMode'
    _AXES = 'axes'
    _DECIMALS = 2

    def _initialise(self):
        """Initialise the new dataclass from the strip
        """
        self.axes = [{STRIPMIN         : 0.0,
                      STRIPMAX         : 0.0,
                      STRIPCENTRE      : 0.0,
                      STRIPWIDTH       : 0.0,
                      STRIPAXISINVERTED: False,  # not sure if this is needed
                      },
                     {STRIPMIN         : 0.0,
                      STRIPMAX         : 0.0,
                      STRIPCENTRE      : 0.0,
                      STRIPWIDTH       : 0.0,
                      STRIPAXISINVERTED: False,
                      }]

        if self.strip:
            for ii in range(len(STRIPAXES)):
                dd = self.axes[ii]
                region = self.strip.getAxisRegion(ii)
                dd[STRIPMIN], dd[STRIPMAX] = min(region), max(region)
                dd[STRIPAXISINVERTED] = True if (region[0] > region[1]) else False  # probably not needed - use setAxisRegion
                dd[STRIPCENTRE] = self.strip.getAxisPosition(ii)
                dd[STRIPWIDTH] = self.strip.getAxisWidth(ii)

    def __repr__(self):
        """Output the string representation
        """
        return f'<{self.strip}: {self.useRegion}, {self.minMaxMode}, {self.axes}>'

    def toDict(self):
        """Output the contents as a dict
        """

        def _func(val):
            return round(val, self._DECIMALS)

        dd = {self._USEREGION : self.useRegion,
              self._MINMAXMODE: self.minMaxMode,
              self._AXES      : [{STRIPMIN         : _func(axis[STRIPMIN]),
                                  STRIPMAX         : _func(axis[STRIPMAX]),
                                  STRIPCENTRE      : _func(axis[STRIPCENTRE]),
                                  STRIPWIDTH       : _func(axis[STRIPWIDTH]),
                                  STRIPAXISINVERTED: axis[STRIPAXISINVERTED],
                                  } for axis in self.axes],
              }
        return dd

    def fromDict(self, value):
        """update contents from a dict
        """
        self.useRegion = value.get(self._USEREGION)
        self.minMaxMode = value.get(self._MINMAXMODE)
        self.axes = _axes = value.get(self._AXES)

        def _func(val):
            return float(val)

        if _axes:
            self.axes = [{STRIPMIN         : _func(axis[STRIPMIN]),
                          STRIPMAX         : _func(axis[STRIPMAX]),
                          STRIPCENTRE      : _func(axis[STRIPCENTRE]),
                          STRIPWIDTH       : _func(axis[STRIPWIDTH]),
                          STRIPAXISINVERTED: axis[STRIPAXISINVERTED],
                          } for axis in _axes]


class _StripListWidget(ListWidget):
    """ListWidget with a new right-mouse menu
    """

    def __init__(self, *args, parentPopup=None, parentCallbacks=None, **kwds):
        if not (parentCallbacks and len(parentCallbacks) == 3):
            raise RuntimeError('bad parentCallbacks')

        super().__init__(*args, **kwds)

        # copy has not been used on the first popup
        self._firstCopy = False
        self._parentPopup = parentPopup

        # make a persistent menu
        self._stripListMenu = contextMenu = Menu('', self, isFloatWidget=True)
        self._copyOption = contextMenu.addItem("Copy", callback=parentCallbacks[0])
        self._pasteOption = contextMenu.addItem("Paste to", callback=parentCallbacks[1])
        self._pasteAllOption = contextMenu.addItem("Paste to Selection", callback=parentCallbacks[2])

    def getContextMenu(self):
        # return the axis menu
        return self._stripListMenu

    def mousePressEvent(self, event):
        if self.itemAt(event.pos()) is None:
            self.clearSelection()

        # want to call ListWidget handler not superclass
        super(ListWidget, self).mousePressEvent(event)

        # get item under mouse
        clicked = self.itemAt(event.pos())
        self._clickedStripId = clicked.text() if clicked else None

        # enable/disable options based on the selection
        _selection = self.selectedItems()
        self._selectedStripIds = [val.text() for val in _selection]

        _opt = self._clickedStripId and self._clickedStripId in self._selectedStripIds
        self._copyOption.setEnabled(True if _opt else False)
        self._copyOption.setText(f'Copy {self._clickedStripId if _opt else "-"}')

        _opt = self._firstCopy and self._clickedStripId and self._clickedStripId in self._selectedStripIds
        self._pasteOption.setEnabled(True if _opt else False)
        self._pasteOption.setText(f'Paste to {self._clickedStripId if _opt else "-"}')

        self._pasteAllOption.setEnabled(self._firstCopy and len(_selection) > 1)

        # raise the copy/paste menu
        if event.button() == QtCore.Qt.RightButton:
            if self.contextMenu:
                option = self.raiseContextMenu(event)  # returns the menu action clicked
                if option == self._copyOption:
                    # enable the paste buttons if the copy has been used at least once
                    self._firstCopy = True


[docs]class ExportStripToFilePopup(ExportDialogABC): """ Class to handle printing strips to file """ _SAVESTRIPS = '_strips' _SAVECURRENTSTRIP = '_currentStrip' _SAVECURRENTAXIS = '_currentAxis' storeStateOnReject = True # permanently enables the saveAndClose button EDITMODE = False def __init__(self, parent=None, mainWindow=None, title='Export Strip to File', fileMode='anyFile', acceptMode='export', selectFile=None, fileFilter=EXPORTFILTERS, strips=None, selectedStrip=None, includeSpectrumDisplays=True, **kwds): """ Initialise the widget """ # initialise attributes self.strips = strips self.objects = {} self.includeSpectrumDisplays = includeSpectrumDisplays self.strip = None self.spectrumDisplay = None self.spectrumDisplays = set() self.specToExport = None self._scalingModeIndex = 0 self._useFontSetting = None self._initialiseStripList() # load the available .ttf fonts - load everytime as user may move/add them _, self.familyFonts = self._getFontPaths() super().__init__(parent=parent, mainWindow=mainWindow, title=title, fileMode=fileMode, acceptMode=acceptMode, selectFile=selectFile, fileFilter=fileFilter, **kwds) self.printSettings = self.application.preferences.printSettings if not strips: showWarning(str(self.windowTitle()), 'No strips selected') self.reject() self._selectedStrip = selectedStrip or (strips[0] if strips else None) self.fullList = GLFULLLIST self._copyRangeValue = None
[docs] def exec_(self) -> Optional[dict]: """Disable strip updating while the popup is visible """ for strip in self.strips: strip._CcpnGLWidget._disableCursorUpdate = True result = super().exec_() for strip in self.strips: strip._CcpnGLWidget._disableCursorUpdate = False return result
[docs] def initialise(self, userFrame): """Create the widgets for the userFrame """ row = 0 self.objectPulldown = PulldownListCompoundWidget(userFrame, grid=(row, 0), gridSpan=(1, 3), vAlign='top', hAlign='left', orientation='left', labelText='Strip/SpectrumDisplay', callback=self._changeObjectPulldownCallback ) # add a spacer to separate from the common save widgets row += 1 HLine(userFrame, grid=(row, 0), gridSpan=(1, 4), colour=getColours()[DIVIDER], height=20) row += 1 topRow = row Label(userFrame, text='Page Size', grid=(row, 0), hAlign='left', vAlign='centre') self.pageSize = PulldownList(userFrame, vAlign='t', grid=(row, 1), callback=self._queuePageSizeCallback) self.pageSize.setData(texts=PAGESIZES) row += 1 Label(userFrame, text='Page orientation', grid=(row, 0), hAlign='left', vAlign='centre') self.pageOrientation = RadioButtons(userFrame, PAGETYPES, grid=(row, 1), direction='h', hAlign='left', spacing=(20, 0), callback=self._queuePageOrientationCallback) row += 1 Label(userFrame, text='Print Type', grid=(row, 0), hAlign='left', vAlign='centre') self.printType = RadioButtons(userFrame, list(EXPORTTYPES.keys()), grid=(row, 1), direction='h', hAlign='left', spacing=(20, 0), callback=self._queuePrintTypeCallback, # callback=self._changePrintType, ) # create a pulldown for the foreground (axes) colour row += 1 foregroundColourFrame = Frame(userFrame, grid=(row, 0), gridSpan=(1, 3), setLayout=True, showBorder=False) Label(foregroundColourFrame, text="Foreground Colour", vAlign='c', hAlign='l', grid=(0, 0)) self.foregroundColourBox = PulldownList(foregroundColourFrame, vAlign='t', grid=(0, 1)) self.foregroundColourButton = Button(foregroundColourFrame, vAlign='t', hAlign='l', grid=(0, 2), hPolicy='fixed', icon='icons/colours') fillColourPulldown(self.foregroundColourBox, allowAuto=False, includeGradients=False) self.foregroundColourBox.currentIndexChanged.connect(self._queueForegroundPulldownCallback) self.foregroundColourButton.clicked.connect(self._queueForegroundButtonCallback) # create a pulldown for the background colour row += 1 backgroundColourFrame = Frame(userFrame, grid=(row, 0), gridSpan=(1, 3), setLayout=True, showBorder=False) Label(backgroundColourFrame, text="Background Colour", vAlign='c', hAlign='l', grid=(0, 0)) self.backgroundColourBox = PulldownList(backgroundColourFrame, vAlign='t', grid=(0, 1)) self.backgroundColourButton = Button(backgroundColourFrame, vAlign='t', hAlign='l', grid=(0, 2), hPolicy='fixed', icon='icons/colours') fillColourPulldown(self.backgroundColourBox, allowAuto=False, includeGradients=False) self.backgroundColourBox.currentIndexChanged.connect(self._queueBackgroundPulldownCallback) self.backgroundColourButton.clicked.connect(self._queueBackgroundButtonCallback) row += 1 self.baseThicknessBox = DoubleSpinBoxCompoundWidget(userFrame, grid=(row, 0), gridSpan=(1, 3), hAlign='left', labelText='Line Thickness', # value=1.0, decimals=2, step=0.05, range=(0.01, 20), callback=self._queueBaseThicknessCallback, ) row += 1 self.stripPaddingBox = DoubleSpinBoxCompoundWidget(userFrame, grid=(row, 0), gridSpan=(1, 3), hAlign='left', labelText='Strip Padding', # value=5, decimals=0, step=1, range=(0, 50), callback=self._queueStripPaddingCallback, ) row += 1 self.exportDpiBox = DoubleSpinBoxCompoundWidget(userFrame, grid=(row, 0), gridSpan=(1, 3), hAlign='left', labelText='Image dpi', # value=300, decimals=0, step=5, range=(36, 2400), callback=self._queueDpiCallback, ) row += 1 HLine(userFrame, grid=(row, 0), gridSpan=(1, 4), colour=getColours()[DIVIDER], height=20) row += 1 self._setupRangeWidget(row, userFrame) row += 1 HLine(userFrame, grid=(row, 0), gridSpan=(1, 4), colour=getColours()[DIVIDER], height=20) row += 1 # widgets for handling screen scaling self._setupScalingWidget(row, userFrame) row += 1 # widgets for handling fonts self._setupFontWidget(row, userFrame) row += 1 self.treeView = PrintTreeCheckBoxes(userFrame, project=None, grid=(row, 0), gridSpan=(1, 4)) self.treeView.itemClicked.connect(self._queueGetPrintOptionCallback) userFrame.layout().setRowStretch(row, 100) userFrame.addSpacer(5, 5, expandX=True, expandY=True, grid=(row, 3))
def _setupRangeWidget(self, row, userFrame): """Set up the widgets for the range frame """ _rangeFrame = Frame(userFrame, setLayout=True, grid=(row, 0), gridSpan=(1, 8), hAlign='left') self._rangeLeft = Frame(_rangeFrame, setLayout=True, grid=(0, 0)) self._rangeRight = Frame(_rangeFrame, setLayout=True, grid=(0, 1), spacing=(0, 4)) _rangeRow = 0 self._useRegion = CheckBoxCompoundWidget( self._rangeRight, grid=(_rangeRow, 0), hAlign='left', gridSpan=(1, 6), orientation='right', labelText='Use override region for printing', callback=self._useOverrideCallback, tipText='If checked, use the regions selected below\notherwise use the visible print region for the strip' ) _rangeRow += 1 # radio buttons for setting mode _texts = ['Min/Max', 'Centre/Width'] _tipTexts = ['Use minimum/maximum values to define the print region', 'Use centre/width values to define the print region'] self._rangeRadio = RadioButtons(self._rangeRight, texts=_texts, tipTexts=_tipTexts, direction='h', hAlign='l', selectedInd=1, grid=(_rangeRow, 0), gridSpan=(1, 8), callback=self._setModeCallback) _rangeRow += 1 # row of labels self._axisLabels = [] for ii, txt in enumerate(STRIPBUTTONS): _label = Label(self._rangeRight, grid=(_rangeRow, ii), text=txt, hAlign='left') _label.setVisible(False if ii > 0 else True) self._axisLabels.append(_label) _rangeRow += 1 # rows containing spinboxes focusColour = getColours()[BORDERFOCUS] axes = STRIPAXES self._axisSpinboxes = [] for ii, axis in enumerate(axes): _label = Label(self._rangeRight, text=axis, grid=(_rangeRow, 0), hAlign='left') # add a box for the selected row _colourBox = HighlightBox(self._rangeRight, grid=(_rangeRow, 0), gridSpan=(1, 6), colour=focusColour, lineWidth=1, showBorder=False) _colourBox.setFixedHeight(_label.height() + 4) _widgets = [_label] for bt in range(len(STRIPBUTTONS[1:])): _spinbox = DoubleSpinbox(self._rangeRight, grid=(_rangeRow, bt + 1), decimals=2, step=0.1, # hAlign='left', callback=partial(self._setSpinbox, ii, STRIPBUTTONS[bt + 1])) _spinbox.setFixedWidth(140) _spinbox._widgetRow = ii # add a filter to update the selected box around the row _spinbox.installEventFilter(self) _spinbox.setVisible(False) _widgets.append(_spinbox) _widgets.append(_colourBox) _rangeRow += 1 # store the widgets for the callbacks... self._axisSpinboxes.append(_widgets) # buttons for setting the spinboxes from strip _texts = ['Set Print Region', 'Set Min', 'Set Max', 'Set Centre', 'Set Width'] _tipTexts = ['Set all values for the print region from the selected strip.\nValues are set for the selected row', 'Set the minimum value for the print region from the selected strip.\nValue is set for the selected row.\n' 'If the maximum value is too low, the minimum value will be set to the closest allowed value', 'Set the maximum value for the print region from the selected strip.\nValue is set for the selected row.\n' 'If the minimum value is too high, the maximum value will be set to the closest allowed value', 'Set the centre value for the print region from the selected strip.\nValue is set for the selected row', 'Set the width value for the print region from the selected strip.\nValue is set for the selected row'] _callbacks = [self._setStripRegion, self._setStripMin, self._setStripMax, self._setStripCentre, self._setStripWidth] self._setRangeButtons = ButtonList(self._rangeRight, texts=_texts, tipTexts=_tipTexts, grid=(_rangeRow, 0), gridSpan=(1, 8), hAlign='l', callbacks=_callbacks, setMinimumWidth=False, setLastButtonFocus=False, ) for _btn in self._setRangeButtons.buttons[1:]: _btn.setVisible(False) _rangeRow += 1 self._rangeRight.addSpacer(5, 5, grid=(_rangeRow, 6), expandX=True) self._rangeRight.addSpacer(4, 4, grid=(_rangeRow, 5)) _rangeRow += 1 # list to hold the current strips Label(self._rangeLeft, grid=(0, 0), text='Strips', hAlign='left') self._stripLists = _StripListWidget(self._rangeLeft, grid=(1, 0), callback=self._setRangeState, multiSelect=True, acceptDrops=False, copyDrop=False, parentPopup=self, parentCallbacks=(self._copyRangeCallback, self._pasteRangeCallback, self._pasteRangeAllCallback) ) self._rangeLeft.setFixedSize(130, 180) _rangeFrame.getLayout().setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignTop) self._rangeRight.getLayout().setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignTop) self._rangeRight.layout().setColumnStretch(6, 1000) self._rangeRight.getLayout().setSizeConstraint(QtWidgets.QLayout.SetMinimumSize) return _rangeRow def _setupScalingWidget(self, row, userFrame): """Set up the widgets for the scaling frame """ _frame = Frame(userFrame, setLayout=True, grid=(row, 0), gridSpan=(1, 8), hAlign='left') _row = 0 self.scalingMode = PulldownListCompoundWidget(_frame, grid=(_row, 0), hAlign='left', orientation='left', labelText='Scaling', texts=SCALING_MODES, tipText='Set the scaling option for printing', # callback=self._scalingCallback callback=self._queueScalingModeCallback, ) self.scalingPercentage = DoubleSpinbox(_frame, grid=(_row, 1), min=1.0, max=100.0, decimals=0, step=1, callback=self._queueScalingPercentageCallback ) self.scalingUnits = ScientificDoubleSpinBox(_frame, grid=(_row, 2), gridSpan=(1, 2), min=0.0, max=1e10, step=0.1, callback=self._queueScalingUnitsCallback, ) self.scalingAxis = PulldownListCompoundWidget(_frame, grid=(_row, 4), labelText='Scale Axis', texts=STRIPAXES, tipText='Select the axis to apply the scaling to', callback=self._queueScalingAxisCallback, ) self.scalingPercentage.setMinimumCharacters(10) self.scalingUnits.setMinimumCharacters(10) self.scalingUnits.setVisible(False) self.scalingAxis.setVisible(False) def _setupFontWidget(self, row, userFrame): """Set up the widgets for the font frame """ _frame = Frame(userFrame, setLayout=True, grid=(row, 0), gridSpan=(1, 8), hAlign='left') _row = 0 _tip = "If checked, use the selected font and fontSize for printing.\n" \ "If the font selected is 'Default Font' then the default font will be used at the specified fontSize.\n" \ "If unchecked, the default font will be scaled proportional to the strip" self._useFontCheckbox = CheckBoxCompoundWidget(_frame, grid=(_row, 0), hAlign='left', orientation='right', labelText='Use selected font for printing', tipText=_tip, callback=self._queueUseFontCallback, ) # self._fontButton = Button(_frame, text='<No Font Set>', grid=(_row, 1), hAlign='l', callback=self._getFont) # self._fontButton.setEnabled(False) # self._fontButton.setVisible(False) # hide for the minute - only .ttf fonts work so using a pulldown self._fontPulldown = PulldownList(_frame, grid=(_row, 2)) self._fontSpinbox = DoubleSpinbox(_frame, min=1, max=100, decimals=0, step=1, grid=(_row, 3), callback=self._queueFontSizeCallback) self._fontPulldown.setEnabled(False) self._fontSpinbox.setEnabled(False) self._fontPulldown.setCallback(self._queueFontNameCallback) # self._fontPulldown.setCallback(partial(self._setPulldownTextColour, self._fontPulldown)) def _initialiseStripList(self): """Setup the lists containing strips.spectrumDisplays before populating """ self.objects = None self._currentStrip = None self._currentStrips = [] # there may be multiple selected in the stripList self._currentAxis = 0 self._stripDict = {} self._localStripDict = {} if self.strips: self.objects = {strip.id: (strip, strip.pid) for strip in self.strips} for strip in self.strips: # make two copies of the strip ranges _data = self._stripDict[strip.id] = _StripData() _data.strip = strip _data._initialise() _data = self._localStripDict[strip.id] = _StripData() _data.strip = strip _data._initialise() # define the contents for the object pulldown if len(self.strips) > 1: specDisplays = set() if self.includeSpectrumDisplays: # get the list of spectrumDisplays containing the strips for strip in self.strips: if len(strip.spectrumDisplay.strips) > 1: specDisplays.add(strip.spectrumDisplay) # add to the pulldown objects for spec in specDisplays: self.objects['SpectrumDisplay: %s' % spec.id] = (spec, spec.pid) def _setStripRegion(self, *args): try: dd = self._stripDict.get(self._currentStrip) ii = self._currentAxis ddAxis = dd.axes[ii] # set all values for the print region region = dd.strip.getAxisRegion(ii) ddAxis[STRIPMIN], ddAxis[STRIPMAX] = min(region), max(region) ddAxis[STRIPCENTRE] = dd.strip.getAxisPosition(ii) ddAxis[STRIPWIDTH] = dd.strip.getAxisWidth(ii) except Exception as es: getLogger().debug2('Error updating _setStripRegion') else: self._setRangeState(self._currentStrip) self._focusButton(self._currentAxis, STRIPMIN if dd.minMaxMode == 0 else STRIPCENTRE) def _setStripMinValue(self, ddAxis, value): """Set the minimum value Update the row values and set the spinbox constraints""" _value = min(value, ddAxis[STRIPMAX]) ddAxis[STRIPMIN] = _value # update the centre/width _centre = (_value + ddAxis[STRIPMAX]) / 2.0 ddAxis[STRIPCENTRE] = _centre ddAxis[STRIPWIDTH] = abs(ddAxis[STRIPMAX] - _value) # value has not been clipped to STRIPMAX value return _value == value def _setStripMin(self, *args): try: dd = self._stripDict.get(self._currentStrip) ddAxis = dd.axes[self._currentAxis] region = dd.strip.getAxisRegion(self._currentAxis) okay = self._setStripMinValue(ddAxis, min(region)) except Exception as es: getLogger().debug2('Error updating _setStripMin') else: self._setRangeState(self._currentStrip) self._focusButton(self._currentAxis, STRIPMIN) if not okay: # flash a quick warning to show that the value has been clipped to the max value self._axisSpinboxes[self._currentAxis][STRIPBUTTONS.index(STRIPMIN)]._flashError() def _setStripMaxValue(self, ddAxis, value): """Set the maximum value Update the row values and set the spinbox constraints""" _value = max(value, ddAxis[STRIPMIN]) ddAxis[STRIPMAX] = _value # update the centre/width _centre = (ddAxis[STRIPMIN] + _value) / 2.0 ddAxis[STRIPCENTRE] = _centre ddAxis[STRIPWIDTH] = abs(_value - ddAxis[STRIPMIN]) # value has not been clipped to STRIPMIN value return _value == value def _setStripMax(self, *args): try: dd = self._stripDict.get(self._currentStrip) ddAxis = dd.axes[self._currentAxis] region = dd.strip.getAxisRegion(self._currentAxis) okay = self._setStripMaxValue(ddAxis, max(region)) except Exception as es: getLogger().debug2('Error updating _setStripMax') else: self._setRangeState(self._currentStrip) self._focusButton(self._currentAxis, STRIPMAX) if not okay: # flash a quick warning to show that the value has been clipped to the min value self._axisSpinboxes[self._currentAxis][STRIPBUTTONS.index(STRIPMAX)]._flashError() def _setStripCentreValue(self, ddAxis, centre): """Set the centre value Update the row values and set the spinbox constraints""" ddAxis[STRIPCENTRE] = centre # update the min/max diff = abs(ddAxis[STRIPWIDTH] / 2.0) ddAxis[STRIPMIN] = centre - diff ddAxis[STRIPMAX] = centre + diff def _setStripCentre(self, *args): try: dd = self._stripDict.get(self._currentStrip) ddAxis = dd.axes[self._currentAxis] centre = dd.strip.getAxisPosition(self._currentAxis) self._setStripCentreValue(ddAxis, centre) except Exception as es: getLogger().debug2('Error updating _setStripCentre') else: self._setRangeState(self._currentStrip) self._focusButton(self._currentAxis, STRIPCENTRE) def _setStripWidthValue(self, ddAxis, width): """Set the width value Update the row values and set the spinbox constraints""" ddAxis[STRIPWIDTH] = width # update the min/max centre = ddAxis[STRIPCENTRE] ddAxis[STRIPMIN] = centre - abs(width / 2.0) ddAxis[STRIPMAX] = centre + abs(width / 2.0) def _setStripWidth(self, *args): try: dd = self._stripDict.get(self._currentStrip) ddAxis = dd.axes[self._currentAxis] width = dd.strip.getAxisWidth(self._currentAxis) self._setStripWidthValue(ddAxis, width) except Exception as es: getLogger().debug2('Error updating _setStripWidth') else: self._setRangeState(self._currentStrip) self._focusButton(self._currentAxis, STRIPWIDTH) def _setSpinbox(self, row, button, value): """Set the value in the storage dict from the spinbox change """ self._setSpinboxAxis(row) try: _dd = self._stripDict.get(self._currentStrip) if button == STRIPMIN: self._setStripMinValue(_dd.axes[row], value) elif button == STRIPMAX: self._setStripMaxValue(_dd.axes[row], value) elif button == STRIPCENTRE: self._setStripCentreValue(_dd.axes[row], value) else: self._setStripWidthValue(_dd.axes[row], value) except Exception as es: getLogger().debug2('Error updating _setSpinbox') else: self._setRangeState(self._currentStrip) self._focusButton(row, button) def _setSpinboxAxis(self, row): """Change the current selected row of spinboxes""" self._currentAxis = row for ii in range(2): _box = self._axisSpinboxes[ii][-1] if _box.isEnabled(): _box.showBorder = (self._currentAxis == ii)
[docs] def eventFilter(self, obj, event): """Event filter to handle focus change on spinboxes """ if event.type() in [QtCore.QEvent.WindowActivate, QtCore.QEvent.FocusIn]: self._setSpinboxAxis(obj._widgetRow) return False
def _useOverrideCallback(self, value): """User has checked/unchecked useOverride """ try: _dd = self._stripDict.get(self._currentStrip) _dd.useRegion = value except Exception as es: getLogger().debug2('Error updating _useOverrideCallback') else: self._setRangeState(self._currentStrip) def _setModeCallback(self): """User has changed minMax/centreWidth mode """ try: _dd = self._stripDict.get(self._currentStrip) _dd.minMaxMode = self._rangeRadio.getIndex() except Exception as es: getLogger().debug2('Error updating _setModeCallback') else: self._setRangeState(self._currentStrip) def _fontCheckBoxCallback(self, value): """Handle checking/unchecking font checkbox """ self._fontPulldown.setEnabled(self._useFontCheckbox.isChecked()) self._fontSpinbox.setEnabled(self._useFontCheckbox.isChecked()) #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[docs] def populate(self, userframe): """Populate the widgets with project """ with self.blockWidgetSignals(self.mainWidget): self._populate() self._setScalingVisible() self._setUseFontVisible() self._setPulldownTextColour(self._fontPulldown)
def _populate(self): """Populate the widget """ self.printSettings = self.application.preferences.printSettings for strip in self.strips: # copy from the local to the stripDict _value = self._localStripDict[strip.id].toDict() self._stripDict[strip.id].fromDict(_value) # # NOTE:ED - ranges are currently not saved to preferences correctly # _value = self.printSettings.printRanges.get(strip.id) # if _value: # self._stripDict[strip.id].fromDict(_value) # self._localStripDict[strip.id].fromDict(_value) # define the contents for the object pulldown if len(self.strips) > 1: pulldownLabel = 'Select Strip:' if self.includeSpectrumDisplays: # get the list of spectrumDisplays containing the strips for strip in self.strips: if len(strip.spectrumDisplay.strips) > 1: pulldownLabel = 'Select Item:' break else: pulldownLabel = 'Current Strip:' self.objectPulldown.setLabelText(pulldownLabel) if self.objects: self.objectPulldown.pulldownList.setData(sorted([ky for ky in self.objects.keys()])) # set the page types self.pageSize.set(self.printSettings.pageSize) self.pageOrientation.set(self.printSettings.pageOrientation, silent=True) self.printType.set(self.printSettings.printType, silent=True) # populate pulldown from foreground colour spectrumColourKeys = list(spectrumColours.keys()) self.foregroundColour = self.printSettings.foregroundColour self.backgroundColour = self.printSettings.backgroundColour if self.foregroundColour in spectrumColourKeys: self.foregroundColourBox.setCurrentText(spectrumColours[self.foregroundColour]) else: # add new colour to the pulldowns if not defined addNewColourString(self.foregroundColour) fillColourPulldown(self.foregroundColourBox, allowAuto=False, includeGradients=False) fillColourPulldown(self.backgroundColourBox, allowAuto=False, includeGradients=False) self.foregroundColourBox.setCurrentText(spectrumColours[self.foregroundColour]) if self.backgroundColour in spectrumColourKeys: self.backgroundColourBox.setCurrentText(spectrumColours[self.backgroundColour]) else: # add new colour to the pulldowns if not defined addNewColourString(self.backgroundColour) fillColourPulldown(self.foregroundColourBox, allowAuto=False, includeGradients=False) fillColourPulldown(self.backgroundColourBox, allowAuto=False, includeGradients=False) self.backgroundColourBox.setCurrentText(spectrumColours[self.backgroundColour]) self.baseThicknessBox.setValue(self.printSettings.baseThickness) self.stripPaddingBox.setValue(self.printSettings.stripPadding) self.exportDpiBox.setValue(self.printSettings.dpi) # set the pulldown to current strip if selected if self.current and self.current.strip: self.objectPulldown.select(self.current.strip.id) self.strip = self.current.strip else: if self.strips: self.objectPulldown.select(self.strips[0].id) self.strip = self.strips[0] else: self.strip = None self.spectrumDisplay = None # fill the range widgets from the strips self._populateRange() # fill the scaling widgets self._populateScaling() # fill the font widgets self._populateFont() # fill the tree from the current strip self._populateTreeView() # set the default save name exType = self.printType.get() or 'PDF' if exType in EXPORTTYPES: exportExtension = EXPORTTYPES[exType][EXPORTEXT] else: raise ValueError('bad export type') self.setSave(self.objectPulldown.getText() + exportExtension) def _populateRange(self): """Populate the list/spinboxes in range widget """ self._rangeLeft.setVisible(self.spectrumDisplay is not None) if self.strip: self._setRangeState(self.strip.id, _updateQueue=False) self._stripLists.clear() self._stripLists.addItems(list(strip.id for strip in self.strips)) self._stripLists.select(self._currentStrip) def _setRangeState(self, strip, setButton=None, setRow=None, _updateQueue=True): try: stripId = strip.text() except Exception as es: stripId = strip finally: self._rangeRight.setVisible(False) with self.blockWidgetSignals(self._rangeRight): # set the current Id for updating range dict self._currentStrip = stripId # remove constraints so spinboxes can be updated self._setSpinboxConstraints(stripId, state=False) _dd = self._stripDict.get(stripId, None) if _dd: self._useRegion.set(_dd.useRegion) self._rangeRadio.setIndex(_dd.minMaxMode) # change visibility of buttons dependent on minMaxMode for btn in [STRIPMIN, STRIPMAX]: self._axisLabels[STRIPBUTTONS.index(btn)].setVisible(_dd.minMaxMode == 0) for ii in range(len(STRIPAXES)): self._axisSpinboxes[ii][STRIPBUTTONS.index(btn)].setVisible(_dd.minMaxMode == 0) self._setRangeButtons.buttons[STRIPBUTTONS.index(btn)].setVisible(_dd.minMaxMode == 0) for btn in [STRIPCENTRE, STRIPWIDTH]: self._axisLabels[STRIPBUTTONS.index(btn)].setVisible(_dd.minMaxMode == 1) for ii in range(len(STRIPAXES)): self._axisSpinboxes[ii][STRIPBUTTONS.index(btn)].setVisible(_dd.minMaxMode == 1) self._setRangeButtons.buttons[STRIPBUTTONS.index(btn)].setVisible(_dd.minMaxMode == 1) for ii in range(len(STRIPAXES)): axis = _dd.axes[ii] self._axisSpinboxes[ii][STRIPBUTTONS.index(STRIPMIN)].set(axis[STRIPMIN]) self._axisSpinboxes[ii][STRIPBUTTONS.index(STRIPMAX)].set(axis[STRIPMAX]) self._axisSpinboxes[ii][STRIPBUTTONS.index(STRIPCENTRE)].set(axis[STRIPCENTRE]) self._axisSpinboxes[ii][STRIPBUTTONS.index(STRIPWIDTH)].set(axis[STRIPWIDTH]) for bb in self._axisSpinboxes[ii]: bb.setEnabled(_dd.useRegion) self._axisSpinboxes[ii][-1].showBorder = (_dd.useRegion and (self._currentAxis == ii)) # self._rangeRadio.setEnabled(_dd.useRegion) self._setRangeButtons.setEnabled(_dd.useRegion) # re-enable constraints self._setSpinboxConstraints(stripId) self._rangeRight.setVisible(True) self._rangeRight.update() if _updateQueue: self._queuePrintRangesCallback(None) def _populateScaling(self): """Populate the widgets in the scaling frame """ self._scalingModeIndex = SCALING_MODES.index(self.printSettings.scalingMode) self.scalingMode.select(self.printSettings.scalingMode) self.scalingPercentage.set(self.printSettings.scalingPercentage) self.scalingUnits.set(self.printSettings.scalingUnits) self.scalingAxis.select(self.printSettings.scalingAxis) def _populateFont(self): """Populate the widgets in the font frame """ # fontString = self.application.preferences.printSettings.get('font') # if fontString: # self._printFont = fontString # self.setFontText(self._fontButton, fontString) # else: # self._printFont = None # self._fontButton.setText('<No Font Set>') self._fontPulldown.setData(texts=[DEFAULT_FONT, ] + sorted(list(self.familyFonts.keys())), ) # add some colour to show the default option model = self._fontPulldown.model() color = QtGui.QColor('gray') model.item(0).setForeground(color) fontName = self.printSettings.get('fontName') fontSize = self.printSettings.get('fontSize') self._fontPulldown.set(fontName) self._fontSpinbox.set(fontSize) value = self.printSettings.useFont self._useFontSetting = value self._useFontCheckbox.set(value) self._setPulldownTextColour(self._fontPulldown) def _setPulldownTextColour(self, combo, value=None): """Set the colour of the pulldown text """ ind = combo.currentIndex() model = combo.model() item = model.item(ind) if item is not None: # and (ind == 0 or combo.isEnabled()): color = item.foreground().color() # use the palette to change the colour of the selection text - may not match for other themes palette = combo.palette() palette.setColor(QtGui.QPalette.Active, QtGui.QPalette.Text, color) combo.setPalette(palette) def _focusButton(self, row, button): """Set the focus to the selected button """ try: self._axisSpinboxes[row][STRIPBUTTONS.index(button)].setFocus() except Exception as es: getLogger().debug2('Error updating _focusButton') def _setSpinboxConstraints(self, stripId, state=True): """Set the min/max/width constraints for the spinboxes associated with the stripId """ try: _dd = self._stripDict.get(stripId, None) if _dd: for ii in range(len(STRIPAXES)): axis = _dd.axes[ii] # set min.max constraints for buttons # not sure if these need to change as the button values are changed self._axisSpinboxes[ii][STRIPBUTTONS.index(STRIPMIN)].setMaximum(axis[STRIPMAX] if state else POSINFINITY) self._axisSpinboxes[ii][STRIPBUTTONS.index(STRIPMAX)].setMinimum(axis[STRIPMIN] if state else -POSINFINITY) self._axisSpinboxes[ii][STRIPBUTTONS.index(STRIPWIDTH)].setMinimum(0.0) except Exception as es: getLogger().debug2('Error updating _setSpinboxConstraints')
[docs] def storeWidgetState(self): """Store the state of the widgets between popups """ _value = {k: v.toDict() for k, v in self._localStripDict.items()} ExportStripToFilePopup._storedState[self._SAVESTRIPS] = _value ExportStripToFilePopup._storedState[self._SAVECURRENTSTRIP] = self._currentStrip ExportStripToFilePopup._storedState[self._SAVECURRENTAXIS] = self._currentAxis
[docs] def restoreWidgetState(self): """Restore the state of the widgets """ values = ExportStripToFilePopup._storedState.get(self._SAVESTRIPS, {}) for k, v in values.items(): _val = _StripData() _val.fromDict(v) self._localStripDict[k] = _val _val = ExportStripToFilePopup._storedState.get(self._SAVECURRENTSTRIP, None) if _val: self._currentStrip = _val self._currentAxis = ExportStripToFilePopup._storedState.get(self._SAVECURRENTAXIS, 0)
def _changeObjectPulldownCallback(self, value): selected = self.objectPulldown.getText() exType = self.printType.get() if exType in EXPORTTYPES: exportExtension = EXPORTTYPES[exType][EXPORTEXT] else: raise ValueError('bad export type') if 'SpectrumDisplay' in selected: self.spectrumDisplay = self.objects[selected][0] self.strip = self._selectedStrip self.setSave(self.spectrumDisplay.id + exportExtension) else: self.spectrumDisplay = None self.strip = self.objects[selected][0] self.setSave(self.strip.id + exportExtension) self._populateRange() selectedList = self.treeView.getCheckStateItems() self._populateTreeView(selectedList) def _populateTreeView(self, selectList=None): self.treeView.clear() printItems = [] if self.strip: # add Spectra to the treeView if self.strip.spectrumViews: item = QtWidgets.QTreeWidgetItem(self.treeView) item.setText(0, OPTIONSPECTRA) item.setFlags(int(item.flags()) | QtCore.Qt.ItemIsTristate | QtCore.Qt.ItemIsUserCheckable) for specView in self.strip.spectrumViews: child = QtWidgets.QTreeWidgetItem(item) child.setFlags(int(child.flags()) | QtCore.Qt.ItemIsUserCheckable) child.setData(1, 0, specView.spectrum) child.setText(0, specView.spectrum.pid) child.setCheckState(0, QtCore.Qt.Checked if specView.isDisplayed else QtCore.Qt.Checked) # find peak/integral/multiplets attached to the spectrumViews peakLists = [] integralLists = [] multipletLists = [] for specView in self.strip.spectrumViews: validPeakListViews = [pp for pp in specView.peakListViews] validIntegralListViews = [pp for pp in specView.integralListViews] validMultipletListViews = [pp for pp in specView.multipletListViews] peakLists.extend(validPeakListViews) integralLists.extend(validIntegralListViews) multipletLists.extend(validMultipletListViews) printItems = [] if peakLists: item = QtWidgets.QTreeWidgetItem(self.treeView) item.setText(0, OPTIONPEAKLISTS) item.setFlags(int(item.flags()) | QtCore.Qt.ItemIsTristate | QtCore.Qt.ItemIsUserCheckable) for pp in peakLists: child = QtWidgets.QTreeWidgetItem(item) child.setFlags(int(child.flags()) | QtCore.Qt.ItemIsUserCheckable) child.setData(1, 0, pp.peakList) child.setText(0, pp.peakList.pid) child.setCheckState(0, QtCore.Qt.Checked if pp.isDisplayed else QtCore.Qt.Checked) printItems.extend((GLPEAKSYMBOLS, GLPEAKLABELS)) if integralLists: item = QtWidgets.QTreeWidgetItem(self.treeView) item.setText(0, 'Integral Lists') item.setFlags(int(item.flags()) | QtCore.Qt.ItemIsTristate | QtCore.Qt.ItemIsUserCheckable) for pp in integralLists: child = QtWidgets.QTreeWidgetItem(item) child.setFlags(int(child.flags()) | QtCore.Qt.ItemIsUserCheckable) child.setData(1, 0, pp.integralList) child.setText(0, pp.integralList.pid) child.setCheckState(0, QtCore.Qt.Checked if pp.isDisplayed else QtCore.Qt.Checked) printItems.extend((GLINTEGRALSYMBOLS, GLINTEGRALLABELS)) if multipletLists: item = QtWidgets.QTreeWidgetItem(self.treeView) item.setText(0, 'Multiplet Lists') item.setFlags(int(item.flags()) | QtCore.Qt.ItemIsTristate | QtCore.Qt.ItemIsUserCheckable) for pp in multipletLists: child = QtWidgets.QTreeWidgetItem(item) child.setFlags(int(child.flags()) | QtCore.Qt.ItemIsUserCheckable) child.setData(1, 0, pp.multipletList) child.setText(0, pp.multipletList.pid) child.setCheckState(0, QtCore.Qt.Checked if pp.isDisplayed else QtCore.Qt.Checked) printItems.extend((GLMULTIPLETSYMBOLS, GLMULTIPLETLABELS)) # populate the treeview with the currently selected peak/integral/multiplet lists self.treeView._uncheckAll() pidList = [] for specView in self.strip.spectrumViews: validPeakListViews = [pp.peakList.pid for pp in specView.peakListViews if pp.isDisplayed and specView.isDisplayed] validIntegralListViews = [pp.integralList.pid for pp in specView.integralListViews if pp.isDisplayed and specView.isDisplayed] validMultipletListViews = [pp.multipletList.pid for pp in specView.multipletListViews if pp.isDisplayed and specView.isDisplayed] pidList.extend(validPeakListViews) pidList.extend(validIntegralListViews) pidList.extend(validMultipletListViews) if specView.isDisplayed: pidList.append(specView.spectrum.pid) self.treeView.selectObjects(pidList) printItems.extend(GLEXTENDEDLIST) selectList = selectList or [] self.printList = [] # add Print Options to the treeView item = QtWidgets.QTreeWidgetItem(self.treeView) item.setText(0, OPTIONPRINT) item.setFlags(item.flags() | QtCore.Qt.ItemIsTristate | QtCore.Qt.ItemIsUserCheckable) for itemName in printItems: child = QtWidgets.QTreeWidgetItem(item) child.setFlags(child.flags() | QtCore.Qt.ItemIsUserCheckable) child.setText(0, itemName) item.setExpanded(True) for child in self.treeView.findItems('', QtCore.Qt.MatchContains | QtCore.Qt.MatchRecursive): itemName = child.text(0) if itemName in OPTIONLIST: continue if itemName in selectList: _state = selectList[itemName] else: _prefState = self.printSettings.printOptions.get(itemName) _state = _prefState if _prefState is not None else QtCore.Qt.Checked child.setCheckState(0, _state) def _changePrintType(self): selected = self.printType.get() lastPath = self.getSaveTextWidget() if selected in EXPORTTYPES: ext = EXPORTTYPES[selected][EXPORTEXT] filt = EXPORTTYPES[selected][EXPORTFILTER] lastPath.assureSuffix(ext) self._dialogFilter = filt self.updateDialog() self._updateButtonText() self.updateFilename(lastPath) else: raise TypeError('bad export type')
[docs] def buildParameters(self): """build parameters dict from the user widgets, to be passed to the export method. :return: dict - user parameters """ if not self.objects: return {} selected = self.objectPulldown.getText() if 'SpectrumDisplay' in selected: spectrumDisplay = self.objects[selected][0] strip = self._selectedStrip stripDirection = self.spectrumDisplay.stripArrangement else: spectrumDisplay = None strip = self.objects[selected][0] stripDirection = self.strip.spectrumDisplay.stripArrangement # prType = self.printType.get() # pageType = self.pageOrientation.get() # foregroundColour = hexToRgbRatio(self.foregroundColour) # backgroundColour = hexToRgbRatio(self.backgroundColour) # baseThickness = self.baseThicknessBox.getValue() # # # there are now unique per-spectrumDisplay, may differ from preferences # symbolThickness = strip.symbolThickness # contourThickness = strip.contourThickness # aliasEnabled = strip.aliasEnabled # aliasShade = strip.aliasShade # aliasLabelsEnabled = strip.aliasLabelsEnabled # peakLabelsEnabled = strip.peakLabelsEnabled # multipletLabelsEnabled = strip.multipletLabelsEnabled # stripPadding = self.stripPaddingBox.getValue() # exportDpi = self.exportDpiBox.getValue() if strip: # return the parameters params = {GLFILENAME : self.exitFilename, GLSPECTRUMDISPLAY : spectrumDisplay, GLSTRIP : strip, GLWIDGET : strip._CcpnGLWidget, GLPRINTTYPE : self.printType.get(), GLPAGETYPE : self.pageOrientation.get(), GLPAGESIZE : self.pageSize.get(), GLFOREGROUND : hexToRgbRatio(self.foregroundColour), GLBACKGROUND : hexToRgbRatio(self.backgroundColour), GLBASETHICKNESS : self.baseThicknessBox.getValue(), # unique per spectrumDisplay - may differ from preferences GLSYMBOLTHICKNESS : strip.symbolThickness, GLCONTOURTHICKNESS : strip.contourThickness, GLALIASENABLED : strip.aliasEnabled, GLALIASSHADE : strip.aliasShade, GLALIASLABELSENABLED : strip.aliasLabelsEnabled, GLPEAKLABELSENABLED : strip.peakLabelsEnabled, GLMULTIPLETLABELSENABLED: strip.multipletLabelsEnabled, GLSTRIPDIRECTION : stripDirection, GLSTRIPPADDING : self.stripPaddingBox.getValue(), GLEXPORTDPI : self.exportDpiBox.getValue(), GLSELECTEDPIDS : self.treeView.getSelectedObjectsPids(), GLSTRIPREGIONS : self._stripDict, GLSCALINGMODE : self.scalingMode.getIndex(), GLSCALINGPERCENT : self.scalingPercentage.get(), GLSCALINGBYUNITS : self.scalingUnits.get(), GLSCALINGAXIS : self.scalingAxis.getIndex(), GLUSEPRINTFONT : self._useFontCheckbox.isChecked(), GLPRINTFONT : (self._fontPulldown.get(), self._fontSpinbox.get()), } selectedList = self.treeView.getSelectedItems() for itemName in self.fullList: params[itemName] = True if itemName in selectedList else False return params
[docs] def exportToFile(self, filename=None, params=None): """Export to file :param filename: filename to export :param params: dict - user defined parameters for export """ if params: filename = params[GLFILENAME] glWidget = params[GLWIDGET] prType = params[GLPRINTTYPE] with catchExceptions(errorStringTemplate='Error writing file; "%s"', printTraceBack=False): if prType == EXPORTPDF: pdfExport = glWidget.exportToPDF(filename, params) if pdfExport: pdfExport.writePDFFile() elif prType == EXPORTSVG: svgExport = glWidget.exportToSVG(filename, params) if svgExport: svgExport.writeSVGFile() elif prType == EXPORTPNG: pngExport = glWidget.exportToPNG(filename, params) if pngExport: pngExport.writePNGFile() elif prType == EXPORTPS: pngExport = glWidget.exportToPS(filename, params) if pngExport: pngExport.writePSFile()
[docs] def actionButtons(self): self.setOkButton(callback=self._saveAndCloseDialog, text='Save and Close', tipText='Export the strip and close the dialog\nAll changes to the print settings are saved') self.setCancelButton(callback=self._closeDialog, text='Close', tipText='Close the dialog\nAll changes to the print settings are saved') self.setCloseButton(callback=self._saveDialog, text='Save', tipText='Export the strip') self.setHelpButton(callback=self._helpClicked, tipText='Help', enabled=False) self.setRevertButton(callback=self._revertClicked, tipText='Revert print settings to the state when the dialog was opened', enabled=False) self.setUserButton(callback=self._rejectDialog, text='Cancel', tipText='Close the dialog\nAny changes to the print settings are ignored', enabled=True) self.setDefaultButton(self.CANCELBUTTON)
def __postInit__(self): """post initialise functions """ super().__postInit__() # self._populate() self._revertButton = self.getButton(self.RESETBUTTON) def _closeDialog(self): self._applyChanges() self._rejectDialog() def _saveDialog(self, exitSaveFileName=None): """save button has been clicked """ selected = self.printType.get() lastPath = self.getSaveTextWidget() if selected in EXPORTTYPES: lastPath = lastPath.assureSuffix(EXPORTTYPES[selected][EXPORTEXT]) self.setSaveTextWidget(lastPath) self.exitFilename = lastPath if self.pathEdited is False: # user has not changed the path so we can accept() self._exportToFile() else: # have edited the path so check the new file if self.exitFilename.is_file(): yes = showYesNoWarning('%s already exists.' % self.exitFilename, 'Do you want to replace it?') if yes: self._exportToFile() else: if self.exitFilename.is_dir(): showWarning('Export Error:', 'Filename must be a file.') else: self._exportToFile() def _applyChanges(self): """ The apply button has been clicked Define an undo block for setting the properties of the object If there is an error setting any values then generate an error message If anything has been added to the undo queue then remove it with application.undo() repopulate the popup widgets This is controlled by a series of dicts that contain change functions - operations that are scheduled by changing items in the popup. These functions are executed when the Apply or OK buttons are clicked Return True unless any errors occurred """ allChanges = True if self._changes else False if not allChanges: return True # apply all changes self._applyAllChanges(self._changes) # remove all changes self._changes.clear() self._revertButton.setEnabled(True) return True def _saveAndCloseDialog(self, exitSaveFilename=None): """save and Close button has been clicked """ selected = self.printType.get() lastPath = self.getSaveTextWidget() if selected in EXPORTTYPES: lastPath = lastPath.assureSuffix(EXPORTTYPES[selected][EXPORTEXT]) self.setSaveTextWidget(lastPath) self.exitFilename = lastPath self._applyChanges() self._acceptDialog() def _revertClicked(self): """Revert button signal comes here Revert (roll-back) the state of the project to before the popup was opened """ self.populate(self.mainWidget) self._revertButton.setEnabled(False) self.update()
[docs] def keyPressEvent(self, a0: QtGui.QKeyEvent) -> None: """Subclass keypress to stop enter/return on default button """ if a0.key() in [QtCore.Qt.Key_Return, QtCore.Qt.Key_Enter]: return super().keyPressEvent(a0)
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) if not path.endswith('.ttf') or path.startswith('.'): continue 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 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # list widget copy/paste callBack def _copyRangeCallback(self): """Copy the axis range values from the selected strip """ clickedId = self._stripLists._clickedStripId if clickedId and clickedId in self._stripDict: self._copyRangeValue = self._stripDict[clickedId].toDict() def _pasteRangeCallback(self): """Paste the axis range values into the selected strip """ if self._copyRangeValue is None: getLogger().debug('Nothing to paste') return clickedId = self._stripLists._clickedStripId if clickedId and clickedId in self._stripDict: self._stripDict[clickedId].fromDict(self._copyRangeValue) # update the queued changes self._setRangeState(clickedId) def _pasteRangeAllCallback(self): """Paste the axis range values into all the selected strips """ if self._copyRangeValue is None: getLogger().debug('Nothing to paste') return clickedId = self._stripLists._clickedStripId selectedIds = self._stripLists._selectedStripIds if clickedId and clickedId and selectedIds: for stripId in selectedIds: if stripId in self._stripDict: self._stripDict[stripId].fromDict(self._copyRangeValue) # update the queued changes self._setRangeState(clickedId) #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # page settings def _getChangeState(self): """Get the change state from the _changes dict """ if not self._changes.enabled: return None applyState = True revertState = False allChanges = True if self._changes else False return changeState(self, allChanges, applyState, revertState, None, None, self._revertButton, self._currentNumApplies) @queueStateChange(_verifyPopupApply) def _queuePageSizeCallback(self, _value): value = self.pageSize.get() if value != self.printSettings.pageSize: return partial(self._setPageSize, value) def _setPageSize(self, value): """Set the page size One of: A0, A1, A2, A3, A4, A5, A6, letter """ self.printSettings.pageSize = value @queueStateChange(_verifyPopupApply) def _queuePageOrientationCallback(self): value = self.pageOrientation.get() if value != self.printSettings.pageOrientation: return partial(self._setPageOrientation, value) def _setPageOrientation(self, value): """Set the page orientation - either portrait or landscape """ self.printSettings.pageOrientation = value @queueStateChange(_verifyPopupApply) def _queuePrintTypeCallback(self): value = self.printType.get() if value != self.printSettings.printType: return partial(self._setPrintType, value) def _setPrintType(self, value): """Set the print type: pdf, ps, svg, etc. """ self.printSettings.printType = value def _changeColourButton(self): """Popup a dialog and set the colour in the pulldowns """ dialog = ColourDialog(self) newColour = dialog.getColor() if newColour: addNewColour(newColour) fillColourPulldown(self.foregroundColourBox, allowAuto=False, includeGradients=False) fillColourPulldown(self.backgroundColourBox, allowAuto=False, includeGradients=False) return newColour def _queueForegroundButtonCallback(self, _value): if (newColour := self._changeColourButton()): self.foregroundColourBox.setCurrentText(spectrumColours[newColour.name()]) self.foregroundColour = newColour.name() @queueStateChange(_verifyPopupApply) def _queueForegroundPulldownCallback(self, _value): if _value >= 0: colName = colourNameNoSpace(self.foregroundColourBox.getText()) if colName in spectrumColours.values(): colName = list(spectrumColours.keys())[list(spectrumColours.values()).index(colName)] if colName != self.printSettings.foregroundColour: self.foregroundColour = colName return partial(self._setForeGroundColour, colName) def _setForeGroundColour(self, value): self.printSettings.foregroundColour = value def _queueBackgroundButtonCallback(self, _value): if (newColour := self._changeColourButton()): self.backgroundColourBox.setCurrentText(spectrumColours[newColour.name()]) self.backgroundColour = newColour.name() @queueStateChange(_verifyPopupApply) def _queueBackgroundPulldownCallback(self, _value): if _value >= 0: colName = colourNameNoSpace(self.backgroundColourBox.getText()) if colName in spectrumColours.values(): colName = list(spectrumColours.keys())[list(spectrumColours.values()).index(colName)] if colName != self.printSettings.backgroundColour: self.backgroundColour = colName return partial(self._setBackgroundColour, colName) def _setBackgroundColour(self, value): self.printSettings.backgroundColour = value @queueStateChange(_verifyPopupApply) def _queueBaseThicknessCallback(self, _value): textFromValue = self.baseThicknessBox.textFromValue oldValue = textFromValue(self.printSettings.baseThickness or 0.0) if _value >= 0 and textFromValue(_value) != oldValue: return partial(self._setBaseThickness, _value) def _setBaseThickness(self, value): self.printSettings.baseThickness = float(value) @queueStateChange(_verifyPopupApply) def _queueStripPaddingCallback(self, _value): textFromValue = self.stripPaddingBox.textFromValue oldValue = textFromValue(self.printSettings.stripPadding or 0.0) if _value >= 0 and textFromValue(_value) != oldValue: return partial(self._setStripPadding, _value) def _setStripPadding(self, value): self.printSettings.stripPadding = float(value) @queueStateChange(_verifyPopupApply) def _queueDpiCallback(self, _value): textFromValue = self.exportDpiBox.textFromValue oldValue = textFromValue(self.printSettings.dpi or 0.0) if _value >= 0 and textFromValue(_value) != oldValue: return partial(self._setDpi, _value) def _setDpi(self, value): self.printSettings.dpi = float(value) #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # scaling def _setScalingVisible(self): _ind = self._scalingModeIndex self.scalingPercentage.setVisible(False if _ind else True) self.scalingUnits.setVisible(True if _ind else False) self.scalingAxis.setVisible(True if _ind else False) @queueStateChange(_verifyPopupApply) def _queueScalingModeCallback(self, _value): value = self.scalingMode.getText() self._scalingModeIndex = SCALING_MODES.index(value) self._setScalingVisible() if value != self.printSettings.scalingMode: return partial(self._setScalingMode, value) def _setScalingMode(self, value): self.printSettings.scalingMode = value @queueStateChange(_verifyPopupApply) def _queueScalingPercentageCallback(self, _value): textFromValue = self.scalingPercentage.textFromValue oldValue = textFromValue(self.printSettings.scalingPercentage or 0.0) if _value >= 0 and textFromValue(_value) != oldValue: return partial(self._setScalingPercentage, _value) def _setScalingPercentage(self, value): self.printSettings.scalingPercentage = float(value) @queueStateChange(_verifyPopupApply) def _queueScalingUnitsCallback(self, _value): textFromValue = self.scalingUnits.textFromValue oldValue = textFromValue(self.printSettings.scalingUnits or 0.0) if _value >= 0 and textFromValue(_value) != oldValue: return partial(self._setScalingUnits, _value) def _setScalingUnits(self, value): self.printSettings.scalingUnits = float(value) @queueStateChange(_verifyPopupApply) def _queueScalingAxisCallback(self, _value): value = self.scalingAxis.getText() if value != self.printSettings.scalingAxis: return partial(self._setScalingAxis, value) def _setScalingAxis(self, value): self.printSettings.scalingAxis = value #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # font selection def _setUseFontVisible(self): """Handle checking/unchecking font checkbox """ self._fontPulldown.setEnabled(self._useFontSetting) self._fontSpinbox.setEnabled(self._useFontSetting) @queueStateChange(_verifyPopupApply) def _queueUseFontCallback(self, _value): value = self._useFontCheckbox.isChecked() self._useFontSetting = value self._setUseFontVisible() if value != self.printSettings.useFont: return partial(self._setUseFont, value) def _setUseFont(self, value): self.printSettings.useFont = value @queueStateChange(_verifyPopupApply) def _queueFontSizeCallback(self, _value): textFromValue = self._fontSpinbox.textFromValue oldValue = textFromValue(self.printSettings.fontSize or 0.0) if _value >= 0 and textFromValue(_value) != oldValue: return partial(self._setFontSize, _value) def _setFontSize(self, value): self.printSettings.fontSize = value @queueStateChange(_verifyPopupApply) def _queueFontNameCallback(self, _value): value = self._fontPulldown.get() self._fontNameSetting = value self._setPulldownTextColour(self._fontPulldown) if value != self.printSettings.fontName: return partial(self._setFontName, value) def _setFontName(self, value): self.printSettings.fontName = value #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # treeview print options def _queueGetPrintOptionCallback(self, _value, _state): # get the name of the option from the tree option = _value.data(0, 0) itemName = _value.text(0) if itemName not in OPTIONLIST: checked = int(_value.checkState(0)) self._queuePrintOptionsCallback(option, checked) @queueStateChange(_verifyPopupApply) def _queuePrintOptionsCallback(self, option, checked): """Toggle a general checkbox option in the preferences Requires the parameter to be called 'option' so that the decorator gives it a unique name in the internal updates dict """ if checked != self.printSettings.printOptions.get(option): return partial(self._togglePrintOptions, option, checked) def _togglePrintOptions(self, option, checked): self.printSettings.printOptions[option] = checked #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # print ranges @queueStateChange(_verifyPopupApply) def _queuePrintRangesCallback(self, _value): """Store the print ranges if they have changed """ # get the first spinBox, assume all are the same _value = {} _valueLocal = {} for strip in self.strips: _value[strip.id] = self._stripDict[strip.id].toDict() _valueLocal[strip.id] = self._localStripDict[strip.id].toDict() # any changes to the ranges dict if _value != _valueLocal: return partial(self._setPrintRange, _value) def _setPrintRange(self, value): # NOTE:ED - ranges are currently not saved to preferences correctly for k, val in value.items(): self._localStripDict[k].fromDict(val)
[docs]def main(): # from sandbox.Geerten.Refactored.framework import Framework # from sandbox.Geerten.Refactored.programArguments import Arguments # # # _makeMainWindowVisible = False # # # class MyProgramme(Framework): # "My first app" # pass # # # myArgs = Arguments() # myArgs.noGui = False # myArgs.debug = True # # application = MyProgramme('MyProgramme', '3.0.0-beta3', args=myArgs) # ui = application.ui # ui.initialize() # # if _makeMainWindowVisible: # ui.mainWindow._updateMainWindow(newProject=True) # ui.mainWindow.show() # QtWidgets.QApplication.setActiveWindow(ui.mainWindow) # # dialog = ExportStripToFilePopup(parent=application.mainWindow, # mainWindow=application.mainWindow, # strips=[], # preferences=application.preferences) # result = dialog.exec_() from ccpn.ui.gui.widgets.Application import newTestApplication from ccpn.framework.Application import getApplication app = newTestApplication() application = getApplication() dialog = ExportStripToFilePopup(strips=[]) result = dialog.exec_()
if __name__ == '__main__': main()