# coding: utf-8 # /*########################################################################## # Copyright (C) 2016-2017 European Synchrotron Radiation Facility # # 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. # # ############################################################################*/ """Browse a data file with a GUI""" __authors__ = ["V. Valls"] __license__ = "MIT" __date__ = "02/10/2017" import sys import os import argparse import logging import collections _logger = logging.getLogger(__name__) """Module logger""" from silx.gui import qt class Viewer(qt.QMainWindow): """ This window allows to browse a data file like images or HDF5 and it's content. """ def __init__(self): """ :param files_: List of HDF5 or Spec files (pathes or :class:`silx.io.spech5.SpecH5` or :class:`h5py.File` instances) """ # Import it here to be sure to use the right logging level import silx.gui.hdf5 from silx.gui.data.DataViewerFrame import DataViewerFrame qt.QMainWindow.__init__(self) self.setWindowTitle("Silx viewer") self.__asyncload = False self.__dialogState = None self.__treeview = silx.gui.hdf5.Hdf5TreeView(self) """Silx HDF5 TreeView""" self.__dataViewer = DataViewerFrame(self) vSpliter = qt.QSplitter(qt.Qt.Vertical) vSpliter.addWidget(self.__dataViewer) vSpliter.setSizes([10, 0]) spliter = qt.QSplitter(self) spliter.addWidget(self.__treeview) spliter.addWidget(vSpliter) spliter.setStretchFactor(1, 1) main_panel = qt.QWidget(self) layout = qt.QVBoxLayout() layout.addWidget(spliter) layout.setStretchFactor(spliter, 1) main_panel.setLayout(layout) self.setCentralWidget(main_panel) model = self.__treeview.selectionModel() model.selectionChanged.connect(self.displayData) self.__treeview.addContextMenuCallback(self.closeAndSyncCustomContextMenu) treeModel = self.__treeview.findHdf5TreeModel() columns = list(treeModel.COLUMN_IDS) columns.remove(treeModel.DESCRIPTION_COLUMN) columns.remove(treeModel.NODE_COLUMN) self.__treeview.header().setSections(columns) self.createActions() self.createMenus() def createActions(self): action = qt.QAction("E&xit", self) action.setShortcuts(qt.QKeySequence.Quit) action.setStatusTip("Exit the application") action.triggered.connect(self.close) self._exitAction = action action = qt.QAction("&Open", self) action.setStatusTip("Open a file") action.triggered.connect(self.open) self._openAction = action action = qt.QAction("&About", self) action.setStatusTip("Show the application's About box") action.triggered.connect(self.about) self._aboutAction = action def createMenus(self): fileMenu = self.menuBar().addMenu("&File") fileMenu.addAction(self._openAction) fileMenu.addSeparator() fileMenu.addAction(self._exitAction) helpMenu = self.menuBar().addMenu("&Help") helpMenu.addAction(self._aboutAction) def open(self): dialog = self.createFileDialog() if self.__dialogState is None: currentDirectory = os.getcwd() dialog.setDirectory(currentDirectory) else: dialog.restoreState(self.__dialogState) result = dialog.exec_() if not result: return self.__dialogState = dialog.saveState() filenames = dialog.selectedFiles() for filename in filenames: self.appendFile(filename) def createFileDialog(self): dialog = qt.QFileDialog(self) dialog.setWindowTitle("Open") dialog.setModal(True) extensions = collections.OrderedDict() # expect h5py extensions["HDF5 files"] = "*.h5 *.hdf" extensions["NeXus files"] = "*.nx *.nxs *.h5 *.hdf" # no dependancy extensions["NeXus layout from spec files"] = "*.dat *.spec *.mca" extensions["Numpy binary files"] = "*.npz *.npy" # expect fabio extensions["NeXus layout from raster images"] = "*.edf *.tif *.tiff *.cbf *.mccd" extensions["NeXus layout from EDF files"] = "*.edf" extensions["NeXus layout from TIFF image files"] = "*.tif *.tiff" extensions["NeXus layout from CBF files"] = "*.cbf" extensions["NeXus layout from MarCCD image files"] = "*.mccd" filters = [] filters.append("All supported files (%s)" % " ".join(extensions.values())) for name, extension in extensions.items(): filters.append("%s (%s)" % (name, extension)) filters.append("All files (*)") dialog.setNameFilters(filters) dialog.setFileMode(qt.QFileDialog.ExistingFiles) return dialog def about(self): from . import qtutils qtutils.About.about(self, "Silx viewer") def appendFile(self, filename): self.__treeview.findHdf5TreeModel().appendFile(filename) def displayData(self): """Called to update the dataviewer with the selected data. """ selected = list(self.__treeview.selectedH5Nodes(ignoreBrokenLinks=False)) if len(selected) == 1: # Update the viewer for a single selection data = selected[0] self.__dataViewer.setData(data) def useAsyncLoad(self, useAsync): self.__asyncload = useAsync def closeAndSyncCustomContextMenu(self, event): """Called to populate the context menu :param silx.gui.hdf5.Hdf5ContextMenuEvent event: Event containing expected information to populate the context menu """ selectedObjects = event.source().selectedH5Nodes(ignoreBrokenLinks=False) menu = event.menu() if len(menu.children()): menu.addSeparator() # Import it here to be sure to use the right logging level import h5py for obj in selectedObjects: if obj.ntype is h5py.File: action = qt.QAction("Remove %s" % obj.local_filename, event.source()) action.triggered.connect(lambda: self.__treeview.findHdf5TreeModel().removeH5pyObject(obj.h5py_object)) menu.addAction(action) action = qt.QAction("Synchronize %s" % obj.local_filename, event.source()) action.triggered.connect(lambda: self.__treeview.findHdf5TreeModel().synchronizeH5pyObject(obj.h5py_object)) menu.addAction(action) def main(argv): """ Main function to launch the viewer as an application :param argv: Command line arguments :returns: exit status """ parser = argparse.ArgumentParser(description=__doc__) parser.add_argument( 'files', nargs=argparse.ZERO_OR_MORE, help='Data file to show (h5 file, edf files, spec files)') parser.add_argument( '--debug', dest="debug", action="store_true", default=False, help='Set logging system in debug mode') parser.add_argument( '--use-opengl-plot', dest="use_opengl_plot", action="store_true", default=False, help='Use OpenGL for plots (instead of matplotlib)') options = parser.parse_args(argv[1:]) if options.debug: logging.root.setLevel(logging.DEBUG) # # Import most of the things here to be sure to use the right logging level # try: # it should be loaded before h5py import hdf5plugin # noqa except ImportError: _logger.debug("Backtrace", exc_info=True) hdf5plugin = None try: import h5py except ImportError: _logger.debug("Backtrace", exc_info=True) h5py = None if h5py is None: message = "Module 'h5py' is not installed but is mandatory."\ + " You can install it using \"pip install h5py\"." _logger.error(message) return -1 if hdf5plugin is None: message = "Module 'hdf5plugin' is not installed. It supports some hdf5"\ + " compressions. You can install it using \"pip install hdf5plugin\"." _logger.warning(message) # # Run the application # if options.use_opengl_plot: from silx.gui.plot import PlotWidget PlotWidget.setDefaultBackend("opengl") app = qt.QApplication([]) qt.QLocale.setDefault(qt.QLocale.c()) sys.excepthook = qt.exceptionHandler window = Viewer() window.resize(qt.QSize(640, 480)) for filename in options.files: try: window.appendFile(filename) except IOError as e: _logger.error(e.args[0]) _logger.debug("Backtrace", exc_info=True) window.show() result = app.exec_() # remove ending warnings relative to QTimer app.deleteLater() return result