예제 #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')]
예제 #2
0
파일: layer_tree.py 프로젝트: ssec/sift
 def select(self,
            uuids,
            lbox: QTreeView = None,
            scroll_to_show_single=True):
     lbox = self.current_set_listbox if lbox is None else lbox
     lbox.clearSelection()
     if not uuids:
         return
     # FUTURE: this is quick and dirty
     rowdict = dict(
         (u, i) for i, u in enumerate(self.doc.current_layer_uuid_order))
     items = QItemSelection()
     q = None
     for uuid in uuids:
         row = rowdict.get(uuid, None)
         if row is None:
             LOG.error(
                 'UUID {} cannot be selected in list view'.format(uuid))
             continue
         q = self.createIndex(row, 0)
         items.select(q, q)
         lbox.selectionModel().select(items, QItemSelectionModel.Select)
         # lbox.setCurrentIndex(q)
     if scroll_to_show_single and len(uuids) == 1 and q is not None:
         lbox.scrollTo(q)
예제 #3
0
파일: layer_tree.py 프로젝트: ssec/sift
    def _init_widget(self, listbox: QTreeView):
        listbox.setModel(self)
        listbox.setItemDelegate(self.item_delegate)
        listbox.setContextMenuPolicy(Qt.CustomContextMenu)
        # listbox.customContextMenuRequested.connect(self.context_menu)
        listbox.customContextMenuRequested.connect(self.menu)
        listbox.setDragEnabled(True)
        listbox.setAcceptDrops(True)
        listbox.setDropIndicatorShown(True)
        listbox.setSelectionMode(listbox.ExtendedSelection)
        # listbox.setMovement(QTreeView.Snap)
        # listbox.setDragDropMode(QTreeView.InternalMove)
        listbox.setDragDropMode(QAbstractItemView.DragDrop)
        # listbox.setAlternatingRowColors(True)
        # listbox.setDefaultDropAction(Qt.MoveAction)
        # listbox.setDragDropOverwriteMode(True)
        # listbox.entered.connect(self.layer_entered)
        # listbox.setFont(QFont('Andale Mono', 13))

        # the various signals that may result from the user changing the selections
        # listbox.activated.connect(self.changedSelection)
        # listbox.clicked.connect(self.changedSelection)
        # listbox.doubleClicked.connect(self.changedSelection)
        # listbox.pressed.connect(self.changedSelection)
        listbox.selectionModel().selectionChanged.connect(
            self.changedSelection)

        self.widgets.append(listbox)
예제 #4
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)
def main():
    import sys
    app = QApplication(sys.argv)

    v = QTreeView()
    model = TreeModel(utils.find_data_file('disease_num2name.p'), 'Disease')
    v.setModel(model)
    v.selectionModel().select(
        QItemSelection(model.index(0, 0), model.index(5, 0)),
        QItemSelectionModel.Select)
    v.setSelectionMode(QAbstractItemView.NoSelection)
    v.setEditTriggers(QAbstractItemView.NoEditTriggers)
    v.show()
    app.exec()
예제 #6
0
class ItemsView(QDockWidget):
    def __init__(self, title, parent, items):
        super().__init__(title, parent)
        self.setFloating(False)
        self.set_items(items)

    def set_items(self, items):
        self.items = items
        self.itemsTree = QTreeView()
        self.itemsTree.setHeaderHidden(True)

        treeModel = QStandardItemModel()
        rootNode = treeModel.invisibleRootItem()

        groups = {}
        for item in items:
            category = item['data']['category']
            node = self.__get_node(item['name'])
            if (category not in groups):
                groups[category] = self.__get_node(category)
            groups[category].appendRow(node)

        for key in groups:
            rootNode.appendRow(groups[key])

        self.itemsTree.setModel(treeModel)
        self.itemsTree.expandAll()
        self.setWidget(self.itemsTree)

    def set_on_item_selected(self, method):
        self.itemsTree.selectionModel().selectionChanged.connect(
            lambda: method(self.__get_selected_item()))

    def __get_selected_item(self):
        current = self.itemsTree.currentIndex()
        found_item = None
        if (current.parent() != None):
            for item in self.items:
                if (item['name'] == current.data()):
                    found_item = item
        return found_item

    def __get_node(self, title):
        node = QStandardItem()
        node.setEditable(False)
        node.setText(title)
        return node
예제 #7
0
class FileSystemView(QWidget):
    def __init__(self, dir_path):
        super().__init__()
        appWidth = 800
        appHeight = 300
        self.setWindowTitle('File System Viewer')
        self.setGeometry(300, 300, appWidth, appHeight)

        self.model = QFileSystemModel()
        self.model.setRootPath(dir_path)
        self.tree = QTreeView()
        self.tree.setModel(self.model)
        self.tree.setRootIndex(self.model.index(dirPath))
        self.tree.setColumnWidth(0, 250)
        self.tree.setAlternatingRowColors(True)

        layout = QVBoxLayout()
        self.photo = QLabel('Hola' + chr(10) + 'Caracola')
        layout.addWidget(self.tree)
        layout.addWidget(self.photo)
        self.setLayout(layout)
        self.tree.selectionModel().selectionChanged.connect(self.select)

    def keyPressEvent(self, event):
        if event.key() == Qt.Key_Space or event.key() == Qt.Key_Return:
            index = self.tree.selectedIndexes()[0]
            crawler = index.model().filePath(index)
            print(crawler)
        #self.tree.keyPressEvent(self, event)

    def select(self, index1):
        index = self.tree.selectedIndexes()[0]
        file_sel = index.model().filePath(index)
        if os.path.isfile(file_sel) and file_sel[-3:].upper() in 'JPG PNG':
            pixmap = QPixmap(file_sel)
            self.photo.setPixmap(pixmap)
            print(file_sel)
예제 #8
0
class Example(QMainWindow):
    def __init__(self):
        super().__init__()

        self.initUI()

    def initUI(self):

        self.central_widget = QWidget()
        self.setCentralWidget(self.central_widget)

        self.folderLayout = QWidget()

        self.pathRoot = QDir.rootPath()

        self.dirmodel = QFileSystemModel(self)
        self.dirmodel.setRootPath(QDir.currentPath())

        self.indexRoot = self.dirmodel.index(self.dirmodel.rootPath())

        self.folder_view = QTreeView()
        self.folder_view.setDragEnabled(True)
        self.folder_view.setModel(self.dirmodel)
        self.folder_view.setRootIndex(self.indexRoot)

        self.selectionModel = self.folder_view.selectionModel()

        self.left_layout = QVBoxLayout()
        self.left_layout.addWidget(self.folder_view)

        self.folderLayout.setLayout(self.left_layout)

        splitter_filebrowser = QSplitter(Qt.Horizontal)
        splitter_filebrowser.addWidget(self.folderLayout)
        splitter_filebrowser.addWidget(Figure_Canvas(self))
        splitter_filebrowser.setStretchFactor(1, 1)

        hbox = QHBoxLayout(self)
        hbox.addWidget(splitter_filebrowser)

        self.centralWidget().setLayout(hbox)

        self.setWindowTitle('Simple drag & drop')
        self.setGeometry(0, 0, 1000, 500)
예제 #9
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))
예제 #10
0
class MainWindow(QMainWindow):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        self._app = QApplication.instance()
        self._lineEdit = None
        self.create_menu()
        self._completer = TreeModelCompleter(None, self)
        self._completer.setModel(self.model_from_file("./treemodel.txt"))
        self._completer.set_separator(".")
        self._completer.highlighted[QModelIndex].connect(self.highlight)

        central_widget = QWidget()
        model_label = QLabel()
        model_label.setText("Tree Model<br>(Double click items to edit)")
        mode_label = QLabel()
        mode_label.setText("Completion Mode")
        self._mode_combo = QComboBox()
        self._mode_combo.addItem(tr("Inline"))
        self._mode_combo.addItem(tr("Filtered Popup"))
        self._mode_combo.addItem(tr("Unfiltered Popup"))
        self._mode_combo.setCurrentIndex(1)

        case_label = QLabel()
        case_label.setText(tr("Case Sensitivity"))
        self._case_combo = QComboBox()
        self._case_combo.addItem(tr("Case Insensitive"))
        self._case_combo.addItem(tr("Case Sensitive"))
        self._case_combo.setCurrentIndex(0)

        separator_label = QLabel()
        separator_label.setText(tr("Tree Separator"))

        separator_line_edit = QLineEdit()
        separator_line_edit.setText(self._completer.separator())
        separator_line_edit.textChanged.connect(self._completer.set_separator)

        wrap_check_box = QCheckBox()
        wrap_check_box.setText(tr("Wrap around completions"))
        wrap_check_box.setChecked(self._completer.wrapAround())
        wrap_check_box.clicked.connect(self._completer.setWrapAround)

        self._contents_label = QLabel()
        self._contents_label.setSizePolicy(QSizePolicy.Fixed,
                                           QSizePolicy.Fixed)
        separator_line_edit.textChanged.connect(self.update_contents_label)

        self._tree_view = QTreeView()
        self._tree_view.setModel(self._completer.model())
        self._tree_view.header().hide()
        self._tree_view.expandAll()

        self._mode_combo.activated.connect(self.change_mode)
        self._case_combo.activated.connect(self.change_case)

        self._line_edit = QLineEdit()
        self._line_edit.setCompleter(self._completer)

        layout = QGridLayout()
        layout.addWidget(model_label, 0,
                         0), layout.addWidget(self._tree_view, 0, 1)
        layout.addWidget(mode_label, 1,
                         0), layout.addWidget(self._mode_combo, 1, 1)
        layout.addWidget(case_label, 2,
                         0), layout.addWidget(self._case_combo, 2, 1)
        layout.addWidget(separator_label, 3,
                         0), layout.addWidget(separator_line_edit, 3, 1)
        layout.addWidget(wrap_check_box, 4, 0)
        layout.addWidget(self._contents_label, 5, 0, 1, 2)
        layout.addWidget(self._line_edit, 6, 0, 1, 2)
        central_widget.setLayout(layout)
        self.setCentralWidget(central_widget)

        self.change_case(self._case_combo.currentIndex())
        self.change_mode(self._mode_combo.currentIndex())

        self.setWindowTitle(tr("Tree Model Completer"))
        self._line_edit.setFocus()

    def create_menu(self):
        exit_action = QAction(tr("Exit"), self)
        about_act = QAction(tr("About"), self)
        about_qt_act = QAction(tr("About Qt"), self)

        exit_action.triggered.connect(QApplication.instance().quit)
        about_act.triggered.connect(self.about)
        about_qt_act.triggered.connect(QApplication.instance().aboutQt)

        file_menu = self.menuBar().addMenu(tr("File"))
        file_menu.addAction(exit_action)

        help_menu = self.menuBar().addMenu(tr("About"))
        help_menu.addAction(about_act)
        help_menu.addAction(about_qt_act)

    def change_mode(self, index):
        modes = (QCompleter.InlineCompletion, QCompleter.PopupCompletion,
                 QCompleter.UnfilteredPopupCompletion)
        self._completer.setCompletionMode(modes[index])

    def model_from_file(self, file_name):
        # file = QFile(file_name)
        # if not file.open(QFile.ReadOnly):
        #     return QStringListModel(self._completer)
        QApplication.instance().setOverrideCursor(QCursor(Qt.WaitCursor))
        model = QStandardItemModel(self._completer)
        parents = [model.invisibleRootItem()]

        with open(file_name) as file:
            pat = re.compile("^\\s+")
            for line in file:
                if not line:
                    continue
                trimmed_line = line.strip()
                if not trimmed_line:
                    continue
                match = pat.match(line)
                if not match:
                    level = 0
                else:
                    length = match.end() - match.start()
                    if line.startswith("\t"):
                        level = length
                    else:
                        level = length // 4

                while len(parents) < level + 2:
                    parents.append(None)

                item = QStandardItem()
                item.setText(trimmed_line)
                parents[level].appendRow(item)
                parents[level + 1] = item
        QApplication.instance().restoreOverrideCursor()
        return model

    @pyqtSlot(QModelIndex)
    def highlight(self, index):
        proxy = self._completer.completionModel()
        source_index = proxy.mapToSource(index)
        self._tree_view.selectionModel().select(
            source_index,
            QItemSelectionModel.ClearAndSelect | QItemSelectionModel.Rows)
        self._tree_view.scrollTo(source_index)

    def about(self):
        QMessageBox.about(
            self, tr("About"),
            tr("This example demonstrates how to use a QCompleter with a custom tree datamodel."
               ))

    def change_case(self, cs):
        self._completer.setCaseSensitivity(
            Qt.CaseSensitive if cs else Qt.CaseInsensitive)

    def update_contents_label(self, sep):
        self._contents_label.setText(
            "Type path from datamodel above with items at each level separated by a '%s'"
            % sep)
예제 #11
0
class Installed(preferences.Group):
    """Overview of installed extensions.

    A QTreeView lists the metadata of all installed extensions.
    If the currently selected extension provides a configuration
    widget it is displayed in the bottom group of the page.

    With a checkbox individual extensions can be deactivated.
    Metadata is listed for all *installed* extensions, regardless
    of manual deactivation or load failure.
    """

    def __init__(self, page):
        super(Installed, self).__init__(page)

        layout = QVBoxLayout()
        self.setLayout(layout)

        # This must be called before self.populate() because
        # the data model relies on the labels
        app.translateUI(self)

        self.tree = QTreeView()
        self.name_items = {}
        self._selected_extension = ''
        self.tree.setModel(QStandardItemModel())
        self.tree.model().setColumnCount(2)
        self.tree.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.tree.setEditTriggers(QAbstractItemView.NoEditTriggers)
        self.tree.setHeaderHidden(True)
        self.tree.header().setSectionResizeMode(
            0, QHeaderView.ResizeToContents)
        self.tree.selectionModel().selectionChanged.connect(
            self.selection_changed)
        self.populate()
        layout.addWidget(self.tree)

    def translateUI(self):
        self.setTitle(_("Installed Extensions"))
        self.config_labels = {
            'extension-name': _("Name"),
            'maintainers': _("Maintainer(s)"),
            'version': _("Version"),
            'api-version': _("API version"),
            'license': _("License"),
            'short-description': _("Short Description"),
            'description': _("Description"),
            'repository': _("Repository"),
            'website': _("Website"),
            'dependencies': _("Dependencies")
        }

    def loadSettings(self):
        s = QSettings()
        self.setEnabled(self.page().active())
        s.beginGroup("extension-settings/installed")
        inactive = s.value("inactive", [], list)
        for ext in self.name_items.keys():
            self.name_items[ext].setCheckState(
                Qt.Checked if ext not in inactive else Qt.Unchecked)
        self.tree.model().dataChanged.connect(self.page().changed)

    def saveSettings(self):
        s = QSettings()
        s.beginGroup("extension-settings/installed")
        inactive = [ext for ext in self.name_items.keys()
            if self.name_items[ext].checkState() == Qt.Unchecked]
        s.setValue("inactive", inactive)

    def populate(self):
        """Populate the tree view with data from the installed extensions.
        """

        # TODO/Question:
        # Would it make sense to move this to a dedicated model class
        # complementing the FailedModel?

        root = self.tree.model().invisibleRootItem()
        extensions = app.extensions()
        for ext in extensions.installed_extensions():
            ext_infos = extensions.infos(ext)
            display_name = ext_infos.get(ext, ext) if ext_infos else ext.name()
            loaded_extension = extensions.get(ext)
            if loaded_extension:
                display_name += ' ({})'.format(loaded_extension.load_time())

            name_item = QStandardItem(display_name)
            name_item.extension_name = ext
            name_item.setCheckable(True)
            self.name_items[ext] = name_item
            icon = extensions.icon(ext)
            if icon:
                name_item.setIcon(icon)
            root.appendRow([name_item])
            for entry in [
                'extension-name',
                'short-description',
                'description',
                'version',
                'api-version',
                'dependencies',
                'maintainers',
                'repository',
                'website',
                'license'
            ]:
                label_item = QStandardItem('{}:'.format(
                    self.config_labels[entry]))
                label_item.setTextAlignment(Qt.AlignTop)
                bold = QFont()
                bold.setWeight(QFont.Bold)
                label_item.setFont(bold)
                details = ext_infos.get(entry, "") if ext_infos else ""
                if type(details) == list:
                    details = '\n'.join(details)
                details_item = QStandardItem(details)
                details_item.setTextAlignment(Qt.AlignTop)
                if entry == 'api-version':
                    # Check for correct(ly formatted) api-version entry
                    # and highlight it in case of mismatch
                    api_version = appinfo.extension_api
                    if not details:
                        details_item.setFont(bold)
                        details_item.setText(
                            _("Misformat: {api}").format(details))
                    elif not details == api_version:
                            details_item.setFont(bold)
                            details_item.setText('{} ({}: {})'.format(
                                details,
                                appinfo.appname,
                                api_version))
                name_item.appendRow([label_item, details_item])

    def selected_extension(self):
        """Return the (directory) name of the extension that
        is currently selected."""
        return self._selected_extension

    def selection_changed(self, new, old):
        """Show the configuration widget for the selected extension,
        if available."""
        config = self.siblingGroup(Config)
        if new.indexes():
            ext_item = self.tree.model().itemFromIndex(new.indexes()[0])
            # NOTE: This may have to be changed if there should be
            # more complexity in the tree model than now (a selected
            # row is either a top-level row or its immediate child)
            if not hasattr(ext_item, 'extension_name'):
                ext_item = ext_item.parent()
            name = ext_item.extension_name
            if name == self.selected_extension():
                return
        else:
            # If nothing is selected, show the "empty" widget
            name = ""

        config.hide_extension()
        self._selected_extension = name
        config.show_extension(name)
예제 #12
0
class ImageViewer(QWidget):
    def __init__(self):

        super(ImageViewer, self).__init__()

        pal = QPalette()
        pal.setColor(QPalette.Background, Qt.lightGray)

        self.factor = 3.0

        self.config = Config()
        self.currentRep = ""

        self.createActions()
        self.createToolbarMenus()
        #self.createMenus()

        self.browserFile()
        self.imgqLabel()
        self.boxSliders()

        self.verticalLayout = QVBoxLayout(self)
        self.horizontalLayout = QHBoxLayout(self)

        self.textInfo = QTextEdit()

        self.textInfoTop = QTextEdit()
        self.textInfoTop.setEnabled(True)
        self.textInfoTop.setSizePolicy(QSizePolicy.Expanding,
                                       QSizePolicy.Ignored)
        self.textInfoTop.setFontPointSize(11)
        self.textInfoTop.setStyleSheet("background-color: lightgray")
        #self.textInfoTop.adjustSize()
        self.textInfoTop.setText('Welcome to IRMaGe')

        self.tableJson = QTableWidget()
        self.tableJson.setColumnCount(2)
        self.tableJson.setColumnWidth(0, 150)
        self.tableJson.setColumnWidth(1, 400)
        self.tableJson.setSizeAdjustPolicy(
            QAbstractScrollArea.AdjustToContents)

        self.tableJson.setHorizontalHeaderLabels(['Keys', 'Values'])
        #self.tableJson.setBackgroundRole(QPalette.Light)

        self.scrollText = QScrollArea()
        self.scrollText.setBackgroundRole(QPalette.Dark)
        self.scrollText.setWidget(self.textInfoTop)
        self.scrollText.setWidgetResizable(True)
        #=======================================================================
        # self.adjustScrollBar(self.scrollText.horizontalScrollBar(), 1.0)
        # self.adjustScrollBar(self.scrollText.verticalScrollBar(), 2.0)
        #=======================================================================
        self.scrollTable = QScrollArea()
        self.scrollTable.setBackgroundRole(QPalette.Dark)
        self.scrollTable.setWidget(self.tableJson)
        self.scrollTable.setWidgetResizable(True)
        #=======================================================================
        # self.adjustScrollBar(self.scrollTable.horizontalScrollBar(), 2.0)
        # self.adjustScrollBar(self.scrollTable.verticalScrollBar(), 2.0)
        #=======================================================================

        self.headerTabData = [
            'Data', 'PatientName', 'StudyName', 'DateCreation', 'PatientSex',
            'PatientWeight', 'ProtocolName', 'SequenceName'
        ]

        self.tableData = TableDataBrower(self)
        self.tableData.setColumnCount(8)
        self.tableData.setRowCount(10)
        self.tableData.setColumnWidth(0, 200)
        self.tableData.setHorizontalHeaderLabels(self.headerTabData)
        self.tableData.setBackgroundRole(QPalette.Light)
        self.tableData.setSizeAdjustPolicy(
            QAbstractScrollArea.AdjustToContents)
        self.tableData.verticalHeader().hide()

        self.scrollBrowser = QScrollArea()
        self.scrollBrowser.setBackgroundRole(QPalette.Dark)
        self.scrollBrowser.setWidget(self.tableData)
        self.scrollBrowser.setWidgetResizable(True)

        self.splitter0 = QSplitter(Qt.Vertical)
        self.splitter0.addWidget(self.scrollText)
        self.splitter0.addWidget(self.scrollTable)

        self.scrollArea = QScrollArea()
        self.scrollArea.setBackgroundRole(QPalette.Dark)
        self.scrollArea.setWidget(self.imageLabel)
        self.scrollArea.setWidgetResizable(False)
        self.scrollArea.setAlignment(Qt.AlignCenter)

        self.adjustScrollBar(self.scrollArea.horizontalScrollBar(), 0.8)
        self.adjustScrollBar(self.scrollArea.verticalScrollBar(), 1.0)

        self.splitter1 = QSplitter(Qt.Horizontal)
        self.splitter1.addWidget(self.splitter0)
        self.splitter1.addWidget(self.scrollArea)
        self.splitter1.addWidget(self.layoutSlide)

        self.splitter3 = QSplitter(Qt.Horizontal)
        self.splitter3.addWidget(self.browser)
        self.splitter3.addWidget(self.scrollBrowser)

        self.splitter2 = QSplitter(Qt.Vertical)
        self.splitter2.addWidget(self.splitter1)
        self.splitter2.addWidget(self.splitter3)
        self.splitter2.setHandleWidth(15)
        #=======================================================================
        # self.splitter2.
        #=======================================================================

        self.verticalLayout.addWidget(self.menuToolBar)
        self.verticalLayout.addWidget(self.splitter2)

        self.setWindowTitle("MRImage Viewer (IRMaGe)")
        self.resize(800, 600)

        self.setAutoFillBackground(True)
        self.setPalette(pal)

    def changeSel(self):
        print('Tab changed')

    def adjustScrollBar(self, scrollBar, factor):
        scrollBar.setValue(
            int(factor * scrollBar.value() +
                ((factor - 1) * scrollBar.pageStep() / 2)))

    def imgqLabel(self):
        QLabel.__init__(self)
        image = QImage('sources_images/LogoIRMaGe.png')
        self.scaleFactor = 1.0
        self.imageLabel = QLabel()
        self.imageLabel.setBackgroundRole(QPalette.Base)
        self.imageLabel.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored)
        self.imageLabel.setScaledContents(True)
        self.imageLabel.setPixmap(QPixmap.fromImage(image))
        self.scaleFactor *= self.factor
        self.imageLabel.adjustSize()
        self.imageLabel.resize(self.scaleFactor *
                               self.imageLabel.pixmap().size())

    def open(self, filePath):
        self.img = nib.load(filePath)
        self.textInfoTop.setText('File : ' + filePath + '\n')
        self.textInfoTop.append('Dim : ' + str(self.img.shape) + '\n')
        self.enableSliders()
        self.a1.setValue(0)
        self.a2.setValue(0)
        self.a3.setValue(0)
        self.c2.setMaximum(self.img.shape[0])
        self.c2.setMinimum(-self.img.shape[0])
        self.c3.setMaximum(self.img.shape[1])
        self.c3.setMinimum(-self.img.shape[1])
        self.navigImage()
        self.fitToWindowAct.setEnabled(True)
        self.fitToWindow()

    def openJson(self, pathJson, fileName):
        with open(pathJson, 'r') as stream:
            try:
                json_object = json.load(stream)
                data = json.dumps(json_object, indent=0, sort_keys=True)
                data = json.loads(data)
                rowPosition = 0
                self.tableJson.setRowCount(0)

                i = 0
                for keyd in self.headerTabData:
                    try:
                        val = str(data[keyd])
                        val = val.replace('[', '')
                        val = val.replace(']', '')
                    except:
                        val = ''
                    #===========================================================
                    # self.tableData.insertRow(i)
                    # self.tableData.setItem(0,i,QTableWidgetItem(val))
                    # i+=1
                    #===========================================================
                #===============================================================
                # self.tableData.setItem(0,0,QTableWidgetItem(fileName))
                # self.tableData.selectRow(0)
                #===============================================================
                for keys in data:
                    stringValue = str(data[keys])
                    stringValue = stringValue.replace('[', '')
                    stringValue = stringValue.replace(']', '')
                    self.tableJson.insertRow(rowPosition)
                    self.tableJson.setItem(rowPosition, 0,
                                           QTableWidgetItem(keys))
                    self.tableJson.setItem(rowPosition, 1,
                                           QTableWidgetItem(stringValue))
                    rowPosition += 1
                self.tableJson.resizeColumnsToContents()
            except json.JSONDecodeError as exc:
                itemError = 'Error Json format'
                self.tableJson.setRowCount(0)
                self.tableJson.insertRow(0)
                self.tableJson.setItem(0, 0, QTableWidgetItem(itemError))
                print(exc)

    def jsonParser(self, pathJson):
        with open(pathJson, 'r') as stream:
            try:
                json_object = json.load(stream)
                listTag = json.dumps(json_object, indent=0, sort_keys=True)
                listTag = json.loads(listTag)
            except json.JSONDecodeError as exc:
                itemError = 'Error Json format'
        return listTag

    def tableDataFill(self, pathRepertory):
        files = [f for f in fnmatch.filter(os.listdir(pathRepertory), '*.nii')]
        self.tableData.setRowCount(0)
        j = 0
        for f in files:
            base = os.path.splitext(f)[0]
            g = os.path.join(pathRepertory, base + ".json")
            self.tableData.insertRow(j)
            if os.path.isfile(g):
                data = self.jsonParser(g)
                i = 0
                for keyw in self.headerTabData:
                    try:
                        val = str(data[keyw])
                        val = val.replace('[', '')
                        val = val.replace(']', '')
                    except:
                        val = ''
                    self.tableData.setItem(j, i, QTableWidgetItem(val))
                    i += 1
            else:
                self.tableData.setItem(j, 1,
                                       QTableWidgetItem('No json file found'))
            self.tableData.setItem(j, 0, QTableWidgetItem(f))
            self.tableData.resizeColumnsToContents()
            j += 1

    def indexImage(self):
        sl1 = self.a1.value()
        sl2 = self.a2.value()
        sl3 = self.a3.value()
        if len(self.img.shape) == 3:
            x = self.img.get_data()[:, :, sl1].copy()
            self.a1.setMaximum(self.img.shape[2] - 1)
            self.a2.setMaximum(0)
            self.a3.setMaximum(0)
        if len(self.img.shape) == 4:
            x = self.img.get_data()[:, :, sl1, sl2].copy()
            self.a1.setMaximum(self.img.shape[2] - 1)
            self.a2.setMaximum(self.img.shape[3] - 1)
            self.a3.setMaximum(0)
        if len(self.img.shape) == 5:
            x = self.img.get_data()[:, :, sl1, sl2, sl3].copy()
            self.a1.setMaximum(self.img.shape[2] - 1)
            self.a2.setMaximum(self.img.shape[3] - 1)
            self.a3.setMaximum(self.img.shape[4] - 1)
        x = rotate(x, -90, reshape=False)
        x = np.uint8((x - x.min()) / x.ptp() * 255.0)
        self.x = x

############################ Slice controls  #########################################

    def boxSliders(self):
        self.k1 = QLabel('Slider 1    ')
        self.k2 = QLabel('Slider 2')
        self.k3 = QLabel('Slider 3')

        self.a1 = self.createSlider(0, 0, 0)
        self.a2 = self.createSlider(0, 0, 0)
        self.a3 = self.createSlider(0, 0, 0)

        self.a1.valueChanged.connect(self.changePosValue)
        self.a2.valueChanged.connect(self.changePosValue)
        self.a3.valueChanged.connect(self.changePosValue)

        self.txta1 = self.createFieldValue()
        self.txta2 = self.createFieldValue()
        self.txta3 = self.createFieldValue()

        self.controlsGroup = QGroupBox('Slice Controls')
        gridCtrl = QGridLayout()
        gridCtrl.addWidget(self.k1, 0, 0)
        gridCtrl.addWidget(self.a1, 0, 1)
        gridCtrl.addWidget(self.txta1, 0, 2)
        gridCtrl.addWidget(self.k2, 1, 0)
        gridCtrl.addWidget(self.a2, 1, 1)
        gridCtrl.addWidget(self.txta2, 1, 2)
        gridCtrl.addWidget(self.k3, 2, 0)
        gridCtrl.addWidget(self.a3, 2, 1)
        gridCtrl.addWidget(self.txta3, 2, 2)
        self.controlsGroup.setLayout(gridCtrl)

        ############################ brightness and contrast  ################################
        self.txtb1 = self.createFieldValue()
        self.txtb2 = self.createFieldValue()
        self.txtb3 = self.createFieldValue()
        self.txtb4 = self.createFieldValue()

        self.l1 = QLabel('Brightness    ')
        self.b1 = self.createSlider(101, 0, 50)
        self.l2 = QLabel('Contrast')
        self.b2 = self.createSlider(101, 0, 50)
        self.l3 = QLabel('Sharpness')
        self.b3 = self.createSlider(101, 0, 50)
        self.l4 = QLabel('Color')
        self.b4 = self.createSlider(101, 0, 50)

        self.b1.valueChanged.connect(self.changeContValue)
        self.b2.valueChanged.connect(self.changeContValue)
        self.b3.valueChanged.connect(self.changeContValue)
        self.b4.valueChanged.connect(self.changeContValue)

        self.txtb1.setText(str(0))
        self.txtb2.setText(str(0))
        self.txtb3.setText(str(0))
        self.txtb4.setText(str(0))

        self.buttonResetContrast = QPushButton('reset', self)
        self.buttonResetContrast.setToolTip('Reset all values')
        self.buttonResetContrast.setEnabled(False)
        self.buttonResetContrast.clicked.connect(self.resetValuesContrast)

        self.contrastGroup = QGroupBox('Brightness and Contrast')
        gridCont = QGridLayout()
        gridCont.addWidget(self.l1, 0, 0)
        gridCont.addWidget(self.b1, 0, 1)
        gridCont.addWidget(self.txtb1, 0, 2)
        gridCont.addWidget(self.l2, 1, 0)
        gridCont.addWidget(self.b2, 1, 1)
        gridCont.addWidget(self.txtb2, 1, 2)
        gridCont.addWidget(self.l3, 2, 0)
        gridCont.addWidget(self.b3, 2, 1)
        gridCont.addWidget(self.txtb3, 2, 2)
        gridCont.addWidget(self.l4, 3, 0)
        gridCont.addWidget(self.b4, 3, 1)
        gridCont.addWidget(self.txtb4, 3, 2)
        gridCont.addWidget(self.buttonResetContrast, 4, 2)
        self.contrastGroup.setLayout(gridCont)

        ############################ Transformation  #########################################
        self.txtc1 = self.createFieldValue()
        self.txtc2 = self.createFieldValue()
        self.txtc3 = self.createFieldValue()
        self.txtc4 = self.createFieldValue()

        self.m1 = QLabel('Rotation')
        self.c1 = self.createSlider(180, -180, 0)
        self.m2 = QLabel('Translate X    ')
        self.c2 = self.createSlider(1, -1, 0)
        self.m3 = QLabel('Translate Y    ')
        self.c3 = self.createSlider(1, -1, 0)
        self.m4 = QLabel('Resize')
        self.c4 = self.createSlider(10, 0, 0)

        self.c1.valueChanged.connect(self.changeTransValue)
        self.c2.valueChanged.connect(self.changeTransValue)
        self.c3.valueChanged.connect(self.changeTransValue)
        self.c4.valueChanged.connect(self.changeTransValue)

        self.txtc1.setText(str(0))
        self.txtc2.setText(str(0))
        self.txtc3.setText(str(0))
        self.txtc4.setText(str(0))

        self.buttonResetTransform = QPushButton('reset', self)
        self.buttonResetTransform.setToolTip('Reset all values')
        self.buttonResetTransform.setEnabled(False)
        self.buttonResetTransform.clicked.connect(self.resetValuesTransform)

        self.transformationGroup = QGroupBox('Transformations')
        gridTransf = QGridLayout()
        gridTransf.addWidget(self.m1, 0, 0)
        gridTransf.addWidget(self.c1, 0, 1)
        gridTransf.addWidget(self.txtc1, 0, 2)
        gridTransf.addWidget(self.m2, 1, 0)
        gridTransf.addWidget(self.c2, 1, 1)
        gridTransf.addWidget(self.txtc2, 1, 2)
        gridTransf.addWidget(self.m3, 2, 0)
        gridTransf.addWidget(self.c3, 2, 1)
        gridTransf.addWidget(self.txtc3, 2, 2)
        gridTransf.addWidget(self.m4, 3, 0)
        gridTransf.addWidget(self.c4, 3, 1)
        gridTransf.addWidget(self.txtc4, 3, 2)
        gridTransf.addWidget(self.buttonResetTransform, 4, 2)
        self.transformationGroup.setLayout(gridTransf)

        ####################################################################################
        self.layoutSliders = QVBoxLayout()
        self.layoutSliders.addWidget(self.controlsGroup)
        self.layoutSliders.addWidget(self.contrastGroup)
        self.layoutSliders.addWidget(self.transformationGroup)

        self.layoutSlide = QWidget()
        self.layoutSlide.setLayout(self.layoutSliders)

    def createSlider(self, maxm=0, minm=0, pos=0):
        slider = QSlider(Qt.Horizontal)
        slider.setFocusPolicy(Qt.StrongFocus)
        #slider.setTickPosition(QSlider.TicksBothSides)
        slider.setTickInterval(1)
        #slider.setSingleStep(1)
        slider.setMaximum(maxm)
        slider.setMinimum(minm)
        slider.setValue(pos)
        slider.setEnabled(False)
        return slider

    def createFieldValue(self):
        fieldValue = QLineEdit()
        fieldValue.setEnabled(False)
        fieldValue.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        return fieldValue

    def displayPosValue(self):
        self.txta1.setText(
            str(self.a1.value() + 1) + ' / ' + str(self.a1.maximum() + 1))
        self.txta2.setText(
            str(self.a2.value() + 1) + ' / ' + str(self.a2.maximum() + 1))
        self.txta3.setText(
            str(self.a3.value() + 1) + ' / ' + str(self.a3.maximum() + 1))

    def changePosValue(self):
        self.navigImage()

    def navigImage(self):
        self.indexImage()
        self.displayPosValue()
        w, h = self.x.shape
        image = QImage(self.x.data, w, h, QImage.Format_Indexed8)
        self.pixm = QPixmap.fromImage(image)
        self.imageLabel.setPixmap(self.pixm)
        self.imageLabel.adjustSize()
        self.imageLabel.resize(self.scaleFactor *
                               self.imageLabel.pixmap().size())
        self.filter()

    def changeContValue(self):
        self.txtb1.setText(str(self.b1.value() - 50))
        self.txtb2.setText(str(self.b2.value() - 50))
        self.txtb3.setText(str(self.b3.value() - 50))
        self.txtb4.setText(str(self.b4.value() - 50))
        self.filter()

    def changeTransValue(self):
        self.txtc1.setText(str(self.c1.value()))
        self.txtc2.setText(str(self.c2.value()))
        self.txtc3.setText(str(self.c3.value()))
        self.txtc4.setText(str(self.c4.value()))
        self.filter()

    def filter(self):
        img = Image.fromarray(self.x, 'L')

        brightness = ImageEnhance.Brightness(img)
        newImg = brightness.enhance(1.2 * (self.b1.value() + 1) / 50.0)

        contrast = ImageEnhance.Contrast(newImg)
        newImg = contrast.enhance((self.b2.value() + 1) / 50.0)

        sharpness = ImageEnhance.Sharpness(newImg)
        newImg = sharpness.enhance(2.0 * (self.b3.value() + 1) / 50.0)

        color = ImageEnhance.Color(newImg)
        newImg = color.enhance((self.b4.value() + 1) / 50.0)

        newImg = newImg.rotate(self.c1.value())

        newImg = newImg.transform(
            img.size, Image.AFFINE,
            (1, 0, self.c2.value(), 0, 1, self.c3.value()))

        size1 = int(img.size[0] * (self.c4.value() + 1))
        size2 = int(img.size[1] * (self.c4.value() + 1))

        newImg = newImg.resize((size1, size2), Image.ANTIALIAS)

        self.pixm = QPixmap.fromImage(newImg.toqimage())
        self.imageLabel.setPixmap(self.pixm)
        self.imageLabel.adjustSize()
        self.imageLabel.resize(self.scaleFactor *
                               self.imageLabel.pixmap().size())

    def resetValuesContrast(self):
        self.b1.setSliderPosition(50)
        self.b2.setSliderPosition(50)
        self.b3.setSliderPosition(50)
        self.b4.setSliderPosition(50)
        self.changeContValue()

    def resetValuesTransform(self):
        self.c1.setSliderPosition(0)
        self.c2.setSliderPosition(0)
        self.c3.setSliderPosition(0)
        self.c4.setSliderPosition(0)
        self.changeTransValue()

    def enableSliders(self):
        self.a1.setEnabled(True)
        self.a2.setEnabled(True)
        self.a3.setEnabled(True)
        self.b1.setEnabled(True)
        self.b2.setEnabled(True)
        self.b3.setEnabled(True)
        self.b4.setEnabled(True)
        self.c1.setEnabled(True)
        self.c2.setEnabled(True)
        self.c3.setEnabled(True)
        self.c4.setEnabled(True)
        self.buttonResetContrast.setEnabled(True)
        self.buttonResetTransform.setEnabled(True)

####################################################################################

    def browserFile(self):

        global Browser, Model

        self.browser = QTreeView()

        model = QFileSystemModel()
        model.setNameFilters(['*.nii'])
        model.setNameFilterDisables(False)
        model.setReadOnly(True)

        self.browser.setModel(model)
        self.browser.expandAll()
        self.browser.setColumnWidth(0, 400)

        self.browser.selectionModel().selectionChanged.connect(self.select)

        Browser = self.browser
        Model = model

        #=======================================================================
        # self.browser.doubleClicked.connect(self.selection)
        #self.browser.clicked.connect(self.selection)
        #=======================================================================

    def select(self, signal):
        file_path = self.browser.model().filePath(signal.indexes()[0])
        shortName, fileExt = os.path.splitext(file_path)
        filePath, fileName = os.path.split(file_path)
        self.textInfo.setText(filePath)
        blackColor = QColor(0, 0, 0)

        if os.path.isfile(file_path):
            if fileExt == ".nii":
                if self.currentRep != filePath:
                    self.tableDataFill(filePath)
                    self.currentRep = filePath
                self.open(file_path)
                self.tableData.selectRow(
                    self.tableData.findItems(fileName,
                                             Qt.MatchExactly)[0].row())
                if os.path.isfile(shortName + '.json'):
                    greenColor = QColor(50, 150, 100)
                    self.textInfoTop.setTextColor(greenColor)
                    self.textInfoTop.append('Json file exists ' + '\n')
                    self.openJson(shortName + '.json', fileName)
                else:
                    redColor = QColor(255, 0, 0)
                    self.textInfoTop.setTextColor(redColor)
                    self.textInfoTop.append('Json file doesn\'t exist' + '\n')
                    self.tableJson.setRowCount(0)
        else:
            self.tableData.setRowCount(0)
            self.currentRep = filePath

        self.textInfoTop.setTextColor(blackColor)
        self.scrollText.setWidgetResizable(True)


####################################################################################

    def createMenus(self):
        self.fileMenu = QMenu("&File", self)
        self.fileMenu.addAction(self.exitAct)

        self.viewMenu = QMenu("&View", self)
        self.viewMenu.addAction(self.zoomInAct)
        self.viewMenu.addAction(self.zoomOutAct)
        self.viewMenu.addAction(self.normalSizeAct)
        self.viewMenu.addSeparator()
        self.viewMenu.addAction(self.fitToWindowAct)
        self.viewMenu.addSeparator()

        self.helpMenu = QMenu("&Help", self)
        self.helpMenu.addAction(self.aboutAct)

        self.menuBar = QMenuBar()

        self.menuBar.addMenu(self.fileMenu)
        self.menuBar.addMenu(self.viewMenu)
        self.menuBar.addMenu(self.helpMenu)

    def createToolbarMenus(self):
        self.menuToolBar = QToolBar()

        viewMenu = QToolButton()
        viewMenu.setText('View')
        viewMenu.setPopupMode(QToolButton.MenuButtonPopup)
        aMenu = QMenu()
        aMenu.addAction(self.zoomInAct)
        aMenu.addAction(self.zoomOutAct)
        aMenu.addAction(self.normalSizeAct)
        aMenu.addSeparator()
        aMenu.addAction(self.fitToWindowAct)
        viewMenu.setMenu(aMenu)

        helpMenu = QToolButton()
        helpMenu.setText('Help')
        helpMenu.setPopupMode(QToolButton.MenuButtonPopup)
        bMenu = QMenu()
        helpMenu.setMenu(bMenu)

        self.menuToolBar.addWidget(viewMenu)
        self.menuToolBar.addWidget(helpMenu)

    def createActions(self):
        self.exitAct = QAction("Exit",
                               self,
                               shortcut="Ctrl+Q",
                               triggered=self.close)

        self.zoomInAct = QAction("Zoom In (25%)",
                                 self,
                                 shortcut="Ctrl++",
                                 enabled=False,
                                 triggered=self.zoomIn)

        self.zoomOutAct = QAction("Zoom Out (25%)",
                                  self,
                                  shortcut="Ctrl+-",
                                  enabled=False,
                                  triggered=self.zoomOut)

        self.normalSizeAct = QAction("Normal Size",
                                     self,
                                     shortcut="Ctrl+S",
                                     enabled=False,
                                     triggered=self.normalSize)

        self.fitToWindowAct = QAction("Fit to Window",
                                      self,
                                      enabled=False,
                                      checkable=True,
                                      shortcut="Ctrl+F",
                                      triggered=self.fitToWindow)

    def zoomIn(self):
        self.factor = 1.25
        self.scaleImage(self.factor)

    def zoomOut(self):
        self.factor = 0.8
        self.scaleImage(self.factor)

    def normalSize(self):
        self.imageLabel.adjustSize()
        self.scaleFactor = 1.0

    def fitToWindow(self):
        fitToWindow = self.fitToWindowAct.isChecked()
        self.scrollArea.setWidgetResizable(fitToWindow)
        self.scrollText.setWidgetResizable(fitToWindow)
        if not fitToWindow:
            self.normalSize()

        self.updateActions()

    def updateActions(self):
        self.zoomInAct.setEnabled(not self.fitToWindowAct.isChecked())
        self.zoomOutAct.setEnabled(not self.fitToWindowAct.isChecked())
        self.normalSizeAct.setEnabled(not self.fitToWindowAct.isChecked())

    def scaleImage(self, factor):
        self.scaleFactor *= factor
        self.imageLabel.resize(self.scaleFactor *
                               self.imageLabel.pixmap().size())

        self.adjustScrollBar(self.scrollArea.horizontalScrollBar(), factor)
        self.adjustScrollBar(self.scrollArea.verticalScrollBar(), factor)

        self.zoomInAct.setEnabled(self.scaleFactor < 5.0)
        self.zoomOutAct.setEnabled(self.scaleFactor > 0.333)

    def close(self):
        self.close()
예제 #13
0
class MangaControlsWidget(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setLayout(QHBoxLayout())
        self.__init_done = False

        self.__model = None
        model = get_default_manga()
        if model is None:
            model = manga_selection_dialog('~', self)

        self.__contents = QTreeView()
        self.__contents.setMaximumWidth(200)
        self.__contents.setMinimumWidth(150)
        if not global_data.get('show_contents'):
            self.__contents.hide()
        self.__display = Display()
        self.__contents.setFocusProxy(self.__display)
        self.layout().addWidget(self.__contents)
        self.layout().addWidget(self.__display)
        self.set_model(model)

    def set_model(self, model):
        self.__model = model
        self.__contents.setModel(model)

        self.__model.position_changed.connect(self.on_position_change)
        self.__contents.selectionModel().currentChanged.connect(
            self.selection_changed)
        self.sync_selection()
        self.__display.display_image(self.__model.current())

    def selection_changed(self, index):
        if self.__init_done:
            self.__model.select(index)
        else:
            self.__init_done = True

    def on_position_change(self, path):
        self.__display.display_image(path)
        self.sync_selection()
        save_manga_state(self.__model)

    def sync_selection(self):
        index = self.__model._current_index()
        self.__contents.selectionModel().setCurrentIndex(
            index, QItemSelectionModel.SelectCurrent)

    def open_dialog(self):
        path, _ = os.path.split(self.__model.path())
        model = manga_selection_dialog(path, self)
        if model is not None:
            self.set_model(model)

    def keyPressEvent(self, event):
        if event.key() == Qt.Key_C:
            self.toggle_contents()
        elif event.key() == Qt.Key_Plus:
            self.__display.zoom(1.05)
        elif event.key() == Qt.Key_Minus:
            self.__display.zoom(1 / 1.05)
        elif event.key() == Qt.Key_J:
            self.__display.scroll(100)
        elif event.key() == Qt.Key_K:
            self.__display.scroll(-100)
        elif event.key() == Qt.Key_L:
            self.__model.next()
        elif event.key() == Qt.Key_H:
            self.__model.prev()
        else:
            event.ignore()

    def toggle_contents(self):
        showed = global_data.get('show_contents')
        if showed:
            self.__contents.hide()
        else:
            self.__contents.show()
        global_data.set('show_contents', not showed)
예제 #14
0
class OWGeneSets(OWWidget):
    name = "Gene Sets"
    description = ""
    icon = "icons/OWGeneSets.svg"
    priority = 9
    want_main_area = True

    # settings
    selected_organism = Setting(0)
    auto_commit = Setting(True)
    auto_apply = Setting(True)

    gene_col_index = ContextSetting(0)
    use_attr_names = ContextSetting(False)

    class Inputs:
        genes = Input("Genes", Table)

    class Outputs:
        matched_genes = Output("Matched Genes", Table)

    class Information(OWWidget.Information):
        pass

    class Error(OWWidget.Error):
        cant_reach_host = Msg("Host orange.biolab.si is unreachable.")
        cant_load_organisms = Msg(
            "No available organisms, please check your connection.")

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

        # commit
        self.commit_button = None

        # progress bar
        self.progress_bar = None
        self.progress_bar_iterations = None

        # data
        self.input_data = None
        self.tax_id = None
        self.input_genes = None
        self.mapped_genes = None
        self.organisms = list()
        self.input_info = None

        self.column_candidates = []

        # filter
        self.lineEdit_filter = None
        self.search_pattern = ''
        self.organism_select_combobox = None

        # data model view
        self.data_view = None
        self.data_model = None

        # gene matcher NCBI
        self.gene_matcher = None

        # filter proxy model
        self.filter_proxy_model = None

        # hierarchy widget
        self.hierarchy_widget = None
        self.hierarchy_state = None

        # input options
        self.gene_columns = None
        self.gene_column_combobox = None
        self.attr_names_checkbox = None

        # threads
        self.threadpool = QThreadPool(self)
        self.workers = None

        # gui
        self.setup_gui()
        self._get_available_organisms()

        # self.handle_input(self.input_genes)
        # self.on_organism_change()

    def _progress_advance(self):
        # GUI should be updated in main thread. That's why we are calling advance method here
        if self.progress_bar:
            self.progress_bar.advance()

    def _get_selected_organism(self):
        return self.organisms[self.selected_organism]

    def _get_available_organisms(self):
        available_organism = sorted([(tax_id, taxonomy.name(tax_id))
                                     for tax_id in taxonomy.common_taxids()],
                                    key=lambda x: x[1])

        self.organisms = [tax_id[0] for tax_id in available_organism]

        self.organism_select_combobox.addItems(
            [tax_id[1] for tax_id in available_organism])

    def _gene_names_from_table(self):
        """ Extract and return gene names from `Orange.data.Table`.
        """
        self.input_genes = []
        if self.input_data:
            if self.use_attr_names:
                self.input_genes = [
                    str(attr.name).strip()
                    for attr in self.input_data.domain.attributes
                ]
            elif self.gene_columns:
                column = self.gene_columns[self.gene_col_index]
                self.input_genes = [
                    str(e[column]) for e in self.input_data
                    if not np.isnan(e[column])
                ]

    def _update_gene_matcher(self):
        self._gene_names_from_table()
        if self.gene_matcher:
            self.gene_matcher.genes = self.input_genes
            self.gene_matcher.organism = self._get_selected_organism()

    def on_input_option_change(self):
        self._update_gene_matcher()
        self.match_genes()

    @Inputs.genes
    def handle_input(self, data):
        if data:
            self.input_data = data
            self.gene_matcher = gene.GeneMatcher(self._get_selected_organism())

            self.gene_column_combobox.clear()
            self.column_candidates = [
                attr for attr in data.domain.variables + data.domain.metas
                if isinstance(attr, (StringVariable, DiscreteVariable))
            ]

            for var in self.column_candidates:
                self.gene_column_combobox.addItem(*attributeItem(var))

            self.tax_id = str(data_hints.get_hint(self.input_data, TAX_ID))
            self.use_attr_names = data_hints.get_hint(
                self.input_data, GENE_NAME, default=self.use_attr_names)
            self.gene_col_index = min(self.gene_col_index,
                                      len(self.column_candidates) - 1)

            if self.tax_id in self.organisms:
                self.selected_organism = self.organisms.index(self.tax_id)

        self.on_input_option_change()

    def update_info_box(self):
        info_string = ''
        if self.input_genes:
            info_string += '{} unique gene names on input.\n'.format(
                len(self.input_genes))
            mapped = self.gene_matcher.get_known_genes()
            if mapped:
                ratio = (len(mapped) / len(self.input_genes)) * 100
                info_string += '{} ({:.2f}%) gene names matched.\n'.format(
                    len(mapped), ratio)
        else:
            info_string += 'No genes on input.\n'

        self.input_info.setText(info_string)

    def match_genes(self):
        if self.gene_matcher:
            # init progress bar
            self.progress_bar = ProgressBar(self,
                                            iterations=len(
                                                self.gene_matcher.genes))
            # status message
            self.setStatusMessage('gene matcher running')

            worker = Worker(self.gene_matcher.run_matcher,
                            progress_callback=True)
            worker.signals.progress.connect(self._progress_advance)
            worker.signals.finished.connect(self.handle_matcher_results)

            # move download process to worker thread
            self.threadpool.start(worker)

    def handle_matcher_results(self):
        assert threading.current_thread() == threading.main_thread()
        if self.progress_bar:
            self.progress_bar.finish()
            self.setStatusMessage('')

        if self.gene_matcher.map_input_to_ncbi():
            self.download_gene_sets()
            self.update_info_box()
        else:
            # reset gene sets
            self.init_item_model()
            self.update_info_box()

    def on_gene_sets_download(self, result):
        # make sure this happens in the main thread.
        # Qt insists that widgets be created within the GUI(main) thread.
        assert threading.current_thread() == threading.main_thread()
        self.progress_bar.finish()
        self.setStatusMessage('')

        tax_id, sets = result
        self.set_hierarchy_model(self.hierarchy_widget,
                                 *hierarchy_tree(tax_id, sets))

        self.organism_select_combobox.setEnabled(True)  # re-enable combobox

        self.update_info_box()
        self.mapped_genes = self.gene_matcher.map_input_to_ncbi()
        self.workers = defaultdict(list)
        self.progress_bar_iterations = dict()

        for selected_hierarchy in [*self.get_hierarchies()]:
            gene_sets = geneset.load_gene_sets(selected_hierarchy)
            worker = Worker(get_collections,
                            gene_sets,
                            self.mapped_genes,
                            progress_callback=True,
                            partial_result=True)
            worker.signals.error.connect(self.handle_error)
            worker.signals.finished.connect(self.handle_worker_finished)
            worker.signals.progress.connect(self._progress_advance)
            worker.signals.partial_result.connect(self.populate_data_model)
            worker.setAutoDelete(False)

            self.workers[selected_hierarchy] = worker
            self.progress_bar_iterations[selected_hierarchy] = len(gene_sets)

    def handle_worker_finished(self):
        # We check if all workers have completed. If not, continue
        # dirty hax, is this ok?
        if self.progress_bar and self.progress_bar.widget.progressBarValue == 100:
            self.progress_bar.finish()
            self.setStatusMessage('')
            self.hierarchy_widget.setDisabled(False)

            # adjust column width
            for i in range(len(DATA_HEADER_LABELS) - 1):

                self.data_view.resizeColumnToContents(i)

            self.filter_proxy_model.setSourceModel(self.data_model)

    def populate_data_model(self, partial_result):
        assert threading.current_thread() == threading.main_thread()

        if partial_result:
            self.data_model.appendRow(partial_result)

    def set_hierarchy_model(self, model, tax_id, sets):
        # TODO: maybe optimize this code?
        for key, value in sets.items():
            item = QTreeWidgetItem(model, [key])
            item.setFlags(item.flags()
                          & (Qt.ItemIsUserCheckable | ~Qt.ItemIsSelectable
                             | Qt.ItemIsEnabled))
            # item.setDisabled(True)
            item.setData(0, Qt.CheckStateRole, Qt.Unchecked)
            item.setExpanded(True)
            item.tax_id = tax_id
            item.hierarchy = key

            if value:
                item.setFlags(item.flags() | Qt.ItemIsTristate)
                self.set_hierarchy_model(item, tax_id, value)
            else:
                if item.parent():
                    item.hierarchy = ((item.parent().hierarchy, key), tax_id)

            if not item.childCount() and not item.parent():
                item.hierarchy = ((key, ), tax_id)

    def download_gene_sets(self):
        tax_id = self._get_selected_organism()

        self.Error.clear()
        # do not allow user to change organism when download task is running
        self.organism_select_combobox.setEnabled(False)
        # reset hierarchy widget state
        self.hierarchy_widget.clear()
        # clear data view
        self.init_item_model()

        # get all gene sets for selected organism
        gene_sets = geneset.list_all(organism=tax_id)
        # init progress bar
        self.progress_bar = ProgressBar(self, iterations=len(gene_sets) * 100)
        # status message
        self.setStatusMessage('downloading sets')

        worker = Worker(download_gene_sets, gene_sets, progress_callback=True)
        worker.signals.progress.connect(self._progress_advance)
        worker.signals.result.connect(self.on_gene_sets_download)
        worker.signals.error.connect(self.handle_error)

        # move download process to worker thread
        self.threadpool.start(worker)

    def display_gene_sets(self):
        self.init_item_model()
        self.hierarchy_widget.setDisabled(True)

        only_selected_hier = [*self.get_hierarchies(only_selected=True)]

        # init progress bar
        iterations = sum([
            self.progress_bar_iterations[hier] for hier in only_selected_hier
        ])
        self.progress_bar = ProgressBar(self, iterations=iterations)
        self.setStatusMessage('displaying gene sets')

        if not only_selected_hier:
            self.progress_bar.finish()
            self.setStatusMessage('')
            self.hierarchy_widget.setDisabled(False)
            return

        for selected_hierarchy in only_selected_hier:
            self.threadpool.start(self.workers[selected_hierarchy])

    def handle_error(self, ex):
        self.progress_bar.finish()
        self.setStatusMessage('')

        if isinstance(ex, ConnectionError):
            self.organism_select_combobox.setEnabled(
                True)  # re-enable combobox
            self.Error.cant_reach_host()

        print(ex)

    def get_hierarchies(self, **kwargs):
        """ return selected hierarchy
        """
        only_selected = kwargs.get('only_selected', None)

        sets_to_display = list()

        if only_selected:
            iterator = QTreeWidgetItemIterator(self.hierarchy_widget,
                                               QTreeWidgetItemIterator.Checked)
        else:
            iterator = QTreeWidgetItemIterator(self.hierarchy_widget)

        while iterator.value():
            # note: if hierarchy value is not a tuple, then this is just top level qTreeWidgetItem that
            #       holds subcategories. We don't want to display all sets from category
            if type(iterator.value().hierarchy) is not str:

                if not only_selected:
                    sets_to_display.append(iterator.value().hierarchy)
                else:
                    if not iterator.value().isDisabled():
                        sets_to_display.append(iterator.value().hierarchy)

            iterator += 1

        return sets_to_display

    def commit(self):
        selection_model = self.data_view.selectionModel()

        if selection_model:
            # genes_from_set = selection_model.selectedRows(GENES)
            matched_genes = selection_model.selectedRows(MATCHED)

            if matched_genes and self.input_genes:
                genes = [
                    model_index.data(Qt.UserRole)
                    for model_index in matched_genes
                ]
                output_genes = [
                    gene_name for gene_name in list(set.union(*genes))
                ]
                input_to_ncbi = self.gene_matcher.map_input_to_ncbi()
                ncbi_to_input = {
                    ncbi_id: input_name
                    for input_name, ncbi_id in
                    self.gene_matcher.map_input_to_ncbi().items()
                }

                if self.use_attr_names:
                    selected = [
                        self.input_data.domain[ncbi_to_input[output_gene]]
                        for output_gene in output_genes
                    ]
                    domain = Domain(selected,
                                    self.input_data.domain.class_vars,
                                    self.input_data.domain.metas)
                    new_data = self.input_data.from_table(
                        domain, self.input_data)
                    self.Outputs.matched_genes.send(new_data)

                elif self.column_candidates:
                    column = self.column_candidates[self.gene_col_index]
                    selected_rows = []

                    for row_index, row in enumerate(self.input_data):
                        if str(row[column]) in input_to_ncbi.keys(
                        ) and input_to_ncbi[str(row[column])] in output_genes:
                            selected_rows.append(row_index)
                    if selected_rows:
                        selected = self.input_data[selected_rows]
                    else:
                        selected = None
                    self.Outputs.matched_genes.send(selected)

    def setup_gui(self):
        # control area
        info_box = vBox(self.controlArea, 'Input info')
        self.input_info = widgetLabel(info_box)

        organism_box = vBox(self.controlArea, 'Organisms')
        self.organism_select_combobox = comboBox(
            organism_box,
            self,
            'selected_organism',
            callback=self.on_input_option_change)

        # Selection of genes attribute
        box = widgetBox(self.controlArea, 'Gene attribute')
        self.gene_columns = itemmodels.VariableListModel(parent=self)
        self.gene_column_combobox = comboBox(
            box, self, 'gene_col_index', callback=self.on_input_option_change)
        self.gene_column_combobox.setModel(self.gene_columns)

        self.attr_names_checkbox = checkBox(
            box,
            self,
            'use_attr_names',
            'Use attribute names',
            disables=[(-1, self.gene_column_combobox)],
            callback=self.on_input_option_change)

        self.gene_column_combobox.setDisabled(bool(self.use_attr_names))

        hierarchy_box = widgetBox(self.controlArea, "Entity Sets")
        self.hierarchy_widget = QTreeWidget(self)
        self.hierarchy_widget.setEditTriggers(QTreeView.NoEditTriggers)
        self.hierarchy_widget.setHeaderLabels(HIERARCHY_HEADER_LABELS)
        self.hierarchy_widget.itemClicked.connect(self.display_gene_sets)
        hierarchy_box.layout().addWidget(self.hierarchy_widget)

        self.commit_button = auto_commit(self.controlArea,
                                         self,
                                         "auto_commit",
                                         "&Commit",
                                         box=False)

        # rubber(self.controlArea)

        # main area
        self.filter_proxy_model = QSortFilterProxyModel(self.data_view)
        self.filter_proxy_model.setFilterKeyColumn(3)

        self.data_view = QTreeView()
        self.data_view.setModel(self.filter_proxy_model)
        self.data_view.setAlternatingRowColors(True)
        self.data_view.setSortingEnabled(True)
        self.data_view.setSelectionMode(QTreeView.ExtendedSelection)
        self.data_view.setEditTriggers(QTreeView.NoEditTriggers)
        self.data_view.viewport().setMouseTracking(True)
        self.data_view.setItemDelegateForColumn(
            TERM, LinkStyledItemDelegate(self.data_view))

        self.data_view.selectionModel().selectionChanged.connect(self.commit)

        self.lineEdit_filter = lineEdit(self.mainArea, self, 'search_pattern',
                                        'Filter gene sets:')
        self.lineEdit_filter.setPlaceholderText('search pattern ...')
        self.lineEdit_filter.textChanged.connect(
            self.filter_proxy_model.setFilterRegExp)

        self.mainArea.layout().addWidget(self.data_view)

    def init_item_model(self):
        if self.data_model:
            self.data_model.clear()
            self.filter_proxy_model.setSourceModel(None)
        else:
            self.data_model = QStandardItemModel()

        self.data_model.setSortRole(Qt.UserRole)
        self.data_model.setHorizontalHeaderLabels(DATA_HEADER_LABELS)

    def sizeHint(self):
        return QSize(1280, 960)
예제 #15
0
    # Defining a couple of items
    americaItem = QStandardItem("America")
    canadaItem = QStandardItem("Canada")
    europeItem = QStandardItem("Europe")
    franceItem = QStandardItem("France")
    brittanyItem = QStandardItem("Brittany")

    # Building up the hierarchy
    rootItem.appendRow(americaItem)
    rootItem.appendRow(europeItem)
    americaItem.appendRow(canadaItem)
    europeItem.appendRow(franceItem)
    franceItem.appendRow(brittanyItem)

    tree_view.setModel(model)

    # Bind selection to the print_selection function (must be after the model setup)
    selection_model = tree_view.selectionModel()
    selection_model.selectionChanged.connect(print_selection)

    tree_view.expandAll()  # expand all (this is not the case by default)
    tree_view.show()

    # The mainloop of the application. The event handling starts from this point.
    # The exec_() method has an underscore. It is because the exec is a Python keyword. And thus, exec_() was used instead.
    exit_code = app.exec_()

    # The sys.exit() method ensures a clean exit.
    # The environment will be informed, how the application ended.
    sys.exit(exit_code)
예제 #16
0
class ZeroConfExplorer(QWidget):
    """
    create a zeroconf qgroubbox with a qlist view 
    """
    def __init__(self, name):
        super(ZeroConfExplorer, self).__init__()
        # init explorer to None
        self.oscquery_device = None
        if not name:
            name = 'OSCJSON thru TCP Explorer'
        # create the view
        self.explorer = QTreeView()
        # Hide Useless Header
        self.explorer.header().hide()
        self.panel = Panel()
        # create right-click menu
        self.explorer.setContextMenuPolicy(Qt.CustomContextMenu)
        self.explorer.customContextMenuRequested.connect(self.contextual_menu)
        # create the model
        self.devices_model = QStandardItemModel()
        # link model to the view
        self.explorer.setModel(self.devices_model)
        self.explorer.selectionModel().selectionChanged.connect(
            self.selection_updated)
        # set selection
        self.device_selection_model = self.explorer.selectionModel()
        # set layout and group
        Layout = QGridLayout()
        # add the view to the layout
        Layout.addWidget(self.explorer, 0, 0)
        Layout.addWidget(self.panel, 0, 1)
        # add the layout to the GroupBox
        self.setLayout(Layout)
        #self.setMinimumSize(300, 300)
        self.explorer.setFixedSize(300, 300)
        # start zeroconf services
        zeroconf = Zeroconf()
        # start the callback, it will create items
        listener = ZeroConfListener(self.devices_model)
        listener.add_device.connect(self.connect_device)
        browser = ServiceBrowser(zeroconf, "_oscjson._tcp.local.", listener)
        self.current_remote = None

    def connect_device(self, device):
        self.panel.device = device

    def contextual_menu(self, position):

        indexes = self.explorer.selectedIndexes()
        if len(indexes) > 0:

            level = 0
            index = indexes[0]
            while index.parent().isValid():
                index = index.parent()
                level += 1
            node = self.devices_model.itemFromIndex(index)
            menu = QMenu()
            if level == 0:
                menu.addAction("Refresh Device Namespace", node.update)
            elif level > 0:
                menu.addAction("Refresh Node", node.update)
            menu.exec_(self.explorer.viewport().mapToGlobal(position))

    def selection_updated(self, *args, **kwargs):
        """
        called when device selection is updated
        we will disconnect our ossia.OSCQueryDevice from the previous device if there was one
        and then we will reconnect it to the current instance of the Device model
        """
        index = self.device_selection_model.selectedIndexes()
        # we consider unique selection
        modelIndex = index[0]
        if modelIndex:
            if self.current_remote:
                self.panel.layout.removeWidget(self.current_remote)
                self.current_remote.deleteLater()
                del self.current_remote
                self.current_remote = None
            node = self.devices_model.itemFromIndex(modelIndex).node
            if node.__class__.__name__ == 'OSCQueryDevice':
                print('Device')
            else:
                if node.parameter:
                    self.current_remote = self.panel.add_remote(node.parameter)
                else:
                    self.current_remote = self.panel.add_inspector(node)
        else:
            print('no node selected')
예제 #17
0
파일: __init__.py 프로젝트: Zubax/kucher
class RegisterViewWidget(WidgetBase):
    def __init__(self, parent: QWidget):
        super(RegisterViewWidget, self).__init__(parent)

        self._registers = []
        self._running_task: asyncio.Task = None

        self._visibility_selector = QComboBox(self)
        self._visibility_selector.addItem("Show all registers", lambda _: True)
        self._visibility_selector.addItem("Only configuration parameters",
                                          lambda r: r.mutable and r.persistent)

        # noinspection PyUnresolvedReferences
        self._visibility_selector.currentIndexChanged.connect(
            lambda _: self._on_visibility_changed())

        self._reset_selected_button = make_button(
            self,
            "Reset selected",
            icon_name="clear-symbol",
            tool_tip=f"Reset the currently selected registers to their default "
            f"values. The restored values will be committed "
            f"immediately. This function is available only if a "
            f"default value is defined. [{RESET_SELECTED_SHORTCUT}]",
            on_clicked=self._do_reset_selected,
        )

        self._reset_all_button = make_button(
            self,
            "Reset all",
            icon_name="skull-crossbones",
            tool_tip=f"Reset the all registers to their default "
            f"values. The restored values will be committed "
            f"immediately.",
            on_clicked=self._do_reset_all,
        )

        self._read_selected_button = make_button(
            self,
            "Read selected",
            icon_name="process",
            tool_tip=f"Read the currently selected registers only "
            f"[{READ_SELECTED_SHORTCUT}]",
            on_clicked=self._do_read_selected,
        )

        self._read_all_button = make_button(
            self,
            "Read all",
            icon_name="process-plus",
            tool_tip="Read all registers from the device",
            on_clicked=self._do_read_all,
        )

        self._export_button = make_button(
            self,
            "Export",
            icon_name="export",
            tool_tip="Export configuration parameters",
            on_clicked=self._do_export,
        )

        self._import_button = make_button(
            self,
            "Import",
            icon_name="import",
            tool_tip="Import configuration parameters",
            on_clicked=self._do_import,
        )

        self._expand_all_button = make_button(
            self,
            "",
            icon_name="expand-arrow",
            tool_tip="Expand all namespaces",
            on_clicked=lambda: self._tree.expandAll(),
        )

        self._collapse_all_button = make_button(
            self,
            "",
            icon_name="collapse-arrow",
            tool_tip="Collapse all namespaces",
            on_clicked=lambda: self._tree.collapseAll(),
        )

        self._status_display = QLabel(self)
        self._status_display.setWordWrap(True)

        self._reset_selected_button.setEnabled(False)
        self._reset_all_button.setEnabled(False)
        self._read_selected_button.setEnabled(False)
        self._read_all_button.setEnabled(False)
        self._export_button.setEnabled(False)
        self._import_button.setEnabled(False)

        self._tree = QTreeView(self)
        self._tree.setVerticalScrollMode(QTreeView.ScrollPerPixel)
        self._tree.setHorizontalScrollMode(QTreeView.ScrollPerPixel)
        self._tree.setAnimated(True)
        self._tree.setSelectionMode(QAbstractItemView.ExtendedSelection)
        self._tree.setAlternatingRowColors(True)
        self._tree.setContextMenuPolicy(Qt.ActionsContextMenu)

        # Not sure about this one. This hardcoded value may look bad on some platforms.
        self._tree.setIndentation(20)

        def add_action(
            callback: typing.Callable[[], None],
            icon_name: str,
            name: str,
            shortcut: typing.Optional[str] = None,
        ):
            action = QAction(get_icon(icon_name), name, self)
            # noinspection PyUnresolvedReferences
            action.triggered.connect(callback)
            if shortcut:
                action.setShortcut(shortcut)
                action.setAutoRepeat(False)
                try:
                    action.setShortcutVisibleInContextMenu(True)
                except AttributeError:
                    pass  # This feature is not available in PyQt before 5.10

            self._tree.addAction(action)

        add_action(self._do_read_all, "process-plus", "Read all registers")
        add_action(
            self._do_read_selected,
            "process",
            "Read selected registers",
            READ_SELECTED_SHORTCUT,
        )
        add_action(
            self._do_reset_selected,
            "clear-symbol",
            "Reset selected to default",
            RESET_SELECTED_SHORTCUT,
        )

        self._tree.setItemDelegateForColumn(
            int(Model.ColumnIndices.VALUE),
            EditorDelegate(self._tree, self._display_status),
        )

        # It doesn't seem to be explicitly documented, but it seems to be necessary to select either top or bottom
        # decoration position in order to be able to use center alignment. Left or right positions do not work here.
        self._tree.setItemDelegateForColumn(
            int(Model.ColumnIndices.FLAGS),
            StyleOptionModifyingDelegate(
                self._tree,
                decoration_position=QStyleOptionViewItem.Top,  # Important
                decoration_alignment=Qt.AlignCenter,
            ),
        )

        header: QHeaderView = self._tree.header()
        header.setSectionResizeMode(QHeaderView.ResizeToContents)
        header.setStretchLastSection(
            False)  # Horizontal scroll bar doesn't work if this is enabled

        buttons_layout = QGridLayout()
        buttons_layout.addWidget(self._read_selected_button, 0, 0)
        buttons_layout.addWidget(self._reset_selected_button, 0, 2)
        buttons_layout.addWidget(self._read_all_button, 1, 0)
        buttons_layout.addWidget(self._reset_all_button, 1, 2)
        buttons_layout.addWidget(self._import_button, 2, 0)
        buttons_layout.addWidget(self._export_button, 2, 2)

        for col in range(3):
            buttons_layout.setColumnStretch(col, 1)

        layout = lay_out_vertically(
            (self._tree, 1),
            buttons_layout,
            lay_out_horizontally(
                self._visibility_selector,
                (None, 1),
                self._expand_all_button,
                self._collapse_all_button,
            ),
            self._status_display,
        )

        self.setLayout(layout)

    def reset(self):
        self.setup([])

    def setup(self, registers: typing.Iterable[Register]):
        self._registers = list(registers)
        self._on_visibility_changed()

    def _replace_model(
            self, register_visibility_predicate: typing.Callable[[Register],
                                                                 bool]):
        # Cancel all operations that might be pending on the old model
        self._cancel_task()

        old_model = self._tree.model()

        # Configure the new model
        filtered_registers = list(
            filter(register_visibility_predicate, self._registers))
        # It is important to set the Tree widget as the parent in order to let the widget take ownership
        new_model = Model(self._tree, filtered_registers)
        _logger.info("New model %r", new_model)
        self._tree.setModel(new_model)

        # The selection model is implicitly replaced when we replace the model, so it has to be reconfigured
        self._tree.selectionModel().selectionChanged.connect(
            lambda *_: self._on_selection_changed())

        # TODO: Something fishy is going on. Something keeps the old model alive when we're replacing it.
        #       We could call deleteLater() on it, but it seems dangerous, because if that something ever decided
        #       to refer to that dead model later for any reason, we'll get a rougue dangling pointer access on
        #       our hands. The horror!
        if old_model is not None:
            import gc

            model_referrers = gc.get_referrers(old_model)
            if len(model_referrers) > 1:
                _logger.warning(
                    "Extra references to the old model %r: %r",
                    old_model,
                    model_referrers,
                )

        # Update the widget - all root items are expanded by default
        for row in itertools.count():
            index = self._tree.model().index(row, 0)
            if not index.isValid():
                break

            self._tree.expand(index)

        self._reset_selected_button.setEnabled(False)
        self._read_selected_button.setEnabled(False)
        self._read_all_button.setEnabled(len(filtered_registers) > 0)
        self._reset_all_button.setEnabled(len(filtered_registers) > 0)
        self._export_button.setEnabled(len(filtered_registers) > 0)
        self._import_button.setEnabled(len(filtered_registers) > 0)

        self._display_status(f"{len(filtered_registers)} registers loaded")

    def _on_visibility_changed(self):
        self._replace_model(self._visibility_selector.currentData())

    def _on_selection_changed(self):
        selected = self._get_selected_registers()

        self._reset_selected_button.setEnabled(
            any(map(lambda r: r.has_default_value, selected)))
        self._read_selected_button.setEnabled(len(selected) > 0)

    def _do_read_selected(self):
        selected = self._get_selected_registers()
        if selected:
            self._read_specific(selected)
        else:
            self._display_status("No registers are selected, nothing to read")

    def _do_reset_selected(self):
        rv = {}
        for r in self._get_selected_registers():
            if r.has_default_value:
                rv[r] = r.default_value

        self._write_specific(rv)

    def _do_reset_all(self):
        rv = {}
        for r in self._registers:
            if r.has_default_value:
                rv[r] = r.default_value

        self._write_specific(rv)

    def _do_read_all(self):
        self._read_specific(self._tree.model().registers)

    def _do_import(self):
        import_registers(parent=self, registers=self._registers)

    def _do_export(self):
        export_registers(parent=self, registers=self._registers)

    def _read_specific(self, registers: typing.List[Register]):
        total_registers_read = None

        def progress_callback(register: Register, current_register_index: int,
                              total_registers: int):
            nonlocal total_registers_read
            total_registers_read = total_registers
            self._display_status(
                f"Reading {register.name!r} "
                f"({current_register_index + 1} of {total_registers})")

        async def executor():
            try:
                _logger.info("Reading registers: %r",
                             [r.name for r in registers])
                mod: Model = self._tree.model()
                await mod.read(registers=registers,
                               progress_callback=progress_callback)
            except asyncio.CancelledError:
                self._display_status(f"Read has been cancelled")
                raise
            except Exception as ex:
                _logger.exception("Register read failed")
                show_error("Read failed", "Could not read registers", repr(ex),
                           self)
                self._display_status(f"Could not read registers: {ex!r}")
            else:
                self._display_status(
                    f"{total_registers_read} registers have been read")

        self._cancel_task()
        self._running_task = asyncio.get_event_loop().create_task(executor())

    def _write_specific(self, register_value_mapping: typing.Dict[Register,
                                                                  typing.Any]):
        total_registers_assigned = None

        def progress_callback(register: Register, current_register_index: int,
                              total_registers: int):
            nonlocal total_registers_assigned
            total_registers_assigned = total_registers
            self._display_status(
                f"Writing {register.name!r} "
                f"({current_register_index + 1} of {total_registers})")

        async def executor():
            try:
                _logger.info(
                    "Writing registers: %r",
                    [r.name for r in register_value_mapping.keys()],
                )
                mod: Model = self._tree.model()
                await mod.write(
                    register_value_mapping=register_value_mapping,
                    progress_callback=progress_callback,
                )
            except asyncio.CancelledError:
                self._display_status(f"Write has been cancelled")
                raise
            except Exception as ex:
                _logger.exception("Register write failed")
                show_error("Write failed", "Could not read registers",
                           repr(ex), self)
                self._display_status(f"Could not write registers: {ex!r}")
            else:
                self._display_status(
                    f"{total_registers_assigned} registers have been written")

        self._cancel_task()
        self._running_task = asyncio.get_event_loop().create_task(executor())

    def _get_selected_registers(self) -> typing.List[Register]:
        selected_indexes: typing.List[
            QModelIndex] = self._tree.selectedIndexes()
        selected_registers = set()
        for si in selected_indexes:
            r = Model.get_register_from_index(si)
            if r is not None:
                selected_registers.add(r)
        # Beware that sets are not sorted, this may lead to weird user experience when watching the registers
        # read in a funny order.
        return list(sorted(selected_registers, key=lambda x: x.name))

    def _cancel_task(self):
        # noinspection PyBroadException
        try:
            self._running_task.cancel()
        except Exception:
            pass
        else:
            _logger.info("A running task had to be cancelled: %r",
                         self._running_task)
        finally:
            self._running_task = None

    def _display_status(self, text=None):
        self._status_display.setText(text)
예제 #18
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)
예제 #19
0
class PyCommonist(QWidget):

    tool = None

    def __init__(self):
        super(PyCommonist, self).__init__()
        self.initUI()
        self.threads = []
        self.workers = []

    def initUI(self):

        self.currentDirectoryPath = ""

        self.generateSplitter()
        self.generateLeftTopFrame()
        self.generateLeftBottomFrame()

        self.showMaximized()
        self.setWindowTitle("PyCommonist - Wikimedia Commons")
        self.show()

    """
        onSelectFolder
    """

    def onSelectFolder(self, selected, deselected):

        try:
            currentIndex = selected.indexes()[0]
            self.currentDirectoryPath = self.modelTree.filePath(currentIndex)
            print(self.currentDirectoryPath)
            self.statusBar.setText("")
            self.exifImageCollection = []

            list_dir = os.listdir(self.currentDirectoryPath)
            files = [
                f for f in sorted(list_dir)
                if isfile(join(self.currentDirectoryPath, f))
            ]
            for file in files:
                fullFilePath = os.path.join(self.currentDirectoryPath, file)
                if fullFilePath.endswith(".jpeg") or fullFilePath.endswith(
                        ".jpg") or fullFilePath.endswith(".png"):

                    currentExifImage = EXIFImage()
                    currentExifImage.fullFilePath = fullFilePath
                    currentExifImage.filename = file
                    tags = None

                    try:
                        """ EXIF """
                        f_exif = open(fullFilePath, "rb")
                        tags = exifread.process_file(f_exif)
                        # print(tags)
                    except:
                        print("A problem with EXIF data reading")
                    """ Location"""
                    # 'GPS GPSLatitude', 'GPS GPSLongitude'] # [45, 49, 339/25] [4, 55, 716/25]
                    # 'GPS GPSImgDirection' 'GPS GPSLatitudeRef'
                    lat = ""
                    long = ""
                    heading = ""
                    try:
                        currentExifImage.lat, currentExifImage.long, currentExifImage.heading = get_exif_location(
                            tags)
                    except:
                        print("A problem with EXIF data reading")

                    dt = None
                    try:
                        """ Date Time """
                        dt = tags[
                            "EXIF DateTimeOriginal"]  # 2021:01:13 14:48:44
                    except:
                        print("A problem with EXIF data reading")

                    print(dt)
                    dt = str(dt)
                    indexSpace = dt.find(" ")
                    currentExifImage.date = dt[0:indexSpace].replace(":", "-")
                    currentExifImage.time = dt[indexSpace + 1:]

                    self.exifImageCollection.append(currentExifImage)
                    print(currentExifImage)

            self.generateRightFrame()

        except:
            print("Something bad happened inside onSelectFolder function")
            traceback.print_exc()

    """
        cbImportNoneStateChanged
    """

    def cbImportNoneStateChanged(self):

        print(self.cbImportNone.isChecked())
        print(len(self._currentUpload))

        if self.cbImportNone.isChecked() and len(self._currentUpload) > 0:

            for element in self._currentUpload:
                element.cbImport.setCheckState(False)

    """
        cbImportAllStateChanged
    """

    def cbImportAllStateChanged(self):

        print(self.cbImportAll.isChecked())
        print(len(self._currentUpload))
        if self.cbImportAll.isChecked() and len(self._currentUpload) > 0:

            for element in self._currentUpload:
                element.cbImport.setCheckState(True)

    """
        onClickImport
    """

    def onClickImport(self):
        if self.tool == None:
            self.tool = UploadTool()
        ret = self.tool.uploadImages(self)

    """
        cleanThreads
    """

    def cleanThreads(self):
        try:
            print("Clean properly threads")

            for thread in self.threads:
                print("Current thread proper deletion")
                thread.quit()
                thread.wait()

        except:
            print("A problem with cleanThreads")

    """
        generateSplitter
    """

    def generateSplitter(self):

        vbox = QVBoxLayout()
        hbox = QHBoxLayout()

        self.leftTopFrame = QFrame()
        self.leftTopFrame.setFrameShape(QFrame.StyledPanel)

        self.rightWidget = QWidget()
        self.rightWidget.resize(300, 300)
        self.layoutRight = QVBoxLayout()
        self.rightWidget.setLayout(self.layoutRight)

        self.scroll = QScrollArea()
        self.layoutRight.addWidget(self.scroll)
        self.scroll.setWidgetResizable(True)
        self.scrollContent = QWidget(self.scroll)

        self.scrollLayout = QVBoxLayout(self.scrollContent)
        self.scrollContent.setLayout(self.scrollLayout)
        self.scroll.setWidget(self.scrollContent)

        self.splitterLeft = QSplitter(Qt.Vertical)
        self.leftBottonFrame = QFrame()
        self.leftBottonFrame.setFrameShape(QFrame.StyledPanel)

        self.splitterLeft.addWidget(self.leftTopFrame)
        self.splitterLeft.addWidget(self.leftBottonFrame)
        self.splitterLeft.setSizes([VERTICAL_TOP_SIZE, VERTICAL_BOTTOM_SIZE])
        """ Horizontal Splitter """
        self.splitterCentral = QSplitter(Qt.Horizontal)
        self.splitterCentral.addWidget(self.splitterLeft)
        self.splitterCentral.addWidget(self.rightWidget)
        self.splitterCentral.setSizes(
            [HORIZONTAL_LEFT_SIZE, HORIZONTAL_RIGHT_SIZE])
        hbox.addWidget(self.splitterCentral)

        vbox.addLayout(hbox)
        """ Status Bar """
        self.statusBar = QLabel()
        self.statusBar.setStyleSheet(STYLE_STATUSBAR)
        vbox.addWidget(self.statusBar)

        self.setLayout(vbox)

    """
        generateLeftTopFrame
    """

    def generateLeftTopFrame(self):

        self.layoutLeftTop = QFormLayout()
        self.layoutLeftTop.setFormAlignment(Qt.AlignTop)

        self.lblUserName = QLabel("Username: "******"Password: "******"Source: ")
        self.lblSource.setAlignment(Qt.AlignLeft)
        self.lineEditSource = QLineEdit()
        self.lineEditSource.setFixedWidth(WIDTH_WIDGET)
        self.lineEditSource.setText(LeftFrameConfig.source)
        self.lineEditSource.setAlignment(Qt.AlignLeft)
        self.layoutLeftTop.addRow(self.lblSource, self.lineEditSource)

        self.lblAuthor = QLabel("Author: ")
        self.lblAuthor.setAlignment(Qt.AlignLeft)
        self.lineEditAuthor = QLineEdit()
        self.lineEditAuthor.setFixedWidth(WIDTH_WIDGET)
        self.lineEditAuthor.setText(LeftFrameConfig.author)
        self.lineEditAuthor.setAlignment(Qt.AlignLeft)
        self.layoutLeftTop.addRow(self.lblAuthor, self.lineEditAuthor)

        self.lblCategories = QLabel("Categories: ")
        self.lblCategories.setAlignment(Qt.AlignLeft)
        self.lineEditCategories = QLineEdit()
        self.lineEditCategories.setText(LeftFrameConfig.categories)
        self.lineEditCategories.setFixedWidth(WIDTH_WIDGET)
        self.lineEditCategories.setAlignment(Qt.AlignLeft)
        self.layoutLeftTop.addRow(self.lblCategories, self.lineEditCategories)

        self.lblLicense = QLabel("License: ")
        self.lblLicense.setAlignment(Qt.AlignLeft)
        self.lineEditLicense = QLineEdit()
        self.lineEditLicense.setFixedWidth(WIDTH_WIDGET)
        self.lineEditLicense.setText(LeftFrameConfig.licence)
        self.lineEditLicense.setAlignment(Qt.AlignLeft)
        self.layoutLeftTop.addRow(self.lblLicense, self.lineEditLicense)

        self.lblDescription = QLabel("Description: ")
        self.lblDescription.setAlignment(Qt.AlignLeft)
        self.lineEditDescription = QPlainTextEdit()
        self.lineEditDescription.setFixedWidth(WIDTH_WIDGET)
        self.layoutLeftTop.addRow(self.lblDescription,
                                  self.lineEditDescription)

        separationLeftTopFrame = QLabel()
        self.layoutLeftTop.addWidget(separationLeftTopFrame)
        """ Button Import & None/All checkboxes"""
        importWidget = QWidget()
        importLayout = QHBoxLayout()
        importWidget.setLayout(importLayout)

        self.cbImportNone = QCheckBox("None")
        self.cbImportNone.stateChanged.connect(self.cbImportNoneStateChanged)

        self.cbImportAll = QCheckBox("All")
        self.cbImportAll.stateChanged.connect(self.cbImportAllStateChanged)

        self.btnImport = QPushButton("Import!")

        self.btnImport.clicked.connect(self.onClickImport)

        importLayout.addWidget(self.cbImportNone)
        importLayout.addWidget(self.cbImportAll)
        importLayout.addWidget(self.btnImport)
        self.layoutLeftTop.addWidget(importWidget)
        importWidget.setStyleSheet("border:1px solid #808080;")
        self.cbImportNone.setStyleSheet("border:0px;")
        self.cbImportAll.setStyleSheet("border:0px;")

        self.btnImport.setStyleSheet(STYLE_IMPORT_BUTTON)
        """ Layout Association of the Left Top Frame"""
        self.leftTopFrame.setLayout(self.layoutLeftTop)

    """
        generateLeftBottomFrame
    """

    def generateLeftBottomFrame(self):

        self.layoutLeftBottom = QVBoxLayout()
        """Model for QTreeView"""
        self.modelTree = QFileSystemModel()
        self.modelTree.setRootPath(QDir.currentPath())
        self.modelTree.setFilter(QDir.Dirs)  # Only directories
        """ QTreeView """
        self.treeLeftBottom = QTreeView()
        self.treeLeftBottom.setModel(self.modelTree)
        self.treeLeftBottom.setAnimated(False)
        self.treeLeftBottom.setIndentation(10)
        self.treeLeftBottom.setColumnWidth(0, 300)
        self.treeLeftBottom.expandAll()
        self.treeLeftBottom.selectionModel().selectionChanged.connect(
            self.onSelectFolder)
        self.layoutLeftBottom.addWidget(self.treeLeftBottom)
        self.leftBottonFrame.setLayout(self.layoutLeftBottom)

    """
        generateRightFrame
    """

    def generateRightFrame(self):

        self._currentUpload = []

        layout = self.scrollLayout
        print(layout)
        print(layout.count())

        while layout.count():
            print("destroy")
            child = layout.takeAt(0)
            if child.widget():
                child.widget().deleteLater()

        for currentExifImage in self.exifImageCollection:
            """ Current image """
            localWidget = ImageUpload()
            localLayout = QHBoxLayout()
            localLayout.setAlignment(Qt.AlignRight)
            localWidget.setLayout(localLayout)
            self.scrollLayout.addWidget(localWidget)
            self._currentUpload.append(localWidget)
            """Local Left Widget"""
            localLeftWidget = QWidget()
            localLeftLayout = QFormLayout()
            localLeftLayout.setAlignment(Qt.AlignRight)
            localLeftWidget.setLayout(localLeftLayout)
            localLayout.addWidget(localLeftWidget)
            """ import? + Import Status """
            cbImport = QCheckBox("Import")
            lblUploadResult = QLabel()
            lblUploadResult.setStyleSheet(STYLE_IMPORT_STATUS)
            localLeftLayout.addRow(cbImport, lblUploadResult)
            localWidget.cbImport = cbImport
            localWidget.lblUploadResult = lblUploadResult
            """ File Name of picture """
            lblFileName = QLabel("Name: ")
            lblFileName.setAlignment(Qt.AlignLeft)
            lineEditFileName = QLineEdit()
            lineEditFileName.setFixedWidth(WIDTH_WIDGET_RIGHT)
            lineEditFileName.setText(currentExifImage.filename)
            lineEditFileName.setAlignment(Qt.AlignLeft)
            localLeftLayout.addRow(lblFileName, lineEditFileName)
            localWidget.lineEditFileName = lineEditFileName
            """ Shadow Real FileName """
            lblRealFileName = QLineEdit()
            lblRealFileName.setText(currentExifImage.filename)
            localWidget.lblRealFileName = lblRealFileName
            localWidget.lblRealFileName.isVisible = False
            """ Description """
            lblDescription = QLabel("Description: ")
            lblDescription.setAlignment(Qt.AlignLeft)
            lineEditDescription = QPlainTextEdit()
            lineEditDescription.setFixedWidth(WIDTH_WIDGET_RIGHT)
            localLeftLayout.addRow(lblDescription, lineEditDescription)
            localWidget.lineEditDescription = lineEditDescription
            """ Categories """
            lblCategories = QLabel("Categories: ")
            lblCategories.setAlignment(Qt.AlignLeft)
            lineEditCategories = QLineEdit()
            lineEditCategories.setFixedWidth(WIDTH_WIDGET_RIGHT)
            lineEditCategories.setAlignment(Qt.AlignLeft)
            localLeftLayout.addRow(lblCategories, lineEditCategories)
            localWidget.lineEditCategories = lineEditCategories

            lblLocation = QLabel("Location: ")
            lblLocation.setAlignment(Qt.AlignLeft)
            lineEditLocation = QLineEdit()
            lineEditLocation.setFixedWidth(WIDTH_WIDGET_RIGHT)
            if currentExifImage.lat == None or currentExifImage.long == None:
                lineEditLocation.setText("")
            else:
                lineEditLocation.setText(
                    str(currentExifImage.lat) + "|" +
                    str(currentExifImage.long) + "|heading:" +
                    str(currentExifImage.heading))
            lineEditLocation.setAlignment(Qt.AlignLeft)
            localLeftLayout.addRow(lblLocation, lineEditLocation)
            localWidget.lineEditLocation = lineEditLocation

            lblDateTime = QLabel("Date Time: ")
            lblDateTime.setAlignment(Qt.AlignLeft)
            lineEditDateTime = QLineEdit()
            lineEditDateTime.setFixedWidth(WIDTH_WIDGET_RIGHT)
            lineEditDateTime.setText(currentExifImage.date + " " +
                                     currentExifImage.time)
            lineEditDateTime.setAlignment(Qt.AlignLeft)
            localLeftLayout.addRow(lblDateTime, lineEditDateTime)
            localWidget.lineEditDateTime = lineEditDateTime
            """ Image itself """
            label = QLabel()
            pixmap = QPixmap(currentExifImage.fullFilePath)
            pixmapResize = pixmap.scaledToWidth(IMAGE_DIMENSION,
                                                Qt.FastTransformation)
            label.setPixmap(pixmapResize)
            localLayout.addWidget(label)
            localWidget.fullFilePath = currentExifImage.fullFilePath

            self.update()
    # Defining a couple of items
    americaItem = QStandardItem("America")
    canadaItem = QStandardItem("Canada")
    europeItem = QStandardItem("Europe")
    franceItem = QStandardItem("France")
    brittanyItem = QStandardItem("Brittany")

    # Building up the hierarchy
    rootItem.appendRow(americaItem)
    rootItem.appendRow(europeItem)
    americaItem.appendRow(canadaItem)
    europeItem.appendRow(franceItem)
    franceItem.appendRow(brittanyItem)

    tree_view.setModel(model)

    # Bind selection to the print_selection function (must be after the model setup)
    selection_model = tree_view.selectionModel()
    selection_model.selectionChanged.connect(print_selection)

    tree_view.expandAll()      # expand all (this is not the case by default)
    tree_view.show()

    # The mainloop of the application. The event handling starts from this point.
    # The exec_() method has an underscore. It is because the exec is a Python keyword. And thus, exec_() was used instead.
    exit_code = app.exec_()

    # The sys.exit() method ensures a clean exit.
    # The environment will be informed, how the application ended.
    sys.exit(exit_code)
예제 #21
0
class MyMainWindow(QMainWindow):

    def __init__(self, parent=None):
        QMainWindow.__init__(self, parent)
        self.config_window()
        self.create_widgets()
        self.config_widgets()
        self.create_menubar()
        self.bind_widgets()
        self.show_widgets()

    def config_window(self):
        self.setWindowTitle('DirectoPy')
        self.setMinimumHeight(600)
        self.setMinimumWidth(1000)

    def create_widgets(self):
        self.central_widget = QWidget()
        self.main_layout = QGridLayout()
        self.moveup_button = QPushButton('Collapse all', self)
        self.goto_lineedit = QLineEdit('', self)
        self.goto_button = QPushButton('Go', self)
        self.folder_view = QTreeView(self)
        self.file_view = QTreeView(self)
        self.folder_model = QFileSystemModel(self)
        self.file_model = QFileSystemModel(self)

    def config_widgets(self):
        self.main_layout.addWidget(self.moveup_button, 0, 0)
        self.main_layout.addWidget(self.goto_lineedit, 0, 1, 1, 2)
        self.main_layout.addWidget(self.goto_button, 0, 3)
        self.main_layout.addWidget(self.folder_view, 1, 0, 1, 2)
        self.main_layout.addWidget(self.file_view, 1, 2, 1, 2)

        self.central_widget.setLayout(self.main_layout)

        # Кнопка "вверх"
        self.moveup_button.setMaximumWidth(100)

        # Кнопка "перейти"
        self.goto_button.setMaximumWidth(70)
        self.setCentralWidget(self.central_widget)
        # панели
        self.folder_model.setRootPath(None)
        self.folder_model.setFilter(QDir.AllDirs | QDir.NoDotAndDotDot)
        self.folder_view.setModel(self.folder_model)
        self.folder_view.setRootIndex(self.folder_model.index(None))
        self.folder_view.clicked[QModelIndex].connect(self.clicked_onfolder)
        self.folder_view.hideColumn(1)
        self.folder_view.hideColumn(2)
        self.folder_view.hideColumn(3)

        self.file_model.setFilter(QDir.Files)
        self.file_view.setModel(self.file_model)
        self.file_model.setReadOnly(False)
        self.file_view.setColumnWidth(0, 200)
        self.file_view.setSelectionMode(QAbstractItemView.ExtendedSelection)

    # открытие папки при нажати на неё в окне папок
    def clicked_onfolder(self, index):
        selection_model = self.folder_view.selectionModel()
        index = selection_model.currentIndex()
        dir_path = self.folder_model.filePath(index)
        self.file_model.setRootPath(dir_path)
        self.file_view.setRootIndex(self.file_model.index(dir_path))

    # ф-я открытия нового файла
    def open_file(self):
        index = self.file_view.selectedIndexes()
        if not index:
            return
        else:
            index = index[0]
        file_path = self.file_model.filePath(index).replace('/', '\\')
        print(file_path)
        self.file_view.update()

    # ф-я создания нового файла
    def new_file(self):
        global file_name
        index = self.folder_view.selectedIndexes()
        if len(index) > 0:
            path = self.folder_model.filePath(index[0])
            for i in range(1, 9999999999999999):
                if not os.path.isfile(os.path.join(path, "newfile{}.txt".format(i))):
                    file_name = os.path.join(path, "newfile{}.txt".format(i))
                    break
            file_name = os.path.abspath(file_name)
            open(file_name, 'w').close()
        else:
            print("Please, select folder")

    # ф-я удаления файла
    def delete_file(self):
        indexes = self.file_view.selectedIndexes()
        for i in indexes:
            self.file_model.remove(i)

    # ф-я переименования файла
    def rename_file(self):
        index = self.file_view.selectedIndexes()
        if not index:
            return
        else:
            index = index[0]
        self.file_view.edit(index)

    # ф-я копирования файла
    def copy_file(self):
        print("COPY")
        ask = QFileDialog.getExistingDirectory(self, "Open Directory", "C:\\",
                                               QFileDialog.ShowDirsOnly |
                                               QFileDialog.DontResolveSymlinks)
        new_path = ask.replace('\\', '/')
        indexes = self.file_view.selectedIndexes()[::4]
        for i in indexes:
            new_filename = new_path + '/' + self.file_model.fileName(i)
            copy2(self.file_model.filePath(i), new_filename)

    # ф-я возвращения к корню пути
    def colapse(self):
        self.folder_view.collapseAll()

    # ф-я перемещения в заданную директорию
    def go_to(self):
        dir_path = self.goto_lineedit.text().replace('\\', '/')
        print(dir_path)
        self.file_model.setRootPath(dir_path)
        self.file_view.setRootIndex(self.file_model.index(dir_path))

        #self.file_model.setRootPath()

    # ф-я перемещения файла
    def move_file(self):
        print("MOVE")
        ask = QFileDialog.getExistingDirectory(self, "Open Directory", "C:\\",
                                               QFileDialog.ShowDirsOnly |
                                               QFileDialog.DontResolveSymlinks)
        if ask == '':
            return
        new_path = ask.replace('\\', '/')
        indexes = self.file_view.selectedIndexes()[::4]
        for i in indexes:
            new_filename = new_path + '/' + self.file_model.fileName(i)
            move(self.file_model.filePath(i), new_filename)

    # ф-я создания новой папки
    def new_folder(self):
        global file_name
        index = self.folder_view.selectedIndexes()
        if len(index) > 0:
            path = self.folder_model.filePath(index[0])
            for i in range(1, 9999999999999999):
                if not os.path.isdir(os.path.join(path, "newfolder{}".format(i))):
                    file_name = os.path.join(path, "newfolder{}".format(i))
                    break
            file_name = os.path.abspath(file_name)
            os.mkdir(file_name)
        else:
            print("Please, select folder")

    # ф-я удаления папки
    def delete_folder(self):
        indexes = self.folder_view.selectedIndexes()
        for i in indexes:
            self.folder_model.remove(i)

    # ф-я переименования папки
    def rename_folder(self):
        index = self.folder_view.selectedIndexes()
        if not index:
            return
        else:
            index = index[0]
        self.folder_view.edit(index)

    # ф-я закрытия окна файлового менеджера
    def exit_application(self):
       print("EXIT")
       self.close()

    # задавание функции каждой кнопке
    def bind_widgets(self):
        self.open_file_action.triggered.connect(self.open_file)
        self.new_file_action.triggered.connect(self.new_file)
        self.delete_file_action.triggered.connect(self.delete_file)
        self.rename_file_action.triggered.connect(self.rename_file)
        self.copy_file_action.triggered.connect(self.copy_file)
        self.move_file_action.triggered.connect(self.move_file)
        self.exit_action.triggered.connect(self.exit_application)
        self.new_folder_action.triggered.connect(self.new_folder)
        self.delete_folder_action.triggered.connect(self.delete_folder)
        self.rename_folder_action.triggered.connect(self.rename_folder)


        self.goto_button.clicked.connect(partial(self.go_to))
        self.moveup_button.clicked.connect(partial(self.colapse))

    # создание меню
    def create_menubar(self):

        self.exit_action = QAction('Exit', self)
        self.exit_action.setShortcut('Ctrl+Q')

        self.new_file_action = QAction('New file', self)
        self.new_file_action.setShortcut('F4')

        self.open_file_action = QAction('Open file', self)
        self.open_file_action.setShortcut('F3')

        self.rename_file_action = QAction('Rename file', self)
        self.rename_file_action.setShortcut('F2')

        self.delete_file_action = QAction('Remove file', self)
        self.delete_file_action.setShortcut(QKeySequence.Delete)

        self.copy_file_action = QAction('Copy folder...', self)
        self.copy_file_action.setShortcut(QKeySequence.Copy)

        self.move_file_action = QAction('Move folder...', self)
        self.move_file_action.setShortcut(QKeySequence.Cut)

        self.new_folder_action = QAction('New folder', self)
        self.new_folder_action.setShortcut('Ctrl+Shift+N')

        self.delete_folder_action = QAction('Delete folder', self)
        self.delete_folder_action.setShortcut('Ctrl+Shift+Del')

        self.rename_folder_action = QAction('Rename folder', self)
        self.rename_folder_action.setShortcut('Ctrl+Shift+R')

        self.menubar = self.menuBar()
        self.file_menu = self.menubar.addMenu('File')
        self.file_menu.addAction(self.new_file_action)
        self.file_menu.addAction(self.open_file_action)
        self.file_menu.addAction(self.rename_file_action)
        self.file_menu.addAction(self.delete_file_action)
        self.file_menu.addAction(self.copy_file_action)
        self.file_menu.addAction(self.move_file_action)
        self.file_menu.addSeparator()
        self.file_menu.addAction(self.exit_action)

        self.folder_menu = self.menubar.addMenu('Folder')
        self.folder_menu.addAction(self.new_folder_action)
        self.folder_menu.addAction(self.delete_folder_action)
        self.folder_menu.addAction(self.rename_folder_action)

    def show_widgets(self):
        self.setLayout(self.main_layout)
예제 #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)
예제 #23
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)
예제 #24
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))
예제 #25
0
파일: TransCodaEditor.py 프로젝트: ag-sd/py
class EncoderSelector(QComboBox):
    __PATH_SEPARATOR__ = " → "
    _PATH_ROLE = 1
    _DETAILS_ROLE = 2

    class EncoderModel(QStandardItemModel):
        def __init__(self, json_file, disable_selections=False):
            super().__init__()
            self.disable_selections = disable_selections
            self.json_file = json_file
            self.json = None
            self.reload()

        def reload(self):
            with open(self.json_file) as config:
                self.json = json.load(config)
                self.beginResetModel()
                self.clear()
                self._build_descendents(self.invisibleRootItem(), self.json,
                                        self.disable_selections)
                self.endResetModel()

        def get_item(self, model_index):
            item = self.itemFromIndex(model_index)
            if item:
                return item.text()
            return None

        def del_item(self, media_type, encoder_group, name):
            del self.json[media_type][encoder_group][name]
            # Clean up ancestors if they dont have any children
            if len(self.json[media_type][encoder_group].keys()) == 0:
                del self.json[media_type][encoder_group]
            if len(self.json[media_type].keys()) == 0:
                del self.json[media_type]

        def add_item(self, media_type, group, name, extension, executable,
                     command):
            if media_type not in self.json:
                self.json[media_type] = {}
            if group not in self.json[media_type]:
                self.json[media_type][group] = {}
            self.json[media_type][group][name] = {
                "extension": extension,
                "executable": executable,
                "command": command
            }

        def backup_and_save(self):
            # Backup current config
            backup_config_file()
            # Save new config
            with open(self.json_file, "w") as config:
                json.dump(self.json, config, indent=4)
            self.reload()

        @staticmethod
        def _build_descendents(parent, _json, disable_selections):
            encoder_keys = {"extension", "executable", "command"}
            intersection = set(_json.keys()) & encoder_keys
            if not intersection:
                # add node and build further
                for key in _json:
                    section_root = QStandardItem(key)
                    parent.appendRow(section_root)
                    if disable_selections:
                        parent.setSelectable(False)
                    EncoderSelector.EncoderModel._build_descendents(
                        section_root, _json[key], disable_selections)
            else:
                if len(intersection) < 3:
                    raise SyntaxError(
                        f"Missing value(s) {encoder_keys - intersection} in node {parent.text()}"
                    )
                else:
                    path = ""
                    item = parent
                    while item.parent() is not None:
                        path = f"{EncoderSelector.__PATH_SEPARATOR__}{item.text()}" + path
                        item = item.parent()
                    path = f"{item.text()}" + path
                    parent.setData(path,
                                   Qt.UserRole + EncoderSelector._PATH_ROLE)
                    parent.setData(_json,
                                   Qt.UserRole + EncoderSelector._DETAILS_ROLE)

    encoder_changed = pyqtSignal('PyQt_PyObject', 'PyQt_PyObject')

    def __init__(self):
        super().__init__()
        self.tree_view = QTreeView(self)
        self.tree_view.setEditTriggers(QTreeView.NoEditTriggers)
        self.tree_view.setSelectionBehavior(QTreeView.SelectRows)
        self.tree_view.setWordWrap(True)
        self.tree_view.setHeaderHidden(True)
        self.setView(self.tree_view)
        self.encoder_model = EncoderSelector.EncoderModel(
            get_config_file(), disable_selections=True)
        self.setModel(self.encoder_model)
        self.refresh_encoders()
        self._selected_encoder = ""

    def paintEvent(self, event):
        style = QApplication.style()
        opt = QStyleOptionComboBox()
        opt.rect = self.rect()
        self.initStyleOption(opt)
        painter = QPainter(self)
        painter.save()
        style.drawComplexControl(QStyle.CC_ComboBox, opt, painter)
        opt.currentText = self._encoder_path()
        style.drawControl(QStyle.CE_ComboBoxLabel, opt, painter)
        painter.restore()

    def hidePopup(self):
        super(EncoderSelector, self).hidePopup()
        selected_index = self.tree_view.selectionModel().currentIndex()
        if selected_index:
            encoder = self.tree_view.model().get_item(selected_index)
            if encoder:
                self._selected_encoder = encoder
                self.encoder_changed.emit(self._encoder_path(),
                                          self._encoder_details())

    def get_encoder(self):
        return self._encoder_path(), self._encoder_details()

    def add_encoder(self, media_type, encoder_group, name, command, executable,
                    extension):
        self.encoder_model.add_item(media_type, encoder_group, name, extension,
                                    executable, command)
        self.encoder_model.backup_and_save()
        self.refresh_encoders()

    def del_encoder(self, media_type, encoder_group, name):
        self.encoder_model.del_item(media_type, encoder_group, name)
        self.encoder_model.backup_and_save()
        self.refresh_encoders()

    def update_encoder(self, media_type, encoder_group, name, command,
                       executable, extension):
        self.encoder_model.del_item(media_type, encoder_group, name)
        self.encoder_model.add_item(media_type, encoder_group, name, extension,
                                    executable, command)
        self.encoder_model.backup_and_save()
        self.refresh_encoders()

    def refresh_encoders(self):
        self.encoder_model.reload()
        self.tree_view.expandAll()

    def select_encoder(self, encoder_name):
        match = self._find_encoder(encoder_name)
        if match:
            self._selected_encoder = encoder_name
        self.encoder_changed.emit(self._encoder_path(),
                                  self._encoder_details())

    def _encoder_path(self):
        if self._selected_encoder:
            match = self._find_encoder(self._selected_encoder)
            if match:
                return match[0].data(Qt.UserRole + self._PATH_ROLE)
        return None

    def _encoder_details(self):
        if self._selected_encoder:
            match = self._find_encoder(self._selected_encoder)
            if match:
                return match[0].data(Qt.UserRole + self._DETAILS_ROLE)
        return None

    def _find_encoder(self, encoder_name):
        return self.model().match(self.model().index(0, 0),
                                  Qt.UserRole + self._PATH_ROLE, encoder_name,
                                  1, Qt.MatchEndsWith | Qt.MatchRecursive)
예제 #26
0
파일: export_frame.py 프로젝트: EHRI/resyto
class Explorer(QDialog):
    def __init__(
        self, parent, window_title=_("Select resources"), subtitle=_("Select files and/or folders to include")
    ):
        super().__init__(parent)
        self.logger = logging.getLogger(__name__)
        self.setModal(True)
        self.setSizeGripEnabled(True)
        self.setWindowTitle(window_title)
        self.subtitle = subtitle
        self.file_set = set()
        self.config = Configuration()
        self.__init_ui__()
        self.filename_filter = FilenameFilter()
        # self.show()

    def __init_ui__(self):
        # layout
        vert = QVBoxLayout(self)
        vert.setContentsMargins(0, 0, 0, 0)

        resource_dir = self.config.cfg_resource_dir()

        p_top = QVBoxLayout()
        lb_subtitle = QLabel(self.subtitle)
        lb_subtitle.setContentsMargins(10, 10, 10, 0)
        p_top.addWidget(lb_subtitle)

        self.model = QFileSystemModel()
        self.model.setRootPath(resource_dir)

        self.view = QTreeView()
        self.view.setModel(self.model)
        self.view.setRootIndex(self.model.index(self.model.rootPath()))
        self.view.setAlternatingRowColors(True)
        self.view.setSelectionMode(QAbstractItemView.MultiSelection)
        self.view.selectionModel().selectionChanged.connect(self.selection_changed)
        self.view.collapsed.connect(self.item_collapsed)
        self.view.expanded.connect(self.item_expanded)
        p_top.addWidget(self.view)

        p_info = QHBoxLayout()
        lb_resource = QLabel(_("resource dir") + ": " + resource_dir)
        lb_resource.setContentsMargins(10, 0, 10, 0)

        self.lb_selection_count = QLabel(str(self.selected_file_count()) + " " + _("resources selected"))
        self.lb_selection_count.setContentsMargins(10, 0, 10, 0)

        p_info.addWidget(self.lb_selection_count)
        p_info.addStretch(1)
        p_info.addWidget(lb_resource)

        p_top.addLayout(p_info)

        p_bottom = QHBoxLayout()

        self.pb_deselect = QPushButton(_("Deselect all"))
        self.pb_deselect.clicked.connect(self.pb_deselect_clicked)
        self.pb_deselect.setEnabled(self.selected_file_count() > 0)
        p_bottom.addWidget(self.pb_deselect)

        p_bottom.addStretch(1)
        self.pb_ok = QPushButton(_("OK"))
        self.pb_ok.setAutoDefault(True)
        self.pb_ok.clicked.connect(self.accept)
        p_bottom.addWidget(self.pb_ok)

        pb_cancel = QPushButton(_("Cancel"))
        pb_cancel.clicked.connect(self.reject)
        p_bottom.addWidget(pb_cancel)

        vert.addLayout(p_top)
        vert.addLayout(p_bottom)

        self.setLayout(vert)
        self.resize(self.config.explorer_width(), self.config.explorer_height())
        width = self.view.width() - 50
        self.view.setColumnWidth(0, width / 2)
        self.view.setColumnWidth(1, width / 6)
        self.view.setColumnWidth(2, width / 6)
        self.view.setColumnWidth(3, width / 6)

    def __persist__(self):
        # persist properties of the explorer
        self.config.set_explorer_width(self.width())
        self.config.set_explorer_height(self.height())
        self.config.persist()

    def __compute_filenames__(self, item_selection):
        # item_selection: a QItemSelection
        # return corresponding absolute filenames as a set, including filenames in underlying folders
        s = set()
        for index in item_selection.indexes():
            # we have an index for each column in the model
            if index.column() == 0:
                path = index.model().filePath(index)
                if os.path.isdir(path):
                    for root, directories, filenames in os.walk(path):
                        for filename in filenames:
                            if self.filename_filter.accept(filename):
                                s.add(os.path.join(root, filename))
                elif os.path.isfile(path):
                    s.add(path)
                else:
                    self.logger.warn("isUnknownThing", path)
        return s

    def showEvent(self, QShowEvent):
        # self.pb_ok.setFocus()
        pass

    def set_filename_filter(self, filename_filter):
        # set the FilenameFilter
        self.filename_filter = filename_filter

    def selected_file_count(self):
        return len(self.file_set)

    def selected_file_set(self):
        return frozenset(self.file_set)

    def selection_changed(self, selected, deselected):
        # selected, deselected: PyQt5.QtCore.QItemSelection
        selected_filenames = self.__compute_filenames__(selected)
        self.file_set.update(selected_filenames)
        deselected_filenames = self.__compute_filenames__(deselected)
        self.file_set.difference_update(deselected_filenames)

        self.pb_deselect.setEnabled(self.selected_file_count() > 0)
        self.lb_selection_count.setText(str(self.selected_file_count()) + " " + _("resources selected"))

    def item_expanded(self, index):
        # index: a QModelIndex
        # show all child items selected/deselected in accordance with state of parent folder
        pass

    def item_collapsed(self, index):
        pass

    def pb_deselect_clicked(self):
        self.view.selectionModel().clear()

    def hideEvent(self, QHideEvent):
        self.__persist__()
예제 #27
0
class EditScreen(QWidget):
    def __init__(self, rester: Rester):
        super(EditScreen, self).__init__()
        self.last_item_type = ''

        self.rester = rester
        self.tree = QTreeView()
        layout = QVBoxLayout()
        layout.addWidget(self.tree)
        self.model = QStandardItemModel()
        self.model.setHorizontalHeaderLabels(['Name'])
        self.tree.header().setDefaultSectionSize(200)
        self.tree.setModel(self.model)
        self.data = self.rester.list_request(list(APIEnum))
        self.import_data(self.data)
        self.tree.setSortingEnabled(False)
        self.tree.expandAll()
        self.tree.setSelectionMode(QAbstractItemView.SingleSelection)
        self.tree.selectionModel().selectionChanged.connect(self.item_selected)

        self.init_ui()

    def init_ui(self):
        hbox = QHBoxLayout()

        vbox = QVBoxLayout()
        left_layout = QFormLayout()
        tree_group_box = QGroupBox()
        self.search_field = QLineEdit()
        # self.search_field.editingFinished.connect(self.search_field_changed)
        self.search_field.textEdited.connect(self.search_field_changed)
        left_layout.addRow(QLabel('Search:'), self.search_field)
        tree_group_box.setLayout(left_layout)

        vbox.addWidget(tree_group_box)
        vbox.addWidget(self.tree)

        gb = QGroupBox()
        gb.setLayout(vbox)

        self.splitter = QSplitter()
        self.splitter.addWidget(gb)

        self.ew = EquipmentEdit()
        self.splitter.addWidget(self.ew)

        hbox.addWidget(self.splitter)

        self.setLayout(hbox)

    def import_data(self, data, root=None):

        self.model.setRowCount(0)
        if root is None:
            root = self.model.invisibleRootItem()
        for key, value_list in data.items():
            parent = TreeItem({'name': key}, '', parent=True)
            parent.setEditable(False)
            root.appendRow([parent])
            for value in value_list:
                parent.appendRow([TreeItem(value, key)])

    def search_field_changed(self):
        term = self.search_field.text()
        data = {}
        for key, value_list in self.data.items():
            li = []
            for value in value_list:
                for k in value.keys():
                    if term in k:
                        li.append(value)
            data[key] = li

        self.import_data(data)

    def item_selected(self):
        indexes = self.tree.selectedIndexes()
        selected = indexes[0]

        item = self.model.itemFromIndex(selected)

        if item.is_parent:
            return

        #if item.typ != self.last_item_type:
        self.ew.hide()
        self.ew.destroy()
        self.ew = get_edit_widget(item.typ, self.data)
        self.splitter.addWidget(self.ew)

        self.last_item_type = item.typ

        data_dict = item.get_data_dict()
        self.ew.load_data(data_dict)
예제 #28
0
class StatWidget(QWidget):
    def __init__(self, parent=None, domain=None, headers=None):
        super().__init__(parent=parent)

        self._root = None
        self._domain = domain

        self._button = QPushButton('Открыть...')
        self._edit = QLineEdit()
        self._tree = QTreeView()

        self._model = StatTreeModel(parent=self, headers=headers)

        self._layoutControl = QHBoxLayout()
        self._layoutControl.addWidget(self._button)
        self._layoutControl.addWidget(self._edit)

        self._layoutMain = QVBoxLayout()
        self._layoutMain.addLayout(self._layoutControl)
        self._layoutMain.addWidget(self._tree)

        self.setLayout(self._layoutMain)

        self._edit.setPlaceholderText('Папка...')
        self._edit.setReadOnly(True)

        self._button.clicked.connect(self._on_button_clicked)

        self._tree.setModel(self._model)
        self._tree.setItemDelegateForColumn(3, ProgressBarDelegate())
        self._tree.setSelectionMode(QAbstractItemView.ExtendedSelection)
        self._tree.setSelectionBehavior(QAbstractItemView.SelectRows)

    def init(self):
        self._domain.workDir = os.path.normpath(os.getcwd()) + '\\xlsx'
        self._edit.setText(self._domain.workDir)
        self._model.init(self._domain._root)

    @pyqtSlot()
    def _on_button_clicked(self):
        dialog = QFileDialog(self, 'Выбрать папку...', self._domain.workDir)
        dialog.setFileMode(QFileDialog.DirectoryOnly)
        if dialog.exec() != QFileDialog.Accepted:
            return

        self._domain.workDir = dialog.selectedFiles()[0]
        self._model.init(self._domain._root)

    @property
    def hasSelection(self):
        return self._tree.selectionModel().hasSelection()

    @property
    def rows(self):
        top_level_rows = [
            index for index in self._tree.selectionModel().selectedIndexes()
            if index.data(StatTreeModel.RoleTier) == StatTreeModel.TIER_1
            and index.column() == 0
        ]
        sorted_row_numbers = list(
            sorted([index.row() for index in top_level_rows]))
        return sorted_row_numbers

    def getEmailData(self, rows):
        # TODO handle selected rows
        # TODO handle different domains for email generation, works only for batch stats now
        email_data = dict()
        for batch in self._model._rootNode.child_nodes:
            specs_for_dev = defaultdict(list)
            for spec in batch.child_nodes:
                if not spec['received']:
                    specs_for_dev[spec['developer']].append(spec)
            email_data[batch] = specs_for_dev

        return email_data

    def resizeTable(self, width, columns):
        for index, column in enumerate(columns):
            self._tree.setColumnWidth(index, width * column)
예제 #29
0
class UIFilterManager(object):

    def __init__(self):
        self.mainDialog = filtermanagerdialog.FilterManagerDialog()
        self.mainLayout = QVBoxLayout(self.mainDialog)
        self.formLayout = QFormLayout()
        self.buttonBox = QDialogButtonBox(
            QDialogButtonBox.Ok | QDialogButtonBox.Cancel)

        self.kritaInstance = krita.Krita.instance()
        self._filters = sorted(self.kritaInstance.filters())
        self._documents = self.kritaInstance.documents()
        self.treeModel = filtermanagertreemodel.FilterManagerTreeModel(self)

        self.documentsTreeView = QTreeView()
        self.filterComboBox = filtercombobox.FilterComboBox(self)

        self.buttonBox.accepted.connect(self.confirmButton)
        self.buttonBox.rejected.connect(self.mainDialog.close)

        self.documentsTreeView.setSelectionMode(
            QAbstractItemView.SingleSelection)
        self.mainDialog.setWindowModality(Qt.NonModal)

    def initialize(self):
        self.documentsTreeView.setModel(self.treeModel)
        self.documentsTreeView.setWindowTitle(i18n("Document Tree Model"))
        self.documentsTreeView.resizeColumnToContents(0)
        self.documentsTreeView.resizeColumnToContents(1)
        self.documentsTreeView.resizeColumnToContents(2)

        self.formLayout.addRow(i18nc("Python filters", "Filters:"), self.filterComboBox)

        self.line = QFrame()
        self.line.setFrameShape(QFrame.HLine)
        self.line.setFrameShadow(QFrame.Sunken)

        self.mainLayout.addWidget(self.documentsTreeView)
        self.mainLayout.addLayout(self.formLayout)
        self.mainLayout.addWidget(self.line)
        self.mainLayout.addWidget(self.buttonBox)

        self.mainDialog.resize(500, 300)
        self.mainDialog.setWindowTitle(i18n("Filter Manager"))
        self.mainDialog.setSizeGripEnabled(True)
        self.mainDialog.show()
        self.mainDialog.activateWindow()

    def confirmButton(self):
        documentsIndexes = []

        selectionModel = self.documentsTreeView.selectionModel()
        for index in selectionModel.selectedRows():
            node = self.treeModel.data(index, Qt.UserRole + 1)
            documentIndex = self.treeModel.data(index, Qt.UserRole + 2)
            _type = self.treeModel.data(index, Qt.UserRole + 3)

            if _type == 'Document':
                self.applyFilterOverDocument(self.documents[documentIndex])
            else:
                self.applyFilterOverNode(node, self.documents[documentIndex])

            documentsIndexes.append(documentIndex)

        self.refreshDocumentsProjections(set(documentsIndexes))

    def refreshDocumentsProjections(self, indexes):
        for index in indexes:
            document = self.documents[index]
            document.refreshProjection()

    def applyFilterOverNode(self, node, document):
        _filter = self.kritaInstance.filter(self.filterComboBox.currentText())
        _filter.apply(node, 0, 0, document.width(), document.height())

    def applyFilterOverDocument(self, document):
        """This method applies the selected filter just to topLevelNodes,
        then if topLevelNodes are GroupLayers, that filter will not be
        applied."""

        for node in document.topLevelNodes():
            self.applyFilterOverNode(node, document)

    @property
    def filters(self):
        return self._filters

    @property
    def documents(self):
        return self._documents
예제 #30
0
class LeftSideBar(QWidget):

    treeViewSelectionChanged = pyqtSignal(QModelIndex, QModelIndex)
    treeViewDoubleClicked = pyqtSignal(QModelIndex)

    addPlaylistRequested = pyqtSignal()
    removePlaylistRequested = pyqtSignal(UUID)
    addToPlaylistRequested = pyqtSignal(UUID)

    playlistAdded = pyqtSignal(UUID)
    playlistRenamed = pyqtSignal(UUID, str)

    def __init__(self, tree_items, parent=None):
        super(LeftSideBar, self).__init__(parent)
        self._tree_items = tree_items
        self._restoreSettings()

        self._setTreeView()
        self._setAlbumCoverBox()

        self._renderUI()

        self.setMinimumWidth(FRONT_COVER_MIN_WIDTH)
        self.setMaximumWidth(FRONT_COVER_MAX_WIDTH)

    def _restoreSettings(self):
        pass

    def _setTreeView(self):
        self.treeModel = TreeModel()

        self.treeModel.addTopLevelItems(self._tree_items.keys())

        self.leftBarView = QTreeView()
        self.leftBarView.setModel(self.treeModel)
        self.leftBarView.setHeaderHidden(True)
        self.leftBarView.setRootIsDecorated(False)
        self.leftBarView.setItemsExpandable(False)
        self.leftBarView.setMouseTracking(True)
        self.leftBarView.expandAll()
        self.leftBarView.setFocusPolicy(Qt.NoFocus)
        self.leftBarView.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.leftBarView.setSelectionMode(QAbstractItemView.SingleSelection)
        self.leftBarView.setEditTriggers(QAbstractItemView.SelectedClicked)

        self.leftBarView.selectionModel().currentRowChanged.connect(
            lambda c, p: self.treeViewSelectionChanged.emit(c, p))

        self.leftBarView.selectionModel().setCurrentIndex(
            self.leftBarView.model().index(
                0, 0, self.leftBarView.model().index(0, 0)),
            QItemSelectionModel.Select)

        self.leftBarView.doubleClicked.connect(
            lambda i: self.treeViewDoubleClicked.emit(i))

        delegate = LeftSideBarDelegate(self.leftBarView)
        self.leftBarView.setItemDelegate(delegate)
        delegate.addPlaylistRequested.connect(
            lambda: self.addPlaylistRequested.emit())
        delegate.removePlaylistRequested.connect(
            lambda i: self.removePlaylistRequested.emit(
                self.__getUuidFromIndex(i)))
        delegate.addToPlaylistRequested.connect(
            lambda i:
            self.addToPlaylistRequested.emit(self.__getUuidFromIndex(i)))
        delegate.editingFinished.connect(self._onRenamed)

    def _onRenamed(self, index, text):
        self.playlistRenamed.emit(
            self.__getUuidFromIndex(index),
            text)

    @property
    def model(self):
        return self.treeModel

    def _setAlbumCoverBox(self):
        self.albumCoverBox = CoverArtBox()

    def _renderUI(self):
        self.layout = QVBoxLayout()
        self.layout.setContentsMargins(0, 0, 0, 0)
        self.layout.setSpacing(0)
        self.layout.addWidget(self.leftBarView)
        self.layout.addWidget(self.albumCoverBox)
        self.setLayout(self.layout)

    @QtCore.pyqtSlot(str, str, bytes)
    def changeCoverArtBoxInformation(self, title, artist, cover):
        self.albumCoverBox.setCoverArtBox(title, artist, cover)

    @QtCore.pyqtSlot(UUID, str, int, int)
    def addPlaylistEntry(self, uuid, name, row=0, column=0):
        model = self.model
        parent = model.getTopLevelIndex('PLAYLISTS')
        if model.insertPlaylistEntry(row, name, uuid, parent):
            self.playlistAdded.emit(uuid)

        child = model.index(row, column, parent)
        self.leftBarView.selectionModel().setCurrentIndex(
            child, QItemSelectionModel.SelectCurrent)
        self.leftBarView.edit(child)

    @QtCore.pyqtSlot(UUID, int, int)
    def createDefaults(self, uuid, row=0, column=0):
        model = self.model
        parent = model.getTopLevelIndex('LIBRARY')
        model.insertPlaylistEntry(row, 'Songs', uuid, parent)

    def __getUuidFromIndex(self, index):
        model = self.model
        uuid = model.getItemUuid(index)
        return uuid

    def __getIndexFromUuid(self, uuid):
        model = self.model
        row = model.getIndexFromUuid(uuid)
        return row

    @QtCore.pyqtSlot(UUID)
    def removePlaylistEntry(self, uuid):
        model = self.model
        row = self.__getIndexFromUuid(uuid)
        parent = model.getTopLevelIndex('PLAYLISTS')

        if not model.removeRow(row, parent):
            return None
예제 #31
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)
예제 #32
0
class RedditScraperWindow(QWidget):
    """The main window of the program."""

    ########### Setup ##################################
    def __init__(self):
        super().__init__()

        self.read_user_config()
        self.load_assets()
        self.initialize_ui()

    def load_assets(self):
        script_folder = os.path.dirname(
            os.path.dirname(os.path.abspath(__file__)))
        asset_folder = os.path.join(script_folder, "assets")
        if os.path.isdir(asset_folder):
            self.download_icon = QIcon(
                os.path.join(asset_folder, 'download_icon.png'))
            self.stop_icon = QIcon(os.path.join(asset_folder, 'stop_icon.png'))
            self.reddit_icon = QIcon(
                os.path.join(asset_folder, 'reddit_icon.png'))
        else:
            self.download_icon = QIcon(
                os.path.join("assets", 'download_icon.png'))
            self.stop_icon = QIcon(os.path.join("assets", 'stop_icon.png'))
            self.reddit_icon = QIcon(os.path.join("assets", 'reddit_icon.png'))

    def initialize_ui(self):
        """sets up the user interface, connects all the signals and shows the window. """

        self.init_components()
        self.read_settings_config()
        self.connect_signals()

        self.setGeometry(100, 100, 1000, 800)
        self.setWindowTitle('Reddit Image Scraper')
        self.setWindowIcon(self.reddit_icon)

        self.show()

    def init_components(self):
        """initializes all components and sets up the layout"""
        internalWidgetInput = QWidget()
        internalWidgetTree = QWidget()

        ############ define components ####################
        self.subredditInput = QComboBox()
        self.subredditInput.setEditable(True)
        self.subredditInput.addItems(self.get_downloaded_subreddits())

        self.numInput = QLineEdit()
        self.onlyInt = QIntValidator()
        self.numInput.setValidator(self.onlyInt)
        subredditLabel = QLabel('subreddit')
        numLabel = QLabel('number of images')
        self.dirLabel = QLabel('choose a directory')
        scale_label = QLabel("Scale images?")
        self.imgView = QLabel()

        self.outputText = QTextEdit('')
        self.outputText.setReadOnly(True)

        self.scale_cb = QCheckBox()

        self.sortingCb = QComboBox()
        self.sortingCb.addItems([
            "Hot", "Top all time", "Top this month", "Top past year", "New",
            "Controversial"
        ])
        sortingLabel = QLabel('sorting method')
        self.runButton = QPushButton('Download')
        self.runButton.setIcon(self.download_icon)
        self.chooseDirButton = QPushButton('Save dir')
        self.stopButton = QPushButton('Stop')
        self.stopButton.setIcon(self.stop_icon)

        self.fileModel = QFileSystemModel()
        self.tree = QTreeView()

        self.tree.setModel(self.fileModel)
        self.tree.setColumnHidden(1, True)
        self.tree.setColumnHidden(2, True)
        self.tree.setColumnHidden(3, True)

        ############## Menu stuff ###################
        menu_bar = QMenuBar()
        file_menu = menu_bar.addMenu('File')
        help_menu = menu_bar.addMenu('Help')

        self.exit_action = QAction('Exit', self)

        file_menu.addAction(self.exit_action)
        self.help_action = QAction('Help', self)
        help_menu.addAction(self.help_action)
        menu_bar.setFixedHeight(30)

        ############# Setup the grid layout###############################
        grid = QGridLayout()
        # grid.addWidget(menu_bar, 1, 0, 1, 4)
        grid.setSpacing(4)
        grid.addWidget(subredditLabel, 1, 0)
        grid.addWidget(self.subredditInput, 1, 1)
        grid.addWidget(numLabel, 2, 0)
        grid.addWidget(self.numInput, 2, 1)
        grid.addWidget(sortingLabel, 3, 0)
        grid.addWidget(self.sortingCb, 3, 1)

        grid.addWidget(self.chooseDirButton, 4, 0)
        grid.addWidget(self.dirLabel, 4, 1)
        grid.addWidget(self.stopButton, 5, 0)
        grid.addWidget(self.runButton, 5, 1)
        grid.addWidget(self.outputText, 7, 0, 7, 2)
        # grid.addWidget(self.tree,1,2, 11,7)

        grid.addWidget(scale_label, 6, 0)
        grid.addWidget(self.scale_cb, 6, 1)

        hboxTree = QVBoxLayout()
        hboxTree.addWidget(self.tree)

        #the image viewer, setting how it behaves under resizing.
        self.imgView.setSizePolicy(
            QSizePolicy(QSizePolicy.MinimumExpanding,
                        QSizePolicy.MinimumExpanding))
        self.imgView.setMaximumHeight(MAX_IMAGE_HEIGHT)

        self.imgView.setAlignment(Qt.AlignmentFlag.AlignCenter)

        img_scroll_area = QScrollArea()
        img_scroll_area.setMinimumHeight(MAX_IMAGE_HEIGHT)
        img_scroll_area.setMinimumWidth(MAX_IMAGE_HEIGHT)
        img_scroll_area.setWidget(self.imgView)

        internalWidgetInput.setLayout(grid)
        # internalWidgetInput.setMinimumWidth(300)
        internalWidgetInput.setFixedWidth(300)
        internalWidgetInput.setSizePolicy(
            QSizePolicy(QSizePolicy.Fixed, QSizePolicy.MinimumExpanding))

        internalWidgetTree.setLayout(hboxTree)
        internalWidgetTree.setFixedWidth(360)
        internalWidgetTree.setSizePolicy(
            QSizePolicy(QSizePolicy.Fixed, QSizePolicy.MinimumExpanding))

        #construct layout of main window.
        hbox = QHBoxLayout()
        hbox.setSpacing(0)
        hbox.setContentsMargins(0, 0, 0, 0)
        hbox.setMenuBar(menu_bar)
        hbox.addWidget(internalWidgetInput)
        hbox.addWidget(internalWidgetTree)
        hbox.addWidget(img_scroll_area)
        self.setLayout(hbox)

    def connect_signals(self):
        """connects all the signals to the right functions"""
        self.chooseDirButton.clicked.connect(self.show_dir_dialog)
        self.runButton.clicked.connect(self.run_download_threaded)
        self.tree.clicked.connect(self.on_treeView_clicked)
        self.stopButton.clicked.connect(self.stop_download)
        self.scale_cb.clicked.connect(self.refresh_image)
        self.exit_action.triggered.connect(exit)

        #self.edit_login_action.triggered.connect(self.edit_login_info)
        self.help_action.triggered.connect(self.show_help)

        self.tree.selectionModel().selectionChanged.connect(
            self.on_selection_change)

    def read_user_config(self):
        """reads in the users username and password from the config file, or if there is no config file,
        shows an input dialog.
        Also tests so that only valid login information gets saved to the config file. """
        config = configparser.ConfigParser()
        self.redditScraper = redditScraper()
        if os.path.exists('redditScraper.ini'):
            config.read('redditScraper.ini')

            if 'DIR' in config:
                self.folder = config['DIR']['root_folder']

        else:
            config['REDDIT'] = {
                'subreddit': "wallpapers",
                'num': 10,
                'sorting': "Hot",
                'downloaded_subreddits': ""
            }

            with open('redditScraper.ini', 'w') as configfile:
                config.write(configfile)

        self.config = config

    def read_settings_config(self):
        """reads the saved settings from the config file, if they're there."""
        if 'DIR' in self.config:
            self.folder = self.config['DIR']['root_folder']
            idx = self.fileModel.setRootPath(str(self.folder))
            self.tree.setRootIndex(idx)
            self.dirLabel.setText(self.folder)

        if 'REDDIT' in self.config:
            self.subredditInput.setCurrentText(
                self.config['REDDIT']['subreddit'])
            self.numInput.setText(self.config['REDDIT']['num'])
            self.sortingCb.setCurrentText(self.config['REDDIT']['sorting'])

    ################### Actions: ##################
    @pyqtSlot(QModelIndex)
    def on_treeView_clicked(self, index):
        """triggers when the user clicks on a file item shown in the treeView, and shows that file in the picture viewer."""
        index = self.fileModel.index(index.row(), 0, index.parent())
        self.show_image(index)

    def on_selection_change(self, selected: QItemSelection,
                            deselected: QItemSelection):
        """ Triggers when the selected item in the treeview changes, and updates the shown picture. """
        self.refresh_image()

    def refresh_image(self):
        selected_image_index = self.tree.selectedIndexes()[0]
        self.show_image(selected_image_index)

    def show_image(self, index: QModelIndex):
        filePath = self.fileModel.filePath(index)
        if os.path.isfile(filePath) and filePath.split(".")[-1] in [
                "jpg", "gif", "png", "jpeg"
        ]:
            pixmap = QPixmap(filePath)
            if self.scale_cb.isChecked():
                self.imgView.setFixedHeight(MAX_IMAGE_HEIGHT)
                scaled_img = pixmap.scaledToHeight(MAX_IMAGE_HEIGHT)
                self.imgView.setFixedWidth(scaled_img.width())
                self.imgView.setPixmap(scaled_img)
            else:
                self.imgView.setFixedHeight(pixmap.height())
                self.imgView.setFixedWidth(pixmap.width())
                self.imgView.setPixmap(pixmap)

    def show_dir_dialog(self):
        """lets the user select the root folder, and saves the choice to the config file."""

        self.folder = QFileDialog.getExistingDirectory(
            self, 'Choose base directory', '/home')
        self.dirLabel.setText(self.folder)

        self.config['DIR'] = {'root_folder': self.folder}
        with open('redditScraper.ini', 'w') as configfile:
            self.config.write(configfile)

        idx = self.fileModel.setRootPath(self.folder)
        self.tree.setRootIndex(idx)

        return self.folder

    @pyqtSlot(str)
    def update_output_text(self, message: str):
        """updates the output text area, to show progress on downloads."""
        self.outputText.setText(message + self.outputText.toPlainText())

    def save_subreddit(self, subreddit: str, num: int, sorting: str):
        """helper function to save the current settings to the config file."""
        downloaded_subreddits = self.get_downloaded_subreddits()
        if subreddit not in downloaded_subreddits:
            downloaded_subreddits.append(subreddit)
            self.subredditInput.addItem(subreddit)

        downloaded_subreddits = ','.join(downloaded_subreddits)
        self.config['REDDIT'] = {
            'subreddit': subreddit,
            'num': str(num),
            'sorting': sorting,
            'downloaded_subreddits': downloaded_subreddits
        }

        with open('redditScraper.ini', 'w') as configfile:
            self.config.write(configfile)

    def get_downloaded_subreddits(self) -> list:
        if 'downloaded_subreddits' in self.config['REDDIT'].keys():
            subreddits = self.config['REDDIT']['downloaded_subreddits'].split(
                ',')
            return subreddits
        return []

    def run_download_threaded(self):
        """downloads the pictures. Runs in a QThread, so that the program does not freeze.
        Also checks whether the specified subreddit exists. """
        subreddit = self.subredditInput.currentText()
        num = int(self.numInput.text())
        sorting = self.sortingCb.currentText()

        if self.redditScraper.sub_exists(subreddit):

            if not hasattr(self, "folder"):
                msgBox = QMessageBox()
                msgBox.setText('You need to set a download folder!')
                msgBox.setWindowTitle("Pick a download folder")
                msgBox.exec_()
                return

            self.save_subreddit(subreddit, num, sorting)
            self.get_thread = RedditDownloadThread(
                self.redditScraper, subreddit, num,
                num * LOOKUP_LIMIT_MULTIPLIER, sorting, self.folder)
            self.get_thread.changeText.connect(self.update_output_text)
            self.get_thread.start()
        else:
            msgBox = QMessageBox()
            msgBox.setText('That subreddit does not exist, please try again')
            msgBox.setWindowTitle("Invalid subreddit")
            msgBox.exec_()

        pass

    def stop_download(self):
        """Stops the download thread and prints a message to the output."""
        try:
            if self.get_thread.isRunning():
                self.get_thread.terminate()
                self.outputText.setText(self.outputText.toPlainText() +
                                        ' Aborted!\n')
        except Exception:
            pass

    ############### Menu actions: ###############

    def show_help(self):
        msgBox = QMessageBox()

        msgBox.setWindowIcon(self.reddit_icon)
        msgBox.setText(
            'This program downloads images posted to reddit.com, or more specifically, to subreddits'
            +
            '(i.e. sub-forums). To use it, one needs a valid reddit account, which one can sign up for '
            +
            'on reddit.com. One also needs to know some names of subreddits. \n Some suggestions for subreddits: '
            + 'wallpapers , earthporn , nature , and pics . \n \n ' +
            "This program will download up to the specified number of images. It can handle png, jpg and gif,"
            +
            "so links to gyf-files, videos or anything else will be ignored. Therefore the number of images actually "
            +
            "downloaded will usually be less than the specified number. So if you want a lot of images, just put"
            +
            " a large limit.\n The images will be placed in a subfolder of the chosen base folder, named after the subreddit. This folder will be created if it does not exist.\n \n "
            +
            "To view the images, click on them in the tree-view, and they will appear on the right"
        )
        msgBox.setWindowTitle("Help")
        msgBox.exec_()
예제 #33
0
class TapeWidget(QWidget):
    def __init__(self, parent = None):
        super().__init__(parent)

        self._main_layout    = QVBoxLayout(self)
        self._search_box     = QLineEdit(self)
        self._button_layout  = QHBoxLayout()

        self._view = QTreeView()
        self._view.setVerticalScrollMode(QAbstractItemView.ScrollPerPixel)
        self._view.setSelectionMode(QAbstractItemView.ExtendedSelection)
        self._view.setHeaderHidden(True)

        self._add_note_button = QPushButton(self)
        self._add_note_button.setText("New note")

        self._add_child_button = QPushButton(self)
        self._add_child_button.setText("New child")

        self._add_sibling_button = QPushButton(self)
        self._add_sibling_button.setText("New sibling")

        self._delete_note_button = QPushButton(self)
        self._delete_note_button.setText("Delete note")

        self._button_layout.addWidget(self._add_note_button)
        self._button_layout.addWidget(self._add_sibling_button)
        self._button_layout.addWidget(self._add_child_button)
        self._button_layout.addWidget(self._delete_note_button)
        self._button_layout.addStretch()

        self._main_layout.addWidget(self._search_box)
        self._main_layout.addLayout(self._button_layout)
        self._main_layout.addWidget(self._view)

        self._tape_filter_proxy_model = TapeFilterProxyModel()
        self._note_delegate           = NoteDelegate()

        self._tape_model = QStandardItemModel()
        self.set_model(self._tape_model)
        self._view.setItemDelegate(self._note_delegate)
        self._view.setModel(self._tape_filter_proxy_model)

        self._add_note_button.clicked.connect(lambda checked: self.add_and_focus_note())
        self._add_sibling_button.clicked.connect(self._new_sibling_handler)
        self._add_child_button.clicked.connect(self._new_child_handler)
        self._delete_note_button.clicked.connect(self.delete_selected_notes)
        self._search_box.textChanged.connect(self._tape_filter_proxy_model.setFilterFixedString)

    def model(self):
        """ Returns the model that contains all notes managed by the tape.

            The model should be treated as read-only. You can only modify it indirectly through
            the methods provided by TapeWidget. """

        return self._tape_model

    def proxy_model(self):
        """ Returns the model that contains notes matching current filter.

            The model should be treated as read-only. You can only modify it indirectly through
            the methods provided by TapeWidget. """

        return self._tape_filter_proxy_model

    def set_model(self, model):
        assert (
            len(set([item_to_id(item) for item in all_items(model) if item_to_id(item) != None])) ==
            len(    [item_to_id(item) for item in all_items(model) if item_to_id(item) != None])
        )

        # NOTE: If there's an exception in setSourceModel(), we can hope that the source model
        # remains unchanged. That's why we assing to _tape_model only if that instruction succeeds.
        self._tape_filter_proxy_model.setSourceModel(model)
        self._tape_model = model

    def notes(self):
        return all_notes(self._tape_model)

    def assign_ids(self):
        assign_note_ids(self._tape_model)

    def create_empty_note(self):
        return Note(
            body       = "",
            tags       = [],
            created_at = datetime.utcnow()
        )

    def add_note(self, note = None, parent_index = None):
        # NOTE: Remember to use indexes from _tape_model, not _tape_filter_proxy_model here
        assert parent_index == None or self._tape_model.itemFromIndex(parent_index) != None and parent_index.isValid()

        root_item = self._tape_model.invisibleRootItem()
        if parent_index == None:
            parent_item = root_item
        else:
            parent_item = self._tape_model.itemFromIndex(parent_index)

        if note != None:
            assert note not in self.notes()
        else:
            note = self.create_empty_note()

        item = QStandardItem()
        set_item_note(item, note)
        parent_item.appendRow(item)

    def add_and_focus_note(self, parent_proxy_index = None):
        if parent_proxy_index != None:
            parent_index = self._tape_filter_proxy_model.mapToSource(parent_proxy_index)
        else:
            parent_index = None

        self.add_note(parent_index = parent_index)

        if parent_proxy_index != None:
            self._view.expand(parent_proxy_index)

            parent_item = self._tape_model.itemFromIndex(parent_index)
        else:
            parent_item = self._tape_model.invisibleRootItem()

        # NOTE: It's likely that the new note does not match the filter and won't not be present
        # in the proxy model. We want to select it and focus on it so the filter must be cleared.
        # And it must be cleared before taking the index in the proxy because changing the filter
        # may change the set of notes present in the proxy and invalidate the index.
        self.set_filter('')

        new_note_index       = parent_item.child(parent_item.rowCount() - 1).index()
        new_note_proxy_index = self._tape_filter_proxy_model.mapFromSource(new_note_index)

        self.clear_selection()
        self.set_note_selection(new_note_proxy_index, True)
        self._view.scrollTo(new_note_proxy_index)

    def remove_notes(self, indexes):
        remove_items(self._tape_model, indexes)

    def clear(self):
        self._tape_model.clear()

    def set_filter(self, text):
        # NOTE: This triggers textChanged() signal which applies the filter
        self._search_box.setText(text)

    def get_filter(self):
        return self._search_box.text()

    def selected_proxy_indexes(self):
        return self._view.selectedIndexes()

    def selected_indexes(self):
        return [self._tape_filter_proxy_model.mapToSource(proxy_index) for proxy_index in self.selected_proxy_indexes()]

    def set_note_selection(self, proxy_index, select):
        assert proxy_index != None and proxy_index.isValid()
        assert self._tape_model.itemFromIndex(self._tape_filter_proxy_model.mapToSource(proxy_index)) != None

        self._view.selectionModel().select(
            QItemSelection(proxy_index, proxy_index),
            QItemSelectionModel.Select if select else QItemSelectionModel.Deselect
        )

    def clear_selection(self):
        self._view.selectionModel().clear()

    def delete_selected_notes(self):
        self.remove_notes(self.selected_indexes())

    def _new_sibling_handler(self):
        selected_proxy_indexes = self._view.selectedIndexes()
        if len(selected_proxy_indexes) > 1:
            self.clear_selection()
            selected_proxy_indexes = []

        if len(selected_proxy_indexes) == 0 or selected_proxy_indexes[0].parent() == QModelIndex():
            self.add_and_focus_note()
        else:
            self.add_and_focus_note(selected_proxy_indexes[0].parent())

    def add_child_to_selected_element(self):
        selected_proxy_indexes = self._view.selectedIndexes()
        if len(selected_proxy_indexes) != 1:
            return False
        else:
            self.add_and_focus_note(selected_proxy_indexes[0])
            return True

    def _new_child_handler(self):
        added = self.add_child_to_selected_element()
        if not added:
            QMessageBox.warning(self, "Can't add note", "To be able to add a new child note select exactly one parent")
예제 #34
0
class HttpMessagesTreeView(QWidget):
    selected = pyqtSignal(object)

    class ModelItem:
        def __init__(self, model, branch):
            self.branch = branch
            self.model = model

    def __init__(self, plugin_registry, parent=None):
        super().__init__(parent)
        self.plugin_registry = plugin_registry
        self.tree_view = QTreeView()
        self.label = QLabel()
        self.clear()

        self.column_definitions = self.plugin_registry.get_columns()

        layout = QVBoxLayout()
        layout.addWidget(self.label)
        layout.addWidget(self.tree_view)
        layout.setContentsMargins(0, 0, 0, 0)
        self.setLayout(layout)

    def getAllMessagePairs(self):
        return (item.model for item in self.__index.values())

    def clear(self):
        self.model = QStandardItemModel()
        self.filteredModel = FilteredModel(self.plugin_registry)
        self.filteredModel.setSourceModel(self.model)

        self.rootNode = self.model.invisibleRootItem()
        self.__index = OrderedDict()
        self.tree_view.setModel(self.filteredModel)
        self.label.setText(self.__getLabelText())

        self.tree_view.selectionModel().selectionChanged.connect(
            self.onSelectionChanged)

    def refresh(self):
        self.filteredModel.invalidateFilter()
        self.label.setText(self.__getLabelText())

    def applyModel(self):
        self.tree_view.setModel(self.filteredModel)
        for i, column in enumerate(self.column_definitions):
            self.model.setHeaderData(i, Qt.Horizontal, column[1])
        column_count = self.model.columnCount()
        column_width = self.tree_view.width() / column_count
        for col in range(0, column_count):
            self.tree_view.setColumnWidth(col, column_width)
        self.label.setText(self.__getLabelText())

    def onSelectionChanged(self, selection: QItemSelection):
        if selection.isEmpty():
            return
        item = selection.indexes()[0]
        data = item.data(ROLE_HTTP_MESSAGE)
        if data:
            self.selected.emit(data)

    def onRequestResponse(self, request_response):
        new_row = request_response.guid not in self.__index

        if not new_row:
            model_item = self.__index[request_response.guid]
            branch = model_item.branch
            model_item.model = request_response
        else:
            branch = [QStandardItem() for x in self.column_definitions]
            self.__index[
                request_response.guid] = HttpMessagesTreeView.ModelItem(
                    request_response, branch)

        branch[0].setData(request_response, ROLE_HTTP_MESSAGE)

        for i, column in enumerate(self.column_definitions):
            text = self.plugin_registry.get_cell_content(
                request_response, column[0])
            if text:
                branch[i].setText(text)

        if new_row:
            self.rootNode.appendRow(branch)
        self.applyModel()

    def __getLabelText(self):
        return "Displaying <b>{}</b> out of <b>{}</b>.".format(
            self.filteredModel.rowCount(), self.model.rowCount())
예제 #35
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')]
예제 #36
0
class Explorer(QDialog):
    def __init__(self,
                 parent,
                 window_title=_("Select resources"),
                 subtitle=_("Select files and/or folders to include")):
        super().__init__(parent)
        self.logger = logging.getLogger(__name__)
        self.setModal(True)
        self.setSizeGripEnabled(True)
        self.setWindowTitle(window_title)
        self.subtitle = subtitle
        self.file_set = set()
        self.config = Configuration()
        self.__init_ui__()
        self.filename_filter = FilenameFilter()
        #self.show()

    def __init_ui__(self):
        # layout
        vert = QVBoxLayout(self)
        vert.setContentsMargins(0, 0, 0, 0)

        resource_dir = self.config.cfg_resource_dir()

        p_top = QVBoxLayout()
        lb_subtitle = QLabel(self.subtitle)
        lb_subtitle.setContentsMargins(10, 10, 10, 0)
        p_top.addWidget(lb_subtitle)

        self.model = QFileSystemModel()
        self.model.setRootPath(resource_dir)

        self.view = QTreeView()
        self.view.setModel(self.model)
        self.view.setRootIndex(self.model.index(self.model.rootPath()))
        self.view.setAlternatingRowColors(True)
        self.view.setSelectionMode(QAbstractItemView.MultiSelection)
        self.view.selectionModel().selectionChanged.connect(
            self.selection_changed)
        self.view.collapsed.connect(self.item_collapsed)
        self.view.expanded.connect(self.item_expanded)
        p_top.addWidget(self.view)

        p_info = QHBoxLayout()
        lb_resource = QLabel(_("resource dir") + ": " + resource_dir)
        lb_resource.setContentsMargins(10, 0, 10, 0)

        self.lb_selection_count = QLabel(
            str(self.selected_file_count()) + " " + _("resources selected"))
        self.lb_selection_count.setContentsMargins(10, 0, 10, 0)

        p_info.addWidget(self.lb_selection_count)
        p_info.addStretch(1)
        p_info.addWidget(lb_resource)

        p_top.addLayout(p_info)

        p_bottom = QHBoxLayout()

        self.pb_deselect = QPushButton(_("Deselect all"))
        self.pb_deselect.clicked.connect(self.pb_deselect_clicked)
        self.pb_deselect.setEnabled(self.selected_file_count() > 0)
        p_bottom.addWidget(self.pb_deselect)

        p_bottom.addStretch(1)
        self.pb_ok = QPushButton(_("OK"))
        self.pb_ok.setAutoDefault(True)
        self.pb_ok.clicked.connect(self.accept)
        p_bottom.addWidget(self.pb_ok)

        pb_cancel = QPushButton(_("Cancel"))
        pb_cancel.clicked.connect(self.reject)
        p_bottom.addWidget(pb_cancel)

        vert.addLayout(p_top)
        vert.addLayout(p_bottom)

        self.setLayout(vert)
        self.resize(self.config.explorer_width(),
                    self.config.explorer_height())
        width = self.view.width() - 50
        self.view.setColumnWidth(0, width / 2)
        self.view.setColumnWidth(1, width / 6)
        self.view.setColumnWidth(2, width / 6)
        self.view.setColumnWidth(3, width / 6)

    def __persist__(self):
        # persist properties of the explorer
        self.config.set_explorer_width(self.width())
        self.config.set_explorer_height(self.height())
        self.config.persist()

    def __compute_filenames__(self, item_selection):
        # item_selection: a QItemSelection
        # return corresponding absolute filenames as a set, including filenames in underlying folders
        s = set()
        for index in item_selection.indexes():
            # we have an index for each column in the model
            if index.column() == 0:
                path = index.model().filePath(index)
                if os.path.isdir(path):
                    for root, directories, filenames in os.walk(path):
                        for filename in filenames:
                            if self.filename_filter.accept(filename):
                                s.add(os.path.join(root, filename))
                elif os.path.isfile(path):
                    s.add(path)
                else:
                    self.logger.warn("isUnknownThing", path)
        return s

    def showEvent(self, QShowEvent):
        #self.pb_ok.setFocus()
        pass

    def set_filename_filter(self, filename_filter):
        # set the FilenameFilter
        self.filename_filter = filename_filter

    def selected_file_count(self):
        return len(self.file_set)

    def selected_file_set(self):
        return frozenset(self.file_set)

    def selection_changed(self, selected, deselected):
        # selected, deselected: PyQt5.QtCore.QItemSelection
        selected_filenames = self.__compute_filenames__(selected)
        self.file_set.update(selected_filenames)
        deselected_filenames = self.__compute_filenames__(deselected)
        self.file_set.difference_update(deselected_filenames)

        self.pb_deselect.setEnabled(self.selected_file_count() > 0)
        self.lb_selection_count.setText(
            str(self.selected_file_count()) + " " + _("resources selected"))

    def item_expanded(self, index):
        # index: a QModelIndex
        # show all child items selected/deselected in accordance with state of parent folder
        pass

    def item_collapsed(self, index):
        pass

    def pb_deselect_clicked(self):
        self.view.selectionModel().clear()

    def hideEvent(self, QHideEvent):
        self.__persist__()
예제 #37
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)
예제 #38
-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)