Example #1
0
 def _setupUi(self, withBtn):
     self.setObjectName("DataList")
     self.setAcceptDrops(True)
     self.verticalLayout = QVBoxLayout(self)
     self.verticalLayout.setObjectName("verticalLayout")
     if withBtn:
         self.loadBtn = QPushButton(self)
         self.loadBtn.setText(tr("load"))
         self.loadBtn.setObjectName("loadBtn")
         self.loadBtn.released.connect(self.loadData)
         self.verticalLayout.addWidget(self.loadBtn)
     self.listWidget = QTreeWidget(self)
     self.listWidget.setHeaderHidden(True)
     self.listWidget.setContextMenuPolicy(Qt.ActionsContextMenu)
     self.listWidget.setEditTriggers(QAbstractItemView.NoEditTriggers)
     self.listWidget.setDragEnabled(True)
     self.listWidget.setDragDropMode(QAbstractItemView.InternalMove)
     self.listWidget.setSelectionMode(QAbstractItemView.ExtendedSelection)
     self.listWidget.setObjectName("listWidget")
     self.listWidget.itemSelectionChanged.connect(self.selectionChanged)
     self.listWidget.itemClicked.connect(self._itemClicked)
     self.listWidget.itemChanged.connect(self._itemChanged)
     self.listWidget.itemDoubleClicked.connect(self.itemDoubleClicked)
     self.listWidget.itemExpanded.connect(self.fitColumnsToContents)
     self.listWidget.itemCollapsed.connect(self.fitColumnsToContents)
     self.verticalLayout.addWidget(self.listWidget)
     self.sigReceivedUrls.connect(self.loadData)
     self.clearSelection = self.listWidget.clearSelection
Example #2
0
    def setupUi(self, *args):
        # called in MainWindowBase.__init__()
        # put the log widget at the bottom
        self.addDockWidget(Qt.BottomDockWidgetArea, self._setupLogWidget())
        # file widget at the top
        self.toolbox = ToolBox(self)
        self._addToolboxItem(self._setupFileWidget())
        self._addToolboxItem(self._setupDataWidget())
        self._addToolboxItem(self._setupOptimWidget())
        self._addToolboxItem(self._setupModelWidget())
        self._addToolboxItem(self._setupStatsWidget())

        # set up central widget of the main window
        self.centralLayout = QVBoxLayout()
        # put buttons in central widget
        self.centralLayout.addWidget(self.toolbox)
        self.centralLayout.addWidget(self._setupStartButton())
        centralWidget = QWidget(self)
        centralWidget.setLayout(self.centralLayout)
        centralWidget.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Maximum)
        self.setCentralWidget(centralWidget)
        self.onStartupSignal.connect(self.initUi)
        # set program icon, same for Win+Lin
        icopath = "resources/icon/mcsas.ico"
        if isMac():
            icopath = "resources/icon/mcsas.icns"
        icopath = QFileInfo(makeAbsolutePath(icopath)).absoluteFilePath()
        self.setWindowIcon(QIcon(icopath))
Example #3
0
class NavViewWidget(QWidget):
    def __init__(self, map_topic = '/map', 
                 paths = ['/move_base/SBPLLatticePlanner/plan', '/move_base/TrajectoryPlannerROS/local_plan'], 
                 polygons= ['/move_base/local_costmap/robot_footprint']):
        super(NavViewWidget, self).__init__()
        self._layout = QVBoxLayout()

        self.setWindowTitle('Navigation Viewer')
        self._nav_view = NavView(map_topic, paths, polygons)
        self._layout.addWidget(self._nav_view)

        self.setLayout(self._layout)
Example #4
0
 def _createEntries(self):
     entryWidget = QWidget(self)
     entryLayout = QHBoxLayout(entryWidget)
     inputWidgets = (self._createParamBox(), self._createAutoRange(),
                     self._createLower(), self._createUpper(),
                     self._createBins(), self._createXScale(),
                     self._createYWeight())
     self._labels = dict()
     # assumes same ordering of entryWidgets above and Histogram.displayData
     for col, inputWidget in zip(Histogram.displayData, inputWidgets):
         fieldWidget = QWidget(self)  # combines label + input
         fieldLayout = QVBoxLayout(fieldWidget)
         fieldLayout.setContentsMargins(QMargins())
         # create label, text is set in _selectParam()
         self._labels[col] = QLabel(self)
         self._labels[col].setAlignment(Qt.AlignHCenter)
         # stack label + input
         fieldLayout.addWidget(self._labels[col])
         fieldLayout.addWidget(inputWidget)
         fieldWidget.setLayout(fieldLayout)
         # add field to row of inputs
         entryLayout.addWidget(fieldWidget)
     entryWidget.setLayout(entryLayout)
     self.pbox.setCurrentIndex(0)
     self.lentry.selectAll()  # select the first input by default
     return entryWidget
Example #5
0
    def __init__(self, parent=None):
        super(MatDataPlot, self).__init__(parent)
        self._canvas = MatDataPlot.Canvas()
        vbox = QVBoxLayout()
        vbox.addWidget(self._canvas)
        self.setLayout(vbox)

        self._color_index = 0
        self._curves = {}

        self.keep_secs = 30
        self.scale_steps = 150
        self.ref_name = 'RTK'
        self.tgt_name = 'FHWA2 Combined'
Example #6
0
    def __init__(self, parent, calculator, *args):
        super(ModelWidget, self).__init__(parent, None, *args)
        self._calculator = calculator
        self.title = TitleHandler.setup(self, "Model")
        # get all loadable ScatteringModels from model directory
        self._models = FindModels()

        layout = QVBoxLayout(self)
        layout.setObjectName("modelLayout")
        self.setLayout(layout)

        self.modelBox = QComboBox(self)
        self.modelBox.setFixedWidth(FIXEDWIDTH)
        layout.addWidget(self.modelBox)
        self.modelWidget = QWidget(self)
        paramLayout = QVBoxLayout(self.modelWidget)
        self.modelWidget.setLayout(paramLayout)
        layout.addWidget(self.modelWidget)
Example #7
0
 def __init__(self, parent, appSettings):
     super(DataWidget, self).__init__(parent)
     self.title = TitleHandler.setup(self, "Data Settings")
     self.appSettings = appSettings  # forwarded to SettingsGridWidget below
     self._widgets = []
     self._restored = set()
     # basic row oriented layout
     hlayout = QVBoxLayout(self)
     hlayout.setObjectName("baseLayout")
     hlayout.setContentsMargins(0, 0, 0, 0)
Example #8
0
 def __init__(self, parent=None, model=None):
     QDialog.__init__(self, parent)
     assert isinstance(model, ScatteringModel)
     self._model = model
     self.setObjectName("AddRangeDialog")
     self.setWindowTitle("Add Range")
     self.setWindowModality(Qt.WindowModal)
     vlayout = QVBoxLayout(self)
     vlayout.setObjectName("vlayout")
     vlayout.addWidget(self._createEntries())
     vlayout.addWidget(self._createButtons())
     self.setLayout(vlayout)
Example #9
0
class MainWindow(MainWindowBase):
    onCloseSignal = Signal()
    _args = None  # python command line arguments parser
    _calculator = None  # calculator calling algorithm on all data

    def __init__(self, parent=None, args=None):
        # calls setupUi() and restoreSettings()
        MainWindowBase.__init__(self, version, parent)
        self._args = args

    @property
    def calculator(self):
        """Returns a calculator object."""
        if self._calculator is None:
            self._calculator = Calculator()
        return self._calculator

    def setupUi(self, *args):
        # called in MainWindowBase.__init__()
        # put the log widget at the bottom
        self.addDockWidget(Qt.BottomDockWidgetArea, self._setupLogWidget())
        # file widget at the top
        self.toolbox = ToolBox(self)
        self._addToolboxItem(self._setupFileWidget())
        self._addToolboxItem(self._setupDataWidget())
        self._addToolboxItem(self._setupOptimWidget())
        self._addToolboxItem(self._setupModelWidget())
        self._addToolboxItem(self._setupStatsWidget())

        # set up central widget of the main window
        self.centralLayout = QVBoxLayout()
        # put buttons in central widget
        self.centralLayout.addWidget(self.toolbox)
        self.centralLayout.addWidget(self._setupStartButton())
        centralWidget = QWidget(self)
        centralWidget.setLayout(self.centralLayout)
        centralWidget.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Maximum)
        self.setCentralWidget(centralWidget)
        self.onStartupSignal.connect(self.initUi)
        # set program icon, same for Win+Lin
        icopath = "resources/icon/mcsas.ico"
        if isMac():
            icopath = "resources/icon/mcsas.icns"
        icopath = QFileInfo(makeAbsolutePath(icopath)).absoluteFilePath()
        self.setWindowIcon(QIcon(icopath))

    def _addToolboxItem(self, widget):
        self.toolbox.addItem(
            widget, "{n}. {t}".format(n=self.toolbox.count() + 1,
                                      t=widget.title()))
        try:
            widget.layout().setContentsMargins(0, 0, 0, 0)
        except:
            pass

    def _setupFileWidget(self):
        # set up file widget
        fileWidget = FileList(self,
                              title="Data Files",
                              withBtn=False,
                              nestedItems=False)
        fileWidget.setToolTip(
            "Right-click to add datafiles.\n" +
            "Double click to use the estimated size for the model.")
        self.fileWidget = fileWidget
        return fileWidget

    def _setupDataWidget(self):
        """Set up property widget with settings."""
        self.dataWidget = DataWidget(self, self.appSettings)
        self.dataWidget.sigConfig.connect(self.fileWidget.setDataConfig)
        self.fileWidget.sigSelectedData.connect(self.dataWidget.onDataSelected)
        self.fileWidget.sigEmpty.connect(self.dataWidget.onEmptyDataList)
        return self.dataWidget

    def _setupOptimWidget(self):
        """Set up property widget with settings."""
        self.optimWidget = OptimizationWidget(self, self.calculator.algo,
                                              self.appSettings)
        self.fileWidget.sigSelectedData.connect(
            self.optimWidget.onDataSelected)
        return self.optimWidget

    def _setupModelWidget(self):
        """Set up property widget with settings."""
        self.modelWidget = ModelWidget(self, self.calculator, self.appSettings)
        self.fileWidget.sigSphericalSizeRange.connect(
            self._onSphericalSizeRange)
        self.fileWidget.sigSelectedData.connect(
            self.modelWidget.onDataSelected)
        return self.modelWidget

    def _onSphericalSizeRange(self, *args):
        if self.modelWidget.setSphericalSizeRange(*args):
            self.toolbox.setCurrentWidget(self.modelWidget)

    def _setupStatsWidget(self):
        """Set up property widget with settings."""
        # setup similar to the file widget
        self.statsWidget = RangeList(parent=self,
                                     calculator=self.calculator,
                                     appSettings=self.appSettings,
                                     title="Post-fit Analysis",
                                     withBtn=False,
                                     nestedItems=False)
        self.modelWidget.setStatsWidget(self.statsWidget)
        self.modelWidget.sigBackendUpdated.connect(
            self.statsWidget.updateHistograms)
        return self.statsWidget

    def _setupLogWidget(self):
        """Set up widget for logging output."""
        logDock = DockWidget(self, LogWidget, appversion=version)
        logWidget = logDock.child
        self.onCloseSignal.connect(logWidget.onCloseSlot)
        logWidget.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding)
        logWidget.append(INFOTEXT)
        if len(CHANGESTEXT):
            logWidget.append(CHANGESTEXT)
        logWidget.append("\n\r")
        self.logWidget = logWidget
        return logDock

    def _setupStartButton(self):
        """Set up "Start/Stop" - button."""
        self.startStopBtn = QPushButton()
        self.startStopBtn.setCheckable(True)
        self.startStopBtn.clicked[bool].connect(self.onStartStopClick)
        btnLayout = QHBoxLayout()
        btnLayout.setContentsMargins(0, 0, 0, 0)
        self.startStopBtn.setSizePolicy(QSizePolicy.Maximum,
                                        QSizePolicy.Maximum)
        btnLayout.addWidget(self.startStopBtn)
        btnWidget = QWidget(self)
        btnWidget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Maximum)
        btnWidget.setLayout(btnLayout)
        return btnWidget

    def restoreSettings(self):
        MainWindowBase.restoreSettings(self)
        for settingsWidget in (self.optimWidget, self.modelWidget):
            # no self.dataWidget, it is restored on demand internally
            settingsWidget.restoreSession()
        if self.appSettings is None:
            return
        try:
            value = str(self.appSettings.value("lastpath").toString())
        except AttributeError:  # QVariant
            value = str(self.appSettings.value("lastpath"))
        if os.path.isdir(value):
            LastPath.set(value)

    def storeSettings(self):
        MainWindowBase.storeSettings(self)
        for settingsWidget in (self.optimWidget, self.modelWidget,
                               self.dataWidget):
            settingsWidget.storeSession()
        if self.appSettings is not None:
            self.appSettings.setValue("lastpath", LastPath.get())
            self.appSettings.sync()
        return
        # test for additionally storing settings to file
        tempSettings = QSettings("/tmp/qsettings.test", QSettings.IniFormat)
        for key in self.appSettings.allKeys():
            if key in ('geometry', 'windowState', 'lastpath'):
                continue
            tempSettings.setValue(key, self.appSettings.value(key))
        tempSettings.sync()

    def fileDialog(self):
        filenames = getOpenFiles(self,
                                 "Load one or more data files",
                                 LastPath.get(),
                                 multiple=True)
        self.loadFiles(filenames)

    def initUi(self):
        self.logWidget.scrollToTop()
        self.fileWidget.loadData(getattr(self._args, "fnames", []))
        self.onStartStopClick(getattr(self._args, "start", False))

    def _updateWidgetsFinally(self):
        for w in self.findChildren(AlgorithmWidget):
            w.updateAll()
        if (not len(self.statsWidget.data())
                and len(self.modelWidget.model.activeParams())):
            # make sure there is an histogram range defined,
            # otherwise ask the user for one
            self.toolbox.setCurrentWidget(self.statsWidget)
            self.statsWidget.loadData()

    def onStartStopClick(self, checked):
        processEventLoop()
        if checked:
            # # write HDF datafile
            # self.hdfStore("test3.h5")
            self.startStopBtn.setText("stop")
            self.startStopBtn.setChecked(True)
            self._updateWidgetsFinally(
            )  # get latest input in case sth didn't update
            self.calc()
        # run this also for 'start' after calculation
        self.calculator.stop()
        self.startStopBtn.setText("start")
        self.startStopBtn.setChecked(False)

    # def hdfWrite(self, hdf):
    #     hdf.writeMember(self, "calculator")

    def calc(self):
        if len(self.fileWidget) <= 0:
            return
        self.logWidget.clear()
        self.logWidget.scrollToBottom()
        idx, data = self.fileWidget.currentSelection()
        # process the selected data only if there is a selection
        selectedOnly = data is not None
        self.calculator.prepare()
        self.fileWidget.updateData(updateFunc=self.calculator,
                                   stopFunc=self.calculator.isStopped,
                                   selectedOnly=selectedOnly,
                                   showProgress=False)
        self.calculator.postProcess()

    def closeEvent(self, closeEvent):
        super(MainWindow, self).closeEvent(closeEvent)
        self.onStartStopClick(False)
        self.onCloseSignal.emit()

    def keyPressEvent(self, keyEvent):
        if keyEvent.key() == Qt.Key_Escape and self.startStopBtn.isChecked():
            self.onStartStopClick(False)  # hit 'stop'
            logging.info("Calculation aborted by user interupt!")
Example #10
0
class DataList(QWidget, DropWidget, ContextMenuWidget):
    """
    Manages all loaded spectra.

    >>> from utilsgui import DialogInteraction, DisplayException
    >>> from spectralist import SpectraList
    >>> sl = DialogInteraction.instance(SpectraList)

    Test available actions
    >>> [str(action.text()) for action in sl.listWidget.actions()]
    ['load spectra', 'remove', '', 'save matrices', 'select all']
    >>> sl.listWidget.count()
    0

    Test methods on empty list
    >>> sl.updateSpectra()
    >>> sl.removeSelectedSpectra()
    >>> [sl.getMatrix(i) for i in -1,0,1]
    [None, None, None]
    >>> DialogInteraction.query(DisplayException, sl.saveMatrix,
    ...                         slot = 'accept')
    >>> sl.selectionChangedSlot()

    """
    sigSelectedData = Signal((object,))
    sigUpdatedData  = Signal((object,))
    sigRemovedData  = Signal(list)
    sigEmpty        = Signal()
    sigReceivedUrls = Signal(list)
    sigEditingFinished = Signal()
    _nestedItems = None # are nested items allowed? (plain list behaviour)

    def __init__(self, parent = None, title = None, withBtn = True,
                 nestedItems = True):
        QWidget.__init__(self, parent)
        ContextMenuWidget.__init__(self)
        self.title = TitleHandler.setup(self, title)
        self._nestedItems = nestedItems
        self._setupUi(withBtn)
        self._setupActions()
        self.setupUi()
        QMetaObject.connectSlotsByName(self)

    def _setupUi(self, withBtn):
        self.setObjectName("DataList")
        self.setAcceptDrops(True)
        self.verticalLayout = QVBoxLayout(self)
        self.verticalLayout.setObjectName("verticalLayout")
        if withBtn:
            self.loadBtn = QPushButton(self)
            self.loadBtn.setText(tr("load"))
            self.loadBtn.setObjectName("loadBtn")
            self.loadBtn.released.connect(self.loadData)
            self.verticalLayout.addWidget(self.loadBtn)
        self.listWidget = QTreeWidget(self)
        self.listWidget.setHeaderHidden(True)
        self.listWidget.setContextMenuPolicy(Qt.ActionsContextMenu)
        self.listWidget.setEditTriggers(QAbstractItemView.NoEditTriggers)
        self.listWidget.setDragEnabled(True)
        self.listWidget.setDragDropMode(QAbstractItemView.InternalMove)
        self.listWidget.setSelectionMode(QAbstractItemView.ExtendedSelection)
        self.listWidget.setObjectName("listWidget")
        self.listWidget.itemSelectionChanged.connect(self.selectionChanged)
        self.listWidget.itemClicked.connect(self._itemClicked)
        self.listWidget.itemChanged.connect(self._itemChanged)
        self.listWidget.itemDoubleClicked.connect(self.itemDoubleClicked)
        self.listWidget.itemExpanded.connect(self.fitColumnsToContents)
        self.listWidget.itemCollapsed.connect(self.fitColumnsToContents)
        self.verticalLayout.addWidget(self.listWidget)
        self.sigReceivedUrls.connect(self.loadData)
        self.clearSelection = self.listWidget.clearSelection

    def _setupActions(self):
        self.addMenuEntry(
            name = "load", text = tr("load %1"), menuStates = "*",
            toolTip = tr("Add one or more %1."),
            callbacks = self.loadData)
        self.addMenuSeparator()
        self.addMenuEntry(
            name = "remove", text = tr("remove"),
            toolTip = tr("Remove selected %1."),
            shortCut = QKeySequence.Delete, menuStates = "isRemovableSelected",
            callbacks = self.removeSelected)
        self.addMenuSeparator("hasSelection")
        self.addMenuSeparator("isNotEmpty")
        self.addMenuEntry(
            name = "selectall", text = tr("select all"),
            shortCut = QKeySequence.SelectAll, menuStates = "isNotEmpty",
            callbacks = self.selectAll)
        self.addMenuEntry(
            name = "expandall", text = tr("expand all"),
            toolTip = tr("Show nested items of this %1. (double-click)"),
            callbacks = self.expandAll,
            menuStates = "itemsHaveChildren")
        self._updateContextMenu()

    def _updateContextMenu(self):
        self.updateMenu(self.listWidget)

    def fitColumnsToContents(self, *args):
        for c in range(0, self.listWidget.columnCount()):
            self.listWidget.resizeColumnToContents(c)

    def setupUi(self):
        """Reimplement this in child classes for custom UI configuration."""
        pass

    def leaveEvent(self, event):
        self.sigEditingFinished.emit()

    def clear(self):
        self.listWidget.clear()

    def selectAll(self):
        """Selects all items in the list if not all are selected.
        Clears the selection if all items in the list already are selected.
        """
        if (old_div(len(self.listWidget.selectedIndexes()), self.listWidget.columnCount())) == len(self):
            self.listWidget.clearSelection()
        else:
            self.listWidget.selectAll()

    def expandAll(self):
        self.listWidget.expandAll()
        self.fitColumnsToContents()

    def __len__(self):
        return self.listWidget.topLevelItemCount()

    def isEmpty(self):
        return len(self) <= 0

    def isNotEmpty(self):
        return not self.isEmpty()

    def hasSelection(self):
        return len(self.listWidget.selectedItems()) > 0

    def isRemovableSelected(self):
        """True, if there is at least one item selected which may be removed"""
        return self.hasSelection()

    def itemsHaveChildren(self):
        return any([item.childCount() > 0 for item in self.topLevelItems()])

    def setHeader(self, labels = None):
        if not isList(labels):
            return
        self.listWidget.setHeaderLabels(labels)
        self.listWidget.setHeaderHidden(False)
        self.listWidget.setColumnCount(len(labels))

    def updateItems(self):
        for item in self.topLevelItems():
            item.update()
        self.fitColumnsToContents()

    def _itemClicked(self, item, column):
        item.setClicked(column)
        if item.wasClickedAndChanged():
            self.itemUpdate(item, column)

    def _itemChanged(self, item, column):
        item.setChanged(column)
        if item.wasClickedAndChanged():
            self.itemUpdate(item, column)

    def itemUpdate(self, item, column):
        """Reimplement to update item if changed by user in GUI"""
        pass

    def itemDoubleClicked(self, item, column):
        pass

    def currentSelection(self):
        selected = self.listWidget.selectedItems()
        index, data = -1, None
        if len(selected) > 0:
            index = selected[0].listIndex()
            data = selected[0].data()
        return index, data

    def selectionChanged(self):
        index, data = self.currentSelection()
        self.sigSelectedData.emit(data)
        self._updateContextMenu()
        if self.isEmpty():
            self.sigEmpty.emit()

    def removeItems(self, indexList):
        """Deletes items specified in the given list of indices."""
        if not isList(indexList) or not len(indexList):
            return
        removedItems = []
        for i in reversed(sorted(indexList)):
            item = self.listWidget.topLevelItem(i)
            if not item.isRemovable:
                continue
            item.remove()
            removedItems.append(item.data())
        self.sigRemovedData.emit(removedItems)
        self.selectionChanged()

    def removeSelected(self):
        selected = self.listWidget.selectedItems()
        index = 0
        self.sigRemovedData.emit(self.data(selected))
        for item in selected:
            if not item.isRemovable:
                continue
            index = item.listIndex()
            item.remove()
        # select the next item after the removed ones
        self.listWidget.clearSelection()
        if index >= len(self):
            index = len(self) - 1
        if index >= 0:
            self.listWidget.setCurrentIndex(
                    self.listWidget.indexFromItem(
                        self.listWidget.topLevelItem(index)))
        self.selectionChanged()

    def setCurrentIndex(self, index):
        if index < 0 or index >= len(self):
            return
        item = self.listWidget.topLevelItem(index)
        index = self.listWidget.indexFromItem(item)
        self.listWidget.setCurrentIndex(index)
        self.selectionChanged()

    def add(self, data):
        if self.isEmpty():
            self.setHeader(data.displayDataDescr)
        DataItem(data) # WTF? w/o it returns QTreeWidgetItem instead of DataItem below!
        self.listWidget.addTopLevelItem(DataItem(data))
        item = self.listWidget.topLevelItem(len(self)-1)
        if not self._nestedItems:
            item.setFlags(int(item.flags()) - int(Qt.ItemIsDropEnabled))
        return item

    def topLevelItems(self):
        return [self.listWidget.topLevelItem(i)
                for i in range(0, len(self))]

    def data(self, indexOrItem = None, selectedOnly = False):
        """
        Returns the list of data for a given list index or list widget item.
        If none is specified return the data of all items or the data of
        selected items only, if desired.
        """
        if type(indexOrItem) is int:
            if indexOrItem < 0 or indexOrItem >= len(self):
                raise IndexError
            items = [self.listWidget.topLevelItem(indexOrItem)]
        elif type(indexOrItem) is DataItem:
            items = [indexOrItem]
        elif (isList(indexOrItem) and
              len(indexOrItem) > 0 and
              type(indexOrItem[0]) is DataItem):
            items = indexOrItem
        else: # no indexOrItem given
            if selectedOnly:
                items = self.listWidget.selectedItems()
            else:
                items = self.topLevelItems()
        return [item.data() for item in items]

    def updateData(self, selectedOnly = False, showProgress = True,
                   updateFunc = None, prepareFunc = None, stopFunc = None,
                   **kwargs):
        """
        Calls the provided function on all data items.

        The object returned by prepareFunc() is forwarded as optional argument
        to updateFunc(dataItem, optionalArguments = None).
        """
        data = self.data(selectedOnly = selectedOnly)
        if data is None or len(data) <= 0:
            self.sigUpdatedData.emit(None)
            return
        progress = None
        if showProgress:
            from gui.utils.progressdialog import ProgressDialog
            progress = ProgressDialog(self, count = len(data))
        updateResult = []
        # check provided stop function
        if (stopFunc is not None and
            not isinstance(stopFunc, collections.Callable)):
            stopFunc = None
        # call provided functions which can raise exceptions
        errorOccured = False # raise error after processing all items
        try:
            # call prepare function
            prepareResult = None
            if prepareFunc is not None:
                prepareResult = prepareFunc(**kwargs)
            if prepareResult is None:
                prepareResult = []
            if not isList(prepareResult):
                prepareResult = [prepareResult,]
            # call update function on each data object
            for item in data:
                try:
                    updateResult.append(
                            updateFunc(item, *prepareResult, **kwargs))
                    if progress is not None and progress.update():
                        break
                    if stopFunc is not None and stopFunc():
                        break
                except Exception as e:
                    errorOccured = True
                    import traceback
                    logging.error(traceback.format_exc())
                    itemName = str(item)
                    try:
                        itemName = item.filename
                    except AttributeError:
                        pass
                    logging.error("Skipping '{}'".format(itemName))
                    continue
            if progress is not None:
                progress.close()
        except Exception as e:
            # progress.cancel()
            # catch and display _all_ exceptions in user friendly manner
            # DisplayException(e)
            errorOccured = True
            import traceback
            logging.error(traceback.format_exc())
            pass
        if errorOccured:
            self.reraiseLast()
#        self.selectionChanged()
        self.sigUpdatedData.emit(self.currentSelection()[1])
        return updateResult

    def loadData(self, sourceList = None, processSourceFunc = None,
                 showProgress = True, alignment = None, **kwargs):
        """
        Loads a list of data source items.

        processSourceFunc is expected to be a function which gets individual
        elements of sourceList as argument. It returns an arbitrary data item
        which is then added to this data list widget.

        Reimplement it in child classes and it will be called on load button
        and add action signal.

        This method handles exceptions and progress indication.

        Test loading a single spectra
        >>> import utils
        >>> from tests import TestData
        >>> from utilsgui import DialogInteraction, UiSettings, fileDialogType
        >>> from chemsettings import ChemSettings
        >>> from datafiltersgui import DataFiltersGui
        >>> from spectralist import SpectraList
        >>> cs = DialogInteraction.instance(ChemSettings)
        >>> dfg = DialogInteraction.instance(DataFiltersGui)
        >>> sl = DialogInteraction.instance(SpectraList, settings = cs)
        >>> utils.LastPath.path = TestData.spectra(0)
        >>> DialogInteraction.query(fileDialogType(), sl.loadData,
        ...                         slot = 'accept')
        >>> sl.updateSpectra()
        >>> utils.LastPath.path = utils.getTempFileName()
        >>> matrixfiles = DialogInteraction.query(fileDialogType(), sl.saveMatrix,
        ...                                       slot = 'accept')
        >>> len(matrixfiles)
        1
        >>> matrixfiles

        Verify written matrix data with existent matrix export
        >>> TestData.verifyMatrix(TestData.spectra(0),
        ...                       matrixfiles[0])
        True
        """
        assert processSourceFunc is not None
        assert isList(sourceList)
        progress = None
        if showProgress:
            from gui.utils.progressdialog import ProgressDialog
            progress = ProgressDialog(self, count = len(sourceList))
        self.listWidget.clearSelection()
        errorOccured = False
        lastItem = None
        for sourceItem in sourceList:
            data = None
            try:
                data = processSourceFunc(sourceItem, **kwargs)
            except Exception as e:
                # progress.cancel()
                # DisplayException(e)
                # on error, skip the current file
                errorOccured = True
                logging.error(str(e).replace("\n"," ") + " ... skipping")
                continue
            if data is not None:
                lastItem = self.add(data)
                try:
                    lastItem.setAlignment(alignment)
                # sometime PySide return a QTreeWidgetItem instead of a DataItem
                except AttributeError: pass
            if progress is not None and progress.update():
                break
        if progress is not None:
            progress.close()
        self.fitColumnsToContents()
        # notify interested widgets about changes
        if lastItem is not None:
            self.listWidget.setCurrentItem(lastItem)
        if errorOccured:
            self.reraiseLast()

    def reraiseLast(self):
        """Reraise the last error if any and display an error message dialog.
        """
        try:
            if sys.exc_info()[0] is not None:
                raise
        except Exception as e:
            DisplayException(e)