Ejemplo n.º 1
0
class OpenedFileExplorer(DockWidget):
    """Opened File Explorer is list widget with list of opened files.
    It implements switching current file, files sorting. Uses _OpenedFileModel internally.
    Class instance created by Workspace.
    """
    def __init__(self, workspace):
        DockWidget.__init__(self, workspace, "&Opened Files", QIcon(":/enkiicons/filtered.png"), "Alt+O")

        self._workspace = workspace

        self.setAllowedAreas( Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea )

        self.tvFiles = QTreeView(self)
        self.tvFiles.setHeaderHidden(True)
        self.tvFiles.setEditTriggers(QAbstractItemView.SelectedClicked)
        self.tvFiles.setContextMenuPolicy(Qt.CustomContextMenu)
        self.tvFiles.setDragEnabled(True)
        self.tvFiles.setDragDropMode(QAbstractItemView.InternalMove)
        self.tvFiles.setRootIsDecorated(False)
        self.tvFiles.setTextElideMode(Qt.ElideMiddle)
        self.tvFiles.setUniformRowHeights( True )

        self.tvFiles.customContextMenuRequested.connect(self._onTvFilesCustomContextMenuRequested)

        self.setWidget(self.tvFiles)
        self.setFocusProxy(self.tvFiles)

        self.model = _OpenedFileModel(self)  # Not protected, because used by Configurator
        self.tvFiles.setModel( self.model )
        self.tvFiles.setAttribute( Qt.WA_MacShowFocusRect, False )
        self.tvFiles.setAttribute( Qt.WA_MacSmallSize )

        self._workspace.currentDocumentChanged.connect(self._onCurrentDocumentChanged)

        # disconnected by startModifyModel()
        self.tvFiles.selectionModel().selectionChanged.connect(self._onSelectionModelSelectionChanged)

        self.tvFiles.activated.connect(self._workspace.focusCurrentDocument)

        core.actionManager().addAction("mView/aOpenedFiles", self.showAction())

        core.uiSettingsManager().dialogAccepted.connect(self._applySettings)
        core.uiSettingsManager().aboutToExecute.connect(self._onSettingsDialogAboutToExecute)

    def del_(self):
        """Explicitly called destructor
        """
        core.actionManager().removeAction("mView/aOpenedFiles")

    def _applySettings(self):
        """Settings dialogue has been accepted.
        Apply settings
        """
        self.model.setSortMode(core.config()["Workspace"]["FileSortMode"])

    def _onSettingsDialogAboutToExecute(self, dialog):
        """UI settings dialogue is about to execute.
        Add own option
        """
        dialog.appendOption(ChoiseOption(dialog, core.config(), "Workspace/FileSortMode",
                                         {dialog.rbOpeningOrder: "OpeningOrder",
                                          dialog.rbFileName: "FileName",
                                          dialog.rbUri: "URL",
                                          dialog.rbSuffix: "Suffixes"}))

    def startModifyModel(self):
        """Blocks signals from model while it is modified by code
        """
        self.tvFiles.selectionModel().selectionChanged.disconnect(self._onSelectionModelSelectionChanged)

    def finishModifyModel(self):
        """Unblocks signals from model
        """
        self.tvFiles.selectionModel().selectionChanged.connect(self._onSelectionModelSelectionChanged)

    def _onSortTriggered(self, action ):
        """ One of sort actions has been triggered in the opened file list context menu
        """
        mode = action.data()
        self.model.setSortMode( mode )

    def _onCurrentDocumentChanged(self, oldDocument, currentDocument ):  # pylint: disable=W0613
        """ Current document has been changed on workspace
        """
        if currentDocument is not None:
            index = self.model.documentIndex( currentDocument )

            self.startModifyModel()
            self.tvFiles.setCurrentIndex( index )
            # scroll the view
            self.tvFiles.scrollTo( index )
            self.finishModifyModel()

    def _onSelectionModelSelectionChanged(self, selected, deselected ):  # pylint: disable=W0613
        """ Item selected in the list. Switch current document
        """
        if not selected.indexes():  # empty list, last file closed
            return

        index = selected.indexes()[0]
        # backup/restore current focused widget as setting active mdi window will steal it
        focusWidget = self.window().focusWidget()

        # set current document
        document = self._workspace.sortedDocuments[index.row()]
        self._workspace.setCurrentDocument( document )

        # restore focus widget
        if  focusWidget :
            focusWidget.setFocus()

    def _onTvFilesCustomContextMenuRequested(self, pos ):
        """Connected automatically by uic
        """
        menu = QMenu()

        menu.addAction( core.actionManager().action( "mFile/mClose/aCurrent" ) )
        menu.addAction( core.actionManager().action( "mFile/mSave/aCurrent" ) )
        menu.addAction( core.actionManager().action( "mFile/mReload/aCurrent" ) )
        menu.addMenu( core.actionManager().action( "mFile/mFileSystem" ).menu() )
        menu.addSeparator()

        # sort menu
        sortMenu = QMenu( self )
        group = QActionGroup( sortMenu )

        group.addAction( self.tr( "Opening order" ) )
        group.addAction( self.tr( "File name" ) )
        group.addAction( self.tr( "URL" ) )
        group.addAction( self.tr( "Suffixes" ) )
        group.triggered.connect(self._onSortTriggered)
        sortMenu.addActions( group.actions() )

        for i, sortMode in enumerate(["OpeningOrder", "FileName", "URL", "Suffixes"]):
            action = group.actions()[i]
            action.setData( sortMode )
            action.setCheckable( True )
            if sortMode == self.model.sortMode():
                action.setChecked( True )

        aSortMenu = QAction( self.tr( "Sorting" ), self )
        aSortMenu.setMenu( sortMenu )
        aSortMenu.setIcon( QIcon( ":/enkiicons/sort.png" ))
        aSortMenu.setToolTip( aSortMenu.text() )

        menu.addAction( sortMenu.menuAction() )
        menu.exec_( self.tvFiles.mapToGlobal( pos ) )
Ejemplo n.º 2
0
class DSBrowser(QWidget):
    """browser for datastorage databases
       Nodes are identified by a string, containing fields separated by '|'.
       - first filed is a capital letter:
       'R'oot, 'P'roject, sensor'G'roup, 'S'ensor and 'C'hart
       - second field is the database folder
       - third filed is the path of the node in the database
       - for charts the fourth field is the chart name (third is path of sensorgroup in this case)
    """

    def __init__(self):
        QWidget.__init__(self)
        self.layout = QVBoxLayout(self)
        self.layout.setSpacing(0)
        self.layout.setMargin(0)

        self.toolBar = QFrame(self)
        self.toolBarLayout = QHBoxLayout(self.toolBar)
        self.toolBarLayout.setMargin(2)
        self.toolBarLayout.setSpacing(2)
        self.layout.addWidget(self.toolBar)

        self.loadButton = QToolButton(self.toolBar)
        self.loadButton.setText(QCoreApplication.translate("DataStorageBrowser", "Open..."))
        self.loadButton.setIcon(QIcon(QPixmap(SimuVis4.Icons.fileOpen)))
        self.loadButton.setToolTip(QCoreApplication.translate("DataStorageBrowser", "Open a datastorage database"))
        self.toolBarLayout.addWidget(self.loadButton)
        self.connect(self.loadButton, SIGNAL("pressed()"), self.loadDatabase)

        self.expandButton = QToolButton(self.toolBar)
        self.expandButton.setText("Expand/Collapse")
        self.expandButton.setIcon(QIcon(QPixmap(Icons.exp_col)))
        self.expandButton.setToolTip(
            QCoreApplication.translate("DataStorageBrowser", "Expand or collapse the whole tree")
        )
        self.toolBarLayout.addWidget(self.expandButton)
        self.connect(self.expandButton, SIGNAL("pressed()"), self.expandCollapseAll)

        self.searchInput = MyLineEdit(self.toolBar)
        self.searchInput.setText(QCoreApplication.translate("DataStorageBrowser", "Enter search text here"))
        self.searchInput.setToolTip(
            QCoreApplication.translate(
                "DataStorageBrowser", "Enter search text using wildcards here, press ENTER again to go to next match!"
            )
        )
        self.toolBarLayout.addWidget(self.searchInput, 100)
        self.connect(self.searchInput, SIGNAL("returnPressed()"), self.searchItem)

        self.helpButton = QToolButton(self.toolBar)
        self.helpButton.setText(QCoreApplication.translate("DataStorageBrowser", "Help"))
        self.helpButton.setIcon(QIcon(QPixmap(SimuVis4.Icons.help)))
        self.helpButton.setToolTip(QCoreApplication.translate("DataStorageBrowser", "Show help for DataStorageBrowser"))
        self.toolBarLayout.addWidget(self.helpButton)
        self.connect(self.helpButton, SIGNAL("pressed()"), self.showHelp)

        self.splitter = QSplitter(self)
        self.splitter.setOrientation(Qt.Vertical)
        self.treeView = QTreeView(self.splitter)
        self.treeView.setAlternatingRowColors(True)
        self.treeView.setEditTriggers(QAbstractItemView.NoEditTriggers)
        self.treeView.setContextMenuPolicy(Qt.CustomContextMenu)
        self.treeView.setAutoExpandDelay(500)
        self.textBrowser = QTextBrowser(self.splitter)
        self.layout.addWidget(self.splitter)
        self.splitter.setStretchFactor(0, 60)
        self.splitter.setStretchFactor(1, 40)
        self.model = DSModel()
        self.treeView.setModel(self.model)
        self.treeView.setSortingEnabled(True)
        self.treeView.expandAll()
        self.connect(self.treeView.selectionModel(), SIGNAL("currentChanged(QModelIndex, QModelIndex)"), self.showItem)
        self.connect(self.treeView, SIGNAL("doubleClicked(QModelIndex)"), self.itemAction)
        self.connect(self.treeView, SIGNAL("customContextMenuRequested(QPoint)"), self.showContextMenu)
        self.selectedNode = None
        self.selectedMI = None
        self.searchText = ""
        self.searchResults = []
        self.collExpand = SimuVis4.Misc.Switcher()

        self.statusBar = SimuVis4.Globals.mainWin.statusBar()

    def loadDatabase(self, dn=None):
        """load a database"""
        if not dn:
            dn = QFileDialog.getExistingDirectory(
                self,
                QCoreApplication.translate("DataStorageBrowser", "Select a folder containing a datastorage database"),
                SimuVis4.Globals.defaultFolder,
            )
            if not dn.isEmpty():
                dn = unicode(dn)
                SimuVis4.Globals.defaultFolder = dn
            else:
                return
        self.model.addDatabase(dn)
        self.treeView.collapseAll()
        self.treeView.expandToDepth(SimuVis4.Globals.config.getint("datastoragebrowser", "expand_tree_depth"))
        self.treeView.resizeColumnToContents(0)

    def showHelp(self):
        SimuVis4.HelpBrowser.showHelp("/plugin/DataStorageBrowser/index.html")

    def showItem(self, mi, pr):
        """show the item at model index mi"""
        t, n = self.model.dsNode(mi)
        txt = ""
        if t == "R":
            txt = rootInfo.substitute(
                name=n.name, title=escape(n.title), folder=n.h5dir, projects=len(n)
            ) + formatMetaData(n)
        elif t == "P":
            txt = projectInfo.substitute(
                name=n.name, path=escape(n.parent.name), title=escape(n.title), groups=len(n)
            ) + formatMetaData(n)
        elif t == "G":
            txt = groupInfo.substitute(
                name=n.name,
                path="/".join(n.path.split("/")[:-1]),
                title=escape(n.title),
                sensors=len(n),
                charts=len(n.getCharts()),
                start=formatTime(n.timegrid.start),
                stop=formatTime(n.timegrid.stop),
                step=n.timegrid.step,
                timezone=n.timegrid.timezone,
            ) + formatMetaData(n)
        elif t == "S":
            txt = sensorInfo.substitute(
                name=n.name,
                path="/".join(n.path.split("/")[:-1]),
                title=escape(n.title),
                start=formatTime(n.timegrid.start),
                stop=formatTime(n.timegrid.stop),
                step=n.timegrid.step,
                length=n.datalen(),
                timezone=n.timegrid.timezone,
            ) + formatMetaData(n)
        elif t == "C":
            txt = chartInfo.substitute(name=n.name, path=n.sensorgroup.path)
        self.textBrowser.setText(txt)
        msg = ": ".join(str(self.model.itemFromIndex(mi).data().toString()).split("|")[1:])
        self.statusBar.showMessage(msg, 5000)

    def searchItem(self):
        """execute the search and highlight the (next) result"""
        txt = str(self.searchInput.text())
        if txt != self.searchText:
            self.searchText = txt
            tmp = self.model.findItems(
                txt, Qt.MatchFixedString | Qt.MatchContains | Qt.MatchWildcard | Qt.MatchRecursive
            )
            self.searchList = [i.index() for i in tmp]
        if self.searchList:
            mi = self.searchList.pop()
            self.treeView.setCurrentIndex(mi)
            self.treeView.expand(mi)
            self.treeView.scrollTo(mi)
        else:
            QMessageBox.information(
                self,
                QCoreApplication.translate("DataStorageBrowser", "No (more) matches!"),
                QCoreApplication.translate(
                    "DataStorageBrowser", "No (more) matches found! Change you search text and try again!"
                ),
            )
            self.searchText = ""

    def expandCollapseAll(self):
        if self.collExpand():
            self.treeView.collapseAll()
        else:
            self.treeView.expandAll()

    def itemAction(self, mi):
        """default action (on doubleclick) for item at model index mi"""
        t, n = self.model.dsNode(mi)
        if t == "S":
            if qwtPlotWindowActive():
                self.addToQwtPlot(n)
            else:
                self.showQwtPlot(n)
        elif t == "C":
            self.showChart(n)

    def showContextMenu(self, pos):
        """show context menu for item at pos"""
        mi = self.treeView.indexAt(pos)
        t, n = self.model.dsNode(mi)
        self.selectedNode = n
        self.selectedMI = mi
        m = QMenu()
        if t == "R":
            m.addAction(QCoreApplication.translate("DataStorageBrowser", "Close"), self.closeDatabase)
            m.addAction(QCoreApplication.translate("DataStorageBrowser", "Reload"), self.reloadDatabase)
        elif t == "P":
            m.addAction(QCoreApplication.translate("DataStorageBrowser", "New sensorgroup"), self.newSensorGroup)
        elif t == "G":
            nCharts = len(n.getCharts())
            if nCharts > 0:
                txt = str(QCoreApplication.translate("DataStorageBrowser", "Show all charts (%d)")) % nCharts
                m.addAction(txt, self.showAllCharts)
                m.addAction(
                    QCoreApplication.translate("DataStorageBrowser", "Save all charts as images"),
                    self.saveAllChartImages,
                )
            m.addAction(QCoreApplication.translate("DataStorageBrowser", "Add chart"), self.newChart)
            m.addAction(QCoreApplication.translate("DataStorageBrowser", "Add/update data"), self.importFiles)
            m.addAction(QCoreApplication.translate("DataStorageBrowser", "Export data"), self.exportSensors)
        elif t == "S":
            m.addAction(QCoreApplication.translate("DataStorageBrowser", "New plot (Qwt)"), self.showQwtPlot)
            if qwtPlotWindowActive():
                m.addAction(QCoreApplication.translate("DataStorageBrowser", "Add to plot (Qwt)"), self.addToQwtPlot)
        elif t == "C":
            m.addAction(QCoreApplication.translate("DataStorageBrowser", "Show"), self.showChart)
            m.addAction(QCoreApplication.translate("DataStorageBrowser", "Delete"), self.deleteItem)
        if t in "RPGS":
            m.addSeparator()
            m.addAction(QCoreApplication.translate("DataStorageBrowser", "Edit metadata"), self.editMetadata)
        a = m.exec_(self.treeView.mapToGlobal(pos))

    def newSensorGroup(self, mi=None):
        if mi is None:
            mi = self.selectedMI
        newSensorGroup(self.model, mi)

    def showChart(self, ch=None):
        if ch is None:
            ch = self.selectedNode
        showChartWindow(ch, maximized=showChartMaximized)

    def importFiles(self, mi=None):
        if mi is None:
            mi = self.selectedMI
        importFiles(self.model, mi)

    def showAllCharts(self, sg=None):
        if sg is None:
            sg = self.selectedNode
        showAllChartWindows(sg, maximized=showChartMaximized)

    def saveAllChartImages(self, sg=None):
        if sg is None:
            sg = self.selectedNode
        saveAllChartImages(sg)

    def exportSensors(self, sg=None):
        if sg is None:
            sg = self.selectedNode
        exportSensors(sg)

    def showQwtPlot(self, se=None):
        if se is None:
            se = self.selectedNode
        showQwtPlotWindow(se, maximized=showChartMaximized)

    def addToQwtPlot(self, se=None):
        if se is None:
            se = self.selectedNode
        addToQwtPlotWindow(se)

    def editMetadata(self, node=None):
        if node is None:
            node = self.selectedNode
        editMetadata(node)

    def closeDatabase(self, mi=None):
        if mi is None:
            mi = self.selectedMI
        self.model.closeDatabase(mi)

    def reloadDatabase(self, mi=None):
        if mi is None:
            mi = self.selectedMI
        dbPath = self.model.dsFolder(mi)
        self.model.closeDatabase(mi)
        self.loadDatabase(dbPath)

    def newChart(self, mi=None):
        """add a chart to sensorgroup at mi using the wizard"""
        if mi is None:
            mi = self.selectedMI
        showNewChartWizard(self.model, mi, self)

    def deleteItem(self, mi=None):
        """delete the item at mi"""
        if mi is None:
            mi = self.selectedMI
        self.model.deleteItem(mi)
Ejemplo n.º 3
0
class ResultView(QWidget):
    """This class represent a search result view.
    """

    def __init__(self, filter='', attributes=[], resultlist=[], parent=None):
        """Initialize a result view for the `SearchPlugin`.

        :param filter: the filter applied on the search
        :type filter: string
        :param attributes: a list containing the attributes used in the
         search operation. Usually extracted from the `filter`.
        :type attributes: list
        :param resultlist: a list of `SmartDataObject` from the search
         operation.
        :type resultlist: list
        :param parent: the parent for this widget.
        :type parent: QWidget
        """
        super(ResultView, self).__init__(parent)
        self.setObjectName('ResultView')
        self.layout = QtGui.QVBoxLayout(self)

        # Only display the no-result message if resultlist is empty
        if len(resultlist) == 0:
            self.retranslate(all=False)
            self.onNoResult()
            return

        # The proxy model is used for sort and filter support
        self.proxymodel = QSortFilterProxyModel(self)
        self.proxymodel.setDynamicSortFilter(True)

        self.headerdata = ['dn']
        self.headerdata.extend(attributes)
        self.resultdata = resultlist

        # FIXME: should we create a custom item model ?
        self.model = QStandardItemModel(0, len(self.headerdata), parent=self)
        #self.model = ResultItemModel(self)
        #self.model = ResultItemModel(self.headerdata, self.resultdata, self)
        self.proxymodel.setSourceModel(self.model)

        self.resultview = QTreeView(self)
        self.resultview.setUniformRowHeights(True)
        self.resultview.setRootIsDecorated(False)
        self.resultview.setAlternatingRowColors(True)
        self.resultview.setSortingEnabled(True)

        self.resultview.setModel(self.proxymodel)

        # For right-click context menu
        self.resultview.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
        self.resultview.setSelectionMode(QAbstractItemView.ExtendedSelection)

        self.layout.addWidget(self.resultview)
        # The filter box enables the user to filter the returned search
        # results. It becomes accessible with Ctrl-F (QKeySequence.Find)
        self.filterBox = ResultFilterWidget(self.headerdata, parent=self)
        self.filterBox.setVisible(False)

        self.layout.addWidget(self.filterBox)

        # We need to call the retranslate method before populating
        # the result data
        self.retranslate()
        #self.model.populateHeader(self.headerdata)
        #self.model.populateModel(self.resultdata)
        self.setHeaderData(self.headerdata)
        self.setResultData(self.resultdata)
        self.resultview.resizeColumnToContents(0)
        self.__createContextMenu()
        self.__connectSlots()

    def __connectSlots(self):
        """Connect signal and slots.
        """
        self.resultview.customContextMenuRequested.connect(
            self.onContextMenuRequested)
        self.filterBox.inputEdit.textChanged['QString'].connect(
            self.onFilterInputChanged)
        self.filterBox.columnBox.currentIndexChanged[int].connect(
            self.onFilterColumnChanged)

    def __getVSpacer(self):
        return QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding,
                                 QtGui.QSizePolicy.Minimum)

    def __createContextMenu(self):
        """Display the context menu.
        """
        self.contextMenu = QtGui.QMenu()
        self.contextMenuView = QtGui.QAction(self)
        self.contextMenuDelete = QtGui.QAction(self)
        self.contextMenuExport = QtGui.QAction(self)
        self.contextMenu.addAction(self.contextMenuView)
        self.contextMenu.addAction(self.contextMenuDelete)
        self.contextMenu.addAction(self.contextMenuExport)

        # Connect the context menu actions to the correct slots
        self.contextMenuView.triggered.connect(self.onViewItemsSelected)
        self.contextMenuDelete.triggered.connect(self.onDeleteItemsSelected)
        self.contextMenuExport.triggered.connect(self.onExportItemsSelected)

    def onNoResult(self):
        """Adds a styled *no result* message to the main layout.
        """
        font = QtGui.QFont()
        font.setBold(True)
        sadface = QtGui.QLabel(self)
        sadface.setPixmap(pixmapFromTheme('face-sad', ':/icons/48/face-sad'))
        noresult = QtGui.QLabel(self)
        noresult.setText(self.str_NO_RESULT)
        noresult.setFont(font)
        hlayout = QtGui.QHBoxLayout()
        hlayout.addItem(self.__getVSpacer())
        hlayout.addWidget(sadface)
        hlayout.addWidget(noresult)
        hlayout.addItem(self.__getVSpacer())

        self.layout.addLayout(hlayout)

    def setHeaderData(self, data=[]):
        """Populates the ``resultview`` model with header data.

        Parameters:

        - `data`: a list with header items. Usually this is the
          attributelist from the LDAP search.
        """
        i = 0
        for header in data:
            self.model.setHeaderData(i, QtCore.Qt.Horizontal, header)
            i += 1

    def setResultData(self, data=[]):
        """Populates the ``resultview`` model with result data.

        Parameters:

        - `data`: a list containing the SmartDataObjects representing
          items in the LDAP search result.
        """
        row = 0
        for obj in data:
            self.model.insertRow(row)
            col = 0
            for attr in self.headerdata:
                if self.isDistinguishedName(attr):
                    modelData = obj.getPrettyDN()
                elif self.isObjectClass(attr):
                    modelData = ','.join(obj.getObjectClasses())
                elif obj.hasAttribute(attr):
                    if obj.isAttributeBinary(attr):
                        modelData = self.str_BINARY_DATA
                    else:
                        modelData = ','.join(obj.getAttributeValueList(attr))

                self.model.setData(self.model.index(row, col), modelData)
                col += 1

            row += 1

    def isDistinguishedName(self, attr):
        """Returns ``True`` if `attr` is a distinguished name,
        ``False`` otherwise.

        Parameters:

        - `attr`: the LDAP string attribute value to check.
        """
        return attr.lower() == 'dn'

    def isObjectClass(self, attr):
        """Returns ``True`` if `attr` is an object class, ``False``
        otherwise.

        Parameters:

        - `attr`: the LDAP string attribute value to check.
        """
        return attr.lower() == 'objectclass'

    def onContextMenuRequested(self, point):
        """Display the context menu
        """

        # FIXME: In order to be able to export, delete and view search
        # result entries. We should make use of the various dialogs in
        # the Browser plugin. Unitl we have refactored the design in a
        # way that allow us to use these without accessing the browser
        # modules, we simple don't provide these options yet.
        return

        self.selection = self.resultview.selectedIndexes()

        deleteSupport = True
        exportSupport = True

        rowsselected = len(self.selection) / len(self.headerdata)

        if not rowsselected > 0:
            self.contextMenu.setEnabled(False)
            self.contextMenu.exec_(self.resultview.mapToGlobal(point))
            return

        self.contextMenu.setEnabled(True)
        # Look over at Browser plugin for implementation of
        # multiselect and operation support validation
        print rowsselected

        self.contextMenuView.setEnabled(True)
        if rowsselected == 1:
            self.contextMenuView.setText(self.str_VIEW_ITEM)
        else:
            self.contextMenuView.setText(self.str_VIEW_ITEMS)

        if deleteSupport:
            self.contextMenuDelete.setEnabled(True)
            if rowsselected == 1:
                self.contextMenuDelete.setText(self.str_DELETE_ITEM)
            else:
                self.contextMenuDelete.setText(self.str_DELETE_ITEMS)

        if exportSupport:
            self.contextMenuExport.setEnabled(True)
            if rowsselected == 1:
                self.contextMenuExport.setText(self.str_EXPORT_ITEM)
            else:
                self.contextMenuExport.setText(self.str_EXPORT_ITEMS)

        # Finally we execute the context menu
        self.contextMenu.exec_(self.resultview.mapToGlobal(point))

    def onViewItemsSelected(self):
        """Slot for the *view* context menu action.
        """
        raise NotImplementedError(
            'Need to implement a proper model for this to be supported')

    def onDeleteItemsSelected(self):
        """Slot for the *delete* context menu action.
        """
        msg = 'Delete from the Search Plugin is not implemented jet.'
        dialog = DeleteDialog(self, msg)
        dialog.setDeleteItems([])
        dialog.exec_()

    def onExportItemsSelected(self):
        """Slot for the 'export' context menu action.
        """
        msg = 'Export from the Search Plugin is not implemented jet.'
        dialog = ExportDialog(self, msg)
        # Only for proof of concept
        dialog.setExportData([])
        dialog.exec_()

    def onFilterBoxVisibilityChanged(self, visible):
        """Slot for the QKeySequence.Find.

        - `visible`: a boolean value indicating wether or not to toggle
          the filter box widget visibility on or off.
        """
        if visible:
            self.filterBox.setVisible(True)
            self.filterBox.inputEdit.setFocus()
        else:
            # I belive it's common practise to clear the filter when
            # the filter box is closed. This is at least the way the
            # filter boxes works for most webbrowsers.
            self.filterBox.inputEdit.clear()
            self.filterBox.setVisible(False)
            self.resultview.setFocus()

    def onFilterInputChanged(self, filter=''):
        """Slot for the filter input in the result filter widget.

        We get the selected syntax from the syntax combobox
        """
        # The PyQt4 QVariant is causing some problems here, when we try
        # to use the <combobox>.itemData directly, even though the data
        # holds valid QRexExp.PatternSyntax values.
        # We therefore need to explicitly make the QVariant and integer.
        i = self.filterBox.syntaxBox.currentIndex()
        syntaxIndex = self.filterBox.syntaxBox.itemData(i).toInt()[0]
        syntax = QtCore.QRegExp.PatternSyntax(syntaxIndex)
        # As of now we do filtering in a case insensitive way, until we
        # come up with a way to introduce case sensitivity selection in a
        # UI inexpensive way. We want to keep the filter widget as clean
        # and simple as possible.
        regex = QtCore.QRegExp(filter, QtCore.Qt.CaseInsensitive, syntax)
        self.proxymodel.setFilterRegExp(regex)

    def onFilterColumnChanged(self, index):
        """Slot for the column combobox in the filter box widget.
        """
        self.proxymodel.setFilterKeyColumn(index)

    def retranslate(self, all=True):
        """For dynamic translation support.
        """
        self.str_VIEW_ITEM = QtGui.QApplication.translate(
            'ResultView', 'View Item')
        self.str_VIEW_ITEMS = QtGui.QApplication.translate(
            'ResultView', 'View Items')
        self.str_DELETE_ITEM = QtGui.QApplication.translate(
            'ResultView', 'Delete Item')
        self.str_DELETE_ITEMS = QtGui.QApplication.translate(
            'ResultView', 'Delete Items')
        self.str_EXPORT_ITEM = QtGui.QApplication.translate(
            'ResultView', 'Export Item')
        self.str_EXPORT_ITEMS = QtGui.QApplication.translate(
            'ResultView', 'Export Items')
        self.str_NO_RESULT = QtGui.QApplication.translate(
            'ResultView', 'Sorry, no result to display!')
        self.str_BINARY_DATA = QtGui.QApplication.translate(
            'ResultView', 'Binary Data')
        if all:
            self.filterBox.retranslate()
Ejemplo n.º 4
0
class OpenedFileExplorer(DockWidget):
    """Opened File Explorer is list widget with list of opened files.
    It implements switching current file, files sorting. Uses _OpenedFileModel internally.
    Class instance created by Workspace.
    """
    def __init__(self, workspace):
        DockWidget.__init__(self, workspace, "&Opened Files",
                            QIcon(":/enkiicons/filtered.png"), "Alt+O")

        self._workspace = workspace

        self.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea)

        self.tvFiles = QTreeView(self)
        self.tvFiles.setHeaderHidden(True)
        self.tvFiles.setContextMenuPolicy(Qt.CustomContextMenu)
        self.tvFiles.setDragEnabled(True)
        self.tvFiles.setDragDropMode(QAbstractItemView.InternalMove)
        self.tvFiles.setRootIsDecorated(False)
        self.tvFiles.setTextElideMode(Qt.ElideMiddle)
        self.tvFiles.setUniformRowHeights(True)

        self.tvFiles.customContextMenuRequested.connect(
            self._onTvFilesCustomContextMenuRequested)

        self.setWidget(self.tvFiles)
        self.setFocusProxy(self.tvFiles)

        self.model = _OpenedFileModel(
            self)  # Not protected, because used by Configurator
        self.tvFiles.setModel(self.model)
        self.tvFiles.setAttribute(Qt.WA_MacShowFocusRect, False)
        self.tvFiles.setAttribute(Qt.WA_MacSmallSize)

        self._workspace.currentDocumentChanged.connect(
            self._onCurrentDocumentChanged)

        # disconnected by startModifyModel()
        self.tvFiles.selectionModel().selectionChanged.connect(
            self._onSelectionModelSelectionChanged)

        self.tvFiles.activated.connect(self._workspace.focusCurrentDocument)

        core.actionManager().addAction("mView/aOpenedFiles", self.showAction())

        core.uiSettingsManager().dialogAccepted.connect(self._applySettings)
        core.uiSettingsManager().aboutToExecute.connect(
            self._onSettingsDialogAboutToExecute)

    def del_(self):
        """Explicitly called destructor
        """
        core.actionManager().removeAction("mView/aOpenedFiles")

    def _applySettings(self):
        """Settings dialogue has been accepted.
        Apply settings
        """
        self.model.setSortMode(core.config()["Workspace"]["FileSortMode"])

    def _onSettingsDialogAboutToExecute(self, dialog):
        """UI settings dialogue is about to execute.
        Add own option
        """
        dialog.appendOption(
            ChoiseOption(
                dialog, core.config(), "Workspace/FileSortMode", {
                    dialog.rbOpeningOrder: "OpeningOrder",
                    dialog.rbFileName: "FileName",
                    dialog.rbUri: "URL",
                    dialog.rbSuffix: "Suffixes"
                }))

    def startModifyModel(self):
        """Blocks signals from model while it is modified by code
        """
        self.tvFiles.selectionModel().selectionChanged.disconnect(
            self._onSelectionModelSelectionChanged)

    def finishModifyModel(self):
        """Unblocks signals from model
        """
        self.tvFiles.selectionModel().selectionChanged.connect(
            self._onSelectionModelSelectionChanged)

    def _onSortTriggered(self, action):
        """ One of sort actions has been triggered in the opened file list context menu
        """
        mode = action.data().toString()
        self.model.setSortMode(mode)

    def _onCurrentDocumentChanged(self, oldDocument, currentDocument):  # pylint: disable=W0613
        """ Current document has been changed on workspace
        """
        if currentDocument is not None:
            index = self.model.documentIndex(currentDocument)

            self.startModifyModel()
            self.tvFiles.setCurrentIndex(index)
            # scroll the view
            self.tvFiles.scrollTo(index)
            self.finishModifyModel()

    def _onSelectionModelSelectionChanged(self, selected, deselected):  # pylint: disable=W0613
        """ Item selected in the list. Switch current document
        """
        if not selected.indexes():  # empty list, last file closed
            return

        index = selected.indexes()[0]
        # backup/restore current focused widget as setting active mdi window will steal it
        focusWidget = self.window().focusWidget()

        # set current document
        document = self._workspace.sortedDocuments[index.row()]
        self._workspace.setCurrentDocument(document)

        # restore focus widget
        if focusWidget:
            focusWidget.setFocus()

    def _onTvFilesCustomContextMenuRequested(self, pos):
        """Connected automatically by uic
        """
        menu = QMenu()

        menu.addAction(core.actionManager().action("mFile/mClose/aCurrent"))
        menu.addAction(core.actionManager().action("mFile/mSave/aCurrent"))
        menu.addAction(core.actionManager().action("mFile/mReload/aCurrent"))
        menu.addSeparator()

        # sort menu
        sortMenu = QMenu(self)
        group = QActionGroup(sortMenu)

        group.addAction(self.tr("Opening order"))
        group.addAction(self.tr("File name"))
        group.addAction(self.tr("URL"))
        group.addAction(self.tr("Suffixes"))
        group.triggered.connect(self._onSortTriggered)
        sortMenu.addActions(group.actions())

        for i, sortMode in enumerate(
            ["OpeningOrder", "FileName", "URL", "Suffixes"]):
            action = group.actions()[i]
            action.setData(sortMode)
            action.setCheckable(True)
            if sortMode == self.model.sortMode():
                action.setChecked(True)

        aSortMenu = QAction(self.tr("Sorting"), self)
        aSortMenu.setMenu(sortMenu)
        aSortMenu.setIcon(QIcon(":/enkiicons/sort.png"))
        aSortMenu.setToolTip(aSortMenu.text())

        menu.addAction(sortMenu.menuAction())
        menu.exec_(self.tvFiles.mapToGlobal(pos))
Ejemplo n.º 5
0
class DSBrowser(QWidget):
    """browser for datastorage databases
       Nodes are identified by a string, containing fields separated by '|'.
       - first filed is a capital letter:
       'R'oot, 'P'roject, sensor'G'roup, 'S'ensor and 'C'hart
       - second field is the database folder
       - third filed is the path of the node in the database
       - for charts the fourth field is the chart name (third is path of sensorgroup in this case)
    """
    def __init__(self):
        QWidget.__init__(self)
        self.layout = QVBoxLayout(self)
        self.layout.setSpacing(0)
        self.layout.setMargin(0)

        self.toolBar = QFrame(self)
        self.toolBarLayout = QHBoxLayout(self.toolBar)
        self.toolBarLayout.setMargin(2)
        self.toolBarLayout.setSpacing(2)
        self.layout.addWidget(self.toolBar)

        self.loadButton = QToolButton(self.toolBar)
        self.loadButton.setText(QCoreApplication.translate('DataStorageBrowser', 'Open...'))
        self.loadButton.setIcon(QIcon(QPixmap(SimuVis4.Icons.fileOpen)))
        self.loadButton.setToolTip(QCoreApplication.translate('DataStorageBrowser', 'Open a datastorage database'))
        self.toolBarLayout.addWidget(self.loadButton)
        self.connect(self.loadButton, SIGNAL('pressed()'), self.loadDatabase)

        self.dropButton = QToolButton(self.toolBar)
        self.dropButton.setText(QCoreApplication.translate('DataStorageBrowser', 'Close All'))
        self.dropButton.setIcon(QIcon(QPixmap(SimuVis4.Icons.clear)))
        self.dropButton.setToolTip(QCoreApplication.translate('DataStorageBrowser', 'Drop all open databases'))
        self.toolBarLayout.addWidget(self.dropButton)
        self.connect(self.dropButton, SIGNAL('pressed()'), self.dropDatabases)
        self.dropButton.setEnabled(False)

        self.toolBarLayout.addStretch(100)

        self.splitter = QSplitter(self)
        self.splitter.setOrientation(Qt.Vertical)
        self.treeView = QTreeView(self.splitter)
        self.treeView.setAlternatingRowColors(True)
        self.treeView.setEditTriggers(QAbstractItemView.NoEditTriggers)
        self.treeView.setContextMenuPolicy(Qt.CustomContextMenu)
        self.treeView.setAutoExpandDelay(500)
        self.textBrowser = QTextBrowser(self.splitter)
        self.layout.addWidget(self.splitter)
        self.splitter.setStretchFactor(0, 60)
        self.splitter.setStretchFactor(1, 40)
        self.model = DSModel()
        self.treeView.setModel(self.model)
        self.treeView.setSortingEnabled(True)
        self.treeView.expandAll()
        self.connect(self.treeView.selectionModel(), SIGNAL("currentChanged(QModelIndex, QModelIndex)"), self.showItem)
        self.connect(self.treeView, SIGNAL("doubleClicked(QModelIndex)"), self.itemAction)
        self.connect(self.treeView, SIGNAL("customContextMenuRequested(QPoint)"), self.showContextMenu)
        self.selectedNode = None
        self.selectedMI = None


    def loadDatabase(self, dn=None):
        """load a database"""
        if not dn:
            dn = QFileDialog.getExistingDirectory(self, QCoreApplication.translate('DataStorageBrowser',
                "Select a folder containing a datastorage database"), SimuVis4.Globals.defaultFolder)
            if not dn.isEmpty():
                dn = unicode(dn)
                SimuVis4.Globals.defaultFolder = dn
            else:
                return
        self.model.addDatabase(dn)
        self.treeView.expandToDepth(1)


    def dropDatabases(self):
        # FIXME: implement it
        pass


    def showItem(self, mi, pr):
        """show the item at model index mi"""
        t, n = self.model.dsNode(mi)
        txt = ""
        if t == 'R':
            # FIXME: no metadata?
            txt = rootInfo.substitute(name=n.name, title=escape(n.title), folder=n.h5dir, projects=len(n)) # + formatMetaData(n)
        elif t == 'P':
            txt = projectInfo.substitute(name=n.name, title=escape(n.title), groups=len(n)) + formatMetaData(n)
        elif t == 'G':
            txt = groupInfo.substitute(name=n.name, title=escape(n.title), sensors=len(n), charts=len(n.charts)) + formatMetaData(n)
        elif t == 'S':
            txt = sensorInfo.substitute(name=n.name, title=escape(n.title), start=n.timegrid.start, stop=n.timegrid.stop,
                step=n.timegrid.step, length=n.datalen()) + formatMetaData(n)
        elif t == 'C':
            txt = chartInfo.substitute(name=n.name)
        self.textBrowser.setText(txt)


    def itemAction(self, mi):
        """default action (on doubleclick) for item at model index mi"""
        t, n = self.model.dsNode(mi)
        if t == 'R':
            pass
        elif t == 'P':
            pass
        elif t == 'G':
            pass
        elif t == 'S':
            print n
            self.showQwtPlot(n)
        elif t == 'C':
            self.showMplChart(n)


    def showContextMenu(self, pos):
        """show context menu for item at pos"""
        mi = self.treeView.indexAt(pos)
        t, n = self.model.dsNode(mi)
        self.selectedNode = n
        self.selectedMI = mi
        m = QMenu()
        if t in 'RPGS':
            p = m.addAction(QCoreApplication.translate('DataStorageBrowser', 'Edit metadata'), self.editMetadata)
        if t == 'R':
            pass
        elif t == 'P':
            pass
        elif t == 'G':
            m.addAction(QCoreApplication.translate('DataStorageBrowser', 'Add Chart'), self.newChart)
        elif t == 'S':
            m.addAction(QCoreApplication.translate('DataStorageBrowser', 'Plot (Qwt)'), self.showQwtPlot)
        elif t == 'C':
            m.addAction(QCoreApplication.translate('DataStorageBrowser', 'Show'), self.showMplChart)
            m.addAction(QCoreApplication.translate('DataStorageBrowser', 'Delete'), self.deleteItem)
        a = m.exec_(self.treeView.mapToGlobal(pos))


    def showMplChart(self, node=None):
        if node is None:
            node = self.selectedNode
        showChartMplWindow(node, maximized=showChartMaximized)


    def showQwtPlot(self, node=None):
        if node is None:
            node = self.selectedNode
        showQwtPlotWindow(node, maximized=showChartMaximized)


    def editMetadata(self, node=None):
        if node is None:
            node = self.selectedNode
        editMetadata(node)


    def newChart(self, mi=None):
        """add a chart to sensorgroup at mi using the wizard"""
        if mi is None:
            mi = self.selectedMI
        showNewChartWizard(self.model, mi)

    def deleteItem(self, mi=None):
        """delete the item at mi"""
        if mi is None:
            mi = self.selectedMI
        self.model.deleteItem(mi)
Ejemplo n.º 6
0
class DSBrowser(QWidget):
    """browser for datastorage databases
       Nodes are identified by a string, containing fields separated by '|'.
       - first filed is a capital letter:
       'R'oot, 'P'roject, sensor'G'roup, 'S'ensor and 'C'hart
       - second field is the database folder
       - third filed is the path of the node in the database
       - for charts the fourth field is the chart name (third is path of sensorgroup in this case)
    """
    def __init__(self):
        QWidget.__init__(self)
        self.layout = QVBoxLayout(self)
        self.layout.setSpacing(0)
        self.layout.setMargin(0)

        self.toolBar = QFrame(self)
        self.toolBarLayout = QHBoxLayout(self.toolBar)
        self.toolBarLayout.setMargin(2)
        self.toolBarLayout.setSpacing(2)
        self.layout.addWidget(self.toolBar)

        self.loadButton = QToolButton(self.toolBar)
        self.loadButton.setText(QCoreApplication.translate('DataStorageBrowser', 'Open...'))
        self.loadButton.setIcon(QIcon(QPixmap(SimuVis4.Icons.fileOpen)))
        self.loadButton.setToolTip(QCoreApplication.translate('DataStorageBrowser', 'Open a datastorage database'))
        self.toolBarLayout.addWidget(self.loadButton)
        self.connect(self.loadButton, SIGNAL('pressed()'), self.loadDatabase)

        self.expandButton = QToolButton(self.toolBar)
        self.expandButton.setText('Expand/Collapse')
        self.expandButton.setIcon(QIcon(QPixmap(Icons.exp_col)))
        self.expandButton.setToolTip(QCoreApplication.translate('DataStorageBrowser', 'Expand or collapse the whole tree'))
        self.toolBarLayout.addWidget(self.expandButton)
        self.connect(self.expandButton, SIGNAL('pressed()'), self.expandCollapseAll)

        self.searchInput = QLineEdit(self.toolBar)
        self.searchInput.setText(QCoreApplication.translate('DataStorageBrowser', 'Enter search text here'))
        self.searchInput.setToolTip(QCoreApplication.translate('DataStorageBrowser',
            'Enter search text using wildcards here, press ENTER again to go to next match!'))
        self.toolBarLayout.addWidget(self.searchInput, 100)
        self.connect(self.searchInput, SIGNAL('returnPressed()'), self.searchItem)

        self.helpButton = QToolButton(self.toolBar)
        self.helpButton.setText(QCoreApplication.translate('DataStorageBrowser', 'Help'))
        self.helpButton.setIcon(QIcon(QPixmap(SimuVis4.Icons.help)))
        self.helpButton.setToolTip(QCoreApplication.translate('DataStorageBrowser', 'Show help for DataStorageBrowser'))
        self.toolBarLayout.addWidget(self.helpButton)
        self.connect(self.helpButton, SIGNAL('pressed()'), self.showHelp)

        self.splitter = QSplitter(self)
        self.splitter.setOrientation(Qt.Vertical)
        self.treeView = QTreeView(self.splitter)
        self.treeView.setAlternatingRowColors(True)
        self.treeView.setEditTriggers(QAbstractItemView.NoEditTriggers)
        self.treeView.setContextMenuPolicy(Qt.CustomContextMenu)
        self.treeView.setAutoExpandDelay(500)
        self.textBrowser = QTextBrowser(self.splitter)
        self.layout.addWidget(self.splitter)
        self.splitter.setStretchFactor(0, 60)
        self.splitter.setStretchFactor(1, 40)
        self.model = DSModel()
        self.treeView.setModel(self.model)
        self.treeView.setSortingEnabled(True)
        self.treeView.expandAll()
        self.connect(self.treeView.selectionModel(), SIGNAL("currentChanged(QModelIndex, QModelIndex)"), self.showItem)
        self.connect(self.treeView, SIGNAL("doubleClicked(QModelIndex)"), self.itemAction)
        self.connect(self.treeView, SIGNAL("customContextMenuRequested(QPoint)"), self.showContextMenu)
        self.selectedNode = None
        self.selectedMI = None
        self.searchText = ''
        self.searchResults = []
        self.collExpand = SimuVis4.Misc.Switcher()


    def loadDatabase(self, dn=None):
        """load a database"""
        if not dn:
            dn = QFileDialog.getExistingDirectory(self, QCoreApplication.translate('DataStorageBrowser',
                "Select a folder containing a datastorage database"), SimuVis4.Globals.defaultFolder)
            if not dn.isEmpty():
                dn = unicode(dn)
                SimuVis4.Globals.defaultFolder = dn
            else:
                return
        self.model.addDatabase(dn)
        self.treeView.expandToDepth(1)
        self.treeView.resizeColumnToContents(0)


    def showHelp(self):
        SimuVis4.HelpBrowser.showHelp('/plugin/DataStorageBrowser/index.html')


    def showItem(self, mi, pr):
        """show the item at model index mi"""
        t, n = self.model.dsNode(mi)
        txt = ""
        if t == 'R':
            # FIXME: no metadata?
            txt = rootInfo.substitute(name=n.name, title=escape(n.title), folder=n.h5dir,
                projects=len(n)) + formatMetaData(n)
        elif t == 'P':
            txt = projectInfo.substitute(name=n.name, path=escape(n.parent.name),
                title=escape(n.title), groups=len(n)) + formatMetaData(n)
        elif t == 'G':
            txt = groupInfo.substitute(name=n.name,  path='/'.join(n.path.split('/')[:-1]),
                title=escape(n.title), sensors=len(n), charts=len(n.charts)) + formatMetaData(n)
        elif t == 'S':
            txt = sensorInfo.substitute(name=n.name,  path='/'.join(n.path.split('/')[:-1]),
                title=escape(n.title), start=n.timegrid.start, stop=n.timegrid.stop,
                step=n.timegrid.step, length=n.datalen()) + formatMetaData(n)
        elif t == 'C':
            txt = chartInfo.substitute(name=n.name, path=n.sensorgroup.path)
        self.textBrowser.setText(txt)


    def searchItem(self):
        """execute the search and highlight the (next) result"""
        txt = str(self.searchInput.text())
        if txt != self.searchText:
            self.searchText = txt
            tmp = self.model.findItems(txt,
                Qt.MatchFixedString | Qt.MatchContains | Qt.MatchWildcard | Qt.MatchRecursive)
            self.searchList = [i.index() for i in tmp]
        if self.searchList:
            mi = self.searchList.pop()
            self.treeView.setCurrentIndex(mi)
            self.treeView.expand(mi)
            self.treeView.scrollTo(mi)
        else:
            QMessageBox.information(self, QCoreApplication.translate('DataStorageBrowser',
                'No (more) matches!'), QCoreApplication.translate('DataStorageBrowser',
                'No (more) matches found! Change you search criteria and try again!'))
            self.searchText = ''


    def expandCollapseAll(self):
        if self.collExpand():
            self.treeView.collapseAll()
        else:
            self.treeView.expandAll()

    def itemAction(self, mi):
        """default action (on doubleclick) for item at model index mi"""
        t, n = self.model.dsNode(mi)
        if t == 'R':
            pass
        elif t == 'P':
            pass
        elif t == 'G':
            pass
        elif t == 'S':
            self.showQwtPlot(n)
        elif t == 'C':
            self.showMplChart(n)


    def showContextMenu(self, pos):
        """show context menu for item at pos"""
        mi = self.treeView.indexAt(pos)
        t, n = self.model.dsNode(mi)
        self.selectedNode = n
        self.selectedMI = mi
        m = QMenu()
        if t == 'R':
            m.addAction(QCoreApplication.translate('DataStorageBrowser', 'Close'), self.closeDatabase)
        elif t == 'P':
            pass
        elif t == 'G':
            nCharts = len(n.charts)
            if nCharts > 0:
                txt = str(QCoreApplication.translate('DataStorageBrowser', 'Show all Charts (%d)')) % nCharts
                m.addAction(txt, self.showAllCharts)
            m.addAction(QCoreApplication.translate('DataStorageBrowser', 'Add Chart'), self.newChart)
        elif t == 'S':
            m.addAction(QCoreApplication.translate('DataStorageBrowser', 'Plot (Qwt)'), self.showQwtPlot)
        elif t == 'C':
            m.addAction(QCoreApplication.translate('DataStorageBrowser', 'Show'), self.showMplChart)
            m.addAction(QCoreApplication.translate('DataStorageBrowser', 'Delete'), self.deleteItem)
        if t in 'RPGS':
            m.addSeparator()
            m.addAction(QCoreApplication.translate('DataStorageBrowser', 'Edit metadata'), self.editMetadata)
        a = m.exec_(self.treeView.mapToGlobal(pos))


    def showMplChart(self, node=None):
        if node is None:
            node = self.selectedNode
        showChartMplWindow(node, maximized=showChartMaximized)


    def showAllCharts(self, node=None):
        if node is None:
            node = self.selectedNode
        for chart in node.charts.values():
            showChartMplWindow(chart, maximized=showChartMaximized)


    def showQwtPlot(self, node=None):
        if node is None:
            node = self.selectedNode
        showQwtPlotWindow(node, maximized=showChartMaximized)


    def editMetadata(self, node=None):
        if node is None:
            node = self.selectedNode
        editMetadata(node)


    def closeDatabase(self, mi=None):
        if mi is None:
            mi = self.selectedMI
        self.model.closeDatabase(mi)


    def newChart(self, mi=None):
        """add a chart to sensorgroup at mi using the wizard"""
        if mi is None:
            mi = self.selectedMI
        showNewChartWizard(self.model, mi)


    def deleteItem(self, mi=None):
        """delete the item at mi"""
        if mi is None:
            mi = self.selectedMI
        self.model.deleteItem(mi)
Ejemplo n.º 7
0
class ResultView(QWidget):
    """This class represent a search result view.
    """
    def __init__(self, filter='', attributes=[], resultlist=[], parent=None):
        """Initialize a result view for the `SearchPlugin`.

        :param filter: the filter applied on the search
        :type filter: string
        :param attributes: a list containing the attributes used in the
         search operation. Usually extracted from the `filter`.
        :type attributes: list
        :param resultlist: a list of `SmartDataObject` from the search
         operation.
        :type resultlist: list
        :param parent: the parent for this widget.
        :type parent: QWidget
        """
        super(ResultView, self).__init__(parent)
        self.setObjectName('ResultView')
        self.layout = QtGui.QVBoxLayout(self)

        # Only display the no-result message if resultlist is empty
        if len(resultlist) == 0:
            self.retranslate(all=False)
            self.onNoResult()
            return

        # The proxy model is used for sort and filter support
        self.proxymodel = QSortFilterProxyModel(self)
        self.proxymodel.setDynamicSortFilter(True)

        self.headerdata = ['dn']
        self.headerdata.extend(attributes)
        self.resultdata = resultlist

        # FIXME: should we create a custom item model ?
        self.model = QStandardItemModel(0, len(self.headerdata), parent=self)
        #self.model = ResultItemModel(self)
        #self.model = ResultItemModel(self.headerdata, self.resultdata, self)
        self.proxymodel.setSourceModel(self.model)

        self.resultview = QTreeView(self)
        self.resultview.setUniformRowHeights(True)
        self.resultview.setRootIsDecorated(False)
        self.resultview.setAlternatingRowColors(True)
        self.resultview.setSortingEnabled(True)

        self.resultview.setModel(self.proxymodel)

        # For right-click context menu
        self.resultview.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
        self.resultview.setSelectionMode(QAbstractItemView.ExtendedSelection)

        self.layout.addWidget(self.resultview)
        # The filter box enables the user to filter the returned search
        # results. It becomes accessible with Ctrl-F (QKeySequence.Find)
        self.filterBox = ResultFilterWidget(self.headerdata, parent=self)
        self.filterBox.setVisible(False)

        self.layout.addWidget(self.filterBox)

        # We need to call the retranslate method before populating
        # the result data
        self.retranslate()
        #self.model.populateHeader(self.headerdata)
        #self.model.populateModel(self.resultdata)
        self.setHeaderData(self.headerdata)
        self.setResultData(self.resultdata)
        self.resultview.resizeColumnToContents(0)
        self.__createContextMenu()
        self.__connectSlots()

    def __connectSlots(self):
        """Connect signal and slots.
        """
        self.resultview.customContextMenuRequested.connect(
            self.onContextMenuRequested)
        self.filterBox.inputEdit.textChanged['QString'].connect(
            self.onFilterInputChanged)
        self.filterBox.columnBox.currentIndexChanged[int].connect(
            self.onFilterColumnChanged)

    def __getVSpacer(self):
        return QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding,
                                 QtGui.QSizePolicy.Minimum)

    def __createContextMenu(self):
        """Display the context menu.
        """
        self.contextMenu = QtGui.QMenu()
        self.contextMenuView = QtGui.QAction(self)
        self.contextMenuDelete = QtGui.QAction(self)
        self.contextMenuExport = QtGui.QAction(self)
        self.contextMenu.addAction(self.contextMenuView)
        self.contextMenu.addAction(self.contextMenuDelete)
        self.contextMenu.addAction(self.contextMenuExport)

        # Connect the context menu actions to the correct slots
        self.contextMenuView.triggered.connect(self.onViewItemsSelected)
        self.contextMenuDelete.triggered.connect(self.onDeleteItemsSelected)
        self.contextMenuExport.triggered.connect(self.onExportItemsSelected)

    def onNoResult(self):
        """Adds a styled *no result* message to the main layout.
        """
        font = QtGui.QFont()
        font.setBold(True)
        sadface = QtGui.QLabel(self)
        sadface.setPixmap(pixmapFromTheme('face-sad', ':/icons/48/face-sad'))
        noresult = QtGui.QLabel(self)
        noresult.setText(self.str_NO_RESULT)
        noresult.setFont(font)
        hlayout = QtGui.QHBoxLayout()
        hlayout.addItem(self.__getVSpacer())
        hlayout.addWidget(sadface)
        hlayout.addWidget(noresult)
        hlayout.addItem(self.__getVSpacer())

        self.layout.addLayout(hlayout)

    def setHeaderData(self, data=[]):
        """Populates the ``resultview`` model with header data.

        Parameters:

        - `data`: a list with header items. Usually this is the
          attributelist from the LDAP search.
        """
        i = 0
        for header in data:
            self.model.setHeaderData(i, QtCore.Qt.Horizontal, header)
            i += 1

    def setResultData(self, data=[]):
        """Populates the ``resultview`` model with result data.

        Parameters:

        - `data`: a list containing the SmartDataObjects representing
          items in the LDAP search result.
        """
        row = 0
        for obj in data:
            self.model.insertRow(row)
            col = 0
            for attr in self.headerdata:
                if self.isDistinguishedName(attr):
                    modelData = obj.getPrettyDN()
                elif self.isObjectClass(attr):
                    modelData = ','.join(obj.getObjectClasses())
                elif obj.hasAttribute(attr):
                    if obj.isAttributeBinary(attr):
                        modelData = self.str_BINARY_DATA
                    else:
                        modelData = ','.join(obj.getAttributeValueList(attr))

                self.model.setData(self.model.index(row, col), modelData)
                col += 1

            row += 1

    def isDistinguishedName(self, attr):
        """Returns ``True`` if `attr` is a distinguished name,
        ``False`` otherwise.

        Parameters:

        - `attr`: the LDAP string attribute value to check.
        """
        return attr.lower() == 'dn'

    def isObjectClass(self, attr):
        """Returns ``True`` if `attr` is an object class, ``False``
        otherwise.

        Parameters:

        - `attr`: the LDAP string attribute value to check.
        """
        return attr.lower() == 'objectclass'

    def onContextMenuRequested(self, point):
        """Display the context menu
        """

        # FIXME: In order to be able to export, delete and view search
        # result entries. We should make use of the various dialogs in
        # the Browser plugin. Unitl we have refactored the design in a
        # way that allow us to use these without accessing the browser
        # modules, we simple don't provide these options yet.
        return

        self.selection = self.resultview.selectedIndexes()

        deleteSupport = True
        exportSupport = True

        rowsselected = len(self.selection) / len(self.headerdata)

        if not rowsselected > 0:
            self.contextMenu.setEnabled(False)
            self.contextMenu.exec_(self.resultview.mapToGlobal(point))
            return

        self.contextMenu.setEnabled(True)
        # Look over at Browser plugin for implementation of
        # multiselect and operation support validation
        print rowsselected

        self.contextMenuView.setEnabled(True)
        if rowsselected == 1:
            self.contextMenuView.setText(self.str_VIEW_ITEM)
        else:
            self.contextMenuView.setText(self.str_VIEW_ITEMS)

        if deleteSupport:
            self.contextMenuDelete.setEnabled(True)
            if rowsselected == 1:
                self.contextMenuDelete.setText(self.str_DELETE_ITEM)
            else:
                self.contextMenuDelete.setText(self.str_DELETE_ITEMS)

        if exportSupport:
            self.contextMenuExport.setEnabled(True)
            if rowsselected == 1:
                self.contextMenuExport.setText(self.str_EXPORT_ITEM)
            else:
                self.contextMenuExport.setText(self.str_EXPORT_ITEMS)

        # Finally we execute the context menu
        self.contextMenu.exec_(self.resultview.mapToGlobal(point))

    def onViewItemsSelected(self):
        """Slot for the *view* context menu action.
        """
        raise NotImplementedError(
            'Need to implement a proper model for this to be supported')

    def onDeleteItemsSelected(self):
        """Slot for the *delete* context menu action.
        """
        msg = 'Delete from the Search Plugin is not implemented jet.'
        dialog = DeleteDialog(self, msg)
        dialog.setDeleteItems([])
        dialog.exec_()

    def onExportItemsSelected(self):
        """Slot for the 'export' context menu action.
        """
        msg = 'Export from the Search Plugin is not implemented jet.'
        dialog = ExportDialog(self, msg)
        # Only for proof of concept
        dialog.setExportData([])
        dialog.exec_()

    def onFilterBoxVisibilityChanged(self, visible):
        """Slot for the QKeySequence.Find.

        - `visible`: a boolean value indicating wether or not to toggle
          the filter box widget visibility on or off.
        """
        if visible:
            self.filterBox.setVisible(True)
            self.filterBox.inputEdit.setFocus()
        else:
            # I belive it's common practise to clear the filter when
            # the filter box is closed. This is at least the way the
            # filter boxes works for most webbrowsers.
            self.filterBox.inputEdit.clear()
            self.filterBox.setVisible(False)
            self.resultview.setFocus()

    def onFilterInputChanged(self, filter=''):
        """Slot for the filter input in the result filter widget.

        We get the selected syntax from the syntax combobox
        """
        # The PyQt4 QVariant is causing some problems here, when we try
        # to use the <combobox>.itemData directly, even though the data
        # holds valid QRexExp.PatternSyntax values.
        # We therefore need to explicitly make the QVariant and integer.
        i = self.filterBox.syntaxBox.currentIndex()
        syntaxIndex = self.filterBox.syntaxBox.itemData(i).toInt()[0]
        syntax = QtCore.QRegExp.PatternSyntax(syntaxIndex)
        # As of now we do filtering in a case insensitive way, until we
        # come up with a way to introduce case sensitivity selection in a
        # UI inexpensive way. We want to keep the filter widget as clean
        # and simple as possible.
        regex = QtCore.QRegExp(filter, QtCore.Qt.CaseInsensitive, syntax)
        self.proxymodel.setFilterRegExp(regex)

    def onFilterColumnChanged(self, index):
        """Slot for the column combobox in the filter box widget.
        """
        self.proxymodel.setFilterKeyColumn(index)

    def retranslate(self, all=True):
        """For dynamic translation support.
        """
        self.str_VIEW_ITEM = QtGui.QApplication.translate(
            'ResultView', 'View Item')
        self.str_VIEW_ITEMS = QtGui.QApplication.translate(
            'ResultView', 'View Items')
        self.str_DELETE_ITEM = QtGui.QApplication.translate(
            'ResultView', 'Delete Item')
        self.str_DELETE_ITEMS = QtGui.QApplication.translate(
            'ResultView', 'Delete Items')
        self.str_EXPORT_ITEM = QtGui.QApplication.translate(
            'ResultView', 'Export Item')
        self.str_EXPORT_ITEMS = QtGui.QApplication.translate(
            'ResultView', 'Export Items')
        self.str_NO_RESULT = QtGui.QApplication.translate(
            'ResultView', 'Sorry, no result to display!')
        self.str_BINARY_DATA = QtGui.QApplication.translate(
            'ResultView', 'Binary Data')
        if all:
            self.filterBox.retranslate()
Ejemplo n.º 8
0
class OpenedFileExplorer(DockWidget):
    """Opened File Explorer is list widget with list of opened files.
    It implements switching current file, files sorting. Uses _OpenedFileModel internally.
    Class instance created by Workspace.
    """
    def __init__(self, workspace):
        DockWidget.__init__(self, workspace, "&Opened Files",
                            QIcon(":/enkiicons/filtered.png"), "Alt+O")

        self._workspace = workspace

        self.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea)

        self.tvFiles = QTreeView(self)
        self.tvFiles.setHeaderHidden(True)
        self.tvFiles.setEditTriggers(QAbstractItemView.SelectedClicked)
        self.tvFiles.setContextMenuPolicy(Qt.CustomContextMenu)
        self.tvFiles.setDragEnabled(True)
        self.tvFiles.setDragDropMode(QAbstractItemView.InternalMove)
        self.tvFiles.setRootIsDecorated(False)
        self.tvFiles.setTextElideMode(Qt.ElideMiddle)
        self.tvFiles.setUniformRowHeights(True)

        self.tvFiles.customContextMenuRequested.connect(
            self._onTvFilesCustomContextMenuRequested)

        self.setWidget(self.tvFiles)
        self.setFocusProxy(self.tvFiles)

        self.model = _OpenedFileModel(
            self)  # Not protected, because used by Configurator
        self.tvFiles.setModel(self.model)
        self.tvFiles.setAttribute(Qt.WA_MacShowFocusRect, False)
        self.tvFiles.setAttribute(Qt.WA_MacSmallSize)

        self._workspace.currentDocumentChanged.connect(
            self._onCurrentDocumentChanged)

        # disconnected by startModifyModel()
        self.tvFiles.selectionModel().selectionChanged.connect(
            self._onSelectionModelSelectionChanged)

        self.tvFiles.activated.connect(self._workspace.focusCurrentDocument)

        core.actionManager().addAction("mView/aOpenedFiles", self.showAction())

    def del_(self):
        """Explicitly called destructor
        """
        core.actionManager().removeAction("mView/aOpenedFiles")

    def startModifyModel(self):
        """Blocks signals from model while it is modified by code
        """
        self.tvFiles.selectionModel().selectionChanged.disconnect(
            self._onSelectionModelSelectionChanged)

    def finishModifyModel(self):
        """Unblocks signals from model
        """
        self.tvFiles.selectionModel().selectionChanged.connect(
            self._onSelectionModelSelectionChanged)

    def _onCurrentDocumentChanged(self, oldDocument, currentDocument):  # pylint: disable=W0613
        """ Current document has been changed on workspace
        """
        if currentDocument is not None:
            index = self.model.documentIndex(currentDocument)

            self.startModifyModel()
            self.tvFiles.setCurrentIndex(index)
            # scroll the view
            self.tvFiles.scrollTo(index)
            self.finishModifyModel()

    def _onSelectionModelSelectionChanged(self, selected, deselected):  # pylint: disable=W0613
        """ Item selected in the list. Switch current document
        """
        if not selected.indexes():  # empty list, last file closed
            return

        index = selected.indexes()[0]
        # backup/restore current focused widget as setting active mdi window will steal it
        focusWidget = self.window().focusWidget()

        # set current document
        document = self._workspace.sortedDocuments[index.row()]
        self._workspace.setCurrentDocument(document)

        # restore focus widget
        if focusWidget:
            focusWidget.setFocus()

    def _onTvFilesCustomContextMenuRequested(self, pos):
        """Connected automatically by uic
        """
        menu = QMenu()

        menu.addAction(core.actionManager().action("mFile/mClose/aCurrent"))
        menu.addAction(core.actionManager().action("mFile/mSave/aCurrent"))
        menu.addAction(core.actionManager().action("mFile/mReload/aCurrent"))
        menu.addSeparator()
        menu.addAction(
            core.actionManager().action("mFile/mFileSystem/aRename"))
        toggleExecutableAction = core.actionManager().action(
            "mFile/mFileSystem/aToggleExecutable")
        if toggleExecutableAction:  # not available on Windows
            menu.addAction(toggleExecutableAction)
        core.actionManager().action("mFile/mFileSystem").menu(
        ).aboutToShow.emit()  # to update aToggleExecutable

        menu.exec_(self.tvFiles.mapToGlobal(pos))