#/*##########################################################################
# Copyright (C) 20016-2017 European Synchrotron Radiation Facility
#
# This file is part of tomogui. Interface for tomography developed at
# the ESRF by the Software group.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
#############################################################################*/
__author__ = ["H. Payno"]
__license__ = "MIT"
__date__ = "24/05/2016"
from silx.gui import qt
import os
import silx
import sys
import tempfile
import numpy
import tomogui
import tomogui.gui
import tomogui.utils as utils
from tomogui.configuration import config as tomoguiconfig
from tomogui.gui.datasource.QDataSourceWidget import QDataSourceWidget
from tomogui.gui.normalization.NormWidget import NormWidget
from tomogui.gui.reconsparam.ReconsParamWidget import ReconsParamWidget
from tomogui.gui.reconstruction import ReconsManager
from tomogui.gui.utils import guiutils
from tomogui.gui.utils import icons as tomoguiIcons
from tomogui.gui.utils.QFileManagement import ConfigurationMenu, QGiveFilePathOrSave
from tomogui.gui.utils.QFileManagement import H5_FILTER, EDF_FILTER
from tomogui.third_party.dialog.ImageFileDialog import ImageFileDialog
from tomogui.gui.datasource.tomoguiFileTypeCB import TomoGUIFileTypeCB
from tomogui.gui.utils.QFileManagement import _getDefaultFolder
import tomogui.core
try:
import freeart
from freeart.configuration import config as freeartconfig
from freeart.configuration import read, save, fileInfo, structs
from freeart.configuration.config import retrieveInfoFile
from freeart.utils.reconstrutils import decreaseMatSize
from tomogui.gui.datasource.QDataSourceWidget import QDataSourceFluoWidget
from tomogui.gui.materials.QSampleComposition import SampleCompositionTab
hasfreeart = True
except ImportError:
hasfreeart = False
from tomogui.third_party.configuration import config as freeartconfig
from tomogui.third_party.configuration import read, save, fileInfo, structs
from tomogui.third_party.configuration.config import retrieveInfoFile
from tomogui.third_party.edfutils import decreaseMatSize
import logging
_logger = logging.getLogger(__name__)
[docs]class ProjectWindow(qt.QMainWindow):
"""
This is the main window to deal with a freeART reconsparam
:param parent: the parent of the widget in the Qt hierarchy
"""
def __init__(self, parent=None, cfgFile=None):
qt.QMainWindow.__init__(self, parent)
# menu bar
self.menuBar = qt.QMenuBar()
self.projectMenu = ConfigurationMenu(self.menuBar, fileCFGPath=cfgFile)
# status bar
self.statusBar().showMessage("welcome to freeart gui", 20000)
# main widget
self.mainWidget = MainWidget(self, self.statusBar())
self.setCentralWidget(self.mainWidget)
self._connectMenuBar()
# tool bar
self._buildToolBar()
# load data if some existing
if cfgFile is not None:
self.setConfFile(cfgFile)
# window properties
self.show()
[docs] def clean(self):
self.mainWidget.clean()
[docs] def setConfFile(self, cfgFile):
assert(os.path.isfile)
assert(ConfigurationMenu.isAValidConfigFile(cfgFile))
self.projectMenu.setFileCFGPath(filePath=cfgFile, merge=False)
self.mainWidget.loadConfigurationFromFile(cfgFile)
self.saveAction.setEnabled(True)
def _buildToolBar(self):
"""
Build the toolbar
"""
self.toolbar = qt.QToolBar('ToolBar')
self.saveAction = self.projectMenu._menuBar.saveAction
self.saveAsAction = self.projectMenu._menuBar.saveAsAction
self.loadAction = self.projectMenu._menuBar.loadAction
self.exitAction = qt.QAction('Exit', self)
self.exitAction.setIcon(tomoguiIcons.getQIcon('exit'))
self.exitAction.setShortcut(qt.QKeySequence.Quit)
self.exitAction.triggered.connect(self.close)
self.toolbar.addAction(self.loadAction)
self.toolbar.addAction(self.saveAction)
self.toolbar.addAction(self.saveAsAction)
self.toolbar.addSeparator()
self.toolbar.addAction(self.exitAction)
self.addToolBar(qt.Qt.TopToolBarArea, self.toolbar)
self.setWindowTitle('FreeART reconsparam configuration')
self.show()
# By default save is unvailable. Need a signal
# ( from the Configuration Menu to be activated )
self._updateSaveAction(False)
self.projectMenu.sigSaveAvailable.connect(self._updateSaveAction)
def _updateSaveAction(self, enable):
"""
Deal with the enable param of the saveAction
"""
self.saveAction.setEnabled(enable)
if enable is True:
self.saveAction.setShortcut(qt.QKeySequence.Save)
self.saveAsAction.setShortcut(qt.QKeySequence())
else:
self.saveAction.setShortcut(qt.QKeySequence())
self.saveAsAction.setShortcut(qt.QKeySequence.Save)
def _connectMenuBar(self):
"""
Connect the menu bar (project save/load) with the main widget
"""
self.projectMenu.sigFileLoading.connect(
self.mainWidget._loadConfiguration)
self.projectMenu.sigFileSaving.connect(
self.mainWidget._saveConfiguration)
[docs] def filterOtherThan(self, id):
"""
set the widget on for the given reconsparam type.
:param id:
:return:
"""
self.mainWidget._tabWidget.dataSourceWidget.setReconstructionType(id)
self.setWindowTitle(id)
self.mainWidget._tabWidget.dataSourceWidget.qcbReconstructionType.hide()
[docs] def setReconsFluoMode(self, opt):
"""
Set the reconsparam from fluo to be made from albsorption files
directly or from I0 and It
:param opt: the reconsparam we want. Should be in
QDataSourceFluoWidget.RECONS_MODE
:return:
"""
assert opt in QDataSourceFluoWidget.RECONS_MODE
self.mainWidget.getDataSourceWidget().qdsFluo.setFluoReconsMode(opt)
[docs] def setSinoToRecons(self, reconsType, sinograms, names):
self.mainWidget.setSinoToRecons(reconsType, sinograms, names)
[docs] def setIt(self, it, name=None):
self.mainWidget.setIt(it, name=name)
[docs] def setI0(self, i0, name=None):
self.mainWidget.setI0(i0, name=name)
[docs] def setLogRecons(self, isLogRecons):
"""Set reconstruction mode for transmission reconstruction"""
self.mainWidget.setLogRecons(isLogRecons)
class _MainTabWidget(qt.QTabWidget):
"""
This is the tab widget used for the freeART reconsparam
Tabs are :
- dataSource : input for the file to treat
- normalization : inputs used for the normalization
(as center of mass definition)
- reconsparam parameters : the parameters of the reconsparam such
as oversampling, number of iterations...
"""
def __init__(self, parent=None):
"""
Constructor
:param parent: the parent of the widget in the Qt hierarchy
"""
qt.QTabWidget.__init__(self, parent)
self.dataSourceWidget = QDataSourceWidget(self)
self.normWidget = NormWidget(self)
self.reconsParamWidget = ReconsParamWidget(self)
dataSources = [self.dataSourceWidget.qdsTx]
if hasfreeart is True:
self.materialsWidget = SampleCompositionTab(self)
self.materialsWidget.isDisplayed = False
self.normWidget.centeringWidget.groupRotationWidget.sigCenterchanged.connect(
self.setMaterialsWidgetVisibility
)
self.dataSourceWidget.qdsFluo.sigRequireMaterials.connect(self.setMaterialsWidgetVisibility)
self.reconsParamWidget.sigInfoForMaterialsChanged.connect(
self.setMaterialsWidgetVisibility
)
dataSources.append(self.dataSourceWidget.qdsFluo)
self.dataSourceWidget.qdsFluo.sigReconsModeChanged.connect(self._updateMatInfo)
else:
self.materialsWidget = None
# is selected
for dataSource in dataSources:
dataSource.sigSinoRefChanged.connect(
self.normWidget.centeringWidget.updateData)
dataSource.sigSinoRefChanged.connect(
self.reconsParamWidget._projectionSelector.setSinogram)
dataSource.sigI0MightChanged.connect(
self.normWidget.I0Normalization.setI0)
dataSource.sigSinoRefDimChanged.connect(
self.reconsParamWidget._sinogramDim_haschanged)
# type of reconsparam
self.dataSourceWidget.sigReconstructionTypeChanged.connect(
self.reconsParamWidget._setReconstructionType)
self.dataSourceWidget.sigReconstructionTypeChanged.connect(
self.normWidget.reconstructionTypeChanged)
self.dataSourceWidget.sigReconstructionTypeChanged.connect(
self._reconstructionTypeChanged)
# tab 0 : data source
self.setTabToolTip(self.addTab(self.dataSourceWidget, "data source"),
self.dataSourceWidget.getShortToolTip())
# tab 1 : centering
self.setTabToolTip(self.addTab(self.normWidget, "normalization"),
self.normWidget.getShortToolTip())
# tab 2 : reconsparam
self._reconsParamScrollArea = qt.QScrollArea(parent=self)
self._reconsParamScrollArea.setWidget(self.reconsParamWidget)
self._reconsParamScrollArea.setWidgetResizable(True)
self.setTabToolTip(self.addTab(self._reconsParamScrollArea, "reconstruction parameters"),
self.reconsParamWidget.getShortToolTip())
self.setMaterialsWidgetVisibility(True)
# will at least emit a signal to update other widgets
self.dataSourceWidget._reconsTypeChanged()
def _reconstructionTypeChanged(self, reconsType):
if reconsType != freeartconfig._ReconsConfig.FLUO_ID:
self.setMaterialsWidgetVisibility(False)
def setMaterialsWidgetVisibility(self, visible):
if hasfreeart is False:
return
if visible is True and self.materialsWidget.isDisplayed is False:
self.dataSourceWidget.qdsFluo.sigInfoForMaterialsChanged.connect(
self._updateMatInfo
)
self.materialsWidget.isDisplayed = True
self.setTabToolTip(
self.addTab(self.materialsWidget, "sample composition"),
self.materialsWidget.getShortToolTip())
if visible is False and self.materialsWidget.isDisplayed is True:
self.removeTab(3)
self.dataSourceWidget.qdsFluo.sigInfoForMaterialsChanged.disconnect(
self._updateMatInfo
)
self.materialsWidget.isDisplayed = False
def _updateMatInfo(self):
""" this function is used to update information about materials
information if there is a self abs or a fluorescence sinogram to deduce
the backgroundimage"""
if self.materialsWidget.isDisplayed is True:
self.materialsWidget.mainWindow.setAbsMat(
self.dataSourceWidget.qdsFluo.getAbsMat())
self.materialsWidget.mainWindow.setIsAbsMatASino(
self.dataSourceWidget.qdsFluo.isAbsMatASino())
self.materialsWidget.mainWindow.setFluoSinograms(
self.dataSourceWidget.qdsFluo.getSinograms())
self.materialsWidget.mainWindow.setReconsParam(
center=self.normWidget.centeringWidget.groupRotationWidget.getCenter(),
minAngle=self.reconsParamWidget.getMinAngle(),
maxAngle=self.reconsParamWidget.getMaxAngle(),
projections=self.reconsParamWidget.getProjections(),
)
[docs]class MainWidget(qt.QWidget):
"""
This is the main widget for a freeART reconsparam.
:param parent: the parent of the widget in the Qt hierarchy
"""
def __init__(self, parent=None, statusBar=None):
qt.QWidget.__init__(self, parent)
self._parentStatusBar = statusBar
layout = qt.QVBoxLayout()
# add the tabWidget
self._tabWidget = _MainTabWidget(self)
layout.addWidget(self._tabWidget)
# add the buttons to move between tabs
self._qgbbuttons = qt.QGroupBox(self)
layoutButtons = qt.QHBoxLayout()
self._qbPreviousTab = qt.QPushButton("previous")
self._qbPreviousTab.setIcon(tomoguiIcons.getQIcon('arrow_left'))
self._qbPreviousTab.setAutoDefault(True)
self._qbPreviousTab.clicked.connect(self._previousTabCallBack)
layoutButtons.addWidget(self._qbPreviousTab)
self._qbNextTab = qt.QPushButton("next")
self._qbNextTab.setIcon(tomoguiIcons.getQIcon('arrow_right'))
self._qbNextTab.setAutoDefault(True)
self._qbNextTab.clicked.connect(self._nextTabCallBack)
layoutButtons.addWidget(self._qbNextTab)
self._updateNextAndPreviousEnable()
self._tabWidget.currentChanged.connect(
self._updateNextAndPreviousEnable)
spacer = qt.QWidget()
spacer.setSizePolicy(qt.QSizePolicy.Expanding,
qt.QSizePolicy.Minimum)
layoutButtons.addWidget(spacer)
self._qbReconstruct = qt.QPushButton("Run reconstruction")
self._qbReconstruct.setToolTip(
"Start the reconsparam from the given parameters")
self._qbReconstruct.setIcon(tomoguiIcons.getQIcon('run'))
self._qbReconstruct.setAutoDefault(True)
f = self._qbReconstruct.font()
f.setBold(True)
f.setPointSize(12)
self._qbReconstruct.setFont(f)
self._qbReconstruct.clicked.connect(self._reconstruct)
layoutButtons.addWidget(self._qbReconstruct)
self._qgbbuttons.setFlat(True)
self._qgbbuttons.setLayout(layoutButtons)
layout.addWidget(self._qgbbuttons)
self.setLayout(layout)
[docs] def clean(self):
self.getDataSourceWidget().clean()
self.getNormalizationWidget().clean()
if self._tabWidget.materialsWidget is not None:
self._tabWidget.materialsWidget.clean()
[docs] def setSinoToRecons(self, reconsType, sinogramsData, names=None):
"""
Set the GUI to run the reconstruction of the given sinograms in the
given mode.
.. note:: for Tx and FBP reconstruction we can now only deal with one
reconstruction. Multiple sinogram reconstruction is only
managed for fluorescence and compton.
:param str reconsType: the type of reconstruction to run
:param list sinogramsData: list of numpy arrays to reconstruct. Numpy
nd arrays should be 2D.
:param names: list of the names of the sinograms
"""
def getFirstSinogram():
if len(sinogramsData) > 0:
return sinogramsData[0]
return None
def convertNdArayToFluoSino(arrays):
res = []
for iArray, array in enumerate(arrays):
sino = structs.FluoSino(name='',
fileInfo=None,
physElmt=None,
ef=1.0,
selfAbsMat=None,
data=decreaseMatSize(array))
name = sino.h5defaultPath
if names is not None:
name = names[iArray]
sino.name = name
res.append(sino)
return res
def convertNdArayToTxSino(arrays):
res = []
for iArray, array in enumerate(arrays):
sino = structs.TxSinogram(name=names[iArray],
data=decreaseMatSize(array))
name = sino.h5defaultPath + '_' + str(iArray)
if names is not None:
name=names[iArray]
sino.name = name
res.append(sino)
return res
if len(sinogramsData) is 0:
_logger.info('list of sinogram is empty')
return
for sino in sinogramsData:
assert isinstance(sino, numpy.ndarray)
assert sino.ndim in (2, 3)
if names:
assert(type(names) in (list, tuple))
if len(names) != len(sinogramsData):
raise ValueError('names and sinogramsData have different length')
else:
names = []
for i in range(len(sinogramsData)):
names.append('sinogram_' + str(i))
conf = None
if reconsType == tomoguiconfig.FBPConfig.FBP_ID:
conf = tomoguiconfig.FBPConfig()
conf.sinograms = convertNdArayToTxSino(sinogramsData)
conf.dampingFactor = 1.0/float(conf.sinograms[0].data.shape[0])
elif hasfreeart is True:
if reconsType == freeartconfig._ReconsConfig.TX_ID:
conf = freeartconfig.TxConfig()
conf.sinograms = convertNdArayToTxSino(sinogramsData)
conf.dampingFactor = 1.0 / float(conf.sinograms[0].data.shape[0])
elif reconsType in (freeartconfig._ReconsConfig.FLUO_ID,
freeartconfig._ReconsConfig.COMPTON_ID):
conf = freeartconfig.FluoConfig()
conf.reconsType = reconsType
conf.sinograms = convertNdArayToFluoSino(sinogramsData)
conf.dampingFactor = 1.0 / float(decreaseMatSize(getFirstSinogram()).shape[0])
if conf is None:
raise ValueError('Requested reconstruction type is not recognized')
self.loadConfiguration(conf)
[docs] def setIt(self, it, name=None):
_it = structs.Sinogram(data=it,
name=name if name is not None else 'ItSinogram')
self.getDataSourceWidget().qdsFluo.setIt(_it)
[docs] def setI0(self, i0, name=None):
assert isinstance(i0, numpy.ndarray)
_i0 = structs.I0Sinogram(data=i0,
name=name if name is not None else 'I0Sinogram')
self.getNormalizationWidget().I0Normalization.setI0(_i0)
[docs] def setLogRecons(self, isLog):
self.getNormalizationWidget().I0Normalization._computeMinusLog.setChecked(isLog)
[docs] def getReconsParamWidget(self):
return self._tabWidget.reconsParamWidget
[docs] def getNormalizationWidget(self):
return self._tabWidget.normWidget
[docs] def getDataSourceWidget(self):
return self._tabWidget.dataSourceWidget
[docs] def getMaterialsWidget(self):
return self._tabWidget.materialsWidget
def _updateNextAndPreviousEnable(self):
"""
Deal with enable state for buttons next and previous
"""
if self._tabWidget.currentIndex() == self._tabWidget.count() - 1:
self._qbNextTab.setEnabled(False)
else:
self._qbNextTab.setEnabled(True)
if self._tabWidget.currentIndex() == 0:
self._qbPreviousTab.setEnabled(False)
else:
self._qbPreviousTab.setEnabled(True)
def _nextTabCallBack(self):
"""
Function called when the \'next\' button is preset
"""
if self._tabWidget.currentIndex() < self._tabWidget.count() - 1:
self._tabWidget.setCurrentIndex(self._tabWidget.currentIndex() + 1)
self._updateNextAndPreviousEnable()
def _previousTabCallBack(self):
"""
Function called when the \'previous\' button is preset
"""
if self._tabWidget.currentIndex() > 0:
self._tabWidget.setCurrentIndex(self._tabWidget.currentIndex() - 1)
self._updateNextAndPreviousEnable()
def _saveConfiguration(self, event):
"""
Callback function used when the user will press the saveConfiguration
button.
This will create a file containing every information needed to run a
freeart reconsparam.
:param event: the event received. Must be \'savingProject\' event
"""
assert(event['event'] == "savingProject")
if self._parentStatusBar:
self._parentStatusBar.clearMessage()
self._parentStatusBar.showMessage("saving project...", 1000)
self.saveConfiguration(event['filePath'], event['merge'])
[docs] def saveConfiguration(self, filePath, merge):
"""
Save the current configuration to the given file paths
"""
conf = self.getConfiguration(filePath)
self._checkUnsavedData(conf, filePath)
save(configuration=conf, filePath=filePath, overwrite=True, merge=merge)
if self._parentStatusBar:
self._parentStatusBar.clearMessage()
self._parentStatusBar.showMessage("project saved", 1000)
def _checkUnsavedData(self, conf, filePath):
# check if some missing information according to the configuration file
# If some missing ask to save data or give path to those data
if filePath is not None:
fn, file_extension = os.path.splitext(filePath)
if file_extension.lower() in ('.h5', '.hdf', '.hdf5'):
# Im this case data with missing fileInfo will be saved in the
# config file
return
if hasattr(conf, 'sinograms') and conf.sinograms is not None:
for sinogram in conf.sinograms:
assert isinstance(sinogram, structs.Sinogram)
if sinogram.data is not None and sinogram.fileInfo is None:
sinogram.fileInfo = self._askUserToSetInfoFile(sinogram)
def _askUserToSetInfoFile(self, data):
"""
Ask to the user where is the file associated to this data or create a data set
:param data:
"""
def getTitle():
if isinstance(data, structs.Sinogram):
return 'Request file information for sinogram %s' % data.name
else:
raise NotImplementedError('')
def getInfo():
if isinstance(data, structs.Sinogram):
return 'Some data are not associated to any file information. \n' \
'In order to be able to retrieve all information please : \n' \
' - give the path to the sinogram %s \n' \
' - save the sinogram to a new file' % data.name
else:
raise NotImplementedError('')
def getUnsaveWarning():
if isinstance(data, structs.Sinogram):
return 'Information concerning sinogram %s will not be saved' \
'So those data won\'t be loaded next time you load this' \
'configuration file' % data.name
else:
raise NotImplementedError('')
assert isinstance(data, structs.DataStored)
assert data.data is not None
assert data.fileInfo is None
dialog = QGiveFilePathOrSave(title=getTitle(),
info=getInfo())
if not dialog.exec_():
_logger.warning(getUnsaveWarning())
return None
if dialog.result() == QGiveFilePathOrSave.SAVE_VAL:
dialog = qt.QFileDialog(self)
dialog.setAcceptMode(qt.QFileDialog.AcceptSave)
dialog.setFileMode(qt.QFileDialog.AnyFile)
dialog.setNameFilters([EDF_FILTER,
H5_FILTER])
if not dialog.exec_():
dialog.close()
return None
outputFile = dialog.selectedFiles()[0]
fn, file_extension = os.path.splitext(outputFile)
if file_extension == '':
if dialog.selectedNameFilter() == EDF_FILTER:
outputFile += '.edf'
file_extension = '.edf'
elif dialog.selectedNameFilter() == H5_FILTER:
outputFile += '.h5'
file_extension = '.h5'
if file_extension.lower().endswith('.edf'):
index = 0
else:
index = data.h5defaultPath
data.fileInfo = retrieveInfoFile(str(outputFile), index)
data.save()
return data
else:
assert dialog.result() == QGiveFilePathOrSave.GIVE_VAL
dialogOpen = ImageFileDialog(self)
dialogOpen.setFileType(TomoGUIFileTypeCB(dialog))
dialogOpen.setDirectory(_getDefaultFolder())
result = dialogOpen.exec_()
if not result:
return
filePath = dialog.selectedFile()
index = dialog.selectedDataUrl().data_path() or '0'
data.fileInfo = fileInfo.MatrixFileInfo(file_path=filePath,
data_path=index)
return data
def _getConf(self):
if self.getReconstructionType() is tomoguiconfig.FBPConfig.FBP_ID:
conf = tomoguiconfig.FBPConfig()
elif hasfreeart is True:
if self.getReconstructionType() in (freeartconfig._ReconsConfig.COMPTON_ID,
freeartconfig._ReconsConfig.FLUO_ID):
conf = freeartconfig.FluoConfig()
elif self.getReconstructionType() == freeartconfig._ReconsConfig.TX_ID:
conf = freeartconfig.TxConfig()
else:
raise ValueError('reconsparam type not recognized')
else:
raise NotImplementedError('reconsparam type not managed')
conf.reconsType = self.getReconstructionType()
conf.softReconsVersion = {}
if self.getReconstructionType() is tomoguiconfig.FBPConfig.FBP_ID:
conf.softReconsVersion['silx'] = silx.version
else:
conf.softReconsVersion['freeart'] = freeart.version
conf.softReconsVersion['tomogui'] = tomogui.version
return conf
[docs] def getConfiguration(self, configFile):
"""Save the current configuration to the required configFile"""
if self.getReconstructionType() is tomoguiconfig.FBPConfig.FBP_ID:
conf = tomoguiconfig.FBPConfig()
elif hasfreeart is True:
if self.getReconstructionType() in (freeartconfig._ReconsConfig.COMPTON_ID,
freeartconfig._ReconsConfig.FLUO_ID):
conf = freeartconfig.FluoConfig()
elif self.getReconstructionType() == freeartconfig._ReconsConfig.TX_ID:
conf = freeartconfig.TxConfig()
else:
raise ValueError('reconsparam type not recognized')
else:
raise NotImplementedError('reconsparam type not managed')
conf.reconsType = self.getReconstructionType()
conf.softReconsVersion = {}
if self.getReconstructionType() is tomoguiconfig.FBPConfig.FBP_ID:
conf.softReconsVersion['silx'] = silx.version
else:
conf.softReconsVersion['freeart'] = freeart.version
conf.softReconsVersion['tomogui'] = tomogui.version
self.getDataSourceWidget().saveConfiguration(conf, refFile=configFile)
self.getNormalizationWidget().saveConfiguration(conf)
self._tabWidget.reconsParamWidget.saveConfiguration(conf)
if hasfreeart is True and self._tabWidget.materialsWidget.isDisplayed is True:
self._tabWidget.materialsWidget.saveConfiguration(conf, configFile)
return conf
[docs] def setReconstructionType(self, reconsType):
self.getDataSourceWidget().setReconstructionType(reconsType)
[docs] def loadConfigurationFromFile(self, filePath):
"""
Update the widget fron a configuraton file
:param filePath: The path of the configuration file.
Warning: This must be a '.cfg' file
"""
try:
config = read(filePath)
self.loadConfiguration(config)
except OSError as e:
_logger.error(e)
[docs] def loadConfiguration(self, config):
# bad hack : we are looking for the reconsparam type.
# Silx configparser as the freeart configparser are able to get it
# since it is the same structure used for this option
if config is None:
return
self.setReconstructionType(config.reconsType)
self.getDataSourceWidget().loadConfiguration(config)
self.getNormalizationWidget().loadConfiguration(config)
self._tabWidget.reconsParamWidget.loadConfiguration(config)
if hasfreeart is True:
self._tabWidget.materialsWidget.loadConfiguration(config)
def _loadConfiguration(self, event):
"""
Callback function for the \'loadingProject event'\.
:param event: The event received. Must be a \'loadingProject\' event
"""
if self._parentStatusBar:
self._parentStatusBar.clearMessage()
self._parentStatusBar.showMessage("loading project...", 5000)
assert(event['event'] == "loadingProject")
filePath = event['filePath']
self.loadConfigurationFromFile(filePath)
if self._parentStatusBar:
self._parentStatusBar.clearMessage()
self._parentStatusBar.showMessage("load completed", 5000)
[docs] def getReconstructionType(self):
return self.getDataSourceWidget().getReconstructionType()
def _reconstruct(self):
"""
Callback of the reconstruct event
"""
tempDir = tempfile.mkdtemp()
cfgFile = tempDir + "tomogui_to_freeart.cfg"
_logger.info('saving configuration into a temporary file at %s' % cfgFile)
tomogui.core._active_file = cfgFile
config = self.getConfiguration(cfgFile)
if isinstance(config.I0, structs.Sinogram):
config.I0.loadData()
if config.reconsType in (freeartconfig._ReconsConfig.COMPTON_ID,
freeartconfig._ReconsConfig.FLUO_ID):
config.absMat.loadData()
for sino in self.getConfiguration(cfgFile).sinograms:
sino.loadData()
if guiutils.checkCanReconstructDialog(self.getConfiguration(cfgFile)):
self.saveConfiguration(cfgFile, merge=False)
plaformId, deviceId = self.getReconsParamWidget().getOpenCLDevice()
ReconsManager.runReconstruction(self.getConfiguration(cfgFile),
platformId=plaformId,
deviceId=deviceId)
if __name__ == "__main__":
"""
TODO
"""
global app # QApplication must be global to avoid seg fault on quit
app = qt.QApplication([])
splash = utils.getMainSplashScreen()
app.processEvents()
cfgFile = None
if len(sys.argv) > 1 and sys.argv[1].endswith(".cfg"):
cfgFile = sys.argv[1]
mainWindow = ProjectWindow(cfgFile=cfgFile)
mainWindow.show()
splash.finish(mainWindow)
app.exec_()