Exemplo n.º 1
0
    def estimate(self):
        """Run parameter estimation function then emit
        :attr:`sigFitWidgetSignal` with a dictionary containing a status
        message and a list of fit parameters estimations
        in the format defined in
        :attr:`silx.math.fit.fitmanager.FitManager.fit_results`

        The emitted dictionary has an *"event"* key that can have
        following values:

            - *'EstimateStarted'*
            - *'EstimateFailed'*
            - *'EstimateFinished'*
        """
        try:
            theory_name = self.fitmanager.selectedtheory
            estimation_function = self.fitmanager.theories[
                theory_name].estimate
            if estimation_function is not None:
                ddict = {'event': 'EstimateStarted', 'data': None}
                self._emitSignal(ddict)
                self.fitmanager.estimate(callback=self.fitStatus)
            else:
                msg = qt.QMessageBox(self)
                msg.setIcon(qt.QMessageBox.Information)
                text = "Function does not define a way to estimate\n"
                text += "the initial parameters. Please, fill them\n"
                text += "yourself in the table and press Start Fit\n"
                msg.setText(text)
                msg.setWindowTitle('FitWidget Message')
                msg.exec()
                return
        except Exception as e:  # noqa (we want to catch and report all errors)
            _logger.warning('Estimate error: %s', traceback.format_exc())
            msg = qt.QMessageBox(self)
            msg.setIcon(qt.QMessageBox.Critical)
            msg.setWindowTitle("Estimate Error")
            msg.setText("Error on estimate: %s" % e)
            msg.exec()
            ddict = {'event': 'EstimateFailed', 'data': None}
            self._emitSignal(ddict)
            return

        self.guiParameters.fillFromFit(self.fitmanager.fit_results, view='Fit')
        self.guiParameters.removeAllViews(keep='Fit')
        ddict = {
            'event': 'EstimateFinished',
            'data': self.fitmanager.fit_results
        }
        self._emitSignal(ddict)
Exemplo n.º 2
0
def displayExceptionBox(message, exc_info):
    """
    Display an exception as a MessageBox

    :param str message: A context message
    :param Union[tuple,Exception] exc_info: An exception or the output of
        exc_info.
    """
    logger.error(message, exc_info=True)

    if isinstance(exc_info, BaseException):
        exc_info = (type(exc_info), exc_info, exc_info.__traceback__)
    elif not isinstance(exc_info, tuple):
        exc_info = sys.exc_info()

    if exc_info[2] is not None:
        # Mimic the syntax of the default Python exception
        import traceback
        detailed = (''.join(traceback.format_tb(exc_info[2])))
        detailed = '{1}\nTraceback (most recent call last):\n{2}{0}: {1}'.format(
            exc_info[0].__name__, exc_info[1], detailed)
    else:
        # There is no backtrace
        detailed = '{0}: {1}'.format(exc_info[0].__name__, exc_info[1])

    from silx.gui import qt
    msg = qt.QMessageBox()
    msg.setWindowTitle(message)
    msg.setIcon(qt.QMessageBox.Critical)
    msg.setInformativeText("%s" % exc_info[1])
    msg.setDetailedText(detailed)

    msg.raise_()
    msg.exec_()
Exemplo n.º 3
0
    def startFit(self):
        """Run fit, then emit :attr:`sigFitWidgetSignal` with a dictionary
        containing a status message and a list of fit
        parameters results in the format defined in
        :attr:`silx.math.fit.fitmanager.FitManager.fit_results`

        The emitted dictionary has an *"event"* key that can have
        following values:

            - *'FitStarted'*
            - *'FitFailed'*
            - *'FitFinished'*
        """
        self.fitmanager.fit_results = self.guiParameters.getFitResults()
        try:
            ddict = {'event': 'FitStarted', 'data': None}
            self._emitSignal(ddict)
            self.fitmanager.runfit(callback=self.fitStatus)
        except Exception as e:  # noqa (we want to catch and report all errors)
            _logger.warning('Estimate error: %s', traceback.format_exc())
            msg = qt.QMessageBox(self)
            msg.setIcon(qt.QMessageBox.Critical)
            msg.setWindowTitle("Fit Error")
            msg.setText("Error on Fit: %s" % e)
            msg.exec()
            ddict = {'event': 'FitFailed', 'data': None}
            self._emitSignal(ddict)
            return

        self.guiParameters.fillFromFit(self.fitmanager.fit_results, view='Fit')
        self.guiParameters.removeAllViews(keep='Fit')
        ddict = {'event': 'FitFinished', 'data': self.fitmanager.fit_results}
        self._emitSignal(ddict)
        return
Exemplo n.º 4
0
    def setup(self):
        """Open a print dialog to ensure the :attr:`printer` is set.

        If the setting fails or is cancelled, :attr:`printer` is reset to
        *None*.
        """
        if self.printer is None:
            self.printer = qt.QPrinter()
        if self.printDialog is None:
            self.printDialog = qt.QPrintDialog(self.printer, self)
        if self.printDialog.exec_():
            if self.printer.width() <= 0 or self.printer.height() <= 0:
                self.message = qt.QMessageBox(self)
                self.message.setIcon(qt.QMessageBox.Critical)
                self.message.setText(
                    "Unknown library error \non printer initialization")
                self.message.setWindowTitle("Library Error")
                self.message.setModal(0)
                self.printer = None
                return
            self.printer.setFullPage(True)
            self._updatePrinter()
        else:
            # printer setup cancelled, check for a possible previous configuration
            if self.page is None:
                # not initialized
                self.printer = None
Exemplo n.º 5
0
    def createCombined(self):
        if self.node is None:
            return
        ind = self.combineType.currentIndex()
        if ind == 0:
            return
        if ind == csp.COMBINE_PCA:
            msgBox = qt.QMessageBox()
            msgBox.information(self,
                               'Not implemented',
                               'PCA is not implemented yet',
                               buttons=qt.QMessageBox.Close)
            return
            nPCA = self.combineN.value()  # !!! TODO !!!
#        isStopHere = self.stopHereCB.checkState() == qt.Qt.Checked
        isStoppedAt = self.combineStopCB.checkState() == qt.Qt.Checked
        kw = dict(dataFormat={'combine': ind}, colorTag=ind)
        kw['originNode'] = self.node
        if isStoppedAt:
            for it in csi.selectedItems:
                it.terminalNode = csi.nodes[self.combineStop.currentText()]
        isMoveToGroup = self.combineMoveToGroupCB.checkState() == qt.Qt.Checked
        model = self.node.widget.tree.model()
        model.beginResetModel()
        csi.selectedItems[0].parentItem.insert_item(csi.selectedItems,
                                                    csi.selectedItems[0].row(),
                                                    **kw)
        model.endResetModel()
        model.dataChanged.emit(qt.QModelIndex(), qt.QModelIndex())
        if isMoveToGroup:
            self.node.widget.tree.groupItems()
Exemplo n.º 6
0
 def __displayLicense(self):
     """Displays the license used by silx."""
     text = self.getHtmlLicense()
     licenseDialog = qt.QMessageBox(self)
     licenseDialog.setWindowTitle("License")
     licenseDialog.setText(text)
     licenseDialog.exec_()
Exemplo n.º 7
0
 def _errorMessage(self, informativeText=''):
     """Display an error message."""
     # TODO issue with QMessageBox size fixed and too small
     msg = qt.QMessageBox(self.plot)
     msg.setIcon(qt.QMessageBox.Critical)
     msg.setInformativeText(informativeText + ' ' + str(sys.exc_info()[1]))
     msg.setDetailedText(traceback.format_exc())
     msg.exec_()
Exemplo n.º 8
0
 def __onAbort(self):
     abort_diag = Qt.QMessageBox(Qt.QMessageBox.Information,
                                 'Aborting...', '<b>Cancelling merge.</b>'
                                 '<center>Please wait...</center>',
                                 parent=self)
     self.__abort_diag = abort_diag
     abort_diag.setTextFormat(Qt.Qt.RichText)
     abort_diag.setAttribute(Qt.Qt.WA_DeleteOnClose)
     abort_diag.setStandardButtons(Qt.QMessageBox.NoButton)
     abort_diag.show()
     self.__merger.abort(wait=False)
Exemplo n.º 9
0
 def load_project(self, fname):
     configProject = config.ConfigParser()
     configProject.optionxform = str  # makes it case sensitive
     try:
         configProject.read(fname)
     except Exception:
         msg = qt.QMessageBox()
         msg.setIcon(qt.QMessageBox.Critical)
         msg.critical(self, "Cannot load project",
                      "Invalid project file {0}".format(fname))
         return
     self.restore_perspective(configProject)
     dataTree = config.get(configProject, 'Data', 'tree', [])
     os.chdir(os.path.dirname(fname))
     csi.model.importData(dataTree, configData=configProject)
Exemplo n.º 10
0
    def __slotParseBnClicked(self):
        self.info_wid = None

        parseBn = self.__parseBn

        specFile = self.__input['specfile']
        imgDir = self.__input['imgdir']
        # version = self.__input['version']
        padding = self.__input['padding']
        offset = self.__input['offset']

        spec_h5 = os.path.join(self.__tmp_dir_merge, 'temp_spec.h5')

        try:
            self.__parser = KmapSpecParser(
                specFile,
                spec_h5,
                img_dir=imgDir,
                # version=version,
                nr_offset=offset,
                nr_padding=padding,
                callback=self.__sigParsed.emit)
        except Exception as ex:
            msg = ('Parsing failed: {0}.\n'
                   'Message : {1}.'
                   ''.format(ex.__class__.__name__, str(ex)))
            Qt.QMessageBox.critical(self, 'Parse error.', msg)
            return

        parseBn.setEnabled(False)
        parseBn.setText('Parsing...')

        info_wid = Qt.QMessageBox(Qt.QMessageBox.Information,
                                  'Parsing...',
                                  '<b>Parsing SPEC file and matching image'
                                  ' files.</b>'
                                  '<center>Please wait...</center>',
                                  parent=self)
        info_wid.setTextFormat(Qt.Qt.RichText)
        info_wid.setAttribute(Qt.Qt.WA_DeleteOnClose)
        info_wid.setStandardButtons(Qt.QMessageBox.NoButton)
        info_wid.show()
        self.info_wid = info_wid
        self.__parser.parse(blocking=False)
Exemplo n.º 11
0
def exception(parent, title, exc_info, logger=None):
    """
    Display an exception as a MessageBox

    :param str title: A context message (displayed a s a title)
    :param qt.QWidget parent: The parent widget
    :param Union[tuple,Exception] exc_info: An exception or the output of
        exc_info.
    :param object logger: Logger to record the error inside. If `None` a
        default logger is provided.
    """
    if logger is None:
        logger = _logger

    logger.error(title, exc_info=True)

    if isinstance(exc_info, BaseException):
        exc_info = (type(exc_info), exc_info, exc_info.__traceback__)
    elif not isinstance(exc_info, tuple):
        exc_info = sys.exc_info()

    if exc_info[2] is not None:
        # Mimic the syntax of the default Python exception
        import traceback
        detailed = (''.join(traceback.format_tb(exc_info[2])))
        detailed = '{1}\nTraceback (most recent call last):\n{2}{0}: {1}'.format(
            exc_info[0].__name__, exc_info[1], detailed)
    else:
        # There is no backtrace
        detailed = '{0}: {1}'.format(exc_info[0].__name__, exc_info[1])

    msg = qt.QMessageBox(parent=parent)
    msg.setWindowTitle(title)
    msg.setIcon(qt.QMessageBox.Critical)
    msg.setInformativeText("%s" % exc_info[1])
    msg.setDetailedText(detailed)

    msg.raise_()
    msg.exec_()
Exemplo n.º 12
0
 def DoAction(self):
     if self.timer.isActive():
         self.timer.stop()
     else:
         self.datDir = self.u.tB_Directory.toPlainText()
         self.trimRange = [self.u.sB_low.value(), self.u.sB_high.value()]
         if os.path.isdir(self.datDir) and self.u.tE_header.toPlainText():
             self.u.progressBar.setMinimum(self.u.sB_start.value())
             self.u.progressBar.setValue(self.u.sB_start.value())
             # self.u.progressBar.setValue(self.u.sB_start)
             self.N = self.u.progressBar.value()
             self.u.progressBar.setMaximum(self.u.sB_End.value())
             self.timer.start(1, self)
         else:
             msg = qt.QMessageBox()
             msg.setText('Something wrong happened...')
             msg.setWindowTitle("!Error!")
             msg.setDetailedText(
                 'The directory or/and File header is/are empty!')
             msg.setStandardButtons(qt.QMessageBox.Ok
                                    | qt.QMessageBox.Cancel)
             msg.exec_()
Exemplo n.º 13
0
    def initializeGL(self):
        if hasattr(self, 'windowHandle'):  # Qt 5
            self._devicePixelRatio = self.windowHandle().devicePixelRatio()
            if self._devicePixelRatio != 1.0:
                _logger.info('High DPI support, devicePixelRatio = %f',
                             self._devicePixelRatio)

        # Check if OpenGL2 is available
        versionflags = self.format().openGLVersionFlags()
        self._isOpenGL21 = bool(versionflags & qt.QGLFormat.OpenGL_Version_2_1)
        if not self._isOpenGL21:
            _logger.error('3D rendering is disabled: OpenGL 2.1 not available')

            messageBox = qt.QMessageBox(parent=self)
            messageBox.setIcon(qt.QMessageBox.Critical)
            messageBox.setWindowTitle('Error')
            messageBox.setText('3D rendering is disabled.\n\n'
                               'Reason: OpenGL 2.1 is not available.')
            messageBox.addButton(qt.QMessageBox.Ok)
            messageBox.setWindowModality(qt.Qt.WindowModal)
            messageBox.setAttribute(qt.Qt.WA_DeleteOnClose)
            messageBox.show()
Exemplo n.º 14
0
 def doSaveProject(self, res):
     fname = res[0][0]
     if not fname.endswith('.pspj'):
         fname += '.pspj'
     try:
         with open(fname, 'w') as f:
             f.write('try')
     except OSError:
         msg = qt.QMessageBox()
         msg.setIcon(qt.QMessageBox.Critical)
         msg.critical(self, "Cannot write file",
                      "Invalid file name {0}".format(fname))
         return
     self.save_project(fname)
     for i, (name, node) in enumerate(csi.nodes.items()):
         node.widget.saveGraph(fname, i, name)
     if len(res) < 5:
         return
     saveNodes, saveTypes, saveScriptMpl, saveScriptSilx = res[1:5]
     plots, h5plots = self.save_data(fname, saveNodes, saveTypes)
     if saveScriptMpl:
         self.save_script(fname, plots, h5plots, 'mpl')
     if saveScriptSilx:
         self.save_script(fname, plots, h5plots, 'silx')
Exemplo n.º 15
0
    def dropMimeData(self, mimedata, action, row, column, parent):
        if mimedata.hasFormat(cco.MIME_TYPE_DATA):
            toItem = parent.internalPointer()
            if toItem is None:
                toItem = csi.dataRootItem
                newParentItem, newRow = toItem, toItem.child_count()
                parents = []
            else:
                newParentItem, newRow = toItem.parentItem, toItem.row()
                parents = [toItem.parentItem]
            rowss = pickle.loads(mimedata.data(cco.MIME_TYPE_DATA))
            dropedItems = []
            for rows in rowss:
                parentItem = self.rootItem
                for r in reversed(rows):
                    item = parentItem.child(r)
                    parentItem = item
                dropedItems.append(item)
                if item.parentItem not in parents:
                    parents.append(item.parentItem)
            for item in dropedItems:
                if item.is_ancestor_of(toItem):
                    msg = qt.QMessageBox()
                    msg.setIcon(qt.QMessageBox.Warning)
                    msg.setText("Cannot drop a group onto its child. Ignored")
                    msg.setWindowTitle("Illegal drop")
                    msg.setStandardButtons(qt.QMessageBox.Close)
                    msg.exec_()
                    return False

            self.beginResetModel()
            if toItem is csi.dataRootItem:
                rdropedItems = dropedItems
            else:
                rdropedItems = reversed(dropedItems)
            for item in rdropedItems:
                oldParentItem, oldRow = item.parentItem, item.row()
                if newParentItem is oldParentItem:
                    sibl = newParentItem.childItems
                    sibl.insert(newRow, sibl.pop(oldRow))
                else:
                    newParentItem.childItems.insert(newRow, item)
                    item.parentItem = newParentItem
                    del oldParentItem.childItems[oldRow]
                    if oldParentItem.child_count() == 0:
                        oldParentItem.delete()
            self.endResetModel()
            for parent in parents:
                parent.init_colors()
            self.dataChanged.emit(qt.QModelIndex(), qt.QModelIndex())
            self.needReplot.emit()
            return True
        elif mimedata.hasFormat(cco.MIME_TYPE_TEXT) or \
                mimedata.hasFormat(cco.MIME_TYPE_HDF5):
            toItem = parent.internalPointer()
            if mimedata.hasFormat(cco.MIME_TYPE_TEXT):
                urls = [url.toLocalFile() for url in reversed(mimedata.urls())]
            else:
                urls = pickle.loads(mimedata.data(cco.MIME_TYPE_HDF5))[::-1]
            if toItem is None:
                toItem = csi.dataRootItem
                urls = urls[::-1]

            if toItem.child_count() > 0:  # is a group
                parentItem, insertAt = toItem, None
            else:
                parentItem, insertAt = toItem.parentItem, toItem.row()
            if csi.currentNode is None:
                return False
            node = csi.currentNode
            if hasattr(node, 'widget'):
                items = node.widget.loadFiles(urls, parentItem, insertAt)


#            if DEBUG > 0:
#                if items is not None:
#                    for item in items:
#                        item.colorTag = 3
            return True
        else:
            return False
Exemplo n.º 16
0
    def loadFiles(self, fileNamesFull=None, parentItem=None, insertAt=None):
        def times(n):
            return " ({0} times)".format(n) if n > 1 else ""

        selectedIndexes = self.files.selectionModel().selectedRows()
        selectedIndex = selectedIndexes[0]
        dataStruct = selectedIndex.data(gft.LOAD_DATASET_ROLE)
        if dataStruct is None:
            return
        colRecs, df = dataStruct

        # spectraInOneFile = 1
        # for col in colRecs:  # col is a list of (expr, d[xx]-expr, data-keys)
        #     spectraInOneFile = max(spectraInOneFile, len(col))
        # if spectraInOneFile > 1:
        #     colMany = []
        #     for icol, col in enumerate(colRecs):
        #         if len(col) not in [1, spectraInOneFile]:
        #             msg = qt.QMessageBox()
        #             msg.setIcon(qt.QMessageBox.Question)
        #             res = msg.critical(
        #                 self, "Cannot load data",
        #                 "Lists of different lengths found")
        #             return
        #         if len(col) == spectraInOneFile:
        #             colMany.append(icol)
        #     if not colMany:
        #         return

        if isinstance(fileNamesFull, qt.QModelIndex):
            if qt.QFileInfo(
                    self.files.model().filePath(fileNamesFull)).isDir():
                return
            fileNamesFull = None
        if not fileNamesFull:
            sIndexes = self.files.selectionModel().selectedRows()
            nodeType = self.files.model().nodeType(sIndexes[0])
            if nodeType == gft.NODE_FS:
                fileNamesFull = \
                    [self.files.model().filePath(i) for i in sIndexes]
            else:  # FileTreeView.NODE_HDF5, FileTreeView.NODE_HDF5_HEAD
                fileNamesFull = \
                    [self.files.model().getHDF5FullPath(i) for i in sIndexes]

        fileNames = [osp.normcase(nf) for nf in fileNamesFull]
        allLoadedItemNames = []
        for d in csi.allLoadedItems:
            lfn = d.madeOf[5:] if d.madeOf.startswith('silx:') else d.madeOf
            lfns = osp.normcase(osp.abspath(lfn))
            if d.madeOf.startswith('silx:'):
                lfns = 'silx:' + lfns
            allLoadedItemNames.append(lfns)
        allLoadedItemsCount = Counter(allLoadedItemNames)
        duplicates, duplicatesN = [], []
        fileNamesFullN = []
        for fname, fnameFull in zip(fileNames, fileNamesFull):
            n = allLoadedItemsCount[osp.normcase(fnameFull)]
            if n > 0:
                duplicates.append(fnameFull)
                duplicatesN.append(n)
            fileNamesFullN.append(n)
        if duplicates:
            duplicatesNStr =\
                [dup + times(n) for dup, n in zip(duplicates, duplicatesN)]
            st1, st2, st3, st4 =\
                ('This', '', 'is', 'it') if len(duplicates) == 1 else\
                ('These', 's', 'are', 'them')
            msg = qt.QMessageBox()
            msg.setIcon(qt.QMessageBox.Question)
            res = msg.question(
                self, "Already in the data list",
                "{0} file{1} {2} already loaded:\n{3}".format(
                    st1, st2, st3, '\n'.join(duplicatesNStr)) +
                "\nDo you want to load {0} gain?".format(st4),
                qt.QMessageBox.Yes | qt.QMessageBox.No, qt.QMessageBox.Yes)
            if res == qt.QMessageBox.No:
                return

        if parentItem is None:
            if csi.selectedItems:
                parentItem = csi.selectedItems[0].parentItem
            else:
                parentItem = csi.dataRootItem

        # !!! TODO !!!
        # here the column format is taken from the present state of
        # ColumnFormatWidget. Should be automatically detected from
        # file format

        # df['dataSource'] = [col[0][0] for col in colRecs]
        items = csi.model.importData(fileNamesFull,
                                     parentItem,
                                     insertAt,
                                     dataFormat=df,
                                     originNode=self.node)

        # if spectraInOneFile == 1:
        #     df['dataSource'] = [col[0][0] for col in colRecs]
        #     items = csi.model.importData(
        #         fileNamesFull, parentItem, insertAt, dataFormat=df,
        #         originNode=self.node)
        # else:
        #     keys = [col[2][0] for col in colRecs[colMany[0]]]
        #     cs = keys[0]
        #     for key in keys[1:]:
        #         cs = cco.common_substring(cs, key)
        #     colNames = [key[len(cs):] for key in keys]
        #     for fname in fileNamesFull:
        #         basename = osp.basename(fname)
        #         groupName = osp.splitext(basename)[0]
        #         if '::' in fname:
        #             h5name = osp.splitext(osp.basename(
        #                 fname[:fname.find('::')]))[0]
        #             groupName = '/'.join([h5name, groupName])
        #         group, = csi.model.importData(groupName, parentItem, insertAt)
        #         for i, colName in enumerate(colNames):
        #             df['dataSource'] = \
        #                 [col[i][0] if len(col) > 1 else col[0][0]
        #                  for col in colRecs]
        #             csi.model.importData(
        #                 fname, group, dataFormat=df,
        #                 alias='{0}_{1}'.format(groupName, colName),
        #                 originNode=self.node)
        #     items = group,

        mode = qt.QItemSelectionModel.Select | qt.QItemSelectionModel.Rows
        for item in items:
            row = item.row()
            index = csi.model.createIndex(row, 0, item)
            csi.selectionModel.select(index, mode)
Exemplo n.º 17
0
    def _pluginClicked(self):
        actionNames = []
        menu = qt.QMenu(self)
        menu.addAction("Reload Plugins")
        actionNames.append("Reload Plugins")
        menu.addAction("Set User Plugin Directory")
        actionNames.append("Set User Plugin Directory")

        if _logger.getEffectiveLevel() == logging.DEBUG:
            text = "Toggle DEBUG mode OFF"
        else:
            text = "Toggle DEBUG mode ON"

        menu.addAction(text)
        menu.addSeparator()
        actionNames.append(text)
        callableKeys = ["Dummy0", "Dummy1", "Dummy2"]
        pluginInstances = self.pluginInstanceDict
        for pluginName in self.pluginList:
            if pluginName in ["PyMcaPlugins.Plugin1DBase", "Plugin1DBase"]:
                continue
            module = sys.modules[pluginName]
            if hasattr(module, 'MENU_TEXT'):
                text = module.MENU_TEXT
            else:
                text = os.path.basename(module.__file__)
                if text.endswith('.pyc'):
                    text = text[:-4]
                elif text.endswith('.py'):
                    text = text[:-3]

            methods = pluginInstances[pluginName].getMethods(
                plottype=self.plot._plotType)
            if not len(methods):
                continue
            elif len(methods) == 1:
                pixmap = pluginInstances[pluginName].getMethodPixmap(
                    methods[0])
                tip = pluginInstances[pluginName].getMethodToolTip(methods[0])
                if pixmap is not None:
                    action = qt.QAction(qt.QIcon(qt.QPixmap(pixmap)), text,
                                        self)
                else:
                    action = qt.QAction(text, self)
                if tip is not None:
                    action.setToolTip(tip)
                menu.addAction(action)
            else:
                menu.addAction(text)
            actionNames.append(text)
            callableKeys.append(pluginName)
        menu.hovered.connect(self._actionHovered)
        a = menu.exec_(qt.QCursor.pos())
        if a is None:
            return None

        idx = actionNames.index(a.text())
        if a.text() == "Reload Plugins":
            n, message = self.getPlugins(exceptions=True)
            if n < 1:
                msg = qt.QMessageBox(self)
                msg.setIcon(qt.QMessageBox.Information)
                msg.setWindowTitle("No plugins")
                msg.setInformativeText(" Problem loading plugins ")
                msg.setDetailedText(message)
                msg.exec_()
            return
        if a.text() == "Set User Plugin Directory":
            dirName = qt.QFileDialog.getExistingDirectory(
                self, "Enter user plugins directory", os.getcwd())
            if len(dirName):
                pluginsDir = self.getPluginDirectoryList()
                pluginsDirList = [pluginsDir[0], dirName]
                self.setPluginDirectoryList(pluginsDirList)
            return
        if "Toggle DEBUG mode" in a.text():
            _toggleLogger()
        key = callableKeys[idx]

        methods = pluginInstances[key].getMethods(plottype=self.plot._plotType)
        if len(methods) == 1:
            idx = 0
        else:
            actionNames = []
            # allow the plugin designer to specify the order
            #methods.sort()
            menu = qt.QMenu(self)
            for method in methods:
                text = method
                pixmap = pluginInstances[key].getMethodPixmap(method)
                tip = pluginInstances[key].getMethodToolTip(method)
                if pixmap is not None:
                    action = qt.QAction(qt.QIcon(qt.QPixmap(pixmap)), text,
                                        self)
                else:
                    action = qt.QAction(text, self)
                if tip is not None:
                    action.setToolTip(tip)
                menu.addAction(action)
                actionNames.append((text, pixmap, tip, action))
            #qt.QObject.connect(menu, qt.SIGNAL("hovered(QAction *)"), self._actionHovered)
            menu.hovered.connect(self._actionHovered)
            a = menu.exec_(qt.QCursor.pos())
            if a is None:
                return None
            idx = -1
            for action in actionNames:
                if a.text() == action[0]:
                    idx = actionNames.index(action)
        try:
            pluginInstances[key].applyMethod(methods[idx])
        except:
            msg = qt.QMessageBox(self)
            msg.setIcon(qt.QMessageBox.Critical)
            msg.setWindowTitle("Plugin error")
            msg.setText("An error has occured while executing the plugin:")
            msg.setInformativeText(str(sys.exc_info()[1]))
            msg.setDetailedText(traceback.format_exc())
            msg.exec_()