diff options
Diffstat (limited to 'examples/customHdf5TreeModel.py')
-rw-r--r-- | examples/customHdf5TreeModel.py | 290 |
1 files changed, 290 insertions, 0 deletions
diff --git a/examples/customHdf5TreeModel.py b/examples/customHdf5TreeModel.py new file mode 100644 index 0000000..8bd444a --- /dev/null +++ b/examples/customHdf5TreeModel.py @@ -0,0 +1,290 @@ +#!/usr/bin/env python +# 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. +# +# ###########################################################################*/ +"""Qt Hdf5 widget examples +""" + +import logging +import sys +import tempfile +import numpy +import h5py + +logging.basicConfig() +_logger = logging.getLogger("customHdf5TreeModel") +"""Module logger""" + +from silx.gui import qt +import silx.gui.hdf5 +from silx.gui.data.DataViewerFrame import DataViewerFrame +from silx.gui.widgets.ThreadPoolPushButton import ThreadPoolPushButton +from silx.gui.hdf5.Hdf5TreeModel import Hdf5TreeModel + + +class CustomTooltips(qt.QIdentityProxyModel): + """Custom the tooltip of the model by composition. + + It is a very stable way to custom it cause it uses the Qt API. Then it will + not change according to the version of Silx. + + But it is not well integrated if you only want to add custom fields to the + default tooltips. + """ + + def data(self, index, role=qt.Qt.DisplayRole): + if role == qt.Qt.ToolTipRole: + + # Reach information from the node + sourceIndex = self.mapToSource(index) + sourceModel = self.sourceModel() + originalTooltip = sourceModel.data(sourceIndex, qt.Qt.ToolTipRole) + originalH5pyObject = sourceModel.data(sourceIndex, Hdf5TreeModel.H5PY_OBJECT_ROLE) + + # We can filter according to the column + if sourceIndex.column() == Hdf5TreeModel.TYPE_COLUMN: + return super(CustomTooltips, self).data(index, role) + + # Let's create our own tooltips + template = u"""<html> + <dl> + <dt><b>Original</b></dt><dd>{original}</dd> + <dt><b>Parent name</b></dt><dd>{parent_name}</dd> + <dt><b>Name</b></dt><dd>{name}</dd> + <dt><b>Power of 2</b></dt><dd>{pow_of_2}</dd> + </dl> + </html> + """ + + try: + data = originalH5pyObject[()] + if data.size <= 10: + result = data ** 2 + else: + result = "..." + except Exception: + result = "NA" + + info = dict( + original=originalTooltip, + parent_name=originalH5pyObject.parent.name, + name=originalH5pyObject.name, + pow_of_2=str(result) + ) + return template.format(**info) + + return super(CustomTooltips, self).data(index, role) + + +_file_cache = {} + + +def get_hdf5_with_all_types(): + global _file_cache + ID = "alltypes" + if ID in _file_cache: + return _file_cache[ID].name + + tmp = tempfile.NamedTemporaryFile(prefix=ID + "_", suffix=".h5", delete=True) + tmp.file.close() + h5 = h5py.File(tmp.name, "w") + + g = h5.create_group("arrays") + g.create_dataset("scalar", data=10) + g.create_dataset("list", data=numpy.arange(10)) + base_image = numpy.arange(10**2).reshape(10, 10) + images = [base_image, + base_image.T, + base_image.size - 1 - base_image, + base_image.size - 1 - base_image.T] + dtype = images[0].dtype + data = numpy.empty((10 * 10, 10, 10), dtype=dtype) + for i in range(10 * 10): + data[i] = images[i % 4] + data.shape = 10, 10, 10, 10 + g.create_dataset("image", data=data[0, 0]) + g.create_dataset("cube", data=data[0]) + g.create_dataset("hypercube", data=data) + g = h5.create_group("dtypes") + g.create_dataset("int32", data=numpy.int32(10)) + g.create_dataset("int64", data=numpy.int64(10)) + g.create_dataset("float32", data=numpy.float32(10)) + g.create_dataset("float64", data=numpy.float64(10)) + g.create_dataset("string_", data=numpy.string_("Hi!")) + # g.create_dataset("string0",data=numpy.string0("Hi!\x00")) + + g.create_dataset("bool", data=True) + g.create_dataset("bool2", data=False) + h5.close() + + _file_cache[ID] = tmp + return tmp.name + + +class Hdf5TreeViewExample(qt.QMainWindow): + """ + This window show an example of use of a Hdf5TreeView. + + The tree is initialized with a list of filenames. A panel allow to play + with internal property configuration of the widget, and a text screen + allow to display events. + """ + + def __init__(self, filenames=None): + """ + :param files_: List of HDF5 or Spec files (pathes or + :class:`silx.io.spech5.SpecH5` or :class:`h5py.File` + instances) + """ + qt.QMainWindow.__init__(self) + self.setWindowTitle("Silx HDF5 widget example") + + self.__asyncload = False + self.__treeview = silx.gui.hdf5.Hdf5TreeView(self) + """Silx HDF5 TreeView""" + + self.__sourceModel = self.__treeview.model() + """Store the source model""" + + self.__text = qt.QTextEdit(self) + """Widget displaying information""" + + self.__dataViewer = DataViewerFrame(self) + vSpliter = qt.QSplitter(qt.Qt.Vertical) + vSpliter.addWidget(self.__dataViewer) + vSpliter.addWidget(self.__text) + 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.addWidget(self.createTreeViewConfigurationPanel(self, self.__treeview)) + layout.setStretchFactor(spliter, 1) + main_panel.setLayout(layout) + + self.setCentralWidget(main_panel) + + # append all files to the tree + for file_name in filenames: + self.__treeview.findHdf5TreeModel().appendFile(file_name) + + self.__treeview.activated.connect(self.displayData) + + def displayData(self): + """Called to update the dataviewer with the selected data. + """ + selected = list(self.__treeview.selectedH5Nodes()) + if len(selected) == 1: + # Update the viewer for a single selection + data = selected[0] + # data is a hdf5.H5Node object + # data.h5py_object is a Group/Dataset object (from h5py, spech5, fabioh5) + # The dataviewer can display both + self.__dataViewer.setData(data) + + def __fileCreated(self, filename): + if self.__asyncload: + self.__treeview.findHdf5TreeModel().insertFileAsync(filename) + else: + self.__treeview.findHdf5TreeModel().insertFile(filename) + + def __hdf5ComboChanged(self, index): + function = self.__hdf5Combo.itemData(index) + self.__createHdf5Button.setCallable(function) + + def __edfComboChanged(self, index): + function = self.__edfCombo.itemData(index) + self.__createEdfButton.setCallable(function) + + def __useCustomLabel(self): + customModel = CustomTooltips(self.__treeview) + customModel.setSourceModel(self.__sourceModel) + self.__treeview.setModel(customModel) + + def __useOriginalModel(self): + self.__treeview.setModel(self.__sourceModel) + + def createTreeViewConfigurationPanel(self, parent, treeview): + """Create a configuration panel to allow to play with widget states""" + panel = qt.QWidget(parent) + panel.setLayout(qt.QHBoxLayout()) + + content = qt.QGroupBox("Create HDF5", panel) + content.setLayout(qt.QVBoxLayout()) + panel.layout().addWidget(content) + + combo = qt.QComboBox() + combo.addItem("Containing all types", get_hdf5_with_all_types) + combo.activated.connect(self.__hdf5ComboChanged) + content.layout().addWidget(combo) + + button = ThreadPoolPushButton(content, text="Create") + button.setCallable(combo.itemData(combo.currentIndex())) + button.succeeded.connect(self.__fileCreated) + content.layout().addWidget(button) + + self.__hdf5Combo = combo + self.__createHdf5Button = button + + content.layout().addStretch(1) + + option = qt.QGroupBox("Custom model", panel) + option.setLayout(qt.QVBoxLayout()) + panel.layout().addWidget(option) + + button = qt.QPushButton("Original model") + button.clicked.connect(self.__useOriginalModel) + option.layout().addWidget(button) + + button = qt.QPushButton("Custom tooltips by composition") + button.clicked.connect(self.__useCustomLabel) + option.layout().addWidget(button) + + option.layout().addStretch(1) + + panel.layout().addStretch(1) + return panel + + +def main(filenames): + """ + :param filenames: list of file paths + """ + app = qt.QApplication([]) + sys.excepthook = qt.exceptionHandler + window = Hdf5TreeViewExample(filenames) + window.show() + result = app.exec_() + # remove ending warnings relative to QTimer + app.deleteLater() + sys.exit(result) + + +if __name__ == "__main__": + main(sys.argv[1:]) |