# Copyright (C) 2017 DESY, Christoph Rosemann, Notkestr. 85, D-22607 Hamburg
#
# lavue is an image viewing program for photon science imaging detectors.
# Its usual application is as a live viewer using hidra as data source.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation in version 2
# of the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301, USA.
#
# Authors:
# Christoph Rosemann <christoph.rosemann@desy.de>
# Jan Kotanski <jan.kotanski@desy.de>
#
""" live viewer settings """
from __future__ import print_function
from __future__ import unicode_literals
import time
import socket
import warnings
import json
from .qtuic import uic
from .qtuic import QWebView
from .qtuic import qwebview_error
from .qtuic import qwebview_traceback
import numpy as np
import pyqtgraph as _pg
from pyqtgraph import QtCore, QtGui
import os
import zmq
import sys
import argparse
import ntpath
import logging
import scipy.ndimage
from . import imageSource as isr
from . import messageBox
# from . import sourceGroupBox
from . import sourceTabWidget
from . import toolWidget
from . import sourceWidget
from . import preparationGroupBox
from . import memoryBufferGroupBox
from . import scalingGroupBox
from . import levelsGroupBox
from . import channelGroupBox
from . import statisticsGroupBox
from . import imageWidget
from . import imageField
from . import configDialog
from . import release
from . import edDictDialog
from . import filters
from . import rangeWindowGroupBox
from . import filtersGroupBox
from . import helpForm
from .sardanaUtils import debugmethod
# from . import imageNexusExporter
try:
from . import controllerClient
TANGOCLIENT = True
except Exception:
TANGOCLIENT = False
from . import imageFileHandler
from . import sardanaUtils
from . import dataFetchThread
from . import settings
from .hidraServerList import HIDRASERVERLIST
try:
from numpy.lib.nanfunctions import _replace_nan
from numpy.lib.nanfunctions import _copyto
NPNAN = True
except Exception:
NPNAN = False
logger = logging.getLogger("lavue")
if sys.version_info > (3,):
basestring = str
unicode = str
__import__("lavuelib.imageNexusExporter")
#: ( (:obj:`str`,:obj:`str`,:obj:`str`) )
#: pg major version, pg minor verion, pg patch version
_VMAJOR, _VMINOR, _VPATCH = _pg.__version__.split(".")[:3] \
if _pg.__version__ else ("0", "9", "0")
_formclass, _baseclass = uic.loadUiType(
os.path.join(os.path.dirname(os.path.abspath(__file__)),
"ui", "MainDialog.ui"))
_logginglevel = 'info'
[docs]def setLoggerLevel(logger, level):
global _logginglevel
""" sets logging level from string
:param logger: logger
:type logger: :obj:`logging.logger`
:param level: logging level
:type level: :obj:`str`
"""
levels = {'debug': logging.DEBUG,
'info': logging.INFO,
'warning': logging.WARNING,
'error': logging.ERROR,
'critical': logging.CRITICAL}
_logginglevel = level if level in levels else "info"
dlevel = levels.get(level, logging.INFO)
logger.setLevel(dlevel)
[docs]class MainWindow(QtGui.QMainWindow):
@debugmethod
def __init__(self, options, parent=None):
""" constructor
:param options: commandline options
:type options: :class:`argparse.Namespace`
:param parent: parent object
:type parent: :class:`pyqtgraph.QtCore.QObject`
"""
QtGui.QMainWindow.__init__(self, parent)
self.__lavue = LiveViewer(options, self)
self.centralwidget = QtGui.QWidget(self)
self.gridLayout = QtGui.QGridLayout(self.centralwidget)
try:
self.gridLayout.setContentsMargins(0, 0, 0, 0)
except Exception:
self.gridLayout.setMargin(0)
self.gridLayout.setSpacing(0)
self.gridLayout.addWidget(self.__lavue, 0, 0, 1, 1)
self.setCentralWidget(self.centralwidget)
if hasattr(options, "instance") and options.instance:
self.setWindowTitle(
"laVue: Live Image Viewer (v%s) [%s]" %
(str(release.__version__), options.instance))
else:
self.setWindowTitle(
"laVue: Live Image Viewer (v%s)" %
str(release.__version__))
[docs] @debugmethod
def closeEvent(self, event):
""" stores the setting before finishing the application
:param event: close event
:type event: :class:`pyqtgraph.QtCore.QEvent`:
"""
self.__lavue.closeEvent(event)
QtGui.QMainWindow.closeEvent(self, event)
[docs]class LavueState(object):
""" lavue state
"""
@debugmethod
def __init__(self):
""" constructor
"""
#: (:obj:`dict` < :obj:`str`, :obj:`any`>) lavue state dictionary
self.__state = {}
[docs] def updateState(self, dct):
"""update lavue state dictonary
:param dct: lavue state dictionary
:type dct: :obj:`dict` < :obj:`str`, :obj:`any`>
"""
if dct is not None:
self.__state.update(dct)
self.__state["__timestamp__"] = time.time()
[docs] def dump(self):
""" returns string representation
:returns: string representation
:rtype: :obj:`str`
"""
return json.dumps(self.__state)
[docs]class PartialData(object):
""" partial data
"""
#: ( :obj:`dict < :obj:`str`, :obj:'bool`> `)
# transformation transpose map
tpose = {
"none": False,
"fup": False,
"flr": False,
"t": True,
"r90": True,
"r180": False,
"r270": True,
"r180t": True,
}
@debugmethod
def __init__(self, name, rawdata, metadata, x, y, tr):
""" constructor
:param name: data name
:type name: :obj:`str`
:param rawdata: raw data
:type rawdata: :obj:`numpy.ndarray`
:param metadata: json dictionary with metadata
:type metadata: :obj:`str`
:param x: x translation
:type x: :obj:`int`
:param y: y translation
:type y: :obj:`int`
:param tr: transformation, e.g. fup, flr, t, r90, r180, r270, r180t
:type tr: :obj:`str`
"""
#: (:obj:`str`) data name
self.name = name
#: (:class:`numpy.ndarray`) raw data
self.__rawdata = rawdata
#: (:class:`numpy.ndarray`) transformed data
self.__data = None
#: (:obj:`str`) json dictionary with metadata
self.metadata = metadata
#: (:obj:`int`) x translation
self.x = x
#: (:obj:`int`) y translation
self.y = y
#: (:obj:`int`) x size
self.sx = 1
#: (:obj:`int`) y size
self.sy = 1
#: (:obj:`int`) color channel size
self.scc = 1
#: (:obj:`str`) transformation
self.tr = tr
self.data()
if hasattr(self.__data, "shape"):
if len(self.__data.shape) > 2:
self.scc = self.__data.shape[0]
self.sx = self.__data.shape[1]
self.sy = self.__data.shape[2]
else:
if len(self.__data.shape) > 0:
self.sx = self.__data.shape[0]
if len(self.__data.shape) > 1:
self.sy = self.__data.shape[1]
[docs] def dtype(self):
""" provides data type
:returns: numpy data type
:rtype: :obj:`numpy.dtype`
"""
if hasattr(self.__data, "dtype"):
return self.__data.dtype
else:
return None
[docs] def intmaxvalue(self):
""" provides data type maximal value
:returns: integer data type maximal value
:rtype: :obj:`int`
"""
maxval = None
if self.dtype() is not None and \
issubclass(self.dtype().type, np.integer):
try:
maxval = np.iinfo(self.dtype()).max
except Exception:
maxval = None
return maxval
[docs] def data(self):
""" provides transformed data
:returns: transformed data
:rtype: :obj:`numpy.ndarray`
"""
if self.__data is None:
if hasattr(self.__rawdata, "shape") and \
len(self.__rawdata.shape) < 2:
nshape = list(self.__rawdata.shape)
while len(nshape) < 2:
nshape.append(1)
rawdata = self.__rawdata.reshape(nshape)
else:
rawdata = self.__rawdata
if not self.tr or self.tr == "none":
self.__data = rawdata
elif self.tr == 'fud':
self.__data = np.fliplr(rawdata)
elif self.tr == 'flr':
self.__data = np.flipud(rawdata)
elif self.tr == 't':
self.__data = np.swapaxes(rawdata, 0, 1)
elif self.tr == 'r90':
self.__data = np.swapaxes(np.flipud(rawdata), 0, 1)
elif self.tr == 'r180':
self.__data = np.flipud(np.fliplr(rawdata))
elif self.tr == 'r270':
self.__data = np.swapaxes(np.fliplr(rawdata), 0, 1)
elif self.tr == 'r180t':
self.__data = np.swapaxes(
np.fliplr(np.flipud(rawdata)), 0, 1)
else:
try:
if self.tr.startswith("rot") and self.tr.endswith("t"):
rot = -float(self.tr[3:-1])
self.__data = scipy.ndimage.rotate(
rawdata, rot).T
elif self.tr.startswith("rot"):
rot = -float(self.tr[3:])
self.__data = scipy.ndimage.rotate(rawdata, rot)
elif self.tr.startswith("r") and self.tr.endswith("t"):
rot = -float(self.tr[1:-1])
self.__data = scipy.ndimage.rotate(
rawdata, rot).T
elif self.tr.startswith("r"):
rot = -float(self.tr[1:])
self.__data = scipy.ndimage.rotate(rawdata, rot)
else:
self.__data = rawdata
except Exception as e:
logger.debug(str(e))
self.__data = rawdata
return self.__data
[docs] @debugmethod
def tolist(self):
""" converts partial data to a list
:returns: a list: [name, rawdata, metadata, x, y, sx, sy, tr]
:rtype: [:obj:`str`, :obj:`numpy.ndarray', :obj:`str`, :obj:`int`,
:obj:`int`, :obj:`int`, :obj:`int`, :obj:`str`]
"""
return [self.name, self.__rawdata, self.metadata,
self.x, self.y, self.sx, self.sy, self.tr]
def _nansum(a, axis=None):
""" old the numpy nansum
:param axis: axis to sum up
:type axis: :obj:`int`
:returns: numpy array with the sum
:rtype: :class:`numpy.ndarray`
"""
if not NPNAN:
return np.nansum(a, axis)
a, mask = _replace_nan(a, 0)
if mask is None:
return np.sum(a, axis=axis)
mask = np.all(mask, axis=axis)
tot = np.sum(a, axis=axis)
if np.any(mask):
tot = _copyto(tot, np.nan, mask)
return tot
[docs]class LiveViewer(QtGui.QDialog):
'''The master class for the dialog, contains all other
widget and handles communication.'''
#: (:class:`pyqtgraph.QtCore.pyqtSignal`) state updated signal
_stateUpdated = QtCore.pyqtSignal(bool)
@debugmethod
def __init__(self, options, parent=None):
""" constructor
:param options: commandline options
:type options: :class:`argparse.Namespace`
:param parent: parent object
:type parent: :class:`pyqtgraph.QtCore.QObject`
"""
QtGui.QDialog.__init__(self, parent)
# self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
if options.mode and options.mode.lower() in ["expert"]:
#: (:obj:`str`) execution mode: expert or user
self.__umode = "expert"
else:
#: (:obj:`str`) execution mode: expert or user
self.__umode = "user"
if hasattr(options, "configpath") and options.configpath:
QtCore.QSettings.setPath(
QtCore.QSettings.NativeFormat,
QtCore.QSettings.UserScope,
options.configpath
)
#: (:obj:`str`) instance name
self.__instance = options.instance
#: (:obj:`bool`) histogram should be updated
self.__updatehisto = False
#: (:obj:`int`) program pid
self.__apppid = os.getpid()
#: (:obj:`str`) host name
self.__targetname = socket.getfqdn()
#: (:obj:`list` < :obj:`str` > ) allowed source metadata
self.__allowedmdata = ["datasources", "asapostreams"]
#: (:obj:`list` < :obj:`str` > ) allowed widget metadata
self.__allowedwgdata = ["axisscales", "axislabels"]
#: (:class:`lavuelib.sardanaUtils.SardanaUtils`)
#: sardana utils
self.__sardana = None
#: (:class:`lavuelib.settings.Settings`) settings
self.__settings = settings.Settings()
self.__settings.load(QtCore.QSettings())
self.__lavuestate = LavueState()
# (:class:`lavuelib.imageSource.BaseSource`) data source object
self.__datasources = [isr.BaseSource()
for _ in range(self.__settings.nrsources)]
#: (:obj:`list` < :obj:`str` > ) source class names
self.__sourcetypes = []
#: (:obj:`list` < :obj:`str` > ) all source aliases
self.__allsourcealiases = []
#: (:obj:`dict` < :obj:`str`, :obj:`str` > ) source alias names
self.__srcaliasnames = {}
#: (:obj:`list` < :obj:`str` > ) tool class names
self.__tooltypes = []
#: (:obj:`list` < :obj:`str` > ) all tool aliases
self.__alltoolaliases = []
#: (:obj:`dict` < :obj:`str`, :obj:`str` > ) tool alias names
self.__tlaliasnames = {}
self.__updateTypeList(
sourceWidget.swproperties, self.__sourcetypes,
self.__allsourcealiases, self.__srcaliasnames
)
self.__updateTypeList(
toolWidget.twproperties, self.__tooltypes,
self.__alltoolaliases, self.__tlaliasnames
)
#: (:class:`lavuelib.controllerClient.ControllerClient`)
#: tango controller client
self.__tangoclient = None
#: (:obj:`int`) stacking dimension
self.__growing = None
#: (:obj:`int`) current frame id
self.__frame = None
#: (:obj:`str`) nexus field path
self.__fieldpath = None
#: (:obj:`bool`) closing flag
self.__closing = False
#: (:obj:`bool`) ploting flag
self.__ploting = False
#: (:class:`filters.FilterList` ) user filters
self.__filters = filters.FilterList()
#: (:obj:`int`) filter state
self.__filterstate = 0
#: (:obj:`float`) last time read
self.__lasttime = 0
#: (:obj:`float`) current time
self.__currentime = 0
# WIDGET DEFINITIONS
#: (:class:`lavuelib.sourceTabWidget.SourceTabWidget`) source groupbox
self.__sourcewg = sourceTabWidget.SourceTabWidget(
parent=self, sourcetypes=self.__sourcetypes,
expertmode=(self.__umode == 'expert'),
nrsources=self.__settings.nrsources
)
self.__sourcewg.updateSourceComboBox(
[self.__srcaliasnames[twn]
for twn in json.loads(str(self.__settings.imagesources))
if twn in self.__srcaliasnames.keys()]
)
#: (:class:`lavuelib.rangeWindowGroupBox.RangeWindowGroupBox`)
#: memory buffer groupbox
self.__rangewg = rangeWindowGroupBox.RangeWindowGroupBox(
parent=self)
self.__rangewg.factorChanged.connect(self._resizePlot)
self.__rangewg.rangeWindowChanged.connect(self._resizePlot)
self.__rangewg.functionChanged.connect(self._resizePlot)
#: (:class:`lavuelib.memoryBufferGroupBox.MemoryBufferGroupBox`)
#: memory buffer groupbox
self.__mbufferwg = memoryBufferGroupBox.MemoryBufferGroupBox(
parent=self)
self.__mbufferwg.setMaxBufferSize(self.__settings.maxmbuffersize)
self.__mbufferwg.setComputeSum(self.__settings.accelbuffersum)
#: (:class:`lavuelib.filtersGroupBox.FiltersGroupBox`)
# filters widget
self.__filterswg = filtersGroupBox.FiltersGroupBox(
parent=self)
#: (:class:`lavuelib.preparationGroupBox.PreparationGroupBox`)
#: preparation groupbox
self.__prepwg = preparationGroupBox.PreparationGroupBox(
parent=self, settings=self.__settings)
#: (:class:`lavuelib.scalingGroupBox.ScalingGroupBox`) scaling groupbox
self.__scalingwg = scalingGroupBox.ScalingGroupBox(parent=self)
#: (:class:`lavuelib.levelsGroupBox.LevelsGroupBox`) level groupbox
self.__levelswg = levelsGroupBox.LevelsGroupBox(
parent=self, settings=self.__settings,
expertmode=(self.__umode == 'expert'))
#: (:class:`lavuelib.levelsGroupBox.LevelsGroupBox`) channel groupbox
self.__channelwg = channelGroupBox.ChannelGroupBox(
parent=self, settings=self.__settings,
expertmode=(self.__umode == 'expert'))
#: (:class:`lavuelib.statisticsGroupBox.StatisticsGroupBox`)
#: statistic groupbox
self.__statswg = statisticsGroupBox.StatisticsGroupBox(parent=self)
#: (:class:`lavuelib.imageWidget.ImageWidget`) image widget
self.__imagewg = imageWidget.ImageWidget(
parent=self, tooltypes=self.__tooltypes,
settings=self.__settings)
self.__imagewg.updateToolComboBox(
[self.__tlaliasnames[twn]
for twn in json.loads(str(self.__settings.toolwidgets))
if twn in self.__tlaliasnames.keys()]
)
for iid in range(3):
self.__levelswg.setImageItem(self.__imagewg.image(iid), iid)
self.__levelswg.showGradient(True)
self.__levelswg.showChannels(False)
self.__channelwg.showGradient(True)
self.__levelswg.updateHistoImage(autoLevel=True)
#: (:class:`lavuelib.maskWidget.MaskWidget`) mask widget
self.__maskwg = self.__prepwg.maskWidget
#: (:class:`lavuelib.highValueMaskWidget.HighValueMaskWidget`)
# high value mask widget
self.__highvaluemaskwg = self.__prepwg.highValueMaskWidget
#: (:class:`lavuelib.bkgSubtractionWidget.BkgSubtractionWidget`)
#: background subtraction widget
self.__bkgsubwg = self.__prepwg.bkgSubWidget
#: (:class:`lavuelib.normalizationWidget.NormalizationWidget`)
#: background subtraction widget
self.__normwg = self.__prepwg.normWidget
#: (:class:`lavuelib.transformationsWidget.TransformationsWidget`)
#: transformations widget
self.__trafowg = self.__prepwg.trafoWidget
# keep a reference to the "raw" image and the current filename
#: (:class:`numpy.ndarray`) raw image
self.__rawimage = None
#: (:class:`numpy.ndarray`) raw image
self.__filteredimage = None
#: (:class:`numpy.ndarray`) raw gray image
self.__rawgreyimage = None
#: (:obj:`str`) image name
self.__imagename = None
#: (:obj:`str`) last image name
self.__lastimagename = None
#: (:obj:`int`) integer max value
self.__intmaxvalue = None
#: (:obj:`str`) metadata JSON dictionary
self.__metadata = ""
#: (:obj:`str`) metadata dictionary
self.__mdata = {}
#: (:class:`numpy.ndarray`) displayed image after preparation
self.__displayimage = None
#: (:class:`numpy.ndarray`) scaled displayed image
self.__scaledimage = None
#: (:class:`numpy.ndarray`) background image
self.__backgroundimage = None
#: (:class:`numpy.ndarray`) scaled background image
self.__scbackgroundimage = None
#: (:class:`numpy.ndarray`) scaled background scale
self.__backgroundscale = None
#: (:class:`numpy.ndarray`) brightfield image
self.__brightfieldimage = None
#: (:class:`numpy.ndarray`) scaled brightfield image
self.__scbrightfieldimage = None
#: (:class:`numpy.ndarray`) scaled brightfield scale
self.__brightfieldscale = None
#: (:obj:`bool`) apply brightfield - darkfield image subtraction
self.__bfmdfimage = None
#: (:obj:`bool`) apply background image subtraction
self.__dobkgsubtraction = False
#: (:obj:`bool`) apply brightfield image subtraction
self.__dobfsubtraction = False
#: (:class:`numpy.ndarray`) mask image
self.__maskimage = None
#: (:obj:`str`) source configuration string
self.__sourceconfiguration = None
#: (:obj:`str`) source label
self.__sourcelabel = None
#: (:obj:`list`< ( :obj:`int`,:obj:`int` ) > )
# image module translations
self.__translations = [(0, 0)]
#: (:obj:`list`< (:obj:`str`) > ) image module transformations
self.__transformations = ['']
#: (:obj:`list`< (:obj:`str`) >) image module
# translations and transformations
self.__trans = ['']
#: (:obj:`dict`< (:obj:`str`, :obj:`str`)) transformation names map
self.__transmap = {
"flipud": "fud",
"flip-up-down": "fud",
"r90t": "fud",
"rot90t": "fud",
"rot90+transpose": "fud",
"fliplr": "flr",
"flip-left-right": "flr",
"r270t": "flr",
"rot270t": "flr",
"rot270+transpose": "flr",
"transpose": "t",
"rot90": "r90",
"rot180": "r180",
"rot270": "r270",
"rot180t": "r180t",
"rot180+transpose": "r180t",
}
#: (:obj:`str`) reload flag
self.__reloadflag = False
#: (:obj:`str`) transformation name
self.__trafoname = "None"
#: (:obj:`bool`) lazy image slider
self.__lazyimageslider = False
#: (:obj: dict < :obj:`str` , :obj:`str` >) unsigned/signed int map
self.__unsignedmap = {
"uint8": "int16",
"uint16": "int32",
"uint32": "int64",
"uint64": "int64"
# "uint64": "float64"
}
#: (:class:`Ui_LevelsGroupBox') ui_groupbox object from qtdesigner
self.__ui = _formclass()
self.__ui.setupUi(self)
# # LAYOUT DEFINITIONS
self.__ui.confVerticalLayout.addWidget(self.__sourcewg)
self.scroll = QtGui.QScrollArea()
self.scroll.setSizeAdjustPolicy(self.scroll.AdjustToContents)
self.scrollWidget = QtGui.QWidget()
self.scrollVerticalLayout = QtGui.QVBoxLayout()
self.scroll.setContentsMargins(0, 0, 0, 0)
self.scroll.setFrameStyle(QtGui.QFrame.NoFrame)
self.scrollWidget.setContentsMargins(0, 0, 0, 0)
self.scrollVerticalLayout.setContentsMargins(0, 0, 0, 0)
self.__ui.confVerticalLayout.addWidget(self.scroll)
self.scrollVerticalLayout.addWidget(self.__rangewg)
self.scrollVerticalLayout.addWidget(self.__filterswg)
self.scrollVerticalLayout.addWidget(self.__mbufferwg)
self.scrollVerticalLayout.addWidget(self.__channelwg)
self.scrollVerticalLayout.addWidget(self.__prepwg)
self.scrollVerticalLayout.addWidget(self.__scalingwg)
self.scrollVerticalLayout.addWidget(self.__levelswg)
self.scrollVerticalLayout.addWidget(self.__statswg)
self.scrollWidget.setLayout(self.scrollVerticalLayout)
self.scroll.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.scroll.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)
self.scroll.setWidgetResizable(True)
self.scroll.setWidget(self.scrollWidget)
self.scroll.setMinimumWidth(380)
self.__ui.imageVerticalLayout.addWidget(self.__imagewg)
spacer = QtGui.QSpacerItem(
0, 0,
QtGui.QSizePolicy.Minimum,
QtGui.QSizePolicy.Expanding
)
self.scrollVerticalLayout.addItem(spacer)
self.__ui.splitter.setStretchFactor(0, 1)
self.__ui.splitter.setStretchFactor(1, 10)
# SIGNAL LOGIC::
# signal from intensity scaling widget:
# self.__scalingwg.scalingChanged.connect(self.scale)
self.__scalingwg.simpleScalingChanged.connect(self._plot)
self.__scalingwg.scalingChanged.connect(
self._setScalingState)
# signal from limit setting widget
self.__levelswg.minLevelChanged.connect(self._setMinLevelState)
self.__levelswg.maxLevelChanged.connect(self._setMaxLevelState)
self.__levelswg.channelLevelsChanged.connect(
self._setChannelLevelState)
self.__levelswg.autoLevelsChanged.connect(self._setAutoLevelsState)
self.__levelswg.levelsChanged.connect(self._setLevelState)
self.__levelswg.gradientChanged.connect(
self._setGradientState)
self.__ui.cnfPushButton.clicked.connect(self._configuration)
self.__ui.quitPushButton.clicked.connect(self.close)
self.__ui.loadPushButton.clicked.connect(self._clickloadfile)
self.__ui.reloadPushButton.clicked.connect(self._pushreloadfile)
self.__ui.helpPushButton.clicked.connect(self._showhelp)
if self.__umode in ["user"]:
self.__ui.cnfPushButton.hide()
if QtGui.QIcon.hasThemeIcon("applications-system"):
icon = QtGui.QIcon.fromTheme("applications-system")
self.__ui.cnfPushButton.setIcon(icon)
self.__ui.cnfPushButton.setText("")
if QtGui.QIcon.hasThemeIcon("document-open-recent"):
icon = QtGui.QIcon.fromTheme("document-open-recent")
self.__ui.reloadPushButton.setIcon(icon)
self.__ui.reloadPushButton.setText("")
if QtGui.QIcon.hasThemeIcon("document-open"):
icon = QtGui.QIcon.fromTheme("document-open")
self.__ui.loadPushButton.setIcon(icon)
# self.__ui.loadPushButton.setText("")
if QtGui.QIcon.hasThemeIcon("application-exit"):
icon = QtGui.QIcon.fromTheme("application-exit")
self.__ui.quitPushButton.setIcon(icon)
# self.__ui.quitPushButton.setText("")
if QtGui.QIcon.hasThemeIcon("help-browser"):
icon = QtGui.QIcon.fromTheme("help-browser")
self.__ui.helpPushButton.setIcon(icon)
# self.__ui.helpPushButton.setText("")
self.__imagewg.roiCoordsChanged.connect(self._calcUpdateStatsSec)
self.__imagewg.currentToolChanged.connect(self._setToolState)
# connecting signals from source widget:
# gradient selector
self.__channelwg.rgbChanged.connect(self._setRGBState)
self.__channelwg.channelChanged.connect(self._setChannelState)
self.__imagewg.aspectLockedToggled.connect(self._setAspectLocked)
self.__levelswg.storeSettingsRequested.connect(
self._storeSettings)
self.__imagewg.replotImage.connect(self._replot)
# simple mutable caching object for data exchange with thread
#: (:class:`lavuelib.dataFetchTread.ExchangeList`)
#: exchange list
self.__exchangelists = []
for ds in self.__datasources:
self.__exchangelists.append(dataFetchThread.ExchangeList())
#: (:class:`lavuelib.dataFetchTread.DataFetchThread`)
#: data fetch thread
self.__dataFetchers = []
for i, ds in enumerate(self.__datasources):
dft = dataFetchThread.DataFetchThread(ds, self.__exchangelists[i])
self.__dataFetchers.append(dft)
self._stateUpdated.connect(dft.changeStatus)
self.__dataFetchers[0].newDataNameFetched.connect(self._getNewData)
self.__sourcewg.sourceStateChanged.connect(self._updateSource)
self.__sourcewg.sourceChanged.connect(self._onSourceChanged)
self.__sourcewg.sourceConnected.connect(self._connectSource)
self.__sourcewg.sourceDisconnected.connect(self._disconnectSource)
self.__bkgsubwg.bkgFileSelected.connect(self._prepareBkgSubtraction)
self.__bkgsubwg.useCurrentImageAsBkg.connect(
self._setCurrentImageAsBkg)
self.__bkgsubwg.applyStateChanged.connect(self._checkBkgSubtraction)
self.__bkgsubwg.bkgScalingFactorChanged.connect(self._updateBkgScale)
self.__normwg.bfFileSelected.connect(self._prepareBFSubtraction)
self.__normwg.useCurrentImageAsBF.connect(
self._setCurrentImageAsBF)
self.__normwg.applyBFStateChanged.connect(self._checkBFSubtraction)
self.__normwg.bfScalingFactorChanged.connect(self._updateBFScale)
self.__maskwg.maskFileSelected.connect(self._prepareMasking)
self.__maskwg.applyStateChanged.connect(self._checkMasking)
self.__highvaluemaskwg.maskHighValueChanged.connect(
self._checkHighMasking)
self.__highvaluemaskwg.applyStateChanged.connect(
self._checkHighMasking)
# signals from transformation widget
self.__trafowg.transformationChanged.connect(
self._assessTransformation)
# signals from transformation widget
self.__filterswg.filtersChanged.connect(
self._assessFilters)
# signals from transformation widget
self.__mbufferwg.bufferSizeChanged.connect(
self._setBufferSizeState)
# set the right target name for the source display at initialization
self.__sourcewg.sourceLabelChanged.connect(
self._switchSourceDisplay)
self.__sourcewg.addIconClicked.connect(
self._addLabel)
self.__sourcewg.removeIconClicked.connect(
self._removeLabel)
self.__sourcewg.translationChanged.connect(self._setTranslation)
self.__ui.frameLineEdit.textChanged.connect(self._spinreloadfile)
self.__ui.lowerframePushButton.clicked.connect(
self._lowerframepushed)
self.__ui.higherframePushButton.clicked.connect(
self._higherframepushed)
self.__ui.frameHorizontalSlider.valueChanged.connect(
self._sliderreloadfilelazy)
self.__connectslider()
self.__sourcewg.updateLayout()
self.__sourcewg.emitSourceChanged()
self.__imagewg.showCurrentTool()
self.__loadSettings()
self.__resetFilters(self.__settings.filters)
self.__updateframeview()
self.__updateframeratetip(self.__settings.refreshrate)
self.__imagewg.setExtensionsRefreshTime(
self.__settings.toolrefreshtime)
start = self.__applyoptions(options)
self._plot()
self.setState()
if start:
self.__sourcewg.start()
self.__updateTool(options.tool)
self.__imagewg.toolConfigurationChanged.connect(
self._onToolConfigurationChanged)
[docs] @debugmethod
def setState(self):
""" set current state """
dssa = ";".join(self.__sourcewg.currentDataSourceAlias())
configuration = ";".join(self.__sourcewg.configuration())
if not self.__levelswg.isAutoLevel():
levels = self.__levelswg.levels()
autofactor = None
else:
autofactor = self.__levelswg.autoFactor()
levels = ""
bkgfile = ""
if self.__bkgsubwg.isBkgSubApplied():
if self.__settings.bkgimagename:
bkgfile = str(self.__settings.bkgimagename)
bffile = ""
if self.__normwg.isBFSubApplied():
if self.__settings.bfimagename:
bffile = str(self.__settings.bfimagename)
maskfile = ""
if self.__maskwg.isMaskApplied():
if self.__settings.maskimagename:
maskfile = str(self.__settings.maskimagename)
maskhighvalue = ""
if self.__settings.showhighvaluemask:
maskhighvalue = str(self.__highvaluemaskwg.mask() or "")
bkgscale = self.__backgroundscale \
if self.__settings.showsubsf else None
bfscale = self.__brightfieldscale \
if self.__settings.shownormsf else None
self.setLavueState(
{
"connected": self.__sourcewg.isConnected(),
"source": dssa,
"configuration": configuration,
"offset": self._translations(),
"mode": self.__umode,
"instance": self.__instance or "",
"scaling": str(self.__scalingwg.currentScaling()),
"transformation": str(self.__trafowg.transformation()),
"tool": str(self.__imagewg.tool()),
"toolconfig": self.__imagewg.toolConfiguration(),
"levels": levels,
"autofactor": autofactor,
"gradient": self.__levelswg.gradient(),
"maskhighvalue": maskhighvalue,
"maskfile": maskfile,
"bkgfile": bkgfile,
"bkgscale": bkgscale,
"brightfieldfile": bffile,
"brightfieldscale": bfscale,
"channel": self.__channelwg.channelLabel(),
"mbuffer": (self.__mbufferwg.bufferSize() or None),
"doordevice": self.__settings.doorname,
"tangodevice": (self.__tangoclient.device()
if self.__tangoclient else ""),
"analysisdevice": self.__settings.analysisdevice,
"rangewindow": self.__rangewg.rangeWindow(),
"dsfactor": self.__rangewg.factor(),
"dsreduction": str(self.__rangewg.function()),
"filters": self.__filterstate,
"imagefile": (self.__settings.imagename or ""),
"version": str(release.__version__),
})
@QtCore.pyqtSlot(str)
def _setScalingState(self, scaling):
""" sets scaling state
:param scalingtype: scaling type, i.e. log, linear, sqrt
:type scalingtype: :obj:`str`
"""
self.__levelswg.setScalingLabel(scaling)
self.setLavueState({"scaling": str(scaling)})
@QtCore.pyqtSlot()
def _setChannelState(self):
""" sets gradient state
"""
# eself.__levelswg.setScalingLabel(scaling)
self.__levelswg.setRGBChannels(self.__channelwg.rgbchannels())
self.setLavueState({"channel": self.__channelwg.channelLabel()})
self._plot()
@QtCore.pyqtSlot()
def _setGradientState(self):
""" sets gradient state
"""
self.setLavueState({"gradient": self.__levelswg.gradient()})
@QtCore.pyqtSlot(str)
def _setToolState(self, _):
""" sets tool state
"""
self.setLavueState({"tool": self.__imagewg.tool()})
@QtCore.pyqtSlot(int)
def _setBufferSizeState(self, size):
""" sets buffer size state
"""
self.setLavueState({"mbuffer": size or None})
if size == 0:
if "channellabels" in self.__mdata:
self.__mdata.pop("channellabels")
if "suminthelast" in self.__mdata:
self.__mdata.pop("suminthelast")
if "skipfirst" in self.__mdata:
self.__mdata.pop("skipfirst")
self.__channelwg.updateChannelLabels()
def __setLevelState(self):
""" sets intensity level state
"""
if not self.__levelswg.isAutoLevel():
levels = self.__levelswg.levels()
autofactor = None
else:
autofactor = self.__levelswg.autoFactor()
levels = ""
self.setLavueState({"levels": levels, "autofactor": autofactor})
def _setLevelState(self):
""" sets intensity level state and plot
"""
self.__setLevelState()
self._plot()
@QtCore.pyqtSlot(float)
def _setMinLevelState(self, level=None):
""" sets minimum intensity level
:param level: minimum intensity
:type level: :obj:`float`
"""
self.__imagewg.setMinLevel(level)
self.__imagewg.setLevelMode(self.__levelswg.levelMode())
self._setLevelState()
@QtCore.pyqtSlot(float)
def _setMaxLevelState(self, level=None):
""" sets maximum intensity level
:param level: maximum intensity
:type level: :obj:`float`
"""
self.__imagewg.setMaxLevel(level)
self.__imagewg.setLevelMode(self.__levelswg.levelMode())
self._setLevelState()
@QtCore.pyqtSlot()
def _setChannelLevelState(self):
""" sets maximum intensity level
:param level: json with channel levels
:type level: :obj:`str`
"""
levels = self.__levelswg.channelLevels()
if levels is not None:
self.__imagewg.setLevelMode(self.__levelswg.levelMode())
self.__imagewg.setChannelLevels(levels)
self._setLevelState()
@QtCore.pyqtSlot(int)
def _setAutoLevelsState(self, autolevels):
""" sets auto levels
:param autolevels: 2: auto levels enabled 1: with autofactor
:type autolevels: :obj:'int`
"""
self.__imagewg.setAutoLevels(autolevels)
self._setLevelState()
[docs] @debugmethod
def setLavueState(self, dct=None):
""" update LavueState of LavueController
:param dct: lavue state dictionary
:type dct: :obj:`dict` < :obj:`str`, :obj:`any`>
"""
if dct is not None:
self.__lavuestate.updateState(dct)
self.__lavuestate.updateState({
"viewrange": self.__imagewg.viewRange()
})
self.__imagewg.writeAttribute(
"LavueState", self.__lavuestate.dump())
@debugmethod
@QtCore.pyqtSlot()
def _showhelp(self):
""" shows the detail help
"""
if QWebView is None:
messageBox.MessageBox.warning(
self, "lavue: "
"Help cannot be display because "
"QtWebKitWigets or QtWebKit module is not installed",
str(qwebview_error),
str(qwebview_traceback),
)
else:
form = helpForm.HelpForm("qrc:/help/index.html", self)
form.show()
@debugmethod
def _translations(self):
""" povides translations and optional transformations
of the given sources
:returns: x,y tranlation or x,y,transformations, e.g. 2345,354,r90
:rtype: :obj:`str`
"""
nrsources = min(len(self.__datasources), len(self.__trans))
return ";".join(self.__trans[:nrsources])
@debugmethod
@QtCore.pyqtSlot(str, int)
def _setTranslation(self, trans, sid):
""" sets translation and optional transfromation of the given source
:param trans: x,y tranlation or x,y,transformations, e.g. 2345,354
:type trans: :obj:`str`
:param sid: source id
:type sid: :obj:`int`
"""
try:
x = None
y = None
tr = ''
strans = trans.split(",")
if len(strans) > 0:
try:
x = int(strans[0].replace("m", "-"))
except Exception:
pass
if len(strans) > 1:
try:
y = int(strans[1].replace("m", "-"))
except Exception:
pass
if len(strans) > 2:
try:
tr = str(strans[2]).lower()
except Exception:
pass
while len(self.__translations) <= sid:
self.__translations.append((None, None))
while len(self.__transformations) <= sid:
self.__transformations.append('')
while len(self.__trans) <= sid:
self.__trans.append('')
self.__translations[sid] = (x, y)
self.__transformations[sid] = self.__transmap.get(tr, tr)
self.__trans[sid] = trans
except Exception:
pass
offset = self._translations()
self.setLavueState({"offset": offset})
# @debugmethod
def __updateTypeList(self, properties, typelist,
allaliases, snametoname):
""" updates type list, i.e. typelist, allaliases and snametoname
:param properties: dictionary with properies, i.e.
{"requires": ("<PACKAGE1>","<PACKAGE2>"),
"alias": "<alias>",
"widget": "<widgettype>",
"name": "<name>"}
:type properties: :obj:`dict` < :obj:`str`, :obj:`str`>
:param typelist: type list
:type typelist: :obj:`list` < :obj:`str`>
:param allaliases: a list of aliases
:type allaliases: :obj:`list` < :obj:`str`>
:param snametoname: alias to name dictionary
:type snametoname: :obj:`dict` < :obj:`str`, :obj:`str`>
"""
typelist[:] = []
allaliases[:] = []
for wp in properties:
avail = True
for req in wp["requires"]:
if not getattr(isr, req):
avail = False
break
if avail:
if wp["alias"] not in typelist:
typelist.append(wp["widget"])
allaliases.append(wp["alias"])
snametoname[wp["alias"]] = wp["name"]
# @debugmethod
def __switchlazysignals(self, lazy=False):
"""switch lazy signals
:param lazy: lazy image slider flag
:type lazy: :obj:`bool`
"""
if lazy:
self.__ui.frameHorizontalSlider.sliderReleased.connect(
self._sliderreloadfile)
self.__lazyimageslider = True
else:
self.__ui.frameHorizontalSlider.sliderReleased.disconnect(
self._sliderreloadfile)
self.__lazyimageslider = False
# @debugmethod
def __disconnectslider(self):
"""switch lazy signals
:param lazy: lazy image slider flag
:type lazy: :obj:`bool`
"""
if self.__lazyimageslider:
self.__ui.frameHorizontalSlider.sliderReleased.disconnect(
self._sliderreloadfile)
# @debugmethod
def __connectslider(self):
"""switch lazy signals
:param lazy: lazy image slider flag
:type lazy: :obj:`bool`
"""
if self.__lazyimageslider:
self.__ui.frameHorizontalSlider.sliderReleased.connect(
self._sliderreloadfile)
# @debugmethod
def __resetFilters(self, filters):
""" resets filters
:param filters: filters settings
:type filters: :obj:`str`
"""
try:
fsettings = json.loads(filters)
self.__filters.reset(fsettings)
label = " | ".join([flt[0].split(".")[-1]
for flt in fsettings if flt[0]])
if len(label) > 32:
label = label[:32] + " ..."
self.__filterswg.setLabel(label)
self.__filterswg.setToolTip(
"\n".join(
["%s(%s)" %
(flt[0],
("'%s'" % flt[1]) if flt[1] else "")
for flt in fsettings if flt[0]])
)
if self.__settings.filters != filters:
self.__settings.filters = filters
except Exception as e:
self.__filterswg.setState(0)
import traceback
value = traceback.format_exc()
messageBox.MessageBox.warning(
self, "lavue: problems in setting filters",
"%s" % str(e),
"%s" % value)
# print(str(e))
@debugmethod
@QtCore.pyqtSlot(str, str)
def _addLabel(self, name, value):
""" emits addIconClicked signal
:param name: object name
:type name: :obj:`str`
:param value: text value
:type value: :obj:`str`
"""
name = str(name)
value = str(value)
labelvalues = json.loads(getattr(self.__settings, name) or '{}')
dform = edDictDialog.EdDictDialog(self)
dform.record = labelvalues
dform.newvalues = [value]
# dform.title = self.__objtitles[repr(obj)]
dform.createGUI()
dform.exec_()
if dform.dirty:
labelvalues = dform.record
for key in list(labelvalues.keys()):
if not str(key).strip():
labelvalues.pop(key)
setattr(self.__settings, name, json.dumps(labelvalues))
self.__updateSource()
self._storeSettings()
@debugmethod
@QtCore.pyqtSlot(str, str)
def _removeLabel(self, name, label):
""" emits addIconClicked signal
:param name: object name
:type name: :obj:`str`
:param value: text value label to remove
:type value: :obj:`str`
"""
name = str(name)
label = str(label)
labelvalues = json.loads(getattr(self.__settings, name) or '{}')
if label in labelvalues.keys():
labelvalues.pop(label)
setattr(self.__settings, name, json.dumps(labelvalues))
self.__updateSource()
self._storeSettings()
@debugmethod
def __updateSource(self):
detservers = json.loads(self.__settings.detservers)
if detservers:
if self.__settings.defdetservers:
serverdict = dict(HIDRASERVERLIST)
defpool = set(serverdict["pool"])
defpool.update(detservers)
serverdict["pool"] = list(defpool)
else:
serverdict = {"pool": list(detservers)}
elif self.__settings.defdetservers:
serverdict = HIDRASERVERLIST
else:
serverdict = {"pool": []}
self.__sourcewg.updateMetaData(
zmqtopics=self.__settings.zmqtopics,
dirtrans=self.__settings.dirtrans,
tangoattrs=self.__settings.tangoattrs,
tangoevattrs=self.__settings.tangoevattrs,
tangofileattrs=self.__settings.tangofileattrs,
tangodirattrs=self.__settings.tangodirattrs,
zmqservers=self.__settings.zmqservers,
httpurls=self.__settings.httpurls,
autozmqtopics=self.__settings.autozmqtopics,
nxslast=self.__settings.nxslast,
nxsopen=self.__settings.nxsopen,
serverdict=serverdict,
hidraport=self.__settings.hidraport,
doocsprops=self.__settings.doocsprops,
tineprops=self.__settings.tineprops,
epicspvnames=self.__settings.epicspvnames,
epicspvshapes=self.__settings.epicspvshapes,
asaposerver=self.__settings.asaposerver,
asapotoken=self.__settings.asapotoken,
asapodatasources=self.__settings.asapodatasources,
asapobeamtime=self.__settings.asapobeamtime,
asaposourcepath=self.__settings.asaposourcepath
)
self._updateSource(-1, -1)
@debugmethod
@QtCore.pyqtSlot(str)
def _updateLavueState(self, state):
""" updates lavue state configuration
:param state: json dictionary with configuration
:type state: :obj:`str`
"""
if state:
dctcnf = json.loads(str(state))
if dctcnf:
srccnf = "source" in dctcnf.keys() or \
"configuration" in dctcnf.keys()
stop = dctcnf["stop"] if "stop" in dctcnf.keys() else None
start = dctcnf["start"] if "start" in dctcnf.keys() else None
tool = dctcnf["tool"] if "tool" in dctcnf.keys() else None
if "connected" in dctcnf.keys() and \
stop is None and start is None:
if dctcnf["connected"]:
start = True
else:
stop = True
running = self.__sourcewg.isConnected()
if srccnf or stop is True or start is True:
if self.__sourcewg.isConnected():
self.__sourcewg.toggleServerConnection()
self.__applyoptionsfromdict(dctcnf)
if not self.__sourcewg.isConnected():
if start is True:
self.__sourcewg.toggleServerConnection()
elif running and srccnf and stop is not True:
self.__sourcewg.toggleServerConnection()
self.__updateTool(tool)
self._plot()
self.setState()
if not dctcnf or \
("__update__" in dctcnf.keys() and dctcnf["__update__"]):
self.setLavueState()
@debugmethod
def __updateTool(self, tool):
""" update current tool
:param tool: alias tool name
:type tool: :obj:`str`
"""
if tool and tool != self.__imagewg.tool():
self.__imagewg.showCurrentTool()
# @debugmethod
def __applyoptionsfromdict(self, dctcnf):
""" apply options
:param dctcnf: commandline options
:type dctcnf: :obj:`dict` <:obj:`str`, any >
"""
if dctcnf:
options = argparse.Namespace()
for key, vl in dctcnf.items():
setattr(options, key, vl)
self.__applyoptions(options)
@debugmethod
def __applyoptions(self, options):
""" apply options
:param options: commandline options
:type options: :class:`argparse.Namespace`
:returns: start flag
:rtype: :obj:`bool`
"""
if hasattr(options, "log") and options.log is not None:
setLoggerLevel(logger, options.log)
self.setLavueState({"log": _logginglevel})
if hasattr(options, "doordevice") and options.doordevice is not None:
self.__settings.doorname = str(options.doordevice)
self.setLavueState({"doordevice": self.__settings.doorname})
if hasattr(options, "analysisdevice") and \
options.analysisdevice is not None:
self.__settings.analysisdevice = str(options.analysisdevice)
self.setLavueState(
{"analysisdevice": self.__settings.analysisdevice})
# load image file
if hasattr(options, "imagefile") and options.imagefile is not None:
oldname = self.__settings.imagename
oldpath = self.__fieldpath
oldgrowing = self.__growing
try:
sfid = 0
self.__growing = 0
newname = str(options.imagefile)
if "," in newname:
splname = newname.split(",")
newname = splname[0]
if splname[1]:
try:
self.__growing = int(splname[1])
except Exception:
pass
if len(splname) > 2:
sfid = splname[2].replace("m", "-")
self.__settings.imagename = newname
if ":/" in self.__settings.imagename:
self.__settings.imagename, self.__fieldpath = \
self.__settings.imagename.split(":/", 1)
else:
self.__fieldpath = None
imagename = self.__settings.imagename
if not imagename.endswith(".nxs") \
and not imagename.endswith(".h5") \
and not imagename.endswith(".nx") \
and not imagename.endswith(".ndf") \
and not imagename.endswith(".hdf"):
fid = self.__findfid(imagename)
else:
try:
fid = int(sfid)
except Exception:
fid = 0
self._loadfile(fid=fid)
except Exception:
self.__settings.imagename = oldname
self.__fieldpath = oldpath
self.__growing = oldgrowing
self.setLavueState(
{"imagefile": (self.__settings.imagename or "")})
# set image source
sourcechanged = False
if hasattr(options, "source") and options.source is not None:
srcnames = str(options.source).split(";")
self.__setNumberOfSources(max(len(srcnames), 1))
for i, srcname in enumerate(srcnames):
if srcname in self.__srcaliasnames.keys():
self.__sourcewg.setSourceComboBoxByName(
i, self.__srcaliasnames[srcname])
sourcechanged = True
if sourcechanged:
self.__updateSource()
QtCore.QCoreApplication.processEvents()
if hasattr(options, "configuration") and \
options.configuration is not None:
cnfs = str(options.configuration).split(";")
for i, cnf in enumerate(cnfs):
if i < self.__sourcewg.count():
self.__sourcewg.configure(i, str(cnf))
if sourcechanged:
self._setSourceConfiguration()
QtCore.QCoreApplication.processEvents()
if hasattr(options, "offset") and options.offset is not None:
offs = str(options.offset).split(";")
for i, offset in enumerate(offs):
if i < self.__sourcewg.count():
self._setTranslation(str(offset), i)
self.__sourcewg.setTranslation(str(offset), i)
if hasattr(options, "rangewindow") and \
options.rangewindow is not None:
if not self.__settings.showrange:
self.__settings.showrange = True
self.__rangewg.changeView(True)
self._resizePlot(True)
self.__rangewg.setFactor(str(options.dsfactor))
self.__rangewg.setRangeWindow(str(options.rangewindow))
if hasattr(options, "dsfactor") and \
options.dsfactor is not None:
if not self.__settings.showrange:
try:
dsf = int(options.dsfactor)
except Exception:
dsf = 1
if dsf != 1:
self.__settings.showrange = True
self.__rangewg.changeView(True)
self._resizePlot(True)
self.__rangewg.setFactor(str(options.dsfactor))
if hasattr(options, "dsreduction") and \
options.dsreduction is not None:
if not self.__settings.showrange:
try:
dsf = int(options.dsfactor)
except Exception:
dsf = 1
if dsf != 1:
self.__settings.showrange = True
self.__rangewg.changeView(True)
self._resizePlot(True)
self.__rangewg.setFunction(str(options.dsreduction))
if hasattr(options, "mbuffer") and options.mbuffer is not None:
if not self.__settings.showmbuffer:
self.__settings.showmbuffer = True
self.__mbufferwg.changeView(True)
self.__mbufferwg.onOff(True)
try:
self.__mbufferwg.setBufferSize(int(options.mbuffer))
except Exception:
pass
if hasattr(options, "bkgfile") and options.bkgfile:
if not self.__settings.showsub:
self.__settings.showsub = True
self.__prepwg.changeView(showsub=True)
self.__bkgsubwg.setBackground(str(options.bkgfile))
elif hasattr(options, "bkgfile"):
self.__bkgsubwg.checkBkgSubtraction(0)
self.__settings.bkgimagename = ""
self.__dobkgsubtraction = None
self.__backgroundimage = None
self.__scbackgroundimage = None
if hasattr(options, "bkgscale") and \
options.bkgscale not in [None, ""]:
if not self.__settings.showsubsf:
self.__settings.showsub = True
self.__settings.showsubsf = True
self.__prepwg.changeView(showsub=True, showsubsf=True)
self.__bkgsubwg.setBkgScalingFactor(str(options.bkgscale))
self._updateBkgScale()
elif hasattr(options, "bkgscale") and \
options.bkgscale in [None, ""]:
self.__bkgsubwg.setBkgScalingFactor("")
self._updateBkgScale()
if hasattr(options, "brightfieldfile") and options.brightfieldfile:
if not self.__settings.shownorm:
self.__settings.shownorm = True
self.__prepwg.changeView(shownorm=True)
self.__normwg.setBrightField(str(options.brightfieldfile))
elif hasattr(options, "brightfieldfile"):
self.__normwg.checkBFSubtraction(0)
self.__dobfsubtraction = None
self.__settings.bfimagename = ""
self.__bfmdfimage = None
self.__brightfieldimage = None
self.__scbrightfieldimage = None
if hasattr(options, "brightfieldscale") and \
options.brightfieldscale not in [None, ""]:
if not self.__settings.shownormsf:
self.__settings.shownorm = True
self.__settings.shownormsf = True
self.__prepwg.changeView(shownorm=True, shownormsf=True)
self.__normwg.setBFScalingFactor(str(options.brightfieldscale))
self._updateBFScale()
elif hasattr(options, "brightfieldscale") and \
options.brightfieldscale in [None, ""]:
self.__normwg.setBFScalingFactor("")
self._updateBFScale()
if hasattr(options, "channel") and options.channel is not None:
try:
ich = int(options.channel) + 1
except Exception:
if options.channel == "mean":
ich = -2
elif options.channel == "rgb":
ich = -1
elif "," in options.channel:
try:
ich = [int(ch) for ch in options.channel.split(",")]
except Exception:
ich = 0
else:
ich = 0
self.__channelwg.setDefaultColorChannel(ich)
if hasattr(options, "maskfile") and options.maskfile:
if not self.__settings.showmask:
self.__settings.showmask = True
self.__prepwg.changeView(True)
self.__maskwg.setMask(str(options.maskfile))
elif hasattr(options, "maskfile"):
self.__maskwg.noImage()
self.__imagewg.setApplyMask(False)
if hasattr(options, "maskhighvalue") and \
options.maskhighvalue is not None:
if not self.__settings.showhighvaluemask:
self.__settings.showhighvaluemask = True
self.__prepwg.changeView(showhighvaluemask=True)
self.__highvaluemaskwg.setMask(str(options.maskhighvalue))
self._checkHighMasking()
if hasattr(options, "transformation") and \
options.transformation is not None:
if not self.__settings.showtrans:
self.__settings.showtrans = True
self.__prepwg.changeView(showtrans=True)
self.__trafowg.setTransformation(str(options.transformation))
if hasattr(options, "filters"):
if options.filters is True:
if not self.__settings.showfilters:
self.__settings.showfilters = True
self.__filterswg.changeView(
showfilters=True)
self.__filterswg.setState(2)
elif options.filters is False:
self.__filterswg.setState(0)
if hasattr(options, "scaling") and options.scaling is not None:
if not self.__settings.showscale:
self.__settings.showscale = True
self.__scalingwg.changeView(True)
self.__scalingwg.setScaling(str(options.scaling))
QtCore.QCoreApplication.processEvents()
if hasattr(options, "gradient") and options.gradient is not None:
if not self.__settings.showlevels:
self.__settings.showslevels = True
self.__levelswg.changeView(showlevels=True)
self.__channelwg.changeView(showlevels=True)
self.__levelswg.setGradient(str(options.gradient))
if hasattr(options, "autofactor") and options.autofactor is not None:
if not self.__settings.showlevels:
self.__settings.showslevels = True
self.__levelswg.changeView(showlevels=True)
self.__channelwg.changeView(showlevels=True)
self.__levelswg.setAutoFactor(str(options.autofactor))
if hasattr(options, "levels") and options.levels is not None:
if not self.__settings.showlevels:
self.__settings.showslevels = True
self.__levelswg.changeView(showlevels=True)
self.__channelwg.changeView(showlevels=True)
self.__levelswg.setLevels(str(options.levels))
if hasattr(options, "tool") and options.tool is not None:
tlname = str(options.tool)
if tlname == 'rgbintensity':
tlname = 'intensity'
if tlname in self.__tlaliasnames.keys():
self.__imagewg.setTool(self.__tlaliasnames[tlname])
if hasattr(options, "toolconfig") and options.toolconfig is not None:
if isinstance(options.toolconfig, dict):
try:
tlconfig = str(json.dumps(options.toolconfig))
except Exception as e:
logger.warning(str(e))
tlconfig = str(options.toolconfig)
else:
tlconfig = str(options.toolconfig)
self.__imagewg.setToolConfiguration(tlconfig)
if hasattr(options, "tangodevice") and \
TANGOCLIENT and options.tangodevice is not None:
if self.__tangoclient is not None:
self.__tangoclient.unsubscribe()
self.__tangoclient = controllerClient.ControllerClient(
str(options.tangodevice))
self.__tangoclient.energyChanged.connect(
self.__imagewg.updateEnergy)
self.__tangoclient.detectorDistanceChanged.connect(
self.__imagewg.updateDetectorDistance)
self.__tangoclient.beamCenterXChanged.connect(
self.__imagewg.updateBeamCenterX)
self.__tangoclient.beamCenterYChanged.connect(
self.__imagewg.updateBeamCenterY)
self.__tangoclient.pixelSizeXChanged.connect(
self.__imagewg.updatePixelSizeX)
self.__tangoclient.pixelSizeYChanged.connect(
self.__imagewg.updatePixelSizeY)
self.__tangoclient.detectorROIsChanged.connect(
self.__imagewg.updateDetectorROIs)
self.__imagewg.setTangoClient(self.__tangoclient)
self.__tangoclient.subscribe()
self.__tangoclient.lavueStateChanged.connect(
self._updateLavueState)
self.setLavueState({"tangodevice": self.__tangoclient.device()})
elif not TANGOCLIENT or (
hasattr(options, "tangodevice") and
not options.tangodevice):
self.__tangoclient = None
self.setLavueState({"tangodevice": ""})
QtCore.QCoreApplication.processEvents()
if hasattr(options, "viewrange") and options.viewrange is not None:
self.__imagewg.setViewRange(str(options.viewrange))
self.__sourcewg.updateLayout()
if hasattr(options, "start"):
return options.start is True
else:
return False
def __loadSettings(self):
""" loads settings from QSettings object
"""
settings = QtCore.QSettings()
if self.parent() is not None:
self.parent().restoreGeometry(settings.value(
"Layout/Geometry", type=QtCore.QByteArray))
self.restoreGeometry(settings.value(
"Layout/DialogGeometry", type=QtCore.QByteArray))
status = self.__settings.load(settings)
logger.debug("lavuelib.liveViewer.LiveViewer.__loadSettings %s"
% self.__settings.__dict__)
self.__levelswg.updateCustomGradients(
self.__settings.customGradients())
for topic, value in status:
text = messageBox.MessageBox.getText(topic)
messageBox.MessageBox.warning(self, topic, text, str(value))
self.__setSardana(self.__settings.sardana)
self.__imagewg.setAspectLocked(self.__settings.aspectlocked)
self.__imagewg.setAutoDownSample(self.__settings.autodownsample)
self._assessTransformation(self.__trafoname)
for i, ds in enumerate(self.__datasources):
ds.setTimeOut(self.__settings.timeout)
dataFetchThread.GLOBALREFRESHRATE = self.__settings.refreshrate
self.__imagewg.setStatsWOScaling(self.__settings.statswoscaling)
self.__imagewg.setColors(self.__settings.roiscolors)
self.__updateSource()
self.__statswg.changeView(
self.__settings.showstats,
self.__settings.calcvariance)
self.__viewFrameRate(False)
self.__levelswg.changeView(
self.__settings.showhisto,
self.__settings.showlevels,
self.__settings.showaddhisto
)
self.__channelwg.changeView(
self.__settings.showlevels
)
self.__prepwg.changeView(
self.__settings.showmask,
self.__settings.showsub,
self.__settings.showtrans,
self.__settings.showhighvaluemask,
self.__settings.showsubsf,
self.__settings.shownorm,
self.__settings.shownormsf
)
self._updateBkgScale()
self._updateBFScale()
self.__rangewg.changeView(self.__settings.showrange)
self._resizePlot(self.__settings.showrange)
self.__filterswg.changeView(self.__settings.showfilters)
self.__mbufferwg.changeView(self.__settings.showmbuffer)
self.__scalingwg.changeView(self.__settings.showscale)
self.__imagewg.setGradientColors(self.__settings.gradientcolors)
self.__levelswg.setGradientColors(self.__settings.gradientcolors)
self.__levelswg.changeView()
self.__channelwg.changeView()
if self.__lazyimageslider != self.__settings.lazyimageslider:
self.__switchlazysignals(self.__settings.lazyimageslider)
# @debugmethod
def __viewFrameRate(self, status):
""" show/hide frame rate
:param status: True for show and False for hide
:type status: :obj:`bool`
"""
if status:
self.__ui.framerateLineEdit.show()
else:
self.__ui.framerateLineEdit.hide()
@debugmethod
@QtCore.pyqtSlot()
def _storeSettings(self):
""" stores settings in QSettings object
"""
settings = QtCore.QSettings()
if self.parent() is not None and self.parent().parent() is not None:
settings.setValue(
"Layout/Geometry",
QtCore.QByteArray(self.parent().parent().saveGeometry()))
settings.setValue(
"Layout/DialogGeometry",
QtCore.QByteArray(self.saveGeometry()))
self.__settings.refreshrate = dataFetchThread.GLOBALREFRESHRATE
self.__settings.sardana = True if self.__sardana is not None else False
self.__settings.store(settings)
@debugmethod
@QtCore.pyqtSlot(bool)
def _setAspectLocked(self, status):
self.__settings.aspectlocked = status
self.__imagewg.setAspectLocked(self.__settings.aspectlocked)
[docs] @debugmethod
def closeEvent(self, event):
""" stores the setting before finishing the application
:param event: close event
:type event: :class:`pyqtgraph.QtCore.QEvent`:
"""
if not self.__closing:
self.__sourcewg.sourceStateChanged.disconnect(
self._updateSource)
self.__sourcewg.sourceChanged.disconnect(
self._onSourceChanged)
self.__sourcewg.sourceConnected.disconnect(
self._connectSource)
self.__sourcewg.sourceDisconnected.disconnect(
self._disconnectSource)
if self.__imagewg:
self.__imagewg.disconnecttool()
if self.__tangoclient:
self.__tangoclient.unsubscribe()
self._storeSettings()
self.__settings.secstream = False
for dft in self.__dataFetchers:
try:
dft.newDataNameFetched.disconnect(self._getNewData)
except Exception:
pass
# except Exception as e:
# print (str(e))
if self.__sourcewg.isConnected():
self.__sourcewg.toggleServerConnection()
self.__disconnectSource()
for df in self.__dataFetchers:
df.stop()
df.wait()
self.__settings.seccontext.destroy()
self.__closing = True
QtGui.QApplication.closeAllWindows()
if event is not None:
event.accept()
@debugmethod
@QtCore.pyqtSlot()
def _clickloadfile(self):
""" reloads the image file
"""
self._loadfile()
@debugmethod
def _loadfile(self, fid=None):
""" reloads the image file
:param fid: frame id
:type fid: :obj:`int`
"""
self._reloadfile(fid, showmessage=True)
@debugmethod
@QtCore.pyqtSlot(str)
def _spinreloadfile(self, fid=None, showmessage=False):
""" reloads the image file
:param fid: frame id
:type fid: :obj:`int` or :obj:`str`
:param showmessage: no image message
:type showmessage: :obj:`bool`
"""
try:
if not self.__reloadflag:
self.__reloadflag = True
if fid is None:
fid = self.__ui.frameLineEdit.text()
try:
fid = int(fid)
self._reloadfile(fid, showmessage)
except Exception:
pass
time.sleep(0.1)
finally:
self.__reloadflag = False
@debugmethod
@QtCore.pyqtSlot()
def _pushreloadfile(self):
""" reloads the image file
:param fid: frame id
:type fid: :obj:`int` or :obj:`str`
:param showmessage: no image message
:type showmessage: :obj:`bool`
"""
try:
self.__reloadflag = True
fid = self.__ui.frameLineEdit.text()
try:
fid = int(fid)
self._reloadfile(fid, True)
except Exception:
pass
finally:
self.__reloadflag = False
@debugmethod
@QtCore.pyqtSlot()
def _lowerframepushed(self):
step = self.__ui.framestepSpinBox.value()
try:
frame = int(self.__ui.frameLineEdit.text())
except Exception:
frame = self.__frame or 0
nframe = frame - step
if frame >= 0:
nframe = max(nframe, 0)
self.__ui.frameLineEdit.setText(str(nframe))
@debugmethod
@QtCore.pyqtSlot()
def _higherframepushed(self):
step = self.__ui.framestepSpinBox.value()
try:
frame = int(self.__ui.frameLineEdit.text())
except Exception:
frame = self.__frame or 0
nframe = frame + step
if frame < 0:
nframe = min(nframe, -1)
self.__ui.frameLineEdit.setText(str(nframe))
@debugmethod
@QtCore.pyqtSlot(int)
def _sliderreloadfilelazy(self, _=None):
""" reloads the image file or
if lazy flag it displays only splider value
"""
if self.__lazyimageslider:
fid = self.__ui.frameHorizontalSlider.value()
self.__ui.frameLineEdit.textChanged.disconnect(
self._spinreloadfile)
self.__ui.frameLineEdit.setText(str(fid))
self.__ui.frameLineEdit.textChanged.connect(
self._spinreloadfile)
else:
self._sliderreloadfile()
@debugmethod
@QtCore.pyqtSlot()
def _sliderreloadfile(self, fid=None, showmessage=False):
""" reloads the image file
:param fid: frame id
:type fid: :obj:`int`
:param showmessage: no image message
:type showmessage: :obj:`bool`
"""
try:
if not self.__reloadflag:
self.__reloadflag = True
if fid is None:
fid = self.__ui.frameHorizontalSlider.value()
self._reloadfile(fid, showmessage)
finally:
self.__reloadflag = False
def __findfid(self, imagename):
""" find file id
"""
try:
ipath, iname = ntpath.split(imagename)
basename, ext = os.path.splitext(iname)
ival = True
w = 0
while ival:
try:
int(basename[(- w - 1):])
w += 1
if w == len(basename):
ival = False
except Exception:
ival = False
ffid = basename[-w:]
fid = int(ffid)
except Exception:
fid = 0
return fid
@debugmethod
def _reloadfile(self, fid=None, showmessage=False, nexus=None):
""" reloads the image file
:param fid: frame id
:type fid: :obj:`int`
:param showmessage: no image message
:type showmessage: :obj:`bool`
:param nexus: nexus image name
:type nexus: :obj:`str`
"""
newimage = None
metadata = None
if fid is not None:
imagename = self.__settings.imagename
if nexus \
or imagename.endswith(".nxs") \
or imagename.endswith(".h5") \
or imagename.endswith(".nx") \
or imagename.endswith(".ndf") \
or imagename.endswith(".hdf"):
self.__frame = int(fid)
elif fid >= 0:
try:
ipath, iname = ntpath.split(imagename)
basename, ext = os.path.splitext(iname)
ival = True
w = 0
while ival:
try:
int(basename[(- w - 1):])
w += 1
if w == len(basename):
ival = False
except Exception:
ival = False
fprefix, ffid = basename[:-w], basename[-w:]
if w:
fmt = "%sd" % w
fmtfid = ("%0" + fmt) % fid
else:
fmtfid = ""
fprefix = ffid
ffid = ""
self.__frame = int(fid)
iname = "%s%s%s" % (fprefix, fmtfid, ext)
imgname = os.path.join(ipath, iname)
if os.path.isfile(imgname):
imagename = imgname
except Exception:
imagename = None
fid = None
if fid is None:
if nexus:
imagename = nexus
else:
fileDialog = QtGui.QFileDialog()
fileout = fileDialog.getOpenFileName(
self, 'Load file', self.__settings.imagename or '.')
if isinstance(fileout, tuple):
imagename = str(fileout[0])
else:
imagename = str(fileout)
if imagename:
if nexus \
or imagename.endswith(".nxs") \
or imagename.endswith(".h5") \
or imagename.endswith(".nx") \
or imagename.endswith(".ndf") \
or imagename.endswith(".hdf"):
try:
handler = imageFileHandler.NexusFieldHandler(
str(imagename))
fields = handler.findImageFields()
self.__settings.imagename = imagename
except Exception as e:
logger.warning(str(e))
# print(str(e))
fields = None
self.setLavueState(
{"imagefile": (self.__settings.imagename or "")})
currentfield = None
if fields:
if fid is None or self.__fieldpath is None:
imgfield = imageField.ImageField(self)
imgfield.fields = fields
imgfield.createGUI()
if imgfield.exec_():
self.__fieldpath = imgfield.field
self.__growing = imgfield.growing
self.__frame = imgfield.frame
else:
return
currentfield = fields[self.__fieldpath]
try:
newimage = handler.getImage(
currentfield["node"],
self.__frame, self.__growing, refresh=False)
except Exception as e:
logger.warning(str(e))
# print(str(e))
metadata = handler.getMetaData(currentfield["node"])
# if metadata:
# print("Metadata = %s" % str(metadata))
self.__ui.frameLineEdit.textChanged.disconnect(
self._spinreloadfile)
self.__disconnectslider()
self.__ui.frameHorizontalSlider.valueChanged.disconnect(
self._sliderreloadfilelazy)
try:
if len(currentfield["shape"]) < 3:
gsize = 0
else:
gsize = currentfield["shape"][self.__growing] - 1
if gsize >= 0:
self.__ui.frameLineEdit.setToolTip(
"current frame (max: %s)" % gsize)
self.__ui.frameHorizontalSlider.setMaximum(gsize)
self.__ui.frameHorizontalSlider.setToolTip(
"current frame (max: %s)" % gsize)
else:
self.__ui.frameLineEdit.setToolTip("current frame")
self.__ui.frameHorizontalSlider.setMaximum(0)
self.__ui.frameHorizontalSlider.setToolTip(
"current frame")
except Exception:
self.__ui.frameLineEdit.setToolTip("current frame")
while newimage is None and self.__frame > 0:
self.__frame -= 1
newimage = handler.getImage(
currentfield["node"],
self.__frame, self.__growing, refresh=False)
if currentfield and len(currentfield["shape"]) > 2:
self.__updateframeview(True, True)
else:
self.__updateframeview()
self.__ui.frameLineEdit.textChanged.connect(
self._spinreloadfile)
self.__ui.frameHorizontalSlider.valueChanged.connect(
self._sliderreloadfilelazy)
self.__connectslider()
else:
if showmessage:
text = messageBox.MessageBox.getText(
"lavue: Image %s cannot be loaded"
% self.__settings.imagename)
messageBox.MessageBox.warning(
self,
"lavue: File %s cannot be loaded"
% self.__settings.imagename,
"File %s without images"
% self.__settings.imagename)
return
if imagename:
imagename = "%s:/%s" % (
self.__settings.imagename,
currentfield["nexus_path"])
if currentfield and len(currentfield["shape"]) > 2:
self.__updateframeview(True, True)
else:
self.__updateframeview()
else:
try:
fh = imageFileHandler.ImageFileHandler(
str(imagename))
newimage = fh.getImage()
if hasattr(newimage, "dtype") \
and str(newimage.dtype) == 'object':
self._reloadfile(fid, showmessage, nexus=imagename)
return
if isinstance(newimage, float) and newimage == -1.0:
newimage = None
if newimage is None:
raise Exception(
"Cannot read the image %s" % str(imagename))
metadata = fh.getMetaData()
self.__settings.imagename = imagename
self.setLavueState(
{"imagefile": (self.__settings.imagename or "")})
try:
ipath, iname = ntpath.split(imagename)
basename, ext = os.path.splitext(iname)
ival = True
w = 0
while ival:
try:
int(basename[(- w - 1):])
w += 1
if w == len(basename):
ival = False
except Exception:
ival = False
fprefix, ffid = basename[:-w], basename[-w:]
self.__frame = int(ffid)
iname = "%s%s%s" % (fprefix, ffid, ext)
imagename = os.path.join(ipath, iname)
except Exception:
self.__frame = None
self.__updateframeview(self.__frame is not None)
self.__fieldpath = None
except Exception as e:
logger.warning(str(e))
# print(str(e))
if newimage is not None:
self.__metadata = metadata
if metadata:
self.__mdata = json.loads(str(metadata))
if self.__settings.geometryfromsource:
self.__settings.updateMetaData(**self.__mdata)
self.__imagewg.updateCenter(
self.__settings.centerx,
self.__settings.centery)
self.__imagewg.mouseImagePositionChanged.emit()
self.__imagewg.geometryChanged.emit()
else:
self.__mdata = {}
self.__imagename = imagename
self.__rawimage = np.transpose(newimage)
self.__intmaxvalue = None
pd = PartialData(imagename, self.__rawimage, metadata,
0, 0, None)
imv = pd.intmaxvalue()
if imv is not None:
self.__intmaxvalue = imv
self._plot()
if fid is None:
self.__imagewg.autoRange()
else:
text = messageBox.MessageBox.getText(
"lavue: File %s cannot be loaded"
% self.__settings.imagename)
messageBox.MessageBox.warning(
self,
"lavue: File %s cannot be loaded"
% self.__settings.imagename,
text,
str("lavue: File %s cannot be loaded"
% self.__settings.imagename))
@debugmethod
@QtCore.pyqtSlot()
def _configuration(self):
""" launches the configuration dialog
"""
cnfdlg = configDialog.ConfigDialog(self)
if not self.__settings.doorname and self.__sardana is not None:
self.__settings.doorname = self.__sardana.getDeviceName("Door")
cnfdlg.sardana = True if self.__sardana is not None else False
cnfdlg.door = self.__settings.doorname
cnfdlg.crosshairlocker = self.__settings.crosshairlocker
cnfdlg.addrois = self.__settings.addrois
cnfdlg.orderrois = self.__settings.orderrois
cnfdlg.imagechannels = self.__settings.imagechannels
cnfdlg.showsub = self.__settings.showsub
cnfdlg.showsubsf = self.__settings.showsubsf
cnfdlg.shownorm = self.__settings.shownorm
cnfdlg.shownormsf = self.__settings.shownormsf
cnfdlg.showtrans = self.__settings.showtrans
cnfdlg.showscale = self.__settings.showscale
cnfdlg.showlevels = self.__settings.showlevels
cnfdlg.showframerate = self.__settings.showframerate
cnfdlg.showhisto = self.__settings.showhisto
cnfdlg.showaddhisto = self.__settings.showaddhisto
cnfdlg.showmask = self.__settings.showmask
cnfdlg.showhighvaluemask = self.__settings.showhighvaluemask
cnfdlg.showmbuffer = self.__settings.showmbuffer
cnfdlg.showrange = self.__settings.showrange
cnfdlg.showfilters = self.__settings.showfilters
cnfdlg.showstats = self.__settings.showstats
cnfdlg.showsteps = self.__settings.showsteps
cnfdlg.calcvariance = self.__settings.calcvariance
cnfdlg.filters = self.__settings.filters
cnfdlg.secautoport = self.__settings.secautoport
cnfdlg.secport = self.__settings.secport
cnfdlg.hidraport = self.__settings.hidraport
cnfdlg.maxmbuffersize = self.__settings.maxmbuffersize
cnfdlg.gradientcolors = self.__settings.gradientcolors
cnfdlg.floattype = self.__settings.floattype
cnfdlg.secstream = self.__settings.secstream
cnfdlg.zeromask = self.__settings.zeromask
cnfdlg.nanmask = self.__settings.nanmask
cnfdlg.negmask = self.__settings.negmask
cnfdlg.refreshrate = dataFetchThread.GLOBALREFRESHRATE
cnfdlg.toolrefreshtime = self.__settings.toolrefreshtime
cnfdlg.toolpollinginterval = self.__settings.toolpollinginterval
cnfdlg.timeout = self.__settings.timeout
cnfdlg.nrsources = self.__settings.nrsources
cnfdlg.aspectlocked = self.__settings.aspectlocked
cnfdlg.autodownsample = self.__settings.autodownsample
cnfdlg.keepcoords = self.__settings.keepcoords
cnfdlg.accelbuffersum = self.__settings.accelbuffersum
cnfdlg.lazyimageslider = self.__settings.lazyimageslider
cnfdlg.statswoscaling = self.__settings.statswoscaling
cnfdlg.zmqtopics = self.__settings.zmqtopics
cnfdlg.autozmqtopics = self.__settings.autozmqtopics
cnfdlg.interruptonerror = self.__settings.interruptonerror
cnfdlg.dirtrans = self.__settings.dirtrans
cnfdlg.tangoattrs = self.__settings.tangoattrs
cnfdlg.tineprops = self.__settings.tineprops
cnfdlg.epicspvnames = self.__settings.epicspvnames
cnfdlg.epicspvshapes = self.__settings.epicspvshapes
cnfdlg.doocsprops = self.__settings.doocsprops
cnfdlg.tangoevattrs = self.__settings.tangoevattrs
cnfdlg.tangofileattrs = self.__settings.tangofileattrs
cnfdlg.tangodirattrs = self.__settings.tangodirattrs
cnfdlg.httpurls = self.__settings.httpurls
cnfdlg.zmqservers = self.__settings.zmqservers
cnfdlg.nxslast = self.__settings.nxslast
cnfdlg.nxsopen = self.__settings.nxsopen
cnfdlg.sendrois = self.__settings.sendrois
cnfdlg.sendresults = self.__settings.sendresults
cnfdlg.singlerois = self.__settings.singlerois
cnfdlg.showallrois = self.__settings.showallrois
cnfdlg.storegeometry = self.__settings.storegeometry
cnfdlg.geometryfromsource = self.__settings.geometryfromsource
cnfdlg.roiscolors = self.__settings.roiscolors
cnfdlg.sourcedisplay = self.__settings.sourcedisplay
cnfdlg.imagesources = self.__settings.imagesources
cnfdlg.imagesourcenames = self.__srcaliasnames
cnfdlg.toolwidgets = self.__settings.toolwidgets
cnfdlg.toolwidgetnames = {}
cnfdlg.diffnpt = self.__settings.diffnpt
cnfdlg.correctsolidangle = self.__settings.correctsolidangle
cnfdlg.availimagesources = self.__allsourcealiases
cnfdlg.availtoolwidgets = self.__alltoolaliases
cnfdlg.defdetservers = self.__settings.defdetservers
cnfdlg.asaposerver = self.__settings.asaposerver
cnfdlg.asapotoken = self.__settings.asapotoken
cnfdlg.asapobeamtime = self.__settings.asapobeamtime
cnfdlg.asaposourcepath = self.__settings.asaposourcepath
cnfdlg.asapodatasources = self.__settings.asapodatasources
cnfdlg.detservers = json.dumps(self.__mergeDetServers(
HIDRASERVERLIST if cnfdlg.defdetservers else {"pool": []},
json.loads(self.__settings.detservers)))
cnfdlg.createGUI()
if cnfdlg.exec_():
self.__updateConfig(cnfdlg)
self._storeSettings()
@debugmethod
def __updateConfig(self, dialog):
""" updates the configuration
"""
replot = False
self.__settings.doorname = dialog.door
self.setLavueState({"doordevice": self.__settings.doorname})
if dialog.sardana != (True if self.__sardana is not None else False):
self.__setSardana(dialog.sardana)
self.__settings.sardana = dialog.sardana
self.__settings.addrois = dialog.addrois
self.__settings.orderrois = dialog.orderrois
self.__settings.imagechannels = dialog.imagechannels
self.__settings.floattype = dialog.floattype
self.__settings.crosshairlocker = dialog.crosshairlocker
if self.__settings.showsub != dialog.showsub:
self.__prepwg.changeView(showsub=dialog.showsub)
self.__settings.showsub = dialog.showsub
if self.__settings.shownorm != dialog.shownorm:
self.__prepwg.changeView(shownorm=dialog.shownorm)
self.__settings.shownorm = dialog.shownorm
if self.__settings.showsubsf != dialog.showsubsf:
if dialog.showsubsf:
self.__settings.showsub = True
self.__prepwg.changeView(showsub=True,
showsubsf=dialog.showsubsf)
else:
self.__prepwg.changeView(showsubsf=dialog.showsubsf)
self.__settings.showsubsf = dialog.showsubsf
self._updateBkgScale()
if self.__settings.shownormsf != dialog.shownormsf:
if dialog.shownormsf:
self.__settings.shownorm = True
self.__prepwg.changeView(shownorm=True,
shownormsf=dialog.shownormsf)
else:
self.__prepwg.changeView(shownormsf=dialog.shownormsf)
self.__settings.shownormsf = dialog.shownormsf
self._updateBFScale()
if self.__settings.showtrans != dialog.showtrans:
self.__prepwg.changeView(showtrans=dialog.showtrans)
self.__settings.showtrans = dialog.showtrans
if self.__settings.showmask != dialog.showmask:
self.__settings.showmask = dialog.showmask
self.__prepwg.changeView(dialog.showmask)
if self.__settings.showhighvaluemask != dialog.showhighvaluemask:
self.__settings.showhighvaluemask = dialog.showhighvaluemask
self.__prepwg.changeView(
showhighvaluemask=dialog.showhighvaluemask)
self._checkHighMasking()
replot = True
if self.__settings.showrange != dialog.showrange:
self.__settings.showrange = dialog.showrange
self.__rangewg.changeView(dialog.showrange)
self._resizePlot(self.__settings.showrange)
if self.__settings.showfilters != dialog.showfilters:
self.__settings.showfilters = dialog.showfilters
self.__filterswg.changeView(
showfilters=dialog.showfilters)
if self.__settings.showmbuffer != dialog.showmbuffer:
self.__settings.showmbuffer = dialog.showmbuffer
self.__mbufferwg.changeView(dialog.showmbuffer)
if self.__settings.showscale != dialog.showscale:
self.__scalingwg.changeView(dialog.showscale)
self.__settings.showscale = dialog.showscale
if self.__settings.showlevels != dialog.showlevels:
self.__levelswg.changeView(showlevels=dialog.showlevels)
self.__channelwg.changeView(showlevels=dialog.showlevels)
self.__settings.showlevels = dialog.showlevels
if self.__settings.showframerate != dialog.showframerate:
self.__settings.showframerate = dialog.showframerate
self.__viewFrameRate(self.__settings.showframerate
and self.__sourcewg.isConnected())
if self.__settings.showhisto != dialog.showhisto:
self.__levelswg.changeView(dialog.showhisto)
self.__settings.showhisto = dialog.showhisto
if self.__settings.showaddhisto != dialog.showaddhisto:
self.__levelswg.changeView(showadd=dialog.showaddhisto)
self.__settings.showaddhisto = dialog.showaddhisto
if self.__settings.showsteps != dialog.showsteps:
self.__settings.showsteps = dialog.showsteps
self.__updateframeview(self.__frame is not None)
statschanged = False
if self.__settings.showstats != dialog.showstats:
self.__settings.showstats = dialog.showstats
statschanged = True
if self.__settings.calcvariance != dialog.calcvariance:
self.__settings.calcvariance = dialog.calcvariance
statschanged = True
if statschanged:
self.__statswg.changeView(
dialog.showstats, dialog.calcvariance)
if self.__settings.imagesources != dialog.imagesources:
self.__settings.imagesources = dialog.imagesources
self.__sourcewg.updateSourceComboBox(
[self.__srcaliasnames[twn]
for twn in json.loads(str(self.__settings.imagesources))],
self.__sourcewg.currentDataSourceNames())
if self.__settings.toolwidgets != dialog.toolwidgets:
self.__settings.toolwidgets = dialog.toolwidgets
self.__imagewg.updateToolComboBox(
[self.__tlaliasnames[twn]
for twn in json.loads(self.__settings.toolwidgets)],
self.__imagewg.currentTool())
dataFetchThread.GLOBALREFRESHRATE = dialog.refreshrate
if self.__settings.refreshrate != dialog.refreshrate:
self.__settings.refreshrate = dialog.refreshrate
self.__updateframeratetip(self.__settings.refreshrate)
if self.__settings.toolrefreshtime != dialog.toolrefreshtime:
self.__settings.toolrefreshtime = dialog.toolrefreshtime
self.__imagewg.setExtensionsRefreshTime(
self.__settings.toolrefreshtime)
if self.__settings.toolpollinginterval != dialog.toolpollinginterval:
self.__settings.toolpollinginterval = dialog.toolpollinginterval
self.__imagewg.setExtensionsRefreshTime(
self.__settings.toolrefreshtime)
if self.__settings.filters != dialog.filters:
self.__resetFilters(dialog.filters)
replot = True
if self.__settings.secstream != dialog.secstream or (
self.__settings.secautoport != dialog.secautoport
and dialog.secautoport):
if self.__settings.secstream:
# workaround for a bug in libzmq
try:
self.__settings.secsocket.unbind(
self.__settings.secsockopt)
except Exception:
pass
if self.__sourcewg.isConnected():
self.__sourcewg.connectSuccess(None)
if dialog.secstream:
if dialog.secautoport:
self.__settings.secsockopt = b"tcp://*:*"
self.__settings.secsocket.bind(self.__settings.secsockopt)
dialog.secport = unicode(
self.__settings.secsocket.getsockopt(
zmq.LAST_ENDPOINT)).split(":")[-1]
else:
self.__settings.secsockopt = b"tcp://*:%s" % dialog.secport
self.__settings.secsocket.bind(self.__settings.secsockopt)
if self.__sourcewg.isConnected():
self.__sourcewg.connectSuccess(dialog.secport)
self.__settings.secautoport = dialog.secautoport
self.__settings.secport = dialog.secport
self.__settings.timeout = dialog.timeout
for i, ds in enumerate(self.__datasources):
ds.setTimeOut(self.__settings.timeout)
self.__settings.aspectlocked = dialog.aspectlocked
self.__imagewg.setAspectLocked(self.__settings.aspectlocked)
self.__settings.autodownsample = dialog.autodownsample
self.__imagewg.setAutoDownSample(self.__settings.autodownsample)
remasking = False
if self.__settings.accelbuffersum != dialog.accelbuffersum:
self.__settings.accelbuffersum = dialog.accelbuffersum
self.__mbufferwg.setComputeSum(self.__settings.accelbuffersum)
if self.__settings.gradientcolors != dialog.gradientcolors:
self.__settings.gradientcolors = dialog.gradientcolors
self.__imagewg.setGradientColors(dialog.gradientcolors)
self.__levelswg.setGradientColors(dialog.gradientcolors)
replot = True
if self.__settings.keepcoords != dialog.keepcoords:
self.__settings.keepcoords = dialog.keepcoords
self._assessTransformation(self.__trafoname)
replot = True
if self.__settings.lazyimageslider != dialog.lazyimageslider:
self.__settings.lazyimageslider = dialog.lazyimageslider
self.__switchlazysignals(self.__settings.lazyimageslider)
setsrc = False
if self.__settings.nrsources != dialog.nrsources:
setsrc = True
oldsize = self.__settings.nrsources
if self.__sourcewg.isConnected():
self.__sourcewg.toggleServerConnection()
QtCore.QCoreApplication.processEvents()
time.sleep(1)
self.__setNumberOfSources(dialog.nrsources)
self.__settings.nrsources = dialog.nrsources
self.__sourcewg.updateSourceComboBox(
[self.__srcaliasnames[twn]
for twn in json.loads(str(self.__settings.imagesources))],
self.__sourcewg.currentDataSourceNames()[:oldsize])
self.__settings.secstream = dialog.secstream
self.__settings.storegeometry = dialog.storegeometry
self.__settings.geometryfromsource = dialog.geometryfromsource
self.__settings.interruptonerror = dialog.interruptonerror
self.__settings.sourcedisplay = dialog.sourcedisplay
if self.__settings.hidraport != dialog.hidraport:
self.__settings.hidraport = dialog.hidraport
setsrc = True
if self.__settings.dirtrans != dialog.dirtrans:
self.__settings.dirtrans = dialog.dirtrans
setsrc = True
if self.__settings.tangoattrs != dialog.tangoattrs:
self.__settings.tangoattrs = dialog.tangoattrs
setsrc = True
if self.__settings.tineprops != dialog.tineprops:
self.__settings.tineprops = dialog.tineprops
setsrc = True
if self.__settings.epicspvnames != dialog.epicspvnames:
self.__settings.epicspvnames = dialog.epicspvnames
setsrc = True
if self.__settings.epicspvshapes != dialog.epicspvshapes:
self.__settings.epicspvshapes = dialog.epicspvshapes
setsrc = True
if self.__settings.doocsprops != dialog.doocsprops:
self.__settings.doocsprops = dialog.doocsprops
setsrc = True
if self.__settings.tangoevattrs != dialog.tangoevattrs:
self.__settings.tangoevattrs = dialog.tangoevattrs
setsrc = True
if self.__settings.tangofileattrs != dialog.tangofileattrs:
self.__settings.tangofileattrs = dialog.tangofileattrs
setsrc = True
if self.__settings.tangodirattrs != dialog.tangodirattrs:
self.__settings.tangodirattrs = dialog.tangodirattrs
setsrc = True
if self.__settings.httpurls != dialog.httpurls:
self.__settings.httpurls = dialog.httpurls
setsrc = True
if self.__settings.zmqservers != dialog.zmqservers:
self.__settings.zmqservers = dialog.zmqservers
setsrc = True
if self.__settings.zmqtopics != dialog.zmqtopics:
self.__settings.zmqtopics = dialog.zmqtopics
setsrc = True
if self.__settings.asapodatasources != dialog.asapodatasources:
self.__settings.asapodatasources = dialog.asapodatasources
setsrc = True
if self.__settings.defdetservers != dialog.defdetservers:
self.__settings.defdetservers = dialog.defdetservers
setsrc = True
detservers = json.dumps(self.__retrieveUserDetServers(
HIDRASERVERLIST if dialog.defdetservers else {"pool": []},
json.loads(dialog.detservers)))
if self.__settings.detservers != detservers:
self.__settings.detservers = detservers
setsrc = True
if self.__settings.asaposerver != dialog.asaposerver:
self.__settings.asaposerver = dialog.asaposerver
setsrc = True
if self.__settings.asapotoken != dialog.asapotoken:
self.__settings.asapotoken = dialog.asapotoken
setsrc = True
if self.__settings.asapobeamtime != dialog.asapobeamtime:
self.__settings.asapobeamtime = dialog.asapobeamtime
setsrc = True
if self.__settings.asaposourcepath != dialog.asaposourcepath:
self.__settings.asaposourcepath = dialog.asaposourcepath
setsrc = True
if self.__settings.autozmqtopics != dialog.autozmqtopics:
self.__settings.autozmqtopics = dialog.autozmqtopics
setsrc = True
if self.__settings.nxsopen != dialog.nxsopen:
self.__settings.nxsopen = dialog.nxsopen
setsrc = True
if self.__settings.nxslast != dialog.nxslast:
self.__settings.nxslast = dialog.nxslast
setsrc = True
if self.__settings.sendrois != dialog.sendrois:
self.__settings.sendrois = dialog.sendrois
if self.__settings.sendresults != dialog.sendresults:
self.__settings.sendresults = dialog.sendresults
if self.__settings.singlerois != dialog.singlerois:
self.__settings.singlerois = dialog.singlerois
if self.__settings.showallrois != dialog.showallrois:
self.__settings.showallrois = dialog.showallrois
if setsrc:
self.__updateSource()
if self.__settings.maxmbuffersize != dialog.maxmbuffersize:
self.__settings.maxmbuffersize = dialog.maxmbuffersize
self.__mbufferwg.setMaxBufferSize(self.__settings.maxmbuffersize)
self.__settings.statswoscaling = dialog.statswoscaling
replot = replot or \
self.__imagewg.setStatsWOScaling(
self.__settings.statswoscaling)
if self.__settings.zeromask != dialog.zeromask:
self.__settings.zeromask = dialog.zeromask
remasking = True
replot = True
if self.__settings.diffnpt != dialog.diffnpt:
self.__settings.diffnpt = dialog.diffnpt
replot = True
if self.__settings.correctsolidangle != dialog.correctsolidangle:
self.__settings.correctsolidangle = dialog.correctsolidangle
replot = True
if self.__settings.nanmask != dialog.nanmask:
self.__settings.nanmask = dialog.nanmask
remasking = True
replot = True
if self.__settings.negmask != dialog.negmask:
self.__settings.negmask = dialog.negmask
remasking = True
replot = True
if self.__settings.roiscolors != dialog.roiscolors:
self.__settings.roiscolors = dialog.roiscolors
self.__imagewg.setColors(self.__settings.roiscolors)
if remasking:
self.__remasking()
if replot:
self._plot()
@debugmethod
def __setNumberOfSources(self, nrsources):
""" set a number of image sources
:param nrsources: a number of image sources
:type nrsources: :obj:`int`
"""
nrsources = max(int(nrsources), 1)
if len(self.__dataFetchers) > nrsources:
for _ in reversed(range(nrsources, len(self.__dataFetchers))):
df = self.__dataFetchers.pop()
df.stop()
df.wait()
df = None
self.__datasources.pop()
self.__exchangelists.pop()
elif len(self.__dataFetchers) < nrsources:
for i in reversed(range(len(self.__dataFetchers), nrsources)):
self.__datasources.append(isr.BaseSource())
self.__exchangelists.append(dataFetchThread.ExchangeList())
dft = dataFetchThread.DataFetchThread(
self.__datasources[-1], self.__exchangelists[-1])
self.__dataFetchers.append(dft)
self._stateUpdated.connect(dft.changeStatus)
self.__sourcewg.setNumberOfSources(nrsources)
self._setSourceConfiguration()
self.__settings.nrsources = nrsources
self.__sourcewg.updateLayout()
QtCore.QCoreApplication.processEvents()
# @debugmethod
def __mergeDetServers(self, detserverdict, detserverlist):
""" merges detector servers from
a dictionary and a list
:param detserverdict: detector server dictionary
:type detserverdict: :obj:`dict` <:obj:`str`, :obj:`list`<:obj:`str`>>
:param detserverklist: detector server list
:type detserverlist: :obj:`list` < :obj:`str`>
:returns: merged detector server list
:rtype: :obj:`list` < :obj:`str`>
"""
servers = set(detserverdict["pool"])
if self.__targetname in detserverdict.keys():
servers.update(detserverdict[self.__targetname])
if detserverlist:
servers.update(detserverlist)
return list(servers)
# @debugmethod
def __retrieveUserDetServers(self, detserverdict, detserverlist):
""" retrives user detector servers from a list
which are not in a dictionary
:param detserverdict: detector server dictionary
:type detserverdict: :obj:`dict` <:obj:`str`, :obj:`list`<:obj:`str`>>
:param detserverklist: detector server list
:type detserverlist: :obj:`list` < :obj:`str`>
:returns: user detector server list
:rtype: :obj:`list` < :obj:`str`>
"""
servers = []
if detserverlist:
defservers = set(detserverdict["pool"])
if self.__targetname in detserverdict.keys():
defservers.update(detserverdict[self.__targetname])
servers = list(set(detserverlist) - defservers)
return list(servers)
@debugmethod
def _setSourceConfiguration(self, sourceConfiguration=None):
""" sets the source configuration
:param sourceConfiguration: source configuration string
:type sourceConfiguration: :obj:`str
"""
if sourceConfiguration is None:
sourceConfiguration = self.__sourcewg.configuration()
self.__sourceconfiguration = sourceConfiguration
cdss = self.__sourcewg.currentDataSources()
for i, ds in enumerate(cdss):
if ds == \
str(type(self.__datasources[i]).__name__):
self.__datasources[i].setConfiguration(sourceConfiguration[i])
self.setLavueState(
{"configuration": ";".join(self.__sourceconfiguration or "")})
@debugmethod
def _switchSourceDisplay(self, label):
"""switches source display parameters
:param sourceConfiguration: source configuration string
:type sourceConfiguration: :obj:`str
"""
if self.__sourcelabel != str(label) and label and \
self.__settings.sourcedisplay:
self.__sourcelabel = str(label)
values = self.__settings.sourceDisplay(
self.__sourcelabel)
self.__applyoptionsfromdict(values)
# @debugmethod
def __setSourceLabel(self):
"""sets source display parameters
"""
if self.__sourcelabel and self.__settings.sourcedisplay:
label = self.__sourcelabel
values = {}
values["transformation"] = self.__trafowg.transformation()
values["tool"] = self.__imagewg.tool()
values["toolconfig"] = self.__imagewg.toolConfiguration()
values["scaling"] = self.__scalingwg.currentScaling()
if not self.__levelswg.isAutoLevel():
values["levels"] = self.__levelswg.levels()
values["autofactor"] = None
else:
values["autofactor"] = self.__levelswg.autoFactor()
values["gradient"] = self.__levelswg.gradient()
if self.__bkgsubwg.isBkgSubApplied() \
and self.__settings.bkgimagename:
values["bkgfile"] = str(self.__settings.bkgimagename)
else:
values["bkgfile"] = ""
if self.__normwg.isBFSubApplied() \
and self.__settings.bfimagename:
values["brightfieldfile"] = str(
self.__settings.bfimagename)
else:
values["brightfieldfile"] = ""
bkgscale = self.__backgroundscale \
if not self.__settings.showsubsf else None
bfscale = self.__brightfieldscale \
if not self.__settings.shownormsf else None
values["bfscale"] = bfscale
values["bkgscale"] = bkgscale
if self.__maskwg.isMaskApplied() and \
self.__settings.maskimagename:
values["maskfile"] = str(self.__settings.maskimagename)
else:
values["maskfile"] = ""
mvalue = self.__highvaluemaskwg.mask()
if mvalue is not None:
values["maskhighvalue"] = str(mvalue)
else:
values["maskhighvalue"] = ""
values["viewrange"] = self.__imagewg.viewRange()
values["offset"] = self._translations()
self.__settings.setSourceDisplay(label, values)
# @debugmethod
def __setSardana(self, status):
""" sets the sardana utils
"""
if status is False:
self.__sardana = None
else:
self.__sardana = sardanaUtils.SardanaUtils()
self.__imagewg.setSardanaUtils(self.__sardana)
@debugmethod
@QtCore.pyqtSlot(str)
def _onSourceChanged(self, status):
""" update a list of sources according to the status
:param status: json list on status, i.e source type ids
:type status: :obj:`str`
"""
lstatus = json.loads(str(status))
dss = self.__sourcewg.currentDataSources()
for i, ds in enumerate(dss):
if lstatus[i]:
if ds != str(type(self.__datasources[i]).__name__):
self.__datasources[i] = getattr(
isr, ds)(self.__settings.timeout)
self._setSourceConfiguration()
for i, ds in enumerate(dss):
self.__sourcewg.updateSourceMetaData(
i, **self.__datasources[i].getMetaData())
dssa = ";".join(self.__sourcewg.currentDataSourceAlias())
self.setLavueState({"source": dssa})
@QtCore.pyqtSlot()
def _onToolConfigurationChanged(self):
""" update tool configuration in lavue state
"""
self.setLavueState(
{"tool": self.__imagewg.tool(),
"toolconfig": self.__imagewg.toolConfiguration()})
@debugmethod
@QtCore.pyqtSlot(int, int)
def _updateSource(self, status, sid):
""" update the current source
:param status: current source combobox status id from starting from 1,
0 is disconnected, -1 current source
:type status: :obj:`int`
:param sid: source tab id starting from 0 and -1 for all
:type sid: :obj:`int`
"""
if status:
dss = self.__sourcewg.currentDataSources()
if sid == -1:
for i, ds in enumerate(self.__datasources):
ds.setTimeOut(self.__settings.timeout)
self.__dataFetchers[i].setDataSource(ds)
if self.__sourceconfiguration and \
self.__sourceconfiguration[i] and \
dss[i] == str(type(ds).__name__):
ds.setConfiguration(self.__sourceconfiguration[i])
self.__sourcewg.updateSourceMetaData(i, **ds.getMetaData())
else:
ds = self.__datasources[sid]
ds.setTimeOut(self.__settings.timeout)
self.__dataFetchers[sid].setDataSource(ds)
if self.__sourceconfiguration and \
self.__sourceconfiguration[sid] and \
dss[sid] == str(type(ds).__name__):
ds.setConfiguration(self.__sourceconfiguration[sid])
self.__sourcewg.updateSourceMetaData(
sid, **ds.getMetaData())
self._stateUpdated.emit(bool(status))
@debugmethod
@QtCore.pyqtSlot(bool)
def _replot(self, autorange):
""" The main command of the live viewer class:
draw a numpy array with the given name and autoRange.
"""
self._plot()
if autorange:
self.__imagewg.autoRange()
@debugmethod
@QtCore.pyqtSlot()
def _plot(self):
""" The main command of the live viewer class:
draw a numpy array with the given name.
"""
if self.__ploting:
return
self.__ploting = True
try:
self.__filteredimage = self.__rawimage
# apply user range
self.__applyRange()
# apply user filters
self.__applyFilters()
if self.__settings.showmbuffer and self.__mbufferwg.isOn():
result = self.__mbufferwg.process(
self.__filteredimage, self.__imagename)
if isinstance(result, tuple) and len(result) == 2:
self.__filteredimage, mdata = result
self.__mdata.update(mdata)
if "channellabels" in self.__mdata:
self.__channelwg.updateChannelLabels(
self.__mdata["channellabels"])
# prepare or preprocess the raw image if present:
self.__prepareImage()
# perform transformation
# (crdtranspose, crdleftrightflip, crdupdownflip,
# orgtranspose, orgleftrightflip, orgupdownflip)
allcrds = self.__transform()
self.__imagewg.setTransformations(*allcrds)
# use the internal raw image to create a display image with chosen
# scaling
self.__scale(self.__scalingwg.currentScaling())
# calculate and update the stats for this
self.__calcUpdateStats()
# calls internally the plot function of the plot widget
if self.__imagename is not None and self.__scaledimage is not None:
self.__ui.fileNameLineEdit.setText(
self.__imagename.replace("\n", " "))
self.__ui.fileNameLineEdit.setToolTip(self.__imagename)
self.__imagewg.plot(
self.__scaledimage,
self.__displayimage
if self.__settings.statswoscaling else self.__scaledimage,
self.__imagename
)
if self.__settings.showhisto and self.__updatehisto:
self.__levelswg.updateHistoImage()
self.__updatehisto = False
finally:
self.__ploting = False
@debugmethod
@QtCore.pyqtSlot()
def _calcUpdateStatsSec(self):
""" calcuates statistics without sending security stream
"""
self.__calcUpdateStats(secstream=False)
# @debugmethod
def __calcUpdateStats(self, secstream=True):
""" calcuates statistics
:param secstream: send security stream flag
:type secstream: :obj:`bool`
"""
# calculate the stats for this
auto = self.__levelswg.isAutoLevel()
stream = secstream and self.__settings.secstream and \
self.__scaledimage is not None
display = self.__settings.showstats
calcvariance = self.__settings.calcvariance
maxval, meanval, varval, minval, maxrawval, maxsval, channels = \
self.__calcStats(
(stream or display,
stream or display,
display and calcvariance,
stream or auto,
stream,
auto)
)
smaxval = "%.4f" % maxval
smeanval = "%.4f" % meanval
svarval = "%.4f" % varval
sminval = "%.3f" % minval
smaxrawval = "%.4f" % maxrawval
calctime = time.time()
currentscaling = self.__scalingwg.currentScaling()
# update the statistics display
if stream:
messagedata = {
'command': 'alive', 'calctime': calctime, 'maxval': smaxval,
'maxrawval': smaxrawval,
'minval': sminval, 'meanval': smeanval, 'pid': self.__apppid,
'scaling': (
'linear'
if self.__settings.statswoscaling
else currentscaling)}
topic = 10001
message = "%d %s" % (
topic, str(json.dumps(messagedata)).encode("ascii"))
self.__settings.secsocket.send_string(str(message))
self.__statswg.updateStatistics(
smeanval, smaxval, svarval,
'linear' if self.__settings.statswoscaling else currentscaling)
# if needed, update the level display
if auto:
self.__levelswg.updateAutoLevels(minval, maxsval, channels)
@debugmethod
def _startPlotting(self):
""" mode changer: start plotting mode.
It starts plotting if the connection is really established.
"""
#
if not self.__sourcewg.isConnected():
return
for dft in self.__dataFetchers:
dft.changeStatus(True)
if not dft.isFetching():
dft.start()
@debugmethod
def _stopPlotting(self):
""" mode changer: stop plotting mode
"""
for dft in self.__dataFetchers:
if dft is not None:
dft.changeStatus(False)
@debugmethod
@QtCore.pyqtSlot(str)
def _connectSource(self, status):
""" calls the connect function of the source interface
:param status: current source status id
:type status: :obj:`int`
"""
# print("Connect Source %s" % status)
lstatus = json.loads(str(status))
status = lstatus[0]
self.__viewFrameRate(self.__settings.showframerate)
for i, status in enumerate(lstatus):
self._updateSource(status, i)
for ds in self.__datasources:
if ds is None:
messageBox.MessageBox.warning(
self, "lavue: No data source is defined",
"No data source is defined",
"Please select the image source")
self._setSourceConfiguration()
consuccess = bool(len(self.__datasources))
states = self.__sourcewg.tabCheckBoxStates()
for ids, ds in enumerate(self.__datasources):
if not ds.connect() and states[ids]:
self.__sourcewg.connectFailure()
messageBox.MessageBox.warning(
self, "lavue: The %s connection could not be established"
% type(ds).__name__,
"The %s connection could not be established"
% type(ds).__name__,
str(ds.errormessage))
consuccess = False
if consuccess:
self.__sourcewg.connectSuccess(
self.__settings.secport if self.__settings.secstream else None)
if self.__settings.secstream:
calctime = time.time()
messagedata = {
'command': 'start', 'calctime': calctime, 'pid': self.__apppid}
topic = 10001
# print(str(messagedata))
self.__settings.secsocket.send_string("%d %s" % (
topic, str(json.dumps(messagedata)).encode("ascii")))
self.__updatehisto = True
self.__setSourceLabel()
self.setLavueState({"connected": self.__sourcewg.isConnected()})
self._startPlotting()
@debugmethod
@QtCore.pyqtSlot()
def _disconnectSource(self):
""" calls the disconnect function of the source interface
"""
self.__disconnectSource()
self._updateSource(0, -1)
self.__setSourceLabel()
self.setLavueState({"connected": self.__sourcewg.isConnected()})
# self.__datasources[0] = None
def __disconnectSource(self):
""" calls the disconnect function of the source interface
"""
self._stopPlotting()
for ds in self.__datasources:
ds.disconnect()
self.__viewFrameRate(False)
if self.__settings.secstream:
calctime = time.time()
messagedata = {
'command': 'stop',
'calctime': calctime,
'pid': self.__apppid}
# print(str(messagedata))
topic = 10001
self.__settings.secsocket.send_string("%d %s" % (
topic, str(json.dumps(messagedata)).encode("ascii")))
# @debugmethod
def __mergeData(self, fulldata, oldname, channels=False):
""" merge data parts to (name, rawdata, metadata)
:param fulldata: a list of PartialData objects
:type fulldata: :obj:`list` <:class:`PartialData`>
:param oldname: old name
:type oldname: :obj:`str`
:param channels: map image sources into different color channels
:type channels: :obj:`bool`
:returns: tuple of exchange object (name, data, metadata)
:rtype: :obj:`list` <:obj:`str`, :class:`numpy.ndarray`, :obj:`str` >
"""
names = [pdata.name for pdata in fulldata if pdata.name]
rawimage = None
metadata = None
if "__ERROR__" in names:
name = "__ERROR__"
rawimage = " ".join(
[str(pdata.tolist()[1])
for pdata in fulldata
if pdata.name == "__ERROR__"]
)
return name, rawimage, metadata
name = "\n".join(names).strip()
name = name or None
mdata = [pdata.metadata for pdata in fulldata if pdata.metadata]
if oldname == name and not mdata:
return None, None, None
if mdata:
dmdata = {}
for md in mdata:
if md:
try:
dmdata.update(json.loads(md))
except Exception as e:
logger.debug(str(e))
logger.debug(
"lavuelib.liveViewer.LiveViewer.__getNewData "
"update metadata %s: (%s)" % (md, type(md)))
metadata = str(json.dumps(dmdata))
if name:
ldata = [pdata for pdata in fulldata if pdata.name]
if len(ldata) == 1:
name, rawimage, metadata = ldata[0].tolist()[:3]
elif len(ldata) > 1:
pd = ldata[0]
shape = [pd.sx, pd.sy]
dtype = pd.dtype()
scc = pd.scc
while len(shape) < 2:
shape.append(1)
pd.x = pd.x or 0
pd.y = pd.y or 0
shape[0] += pd.x
shape[1] += pd.y
nx = min(0, pd.x)
ny = min(0, pd.y)
for pd in ldata[1:]:
scc = max(scc, pd.scc)
pd.x = pd.x or 0
if pd.y is None:
pd.y = shape[1]
psh = [pd.sx, pd.sy]
while len(psh) < 2:
psh.append(1)
psh[0] += pd.x
psh[1] += pd.y
if dtype != pd.dtype():
dtype = self.__settings.floattype
shape[0] = max(shape[0], psh[0])
shape[1] = max(shape[1], psh[1])
nx = min(nx, pd.x)
ny = min(ny, pd.y)
if channels:
scc = max(len(ldata), scc)
if scc > 1:
nshape = (scc, shape[0] - nx, shape[1] - ny)
else:
nshape = (shape[0] - nx, shape[1] - ny)
if self.__settings.nanmask:
dtype = self.__settings.floattype
rawimage = np.zeros(shape=nshape, dtype=dtype)
if self.__settings.nanmask:
rawimage.fill(np.nan)
for i, pd in enumerate(ldata):
lsh = len(pd.data().shape)
if lsh == 2:
if scc == 1:
rawimage[
pd.x - nx: pd.sx + pd.x - nx,
pd.y - ny: pd.sy + pd.y - ny] = \
pd.data()
else:
rawimage[
i,
pd.x - nx: pd.sx + pd.x - nx,
pd.y - ny: pd.sy + pd.y - ny] = \
pd.data()
else:
if pd.scc == 1:
rawimage[
i: i+1,
pd.x - nx: pd.sx + pd.x - nx,
pd.y - ny: pd.sy + pd.y - ny
] = \
pd.data()
else:
rawimage[
0: pd.scc,
pd.x - nx: pd.sx + pd.x - nx,
pd.y - ny: pd.sy + pd.y - ny] = \
pd.data()
return name, rawimage, metadata
@debugmethod
@QtCore.pyqtSlot(str, str)
def _getNewData(self, name, metadata=None):
""" checks if data is there at all
:param name: image name
:type name: :obj:`str`
:param metadata: JSON dictionary with metadata
:type metadata: :obj:`str`
"""
fulldata = []
states = self.__sourcewg.tabCheckBoxStates()
name = None
for i, df in enumerate(self.__dataFetchers):
if states[i]:
cnt = 0
name = None
while (not df.fetching() and self.__sourcewg.isConnected()
and cnt < 100 and not name):
time.sleep(self.__settings.refreshrate/100.)
cnt += 1
if cnt < 100:
name, rawimage, metadata = \
self.__exchangelists[i].readData()
else:
name, rawimage, metadata = None, None, None
if i < len(self.__translations):
x, y = self.__translations[i]
else:
x, y = None, None
if i < len(self.__transformations):
tr = self.__transformations[i]
else:
tr = ''
logger.debug(
"lavuelib.liveViewer.LiveViewer.__getNewData "
"%s: %s %s %s, [%s, %s]" %
(i, name, metadata,
rawimage.shape if hasattr(rawimage, "shape") else "",
x or "", y or "")
)
fulldata.append(
PartialData(name, rawimage, metadata, x, y, tr))
self.__intmaxvalue = None
if self.__settings.negmask:
imvtb = [fd.intmaxvalue() for fd in fulldata]
imvtb = [vl for vl in imvtb if vl is not None]
if imvtb:
self.__intmaxvalue = np.max(imvtb)
if not self.__sourcewg.isConnected():
return
if len(fulldata) == 1:
name, rawimage, metadata = fulldata[0].tolist()[:3]
else:
name, rawimage, metadata = self.__mergeData(
fulldata, str(self.__imagename).strip(),
self.__settings.imagechannels)
if hasattr(rawimage, "dtype") and str(rawimage.dtype) == 'object' \
and rawimage:
rawimage = "%s string (%s) cannot be read" % (name, rawimage)
name = "__ERROR__"
if str(self.__imagename).strip() == str(name).strip() and not metadata:
for dft in self.__dataFetchers:
dft.ready()
return
if name == "__ERROR__":
if self.__settings.interruptonerror:
if self.__sourcewg.isConnected():
self.__sourcewg.toggleServerConnection()
errortext = rawimage
messageBox.MessageBox.warning(
self, "lavue: Error in reading data",
"Viewing will be interrupted", str(errortext))
else:
self.__sourcewg.setErrorStatus(name)
for dft in self.__dataFetchers:
dft.ready()
return
self.__sourcewg.setErrorStatus("")
if name is None:
for dft in self.__dataFetchers:
dft.ready()
return
# first time:
if str(self.__metadata) != str(metadata) and str(metadata).strip():
imagename, self.__metadata = name, metadata
if str(imagename).strip() and \
not isinstance(rawimage, basestring):
if not hasattr(rawimage, "size") or rawimage.size != 0:
self.__imagename = imagename
self.__rawimage = rawimage
try:
if metadata:
self.__mdata = json.loads(str(metadata))
else:
self.__mdata = {}
if self.__mdata and isinstance(self.__mdata, dict):
resdata = dict((k, v) for (k, v) in self.__mdata.items()
if k in self.__allowedmdata)
wgdata = dict((k, v) for (k, v) in self.__mdata.items()
if k in self.__allowedwgdata)
if wgdata:
self.__imagewg.updateMetaData(**wgdata)
if resdata:
self.__sourcewg.updateMetaData(**resdata)
if self.__settings.geometryfromsource:
self.__settings.updateMetaData(**self.__mdata)
self.__imagewg.updateCenter(
self.__settings.centerx, self.__settings.centery)
self.__imagewg.mouseImagePositionChanged.emit()
self.__imagewg.geometryChanged.emit()
except Exception as e:
logger.warning(str(e))
# print(str(e))
elif str(name).strip():
if self.__imagename is None or str(self.__imagename) != str(name):
self.__imagename, self.__metadata \
= name, metadata
if not isinstance(rawimage, basestring):
if not hasattr(rawimage, "size") or rawimage.size != 0:
self.__rawimage = rawimage
if not str(metadata).strip():
self.__mdata = {}
self.__updateframeview()
self.__currenttime = time.time()
if self.__settings.showframerate and self.__lasttime:
self.__updateframerate(self.__currenttime - self.__lasttime)
self.__lasttime = self.__currenttime
self._plot()
QtCore.QCoreApplication.processEvents()
for dft in self.__dataFetchers:
dft.ready()
# @debugmethod
def __updateframeview(self, status=False, slider=False):
if status and self.__settings.showsteps:
if self.__frame is not None:
self.__ui.frameLineEdit.setText(str(self.__frame))
if slider:
if self.__frame >= 0:
self.__ui.frameHorizontalSlider.setValue(self.__frame)
else:
self.__ui.frameHorizontalSlider.setValue(
self.__ui.frameHorizontalSlider.maximum())
self.__ui.framestepSpinBox.show()
self.__ui.framestepLabel.show()
self.__ui.lowerframePushButton.show()
self.__ui.higherframePushButton.show()
self.__ui.frameLineEdit.show()
else:
# self.__fieldpath = None
self.__ui.lowerframePushButton.hide()
self.__ui.higherframePushButton.hide()
self.__ui.framestepSpinBox.hide()
self.__ui.frameLineEdit.hide()
self.__ui.framestepLabel.hide()
if slider:
self.__ui.frameHorizontalSlider.show()
else:
self.__ui.frameHorizontalSlider.hide()
# @debugmethod
def __updateframerate(self, ratetime):
if ratetime:
fr = 1.0/float(ratetime)
if fr >= 9.9:
self.__ui.framerateLineEdit.setText("%.0f Hz" % fr)
else:
self.__ui.framerateLineEdit.setText("%.1f Hz" % fr)
else:
self.__ui.framerateLineEdit.setText("")
# @debugmethod
def __updateframeratetip(self, ratetime):
if ratetime:
fr = 1.0/float(ratetime)
if fr >= 9.9:
self.__ui.framerateLineEdit.setToolTip(
"Set frame rate: %.0f Hz" % fr)
else:
self.__ui.framerateLineEdit.setToolTip(
"Set frame rate: %.1f Hz" % fr)
else:
self.__ui.framerateLineEdit.setToolTip("Frame rate in Hz")
# @debugmethod
def __prepareImage(self):
"""applies: make image gray, substracke the background image and
apply the mask
"""
if self.__filteredimage is None:
return
ics = 0
if "suminthelast" in self.__mdata.keys():
ics = int(self.__mdata["suminthelast"])
if len(self.__filteredimage.shape) == 3:
self.__channelwg.setNumberOfChannels(
self.__filteredimage.shape[0] - ics)
if not self.__channelwg.colorChannel():
if self.rgb():
self.setrgb(False)
self.__levelswg.showGradient(True)
if ics:
self.__rawgreyimage = self.__filteredimage[-1, :, :]
elif ("skipfirst" in self.__mdata.keys() and
self.__mdata["skipfirst"]):
self.__rawgreyimage = _nansum(
self.__filteredimage[1:, :, :], 0)
else:
self.__rawgreyimage = _nansum(self.__filteredimage, 0)
else:
try:
if len(self.__filteredimage) - ics >= \
self.__channelwg.colorChannel():
self.__rawgreyimage = self.__filteredimage[
self.__channelwg.colorChannel() - 1]
if self.rgb():
self.setrgb(False)
self.__levelswg.showGradient(True)
elif (len(self.__filteredimage) + 1 - ics ==
self.__channelwg.colorChannel()):
if self.rgb():
self.setrgb(False)
self.__levelswg.showGradient(True)
with np.warnings.catch_warnings():
np.warnings.filterwarnings(
'ignore',
r'Mean of empty slice')
if "skipfirst" in self.__mdata.keys() and \
self.__mdata["skipfirst"]:
self.__rawgreyimage = np.nanmean(
self.__filteredimage[1:, :, :], 0)
else:
self.__rawgreyimage = np.nanmean(
self.__filteredimage, 0)
elif self.__filteredimage.shape[0] - ics > 1:
if not self.rgb():
self.setrgb(True)
self.__levelswg.showGradient(False)
self.__rawgreyimage = np.moveaxis(
self.__filteredimage, 0, -1)
rgbs = self.__channelwg.rgbchannels()
if rgbs == (0, 1, 2):
if self.__rawgreyimage.shape[-1] > 3:
self.__rawgreyimage = \
self.__rawgreyimage[:, :, :3]
elif self.__filteredimage.shape[-1] == 2:
nshape = list(self.__rawgreyimage.shape)
nshape[-1] = 1
self.__rawgreyimage = np.concatenate(
(self.__rawgreyimage,
np.zeros(
shape=nshape.
shape[:, :, -1],
dtype=self.__rawgreyimage.dtype)),
axis=2)
else:
zeros = None
nshape = list(self.__rawgreyimage.shape)
nshape[-1] = 1
if -1 in rgbs:
zeros = np.zeros(
shape=nshape,
dtype=self.__rawgreyimage.dtype)
if self.__settings.nanmask and \
self.__rawgreyimage.dtype.kind == 'f':
zeros[:] = np.nan
self.__rawgreyimage = np.concatenate(
(self.__rawgreyimage[:, :, rgbs[0]].
reshape(nshape)
if rgbs[0] != -1 else zeros,
self.__rawgreyimage[:, :, rgbs[1]].
reshape(nshape)
if rgbs[1] != -1 else zeros,
self.__rawgreyimage[:, :, rgbs[2]].
reshape(nshape)
if rgbs[2] != -1 else zeros),
axis=2)
elif self.__filteredimage.shape[0] - ics == 1:
if self.rgb():
self.setrgb(False)
self.__channelwg.showGradient(True)
self.__levelswg.showGradient(True)
self.__rawgreyimage = self.__filteredimage[:, :, 0]
except Exception:
import traceback
value = traceback.format_exc()
text = messageBox.MessageBox.getText(
"lavue: color channel %s does not exist."
" Reset to grey scale"
% self.__channelwg.colorChannel())
messageBox.MessageBox.warning(
self,
"lavue: color channel %s does not exist. "
" Reset to grey scale"
% self.__channelwg.colorChannel(),
text, str(value))
self.__channelwg.setChannel(0)
elif len(self.__filteredimage.shape) == 2:
if self.rgb():
self.setrgb(False)
self.__channelwg.showGradient(True)
self.__levelswg.showGradient(True)
if self.__imagewg.applyMask():
self.__rawgreyimage = np.array(self.__filteredimage)
else:
self.__rawgreyimage = self.__filteredimage
self.__channelwg.setNumberOfChannels(0)
elif len(self.__filteredimage.shape) == 1:
if self.rgb():
self.setrgb(False)
self.__channelwg.showGradient(True)
self.__levelswg.showGradient(True)
self.__rawgreyimage = np.array(
self.__filteredimage).reshape(
(self.__filteredimage.shape[0], 1))
self.__channelwg.setNumberOfChannels(0)
self.__displayimage = self.__rawgreyimage
if self.__dobkgsubtraction and self.__scbackgroundimage is not None:
try:
if (hasattr(self.__rawgreyimage, "dtype") and
self.__rawgreyimage.dtype.name in
self.__unsignedmap.keys()) \
and (hasattr(self.__scbackgroundimage, "dtype") and
self.__scbackgroundimage.dtype.name in
self.__unsignedmap.keys()):
self.__displayimage = np.subtract(
self.__rawgreyimage, self.__scbackgroundimage,
dtype=self.__unsignedmap[
self.__rawgreyimage.dtype.name])
else:
self.__displayimage = \
self.__rawgreyimage - self.__scbackgroundimage
except Exception:
self._checkBkgSubtraction(0)
self.__backgroundimage = None
self.__scbackgroundimage = None
self.__dobkgsubtraction = False
import traceback
value = traceback.format_exc()
text = messageBox.MessageBox.getText(
"lavue: Background image does not match "
"to the current image")
messageBox.MessageBox.warning(
self, "lavue: Background image does not match "
"to the current image",
text, str(value))
if self.__dobfsubtraction and self.__bfmdfimage is not None:
try:
self.__displayimage = self.__displayimage * self.__bfmdfimage
except Exception:
self._checkBFSubtraction(0)
self.__bfmdfimage = None
self.__brightfieldimage = None
self.__scbrightfieldimage = None
self.__dobfsubtraction = False
import traceback
value = traceback.format_exc()
text = messageBox.MessageBox.getText(
"lavue: Bright field image does not match "
"to the current image")
messageBox.MessageBox.warning(
self, "lavue: Bright field image does not match "
"to the current image",
text, str(value))
if self.__settings.showmask and self.__imagewg.applyMask() and \
self.__imagewg.maskIndices() is not None:
# set all masked (non-zero values) to zero by index
try:
if not self.__settings.nanmask:
self.__displayimage = np.array(self.__displayimage)
self.__displayimage[self.__imagewg.maskIndices()] = 0
else:
self.__displayimage = np.array(
self.__displayimage,
dtype=self.__settings.floattype)
self.__displayimage[self.__imagewg.maskIndices()] = np.nan
except IndexError:
self.__maskwg.noImage()
self.__imagewg.setApplyMask(False)
import traceback
value = traceback.format_exc()
text = messageBox.MessageBox.getText(
"lavue: Mask image does not match "
"to the current image")
messageBox.MessageBox.warning(
self, "lavue: Mask image does not match "
"to the current image",
text, str(value))
if self.__settings.showhighvaluemask and \
self.__imagewg.maskValue() is not None:
maskvalue = self.__imagewg.maskValue()
if self.__settings.negmask and \
maskvalue < 0 and self.__intmaxvalue is not None:
maskvalue += self.__intmaxvalue
try:
if self.__settings.nanmask:
self.__displayimage = np.array(
self.__displayimage,
dtype=self.__settings.floattype)
with np.warnings.catch_warnings():
np.warnings.filterwarnings(
'ignore', r'invalid value encountered in greater')
self.__imagewg.setMaskValueIndices(
self.__displayimage > maskvalue)
self.__displayimage[
self.__imagewg.maskValueIndices()] = np.nan
else:
self.__displayimage = np.array(self.__displayimage)
self.__imagewg.setMaskValueIndices(
self.__displayimage > maskvalue)
self.__displayimage[
self.__imagewg.maskValueIndices()] = 0
except IndexError:
# self.__highvaluemaskwg.noValue()
import traceback
value = traceback.format_exc()
text = messageBox.MessageBox.getText(
"lavue: Cannot apply high value mask to the current image")
messageBox.MessageBox.warning(
self, "lavue: Cannot apply high value mask"
" to the current image",
text, str(value))
# @debugmethod
def __transform(self):
""" does the image transformation on the given numpy array.
:returns: crdtranspose, crdleftrightflip, crdupdownflip,
orgtranspose, orgleftrightflip, orgupdownflip flags
:rtype: (:obj:`bool`, :obj:`bool`, :obj:`bool`,:obj:`bool`,
:obj:`bool`, :obj:`bool`)
"""
crdupdownflip = False
crdleftrightflip = False
crdtranspose = False
orgupdownflip = False
orgleftrightflip = False
orgtranspose = False
if self.__trafoname == "none":
pass
elif self.__trafoname == "flip (up-down)":
orgupdownflip = True
if self.__settings.keepcoords:
crdupdownflip = True
elif self.__displayimage is not None:
self.__displayimage = np.fliplr(self.__displayimage)
elif self.__trafoname == "flip (left-right)":
orgleftrightflip = True
if self.__settings.keepcoords:
crdleftrightflip = True
elif self.__displayimage is not None:
self.__displayimage = np.flipud(self.__displayimage)
elif self.__trafoname == "transpose":
orgtranspose = True
if self.__displayimage is not None:
self.__displayimage = np.swapaxes(
self.__displayimage, 0, 1)
# self.__displayimage = np.transpose(self.__displayimage)
if self.__settings.keepcoords:
crdtranspose = True
elif self.__trafoname == "rot90 (clockwise)":
orgtranspose = True
orgupdownflip = True
if self.__settings.keepcoords:
crdtranspose = True
crdupdownflip = True
if self.__displayimage is not None:
self.__displayimage = np.swapaxes(
self.__displayimage, 0, 1)
# self.__displayimage = np.transpose(self.__displayimage)
elif self.__displayimage is not None:
# self.__displayimage = np.transpose(
# np.flipud(self.__displayimage))
self.__displayimage = np.swapaxes(
np.flipud(self.__displayimage), 0, 1)
elif self.__trafoname == "rot180":
orgupdownflip = True
orgleftrightflip = True
if self.__settings.keepcoords:
crdupdownflip = True
crdleftrightflip = True
elif self.__displayimage is not None:
self.__displayimage = np.flipud(
np.fliplr(self.__displayimage))
elif self.__trafoname == "rot270 (clockwise)":
orgtranspose = True
orgleftrightflip = True
if self.__settings.keepcoords:
crdtranspose = True
crdleftrightflip = True
if self.__displayimage is not None:
self.__displayimage = np.swapaxes(
self.__displayimage, 0, 1)
# self.__displayimage = np.transpose(self.__displayimage)
elif self.__displayimage is not None:
self.__displayimage = np.swapaxes(
np.fliplr(self.__displayimage), 0, 1)
# self.__displayimage = np.transpose(
# np.fliplr(self.__displayimage))
elif self.__trafoname == "rot180 + transpose":
orgtranspose = True
orgupdownflip = True
orgleftrightflip = True
if self.__settings.keepcoords:
crdtranspose = True
crdupdownflip = True
crdleftrightflip = True
if self.__displayimage is not None:
# self.__displayimage = np.transpose(self.__displayimage)
self.__displayimage = np.swapaxes(
self.__displayimage, 0, 1)
elif self.__displayimage is not None:
self.__displayimage = np.swapaxes(
np.fliplr(np.flipud(self.__displayimage)), 0, 1)
# self.__displayimage = np.transpose(
# np.fliplr(np.flipud(self.__displayimage)))
return (crdtranspose, crdleftrightflip, crdupdownflip,
orgtranspose, orgleftrightflip, orgupdownflip)
# @debugmethod
def __applyRange(self):
""" applies user range
"""
if self.__settings.showrange and \
self.__filteredimage is not None:
x1, y1, x2, y2 = self.__rangewg.rangeWindow()
position = [None, None]
if x1 is not None or y1 is not None or \
x2 is not None or y2 is not None:
position = self.__setrange(x1, y1, x2, y2)
scale = [None, None]
factor = self.__rangewg.factor()
if factor > 1:
function = self.__rangewg.function()
scale = self.__npresize(factor, function)
if self.__trafoname in ["transpose",
"rot90 (clockwise)",
"rot270 (clockwise)",
"rot180 + transpose"]:
position = [position[1], position[0]]
scale = [scale[1], scale[0]]
# @debugmethod
def __setrange(self, x1, y1, x2, y2):
""" sets window range
:param x1: x1 position
:type x1: :obj:`int`
:param y1: y1 position
:type y1: :obj:`int`
:param x2: x2 position
:type x2: :obj:`int`
:param y2: y2 position
:type y2: :obj:`int`
:returns: x,y - start x,y-position
:rtype: (:obj:`int`, :obj:`int`)
"""
image = None
position = [0, 0]
shape = self.__filteredimage.shape
if len(shape) == 1 and shape[0]:
image = self.__filteredimage[x1:x2]
elif len(shape) == 2:
image = self.__filteredimage[x1:x2, y1:y2]
elif len(shape) == 3:
image = self.__filteredimage[:, x1:x2, y1:y2]
if image is not None and image.size > 0:
self.__filteredimage = image
position = [x1 or 0, y1 or 0]
return position
# @debugmethod
def __npresize(self, factor, function):
""" resizes image
:param factor: down-sampling factor
:type factor: :obj:`int`
:param function: reduction function
:type function: :obj:`str`
:returns: x,y - invert scale
:rtype: (:obj:`int`, :obj:`int`)
"""
shape = self.__filteredimage.shape
scale = [1, 1]
if len(shape) > 1 and factor > 1:
w = shape[-2] // factor
h = shape[-1] // factor
ww = w * factor
hh = h * factor
if ww < factor or hh < factor:
nfactor = max(min(int(shape[-2]), int(shape[-1])), 1)
self.__rangewg.setFactor(nfactor)
factor = 1
w = shape[-2] // factor
h = shape[-1] // factor
ww = w * factor
hh = h * factor
if len(shape) == 2 and factor > 1:
self.__filteredimage = \
getattr(
self.__filteredimage[:ww, :hh].
reshape(w, factor, h, factor),
function)((-1, -3))
elif len(shape) == 3:
self.__filteredimage = \
getattr(
self.__filteredimage[:, :ww, :hh].
reshape(shape[0], w, factor, h, factor),
function)((-1, -3))
scale = [factor, factor]
return scale
@debugmethod
@QtCore.pyqtSlot()
def _resizePlot(self, show=True):
""" resize window and plot
:param show: enable/disable resizing
:type show: :obj:`bool`
"""
x1, y1, x2, y2 = self.__rangewg.rangeWindow()
factor = self.__rangewg.factor()
positionscale = [x1, y1, factor, factor]
if show and (x1 or y1 or factor != 1):
self.__imagewg.updateMetaData(
positionscale,
rescale=True)
else:
self.__imagewg.updateMetaData(
[0, 0, 1, 1],
rescale=False)
self.setLavueState({
"rangewindow": self.__rangewg.rangeWindow(),
"dsfactor": self.__rangewg.factor(),
"dsreduction": str(self.__rangewg.function())})
self._plot()
# @debugmethod
def __applyFilters(self):
""" applies user filters
"""
# self.__filteredimage = self.__rawimage
if self.__filterstate:
for flt in self.__filters:
try:
if self.__filteredimage is not None:
image = flt(
self.__filteredimage,
self.__imagename,
self.__metadata,
self.__imagewg
)
fltmdata = None
if isinstance(image, tuple):
if len(image) >= 2:
image, fltmdata = image[:2]
if len(image) == 1:
image = image
if image is not None and (
hasattr(image, "size") and image.size > 1):
self.__filteredimage = image
if isinstance(fltmdata, dict):
self.__mdata.update(fltmdata)
except Exception as e:
self.__filterswg.setState(0)
import traceback
value = traceback.format_exc()
messageBox.MessageBox.warning(
self, "lavue: problems in applying filters",
"%s" % str(e),
"%s" % value)
# print(str(e))
# @debugmethod
def __scale(self, scalingtype):
""" sets scaletype on the image
:param scalingtype: scaling type
:type scalingtype: :obj:`str`
"""
self.__imagewg.setScalingType(scalingtype)
if self.__displayimage is None:
self.__scaledimage = None
elif scalingtype == "sqrt":
self.__scaledimage = np.clip(self.__displayimage, 0, np.inf)
self.__scaledimage = np.sqrt(self.__scaledimage)
elif scalingtype == "log":
self.__scaledimage = np.clip(self.__displayimage, 10e-3, np.inf)
self.__scaledimage = np.log10(self.__scaledimage)
elif _VMAJOR == '0' and _VMINOR == '9' and int(_VPATCH) > 7:
# (for 0.9.8 <= version < 0.10.0 i.e. ubuntu 16.04)
self.__scaledimage = self.__displayimage.astype(
self.__settings.floattype)
else:
self.__scaledimage = self.__displayimage
# @debugmethod
def __calcStats(self, flag):
""" calcualtes scaled limits for intesity levels
:param flag: (max value, mean value, variance value,
min scaled value, max raw value, max scaled value)
to calculate
:type flag: [:obj:`bool`, :obj:`bool`, :obj:`bool`,
:obj:`bool`, :obj:`bool`, :obj:`bool`]
:returns: max value, mean value, variance value,
min scaled value, max raw value, max scaled value
:rtype: [:obj:`str`, :obj:`str`, :obj:`str`, :obj:`str`,
:obj:`str`, :obj:`str`]
"""
if self.__settings.statswoscaling and self.__displayimage is not None \
and self.__displayimage.size > 0:
with warnings.catch_warnings():
warnings.filterwarnings(
'ignore', r'All-NaN slice encountered')
maxval = np.nanmax(self.__displayimage) if flag[0] else 0.0
meanval = np.nanmean(self.__displayimage) if flag[1] else 0.0
varval = np.nanvar(self.__displayimage) if flag[2] else 0.0
maxsval = np.nanmax(self.__scaledimage) if flag[5] else 0.0
elif (not self.__settings.statswoscaling
and self.__scaledimage is not None
and self.__displayimage.size > 0):
maxval = np.nanmax(self.__scaledimage) \
if flag[0] or flag[5] else 0.0
meanval = np.nanmean(self.__scaledimage) if flag[1] else 0.0
varval = np.nanvar(self.__scaledimage) if flag[2] else 0.0
maxsval = maxval
else:
return 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, None
with warnings.catch_warnings():
warnings.filterwarnings(
'ignore', r'All-NaN slice encountered')
maxrawval = np.nanmax(self.__rawgreyimage) if flag[4] else 0.0
minval = np.nanmin(self.__scaledimage) if flag[3] else 0.0
channels = None
if hasattr(self.__scaledimage, "shape") \
and len(self.__scaledimage.shape) == 3:
with warnings.catch_warnings():
warnings.filterwarnings(
'ignore', r'All-NaN (slice|axis) encountered')
nch = self.__scaledimage.shape[2]
channels = [
[np.nanmin(self.__scaledimage[:, :, i])
if flag[3] else 0.0,
np.nanmax(self.__scaledimage[:, :, i])
if flag[5] else 0.0]
for i in range(nch)]
else:
channels = None
return (maxval, meanval, varval, minval, maxrawval, maxsval, channels)
@debugmethod
@QtCore.pyqtSlot(str)
@QtCore.pyqtSlot(int)
def _checkHighMasking(self, _=''):
""" reads the mask image, select non-zero elements
and store the indices
"""
value = self.__highvaluemaskwg.mask()
try:
self.__imagewg.setMaskValue(float(value))
except Exception:
self.__imagewg.setMaskValue(None)
maskhighvalue = ""
if self.__settings.showhighvaluemask:
maskhighvalue = str(value or "")
self.setLavueState({"maskhighvalue": maskhighvalue})
self._plot()
@debugmethod
@QtCore.pyqtSlot(int)
def _checkMasking(self, state):
""" replots the image with mask if mask exists
"""
self.__imagewg.setApplyMask(state)
if self.__imagewg.applyMask() and self.__maskimage is None:
self.__maskwg.noImage()
maskfile = ""
if self.__maskwg.isMaskApplied() and self.__settings.maskimagename:
maskfile = str(self.__settings.maskimagename)
self.setLavueState({"maskfile": maskfile})
self._plot()
@debugmethod
@QtCore.pyqtSlot(str)
def _prepareMasking(self, imagename):
""" reads the mask image, select non-zero elements
and store the indices
"""
imagename = str(imagename)
if imagename:
if imagename.endswith(".nxs") or imagename.endswith(".h5") \
or imagename.endswith(".nx") or imagename.endswith(".ndf"):
fieldpath = None
growing = 0
frame = 0
handler = imageFileHandler.NexusFieldHandler(
str(imagename))
fields = handler.findImageFields()
if fields:
imgfield = imageField.ImageField(self)
imgfield.fields = fields
imgfield.frame = 0
imgfield.createGUI()
if imgfield.exec_():
fieldpath = imgfield.field
growing = imgfield.growing
frame = imgfield.frame
else:
return
currentfield = fields[fieldpath]
self.__maskimage = np.transpose(handler.getImage(
currentfield["node"],
frame, growing, refresh=False))
else:
return
else:
self.__maskimage = np.transpose(
imageFileHandler.ImageFileHandler(
str(imagename)).getImage())
if self.__settings.zeromask:
self.__imagewg.setMaskIndices(self.__maskimage == 0)
else:
self.__imagewg.setMaskIndices(self.__maskimage != 0)
else:
self.__maskimage = None
# @debugmethod
def __remasking(self):
""" recalculates the mask
"""
if self.__maskimage is not None:
if self.__settings.zeromask:
self.__imagewg.setMaskIndices(self.__maskimage == 0)
else:
self.__imagewg.setMaskIndices(self.__maskimage != 0)
@debugmethod
@QtCore.pyqtSlot(int)
def _checkBkgSubtraction(self, state):
""" replots the image with subtranction if background image exists
:param state: checkbox state
:type state: :obj:`int`
"""
self.__dobkgsubtraction = bool(state)
if self.__dobkgsubtraction and self.__scbackgroundimage is None:
self.__bkgsubwg.setDisplayedName("")
else:
self.__bkgsubwg.checkBkgSubtraction(state)
self.__imagewg.setDoBkgSubtraction(state)
bkgfile = ""
if self.__bkgsubwg.isBkgSubApplied() and \
self.__settings.bkgimagename:
bkgfile = str(self.__settings.bkgimagename)
self.setLavueState({"bkgfile": bkgfile})
self._plot()
@debugmethod
@QtCore.pyqtSlot(int)
def _checkBFSubtraction(self, state):
""" replots the image with subtranction if background image exists
:param state: checkbox state
:type state: :obj:`int`
"""
self.__dobfsubtraction = bool(state)
if self.__dobfsubtraction and self.__scbrightfieldimage is None:
self.__normwg.setDisplayedBFName("")
else:
self.__normwg.checkBFSubtraction(state)
self.__imagewg.setDoBFSubtraction(state)
bffile = ""
if self.__normwg.isBFSubApplied() and \
self.__settings.bfimagename:
bffile = str(self.__settings.bfimagename)
self.setLavueState({"brightfieldfile": bffile})
self._plot()
@debugmethod
@QtCore.pyqtSlot(str)
def _prepareBkgSubtraction(self, imagename):
""" reads the background image
:param imagename: image name
:type imagename: :obj:`str`
"""
imagename = str(imagename)
if imagename:
if imagename.endswith(".nxs") or imagename.endswith(".h5") \
or imagename.endswith(".nx") or imagename.endswith(".ndf"):
fieldpath = None
growing = 0
frame = 0
handler = imageFileHandler.NexusFieldHandler(
str(imagename))
fields = handler.findImageFields()
if fields:
imgfield = imageField.ImageField(self)
imgfield.fields = fields
imgfield.frame = 0
imgfield.createGUI()
if imgfield.exec_():
fieldpath = imgfield.field
growing = imgfield.growing
frame = imgfield.frame
else:
return
currentfield = fields[fieldpath]
self.__backgroundimage = np.transpose(
handler.getImage(
currentfield["node"],
frame, growing, refresh=False))
self._updateBkgScale(False)
else:
return
else:
self.__backgroundimage = np.transpose(
imageFileHandler.ImageFileHandler(
str(imagename)).getImage())
self._updateBkgScale(False)
else:
self.__backgroundimage = None
self.__scbackgroundimage = None
self.__updatebfmdf()
@debugmethod
@QtCore.pyqtSlot(str)
def _prepareBFSubtraction(self, imagename):
""" reads the brightfield image
:param imagename: image name
:type imagename: :obj:`str`
"""
imagename = str(imagename)
if imagename:
if imagename.endswith(".nxs") or imagename.endswith(".h5") \
or imagename.endswith(".nx") or imagename.endswith(".ndf"):
fieldpath = None
growing = 0
frame = 0
handler = imageFileHandler.NexusFieldHandler(
str(imagename))
fields = handler.findImageFields()
if fields:
imgfield = imageField.ImageField(self)
imgfield.fields = fields
imgfield.frame = 0
imgfield.createGUI()
if imgfield.exec_():
fieldpath = imgfield.field
growing = imgfield.growing
frame = imgfield.frame
else:
return
currentfield = fields[fieldpath]
self.__brightfieldimage = np.transpose(
handler.getImage(
currentfield["node"],
frame, growing, refresh=False))
else:
return
else:
self.__brightfieldimage = np.transpose(
imageFileHandler.ImageFileHandler(
str(imagename)).getImage())
self._updateBFScale(False)
else:
self.__brightfield = None
self.__updatebfmdf()
@debugmethod
@QtCore.pyqtSlot()
def _setCurrentImageAsBkg(self):
""" sets the chrrent image as the background image
"""
if self.__rawgreyimage is not None:
self.__backgroundimage = self.__rawgreyimage
self._updateBkgScale(False)
self.__bkgsubwg.setDisplayedName(str(self.__imagename))
else:
self.__bkgsubwg.setDisplayedName("")
@debugmethod
@QtCore.pyqtSlot()
def _updateBkgScale(self, fetchscale=True):
if fetchscale:
self.__backgroundscale = self.__bkgsubwg.bkgScalingFactor()
if self.__backgroundimage is None:
self.__scbackgroundimage = None
elif self.__backgroundscale in [1, None] \
or not self.__settings.showsubsf:
self.__scbackgroundimage = self.__backgroundimage
else:
self.__scbackgroundimage = \
self.__backgroundimage * self.__backgroundscale
self.__updatebfmdf()
@debugmethod
@QtCore.pyqtSlot()
def _updateBFScale(self, fetchscale=True):
if fetchscale:
self.__brightfieldscale = self.__normwg.bfScalingFactor()
if self.__brightfieldimage is None:
self.__scbrightfieldimage = None
elif self.__brightfieldscale in [1, None] \
or not self.__settings.shownormsf:
self.__scbrightfieldimage = self.__brightfieldimage
else:
self.__scbrightfieldimage = \
self.__brightfieldimage * self.__brightfieldscale
self.__updatebfmdf()
@debugmethod
@QtCore.pyqtSlot()
def _setCurrentImageAsBF(self):
""" sets the chrrent image as the brightfield image
"""
if self.__rawgreyimage is not None:
self.__brightfieldimage = self.__rawgreyimage
self._updateBFScale(False)
self.__normwg.setDisplayedBFName(str(self.__imagename))
else:
self.__normwg.setDisplayedBFName("")
def __updatebfmdf(self):
""" sets brightfield - darkfield image
"""
if self.__scbrightfieldimage is not None and \
self.__scbackgroundimage is not None:
try:
with np.errstate(divide='ignore', invalid='ignore'):
self.__bfmdfimage = np.true_divide(
1,
(self.__scbrightfieldimage
- self.__scbackgroundimage),
dtype=self.__settings.floattype)
self.__bfmdfimage[np.isinf(self.__bfmdfimage)] = np.nan
except Exception as e:
logger.warning(str(e))
self._checkBFSubtraction(0)
self.__brightfieldimage = None
self.__scbrightfieldimage = None
self.__dobfsubtraction = False
import traceback
value = traceback.format_exc()
text = messageBox.MessageBox.getText(
"lavue: Bright field image does not match "
"to the current image")
messageBox.MessageBox.warning(
self, "lavue: Bright field image does not match "
"to the current image",
text, str(value))
self.__bfmdfimage = None
elif self.__scbrightfieldimage is not None:
with np.errstate(divide='ignore', invalid='ignore'):
self.__bfmdfimage = np.true_divide(
1, self.__scbrightfieldimage,
dtype=self.__settings.floattype)
self.__bfmdfimage[np.isinf(self.__bfmdfimage)] = np.nan
else:
self.__bfmdfimage = None
@debugmethod
@QtCore.pyqtSlot(bool)
def _assessFilters(self, state):
""" assesses the filter on/off state
:param state: assess filter status
:type state: :obj:`bool`
"""
if self.__filterstate != state:
self.__filterstate = state
for flt in self.__filters:
try:
if state:
if hasattr(flt, "initialize"):
flt.initialize()
else:
if hasattr(flt, "terminate"):
flt.terminate()
except Exception as e:
self.__filterswg.setState(0)
import traceback
value = traceback.format_exc()
messageBox.MessageBox.warning(
self, "lavue: problems in starting or stoping filters",
"%s" % str(e),
"%s" % value)
# print(str(e))
self.setLavueState({"filters": bool(self.__filterstate)})
if self.__displayimage is not None:
self._plot()
@debugmethod
@QtCore.pyqtSlot(str)
def _assessTransformation(self, trafoname):
""" assesses the transformation and replot it
:param imagename: transformation name
:type imagename: :obj:`str`
"""
self.__trafoname = trafoname
if trafoname in [
"transpose", "rot90 (clockwise)",
"rot270 (clockwise)", "rot180 + transpose"]:
self.__trafowg.setKeepCoordsLabel(
self.__settings.keepcoords, True)
else:
self.__trafowg.setKeepCoordsLabel(
self.__settings.keepcoords, False)
self.setLavueState({"transformation": trafoname})
self._plot()
[docs] @debugmethod
def keyPressEvent(self, event):
""" skips escape key action
:param event: close event
:type event: :class:`pyqtgraph.QtCore.QEvent`:
"""
if event.key() != QtCore.Qt.Key_Escape:
QtGui.QDialog.keyPressEvent(self, event)
# else:
# self.closeEvent(None)
[docs] @debugmethod
@QtCore.pyqtSlot(bool)
def setrgb(self, status=True):
""" sets RGB on/off
:param status: True for on and False for off
:type status: :obj:`bool`
"""
self.__levelswg.setrgb(status)
self.__imagewg.setrgb(status)
self._plot()
@debugmethod
@QtCore.pyqtSlot(bool)
def _setRGBState(self, status=True):
""" sets RGB on/off
:param status: True for on and False for off
:type status: :obj:`bool`
"""
self.__levelswg.setrgb(status)
self.__imagewg.setrgb(status)
self.setLavueState({"channel": self.__channelwg.channelLabel()})
self._plot()
[docs] @debugmethod
def rgb(self):
""" gets RGB on/off
:returns: True for on and False for off
:rtype: :obj:`bool`
"""
return self.__imagewg.rgb()