Exemplo n.º 1
1
class FilenamePrompt(_BasePrompt):

    """A prompt for a filename."""

    def __init__(self, question, parent=None):
        super().__init__(question, parent)
        self._init_texts(question)
        self._init_fileview()
        self._set_fileview_root(question.default)

        self._lineedit = LineEdit(self)
        if question.default:
            self._lineedit.setText(question.default)
        self._lineedit.textEdited.connect(self._set_fileview_root)
        self._vbox.addWidget(self._lineedit)

        self.setFocusProxy(self._lineedit)
        self._init_key_label()

        if config.get('ui', 'prompt-filebrowser'):
            self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)

    @pyqtSlot(str)
    def _set_fileview_root(self, path, *, tabbed=False):
        """Set the root path for the file display."""
        separators = os.sep
        if os.altsep is not None:
            separators += os.altsep

        dirname = os.path.dirname(path)

        try:
            if not path:
                pass
            elif path in separators and os.path.isdir(path):
                # Input "/" -> don't strip anything
                pass
            elif path[-1] in separators and os.path.isdir(path):
                # Input like /foo/bar/ -> show /foo/bar/ contents
                path = path.rstrip(separators)
            elif os.path.isdir(dirname) and not tabbed:
                # Input like /foo/ba -> show /foo contents
                path = dirname
            else:
                return
        except OSError:
            log.prompt.exception("Failed to get directory information")
            return

        root = self._file_model.setRootPath(path)
        self._file_view.setRootIndex(root)

    @pyqtSlot(QModelIndex)
    def _insert_path(self, index, *, clicked=True):
        """Handle an element selection.

        Args:
            index: The QModelIndex of the selected element.
            clicked: Whether the element was clicked.
        """
        path = os.path.normpath(self._file_model.filePath(index))
        if clicked:
            path += os.sep
        else:
            # On Windows, when we have C:\foo and tab over .., we get C:\
            path = path.rstrip(os.sep)

        log.prompt.debug('Inserting path {}'.format(path))
        self._lineedit.setText(path)
        self._lineedit.setFocus()
        self._set_fileview_root(path, tabbed=True)
        if clicked:
            # Avoid having a ..-subtree highlighted
            self._file_view.setCurrentIndex(QModelIndex())

    def _init_fileview(self):
        self._file_view = QTreeView(self)
        self._file_model = QFileSystemModel(self)
        self._file_view.setModel(self._file_model)
        self._file_view.clicked.connect(self._insert_path)

        if config.get('ui', 'prompt-filebrowser'):
            self._vbox.addWidget(self._file_view)
        else:
            self._file_view.hide()

        # Only show name
        self._file_view.setHeaderHidden(True)
        for col in range(1, 4):
            self._file_view.setColumnHidden(col, True)
        # Nothing selected initially
        self._file_view.setCurrentIndex(QModelIndex())
        # The model needs to be sorted so we get the correct first/last index
        self._file_model.directoryLoaded.connect(
            lambda: self._file_model.sort(0))

    def accept(self, value=None):
        text = value if value is not None else self._lineedit.text()
        text = downloads.transform_path(text)
        if text is None:
            message.error("Invalid filename")
            return False
        self.question.answer = text
        return True

    def item_focus(self, which):
        # This duplicates some completion code, but I don't see a nicer way...
        assert which in ['prev', 'next'], which
        selmodel = self._file_view.selectionModel()

        parent = self._file_view.rootIndex()
        first_index = self._file_model.index(0, 0, parent)
        row = self._file_model.rowCount(parent) - 1
        last_index = self._file_model.index(row, 0, parent)

        if not first_index.isValid():
            # No entries
            return

        assert last_index.isValid()

        idx = selmodel.currentIndex()
        if not idx.isValid():
            # No item selected yet
            idx = last_index if which == 'prev' else first_index
        elif which == 'prev':
            idx = self._file_view.indexAbove(idx)
        else:
            assert which == 'next', which
            idx = self._file_view.indexBelow(idx)

        # wrap around if we arrived at beginning/end
        if not idx.isValid():
            idx = last_index if which == 'prev' else first_index

        selmodel.setCurrentIndex(
            idx, QItemSelectionModel.ClearAndSelect | QItemSelectionModel.Rows)
        self._insert_path(idx, clicked=False)

    def _allowed_commands(self):
        return [('prompt-accept', 'Accept'), ('leave-mode', 'Abort')]
Exemplo n.º 2
0
def _mock_view_index(model, category_idx, child_idx, qtbot):
    """Create a tree view from a model and set the current index.

    Args:
        model: model to create a fake view for.
        category_idx: index of the category to select.
        child_idx: index of the child item under that category to select.
    """
    view = QTreeView()
    qtbot.add_widget(view)
    view.setModel(model)
    idx = model.indexFromItem(model.item(category_idx).child(child_idx))
    view.setCurrentIndex(idx)
    return view
Exemplo n.º 3
0
class QTreeSelection(QToolButton):
    currentIndexChanged = pyqtSignal(QModelIndex, QModelIndex)
    currentDataChanged = pyqtSignal(object)

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.setPopupMode(QToolButton.MenuButtonPopup)
        self.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)

        self.tree = QTreeView(self)
        self.tree.setMinimumWidth(640)
        self.tree.setSelectionMode(QTreeView.SingleSelection)
        self.tree.setSelectionBehavior(QTreeView.SelectRows)

        act = QWidgetAction(self)
        act.setDefaultWidget(self.tree)

        self.menu = QMenu(self)
        self.menu.addAction(act)
        self.setMenu(self.menu)
        self.clicked.connect(self.showMenu)

    def _currentIndexChanged(self, newindex, oldindex):
        self.menu.close()
        selected = newindex.sibling(newindex.row(), 0)

        display = selected.data(Qt.DisplayRole)
        icon = selected.data(Qt.DecorationRole)

        self.setText(display or "")
        self.setIcon(icon or QIcon())
        self.currentIndexChanged.emit(newindex, oldindex)
        self.currentDataChanged.emit(newindex.data(Qt.UserRole))

    def currentIndex(self):
        return self.tree.currentIndex()

    def currentData(self):
        return self.currentIndex().data(Qt.UserRole)

    def setCurrentIndex(self, index):
        return self.tree.setCurrentIndex(index)

    def model(self):
        return self.tree.model()

    def setModel(self, model):
        self.tree.setModel(model)
        self.tree.selectionModel().currentChanged.connect(
            self._currentIndexChanged)

    def keyPressEvent(self, event):
        if (event.key() in (Qt.Key_Up, Qt.Key_Down)
                and not (event.modifiers()
                         & (Qt.ShiftModifier | Qt.AltModifier
                            | Qt.ControlModifier))):
            self.tree.keyPressEvent(event)

        return super().keyPressEvent(event)
Exemplo 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.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 terminate(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)

    @pyqtSlot(Document, Document)
    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()

    @pyqtSlot(QItemSelection, QItemSelection)
    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()

    @pyqtSlot(QPoint)
    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))
Exemplo n.º 5
0
class Widget(QWidget):
    def __init__(self, panel):
        super(Widget, self).__init__(panel)
        
        layout = QVBoxLayout()
        self.setLayout(layout)
        layout.setSpacing(0)
        
        self.searchEntry = SearchLineEdit()
        self.treeView = QTreeView(contextMenuPolicy=Qt.CustomContextMenu)
        self.textView = QTextBrowser()
        
        applyButton = QToolButton(autoRaise=True)
        editButton = QToolButton(autoRaise=True)
        addButton = QToolButton(autoRaise=True)
        self.menuButton = QPushButton(flat=True)
        menu = QMenu(self.menuButton)
        self.menuButton.setMenu(menu)
        
        splitter = QSplitter(Qt.Vertical)
        top = QHBoxLayout()
        layout.addLayout(top)
        splitter.addWidget(self.treeView)
        splitter.addWidget(self.textView)
        layout.addWidget(splitter)
        splitter.setSizes([200, 100])
        splitter.setCollapsible(0, False)
        
        top.addWidget(self.searchEntry)
        top.addWidget(applyButton)
        top.addSpacing(10)
        top.addWidget(addButton)
        top.addWidget(editButton)
        top.addWidget(self.menuButton)
        
        # action generator for actions added to search entry
        def act(slot, icon=None):
            a = QAction(self, triggered=slot)
            self.addAction(a)
            a.setShortcutContext(Qt.WidgetWithChildrenShortcut)
            icon and a.setIcon(icons.get(icon))
            return a
        
        # hide if ESC pressed in lineedit
        a = act(self.slotEscapePressed)
        a.setShortcut(QKeySequence(Qt.Key_Escape))
        
        # import action
        a = self.importAction = act(self.slotImport, 'document-open')
        menu.addAction(a)
        
        # export action
        a = self.exportAction = act(self.slotExport, 'document-save-as')
        menu.addAction(a)
        
        # apply button
        a = self.applyAction = act(self.slotApply, 'edit-paste')
        applyButton.setDefaultAction(a)
        menu.addSeparator()
        menu.addAction(a)
        
        # add button
        a = self.addAction_ = act(self.slotAdd, 'list-add')
        a.setShortcut(QKeySequence(Qt.Key_Insert))
        addButton.setDefaultAction(a)
        menu.addSeparator()
        menu.addAction(a)
        
        # edit button
        a = self.editAction = act(self.slotEdit, 'document-edit')
        a.setShortcut(QKeySequence(Qt.Key_F2))
        editButton.setDefaultAction(a)
        menu.addAction(a)
        
        # set shortcut action
        a = self.shortcutAction = act(self.slotShortcut, 'preferences-desktop-keyboard-shortcuts')
        menu.addAction(a)
        
        # delete action
        a = self.deleteAction = act(self.slotDelete, 'list-remove')
        a.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_Delete))
        menu.addAction(a)
        
        # restore action
        a = self.restoreAction = act(self.slotRestore)
        menu.addSeparator()
        menu.addAction(a)
        
        # help button
        a = self.helpAction = act(self.slotHelp, 'help-contents')
        menu.addSeparator()
        menu.addAction(a)
        
        self.treeView.setSelectionBehavior(QTreeView.SelectRows)
        self.treeView.setSelectionMode(QTreeView.ExtendedSelection)
        self.treeView.setRootIsDecorated(False)
        self.treeView.setAllColumnsShowFocus(True)
        self.treeView.setModel(model.model())
        self.treeView.setCurrentIndex(QModelIndex())
        
        # signals
        self.searchEntry.returnPressed.connect(self.slotReturnPressed)
        self.searchEntry.textChanged.connect(self.updateFilter)
        self.treeView.doubleClicked.connect(self.slotDoubleClicked)
        self.treeView.customContextMenuRequested.connect(self.showContextMenu)
        self.treeView.selectionModel().currentChanged.connect(self.updateText)
        self.treeView.model().dataChanged.connect(self.updateFilter)
        
        # highlight text
        self.highlighter = highlight.Highlighter(self.textView.document())
        
        # complete on snippet variables
        self.searchEntry.setCompleter(QCompleter([
            ':icon', ':indent', ':menu', ':name', ':python', ':selection',
            ':set', ':symbol', ':template', ':template-run'], self.searchEntry))
        self.readSettings()
        app.settingsChanged.connect(self.readSettings)
        app.translateUI(self)
        self.updateColumnSizes()
        self.setAcceptDrops(True)

    def dropEvent(self, ev):
        if not ev.source() and ev.mimeData().hasUrls():
            filename = ev.mimeData().urls()[0].toLocalFile()
            if filename:
                ev.accept()
                from . import import_export
                import_export.load(filename, self)
        
    def dragEnterEvent(self, ev):
        if not ev.source() and ev.mimeData().hasUrls():
            ev.accept()
        
    def translateUI(self):
        try:
            self.searchEntry.setPlaceholderText(_("Search..."))
        except AttributeError:
            pass # not in Qt 4.6
        shortcut = lambda a: a.shortcut().toString(QKeySequence.NativeText)
        self.menuButton.setText(_("&Menu"))
        self.addAction_.setText(_("&Add..."))
        self.addAction_.setToolTip(
            _("Add a new snippet. ({key})").format(key=shortcut(self.addAction_)))
        self.editAction.setText(_("&Edit..."))
        self.editAction.setToolTip(
            _("Edit the current snippet. ({key})").format(key=shortcut(self.editAction)))
        self.shortcutAction.setText(_("Configure Keyboard &Shortcut..."))
        self.deleteAction.setText(_("&Remove"))
        self.deleteAction.setToolTip(_("Remove the selected snippets."))
        self.applyAction.setText(_("A&pply"))
        self.applyAction.setToolTip(_("Apply the current snippet."))
        self.importAction.setText(_("&Import..."))
        self.importAction.setToolTip(_("Import snippets from a file."))
        self.exportAction.setText(_("E&xport..."))
        self.exportAction.setToolTip(_("Export snippets to a file."))
        self.restoreAction.setText(_("Restore &Built-in Snippets..."))
        self.restoreAction.setToolTip(
            _("Restore deleted or changed built-in snippets."))
        self.helpAction.setText(_("&Help"))
        self.searchEntry.setToolTip(_(
            "Enter text to search in the snippets list.\n"
            "See \"What's This\" for more information."))
        self.searchEntry.setWhatsThis(''.join(map("<p>{0}</p>\n".format, (
            _("Enter text to search in the snippets list, and "
              "press Enter to apply the currently selected snippet."),
            _("If the search text fully matches the value of the '{name}' variable "
              "of a snippet, that snippet is selected.").format(name="name"),
            _("If the search text starts with a colon ':', the rest of the "
              "search text filters snippets that define the given variable. "
              "After a space a value can also be entered, snippets will then "
              "match if the value of the given variable contains the text after "
              "the space."),
            _("E.g. entering {menu} will show all snippets that are displayed "
              "in the insert menu.").format(menu="<code>:menu</code>"),
            ))))
    
    def sizeHint(self):
        return self.parent().mainwindow().size() / 4
        
    def readSettings(self):
        data = textformats.formatData('editor')
        self.textView.setFont(data.font)
        self.textView.setPalette(data.palette())

    def showContextMenu(self, pos):
        """Called when the user right-clicks the tree view."""
        self.menuButton.menu().popup(self.treeView.viewport().mapToGlobal(pos))
    
    def slotReturnPressed(self):
        """Called when the user presses Return in the search entry. Applies current snippet."""
        name = self.currentSnippet()
        if name:
            view = self.parent().mainwindow().currentView()
            insert.insert(name, view)
            self.parent().hide() # make configurable?
            view.setFocus()

    def slotEscapePressed(self):
        """Called when the user presses ESC in the search entry. Hides the panel."""
        self.parent().hide()
        self.parent().mainwindow().currentView().setFocus()
    
    def slotDoubleClicked(self, index):
        name = self.treeView.model().name(index)
        view = self.parent().mainwindow().currentView()
        insert.insert(name, view)
        
    def slotAdd(self):
        """Called when the user wants to add a new snippet."""
        edit.Edit(self, None)
        
    def slotEdit(self):
        """Called when the user wants to edit a snippet."""
        name = self.currentSnippet()
        if name:
            edit.Edit(self, name)
        
    def slotShortcut(self):
        """Called when the user selects the Configure Shortcut action."""
        from widgets import shortcuteditdialog
        name = self.currentSnippet()
        if name:
            collection = self.parent().snippetActions
            action = actions.action(name, None, collection)
            default = collection.defaults().get(name)
            mgr = actioncollectionmanager.manager(self.parent().mainwindow())
            cb = mgr.findShortcutConflict
            dlg = shortcuteditdialog.ShortcutEditDialog(self, cb, (collection, name))
            
            if dlg.editAction(action, default):
                mgr.removeShortcuts(action.shortcuts())
                collection.setShortcuts(name, action.shortcuts())
                self.treeView.update()
            
    def slotDelete(self):
        """Called when the user wants to delete the selected rows."""
        rows = sorted(set(i.row() for i in self.treeView.selectedIndexes()), reverse=True)
        if rows:
            for row in rows:
                name = self.treeView.model().names()[row]
                self.parent().snippetActions.setShortcuts(name, [])
                self.treeView.model().removeRow(row)
            self.updateFilter()
    
    def slotApply(self):
        """Called when the user clicks the apply button. Applies current snippet."""
        name = self.currentSnippet()
        if name:
            view = self.parent().mainwindow().currentView()
            insert.insert(name, view)
    
    def slotImport(self):
        """Called when the user activates the import action."""
        filetypes = "{0} (*.xml);;{1} (*)".format(_("XML Files"), _("All Files"))
        caption = app.caption(_("dialog title", "Import Snippets"))
        filename = None
        filename = QFileDialog.getOpenFileName(self, caption, filename, filetypes)[0]
        if filename:
            from . import import_export
            import_export.load(filename, self)
        
    def slotExport(self):
        """Called when the user activates the export action."""
        allrows = [row for row in range(model.model().rowCount())
                       if not self.treeView.isRowHidden(row, QModelIndex())]
        selectedrows = [i.row() for i in self.treeView.selectedIndexes()
                                if i.column() == 0 and i.row() in allrows]
        names = self.treeView.model().names()
        names = [names[row] for row in selectedrows or allrows]
        
        filetypes = "{0} (*.xml);;{1} (*)".format(_("XML Files"), _("All Files"))
        n = len(names)
        caption = app.caption(_("dialog title",
            "Export {num} Snippet", "Export {num} Snippets", n).format(num=n))
        filename = QFileDialog.getSaveFileName(self, caption, None, filetypes)[0]
        if filename:
            from . import import_export
            try:
                import_export.save(names, filename)
            except (IOError, OSError) as e:
                QMessageBox.critical(self, _("Error"), _(
                    "Can't write to destination:\n\n{url}\n\n{error}").format(
                    url=filename, error=e.strerror))
        
    def slotRestore(self):
        """Called when the user activates the Restore action."""
        from . import restore
        dlg = restore.RestoreDialog(self)
        dlg.setWindowModality(Qt.WindowModal)
        dlg.populate()
        dlg.show()
        dlg.finished.connect(dlg.deleteLater)
        
    def slotHelp(self):
        """Called when the user clicks the small help button."""
        userguide.show("snippets")
        
    def currentSnippet(self):
        """Returns the name of the current snippet if it is visible."""
        row = self.treeView.currentIndex().row()
        if row != -1 and not self.treeView.isRowHidden(row, QModelIndex()):
            return self.treeView.model().names()[row]

    def updateFilter(self):
        """Called when the text in the entry changes, updates search results."""
        text = self.searchEntry.text()
        ltext = text.lower()
        filterVars = text.startswith(':')
        if filterVars:
            try:
                fvar, fval = text[1:].split(None, 1)
                fhide = lambda v: v.get(fvar) in (True, None) or fval not in v.get(fvar)
            except ValueError:
                fvar = text[1:].strip()
                fhide = lambda v: not v.get(fvar)
        for row in range(self.treeView.model().rowCount()):
            name = self.treeView.model().names()[row]
            nameid = snippets.get(name).variables.get('name', '')
            if filterVars:
                hide = fhide(snippets.get(name).variables)
            elif nameid == text:
                i = self.treeView.model().createIndex(row, 0)
                self.treeView.selectionModel().setCurrentIndex(i, QItemSelectionModel.SelectCurrent | QItemSelectionModel.Rows)
                hide = False
            elif nameid.lower().startswith(ltext):
                hide = False
            elif ltext in snippets.title(name).lower():
                hide = False
            else:
                hide = True
            self.treeView.setRowHidden(row, QModelIndex(), hide)
        self.updateText()
            
    def updateText(self):
        """Called when the current snippet changes."""
        name = self.currentSnippet()
        self.textView.clear()
        if name:
            s = snippets.get(name)
            self.highlighter.setPython('python' in s.variables)
            self.textView.setPlainText(s.text)
        
    def updateColumnSizes(self):
        self.treeView.resizeColumnToContents(0)
        self.treeView.resizeColumnToContents(1)
Exemplo n.º 6
0
class AddBookmarkDialog(QDialog, Ui_AddBookmarkDialog):
    """
    Class implementing a dialog to add a bookmark or a bookmark folder.
    """
    def __init__(self, parent=None, bookmarksManager=None):
        """
        Constructor
        
        @param parent reference to the parent widget (QWidget)
        @param bookmarksManager reference to the bookmarks manager
            object (BookmarksManager)
        """
        super(AddBookmarkDialog, self).__init__(parent)
        self.setupUi(self)

        self.__bookmarksManager = bookmarksManager
        self.__addedNode = None
        self.__addFolder = False

        if self.__bookmarksManager is None:
            import Helpviewer.HelpWindow
            self.__bookmarksManager = \
                Helpviewer.HelpWindow.HelpWindow.bookmarksManager()

        self.__proxyModel = AddBookmarkProxyModel(self)
        model = self.__bookmarksManager.bookmarksModel()
        self.__proxyModel.setSourceModel(model)

        self.__treeView = QTreeView(self)
        self.__treeView.setModel(self.__proxyModel)
        self.__treeView.expandAll()
        self.__treeView.header().setStretchLastSection(True)
        self.__treeView.header().hide()
        self.__treeView.setItemsExpandable(False)
        self.__treeView.setRootIsDecorated(False)
        self.__treeView.setIndentation(10)
        self.__treeView.show()

        self.locationCombo.setModel(self.__proxyModel)
        self.locationCombo.setView(self.__treeView)

        self.addressEdit.setInactiveText(self.tr("Url"))
        self.nameEdit.setInactiveText(self.tr("Title"))

        self.resize(self.sizeHint())

    def setUrl(self, url):
        """
        Public slot to set the URL of the new bookmark.
        
        @param url URL of the bookmark (string)
        """
        self.addressEdit.setText(url)
        self.resize(self.sizeHint())

    def url(self):
        """
        Public method to get the URL of the bookmark.
        
        @return URL of the bookmark (string)
        """
        return self.addressEdit.text()

    def setTitle(self, title):
        """
        Public method to set the title of the new bookmark.
        
        @param title title of the bookmark (string)
        """
        self.nameEdit.setText(title)

    def title(self):
        """
        Public method to get the title of the bookmark.
        
        @return title of the bookmark (string)
        """
        return self.nameEdit.text()

    def setDescription(self, description):
        """
        Public method to set the description of the new bookmark.
        
        @param description description of the bookamrk (string)
        """
        self.descriptionEdit.setPlainText(description)

    def description(self):
        """
        Public method to get the description of the bookmark.
        
        @return description of the bookamrk (string)
        """
        return self.descriptionEdit.toPlainText()

    def setCurrentIndex(self, idx):
        """
        Public method to set the current index.
        
        @param idx current index to be set (QModelIndex)
        """
        proxyIndex = self.__proxyModel.mapFromSource(idx)
        self.__treeView.setCurrentIndex(proxyIndex)
        self.locationCombo.setCurrentIndex(proxyIndex.row())

    def currentIndex(self):
        """
        Public method to get the current index.
        
        @return current index (QModelIndex)
        """
        idx = self.locationCombo.view().currentIndex()
        idx = self.__proxyModel.mapToSource(idx)
        return idx

    def setFolder(self, folder):
        """
        Public method to set the dialog to "Add Folder" mode.
        
        @param folder flag indicating "Add Folder" mode (boolean)
        """
        self.__addFolder = folder

        if folder:
            self.setWindowTitle(self.tr("Add Folder"))
            self.addressEdit.setVisible(False)
        else:
            self.setWindowTitle(self.tr("Add Bookmark"))
            self.addressEdit.setVisible(True)

        self.resize(self.sizeHint())

    def isFolder(self):
        """
        Public method to test, if the dialog is in "Add Folder" mode.
        
        @return flag indicating "Add Folder" mode (boolean)
        """
        return self.__addFolder

    def addedNode(self):
        """
        Public method to get a reference to the added bookmark node.
        
        @return reference to the added bookmark node (BookmarkNode)
        """
        return self.__addedNode

    def accept(self):
        """
        Public slot handling the acceptance of the dialog.
        """
        if (not self.__addFolder and not self.addressEdit.text()) or \
           not self.nameEdit.text():
            super(AddBookmarkDialog, self).accept()
            return

        from .BookmarkNode import BookmarkNode

        idx = self.currentIndex()
        if not idx.isValid():
            idx = self.__bookmarksManager.bookmarksModel().index(0, 0)
        parent = self.__bookmarksManager.bookmarksModel().node(idx)

        if self.__addFolder:
            type_ = BookmarkNode.Folder
        else:
            type_ = BookmarkNode.Bookmark
        bookmark = BookmarkNode(type_)
        bookmark.title = self.nameEdit.text()
        if not self.__addFolder:
            bookmark.url = self.addressEdit.text()
        bookmark.desc = self.descriptionEdit.toPlainText()

        self.__bookmarksManager.addBookmark(parent, bookmark)
        self.__addedNode = bookmark

        super(AddBookmarkDialog, self).accept()
Exemplo n.º 7
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 terminate(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)

    @pyqtSlot(Document, Document)
    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()

    @pyqtSlot(QItemSelection, QItemSelection)
    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()

    @pyqtSlot(QPoint)
    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))
Exemplo n.º 8
0
class PYCFScape(QMainWindow):
    def __init__(self):
        super().__init__()

        # We determine where the script is placed, for acessing files we use (such as the icon)
        self.we_live_in = sys.argv[0]
        self.we_live_in = os.path.split(self.we_live_in)[0]

        self.build_interface()

        self.opened_file = None  # stores the filepath
        self.opened_vpk = None  # stores the opened vpk
        self.internal_directory_understanding = {
        }  # a dictionary version of the paths
        self.vpk_loaded = False  # whether or not we have a vpk file open
        self.export_paths = [
        ]  # Paths we want to export when file->export is selected

    def build_interface(self):
        self.setWindowTitle("PYCFScape")
        self.setWindowIcon(
            QIcon(os.path.join(self.we_live_in, 'res/Icon64.ico')))

        self.main_content = QWidget()
        self.main_content_layout = QVBoxLayout()
        self.main_content.setLayout(self.main_content_layout)

        # set up the interface parts
        self.output_console = QTextEdit()  # Will basically just copy stdout
        self.output_console.setReadOnly(True)
        sys.stdout = bob_logger(self.output_console, False, sys.stdout)
        sys.stderr = bob_logger(self.output_console, True, sys.stderr)

        self.vpk_tree = QTreeView()  # Displays the tree of the vpk's content
        self.vpk_tree_model = QStandardItemModel(self.vpk_tree)
        self.vpk_tree.setModel(self.vpk_tree_model)
        self.vpk_tree._mousePressEvent = self.vpk_tree.mousePressEvent  # store it so we can call it
        self.vpk_tree.mousePressEvent = self.vpk_tree_item_clicked

        self.vpk_tree.setHeaderHidden(True)

        # We use a QTreeView to also display headers.
        # We however, still treat it as a list view.
        self.dir_list = QTreeView(
        )  # Displays the list of the current vpk's directory's content
        self.dir_list_model = QStandardItemModel(self.dir_list)
        self.dir_list.setModel(self.dir_list_model)
        self.dir_list.doubleClicked.connect(self.dir_list_item_double_clicked)
        self.dir_list_model.itemChanged.connect(self.dir_list_item_updated)
        self.dir_list.setContextMenuPolicy(Qt.CustomContextMenu)
        self.dir_list.customContextMenuRequested.connect(
            self.dir_list_context_menu)

        self.dir_list_model.setColumnCount(2)
        self.dir_list_model.setHorizontalHeaderLabels(["Name", "Type"])
        self.dir_list.header().resizeSection(0, 250)

        # The tool bar - WARNING: MESSY CODE!
        self.actions_toolbar = self.addToolBar("")
        # OPEN BUTTON
        self.open_button = self.actions_toolbar.addAction(
            QIcon.fromTheme("document-open"), "Open File")
        self.open_button.triggered.connect(self.open_file)

        self.actions_toolbar.addSeparator()

        self.back_button = QToolButton()  # GO BACK BUTTON
        self.back_button.setIcon(QIcon.fromTheme("go-previous"))
        self.back_button.setDisabled(True)
        self.actions_toolbar.addWidget(self.back_button)

        self.forward_button = QToolButton()  # GO FORWARD BUTTON
        self.forward_button.setIcon(QIcon.fromTheme("go-next"))
        self.forward_button.setDisabled(True)
        self.actions_toolbar.addWidget(self.forward_button)

        self.up_button = QToolButton()  # GO UP BUTTON
        self.up_button.setIcon(QIcon.fromTheme("go-up"))
        self.up_button.setDisabled(True)
        self.actions_toolbar.addWidget(self.up_button)

        self.actions_toolbar.addSeparator()

        # FIND BUTTON
        self.search_button = self.actions_toolbar.addAction(
            QIcon.fromTheme("system-search"), "Find in file")
        self.search_button.setDisabled(True)
        self.search_button.triggered.connect(self.search_for_file)

        self.actions_toolbar.addSeparator()

        # EXPORT BUTTON
        self.export_button = self.actions_toolbar.addAction(
            QIcon.fromTheme("extract-archive"), "Export Selection")
        self.export_button.setDisabled(False)
        self.export_button.triggered.connect(self.export_selection)

        self.main_content_layout.addWidget(self.actions_toolbar)

        # now we want the menubar
        self.menu_bar = self.menuBar()
        self.file_menu = self.menu_bar.addMenu("&File")
        self.edit_menu = self.menu_bar.addMenu("&Edit")
        self.menu_bar.addSeparator()
        self.help_menu = self.menu_bar.addMenu("&Help")

        self.file_menu.addActions([self.open_button])

        self.close_button = self.file_menu.addAction(
            QIcon.fromTheme("document-close"),
            "Close File"  # bit redundant, i actually see no use
        )
        self.close_button.triggered.connect(self.close_vpk)

        self.file_menu.addSeparator()

        self.file_menu.addAction(QIcon.fromTheme("application-exit"),
                                 "Exit").triggered.connect(self.close)

        self.edit_menu.addActions([self.search_button])

        self.help_menu.addAction(QIcon.fromTheme("help-about"),
                                 "About && License").triggered.connect(
                                     self.about)

        # the statusbar
        self.status_bar = self.statusBar()

        # set up the splitters

        # horizontal
        self.horz_splitter_container = QWidget()
        self.horz_splitter_container_layout = QVBoxLayout()
        self.horz_splitter_container.setLayout(
            self.horz_splitter_container_layout)

        self.horz_splitter = QSplitter(Qt.Horizontal)

        self.horz_splitter.addWidget(self.vpk_tree)
        self.horz_splitter.addWidget(self.dir_list)

        self.horz_splitter.setSizes([50, 200])

        self.horz_splitter_container_layout.addWidget(self.horz_splitter)

        # vertical
        self.vert_splitter = QSplitter(Qt.Vertical)

        self.vert_splitter.addWidget(self.horz_splitter_container)
        self.vert_splitter.addWidget(self.output_console)

        self.vert_splitter.setSizes([200, 50])

        self.main_content_layout.addWidget(self.vert_splitter)
        self.setCentralWidget(self.main_content)

        self.show()

    ##
    # Update Functions
    ##
    def update_console(self, text, is_stdout):
        colour = Qt.Red if not is_stdout else Qt.black
        self.output_console.setTextColor(colour)
        self.output_console.moveCursor(QTextCursor.End)
        self.output_console.insertPlainText(text)

    def update_interface(self):

        # update the tree view
        self.update_vpk_tree()

        # update the list view
        self.update_dir_list()

        self.search_button.setDisabled(not self.vpk_loaded)

    def update_vpk_tree(self):
        self.vpk_tree_model.removeRows(0, self.vpk_tree_model.rowCount())
        if self.opened_vpk:
            self.tree_magic(self.internal_directory_understanding)

    def update_dir_list(self):
        self.dir_list_model.removeRows(0, self.dir_list_model.rowCount(
        ))  # remove anyway (for instances such as opening a new file)

        selected = self.vpk_tree.selectedIndexes()
        if not selected:
            return
        selected = selected[0]
        selected_item = self.vpk_tree_model.itemFromIndex(selected)
        if not selected_item:
            return

        if not selected_item.is_dir:
            return

        path = selected_item.path
        understanding = self.get_understanding_from(
            self.internal_directory_understanding, path)
        self.list_magic(understanding, path)

    ##
    # Events
    ##
    def vpk_tree_item_clicked(self, event):
        self.vpk_tree._mousePressEvent(event)

        index = self.vpk_tree.indexAt(event.pos())

        # We can rather safely assume any items in the vpk tree will have OUR special attributes
        if index.isValid():
            item = self.vpk_tree_model.itemFromIndex(index)
            print("selected", item.path)

            if item.is_dir:
                self.update_dir_list()

    def dir_list_item_double_clicked(self, index):

        item = self.dir_list_model.itemFromIndex(index)
        if not item.column() == 0:
            return
        print("double clicked", item.path)

        if item.is_dir:
            print("is dir")
            # this is probably a REALLY **REALLY** awful way of doing this, but you're welcome to PR a better way. :)
            index_in_tree = self.find_in_model(self.vpk_tree_model, item.path)

            if index_in_tree.isValid():
                self.vpk_tree.setCurrentIndex(index_in_tree)
                self.update_dir_list()
        else:
            self.status_bar.showMessage(MSG_EXPORT)

            # we clearly wanna export the file and open it
            bits = self.opened_vpk[item.path[1:]].read()

            path = compat.write_to_temp(bits, os.path.split(item.path)[1])
            print("Wrote to", path)
            compat.tell_os_open(path)

            self.status_bar.clearMessage()

    def dir_list_item_updated(self, item):
        if item.checkState() == Qt.Checked:
            if not item.is_dir:
                self.export_paths.append(item.path)
            else:
                index_in_tree = self.find_in_model(self.vpk_tree_model,
                                                   item.path)

                if index_in_tree.isValid():
                    paths = self.recursively_get_paths_from_dir_index_item(
                        index_in_tree, self.vpk_tree_model)

                    self.export_paths += paths

        elif item.checkState() == Qt.Unchecked:
            if not item.is_dir:
                if item.path in self.export_paths:
                    self.export_paths.remove(item.path)
            else:
                index_in_tree = self.find_in_model(self.vpk_tree_model,
                                                   item.path)

                if index_in_tree.isValid():
                    paths = self.recursively_get_paths_from_dir_index_item(
                        index_in_tree, self.vpk_tree_model)

                    for path in paths:
                        if path in self.export_paths:
                            self.export_paths.remove(path)

    ##
    # The next 3 functions are the original pathtodir but merged with the program
    ##
    def nested_dict(self):
        return defaultdict(self.nested_dict)

    def nested_dict_to_regular(self, d):
        if not isinstance(d, defaultdict):
            return d
        return {k: self.nested_dict_to_regular(v) for k, v in d.items()}

    def understand_directories(self, list_of_paths):
        use_dict = defaultdict(self.nested_dict)

        for path in list_of_paths:
            parts = path.split('/')
            if parts:
                marcher = use_dict
                for key in parts[:-1]:
                    marcher = marcher[key]
                marcher[parts[-1]] = parts[-1]

        return dict(use_dict)

    def get_understanding_from(self, understanding, path):
        keys = path.split('/')
        if keys[0] == '':
            keys = keys[1:]

        if keys:
            marcher = understanding
            for key in keys:
                marcher = marcher[key]

            # we can now assume marcher is the understanding we want
            return marcher

    ##
    # Utility
    ##
    def tree_magic(self, dict_of_things, parent=None, root=''):

        if not parent:
            parent = self.vpk_tree_model

        # Stack overflow 14478170
        for thing in sorted(dict_of_things, key=lambda f: os.path.splitext(f)):

            path = root + '/{}'.format(thing)

            thing_item = QStandardItem()
            thing_item.setText(thing)
            thing_item.setEditable(False)

            thing_item.path = path
            thing_item.is_dir = False

            icon, _ = self.get_info_from_path(path)

            thing_item.setIcon(icon)

            if isinstance(dict_of_things[thing], dict):
                thing_item.setIcon(QIcon.fromTheme("folder"))
                self.tree_magic(dict_of_things[thing], thing_item, path)

                thing_item.is_dir = True

            parent.appendRow(thing_item)

    def list_magic(self, dict_of_things, root=''):
        # like tree_magic but operates on dir_list

        for thing in sorted(dict_of_things, key=lambda f: os.path.splitext(f)):

            path = root + '/{}'.format(thing)

            thing_item = QStandardItem()
            thing_item.setText(thing)
            thing_item.setEditable(False)
            thing_item.setCheckable(True)

            thing_item_type = QStandardItem(
            )  # This doesn't actually do anything but convey more information to the user
            thing_item_type.setEditable(False)

            if path in self.export_paths:
                thing_item.setCheckState(Qt.Checked)

            thing_item.path = path
            thing_item.is_dir = False

            icon, desc = self.get_info_from_path(path)

            thing_item.setIcon(icon)
            thing_item_type.setText(desc)

            if isinstance(dict_of_things[thing], dict):
                thing_item.setIcon(QIcon.fromTheme("folder"))
                thing_item.is_dir = True
                thing_item_type.setText("Directory")

            self.dir_list_model.appendRow([thing_item, thing_item_type])

    def get_info_from_path(self,
                           path):  # returns the icon AND description string

        icon = None
        desc = None

        # first we test against mimetype
        # probably bad code, but it works!

        thing_mimetype = mimetypes.guess_type(path)[0]

        if thing_mimetype:
            if thing_mimetype[:6] == "audio/":
                icon = QIcon.fromTheme("audio-x-generic")
            elif thing_mimetype[:12] == "application/":
                icon = QIcon.fromTheme("application-x-generic")
            elif thing_mimetype[:5] == "text/":
                icon = QIcon.fromTheme("text-x-generic")
            elif thing_mimetype[:6] == "image/":
                icon = QIcon.fromTheme("image-x-generic")
            elif thing_mimetype[:6] == "video/":
                icon = QIcon.fromTheme("video-x-generic")

            desc = thing_mimetype

        # well ok, maybe that didn't work, let's test the filepath ourselves.

        file_ext = os.path.splitext(path)[1]

        if file_ext:
            if file_ext in [".vtf"]:
                icon = QIcon.fromTheme("image-x-generic")
                desc = "Valve Texture File"
            elif file_ext in [".vmt"]:
                icon = QIcon.fromTheme("text-x-generic")
                desc = "Valve Material File"
            elif file_ext in [
                    ".pcf"
            ]:  # we can safely assume they are not fonts in this context, but rather
                icon = QIcon.fromTheme("text-x-script")
                desc = "Valve DMX Implementation"  # TODO: is there a better name
            elif file_ext in [".bsp"]:
                icon = QIcon.fromTheme("text-x-generic")
                desc = "Binary Space Partition"
            elif file_ext in [".res"]:
                icon = QIcon.fromTheme("text-x-generic")
                desc = "Valve Key Value"
            elif file_ext in [".vcd"]:
                icon = QIcon.fromTheme("text-x-generic")
                desc = "Valve Choreography Data"

        if not icon:  # If all else fails, display SOMETHING
            icon = QIcon.fromTheme("text-x-generic")
        if not desc:
            desc = "File"

        return icon, desc

    def find_in_model(self, model: QStandardItemModel, path):
        for i in range(0, model.rowCount()):
            index_in_tree = model.index(i, 0)

            if model.itemFromIndex(index_in_tree).path == path:
                return index_in_tree

            if model.itemFromIndex(index_in_tree).is_dir:
                index_in_tree = self.find_in_model_parent(model,
                                                          path,
                                                          parent=index_in_tree)

                if not index_in_tree.isValid():
                    continue

                if model.itemFromIndex(index_in_tree).path == path:
                    return index_in_tree

    def find_in_model_parent(self, model: QStandardItemModel, path, parent):
        for i in range(0, model.rowCount(parent)):
            index_in_tree = model.index(i, 0, parent)

            if model.itemFromIndex(index_in_tree).path == path:
                return index_in_tree

            if model.itemFromIndex(index_in_tree).is_dir:
                index_in_tree = self.find_in_model_parent(model,
                                                          path,
                                                          parent=index_in_tree)

                if not index_in_tree.isValid():
                    continue

                if model.itemFromIndex(index_in_tree).path == path:
                    return index_in_tree

        return QModelIndex()

    def export_file(self, path, out_dir):
        filepath = os.path.split(path)[0]

        if not os.path.isdir('{}{}'.format(out_dir, filepath)):
            os.makedirs('{}{}'.format(out_dir, filepath))

        print("Attempting to export to", "{}{}".format(out_dir, filepath),
              "from", path[1:], "in the vpk")
        outcontents = self.opened_vpk.get_file(path[1:]).read()

        outfile = open('{}{}'.format(out_dir, path), 'wb')
        outfile.write(outcontents)
        outfile.close()

    def recursively_get_paths_from_dir_index_item(self, index_in_tree, model):
        paths = []

        for i in range(
                self.vpk_tree_model.itemFromIndex(index_in_tree).rowCount()):
            index = self.vpk_tree_model.index(i, 0, index_in_tree)
            index_item = self.vpk_tree_model.itemFromIndex(index)

            if not index_item.is_dir:
                paths.append(index_item.path)
            else:
                paths += self.recursively_get_paths_from_dir_index_item(
                    index, model)

        return paths

    def close_vpk(self):  # We trash everything!
        self.vpk_loaded = False
        self.opened_file = None
        self.opened_vpk = {}
        self.export_paths = []
        self.internal_directory_understanding = {}

        self.status_bar.showMessage(MSG_UPDATE_UI)
        self.update_interface()
        self.status_bar.clearMessage()

    def open_vpk(self, vpk_path):
        if self.vpk_loaded:  # if we already have a file open, close it.
            self.close_vpk()

        self.status_bar.showMessage(MSG_OPEN_VPK)

        if not os.path.exists(vpk_path):
            print(
                "Attempted to open {}, which doesn't exist.".format(vpk_path))
            return

        self.opened_file = vpk_path

        try:
            self.opened_vpk = vpk.open(vpk_path)
        except Exception as e:
            print("Ran into an error from the VPK Library.")
            sys.stdout.write(str(e))
            self.error_box(str(e))
            return

        self.vpk_loaded = True

        self.status_bar.showMessage(MSG_UNDERSTAND_VPK)

        # Now we attempt to understand the vpk
        self.internal_directory_understanding = self.understand_directories(
            self.opened_vpk)

        self.status_bar.showMessage(MSG_UPDATE_UI)
        self.update_interface()
        self.status_bar.clearMessage()

    ##
    # Dialogs
    ##
    def open_dialog(self):
        fn = QFileDialog.getOpenFileName(None,
                                         "Open Package",
                                         str(pathlib.Path.home()),
                                         filter=("Valve Pak Files (*.vpk)"))

        return fn

    def open_dir_dialog(self, title="Open Directory"):
        fn = QFileDialog.getExistingDirectory(None, title,
                                              str(pathlib.Path.home()))

        return fn

    def error_box(self, text="...", title="Error"):
        box = QMessageBox()
        box.setIcon(QMessageBox.Critical)
        box.setText(text)
        box.setWindowTitle(title)
        box.setStandardButtons(QMessageBox.Ok)

        return box.exec()

    def info_box(self, text="...", title="Info"):
        box = QMessageBox()
        box.setIcon(QMessageBox.Information)
        box.setText(text)
        box.setWindowTitle(title)
        box.setStandardButtons(QMessageBox.Ok)

        return box.exec()

    def dir_list_context_menu(self, event):
        menu = QMenu(self)

        selected = self.dir_list.selectedIndexes()
        if not selected:
            return
        selected = selected[0]
        selected_item = self.dir_list_model.itemFromIndex(selected)
        path = selected_item.path

        extract = menu.addAction(QIcon.fromTheme("extract-archive"), "Extract")
        validate = menu.addAction(QIcon.fromTheme("view-certificate"),
                                  "Validate")  # TODO: better validation icon
        menu.addSeparator()
        gotodirectory = menu.addAction(QIcon.fromTheme("folder"),
                                       "Go To Directory")
        menu.addSeparator()
        properties = menu.addAction(QIcon.fromTheme("settings-configure"),
                                    "Properties")

        extract.setDisabled(selected_item.is_dir)
        validate.setDisabled(selected_item.is_dir)

        action = menu.exec_(self.dir_list.mapToGlobal(event))
        if action == extract:
            self.export_selected_file(path)
        elif action == validate:
            self.validate_file(path)
        elif action in [gotodirectory, properties]:
            self.info_box(
                "I'm not sure what this does in the original GCFScape.\nIf you know, please make an issue on github!"
            )

    def about(self):
        box = QMessageBox()
        box.setWindowTitle(
            "About PYCFScape")  # TODO: what do we version and how
        box.setText("""PYCFScape
Version 0
MIT LICENSE V1.00 OR LATER
Python {}
QT {}
AUTHORS (Current Version):
ACBob - https://acbob.gitlab.io

Project Homepage
https://github.com/acbob/pycfscape""".format(sys.version, QT_VERSION_STR))

        box.setIcon(QMessageBox.Information)

        box.exec()

    ##
    # Actions
    ##
    def open_file(self):
        self.status_bar.showMessage(MSG_USER_WAIT)

        fn = self.open_dialog()[0]
        if not fn:
            self.status_bar.clearMessage()
            return

        self.open_vpk(fn)

    def search_for_file(self, event):
        print(event)

    def export_selection(self):
        if not self.export_paths:
            self.info_box(
                "You can't export nothing!\n(Please select some items to export.)"
            )
            return

        self.status_bar.showMessage(MSG_USER_WAIT)
        output_dir = self.open_dir_dialog("Export to...")

        if output_dir:
            self.status_bar.showMessage(MSG_EXPORT)
            print("attempting export to", output_dir)

            for file in self.export_paths:
                self.export_file(file, output_dir)

        self.status_bar.clearMessage()

    def export_selected_file(self, file):
        self.status_bar.showMessage(MSG_USER_WAIT)
        output_dir = self.open_dir_dialog("Export to...")

        if output_dir:
            self.status_bar.showMessage(MSG_EXPORT)
            print("attempting export to", output_dir)

            self.export_file(file, output_dir)

        self.status_bar.clearMessage()

    def validate_file(self, file):
        filetoverify = self.opened_vpk.get_file(file[1:])

        if filetoverify:
            verified = filetoverify.verify()
            if verified:
                self.info_box("{} is a perfectly healthy file.".format(file),
                              "All's good.")
            else:
                self.error_box("{} is not valid!".format(file), "Uh oh.")
        else:
            print("What? file doesn't exist? HOW IS THIS MAN")
Exemplo n.º 9
0
class StatsGui(QWidget):
    ''' 
    This class accepts a glue data collection object, and builds an interactive window
    to display basic statistics (e.g. mean, median, mode) about each dataset
    '''

    released = QtCore.pyqtSignal(object)

    def __init__(self, dc):

        # Initialize the object as a QWidget
        QWidget.__init__(self)

        #Save the datacollection object as an attribute of class StatsGui
        self.dc = dc

        #Set the title of the main GUI window
        self.setWindowTitle('Statistics')

        # Set up dicts for row indices
        self.subset_dict = dict()
        self.component_dict = dict()

        self.selected_dict = dict()
        self.selected_indices = []

        #Set up tree view and fix it to the top half of the window
        self.treeview = QTreeView(self)

        # Set the default clicking behavior to be row selection
        self.treeview.setSelectionBehavior(QAbstractItemView.SelectRows)

        # Set up expand all, collapse all, select all and deselect all buttons

        # Layout for expand/collapse/select/deselect
        layout_left_options = QHBoxLayout()

        self.expand_data = QToolButton(self)
        self.expand_data.setText("Expand all data and subsets")
        self.expand_data.clicked.connect(self.expandClicked)
        layout_left_options.addWidget(self.expand_data)

        self.all = QToolButton(self)
        self.all.setText('Select all')
        self.all.clicked.connect(self.allClicked)
        layout_left_options.addWidget(self.all)

        self.none = QToolButton(self)
        self.none.setText('Deselect all')
        self.none.clicked.connect(self.noneClicked)
        layout_left_options.addWidget(self.none)

        # Set default significant figures to 5
        getcontext().prec = 5

        # Set up past selected items
        self.past_selected = []

        # Sort by subsets as a default
        self.sortBySubsets()

        # Set up the combo box for users to choose the number of significant figures in the table

        # Set up bottom options layout
        layout_bottom_options = QHBoxLayout()

        self.siglabel = QLabel(self)
        self.siglabel.setText('Number of significant figures:')
        layout_bottom_options.addWidget(self.siglabel)

        self.sigfig = QSpinBox(self)
        self.sigfig.setRange(1, 10)
        self.sigfig.setValue(5)
        self.sigfig.valueChanged.connect(self.sigchange)
        layout_bottom_options.addWidget(self.sigfig)

        # Export to file button
        self.export = QPushButton(self)
        self.export.setText('Export to file')
        self.export.clicked.connect(self.exportToFile)
        layout_bottom_options.addWidget(self.export)

        # Set up the toggle button to switch tree sorting modes
        self.switch_mode = QToolButton(self)
        self.switch_mode.setText('Sort tree by components')
        self.switch_mode.clicked.connect(self.switchMode)
        layout_left_options.addWidget(self.switch_mode)

        # Add instructions to sort the table
        self.how = QLabel(self)
        self.how.setText('Click each header to sort table')
        layout_left_options.addWidget(self.how)

        #################Set up the QTableView Widget#############################
        self.table = QTableView(self)
        self.table.setSortingEnabled(True)
        self.table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
        self.table.verticalHeader().setVisible(False)

        #Set the table headings
        self.headings = ('Subset', 'Dataset', 'Component', 'Mean', 'Median',
                         'Minimum', 'Maximum', 'Sum')
        self.data_frame = pd.DataFrame(columns=self.headings)
        self.data_accurate = pd.DataFrame(columns=self.headings)
        self.model = pandasModel(self.data_frame, self.dc)

        self.table.setModel(self.model)

        layout_table = QHBoxLayout()
        layout_table.addWidget(self.table)
        layout_table.stretch(10)

        # Finish nesting all the layouts
        main_layout = QVBoxLayout()

        main_layout.addWidget(self.treeview)
        main_layout.addLayout(layout_left_options)
        main_layout.addLayout(layout_table)
        main_layout.addLayout(layout_bottom_options)

        self.setLayout(main_layout)

        # Set up dict for caching
        self.cache_stash = dict()

    def myPressedEvent(self, currentQModelIndex):
        ''' 
        Every time a row (or rows) in the tree view is clicked:
        if it is selected, add it to the table
        if it is deselected, remove it from the table
        '''

        # Get the indexes of all the selected components
        self.selected_indices = self.treeview.selectionModel().selectedRows()

        newly_selected = np.setdiff1d(self.selected_indices,
                                      self.past_selected)

        for index in range(0, len(newly_selected)):

            # Check which view mode the tree is in to get the correct indices
            if self.switch_mode.text() == 'Sort tree by components':
                data_i = newly_selected[index].parent().row()
                comp_i = newly_selected[index].row()
                subset_i = newly_selected[index].parent().parent().row()

            else:
                data_i = newly_selected[index].parent().parent().row()
                comp_i = newly_selected[index].parent().row()
                subset_i = newly_selected[index].row() - 1

            is_subset = newly_selected[index].parent().parent().parent().row(
            ) == 1 or (self.switch_mode.text() == 'Sort tree by subsets'
                       and subset_i != -1)

            # Check if its a subset and if so run subset stats
            if is_subset:
                self.runSubsetStats(subset_i, data_i, comp_i)
            else:
                # Run standard data stats
                self.runDataStats(data_i, comp_i)

        newly_dropped = np.setdiff1d(self.past_selected, self.selected_indices)

        for index in range(0, len(newly_dropped)):

            # Check which view mode the tree is in to get the correct indices
            if self.switch_mode.text() == 'Sort tree by components':
                data_i = newly_dropped[index].parent().row()
                comp_i = newly_dropped[index].row()
                subset_i = newly_dropped[index].parent().parent().row()

            else:
                data_i = newly_dropped[index].parent().parent().row()
                comp_i = newly_dropped[index].parent().row()
                subset_i = newly_dropped[index].row() - 1

            is_subset = newly_dropped[index].parent().parent().parent().row(
            ) == 1 or (self.switch_mode.text() == 'Sort tree by subsets'
                       and subset_i != -1)

            if is_subset:
                try:
                    # Get the indices that match the component, dataset, and subset requirements
                    idx_c = np.where(self.data_frame['Component'] ==
                                     self.dc[data_i].components[comp_i].label)
                    idx_d = np.where(
                        self.data_frame['Dataset'] == self.dc[data_i].label)
                    idx_s = np.where(self.data_frame['Subset'] ==
                                     self.dc[data_i].subsets[subset_i].label)
                    idx1 = np.intersect1d(idx_c, idx_d)
                    idx2 = np.intersect1d(idx1, idx_s)

                    self.data_frame = self.data_frame.drop(idx2)
                except:
                    pass

            else:
                try:
                    # Find the index in the table of the unchecked element, if it's in the table

                    # Find the matching component and dataset indices and intersect them to get the unique index
                    idx_c = np.where(self.data_frame['Component'] ==
                                     self.dc[data_i].components[comp_i].label)
                    idx_d = np.where(
                        self.data_frame['Dataset'] == self.dc[data_i].label)
                    idx_s = np.where(self.data_frame['Subset'] == '--')
                    idx1 = np.intersect1d(idx_c, idx_d)
                    idx2 = np.intersect1d(idx1, idx_s)

                    self.data_frame = self.data_frame.drop(idx2)
                except:
                    pass

        # Update the past selected indices
        self.past_selected = self.selected_indices

        model = pandasModel(self.data_frame, self.dc)

        self.table.setModel(model)

        self.table.setSortingEnabled(True)
        self.table.setShowGrid(False)

    def runDataStats(self, data_i, comp_i):
        '''
        Runs statistics for the component comp_i of data set data_i
        '''

        subset_label = "--"
        data_label = dc[data_i].label
        comp_label = self.dc[data_i].components[
            comp_i].label  # add to the name array to build the table

        # Build the cache key
        cache_key = subset_label + data_label + comp_label

        # See if the values have already been cached
        try:
            column_data = self.cache_stash[cache_key]

        except:
            # Find the stat values
            # Save the data in the cache
            mean_val = self.dc[data_i].compute_statistic(
                'mean', self.dc[data_i].components[comp_i])
            median_val = self.dc[data_i].compute_statistic(
                'median', self.dc[data_i].components[comp_i])
            min_val = self.dc[data_i].compute_statistic(
                'minimum', self.dc[data_i].components[comp_i])
            max_val = self.dc[data_i].compute_statistic(
                'maximum', self.dc[data_i].components[comp_i])
            sum_val = self.dc[data_i].compute_statistic(
                'sum', self.dc[data_i].components[comp_i])

            column_data = np.asarray([[subset_label],
                                      [data_label], [comp_label], [mean_val],
                                      [median_val], [min_val], [max_val],
                                      [sum_val]]).transpose()

            self.cache_stash[cache_key] = column_data

        # Save the accurate data in self.data_accurate
        column_df = pd.DataFrame(column_data, columns=self.headings)
        self.data_accurate = self.data_accurate.append(column_df,
                                                       ignore_index=True)

        # Round the values according to the number of significant figures set by the user
        mean_val = Decimal(float(column_data[0][3])) * Decimal(1)
        median_val = Decimal(float(column_data[0][4])) * Decimal(1)
        min_val = Decimal(float(column_data[0][5])) * Decimal(1)
        max_val = Decimal(float(column_data[0][6])) * Decimal(1)
        sum_val = Decimal(float(column_data[0][7])) * Decimal(1)

        # Create the column data array and append it to the data frame
        column_data = np.asarray([[subset_label], [data_label], [comp_label],
                                  [mean_val], [median_val], [min_val],
                                  [max_val], [sum_val]]).transpose()
        column_df = pd.DataFrame(column_data, columns=self.headings)
        self.data_frame = self.data_frame.append(column_df, ignore_index=True)

    def runSubsetStats(self, subset_i, data_i, comp_i):
        '''
        Runs statistics for the subset subset_i with respect to the component comp_i of data set data_i
        '''

        subset_label = dc[data_i].subsets[subset_i].label
        data_label = dc[data_i].label
        comp_label = self.dc[data_i].components[
            comp_i].label  # add to the name array to build the table

        # Build the cache key
        cache_key = subset_label + data_label + comp_label

        # See if the statistics are already in the cache
        try:
            column_data = self.cache_stash[cache_key]

        # Find the stats if not in the cache
        # Save in the cache

        except:
            mean_val = self.dc[data_i].compute_statistic(
                'mean',
                self.dc[data_i].subsets[subset_i].components[comp_i],
                subset_state=self.dc[data_i].subsets[subset_i].subset_state)
            median_val = self.dc[data_i].compute_statistic(
                'median',
                self.dc[data_i].subsets[subset_i].components[comp_i],
                subset_state=self.dc.subset_groups[subset_i].subset_state)
            min_val = self.dc[data_i].compute_statistic(
                'minimum',
                self.dc[data_i].subsets[subset_i].components[comp_i],
                subset_state=self.dc.subset_groups[subset_i].subset_state)
            max_val = self.dc[data_i].compute_statistic(
                'maximum',
                self.dc[data_i].subsets[subset_i].components[comp_i],
                subset_state=self.dc.subset_groups[subset_i].subset_state)
            sum_val = self.dc[data_i].compute_statistic(
                'sum',
                self.dc[data_i].subsets[subset_i].components[comp_i],
                subset_state=self.dc.subset_groups[subset_i].subset_state)

            column_data = np.asarray([[subset_label],
                                      [data_label], [comp_label], [mean_val],
                                      [median_val], [min_val], [max_val],
                                      [sum_val]]).transpose()

            self.cache_stash[cache_key] = column_data

        # Save the data in self.data_accurate
        column_df = pd.DataFrame(column_data, columns=self.headings)
        self.data_accurate = self.data_accurate.append(column_df,
                                                       ignore_index=True)

        # Round the values according to the number of significant figures set by the user
        mean_val = Decimal(float(column_data[0][3])) * Decimal(1)
        median_val = Decimal(float(column_data[0][4])) * Decimal(1)
        min_val = Decimal(float(column_data[0][5])) * Decimal(1)
        max_val = Decimal(float(column_data[0][6])) * Decimal(1)
        sum_val = Decimal(float(column_data[0][7])) * Decimal(1)

        # Create the column data array and append it to the data frame
        column_data = np.asarray([[subset_label], [data_label], [comp_label],
                                  [mean_val], [median_val], [min_val],
                                  [max_val], [sum_val]]).transpose()
        column_df = pd.DataFrame(column_data, columns=self.headings)
        self.data_frame = self.data_frame.append(column_df, ignore_index=True)

    def sigchange(self, i):
        # Set the number of significant figures according to what the user selects
        getcontext().prec = i

        # Retrospectively change the number of significant figures in the table

        data_labels = self.data_frame['Dataset']
        comp_labels = self.data_frame['Component']
        subset_labels = self.data_frame['Subset']

        mean_vals = []
        median_vals = []
        min_vals = []
        max_vals = []
        sum_vals = []

        # Get the values from the self.data_accurate array and append them
        for i in range(0, len(self.data_frame)):
            mean_vals.append(
                Decimal(self.data_accurate['Mean'][i]) * Decimal(1))
            median_vals.append(
                Decimal(self.data_accurate['Median'][i]) * Decimal(1))
            min_vals.append(
                Decimal(self.data_accurate['Minimum'][i]) * Decimal(1))
            max_vals.append(
                Decimal(self.data_accurate['Maximum'][i]) * Decimal(1))
            sum_vals.append(Decimal(self.data_accurate['Sum'][i]) * Decimal(1))

        column_data = np.asarray([
            subset_labels, data_labels, comp_labels, mean_vals, median_vals,
            min_vals, max_vals, sum_vals
        ]).transpose()
        self.data_frame = pd.DataFrame(column_data, columns=self.headings)
        model = pandasModel(self.data_frame, self.dc)
        self.table.setModel(model)
        self.table.setSortingEnabled(True)
        self.table.setShowGrid(False)

    def expandClicked(self):
        if self.expand_data.text() == "Expand all data and subsets":
            self.treeview.expandAll()
            self.expand_data.setText("Collapse all data and subsets")
        else:
            self.treeview.collapseAll()
            self.expand_data.setText("Expand all data and subsets")

    def allClicked(self):
        # Select all components of the treeview if checked and fill the table with newly checked items
        # Does not deselect if user unclicks it

        original_idx = self.treeview.selectionModel().selectedRows()

        self.treeview.selectAll()
        end_idx = self.treeview.selectionModel().selectedRows()
        for index in end_idx:
            if index not in original_idx:
                # Check to see if the clicked item is a subset component or a data component
                if index.parent().parent().parent().row() != 1:
                    self.runDataStats(index.parent().row(), index.row())
                else:
                    self.runSubsetStats(index.parent().parent().row(),
                                        index.parent().row(), index.row())

        # Set the table to display the correct data frame
        model = pandasModel(self.data_frame, self.dc)
        self.table.setModel(model)
        self.table.setSortingEnabled(True)
        self.table.setShowGrid(False)

    def noneClicked(self):
        self.treeview.clearSelection()
        self.data_frame = pd.DataFrame(columns=self.headings)
        model = pandasModel(self.data_frame, self.dc)
        self.table.setModel(model)
        self.table.setSortingEnabled(True)
        self.table.setShowGrid(False)

    def exportToFile(self):
        file_name, fltr = compat.getsavefilename(
            caption="Choose an output filename")

        try:
            self.data_frame.to_csv(str(file_name), index=False)
        except:
            print("passed")
            pass

    def switchMode(self):
        # if the user clicks to sort by components, change the text to "sort by subsets" and sort tree by components
        if self.switch_mode.text() == 'Sort tree by components':
            self.sortByComponents()
            self.switch_mode.setText('Sort tree by subsets')
        # otherwise the user wants to sort by subsets, change text to "sort by components" and sort tree by subsets
        else:
            self.sortBySubsets()
            self.switch_mode.setText('Sort tree by components')

    def sizeHint(self):
        return QSize(600, 800)

    def sortBySubsets(self):
        '''
        Sorts the treeview by subsets- Dataset then subset then component.
        What we originally had as the default
        '''
        # Save the selected rows from the component view
        try:
            selected = dict()
            for i in range(0, len(self.selected_indices)):
                item = self.model_components.itemFromIndex(
                    self.selected_indices[i])
                if item.row() != 0:
                    key = item.text() + " (" + item.parent().parent().text(
                    ) + ")" + item.parent().text()
                    selected[key] = item.index()
                else:
                    key = item.text() + item.parent().text()
                    selected[key] = item.index()
        except:
            pass

        # Set Expand/collapse button to "expand all"
        self.expand_data.setText("Expand all data and subsets")

        #Allow the user to select multiple rows at a time
        self.selection_model = QAbstractItemView.MultiSelection
        self.treeview.setSelectionMode(self.selection_model)

        # See if the model already exists instead of regenerating
        try:
            self.treeview.setModel(self.model_subsets)

        except:
            self.model_subsets = QStandardItemModel()
            self.model_subsets.setHorizontalHeaderLabels([''])

            self.treeview.setModel(self.model_subsets)
            self.treeview.setUniformRowHeights(True)

            # populate the tree
            # Make all the datasets be parents, and make it so they are not selectable
            parent_data = QStandardItem('{}'.format('Data'))
            parent_data.setEditable(False)
            parent_data.setSelectable(False)

            for i in range(0, len(self.dc)):
                parent = QStandardItem('{}'.format(self.dc.labels[i]))
                parent.setIcon(helpers.layer_icon(self.dc[i]))
                parent.setEditable(False)
                parent.setSelectable(False)

                # Make all the data components be children, nested under their parent
                for j in range(0, len(self.dc[i].components)):
                    child = QStandardItem('{}'.format(
                        str(self.dc[i].components[j])))
                    child.setEditable(False)

                    # Add to the subset_dict
                    key = self.dc[i].label + self.dc[i].components[
                        j].label + "All data-" + self.dc[i].label
                    self.subset_dict[key] = child.index()

                    parent.appendRow(child)

                parent_data.appendRow(parent)

                #Add the parents with their children to the QStandardItemModel
            self.model_subsets.appendRow(parent_data)

            parent_subset = QStandardItem('{}'.format('Subsets'))
            parent_subset.setEditable(False)
            parent_subset.setSelectable(False)

            # Set up the subsets as Subsets > choose subset > choose data set > choose component

            for j in range(0, len(self.dc.subset_groups)):
                grandparent = QStandardItem('{}'.format(
                    self.dc.subset_groups[j].label))
                grandparent.setIcon(
                    helpers.layer_icon(self.dc.subset_groups[j]))

                grandparent.setEditable(False)
                grandparent.setSelectable(False)

                for i in range(0, len(self.dc)):
                    parent = QStandardItem(
                        '{}'.format(self.dc.subset_groups[j].label) + ' (' +
                        '{}'.format(self.dc[i].label) + ')')

                    # Set up the circles
                    parent.setIcon(helpers.layer_icon(
                        self.dc.subset_groups[j]))
                    parent.setEditable(False)
                    parent.setSelectable(False)

                    try:
                        self.dc[i].compute_statistic(
                            'mean',
                            self.dc[i].subsets[j].components[0],
                            subset_state=self.dc[i].subsets[j].subset_state)

                    except:
                        parent.setForeground(QtGui.QBrush(Qt.gray))

                    for k in range(0, len(self.dc[i].components)):

                        child = QStandardItem('{}'.format(
                            str(self.dc[i].components[k])))
                        child.setEditable(False)

                        # Update the dict to keep track of row indices
                        key = self.dc[i].label + self.dc[i].components[
                            k].label + self.dc[i].subsets[j].label
                        self.subset_dict[key] = child.index()

                        parent.appendRow(child)

                        # Make gray and unselectable components that aren't defined for a subset
                        try:
                            self.dc[i].compute_statistic(
                                'mean',
                                self.dc[i].subsets[j].components[k],
                                subset_state=self.dc[i].subsets[j].subset_state
                            )

                        except:
                            #                             print("Glue has raised an Incompatible Attribute error on this component. Let's do this instead.")
                            child.setEditable(False)
                            child.setSelectable(False)
                            child.setForeground(QtGui.QBrush(Qt.gray))

                    grandparent.appendRow(parent)
                parent_subset.appendRow(grandparent)
            self.model_subsets.appendRow(parent_subset)

            # Fill out the dict now that the indices are connected to the QStandardItemModel

            # Full datasets
            for i in range(0, parent_data.rowCount()):
                for j in range(0, parent_data.child(i).rowCount()):
                    key = "All data (" + parent_data.child(
                        i).text() + ")" + parent_data.child(i).child(j).text()
                    self.subset_dict[key] = parent_data.child(i).child(
                        j).index()

            # Subsets
            for i in range(0, parent_subset.rowCount()):
                for j in range(0, parent_subset.child(i).rowCount()):
                    for k in range(0,
                                   parent_subset.child(i).child(j).rowCount()):
                        key = parent_subset.child(i).child(j).text(
                        ) + parent_subset.child(i).child(j).child(k).text()
                        self.subset_dict[key] = parent_subset.child(i).child(
                            j).child(k).index()

        self.treeview.setUniformRowHeights(True)

        selection_model = QItemSelectionModel(self.model_subsets)
        self.treeview.setSelectionModel(selection_model)
        selection_model.selectionChanged.connect(self.myPressedEvent)

        # Select rows that should be selected

        sel_mod = self.treeview.selectionModel()

        for i in range(0, len(selected)):
            #             key = list(self.selected_dict.keys())[list(self.selected_dict.values()).index(self.selected_dict[i])]
            key = list(selected.keys())[i]
            index = self.subset_dict[key]
            print(index.parent().row(), index.row())
            #             print(index, type(index))
            #             print(type(self.treeview.selectionModel().select(index, QItemSelectionModel.Select)))
            #             sel_mod.select(index, QItemSelectionModel.Select|QItemSelectionModel.Rows)
            self.treeview.setCurrentIndex(index)

        self.treeview.setSelectionModel(sel_mod)

    def sortByComponents(self):
        '''
        Sorts the treeview by components- Dataset then component then subsets
        '''
        # Save the selected rows from the subset view if applicable

        try:
            selected = dict()
            for i in range(0, len(self.selected_indices)):
                item = self.model_subsets.itemFromIndex(
                    self.selected_indices[i])
                if item.parent().parent().text() == "Data":
                    key = "All data (" + item.parent().text(
                    ) + ")" + item.text()
                    selected[key] = item.index()
                else:
                    key = item.parent().text() + item.text()
                    selected[key] = item.index()
        except:
            pass

        # Set Expand/collapse button to "expand all"
        self.expand_data.setText("Expand all data and subsets")

        self.selection_model = QAbstractItemView.MultiSelection
        self.treeview.setSelectionMode(self.selection_model)

        # See if the model already exists
        try:
            self.treeview.setModel(self.model_components)

        except:

            self.model_components = QStandardItemModel()
            self.model_components.setHorizontalHeaderLabels([''])

            self.treeview.setModel(self.model_components)
            self.treeview.setUniformRowHeights(True)

            # populate the tree
            # Make all the datasets be parents, and make it so they are not selectable

            for i in range(0, len(dc)):
                grandparent = QStandardItem('{}'.format(self.dc.labels[i]))
                grandparent.setIcon(helpers.layer_icon(self.dc[i]))
                grandparent.setEditable(False)
                grandparent.setSelectable(False)

                # Make all the data components be children, nested under their parent
                for k in range(0, len(self.dc[i].components)):
                    parent = QStandardItem('{}'.format(
                        str(self.dc[i].components[k])))
                    parent.setEditable(False)
                    parent.setSelectable(False)

                    child = QStandardItem('{}'.format('All data (' +
                                                      self.dc.labels[i] + ')'))
                    child.setIcon(helpers.layer_icon(self.dc[i]))
                    child.setEditable(False)

                    parent.appendRow(child)

                    for j in range(0, len(self.dc.subset_groups)):
                        child = QStandardItem('{}'.format(
                            self.dc.subset_groups[j].label))
                        child.setEditable(False)
                        child.setIcon(
                            helpers.layer_icon(self.dc.subset_groups[j]))

                        try:
                            self.dc[i].compute_statistic(
                                'mean',
                                self.dc[i].subsets[j].components[k],
                                subset_state=self.dc[i].subsets[j].subset_state
                            )

                        except:
                            #                             print("Glue has raised an Incompatible Attribute error on this component. Let's do this instead.")
                            child.setEditable(False)
                            child.setSelectable(False)
                            child.setForeground(QtGui.QBrush(Qt.gray))

                        parent.appendRow(child)
                    grandparent.appendRow(parent)
                self.model_components.appendRow(grandparent)

                # Fill out the dict now that the indices are connected to the QStandardItemModel
                for i in range(0, grandparent.rowCount()):
                    for j in range(0, grandparent.child(i).rowCount()):
                        if grandparent.child(i).child(j).row() == 0:
                            key = grandparent.child(i).child(
                                j).text() + grandparent.child(i).text()
                            self.component_dict[key] = grandparent.child(
                                i).child(j).index()
                        else:
                            key = grandparent.child(i).child(
                                j).text() + " (" + grandparent.text(
                                ) + ")" + grandparent.child(i).text()
                            self.component_dict[key] = grandparent.child(
                                i).child(j).index()

        self.treeview.setUniformRowHeights(True)

        selection_model = QItemSelectionModel(self.model_components)
        self.treeview.setSelectionModel(selection_model)
        selection_model.selectionChanged.connect(self.myPressedEvent)

        # Select the rows that should be selected

        sel_mod = self.treeview.selectionModel()

        for i in range(0, len(selected)):
            key = list(selected.keys())[i]
            index = self.component_dict[key]
            #             self.treeview.selectionModel().select(index, QItemSelectionModel.Select)
            print(index.parent().row(), index.row())
            # This causes an error when it runs
            #             sel_mod.select(index, QItemSelectionModel.Select|QItemSelectionModel.Rows)
            self.treeview.setCurrentIndex(index)

        self.treeview.setSelectionModel(sel_mod)
Exemplo n.º 10
0
class TreeView(QWidget):
    def __init__(self, table, parent):
        QWidget.__init__(self, parent)
        self.window = parent
        self.tree = QTreeView(self)
        indent = self.tree.indentation()
        self.tree.setIndentation(indent / 2)

        self.model = DataModel(table)
        self.sorter = sorter = FilterModel(self)
        sorter.setSourceModel(self.model)
        self.tree.setModel(sorter)
        for col in range(3, 9):
            self.tree.setItemDelegateForColumn(col, PercentDelegate(self))
        self.tree.header().setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
        self.tree.header().customContextMenuRequested.connect(
            self._on_header_menu)
        self.tree.setSortingEnabled(True)
        self.tree.setAutoExpandDelay(0)
        self.tree.resizeColumnToContents(0)
        self.tree.resizeColumnToContents(NAME_COLUMN)
        self.tree.expand(self.sorter.index(0, 0))
        #self.tree.expandAll()

        self.tree.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
        self.tree.customContextMenuRequested.connect(self._on_tree_menu)

        searchbox = QHBoxLayout()
        self.search = QLineEdit(self)

        searchbox.addWidget(self.search)

        self.search_type = QComboBox(self)
        self.search_type.addItem("Contains", SEARCH_CONTAINS)
        self.search_type.addItem("Exact", SEARCH_EXACT)
        self.search_type.addItem("Reg.Exp", SEARCH_REGEXP)
        searchbox.addWidget(self.search_type)

        btn = QPushButton("&Search", self)
        searchbox.addWidget(btn)
        btn.clicked.connect(self._on_search)

        btn = QPushButton("&Next", self)
        searchbox.addWidget(btn)
        btn.clicked.connect(self._on_search_next)

        filterbox = QHBoxLayout()

        label = QLabel("Time Individual", self)
        filterbox.addWidget(label)
        self.individual_time = QSpinBox(self)
        self.individual_time.setMinimum(0)
        self.individual_time.setMaximum(100)
        self.individual_time.setSuffix(" %")
        filterbox.addWidget(self.individual_time)

        label = QLabel("Alloc Individual", self)
        filterbox.addWidget(label)
        self.individual_alloc = QSpinBox(self)
        self.individual_alloc.setMinimum(0)
        self.individual_alloc.setMaximum(100)
        self.individual_alloc.setSuffix(" %")
        filterbox.addWidget(self.individual_alloc)

        label = QLabel("Time Inherited", self)
        filterbox.addWidget(label)
        self.inherited_time = QSpinBox(self)
        self.inherited_time.setMinimum(0)
        self.inherited_time.setMaximum(100)
        self.inherited_time.setSuffix(" %")
        filterbox.addWidget(self.inherited_time)

        label = QLabel("Alloc Inherited", self)
        filterbox.addWidget(label)
        self.inherited_alloc = QSpinBox(self)
        self.inherited_alloc.setMinimum(0)
        self.inherited_alloc.setMaximum(100)
        self.inherited_alloc.setSuffix(" %")
        filterbox.addWidget(self.inherited_alloc)

        btn = QPushButton("&Filter", self)
        btn.clicked.connect(self._on_filter)
        filterbox.addWidget(btn)
        btn = QPushButton("&Reset", self)
        filterbox.addWidget(btn)
        btn.clicked.connect(self._on_reset_filter)

        vbox = QVBoxLayout()
        vbox.addLayout(searchbox)
        vbox.addLayout(filterbox)
        vbox.addWidget(self.tree)
        self.setLayout(vbox)

        self._search_idxs = None
        self._search_idx_no = 0

    def _expand_to(self, idx):
        idxs = [idx]
        parent = idx
        while parent and parent.isValid():
            parent = self.sorter.parent(parent)
            idxs.append(parent)
        #print(idxs)
        for idx in reversed(idxs[:-1]):
            data = self.sorter.data(idx, QtCore.Qt.DisplayRole)
            #print(data)
            self.tree.expand(idx)

    def _on_search(self):
        text = self.search.text()
        selected = self.tree.selectedIndexes()
        #         if selected:
        #             start = selected[0]
        #         else:
        start = self.sorter.index(0, NAME_COLUMN)
        search_type = self.search_type.currentData()
        if search_type == SEARCH_EXACT:
            method = QtCore.Qt.MatchFixedString
        elif search_type == SEARCH_CONTAINS:
            method = QtCore.Qt.MatchContains
        else:
            method = QtCore.Qt.MatchRegExp

        self._search_idxs = idxs = self.sorter.search(start, text, search_type)
        if idxs:
            self.window.statusBar().showMessage(
                "Found: {} occurence(s)".format(len(idxs)))
            self._search_idx_no = 0
            idx = idxs[0]
            self._locate(idx)
        else:
            self.window.statusBar().showMessage("Not found")

    def _locate(self, idx):
        self.tree.resizeColumnToContents(0)
        self.tree.resizeColumnToContents(NAME_COLUMN)
        self._expand_to(idx)
        self.tree.setCurrentIndex(idx)
        #self.tree.selectionModel().select(idx, QItemSelectionModel.ClearAndSelect | QItemSelectionModel.Current | QItemSelectionModel.Rows)
        #self.tree.scrollTo(idx, QAbstractItemView.PositionAtCenter)

    def _on_search_next(self):
        if self._search_idxs:
            n = len(self._search_idxs)
            self._search_idx_no = (self._search_idx_no + 1) % n
            idx = self._search_idxs[self._search_idx_no]
            self.window.statusBar().showMessage("Occurence {} of {}".format(
                self._search_idx_no, n))
            self._locate(idx)
        else:
            self.window.statusBar().showMessage("No search results")

    def _on_filter(self):
        self.sorter.setFilter(self.search.text(), self.individual_time.value(),
                              self.individual_alloc.value(),
                              self.inherited_time.value(),
                              self.inherited_alloc.value())

    def _on_reset_filter(self):
        self.sorter.reset()

    def _on_header_menu(self, pos):
        menu = make_header_menu(self.tree)
        menu.exec_(self.mapToGlobal(pos))

    def _on_tree_menu(self, pos):
        index = self.tree.indexAt(pos)
        #print("index: {}".format(index))
        if index.isValid():
            record = self.sorter.data(index, QtCore.Qt.UserRole + 1)
            #print("okay?..")
            #print("context: {}".format(record))
            menu = self.window.make_item_menu(self.model, record)
            menu.exec_(self.tree.viewport().mapToGlobal(pos))
Exemplo n.º 11
0
class DebugWindow(QMainWindow):
    """A main window to edit text and examine the generated token structure.

    Example::

        from PyQt5.Qt import *
        a=QApplication([])

        from parceqt.debug import DebugWindow
        w = DebugWindow()
        w.resize(1200,900)
        w.show()

        w.set_theme("default", True)

        from parce.lang.css import *
        w.set_root_lexicon(Css.root)
        w.set_text(open("path/to/parce/themes/default.css").read())

    In the debug window you can edit the text at the left and directly at the
    right examine the tree structure. Along the top of the window the path to
    the token at the current cursor position is displayed, from the root
    lexicon upto the token, from which the action is displayed.

    Clicking a button selects the associated range of the context or token in
    the text view. Clicking an item in the tree also selects that range in the
    text.

    Moving the cursor in the text updates the current item in the tree,
    and the displayed ancestor path.

    """

    show_updated_region_enabled = False

    def __init__(self, parent=None):
        super().__init__(parent, windowTitle="parceqt debugger")

        f = self._updated_format = QTextCharFormat()
        c = QColor("palegreen")
        c.setAlpha(64)
        f.setBackground(c)
        f = self._currentline_format = QTextCharFormat()
        f.setProperty(QTextCharFormat.FullWidthSelection, True)

        self._actions = Actions(self)
        self._actions.add_menus(self.menuBar())

        widget = QWidget(self)
        self.setCentralWidget(widget)
        layout = QVBoxLayout(margin=4, spacing=2)
        widget.setLayout(layout)

        top_layout = QHBoxLayout(margin=0, spacing=0)

        self.guessButton = QToolButton(self,
                                       clicked=self.guess_root_lexicon,
                                       toolTip="Guess Language",
                                       icon=self.style().standardIcon(
                                           QStyle.SP_BrowserReload))
        self.lexiconChooser = LexiconChooser(self)
        self.ancestorView = AncestorView(self)
        top_layout.addWidget(self.guessButton)
        top_layout.addWidget(self.lexiconChooser)
        top_layout.addWidget(self.ancestorView)
        top_layout.addStretch(10)
        layout.addLayout(top_layout)
        self.guessButton.setFixedHeight(
            self.lexiconChooser.sizeHint().height())

        splitter = QSplitter(self, orientation=Qt.Horizontal)
        layout.addWidget(splitter, 100)

        self.textEdit = QPlainTextEdit(lineWrapMode=QPlainTextEdit.NoWrap,
                                       cursorWidth=2)
        self.treeView = QTreeView()

        splitter.addWidget(self.textEdit)
        splitter.addWidget(self.treeView)
        splitter.setStretchFactor(0, 3)
        splitter.setStretchFactor(1, 2)

        self.extraSelectionManager = ExtraSelectionManager(self.textEdit)

        self.document = d = self.textEdit.document()
        self.textEdit.setDocument(self.document)

        self.worker = w = parceqt.worker(d)
        self.builder = b = w.builder()
        w.debugging = True

        self.setStatusBar(QStatusBar())
        self.create_model()

        # signal connections
        self.textEdit.viewport().installEventFilter(self)
        self.textEdit.installEventFilter(self)
        self.lexiconChooser.lexicon_changed.connect(
            self.slot_root_lexicon_changed)
        self.ancestorView.node_clicked.connect(self.slot_node_clicked)
        w.started.connect(self.slot_build_started)
        w.tree_updated.connect(self.slot_build_updated)
        self.textEdit.cursorPositionChanged.connect(
            self.slot_cursor_position_changed)
        self.treeView.clicked.connect(self.slot_item_clicked)

        self.textEdit.setFocus()
        self.set_theme()

        # somewhat larger font by default
        font = self.textEdit.font()
        font.setPointSizeF(11)
        self.textEdit.setFont(font)

    def create_model(self):
        """Instantiate a tree model for the tree view."""
        m = self.treeView.model()
        if not m:
            m = parceqt.treemodel.TreeModel(self.builder.root)
            m.connect_debugging_builder(self.builder)
            self.treeView.setModel(m)

    def delete_model(self):
        """Delete the model and remove it from the tree."""
        m = self.treeView.model()
        if m:
            m.disconnect_debugging_builder(self.builder)
            self.treeView.setModel(None)
            m.deleteLater()

    def set_text(self, text):
        """Set the text in the text edit."""
        self.document.setPlainText(text)

    def set_root_lexicon(self, lexicon):
        """Set the root lexicon to use."""
        self.lexiconChooser.set_root_lexicon(lexicon)

    def guess_root_lexicon(self):
        """Again choose the root lexicon based on the text."""
        text = self.document.toPlainText()
        if text:
            self.set_root_lexicon(parce.find(contents=text))

    def open_file(self, filename):
        """Read a file from disk and guess the language."""
        text = read_file(filename)
        root_lexicon = parce.find(filename=filename, contents=text)
        self.set_text(text)
        self.set_root_lexicon(root_lexicon)
        c = self.textEdit.textCursor()
        c.setPosition(0)
        self.textEdit.setTextCursor(c)

    def set_theme(self, theme="default", adjust_widget=True):
        """Set the theme to use for the text edit."""
        if isinstance(theme, str):
            theme = parce.theme_by_name(theme)
        formatter = parceqt.formatter.Formatter(theme) if theme else None
        if adjust_widget:
            if formatter:
                font = formatter.font(self)
                self.textEdit.setPalette(formatter.palette(self))
            else:
                font = QApplication.font(self)
                self.textEdit.setPalette(QApplication.palette(self))
            font.setPointSizeF(self.textEdit.font().pointSizeF())  # keep size
            self.textEdit.setFont(font)
            self.highlight_current_line()
        h = parceqt.highlighter.SyntaxHighlighter.instance(self.worker)
        h.set_formatter(formatter)

    def slot_build_started(self):
        """Called when the tree builder has started a build."""
        self.treeView.setCursor(Qt.BusyCursor)

    def slot_build_updated(self):
        """Called when the tree builder has finished a build."""
        self.treeView.unsetCursor()
        self.slot_cursor_position_changed()
        self.statusBar().showMessage(", ".join(
            lexicon_names(self.builder.lexicons)))
        tree = self.worker.get_root()
        self.lexiconChooser.setToolTip(
            parceqt.treemodel.TreeModel.node_tooltip(tree))
        if self.show_updated_region_enabled:
            self.show_updated_region()

    def slot_cursor_position_changed(self):
        """Called when the text cursor moved."""
        tree = self.worker.get_root()
        if tree:
            pos = self.textEdit.textCursor().position()
            doc = parceqt.document.Document(self.document)
            token = doc.token(pos)
            if token:
                self.ancestorView.set_token_path(token)
                model = self.treeView.model()
                if model:
                    index = model.get_model_index(token)
                    self.treeView.setCurrentIndex(index)
        elif tree is not None:
            self.ancestorView.clear()
        self.highlight_current_line()

    def slot_item_clicked(self, index):
        """Called when a node in the tree view is clicked."""
        tree = self.worker.get_root()
        if tree:
            model = self.treeView.model()
            if model:
                node = self.treeView.model().get_node(index)
                cursor = self.textEdit.textCursor()
                cursor.setPosition(node.end)
                cursor.setPosition(node.pos, QTextCursor.KeepAnchor)
                self.textEdit.setTextCursor(cursor)
        self.textEdit.setFocus()

    def slot_node_clicked(self, node):
        """Called when a button in the ancestor view is clicked."""
        tree = self.worker.get_root()
        if tree and node.root() is tree:
            cursor = self.textEdit.textCursor()
            cursor.setPosition(node.end)
            cursor.setPosition(node.pos, QTextCursor.KeepAnchor)
            self.textEdit.setTextCursor(cursor)
            self.textEdit.setFocus()
            model = self.treeView.model()
            if model:
                index = model.get_model_index(node)
                self.treeView.expand(index)
                self.treeView.setCurrentIndex(index)

    def slot_root_lexicon_changed(self, lexicon):
        """Called when the root lexicon is changed."""
        parceqt.set_root_lexicon(self.document, lexicon)

    def highlight_current_line(self):
        """Highlight the current line."""
        group = QPalette.Active if self.textEdit.hasFocus(
        ) else QPalette.Inactive
        p = self.textEdit.palette()
        color = p.color(group, QPalette.AlternateBase)
        self._currentline_format.setBackground(color)
        if color != p.color(group, QPalette.Base):
            c = self.textEdit.textCursor()
            c.clearSelection()
            self.extraSelectionManager.highlight(self._currentline_format, [c])
        else:
            self.extraSelectionManager.clear(self._currentline_format)

    def show_updated_region(self):
        """Highlight the updated region for 2 seconds."""
        end = self.builder.end
        if end >= self.document.characterCount() - 1:
            end = self.document.characterCount() - 1
            if self.builder.start == 0:
                return
        c = QTextCursor(self.document)
        c.setPosition(end)
        c.setPosition(self.builder.start, QTextCursor.KeepAnchor)
        self.extraSelectionManager.highlight(self._updated_format, [c],
                                             msec=2000)

    def clear_updated_region(self):
        self.extraSelectionManager.clear(self._updated_format)

    def eventFilter(self, obj, ev):
        """Implemented to support Ctrl+wheel zooming and keybfocus handling."""
        if obj == self.textEdit:
            if ev.type() in (QEvent.FocusIn, QEvent.FocusOut):
                self.highlight_current_line()
        else:  # viewport
            if ev.type() == QEvent.Wheel and ev.modifiers(
            ) == Qt.ControlModifier:
                if ev.angleDelta().y() > 0:
                    self.textEdit.zoomIn()
                elif ev.angleDelta().y() < 0:
                    self.textEdit.zoomOut()
                return True
        return False
Exemplo n.º 12
0
    class App(QWidget):
        def __init__(self):
            super().__init__()
            self.left = 100
            self.top = 100
            self.width = 1200
            self.height = 720
            self.setWindowTitle('Mammogram Prediction')
            self.setGeometry(self.left, self.top, self.width, self.height)

            self.initUI()

        def initUI(self):
            # Widget for showing picture. The QLabel gets a Pixmap added to it to show picture
            self.picture_name_label = QLabel(currently_selected_picture)
            self.picture_label = QLabel()
            self.prediction_text = QTextEdit()
            self.prediction_text.setReadOnly(True)
            self.model_label = QLabel()

            self.init_picture_and_predictions()

            self.resized_picture = self.picture.scaled(299, 299,
                                                       Qt.KeepAspectRatio,
                                                       Qt.FastTransformation)
            self.picture_label.setPixmap(self.resized_picture)
            self.picture_label.setMinimumWidth(299)
            self.picture_label.setMinimumHeight(299)
            self.picture_label.setContentsMargins(0, 19, 0, 0)

            # Tree and List view for file directory overview of pictures
            self.picture_directory_label = QLabel('Select a Picture:')
            picture_dir_path = 'pictures\\'
            picture_file_path = 'pictures\\'
            self.treeview_picture = QTreeView()
            self.listview_picture = QListView()

            self.dirModel_picture = QFileSystemModel()
            self.dirModel_picture.setRootPath(picture_dir_path)
            self.dirModel_picture.setFilter(QDir.NoDotAndDotDot | QDir.AllDirs)

            self.fileModel_picture = QFileSystemModel()
            self.fileModel_picture.setRootPath(picture_file_path)  #
            self.fileModel_picture.setFilter(QDir.NoDotAndDotDot | QDir.Files)

            self.treeview_picture.setModel(self.dirModel_picture)
            self.listview_picture.setModel(self.fileModel_picture)

            self.treeview_picture.setRootIndex(
                self.dirModel_picture.index(picture_dir_path))
            self.listview_picture.setRootIndex(
                self.fileModel_picture.index(picture_file_path))  #
            self.treeview_picture.setCurrentIndex(
                self.dirModel_picture.index(0, 0))

            self.treeview_picture.clicked.connect(
                self.on_picture_treeview_clicked)
            self.listview_picture.clicked.connect(
                self.on_picture_listview_clicked)
            self.treeview_picture.setColumnHidden(1, True)
            self.treeview_picture.setColumnWidth(0, 275)
            self.treeview_picture.setMinimumWidth(500)
            self.listview_picture.setMinimumWidth(500)

            # List view for file directory overview of five label models
            self.model_directory_label = QLabel('Select a Five Label Model:')
            model_path = 'trained_five_Models\\'
            self.listview_model = QListView()
            self.dirModel_model = QFileSystemModel()

            self.dirModel_model.setRootPath(model_path)
            self.dirModel_model.setFilter(QDir.NoDotAndDotDot | QDir.AllDirs)

            self.listview_model.setModel(self.dirModel_model)
            self.listview_model.setRootIndex(
                self.dirModel_model.index(model_path))
            self.listview_model.clicked.connect(self.on_model_listview_clicked)

            # List view for file directory overview of binary models
            self.model_binary_directory_label = QLabel(
                'Select a Binary Model:')
            model_binary_path = 'trained_binary_Models\\'
            self.listview_model_binary = QListView()
            self.dirModel_model_binary = QFileSystemModel()

            self.dirModel_model_binary.setRootPath(model_binary_path)
            self.dirModel_model_binary.setFilter(QDir.NoDotAndDotDot
                                                 | QDir.AllDirs)

            self.listview_model_binary.setModel(self.dirModel_model_binary)
            self.listview_model_binary.setRootIndex(
                self.dirModel_model_binary.index(model_binary_path))
            self.listview_model_binary.setSelectionMode(
                QAbstractItemView.MultiSelection)
            self.listview_model_binary.clicked.connect(
                self.on_model_binary_listview_clicked)

            # Layout handling.

            # self.gridlayout = QGridLayout()
            # self.gridlayout.addWidget(self.model_directory_label, 0, 0)
            # self.gridlayout.setColumnStretch(-15, -11)
            # self.gridlayout.addWidget(self.listview_model, 1, 0)
            # self.gridlayout.addWidget(self.picture_directory_label, 2, 0)
            # self.gridlayout.addWidget(self.treeview_picture, 3, 0)
            #
            # self.gridlayout.addWidget(self.model_binary_directory_label, 0, 1)
            # self.gridlayout.addWidget(self.listview_model_binary, 1, 1)
            # self.gridlayout.addWidget(self.listview_picture, 3, 1)
            #
            # self.gridlayout.addWidget(self.picture_label, 0, 2)
            # self.gridlayout.addWidget(self.picture_name_label, 1, 2)
            # self.gridlayout.addWidget(self.prediction_text, 3, 2)

            self.vbox = QVBoxLayout()
            self.vbox_left = QVBoxLayout()
            self.vbox_right = QVBoxLayout()
            self.hbox_outer = QHBoxLayout()
            self.hbox_top = QHBoxLayout()
            self.hbox_buttom = QHBoxLayout()
            self.vbox_five_label = QVBoxLayout()
            self.vbox_binary = QVBoxLayout()

            self.vbox.addLayout(self.hbox_outer)
            self.hbox_outer.addLayout(self.vbox_left)
            self.hbox_outer.addLayout(self.vbox_right)
            self.vbox_left.addLayout(self.hbox_top)
            self.hbox_top.addLayout(self.vbox_five_label)
            self.hbox_top.addLayout(self.vbox_binary)

            self.vbox_five_label.addWidget(self.model_directory_label)
            self.vbox_five_label.addWidget(self.listview_model)
            self.vbox_binary.addWidget(self.model_binary_directory_label)
            self.vbox_binary.addWidget(self.listview_model_binary)

            self.vbox_left.addWidget(self.picture_directory_label)
            self.vbox_left.addLayout(self.hbox_buttom)
            self.hbox_buttom.addWidget(self.treeview_picture)
            self.hbox_buttom.addWidget(self.listview_picture)

            self.vbox_right.addWidget(self.picture_label,
                                      alignment=Qt.AlignHCenter)
            self.vbox_right.addWidget(self.picture_name_label,
                                      alignment=Qt.AlignHCenter)
            self.vbox_right.addWidget(self.model_label,
                                      alignment=Qt.AlignHCenter)
            self.vbox_right.addWidget(self.prediction_text)

            self.vbox_right.setAlignment(Qt.AlignCenter)

            self.setLayout(self.vbox)
            self.sizePolicy = QSizePolicy(QSizePolicy.Minimum,
                                          QSizePolicy.Preferred)
            self.setSizePolicy(self.sizePolicy)
            self.show()

        def init_picture_and_predictions(self):
            if currently_selected_picture == 'Currently No Image Selected':
                self.picture = QtGui.QPixmap('GUI/no_image_selected.png')
                self.prediction_text.setText('')

        def on_picture_treeview_clicked(self, index):
            pathof_selected_dir = self.dirModel_picture.fileInfo(
                index).absoluteFilePath()
            self.listview_picture.setRootIndex(
                self.fileModel_picture.setRootPath(pathof_selected_dir))

        def on_picture_listview_clicked(self, index):
            global currently_selected_model
            global currently_selected_model_name
            global currently_selected_picture

            currently_selected_picture = self.fileModel_picture.fileInfo(
                index).absoluteFilePath()

            try:
                Image.open(currently_selected_picture)
                self.picture_name_label.setText(currently_selected_picture)
                self.picture_label.setPixmap(
                    QtGui.QPixmap(currently_selected_picture))
            except IOError:
                print('Exception: Chosen file is not a picture')

            # Checks if the selected picture has size 299
            image_in = cv2.imread(currently_selected_picture)
            size = image_in.shape[:2]
            if size[0] == 299:
                if currently_selected_model is not None:
                    for model in currently_selected_model:
                        new_prediction = self.makePrediction(
                            model,
                            self.convertPictureToNumpy(
                                currently_selected_picture))
                        split = currently_selected_model_name.split('_')
                        if split[4] in ('neg', 'bc', 'bm', 'mc', 'mm'):
                            self.show_binary_prediction(
                                new_prediction, split[4])
                        else:
                            self.show_five_prediction(new_prediction)
                else:
                    self.prediction_text.setText(
                        'No Model is Chosen for Prediction. Choose one to the left.'
                    )
            # If the selected picture is not size 299 it will be padded and cropped
            else:
                cropped_images = resize_image_padding(
                    currently_selected_picture)
                self.listview_picture.setRootIndex(
                    self.fileModel_picture.setRootPath('pictures/cropped/%s' %
                                                       cropped_images))

        def on_model_listview_clicked(self, index):
            global currently_selected_model
            global currently_selected_picture

            currently_selected_model = []

            selected_model_path = self.dirModel_model.fileInfo(
                index).absoluteFilePath()
            currently_selected_model.append(self.getModel(selected_model_path))

            self.model_label.setText(currently_selected_model_name[0])

            if currently_selected_picture != 'Currently No Image Selected':
                for model in currently_selected_model:
                    new_prediction = self.makePrediction(
                        model,
                        self.convertPictureToNumpy(currently_selected_picture))
                    self.show_five_prediction(new_prediction)

        def on_model_binary_listview_clicked(self):
            global currently_selected_model
            global currently_selected_picture
            currently_selected_model = []

            self.prediction_text.setText('')
            self.model_label.setText('')

            for x in self.listview_model_binary.selectedIndexes():
                selected_model_path = self.dirModel_model_binary.fileInfo(
                    x).absoluteFilePath()
                currently_selected_model.append(
                    self.getModel(selected_model_path))
            # if not currently_selected_model_name:
            #     self.model_label.setText(currently_selected_model_name)

            if currently_selected_picture != 'Currently No Image Selected':
                for y in currently_selected_model:
                    new_prediction = self.makePrediction(
                        y,
                        self.convertPictureToNumpy(currently_selected_picture))
                    self.show_binary_prediction(new_prediction, y.category)

        def getModel(self, model_path):
            global currently_selected_model_name
            currently_selected_model_name = []

            split = os.path.split(model_path)[1].split('_')
            model_version = split[0] + '_' + split[1] + '_' + split[
                2] + '_' + split[3]
            currently_selected_model_name.append(os.path.split(model_path)[1])

            model = getattr(sys.modules[__name__], model_version)()
            checkpoint_path = model_path + '/cp.ckpt'
            model.load_weights(checkpoint_path)
            return model

        def makePrediction(self, input_model, input_picture):
            image = tf.reshape(input_picture, [-1, 299, 299, 1])
            image = tf.cast(image, tf.float32)
            image = image / 255.0
            return input_model.predict(image)

        def convertPictureToNumpy(self, filename):
            img = Image.open(filename)
            np_array = np.array(img, dtype='uint8')
            return np_array

        def show_five_prediction(self, prediction):
            self.prediction_text.setText(
                "Probability of Negative: %s" % prediction[0, 0] +
                "\n\nProbability of Benign Calcification: %s" %
                prediction[0, 1] +
                "\n\nProbability of Benign Mass: %s" % prediction[0, 2] +
                "\n\nProbability of Malignant Calcification: %s" %
                prediction[0, 3] +
                "\n\nProbability of Malignant Mass: %s" % prediction[0, 4])

        def show_binary_prediction(self, prediction, category):
            if category == 'neg':
                self.prediction_text.append(
                    "Probability of Negative: %s %s \n" %
                    (prediction[0, 0], prediction[0, 1]))
            elif category == 'bc':
                self.prediction_text.append(
                    "Probability of Benign Calcification:  %s %s \n" %
                    (prediction[0, 0], prediction[0, 1]))
            elif category == 'bm':
                self.prediction_text.append(
                    "Probability of Benign Mass: %s %s \n" %
                    (prediction[0, 0], prediction[0, 1]))
            elif category == 'mc':
                self.prediction_text.append(
                    "Probability of Malignant Calcification: %s %s \n" %
                    (prediction[0, 0], prediction[0, 1]))
            elif category == 'mm':
                self.prediction_text.append(
                    "Probability of Malignant Mass: %s %s \n" %
                    (prediction[0, 0], prediction[0, 1]))
            else:
                self.prediction_text.append(
                    "Probability of ????: %s %s \n" %
                    (prediction[0, 0], prediction[0, 1]))

        def openFileDialog(self):
            fileName, _ = QFileDialog.getOpenFileName(
                self, "QFileDialog.getOpenFileName()", "",
                "All Files (*);;Python Files (*.py)")
            if fileName:
                return fileName

        def is_png(data):
            return data[:8] == '\x89PNG\x0d\x0a\x1a\x0a'
Exemplo n.º 13
0
class _LocatorDialog(QDialog):
    """Locator widget and implementation
    """

    def __init__(self, parent, commandClasses):
        QDialog.__init__(self, parent)
        self._terminated = False
        self._commandClasses = commandClasses

        self._createUi()

        self._loadingTimer = QTimer(self)
        self._loadingTimer.setSingleShot(True)
        self._loadingTimer.setInterval(200)
        self._loadingTimer.timeout.connect(self._applyLoadingCompleter)

        self._completerLoaderThread = _CompleterLoaderThread(self)

        self.finished.connect(self._terminate)

        self._command = None
        self._updateCurrentCommand()

    def _createUi(self):
        self.setWindowTitle(core.project().path().replace(os.sep, '/') or 'Locator')

        self.setLayout(QVBoxLayout())
        self.layout().setContentsMargins(0, 0, 0, 0)
        self.layout().setSpacing(1)

        biggerFont = self.font()
        biggerFont.setPointSizeF(biggerFont.pointSizeF() * 2)
        self.setFont(biggerFont)

        self._edit = _CompletableLineEdit(self)
        self._edit.updateCurrentCommand.connect(self._updateCurrentCommand)
        self._edit.enterPressed.connect(self._onEnterPressed)
        self._edit.installEventFilter(self)  # catch Up, Down
        self._edit.setFont(biggerFont)
        self.layout().addWidget(self._edit)
        self.setFocusProxy(self._edit)

        self._table = QTreeView(self)
        self._table.setFont(biggerFont)
        self._model = _CompleterModel()
        self._table.setModel(self._model)
        self._table.setItemDelegate(HTMLDelegate(self._table))
        self._table.setRootIsDecorated(False)
        self._table.setHeaderHidden(True)
        self._table.clicked.connect(self._onItemClicked)
        self._table.setAlternatingRowColors(True)
        self._table.installEventFilter(self)  # catch focus and give to the edit
        self.layout().addWidget(self._table)

        width = QFontMetrics(self.font()).width('x' * 64)  # width of 64 'x' letters
        self.resize(width, width * 0.62)

    def _terminate(self):
        if not self._terminated:
            if self._command is not None:
                self._command.terminate()
                self._command = None

            self._edit.terminate()

            self._completerLoaderThread.terminate()
            if self._model:
                self._model.terminate()
            core.workspace().focusCurrentDocument()
            self._terminated = True

    def _updateCurrentCommand(self):
        """Try to parse line edit text and set current command
        """
        if self._terminated:
            return

        newCommand = self._parseCurrentCommand()

        if newCommand is not self._command:
            if self._command is not None:
                self._command.updateCompleter.disconnect(self._updateCompletion)
                self._command.terminate()

            self._command = newCommand
            if self._command is not None:
                self._command.updateCompleter.connect(self._updateCompletion)

        self._updateCompletion()

    def _updateCompletion(self):
        """User edited text or moved cursor. Update inline and TreeView completion
        """
        if self._command is not None:
            completer = self._command.completer()

            if completer is not None and completer.mustBeLoaded:
                self._loadingTimer.start()
                self._completerLoaderThread.loadCompleter(self._command, completer)
            else:
                self._applyCompleter(self._command, completer)
        else:
            self._applyCompleter(None, _HelpCompleter(self._commandClasses))

    def _applyLoadingCompleter(self):
        """Set 'Loading...' message
        """
        self._applyCompleter(None, StatusCompleter('<i>Loading...</i>'))

    def onCompleterLoaded(self, command, completer):
        """The method called from _CompleterLoaderThread when the completer is ready
        This code works in the GUI thread
        """
        self._applyCompleter(command, completer)

    def _applyCompleter(self, command, completer):
        """Apply completer. Called by _updateCompletion or by thread function when Completer is constructed
        """
        self._loadingTimer.stop()

        if command is not None:
            command.onCompleterLoaded(completer)

        if completer is None:
            completer = _HelpCompleter([command])

        if self._edit.cursorPosition() == len(self._edit.text()):  # if cursor at the end of text
            self._edit.setInlineCompletion(completer.inline())

        self._model.setCompleter(completer)
        if completer.columnCount() > 1:
            self._table.resizeColumnToContents(0)
            self._table.setColumnWidth(0, self._table.columnWidth(0) + 20)  # 20 px spacing between columns

        selItem = completer.autoSelectItem()
        if selItem:
            index = self._model.createIndex(selItem[0],
                                            selItem[1])
            self._table.setCurrentIndex(index)

    def _onItemClicked(self, index):
        """Item in the TreeView has been clicked.
        Open file, if user selected it
        """
        if self._command is not None:
            fullText = self._model.completer.getFullText(index.row())
            if fullText is not None:
                self._command.onItemClicked(fullText)
                if self._tryExecCurrentCommand():
                    self.accept()
                    return
                else:
                    self._edit.setText(self._command.lineEditText())
                    self._updateCurrentCommand()

        self._edit.setFocus()

    def _onEnterPressed(self):
        """User pressed Enter or clicked item. Execute command, if possible
        """
        if self._table.currentIndex().isValid():
            self._onItemClicked(self._table.currentIndex())
        else:
            self._tryExecCurrentCommand()

    def _tryExecCurrentCommand(self):
        if self._command is not None and self._command.isReadyToExecute():
            self._command.execute()
            self.accept()
            return True
        else:
            return False

    def _chooseCommand(self, words):
        for cmd in self._commandClasses:
            if cmd.command == words[0]:
                return cmd, words[1:]

        isPath = words and (words[0].startswith('/') or
                            words[0].startswith('./') or
                            words[0].startswith('../') or
                            words[0].startswith('~/') or
                            words[0][1:3] == ':\\' or
                            words[0][1:3] == ':/'
                            )
        isNumber = len(words) == 1 and all([c.isdigit() for c in words[0]])

        def matches(cmd):
            if isPath:
                return cmd.isDefaultPathCommand
            elif isNumber:
                return cmd.isDefaultNumericCommand
            else:
                return cmd.isDefaultCommand

        for cmd in self._commandClasses:
            if matches(cmd):
                return cmd, words

    def _parseCurrentCommand(self):
        """ Parse text and try to get (command, completable word index)
        Return None if failed to parse
        """
        # Split line
        text = self._edit.commandText()
        words = splitLine(text)
        if not words:
            return None

        # Find command
        cmdClass, args = self._chooseCommand(words)

        if isinstance(self._command, cmdClass):
            command = self._command
        else:
            command = cmdClass()

        # Try to make command object
        try:
            command.setArgs(args)
        except InvalidCmdArgs:
            return None
        else:
            return command

    def eventFilter(self, obj, event):
        if obj is self._edit:
            if event.type() == QEvent.KeyPress and \
               event.key() in (Qt.Key_Up, Qt.Key_Down, Qt.Key_PageUp, Qt.Key_PageDown):
                return self._table.event(event)
        elif obj is self._table:
            if event.type() == QEvent.FocusIn:
                self._edit.setFocus()
                return True

        return False
Exemplo n.º 14
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())

        # Add auto-hide capability.
        self._waitForCtrlRelease = False
        core.actionManager().action("mNavigation/aNext").triggered.connect(
            self._setWaitForCtrlRelease)
        core.actionManager().action("mNavigation/aPrevious").triggered.connect(
            self._setWaitForCtrlRelease)
        QApplication.instance().installEventFilter(self)

    def terminate(self):
        """Explicitly called destructor
        """
        core.actionManager().removeAction("mView/aOpenedFiles")
        QApplication.instance().removeEventFilter(self)

    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)

    @pyqtSlot(Document, Document)
    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()

    @pyqtSlot(QItemSelection, QItemSelection)
    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()

    @pyqtSlot(QPoint)
    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))

    def _setWaitForCtrlRelease(self):
        # We can't see actual Ctrl+PgUp/PgDn keypresses, since these get eaten
        # by the QAction and don't even show up in the event filter below. We
        # want to avoid waiting for a Ctrl release if the menu item brought us
        # here. As a workaround, check that Ctrl is pressed. If so, it's
        # unlikely to be the menu item.
        if QApplication.instance().keyboardModifiers() & Qt.ControlModifier:
            self._waitForCtrlRelease = True
            self.show()
        else:
            # If this was a menu selection, then update the MRU list. We can't
            # do this now, since the current document hasn't been changed yet.
            QTimer.singleShot(0, self.model.sortDocuments)

    def eventFilter(self, obj, event):
        """An event filter that looks for ctrl key releases and focus out
           events."""
        # Wait for the user to release the Ctrl key.
        if (self._waitForCtrlRelease and event.type() == QEvent.KeyRelease
                and event.key() == Qt.Key_Control
                and event.modifiers() == Qt.NoModifier):
            self.model.sortDocuments()
            self._waitForCtrlRelease = False
            if not self.isPinned():
                self.hide()
        # Look for a focus out event sent by the containing widget's focus
        # proxy.
        if event.type() == QEvent.FocusOut and obj == self.focusProxy():
            self.model.sortDocuments()
        return QObject.eventFilter(self, obj, event)
Exemplo n.º 15
0
    class ClientList(QWidget):
        def __init__(self, data: ()):
            super().__init__()
            self.tree_view = QTreeView()

            self.model = QStandardItemModel()
            self.rootNode = self.model.invisibleRootItem()
            self.applyModel()

            buttons = QHBoxLayout()
            add = QPushButton("Add")
            add.clicked.connect(self.addClient)
            remove = QPushButton("Remove")
            remove.clicked.connect(self.removeClient)
            buttons.addWidget(add)
            buttons.addWidget(remove)

            layout = QVBoxLayout()
            layout.addLayout(buttons)
            layout.addWidget(self.tree_view)
            self.setLayout(layout)

            if data:
                self.setData(data)

            self.in_progress = False

        def addClient(self):
            branch = [QStandardItem(), QStandardItem()]
            branch[0].setText("/")
            branch[1].setText("client_")
            self.model.appendRow(branch)
            index = self.model.index(self.model.rowCount() - 1, 0)
            self.tree_view.setCurrentIndex(index)
            self.applyModel()

        def removeClient(self):
            index = self.tree_view.currentIndex()
            if index.isValid():
                self.model.removeRow(index.row())
                self.applyModel()

        def applyModel(self):
            self.model.setHeaderData(0, Qt.Horizontal, "Path")
            self.model.setHeaderData(1, Qt.Horizontal, "Client variable")
            self.tree_view.setModel(self.model)

        def getData(self):
            data = {}

            for row in range(self.model.rowCount()):
                path = self.model.data(self.model.index(row, 0))
                client = self.model.data(self.model.index(row, 1))

                if path and client:
                    data[path] = client

            return data

        def setData(self, data):
            for path, client in data.items():
                branch = [QStandardItem(), QStandardItem()]
                branch[0].setText(path)
                branch[1].setText(client)
                self.model.appendRow(branch)
            self.applyModel()
Exemplo n.º 16
0
class VisualStates(QMainWindow):
    def __init__(self, parent=None):
        super(QMainWindow, self).__init__()

        self.setWindowTitle("VisualStates")
        self.configDialog = None

        # root state
        self.globalNamespace = Namespace('', '')
        self.localNamespace = Namespace('', '')
        self.rootState = State(0, "root", True, self.localNamespace)
        self.activeState = self.rootState
        self.activeNamespace = self.localNamespace

        self.statusBar()
        self.createMenu()
        self.createTreeView()
        self.createStateCanvas()

        self.setGeometry(0, 0, 800, 600)
        self.show()

        self.fileManager = FileManager()
        self.importManager = ImportManager()
        self.automataPath = None

        self.libraries = []
        self.config = None

    def createMenu(self):
        # create actions
        newAction = QAction('&New', self)
        newAction.setShortcut('Ctrl+N')
        newAction.setStatusTip('Create New Visual States')
        newAction.triggered.connect(self.newAction)

        openAction = QAction('&Open', self)
        openAction.setShortcut('Ctrl+O')
        openAction.setStatusTip('Open Visual States')
        openAction.triggered.connect(self.openAction)

        importAction = QAction('&Import', self)
        openAction.setShortcut('Ctrl+I')
        importAction.setStatusTip('Import A State')
        importAction.triggered.connect(self.importAction)

        saveAction = QAction('&Save', self)
        saveAction.setShortcut('Ctrl+S')
        saveAction.setStatusTip('Save Visual States')
        saveAction.triggered.connect(self.saveAction)

        saveAsAction = QAction('&Save As', self)
        saveAsAction.setShortcut('Ctrl+Shift+S')
        saveAsAction.setStatusTip('Save Visual States as New One')
        saveAsAction.triggered.connect(self.saveAsAction)

        quitAction = QAction('&Quit', self)
        quitAction.setShortcut('Ctrl+Q')
        quitAction.setStatusTip('Quit Visual States')
        quitAction.triggered.connect(self.quitAction)

        # figures menu
        stateAction = QAction('&State', self)
        stateAction.setStatusTip('Create a state')
        stateAction.triggered.connect(self.stateAction)

        transitionAction = QAction('&Transition', self)
        transitionAction.setStatusTip('Create a transition')
        transitionAction.triggered.connect(self.transitionAction)

        # data menu
        timerAction = QAction('&Timer', self)
        timerAction.setShortcut('Ctrl+M')
        timerAction.setStatusTip('Set timing of states')
        timerAction.triggered.connect(self.timerAction)

        globalNamespaceAction = QAction('&Global Namespace', self)
        globalNamespaceAction.setShortcut('Ctrl+G')
        globalNamespaceAction.setStatusTip('Open Global Namespace')
        globalNamespaceAction.triggered.connect(self.globalNamespaceAction)

        stateNamespaceAction = QAction('&State Namespace', self)
        stateNamespaceAction.setShortcut('Ctrl+T')
        stateNamespaceAction.setStatusTip('Open State Namespace')
        stateNamespaceAction.triggered.connect(self.localNamespaceAction)

        # actions menu
        librariesAction = QAction('&Libraries', self)
        librariesAction.setShortcut('Ctrl+L')
        librariesAction.setStatusTip('Add additional libraries')
        librariesAction.triggered.connect(self.librariesAction)

        configFileAction = QAction('&ROS Config', self)
        configFileAction.setShortcut('Ctrl+R')
        configFileAction.setStatusTip('Edit ROS configuration')
        configFileAction.triggered.connect(self.configFileAction)

        generateCppAction = QAction('&Generate C++', self)
        generateCppAction.setShortcut('Ctrl+U')
        generateCppAction.setStatusTip('Generate C++ code')
        generateCppAction.triggered.connect(self.generateCppAction)

        generatePythonAction = QAction('&Generate Python', self)
        generatePythonAction.setShortcut('Ctrl+Y')
        generatePythonAction.setStatusTip('Generate Python code')
        generatePythonAction.triggered.connect(self.generatePythonAction)

        # help menu
        aboutAction = QAction('&About', self)
        aboutAction.setShortcut('F1')
        aboutAction.setStatusTip('Information about VisualStates')
        aboutAction.triggered.connect(self.aboutAction)

        # create main menu
        menubar = self.menuBar()
        archieveMenu = menubar.addMenu('&File')
        archieveMenu.addAction(newAction)
        archieveMenu.addAction(openAction)
        archieveMenu.addAction(importAction)
        archieveMenu.addAction(saveAction)
        archieveMenu.addAction(saveAsAction)
        archieveMenu.addAction(quitAction)

        figuresMenu = menubar.addMenu('&Figures')
        figuresMenu.addAction(stateAction)
        figuresMenu.addAction(transitionAction)

        dataMenu = menubar.addMenu('&Data')
        dataMenu.addAction(timerAction)
        dataMenu.addAction(globalNamespaceAction)
        dataMenu.addAction(stateNamespaceAction)

        actionsMenu = menubar.addMenu('&Actions')
        actionsMenu.addAction(librariesAction)
        actionsMenu.addAction(configFileAction)
        actionsMenu.addAction(generateCppAction)
        # actionsMenu.addAction(compileCppAction)
        actionsMenu.addAction(generatePythonAction)

        helpMenu = menubar.addMenu('&Help')
        helpMenu.addAction(aboutAction)

    def newAction(self):
        self.automataScene.clearScene()
        self.treeModel.removeAll()

        self.fileManager.setPath("")

        # create new root state
        self.globalNamespace = Namespace('', '')
        self.localNamespace = Namespace('', '')
        self.rootState = State(0, 'root', True, self.localNamespace)

        self.automataScene.setActiveState(self.rootState)

        self.automataScene.resetIndexes()

        self.libraries = []
        self.config = None

    def openAction(self):
        fileDialog = QFileDialog(self)
        fileDialog.setWindowTitle("Open VisualStates File")
        fileDialog.setViewMode(QFileDialog.Detail)
        fileDialog.setNameFilters(['VisualStates File (*.xml)'])
        fileDialog.setDefaultSuffix('.xml')
        fileDialog.setAcceptMode(QFileDialog.AcceptOpen)
        if fileDialog.exec_():
            self.openFile(fileDialog.selectedFiles()[0])

    def openFile(self, fileName):
        (rootState, config, libraries, globalNamespace) = self.fileManager.open(fileName)
        if rootState is not None:
            (self.rootState, self.config, self.libraries, self.globalNamespace) = (rootState, config, libraries, globalNamespace)
            self.automataPath = self.fileManager.fullPath
            self.treeModel.removeAll()
            self.treeModel.loadFromRoot(self.rootState)
            # set the active state as the loaded state
            self.automataScene.setActiveState(self.rootState)
            self.automataScene.setLastIndexes(self.rootState)
        else:
            self.showWarning("Wrong file selected",
                             "The selected file is not a valid VisualStates file")


    def saveAction(self):
        if len(self.fileManager.getFileName()) == 0:
            self.saveAsAction()
        else:
            self.fileManager.save(self.rootState, self.config, self.libraries, self.globalNamespace)

    def saveAsAction(self):
        fileDialog = QFileDialog(self)
        fileDialog.setWindowTitle("Save VisualStates Project")
        fileDialog.setViewMode(QFileDialog.Detail)
        fileDialog.setNameFilters(['VisualStates File (*.xml)'])
        fileDialog.setAcceptMode(QFileDialog.AcceptSave)
        if fileDialog.exec_():
            self.fileManager.setFullPath(fileDialog.selectedFiles()[0])
            self.fileManager.save(self.rootState, self.config, self.libraries, self.globalNamespace)

    def quitAction(self):
        # print('Quit')
        self.close()

    def stateAction(self):
        self.automataScene.setOperationType(OpType.ADDSTATE)

    def transitionAction(self):
        self.automataScene.setOperationType(OpType.ADDTRANSITION)

    def importAction(self):
        fileDialog = QFileDialog(self)
        fileDialog.setWindowTitle("Import VisualStates File")
        fileDialog.setViewMode(QFileDialog.Detail)
        fileDialog.setNameFilters(['VisualStates File (*.xml)'])
        fileDialog.setDefaultSuffix('.xml')
        fileDialog.setAcceptMode(QFileDialog.AcceptOpen)
        if fileDialog.exec_():
            tempPath = self.fileManager.getFullPath()
            file = self.fileManager.open(fileDialog.selectedFiles()[0])
            if file[0] is not None:
                self.fileManager.setPath(tempPath)
                # if the current active state already has an initial state make sure that
                # there will not be any initial state in the imported state
                if self.activeState.getInitialChild() is not None:
                    for childState in file[0].getChildren():
                        childState.setInitial(False)

                # Update importing Namespaces
                importedState, self.config, self.libraries, self.globalNamespace = self.importManager.updateAuxiliaryData(file, self)
                self.treeModel.loadFromRoot(importedState, self.activeState)
                self.automataScene.displayState(self.activeState)
                self.automataScene.setLastIndexes(self.rootState)
            else:
                self.showWarning("Wrong file selected",
                                 "The selected file is not a valid VisualStates file")

    def timerAction(self):
        if self.activeState is not None:
            timerDialog = TimerDialog('Time Step Duration', str(self.activeState.getTimeStep()))
            timerDialog.timeChanged.connect(self.timeStepDurationChanged)
            timerDialog.exec_()

    def globalNamespaceAction(self):
        self.globalNamespaceDialog = NamespaceDialog('Global Namespace', self.globalNamespace)
        self.globalNamespaceDialog.namespaceChanged.connect(self.globalNamespaceChanged)
        self.globalNamespaceDialog.exec_()

    def localNamespaceAction(self):
        self.localNamespaceDialog = NamespaceDialog('Local Namespace', self.activeNamespace)
        self.localNamespaceDialog.namespaceChanged.connect(self.localNamespaceChanged)
        self.localNamespaceDialog.exec_()

    def librariesAction(self):
        librariesDialog = LibrariesDialog('Libraries', self.libraries)
        librariesDialog.librariesChanged.connect(self.librariesChanged)
        librariesDialog.exec_()

    def configFileAction(self):
        if self.config is None:
            self.config = RosConfig()
        self.configDialog = RosConfigDialog('Config', self.config)
        self.configDialog.exec_()

    def showWarning(self, title, msg):
        QMessageBox.warning(self, title, msg)

    def showInfo(self, title, msg):
        QMessageBox.information(self, title, msg)

    def generateCppAction(self):
        stateList = []
        if self.fileManager.hasFile():
            self.getStateList(self.rootState, stateList)
            if self.config is None:
                self.config = RosConfig()
            generator = CppRosGenerator(self.libraries, self.config, stateList, self.globalNamespace)
            generator.generate(self.fileManager.getPath(), self.fileManager.getFileName())
            self.showInfo('C++ Code Generation', 'C++ code generation is successful.')
        else:
            self.showWarning('C++ Generation', 'Please save the project before code generation.')

    # def compileCppAction(self):
    #     # print('compile cpp action')
    #     pass

    def generatePythonAction(self):
        stateList = []
        if self.fileManager.hasFile():
            self.getStateList(self.rootState, stateList)
            if self.config is None:
                self.config = RosConfig()
            generator = PythonRosGenerator(self.libraries, self.config, stateList, self.globalNamespace)
            generator.generate(self.fileManager.getPath(), self.fileManager.getFileName())
            self.showInfo('Python Code Generation', 'Python code generation is successful.')
        else:
            self.showWarning('Python Generation', 'Please save the project before code generation.')

    def aboutAction(self):
        aboutDialog = AboutDialog()
        aboutDialog.exec_()

    def createTreeView(self):
        dockWidget = QDockWidget()
        dockWidget.setAllowedAreas(Qt.LeftDockWidgetArea)
        dockWidget.setFeatures(QDockWidget.NoDockWidgetFeatures)
        dockWidget.setTitleBarWidget(QWidget())
        self.treeView = QTreeView()
        self.treeView.clicked.connect(self.treeItemClicked)
        self.treeModel = TreeModel()
        self.treeView.setModel(self.treeModel)

        self.upButton = QPushButton()
        self.upButton.setText('Up')
        self.upButton.clicked.connect(self.upButtonClicked)

        leftContainer = QWidget()
        leftLayout = QVBoxLayout()
        leftLayout.addWidget(self.treeView)
        leftLayout.addWidget(self.upButton)
        leftContainer.setLayout(leftLayout)

        dockWidget.setWidget(leftContainer)
        self.addDockWidget(Qt.LeftDockWidgetArea, dockWidget)

    def createStateCanvas(self):
        self.stateCanvas = QGraphicsView()
        self.automataScene = AutomataScene()
        self.automataScene.setSceneRect(0, 0, 2000, 2000)
        self.automataScene.activeStateChanged.connect(self.activeStateChanged)
        self.automataScene.activeNamespaceChanged.connect(self.activeNamespaceChanged)
        self.automataScene.stateInserted.connect(self.stateInserted)
        self.automataScene.stateRemoved.connect(self.stateRemoved)
        self.automataScene.stateImported.connect(self.stateImported)
        self.automataScene.transitionInserted.connect(self.transitionInserted)
        self.automataScene.stateNameChangedSignal.connect(self.stateNameChanged)
        self.automataScene.setActiveState(self.rootState)

        self.setCentralWidget(self.stateCanvas)
        self.stateCanvas.setScene(self.automataScene)
        self.stateCanvas.setRenderHint(QPainter.Antialiasing)
        self.stateCanvas.setAcceptDrops(True)

    def stateInserted(self, state):
        if self.activeState != self.rootState:
            parent = self.treeModel.getByDataId(self.activeState.id)
            self.treeModel.insertState(state, QColor(Qt.white), parent)
        else:
            self.treeModel.insertState(state, QColor(Qt.white))

    def stateRemoved(self, state):
        if self.activeState != self.rootState:
            parent = self.treeModel.getByDataId(self.activeState.id)
            self.treeModel.removeState(state.stateData, parent)
        else:
            self.treeModel.removeState(state.stateData)

    def stateImported(self):
        self.importAction()

    def transitionInserted(self, tran):
        # print('transition inserted:' + tran.transitionData.name)
        pass

    def stateNameChanged(self, state):
        dataItem = self.treeModel.getByDataId(state.stateData.id)
        if dataItem != None:
            dataItem.name = state.stateData.name
            self.treeModel.layoutChanged.emit()

    def activeStateChanged(self):
        if self.automataScene.activeState != self.activeState:
            # print('visual states active state changed:' + self.automataScene.activeState.name)
            self.activeState = self.automataScene.activeState
            if self.activeState == self.rootState:
                self.treeView.selectionModel().clearSelection()
            else:
                self.treeView.setCurrentIndex(self.treeModel.indexOf(self.treeModel.getByDataId(self.activeState.id)))

    def activeNamespaceChanged(self):
        if self.automataScene.activeNamespace != self.activeNamespace:
            self.activeNamespace = self.automataScene.activeNamespace

    def upButtonClicked(self):
        if self.activeState != None:
            if self.activeState.parent != None:
                #print(self.activeState.parent.id)
                self.automataScene.setActiveState(self.activeState.parent)

    def getStateById(self, state, id):
        if state.id == id:
            return state
        else:
            result = None
            for child in state.getChildren():
                result = self.getStateById(child, id)
                if result is not None:
                    return result
            return result

    def treeItemClicked(self, index):
        # print('clicked item.id:' + str(index.internalPointer().id))
        state = self.getStateById(self.rootState, index.internalPointer().id)
        if state is not None:
            # set the active state as the loaded state
            self.automataScene.setActiveState(state)

    def timeStepDurationChanged(self, duration):
        if self.activeState is not None:
            self.activeState.setTimeStep(duration)

    def librariesChanged(self, libraries):
        self.libraries = libraries

    def globalNamespaceChanged(self):
        if self.globalNamespaceDialog:
            self.globalNamespace = self.globalNamespaceDialog.getNamespace()

    def localNamespaceChanged(self):
        if self.localNamespaceDialog:
            self.activeNamespace = self.localNamespaceDialog.getNamespace()

    def getStateList(self, state, stateList):
        if len(state.getChildren()) > 0:
            stateList.append(state)

        for s in state.getChildren():
            self.getStateList(s, stateList)
Exemplo n.º 17
0
class _LocatorDialog(QDialog):
    """Locator widget and implementation
    """
    def __init__(self, parent, commandClasses):
        QDialog.__init__(self, parent)
        self._terminated = False
        self._commandClasses = commandClasses

        self._createUi()

        self._loadingTimer = QTimer(self)
        self._loadingTimer.setSingleShot(True)
        self._loadingTimer.setInterval(200)
        self._loadingTimer.timeout.connect(self._applyLoadingCompleter)

        self._completerLoaderThread = _CompleterLoaderThread(self)

        self.finished.connect(self._terminate)

        self._command = None
        self._updateCurrentCommand()

    def _createUi(self):
        self.setWindowTitle(core.project().path() or 'Locator')

        self.setLayout(QVBoxLayout())
        self.layout().setContentsMargins(0, 0, 0, 0)
        self.layout().setSpacing(1)

        biggerFont = self.font()
        biggerFont.setPointSizeF(biggerFont.pointSizeF() * 2)
        self.setFont(biggerFont)

        self._edit = _CompletableLineEdit(self)
        self._edit.updateCurrentCommand.connect(self._updateCurrentCommand)
        self._edit.enterPressed.connect(self._onEnterPressed)
        self._edit.installEventFilter(self)  # catch Up, Down
        self.layout().addWidget(self._edit)
        self.setFocusProxy(self._edit)

        self._table = QTreeView(self)
        self._table.setFont(biggerFont)
        self._model = _CompleterModel()
        self._table.setModel(self._model)
        self._table.setItemDelegate(HTMLDelegate(self._table))
        self._table.setRootIsDecorated(False)
        self._table.setHeaderHidden(True)
        self._table.clicked.connect(self._onItemClicked)
        self._table.setAlternatingRowColors(True)
        self._table.installEventFilter(
            self)  # catch focus and give to the edit
        self.layout().addWidget(self._table)

        width = QFontMetrics(self.font()).width('x' *
                                                64)  # width of 64 'x' letters
        self.resize(width, width * 0.62)

    def _terminate(self):
        if not self._terminated:
            if self._command is not None:
                self._command.terminate()
                self._command = None

            self._edit.terminate()

            self._completerLoaderThread.terminate()
            core.workspace().focusCurrentDocument()
            self._terminated = True

    def _updateCurrentCommand(self):
        """Try to parse line edit text and set current command
        """
        if self._terminated:
            return

        newCommand = self._parseCurrentCommand()

        if newCommand is not self._command:
            if self._command is not None:
                self._command.updateCompleter.disconnect(
                    self._updateCompletion)
                self._command.terminate()

            self._command = newCommand
            if self._command is not None:
                self._command.updateCompleter.connect(self._updateCompletion)

        self._updateCompletion()

    def _updateCompletion(self):
        """User edited text or moved cursor. Update inline and TreeView completion
        """
        if self._command is not None:
            completer = self._command.completer()

            if completer is not None and completer.mustBeLoaded:
                self._loadingTimer.start()
                self._completerLoaderThread.loadCompleter(
                    self._command, completer)
            else:
                self._applyCompleter(self._command, completer)
        else:
            self._applyCompleter(None, _HelpCompleter(self._commandClasses))

    def _applyLoadingCompleter(self):
        """Set 'Loading...' message
        """
        self._applyCompleter(None, StatusCompleter('<i>Loading...</i>'))

    def onCompleterLoaded(self, command, completer):
        """The method called from _CompleterLoaderThread when the completer is ready
        This code works in the GUI thread
        """
        self._applyCompleter(command, completer)

    def _applyCompleter(self, command, completer):
        """Apply completer. Called by _updateCompletion or by thread function when Completer is constructed
        """
        self._loadingTimer.stop()

        if command is not None:
            command.onCompleterLoaded(completer)

        if completer is None:
            completer = _HelpCompleter([command])

        if self._edit.cursorPosition() == len(
                self._edit.text()):  # if cursor at the end of text
            self._edit.setInlineCompletion(completer.inline())

        self._model.setCompleter(completer)
        if completer.columnCount() > 1:
            self._table.resizeColumnToContents(0)
            self._table.setColumnWidth(0,
                                       self._table.columnWidth(0) +
                                       20)  # 20 px spacing between columns

        selItem = completer.autoSelectItem()
        if selItem:
            index = self._model.createIndex(selItem[0], selItem[1])
            self._table.setCurrentIndex(index)

    def _onItemClicked(self, index):
        """Item in the TreeView has been clicked.
        Open file, if user selected it
        """
        if self._command is not None:
            fullText = self._model.completer.getFullText(index.row())
            if fullText is not None:
                self._command.onItemClicked(fullText)
                if self._tryExecCurrentCommand():
                    self.accept()
                    return
                else:
                    self._edit.setText(self._command.lineEditText())
                    self._updateCurrentCommand()

        self._edit.setFocus()

    def _onEnterPressed(self):
        """User pressed Enter or clicked item. Execute command, if possible
        """
        if self._table.currentIndex().isValid():
            self._onItemClicked(self._table.currentIndex())
        else:
            self._tryExecCurrentCommand()

    def _tryExecCurrentCommand(self):
        if self._command is not None and self._command.isReadyToExecute():
            self._command.execute()
            self.accept()
            return True
        else:
            return False

    def _chooseCommand(self, words):
        for cmd in self._commandClasses:
            if cmd.command == words[0]:
                return cmd, words[1:]

        isPath = words and (words[0].startswith('/')
                            or words[0].startswith('./')
                            or words[0].startswith('../')
                            or words[0].startswith('~/')
                            or words[0][1:3] == ':\\' or words[0][1:3] == ':/')
        isNumber = len(words) == 1 and all([c.isdigit() for c in words[0]])

        def matches(cmd):
            if isPath:
                return cmd.isDefaultPathCommand
            elif isNumber:
                return cmd.isDefaultNumericCommand
            else:
                return cmd.isDefaultCommand

        for cmd in self._commandClasses:
            if matches(cmd):
                return cmd, words

    def _parseCurrentCommand(self):
        """ Parse text and try to get (command, completable word index)
        Return None if failed to parse
        """
        # Split line
        text = self._edit.commandText()
        words = splitLine(text)
        if not words:
            return None

        # Find command
        cmdClass, args = self._chooseCommand(words)

        if isinstance(self._command, cmdClass):
            command = self._command
        else:
            command = cmdClass()

        # Try to make command object
        try:
            command.setArgs(args)
        except InvalidCmdArgs:
            return None
        else:
            return command

    def eventFilter(self, obj, event):
        if obj is self._edit:
            if event.type() == QEvent.KeyPress and \
               event.key() in (Qt.Key_Up, Qt.Key_Down, Qt.Key_PageUp, Qt.Key_PageDown):
                return self._table.event(event)
        elif obj is self._table:
            if event.type() == QEvent.FocusIn:
                self._edit.setFocus()
                return True

        return False
Exemplo n.º 18
0
Arquivo: dock.py Projeto: gpa14/enki
class NavigatorDock(DockWidget):

    def __init__(self):
        DockWidget.__init__(self, core.mainWindow(), '&Navigator', QIcon(':/enkiicons/goto.png'), "Alt+N")

        self._tags = []

        self._tree = QTreeView(self)
        self._tree.installEventFilter(self)
        self._tree.setHeaderHidden(True)
        self.setFocusProxy(self._tree)

        self._filterEdit = LineEdit(self)
        self._filterEdit.setClearButtonVisible(True)
        self._filterEdit.textEdited.connect(self._applyFilter)
        self._filterEdit.clearButtonClicked.connect(self._applyFilter)
        self._filterEdit.clearButtonClicked.connect(self._tree.setFocus)
        self._filterEdit.clearButtonClicked.connect(self._hideFilter)
        self._filterEdit.installEventFilter(self)

        self._displayWidget = QWidget(self)
        layout = QVBoxLayout(self._displayWidget)
        layout.addWidget(self._tree)
        layout.addWidget(self._filterEdit)
        layout.setContentsMargins(0, 0, 0, 0)

        self.setWidget(self._displayWidget)

        self._tagModel = _TagModel(self._tree)
        self._tagModel.jumpToTagDone.connect(self._hideFilter)

        self._tree.setModel(self._tagModel)
        self._tree.activated.connect(self._tagModel.onActivated)
        self._tree.clicked.connect(self._tagModel.onActivated)
        self._tagModel.modelAboutToBeReset.connect(self._onModelAboutToBeReset)
        self._tagModel.modelReset.connect(self._onModelReset)
        self._currentTagPath = None

        self._errorLabel = None

        self._installed = False

    def term(self):
        self._tagModel.term()

    def install(self):
        if not self._installed:
            core.mainWindow().addDockWidget(Qt.RightDockWidgetArea, self)
            core.actionManager().addAction("mView/aNavigator", self.showAction())
            self._installed = True

    def remove(self):
        if self._installed:
            core.mainWindow().removeDockWidget(self)
            core.actionManager().removeAction("mView/aNavigator")
            self.hide()
            self._installed = False

    def setTags(self, tags):
        self._tags = tags
        self._setFilteredTags(tags)
        self._hideFilter()

        if self.widget() is not self._displayWidget:
            self.setWidget(self._displayWidget)
            self._displayWidget.show()
        if self._errorLabel is not None:
            self._errorLabel.hide()

    def _setFilteredTags(self, tags):
        self._tagModel.setTags(tags)

    def onError(self, error):
        self._displayWidget.hide()
        if self._errorLabel is None:
            self._errorLabel = QLabel(self)
            self._errorLabel.setWordWrap(True)

        self._errorLabel.setText(error)

        if not self.widget() is self._errorLabel:
            self.setWidget(self._errorLabel)
            self._errorLabel.show()
            self._displayWidget.hide()

    def _onModelAboutToBeReset(self):
        currIndex = self._tree.currentIndex()
        self._currentTagPath = self._tagModel.tagPathForIndex(currIndex) if currIndex.isValid() else None

    def _onModelReset(self):
        self._tree.expandAll()

        # restore current item
        if self._currentTagPath is not None:
            index = self._tagModel.indexForTagPath(self._currentTagPath)
            if index.isValid():
                self._tree.setCurrentIndex(index)

    def eventFilter(self, object_, event):
        if object_ is self._tree:
            if event.type() == QEvent.KeyPress:
                if event.key() == Qt.Key_Backspace:
                    if event.modifiers() == Qt.ControlModifier:
                        self._onTreeCtrlBackspace()
                    else:
                        self._onTreeBackspace()
                    return True
                elif event.text() and \
                        (event.text().isalnum() or event.text() == '_'):
                    self._onTreeTextTyped(event.text())
                    return True
        elif object_ is self._filterEdit:
            if event.type() == QEvent.KeyPress:
                if event.key() in (Qt.Key_Up, Qt.Key_Down):
                    self._tree.setFocus()
                    self._tree.event(event)
                    return True
                elif event.key() in (Qt.Key_Enter, Qt.Key_Return):
                    currIndex = self._tree.currentIndex()
                    if currIndex.isValid():
                        self._tagModel.onActivated(currIndex)

        return DockWidget.eventFilter(self, object_, event)

    def _hideFilter(self):
        hadText = self._filterEdit.text() != ''
        self._filterEdit.clear()
        self._filterEdit.hide()
        if hadText:
            self._applyFilter()

    def _applyFilter(self):
        text = self._filterEdit.text()
        if text:
            if not text.startswith('*'):
                text = '*' + text
            if not text.endswith('*'):
                text = text + '*'

            wildcard = text.lower()

            filteredTags = _filterTags(wildcard, self._tags)

            self._setFilteredTags(filteredTags)
            self._tree.expandAll()
            if filteredTags:
                firstMatchingTag = _findFirstMatching(wildcard, filteredTags)
                path = _tagPath(firstMatchingTag)
                index = self._tagModel.indexForTagPath(path)
                self._tree.setCurrentIndex(index)
        else:
            self._setFilteredTags(self._tags)

        if text:
            self._filterEdit.show()
        elif not self._filterEdit.hasFocus():
            self._hideFilter()

    def _onTreeTextTyped(self, text):
        self._filterEdit.setText(self._filterEdit.text() + text)
        self._applyFilter()

    def _onTreeBackspace(self):
        text = self._filterEdit.text()
        if text:
            self._filterEdit.setText(text[:-1])
            self._applyFilter()

    def _onTreeCtrlBackspace(self):
        self._hideFilter()
        self._applyFilter()
Exemplo n.º 19
0
class Dialog_ImageFolder():
    def __init__(self, parent, title, init_path):
        self.w = QDialog(parent)

        self.parent = parent
        self.left = 300
        self.top = 300
        self.width = 600
        self.height = 400
        self.title = title

        self.dirModel = QFileSystemModel()
        self.dirModel.setRootPath(init_path)
        self.dirModel.setFilter(QDir.NoDotAndDotDot | QDir.AllDirs)
        self.treeview = QTreeView()
        self.treeview.setModel(self.dirModel)
        self.treeview.setRootIndex(self.dirModel.index(""))
        self.treeview.clicked.connect(self.on_clicked)
        #--- Hide All Header Sections Except First ----
        header = self.treeview.header()
        for sec in range(1, header.count()):
            header.setSectionHidden(sec, True)
        #--- ---- ---- ---- ---- ---- ---- ---- ---- --

        focus_index = self.dirModel.index(init_path)
        self.treeview.setCurrentIndex(focus_index)
        self.current_row_changed()

        self.listview = QListView()
        self.listview.setViewMode(QListView.IconMode)
        self.listview.setIconSize(QSize(192, 192))

        targetfiles1 = glob.glob(os.path.join(init_path, '*.png'))
        targetfiles2 = glob.glob(os.path.join(init_path, '*.tif'))
        targetfiles3 = glob.glob(os.path.join(init_path, '*.tiff'))
        targetfiles = targetfiles1 + targetfiles2 + targetfiles3
        lm = _MyListModel(targetfiles, self.parent)
        self.listview.setModel(lm)

        self.sub_layout = QHBoxLayout()
        self.sub_layout.addWidget(self.treeview)
        self.sub_layout.addWidget(self.listview)

        self.buttonBox = QDialogButtonBox(QDialogButtonBox.Open
                                          | QDialogButtonBox.Cancel)
        self.buttonBox.accepted.connect(self.accept)
        self.buttonBox.rejected.connect(self.reject)

        self.main_layout = QVBoxLayout()
        self.main_layout.addLayout(self.sub_layout)
        self.main_layout.addWidget(self.buttonBox)

        self.w.setGeometry(self.left, self.top, self.width, self.height)
        self.w.setWindowTitle(self.title)
        self.w.setWindowIcon(QIcon(os.path.join(icon_dir, 'Mojo2_16.png')))
        self.w.setLayout(self.main_layout)

    def current_row_changed(self):
        index = self.treeview.currentIndex()
        self.treeview.scrollTo(index, QAbstractItemView.EnsureVisible)
        self.treeview.resizeColumnToContents(0)

    def on_clicked(self, index):
        path = self.dirModel.fileInfo(index).absoluteFilePath()

        targetfiles1 = glob.glob(os.path.join(path, '*.png'))
        targetfiles2 = glob.glob(os.path.join(path, '*.tif'))
        targetfiles3 = glob.glob(os.path.join(path, '*.tiff'))
        targetfiles = targetfiles1 + targetfiles2 + targetfiles3

        lm = _MyListModel(targetfiles, self.parent)
        self.listview.setModel(lm)

    def accept(self):
        index = self.treeview.currentIndex()
        self.newdir = self.dirModel.filePath(index)
        self.w.done(1)

    def reject(self):
        self.w.done(0)

    def GetValue(self):
        index = self.treeview.currentIndex()
        self.newdir = self.dirModel.filePath(index)
        return self.newdir
Exemplo n.º 20
0
class MainWindow(QWidget):
    Id, Password = range(2)
    CONFIG_FILE = 'config'

    def __init__(self):
        super().__init__()
        with open(self.CONFIG_FILE, 'a'):
            pass
        self.init()

    def init(self):
        # ------ initUI
        self.resize(555, 245)
        self.setFixedSize(555, 245)
        self.center()
        self.setWindowTitle('Portal Connector')
        self.setWindowIcon(QIcon('gao.ico'))
        self.backgroundRole()
        palette1 = QPalette()
        palette1.setColor(self.backgroundRole(), QColor(250, 250,
                                                        250))  # 设置背景颜色

        self.setPalette(palette1)

        # ------setLeftWidget

        self.dataGroupBox = QGroupBox("Saved", self)
        self.dataGroupBox.setGeometry(10, 10, 60, 20)
        self.dataGroupBox.setStyleSheet(MyGroupBox)

        self.model = QStandardItemModel(0, 2, self)
        self.model.setHeaderData(self.Id, Qt.Horizontal, "Id")
        self.model.setHeaderData(self.Password, Qt.Horizontal, "Pw")

        self.dataView = QTreeView(self)
        self.dataView.setGeometry(10, 32, 255, 150)
        self.dataView.setRootIsDecorated(False)
        self.dataView.setAlternatingRowColors(True)
        self.dataView.setModel(self.model)
        self.dataView.setStyleSheet(MyTreeView)

        save_btn = QPushButton('Save', self)
        save_btn.setGeometry(15, 195, 100, 35)
        save_btn.setStyleSheet(MyPushButton)

        delete_btn = QPushButton('Delete', self)
        delete_btn.setGeometry(135, 195, 100, 35)
        delete_btn.setStyleSheet(MyPushButton)

        # ------ setRightWidget

        username = QLabel('Id:', self)
        username.setGeometry(300, 45, 50, 30)
        username.setStyleSheet(MyLabel)

        self.username_edit = QLineEdit(self)
        self.username_edit.setGeometry(350, 40, 190, 35)
        self.username_edit.setStyleSheet(MyLineEdit)

        password = QLabel('Pw:', self)
        password.setGeometry(300, 100, 50, 30)
        password.setStyleSheet(MyLabel)

        self.password_edit = QLineEdit(self)
        self.password_edit.setGeometry(350, 95, 190, 35)
        self.password_edit.setStyleSheet(MyLineEdit)

        status_label = QLabel('Result:', self)
        status_label.setGeometry(295, 150, 70, 30)
        status_label.setStyleSheet(UnderLabel)

        self.status = QLabel('Disconnect', self)
        self.status.setGeometry(360, 150, 190, 30)
        self.status.setStyleSheet(UnderLabel)

        connect_btn = QPushButton('Connect', self)
        connect_btn.setGeometry(320, 195, 100, 35)
        connect_btn.setStyleSheet(MyPushButton)

        test_btn = QPushButton('Test', self)
        test_btn.setGeometry(440, 195, 100, 35)
        test_btn.setStyleSheet(MyPushButton)

        # ------setTabOrder

        self.setTabOrder(self.username_edit, self.password_edit)
        self.setTabOrder(self.password_edit, connect_btn)
        self.setTabOrder(connect_btn, test_btn)

        # ------setEvent

        self.dataView.mouseDoubleClickEvent = self.set_text
        self.dataView.mousePressEvent = self.set_focus
        delete_btn.clicked.connect(self.removeItem)
        connect_btn.clicked.connect(self.connect_clicked)
        save_btn.clicked.connect(self.save_infomation)
        test_btn.clicked.connect(self.test_network)

        self.readItem(self.CONFIG_FILE)
        self.connect_clicked()
        self.show()

    def connect_clicked(self):
        result = connect_portal(self.username_edit.text(),
                                self.password_edit.text())
        self.status.setText(result)

    def save_infomation(self):
        if self.username_edit.text() and self.password_edit.text():
            try:
                selected = self.dataView.selectedIndexes()[0].row()
                self.modifyItem(selected)
            except IndexError:
                self.addItem(self.username_edit.text(),
                             self.password_edit.text())

    def test_network(self):
        result = test_public()
        self.status.setText(result)

    def set_text(self, event=None):
        try:
            self.username_edit.setText(
                self.dataView.selectedIndexes()[0].data())
            self.password_edit.setText(
                self.dataView.selectedIndexes()[1].data())
        except IndexError:
            pass

    def set_focus(self, event):
        index = self.dataView.indexAt(event.pos())
        if not index.isValid():
            self.dataView.clearSelection()
        else:
            self.dataView.setCurrentIndex(index)

    def readItem(self, filename):
        with open(filename, 'r') as f:
            for line in f.readlines():
                self.addItem(*(line.split()))

        self.dataView.setCurrentIndex(self.dataView.indexAt(QPoint(1, 1)))
        self.set_text()

    def addItem(self, username, password):
        self.model.insertRow(0)
        self.model.setData(self.model.index(0, self.Id), username)
        self.model.setData(self.model.index(0, self.Password), password)
        self.save_to_file()

    def modifyItem(self, row):
        self.model.setData(self.model.index(row, self.Id),
                           self.username_edit.text())
        self.model.setData(self.model.index(row, self.Password),
                           self.password_edit.text())
        self.save_to_file()

    def removeItem(self):
        try:
            self.model.removeRow(self.dataView.selectedIndexes()[0].row())
            self.save_to_file()
        except IndexError:
            pass

    def save_to_file(self):
        with open(self.CONFIG_FILE, 'w') as f:
            for x in range(self.model.rowCount()):
                for y in range(self.model.columnCount()):
                    f.write(self.model.data(self.model.index(x, y)) + " ")
                f.write("\n")

    def center(self):
        qr = self.frameGeometry()
        cp = QDesktopWidget().availableGeometry().center()
        qr.moveCenter(cp)
        self.move(qr.topLeft())
Exemplo n.º 21
0
class AddBookmarkDialog(QDialog, Ui_AddBookmarkDialog):
    """
    Class implementing a dialog to add a bookmark or a bookmark folder.
    """
    def __init__(self, parent=None, bookmarksManager=None):
        """
        Constructor
        
        @param parent reference to the parent widget (QWidget)
        @param bookmarksManager reference to the bookmarks manager
            object (BookmarksManager)
        """
        super(AddBookmarkDialog, self).__init__(parent)
        self.setupUi(self)
        
        self.__bookmarksManager = bookmarksManager
        self.__addedNode = None
        self.__addFolder = False
        
        if self.__bookmarksManager is None:
            import Helpviewer.HelpWindow
            self.__bookmarksManager = \
                Helpviewer.HelpWindow.HelpWindow.bookmarksManager()
        
        self.__proxyModel = AddBookmarkProxyModel(self)
        model = self.__bookmarksManager.bookmarksModel()
        self.__proxyModel.setSourceModel(model)
        
        self.__treeView = QTreeView(self)
        self.__treeView.setModel(self.__proxyModel)
        self.__treeView.expandAll()
        self.__treeView.header().setStretchLastSection(True)
        self.__treeView.header().hide()
        self.__treeView.setItemsExpandable(False)
        self.__treeView.setRootIsDecorated(False)
        self.__treeView.setIndentation(10)
        self.__treeView.show()
        
        self.locationCombo.setModel(self.__proxyModel)
        self.locationCombo.setView(self.__treeView)
        
        self.addressEdit.setInactiveText(self.tr("Url"))
        self.nameEdit.setInactiveText(self.tr("Title"))
        
        self.resize(self.sizeHint())
    
    def setUrl(self, url):
        """
        Public slot to set the URL of the new bookmark.
        
        @param url URL of the bookmark (string)
        """
        self.addressEdit.setText(url)
        self.resize(self.sizeHint())
    
    def url(self):
        """
        Public method to get the URL of the bookmark.
        
        @return URL of the bookmark (string)
        """
        return self.addressEdit.text()
    
    def setTitle(self, title):
        """
        Public method to set the title of the new bookmark.
        
        @param title title of the bookmark (string)
        """
        self.nameEdit.setText(title)
    
    def title(self):
        """
        Public method to get the title of the bookmark.
        
        @return title of the bookmark (string)
        """
        return self.nameEdit.text()
    
    def setDescription(self, description):
        """
        Public method to set the description of the new bookmark.
        
        @param description description of the bookamrk (string)
        """
        self.descriptionEdit.setPlainText(description)
    
    def description(self):
        """
        Public method to get the description of the bookmark.
        
        @return description of the bookamrk (string)
        """
        return self.descriptionEdit.toPlainText()
    
    def setCurrentIndex(self, idx):
        """
        Public method to set the current index.
        
        @param idx current index to be set (QModelIndex)
        """
        proxyIndex = self.__proxyModel.mapFromSource(idx)
        self.__treeView.setCurrentIndex(proxyIndex)
        self.locationCombo.setCurrentIndex(proxyIndex.row())
    
    def currentIndex(self):
        """
        Public method to get the current index.
        
        @return current index (QModelIndex)
        """
        idx = self.locationCombo.view().currentIndex()
        idx = self.__proxyModel.mapToSource(idx)
        return idx
    
    def setFolder(self, folder):
        """
        Public method to set the dialog to "Add Folder" mode.
        
        @param folder flag indicating "Add Folder" mode (boolean)
        """
        self.__addFolder = folder
        
        if folder:
            self.setWindowTitle(self.tr("Add Folder"))
            self.addressEdit.setVisible(False)
        else:
            self.setWindowTitle(self.tr("Add Bookmark"))
            self.addressEdit.setVisible(True)
        
        self.resize(self.sizeHint())
    
    def isFolder(self):
        """
        Public method to test, if the dialog is in "Add Folder" mode.
        
        @return flag indicating "Add Folder" mode (boolean)
        """
        return self.__addFolder
    
    def addedNode(self):
        """
        Public method to get a reference to the added bookmark node.
        
        @return reference to the added bookmark node (BookmarkNode)
        """
        return self.__addedNode
    
    def accept(self):
        """
        Public slot handling the acceptance of the dialog.
        """
        if (not self.__addFolder and not self.addressEdit.text()) or \
           not self.nameEdit.text():
            super(AddBookmarkDialog, self).accept()
            return
        
        from .BookmarkNode import BookmarkNode
        
        idx = self.currentIndex()
        if not idx.isValid():
            idx = self.__bookmarksManager.bookmarksModel().index(0, 0)
        parent = self.__bookmarksManager.bookmarksModel().node(idx)
        
        if self.__addFolder:
            type_ = BookmarkNode.Folder
        else:
            type_ = BookmarkNode.Bookmark
        bookmark = BookmarkNode(type_)
        bookmark.title = self.nameEdit.text()
        if not self.__addFolder:
            bookmark.url = self.addressEdit.text()
        bookmark.desc = self.descriptionEdit.toPlainText()
        
        self.__bookmarksManager.addBookmark(parent, bookmark)
        self.__addedNode = bookmark
        
        super(AddBookmarkDialog, self).accept()
Exemplo n.º 22
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())

        # Add auto-hide capability.
        self._waitForCtrlRelease = False
        core.actionManager().action("mNavigation/aNext").triggered.connect(
          self._setWaitForCtrlRelease)
        core.actionManager().action("mNavigation/aPrevious").triggered.connect(
          self._setWaitForCtrlRelease)
        QApplication.instance().installEventFilter(self)

    def terminate(self):
        """Explicitly called destructor
        """
        core.actionManager().removeAction("mView/aOpenedFiles")
        QApplication.instance().removeEventFilter(self)

    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)

    @pyqtSlot(Document, Document)
    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()

    @pyqtSlot(QItemSelection, QItemSelection)
    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()

    @pyqtSlot(QPoint)
    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))

    def _setWaitForCtrlRelease(self):
        # We can't see actual Ctrl+PgUp/PgDn keypresses, since these get eaten
        # by the QAction and don't even show up in the event filter below. We
        # want to avoid waiting for a Ctrl release if the menu item brought us
        # here. As a workaround, check that Ctrl is pressed. If so, it's
        # unlikely to be the menu item.
        if QApplication.instance().keyboardModifiers() & Qt.ControlModifier:
            self._waitForCtrlRelease = True
            self.show()
        else:
            # If this was a menu selection, then update the MRU list. We can't
            # do this now, since the current document hasn't been changed yet.
            QTimer.singleShot(0, self.model.sortDocuments)

    def eventFilter(self, obj, event):
        """An event filter that looks for ctrl key releases and focus out
           events."""
        # Wait for the user to release the Ctrl key.
        if ( self._waitForCtrlRelease and event.type() == QEvent.KeyRelease and
          event.key() == Qt.Key_Control and
          event.modifiers() == Qt.NoModifier):
            self.model.sortDocuments()
            self._waitForCtrlRelease = False
            if not self.isPinned():
                self.hide()
        # Look for a focus out event sent by the containing widget's focus
        # proxy.
        if event.type() == QEvent.FocusOut and obj == self.focusProxy():
            self.model.sortDocuments()
        return QObject.eventFilter(self, obj, event)
Exemplo n.º 23
0
class Widget(QWidget):
    def __init__(self, panel):
        super(Widget, self).__init__(panel)

        layout = QVBoxLayout()
        self.setLayout(layout)
        layout.setSpacing(0)

        self.searchEntry = SearchLineEdit()
        self.treeView = QTreeView(contextMenuPolicy=Qt.CustomContextMenu)
        self.textView = QTextBrowser()

        applyButton = QToolButton(autoRaise=True)
        editButton = QToolButton(autoRaise=True)
        addButton = QToolButton(autoRaise=True)
        self.menuButton = QPushButton(flat=True)
        menu = QMenu(self.menuButton)
        self.menuButton.setMenu(menu)

        splitter = QSplitter(Qt.Vertical)
        top = QHBoxLayout()
        layout.addLayout(top)
        splitter.addWidget(self.treeView)
        splitter.addWidget(self.textView)
        layout.addWidget(splitter)
        splitter.setSizes([200, 100])
        splitter.setCollapsible(0, False)

        top.addWidget(self.searchEntry)
        top.addWidget(applyButton)
        top.addSpacing(10)
        top.addWidget(addButton)
        top.addWidget(editButton)
        top.addWidget(self.menuButton)

        # action generator for actions added to search entry
        def act(slot, icon=None):
            a = QAction(self, triggered=slot)
            self.addAction(a)
            a.setShortcutContext(Qt.WidgetWithChildrenShortcut)
            icon and a.setIcon(icons.get(icon))
            return a

        # hide if ESC pressed in lineedit
        a = act(self.slotEscapePressed)
        a.setShortcut(QKeySequence(Qt.Key_Escape))

        # import action
        a = self.importAction = act(self.slotImport, 'document-open')
        menu.addAction(a)

        # export action
        a = self.exportAction = act(self.slotExport, 'document-save-as')
        menu.addAction(a)

        # apply button
        a = self.applyAction = act(self.slotApply, 'edit-paste')
        applyButton.setDefaultAction(a)
        menu.addSeparator()
        menu.addAction(a)

        # add button
        a = self.addAction_ = act(self.slotAdd, 'list-add')
        a.setShortcut(QKeySequence(Qt.Key_Insert))
        addButton.setDefaultAction(a)
        menu.addSeparator()
        menu.addAction(a)

        # edit button
        a = self.editAction = act(self.slotEdit, 'document-edit')
        a.setShortcut(QKeySequence(Qt.Key_F2))
        editButton.setDefaultAction(a)
        menu.addAction(a)

        # set shortcut action
        a = self.shortcutAction = act(
            self.slotShortcut, 'preferences-desktop-keyboard-shortcuts')
        menu.addAction(a)

        # delete action
        a = self.deleteAction = act(self.slotDelete, 'list-remove')
        a.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_Delete))
        menu.addAction(a)

        # restore action
        a = self.restoreAction = act(self.slotRestore)
        menu.addSeparator()
        menu.addAction(a)

        # help button
        a = self.helpAction = act(self.slotHelp, 'help-contents')
        menu.addSeparator()
        menu.addAction(a)

        self.treeView.setSelectionBehavior(QTreeView.SelectRows)
        self.treeView.setSelectionMode(QTreeView.ExtendedSelection)
        self.treeView.setRootIsDecorated(False)
        self.treeView.setAllColumnsShowFocus(True)
        self.treeView.setModel(model.model())
        self.treeView.setCurrentIndex(QModelIndex())

        # signals
        self.searchEntry.returnPressed.connect(self.slotReturnPressed)
        self.searchEntry.textChanged.connect(self.updateFilter)
        self.treeView.doubleClicked.connect(self.slotDoubleClicked)
        self.treeView.customContextMenuRequested.connect(self.showContextMenu)
        self.treeView.selectionModel().currentChanged.connect(self.updateText)
        self.treeView.model().dataChanged.connect(self.updateFilter)

        # highlight text
        self.highlighter = highlight.Highlighter(self.textView.document())

        # complete on snippet variables
        self.searchEntry.setCompleter(
            QCompleter([
                ':icon', ':indent', ':menu', ':name', ':python', ':selection',
                ':set', ':symbol', ':template', ':template-run'
            ], self.searchEntry))
        self.readSettings()
        app.settingsChanged.connect(self.readSettings)
        app.translateUI(self)
        self.updateColumnSizes()
        self.setAcceptDrops(True)

    def dropEvent(self, ev):
        if not ev.source() and ev.mimeData().hasUrls():
            filename = ev.mimeData().urls()[0].toLocalFile()
            if filename:
                ev.accept()
                from . import import_export
                import_export.load(filename, self)

    def dragEnterEvent(self, ev):
        if not ev.source() and ev.mimeData().hasUrls():
            ev.accept()

    def translateUI(self):
        try:
            self.searchEntry.setPlaceholderText(_("Search..."))
        except AttributeError:
            pass  # not in Qt 4.6
        shortcut = lambda a: a.shortcut().toString(QKeySequence.NativeText)
        self.menuButton.setText(_("&Menu"))
        self.addAction_.setText(_("&Add..."))
        self.addAction_.setToolTip(
            _("Add a new snippet. ({key})").format(
                key=shortcut(self.addAction_)))
        self.editAction.setText(_("&Edit..."))
        self.editAction.setToolTip(
            _("Edit the current snippet. ({key})").format(
                key=shortcut(self.editAction)))
        self.shortcutAction.setText(_("Configure Keyboard &Shortcut..."))
        self.deleteAction.setText(_("&Remove"))
        self.deleteAction.setToolTip(_("Remove the selected snippets."))
        self.applyAction.setText(_("A&pply"))
        self.applyAction.setToolTip(_("Apply the current snippet."))
        self.importAction.setText(_("&Import..."))
        self.importAction.setToolTip(_("Import snippets from a file."))
        self.exportAction.setText(_("E&xport..."))
        self.exportAction.setToolTip(_("Export snippets to a file."))
        self.restoreAction.setText(_("Restore &Built-in Snippets..."))
        self.restoreAction.setToolTip(
            _("Restore deleted or changed built-in snippets."))
        self.helpAction.setText(_("&Help"))
        self.searchEntry.setToolTip(
            _("Enter text to search in the snippets list.\n"
              "See \"What's This\" for more information."))
        self.searchEntry.setWhatsThis(''.join(
            map("<p>{0}</p>\n".format, (
                _("Enter text to search in the snippets list, and "
                  "press Enter to apply the currently selected snippet."),
                _("If the search text fully matches the value of the '{name}' variable "
                  "of a snippet, that snippet is selected.").format(
                      name="name"),
                _("If the search text starts with a colon ':', the rest of the "
                  "search text filters snippets that define the given variable. "
                  "After a space a value can also be entered, snippets will then "
                  "match if the value of the given variable contains the text after "
                  "the space."),
                _("E.g. entering {menu} will show all snippets that are displayed "
                  "in the insert menu.").format(menu="<code>:menu</code>"),
            ))))

    def sizeHint(self):
        return self.parent().mainwindow().size() / 4

    def readSettings(self):
        data = textformats.formatData('editor')
        self.textView.setFont(data.font)
        self.textView.setPalette(data.palette())

    def showContextMenu(self, pos):
        """Called when the user right-clicks the tree view."""
        self.menuButton.menu().popup(self.treeView.viewport().mapToGlobal(pos))

    def slotReturnPressed(self):
        """Called when the user presses Return in the search entry. Applies current snippet."""
        name = self.currentSnippet()
        if name:
            view = self.parent().mainwindow().currentView()
            insert.insert(name, view)
            self.parent().hide()  # make configurable?
            view.setFocus()

    def slotEscapePressed(self):
        """Called when the user presses ESC in the search entry. Hides the panel."""
        self.parent().hide()
        self.parent().mainwindow().currentView().setFocus()

    def slotDoubleClicked(self, index):
        name = self.treeView.model().name(index)
        view = self.parent().mainwindow().currentView()
        insert.insert(name, view)

    def slotAdd(self):
        """Called when the user wants to add a new snippet."""
        edit.Edit(self, None)

    def slotEdit(self):
        """Called when the user wants to edit a snippet."""
        name = self.currentSnippet()
        if name:
            edit.Edit(self, name)

    def slotShortcut(self):
        """Called when the user selects the Configure Shortcut action."""
        from widgets import shortcuteditdialog
        name = self.currentSnippet()
        if name:
            collection = self.parent().snippetActions
            action = actions.action(name, None, collection)
            default = collection.defaults().get(name)
            mgr = actioncollectionmanager.manager(self.parent().mainwindow())
            cb = mgr.findShortcutConflict
            dlg = shortcuteditdialog.ShortcutEditDialog(
                self, cb, (collection, name))

            if dlg.editAction(action, default):
                mgr.removeShortcuts(action.shortcuts())
                collection.setShortcuts(name, action.shortcuts())
                self.treeView.update()

    def slotDelete(self):
        """Called when the user wants to delete the selected rows."""
        rows = sorted(set(i.row() for i in self.treeView.selectedIndexes()),
                      reverse=True)
        if rows:
            for row in rows:
                name = self.treeView.model().names()[row]
                self.parent().snippetActions.setShortcuts(name, [])
                self.treeView.model().removeRow(row)
            self.updateFilter()

    def slotApply(self):
        """Called when the user clicks the apply button. Applies current snippet."""
        name = self.currentSnippet()
        if name:
            view = self.parent().mainwindow().currentView()
            insert.insert(name, view)

    def slotImport(self):
        """Called when the user activates the import action."""
        filetypes = "{0} (*.xml);;{1} (*)".format(_("XML Files"),
                                                  _("All Files"))
        caption = app.caption(_("dialog title", "Import Snippets"))
        filename = None
        filename = QFileDialog.getOpenFileName(self, caption, filename,
                                               filetypes)[0]
        if filename:
            from . import import_export
            import_export.load(filename, self)

    def slotExport(self):
        """Called when the user activates the export action."""
        allrows = [
            row for row in range(model.model().rowCount())
            if not self.treeView.isRowHidden(row, QModelIndex())
        ]
        selectedrows = [
            i.row() for i in self.treeView.selectedIndexes()
            if i.column() == 0 and i.row() in allrows
        ]
        names = self.treeView.model().names()
        names = [names[row] for row in selectedrows or allrows]

        filetypes = "{0} (*.xml);;{1} (*)".format(_("XML Files"),
                                                  _("All Files"))
        n = len(names)
        caption = app.caption(
            _("dialog title", "Export {num} Snippet", "Export {num} Snippets",
              n).format(num=n))
        filename = QFileDialog.getSaveFileName(self, caption, None,
                                               filetypes)[0]
        if filename:
            from . import import_export
            try:
                import_export.save(names, filename)
            except (IOError, OSError) as e:
                QMessageBox.critical(
                    self, _("Error"),
                    _("Can't write to destination:\n\n{url}\n\n{error}").
                    format(url=filename, error=e.strerror))

    def slotRestore(self):
        """Called when the user activates the Restore action."""
        from . import restore
        dlg = restore.RestoreDialog(self)
        dlg.setWindowModality(Qt.WindowModal)
        dlg.populate()
        dlg.show()
        dlg.finished.connect(dlg.deleteLater)

    def slotHelp(self):
        """Called when the user clicks the small help button."""
        userguide.show("snippets")

    def currentSnippet(self):
        """Returns the name of the current snippet if it is visible."""
        row = self.treeView.currentIndex().row()
        if row != -1 and not self.treeView.isRowHidden(row, QModelIndex()):
            return self.treeView.model().names()[row]

    def updateFilter(self):
        """Called when the text in the entry changes, updates search results."""
        text = self.searchEntry.text()
        ltext = text.lower()
        filterVars = text.startswith(':')
        if filterVars:
            try:
                fvar, fval = text[1:].split(None, 1)
                fhide = lambda v: v.get(fvar) in (True, None
                                                  ) or fval not in v.get(fvar)
            except ValueError:
                fvar = text[1:].strip()
                fhide = lambda v: not v.get(fvar)
        for row in range(self.treeView.model().rowCount()):
            name = self.treeView.model().names()[row]
            nameid = snippets.get(name).variables.get('name', '')
            if filterVars:
                hide = fhide(snippets.get(name).variables)
            elif nameid == text:
                i = self.treeView.model().createIndex(row, 0)
                self.treeView.selectionModel().setCurrentIndex(
                    i, QItemSelectionModel.SelectCurrent
                    | QItemSelectionModel.Rows)
                hide = False
            elif nameid.lower().startswith(ltext):
                hide = False
            elif ltext in snippets.title(name).lower():
                hide = False
            else:
                hide = True
            self.treeView.setRowHidden(row, QModelIndex(), hide)
        self.updateText()

    def updateText(self):
        """Called when the current snippet changes."""
        name = self.currentSnippet()
        self.textView.clear()
        if name:
            s = snippets.get(name)
            self.highlighter.setPython('python' in s.variables)
            self.textView.setPlainText(s.text)

    def updateColumnSizes(self):
        self.treeView.resizeColumnToContents(0)
        self.treeView.resizeColumnToContents(1)
Exemplo n.º 24
0
class MainWindow(QMainWindow):
    def __init__(self):
        QMainWindow.__init__(self)

        self.setMinimumSize(QSize(480, 80))
        self.setWindowTitle("PyQtSample")
        central_widget = QWidget(self)
        self.setCentralWidget(central_widget)

        hbox_layout = QHBoxLayout(self)
        central_widget.setLayout(hbox_layout)

        buttons_background = QWidget(self)
        buttons_layout = QHBoxLayout()
        buttons_background.setLayout(buttons_layout)

        self.add_button = QPushButton("Add")
        buttons_layout.addWidget(self.add_button)

        self.remove_button = QPushButton("Remove")
        buttons_layout.addWidget(self.remove_button)

        tree_layout = QVBoxLayout(self)
        self.tree_view = QTreeView()
        self.tree_view.header().hide()
        self.tree_view.setMaximumWidth(300)
        self.tree_model = ObjectsModel.TreeModel()
        self.tree_view.setModel(self.tree_model)
        tree_layout.addWidget(buttons_background)
        tree_layout.addWidget(self.tree_view)

        hbox_layout.addLayout(tree_layout)

        self.graphics_view = QGraphicsView()
        self.scene = QGraphicsScene()
        self.graphics_view.setScene(self.scene)
        hbox_layout.addWidget(self.graphics_view)

        self.properties_view = QTableView()
        self.properties_view.setMaximumWidth(300)
        self.properties_model = PropertiesModel.TableModel()
        self.properties_view.setModel(self.properties_model)
        hbox_layout.addWidget(self.properties_view)

        self.init_menu()
        self.test()
        self.connectSignals()

    def connectSignals(self):
        self.add_button.clicked.connect(self.onAddClicked)
        self.remove_button.clicked.connect(self.onRemoveClicked)
        self.tree_view.clicked.connect(self.onClicked)
        self.properties_model.dataChanged.connect(self.onPropertyChanged)

    def onPropertyChanged(self):
        self.rebuildModel()

    def rebuildModel(self):
        #save index hierarhy
        indexes = []
        tmp_index = self.tree_view.currentIndex()
        indexes.append(tmp_index)
        while tmp_index.parent().isValid():
            indexes.append(tmp_index.parent())
            tmp_index = tmp_index.parent()

        self.tree_model.initRoot(self.items)
        self.tree_view.expandAll()

        if len(indexes) == 0:
            self.onClicked(self.tree_model.index(0, 0))
        else:
            last_index = indexes.pop(-1)
            index = self.tree_model.index(last_index.row(),
                                          last_index.column())
            #restore index hierarchy
            while len(indexes) > 0:
                last_index = indexes.pop(-1)
                index = self.tree_model.index(last_index.row(),
                                              last_index.column(), index)

            if index.isValid():
                self.onClicked(index)
            else:
                self.onClicked(self.tree_model.index(0, 0))

    def init_menu(self):
        exit_action = QAction("&Exit", self)
        exit_action.setShortcut('Ctrl+Q')
        exit_action.triggered.connect(qApp.quit)
        file_menu = self.menuBar().addMenu("&File")
        file_menu.addAction(QAction("Open", self))
        file_menu.addAction(QAction("Save", self))
        file_menu.addAction(QAction("SaveAs", self))
        file_menu.addSeparator()
        file_menu.addAction(exit_action)

    def appendObjectOnScene(self, object=DataStructures.Object):
        if object.rect.isValid():
            self.scene.addRect(object.rect, object.color)
        for child in object.childrens:
            self.appendObjectOnScene(child)

    def onClicked(self, index):
        object = index.data(Qt.UserRole + 1)

        self.properties_model.initProperties(object)

        self.scene.clear()
        self.appendObjectOnScene(object)
        self.tree_view.setCurrentIndex(index)

    def onAddClicked(self):
        index = self.tree_view.currentIndex()
        print(index)
        object = index.data(Qt.UserRole + 1)
        print(object.description())
        object.add_children(DataStructures.Object("New item"))
        self.rebuildModel()

    def onRemoveClicked(self):
        index = self.tree_view.currentIndex()
        object = index.data(Qt.UserRole + 1)
        if not object.parent:
            self.items.remove(object)
        else:
            object.parent.childrens.remove(object)

        self.rebuildModel()

    def test(self):
        self.items = []
        static = DataStructures.Object(
            "Static", DataStructures.createRect(0, 0, 800, 200))
        static.add_children(DataStructures.Object("child_1"))
        static.add_children(DataStructures.Object("child_2"))
        static.add_children(DataStructures.Object("child_3"))
        static.color = QColor(200, 0, 0).name()
        static.childrens[0].add_children(
            DataStructures.Object("child_1.1",
                                  DataStructures.createRect(40, 40, 80, 40)))

        self.items.append(static)

        dynamic = DataStructures.Object(
            "Dynamic", DataStructures.createRect(0, 0, 200, 800))
        dynamic.add_children(DataStructures.Object("child_1"))
        dynamic.add_children(DataStructures.Object("child_2"))
        dynamic.add_children(DataStructures.Object("child_3"))
        dynamic.childrens[2].add_children(DataStructures.Object("child_2.1"))
        dynamic.color = QColor(0, 0, 200).name()
        self.items.append(dynamic)

        self.rebuildModel()
Exemplo n.º 25
0
class FilenamePrompt(_BasePrompt):

    """A prompt for a filename."""

    def __init__(self, question, parent=None):
        super().__init__(question, parent)
        self._init_texts(question)
        self._init_key_label()

        self._lineedit = LineEdit(self)
        if question.default:
            self._lineedit.setText(question.default)
        self._lineedit.textEdited.connect(self._set_fileview_root)
        self._vbox.addWidget(self._lineedit)

        self.setFocusProxy(self._lineedit)

        self._init_fileview()
        self._set_fileview_root(question.default)

        if config.val.prompt.filebrowser:
            self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)

        self._to_complete = ''

    @pyqtSlot(str)
    def _set_fileview_root(self, path, *, tabbed=False):
        """Set the root path for the file display."""
        separators = os.sep
        if os.altsep is not None:
            separators += os.altsep

        dirname = os.path.dirname(path)
        basename = os.path.basename(path)
        if not tabbed:
            self._to_complete = ''

        try:
            if not path:
                pass
            elif path in separators and os.path.isdir(path):
                # Input "/" -> don't strip anything
                pass
            elif path[-1] in separators and os.path.isdir(path):
                # Input like /foo/bar/ -> show /foo/bar/ contents
                path = path.rstrip(separators)
            elif os.path.isdir(dirname) and not tabbed:
                # Input like /foo/ba -> show /foo contents
                path = dirname
                self._to_complete = basename
            else:
                return
        except OSError:
            log.prompt.exception("Failed to get directory information")
            return

        root = self._file_model.setRootPath(path)
        self._file_view.setRootIndex(root)

    @pyqtSlot(QModelIndex)
    def _insert_path(self, index, *, clicked=True):
        """Handle an element selection.

        Args:
            index: The QModelIndex of the selected element.
            clicked: Whether the element was clicked.
        """
        if index == QModelIndex():
            path = os.path.join(self._file_model.rootPath(), self._to_complete)
        else:
            path = os.path.normpath(self._file_model.filePath(index))

        if clicked:
            path += os.sep
        else:
            # On Windows, when we have C:\foo and tab over .., we get C:\
            path = path.rstrip(os.sep)

        log.prompt.debug('Inserting path {}'.format(path))
        self._lineedit.setText(path)
        self._lineedit.setFocus()
        self._set_fileview_root(path, tabbed=True)
        if clicked:
            # Avoid having a ..-subtree highlighted
            self._file_view.setCurrentIndex(QModelIndex())

    def _init_fileview(self):
        self._file_view = QTreeView(self)
        self._file_model = QFileSystemModel(self)
        self._file_view.setModel(self._file_model)
        self._file_view.clicked.connect(self._insert_path)

        if config.val.prompt.filebrowser:
            self._vbox.addWidget(self._file_view)
        else:
            self._file_view.hide()

        # Only show name
        self._file_view.setHeaderHidden(True)
        for col in range(1, 4):
            self._file_view.setColumnHidden(col, True)
        # Nothing selected initially
        self._file_view.setCurrentIndex(QModelIndex())
        # The model needs to be sorted so we get the correct first/last index
        self._file_model.directoryLoaded.connect(
            lambda: self._file_model.sort(0))

    def accept(self, value=None, save=False):
        self._check_save_support(save)
        text = value if value is not None else self._lineedit.text()
        text = downloads.transform_path(text)
        if text is None:
            message.error("Invalid filename")
            return False
        self.question.answer = text
        return True

    def item_focus(self, which):
        # This duplicates some completion code, but I don't see a nicer way...
        assert which in ['prev', 'next'], which
        selmodel = self._file_view.selectionModel()

        parent = self._file_view.rootIndex()
        first_index = self._file_model.index(0, 0, parent)
        row = self._file_model.rowCount(parent) - 1
        last_index = self._file_model.index(row, 0, parent)

        if not first_index.isValid():
            # No entries
            return

        assert last_index.isValid()

        idx = selmodel.currentIndex()

        if not idx.isValid():
            # No item selected yet
            idx = last_index if which == 'prev' else first_index
        elif which == 'prev':
            idx = self._file_view.indexAbove(idx)
        else:
            assert which == 'next', which
            idx = self._file_view.indexBelow(idx)

        # wrap around if we arrived at beginning/end
        if not idx.isValid():
            idx = last_index if which == 'prev' else first_index

        idx = self._do_completion(idx, which)

        selmodel.setCurrentIndex(
            idx,
            QItemSelectionModel.ClearAndSelect |  # type: ignore[arg-type]
            QItemSelectionModel.Rows)
        self._insert_path(idx, clicked=False)

    def _do_completion(self, idx, which):
        filename = self._file_model.fileName(idx)
        while not filename.startswith(self._to_complete) and idx.isValid():
            if which == 'prev':
                idx = self._file_view.indexAbove(idx)
            else:
                assert which == 'next', which
                idx = self._file_view.indexBelow(idx)
            filename = self._file_model.fileName(idx)

        return idx

    def _allowed_commands(self):
        return [('prompt-accept', 'Accept'), ('leave-mode', 'Abort')]
Exemplo n.º 26
0
class FileBrowserWidget(QWidget):
    on_open = pyqtSignal(str)

    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.model = QFileSystemModel()
        self.rootFolder = ''
        self.model.setRootPath(self.rootFolder)
        self.tree = QTreeView()
        self.tree.setModel(self.model)

        self.tree.setAnimated(False)
        self.tree.setIndentation(20)
        self.tree.setSortingEnabled(True)
        self.tree.sortByColumn(0, 0)
        self.tree.setColumnWidth(0, 200)
        self.tree.setDragEnabled(True)

        self.tree.setWindowTitle("Dir View")
        self.tree.resize(640, 480)
        self.tree.doubleClicked.connect(self.onDblClick)
        self.tree.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
        self.tree.customContextMenuRequested.connect(
            self.onCustomContextMenuRequested)

        windowLayout = QVBoxLayout()
        windowLayout.addWidget(self.tree)
        windowLayout.setContentsMargins(0, 0, 0, 0)
        self.setLayout(windowLayout)

    def onCustomContextMenuRequested(self, point):
        index = self.tree.indexAt(point)
        selectedFile = None
        selectedFolder = None
        ctx = QMenu("Context menu", self)
        if index.isValid():
            file = self.model.fileInfo(index)
            selectedFile = file.absoluteFilePath()
            selectedFolder = selectedFile if file.isDir(
            ) else file.absolutePath()
            if file.isDir():
                ctx.addAction(
                    "Open in file manager", lambda: QDesktopServices.openUrl(
                        QUrl.fromLocalFile(selectedFile)))
            if not file.isDir():
                for wndTyp, meta in WindowTypes.types:
                    text = 'Open with ' + meta.get('displayName', meta['name'])
                    print(wndTyp, meta)
                    ctx.addAction(
                        QAction(text,
                                self,
                                statusTip=text,
                                triggered=lambda dummy, meta=meta: navigate(
                                    "WINDOW", "Type=" + meta['name'],
                                    "FileName=" + selectedFile)))
                ctx.addSeparator()

        ctx.addAction("Set root folder ...",
                      lambda: self.selectRootFolder(preselect=selectedFolder))
        ctx.exec(self.tree.viewport().mapToGlobal(point))

    def selectRootFolder(self, preselect=None):
        if preselect == None: preselect = self.rootFolder
        dir = QFileDialog.getExistingDirectory(self, "Set root folder",
                                               preselect)
        if dir != None:
            self.setRoot(dir)

    def setRoot(self, dir):
        self.rootFolder = dir
        self.model.setRootPath(dir)
        self.tree.setRootIndex(self.model.index(dir))

    def onDblClick(self, index):
        if index.isValid():
            file = self.model.fileInfo(index)
            if not file.isDir():
                navigate("OPEN", "FileName=" + file.absoluteFilePath())

    def saveState(self):
        if self.tree.currentIndex().isValid():
            info = self.model.fileInfo(self.tree.currentIndex())
            return {"sel": info.absoluteFilePath(), "root": self.rootFolder}

    def restoreState(self, state):
        try:
            self.setRoot(state["root"])
        except:
            pass
        try:
            idx = self.model.index(state["sel"])
            if idx.isValid():
                self.tree.expand(idx)
                self.tree.setCurrentIndex(idx)
                self.tree.scrollTo(idx, QAbstractItemView.PositionAtCenter)
        except:
            pass
Exemplo n.º 27
-1
class VisualStates(QMainWindow):
    def __init__(self, parent=None):
        super(QMainWindow, self).__init__()

        self.setWindowTitle("VisualStates")
        self.configDialog = None

        # root state
        self.rootState = State(0, "root", True)
        self.activeState = self.rootState

        # create status bar
        self.statusBar()

        self.createMenu()
        self.createTreeView()
        self.createStateCanvas()

        self.setGeometry(0, 0, 800, 600)
        self.show()

        self.fileManager = FileManager()

        self.libraries = []
        self.config = None
        self.interfaceHeaderMap = Interfaces.getInterfaces()

    def createMenu(self):
        # create actions
        newAction = QAction('&New', self)
        newAction.setShortcut('Ctrl+N')
        newAction.setStatusTip('Create New Visual States')
        newAction.triggered.connect(self.newAction)

        openAction = QAction('&Open', self)
        openAction.setShortcut('Ctrl+O')
        openAction.setStatusTip('Open Visual States')
        openAction.triggered.connect(self.openAction)

        saveAction = QAction('&Save', self)
        saveAction.setShortcut('Ctrl+S')
        saveAction.setStatusTip('Save Visual States')
        saveAction.triggered.connect(self.saveAction)

        saveAsAction = QAction('&Save As', self)
        saveAsAction.setShortcut('Ctrl+S')
        saveAsAction.setStatusTip('Save Visual States as New One')
        saveAsAction.triggered.connect(self.saveAsAction)

        quitAction = QAction('&Quit', self)
        quitAction.setShortcut('Ctrl+Q')
        quitAction.setStatusTip('Quit Visual States')
        quitAction.triggered.connect(self.quitAction)

        # figures menu
        stateAction = QAction('&State', self)
        stateAction.setStatusTip('Create a state')
        stateAction.triggered.connect(self.stateAction)

        transitionAction = QAction('&Transition', self)
        transitionAction.setStatusTip('Create a transition')
        transitionAction.triggered.connect(self.transitionAction)

        # data menu
        timerAction = QAction('&Timer', self)
        timerAction.setShortcut('Ctrl+M')
        timerAction.setStatusTip('Set timing of states')
        timerAction.triggered.connect(self.timerAction)

        variablesAction = QAction('&Variables', self)
        variablesAction.setShortcut('Ctrl+V')
        variablesAction.setStatusTip('Define state variables')
        variablesAction.triggered.connect(self.variablesAction)

        functionsAction = QAction('&Functions', self)
        functionsAction.setShortcut('Ctrl+F')
        functionsAction.setStatusTip('Define functions')
        functionsAction.triggered.connect(self.functionsAction)

        # actions menu
        librariesAction = QAction('&Libraries', self)
        librariesAction.setShortcut('Ctrl+L')
        librariesAction.setStatusTip('Add additional libraries')
        librariesAction.triggered.connect(self.librariesAction)

        configFileAction = QAction('&Config File', self)
        configFileAction.setShortcut('Ctrl+C')
        configFileAction.setStatusTip('Edit configuration file')
        configFileAction.triggered.connect(self.configFileAction)

        generateCppAction = QAction('&Generate C++', self)
        generateCppAction.setShortcut('Ctrl+G')
        generateCppAction.setStatusTip('Generate C++ code')
        generateCppAction.triggered.connect(self.generateCppAction)

        compileCppAction = QAction('&Compile C++', self)
        compileCppAction.setShortcut('Ctrl+P')
        compileCppAction.setStatusTip('Compile generated C++ code')
        compileCppAction.triggered.connect(self.compileCppAction)

        generatePythonAction = QAction('&Generate Python', self)
        generatePythonAction.setShortcut('Ctrl+Y')
        generatePythonAction.setStatusTip('Generate Python code')
        generatePythonAction.triggered.connect(self.generatePythonAction)

        # help menu
        aboutAction = QAction('&About', self)
        aboutAction.setShortcut('F1')
        aboutAction.setStatusTip('Information about VisualStates')
        aboutAction.triggered.connect(self.aboutAction)

        # create main menu
        menubar = self.menuBar()
        archieveMenu = menubar.addMenu('&File')
        archieveMenu.addAction(newAction)
        archieveMenu.addAction(openAction)
        archieveMenu.addAction(saveAction)
        archieveMenu.addAction(saveAsAction)
        archieveMenu.addAction(quitAction)

        figuresMenu = menubar.addMenu('&Figures')
        figuresMenu.addAction(stateAction)
        figuresMenu.addAction(transitionAction)

        dataMenu = menubar.addMenu('&Data')
        dataMenu.addAction(timerAction)
        dataMenu.addAction(variablesAction)
        dataMenu.addAction(functionsAction)

        actionsMenu = menubar.addMenu('&Actions')
        actionsMenu.addAction(librariesAction)
        actionsMenu.addAction(configFileAction)
        actionsMenu.addAction(generateCppAction)
        actionsMenu.addAction(compileCppAction)
        actionsMenu.addAction(generatePythonAction)

        helpMenu = menubar.addMenu('&Help')
        helpMenu.addAction(aboutAction)


    def newAction(self):
        self.automataScene.clearScene()
        # create new root state
        self.rootState = State(0, 'root', True)
        self.automataScene.setActiveState(self.rootState)
        self.automataScene.resetIndexes()

    def openAction(self):
        fileDialog = QFileDialog(self)
        fileDialog.setWindowTitle("Open VisualStates File")
        fileDialog.setViewMode(QFileDialog.Detail)
        fileDialog.setNameFilters(['VisualStates File (*.xml)'])
        fileDialog.setDefaultSuffix('.xml')
        fileDialog.setAcceptMode(QFileDialog.AcceptOpen)
        if fileDialog.exec_():
            (self.rootState, self.config, self.libraries) = self.fileManager.open(fileDialog.selectedFiles()[0])
            self.treeModel.removeAll()
            self.treeModel.loadFromRoot(self.rootState)
            # set the active state as the loaded state
            self.automataScene.setActiveState(self.rootState)
            self.automataScene.setLastIndexes(self.rootState)
            # print(str(self.config))
        # else:
        #     print('open is canceled')



    def saveAction(self):
        if len(self.fileManager.getFileName()) == 0:
            self.saveAsAction()
        else:
            self.fileManager.save(self.rootState, self.config, self.libraries)

    def saveAsAction(self):
        fileDialog = QFileDialog(self)
        fileDialog.setWindowTitle("Save VisualStates Project")
        fileDialog.setViewMode(QFileDialog.Detail)
        fileDialog.setNameFilters(['VisualStates File (*.xml)'])
        fileDialog.setAcceptMode(QFileDialog.AcceptSave)
        if fileDialog.exec_():
            self.fileManager.setFullPath(fileDialog.selectedFiles()[0])
            self.fileManager.save(self.rootState, self.config, self.libraries)
        # else:
        #     print('file dialog canceled')


    def quitAction(self):
        # print('Quit')
        self.close()

    def stateAction(self):
        self.automataScene.setOperationType(OpType.ADDSTATE)

    def transitionAction(self):
        self.automataScene.setOperationType(OpType.ADDTRANSITION)

    def timerAction(self):
        if self.activeState is not None:
            timerDialog = TimerDialog('Time Step Duration', str(self.activeState.getTimeStep()))
            timerDialog.timeChanged.connect(self.timeStepDurationChanged)
            timerDialog.exec_()

    def variablesAction(self):
        if self.activeState is not None:
            variablesDialog = CodeDialog('Variables', self.activeState.getVariables())
            variablesDialog.codeChanged.connect(self.variablesChanged)
            variablesDialog.exec_()
        else:
            self.showWarning('Choose a state', 'You can create variables only for a selected state')

    def functionsAction(self):
        if self.activeState is not None:
            functionsDialog = CodeDialog('Functions', self.activeState.getFunctions())
            functionsDialog.codeChanged.connect(self.functionsChanged)
            functionsDialog.exec_()
        else:
            self.showWarning('Choose a state', 'You can create functions only for a selected state')

    def librariesAction(self):
        librariesDialog = LibrariesDialog('Libraries', self.libraries)
        librariesDialog.librariesChanged.connect(self.librariesChanged)
        librariesDialog.exec_()

    def configFileAction(self):
        self.configDialog = ConfigDialog('Config', self.config)
        self.configDialog.configChanged.connect(self.configChanged)
        self.configDialog.exec_()


    def showWarning(self, title, msg):
        QMessageBox.warning(self, title, msg)

    def showInfo(self, title, msg):
        QMessageBox.information(self, title, msg)

    def generateCppAction(self):
        stateList = []
        if self.fileManager.hasFile():
            self.getStateList(self.rootState, stateList)
            if self.config.type == ROS:
                generator = CppRosGenerator(self.libraries, self.config, self.interfaceHeaderMap, stateList)
            elif self.config.type == JDEROBOTCOMM:
                generator = CppGenerator(self.libraries, self.config, self.interfaceHeaderMap, stateList)

            generator.generate(self.fileManager.getPath(), self.fileManager.getFileName())
            self.showInfo('C++ Code Generation', 'C++ code generation is successful.')
        else:
            self.showWarning('C++ Generation', 'Please save the project before code generation.')


    def compileCppAction(self):
        # print('compile cpp action')
        pass

    def generatePythonAction(self):
        stateList = []
        if self.fileManager.hasFile():
            self.getStateList(self.rootState, stateList)
            if self.config.type == ROS:
                generator = PythonRosGenerator(self.libraries, self.config, stateList)
            elif self.config.type == JDEROBOTCOMM:
                generator = PythonGenerator(self.libraries, self.config, self.interfaceHeaderMap, stateList)
            generator.generate(self.fileManager.getPath(), self.fileManager.getFileName())
            self.showInfo('Python Code Generation', 'Python code generation is successful.')
        else:
            self.showWarning('Python Generation', 'Please save the project before code generation.')

    def aboutAction(self):
        pass
        # print('about action')

    def createTreeView(self):
        dockWidget = QDockWidget()
        dockWidget.setAllowedAreas(Qt.LeftDockWidgetArea)
        dockWidget.setFeatures(QDockWidget.NoDockWidgetFeatures)
        dockWidget.setTitleBarWidget(QWidget())
        self.treeView = QTreeView()
        self.treeView.clicked.connect(self.treeItemClicked)
        self.treeModel = TreeModel()
        self.treeView.setModel(self.treeModel)

        self.logo = QLabel()
        logoPixmap = QPixmap(CMAKE_INSTALL_PREFIX + '/share/jderobot/resources/jderobot.png')
        self.logo.setPixmap(logoPixmap)

        self.upButton = QPushButton()
        self.upButton.setText('Up')
        self.upButton.clicked.connect(self.upButtonClicked)

        leftContainer = QWidget()
        leftLayout = QVBoxLayout()
        leftLayout.addWidget(self.treeView)
        leftLayout.addWidget(self.upButton)
        leftLayout.addWidget(self.logo)
        leftContainer.setLayout(leftLayout)

        dockWidget.setWidget(leftContainer)
        self.addDockWidget(Qt.LeftDockWidgetArea, dockWidget)

    def createStateCanvas(self):
        self.stateCanvas = QGraphicsView()
        self.automataScene = AutomataScene()
        self.automataScene.setSceneRect(0, 0, 2000, 2000)
        self.automataScene.activeStateChanged.connect(self.activeStateChanged)
        self.automataScene.stateInserted.connect(self.stateInserted)
        self.automataScene.stateRemoved.connect(self.stateRemoved)
        self.automataScene.transitionInserted.connect(self.transitionInserted)
        self.automataScene.stateNameChangedSignal.connect(self.stateNameChanged)
        self.automataScene.setActiveState(self.rootState)

        self.setCentralWidget(self.stateCanvas)
        self.stateCanvas.setScene(self.automataScene)
        self.stateCanvas.setRenderHint(QPainter.Antialiasing)
        self.stateCanvas.setAcceptDrops(True)

    def stateInserted(self, state):
        if self.activeState != self.rootState:
            parent = self.treeModel.getByDataId(self.activeState.id)
            self.treeModel.insertState(state, QColor(Qt.white), parent)
        else:
            self.treeModel.insertState(state, QColor(Qt.white))

    def stateRemoved(self, state):
        if self.activeState != self.rootState:
            self.treeModel.removeState(state.stateData, self.activeState)
        else:
            self.treeModel.removeState(state.stateData)

    def transitionInserted(self, tran):
        # print('transition inserted:' + tran.transitionData.name)
        pass

    def stateNameChanged(self, state):
        dataItem = self.treeModel.getByDataId(state.stateData.id)
        if dataItem != None:
            dataItem.name = state.stateData.name
            self.treeModel.layoutChanged.emit()

    def activeStateChanged(self):
        if self.automataScene.activeState != self.activeState:
            # print('visual states active state changed:' + self.automataScene.activeState.name)
            self.activeState = self.automataScene.activeState
            if self.activeState == self.rootState:
                self.treeView.selectionModel().clearSelection()
            else:
                self.treeView.setCurrentIndex(self.treeModel.indexOf(self.treeModel.getByDataId(self.activeState.id)))

    def upButtonClicked(self):
        if self.activeState != None:
            if self.activeState.parent != None:
                # print('parent name:' + self.activeState.parent.name)
                self.automataScene.setActiveState(self.activeState.parent)


    def getStateById(self,state, id):
        if state.id == id:
            return state
        else:
            result = None
            for child in state.getChildren():
                result = self.getStateById(child, id)
                if result is not None:
                    return result
            return result

    def treeItemClicked(self, index):
        # print('clicked item.id:' + str(index.internalPointer().id))
        state = self.getStateById(self.rootState, index.internalPointer().id)
        if state is not None:
            # set the active state as the loaded state
            self.automataScene.setActiveState(state)

    def timeStepDurationChanged(self, duration):
        if self.activeState is not None:
            self.activeState.setTimeStep(duration)

    def variablesChanged(self, variables):
        if self.activeState is not None:
            self.activeState.setVariables(variables)

    def functionsChanged(self, functions):
        if self.activeState is not None:
            self.activeState.setFunctions(functions)

    def librariesChanged(self, libraries):
        self.libraries = libraries

    def configChanged(self):
        if self.configDialog is not None:
            self.config = self.configDialog.getConfig()

    def getStateList(self, state, stateList):
        if len(state.getChildren()) > 0:
            stateList.append(state)

        for s in state.getChildren():
            self.getStateList(s, stateList)