예제 #1
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)
예제 #2
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)
예제 #3
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))
예제 #4
0
class OpenedFileExplorer(DockWidget):
    """Opened File Explorer is list widget with list of opened files.
    It implements switching current file, files sorting. Uses _OpenedFileModel internally.
    Class instance created by Workspace.
    """
    def __init__(self, workspace):
        DockWidget.__init__(self, workspace, "&Opened Files",
                            QIcon(":/enkiicons/filtered.png"), "Alt+O")

        self._workspace = workspace

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        menu.exec_(self.tvFiles.mapToGlobal(pos))
예제 #5
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)
예제 #6
0
    def createGUI(self):
        root = TagManager.createSimpleTree()

        model = TagTreeModel(root,
                             self._tag_checker,
                             self._color_helper,
                             parent=self)
        model.invalidValueSetted.connect(
            self.invalidValueSettedByUserToTreeViewModel)
        #model.dataChanged.connect(dataChanged)

        self.setMinimumSize(300, 150)
        self.setWindowTitle('My Tag Manager')
        layout = QVBoxLayout(self)
        ## Menu Layout
        menu_layout = QHBoxLayout(self)
        but_save = QPushButton('Save', self)
        but_save.setIcon(QApplication.style().standardIcon(
            QStyle.SP_DialogSaveButton))
        menu_layout.addWidget(but_save)
        but_save.clicked.connect(self.but_save_clicked)

        but_load = QPushButton('Load', self)
        but_load.setIcon(QApplication.style().standardIcon(
            QStyle.SP_DirOpenIcon))
        menu_layout.addWidget(but_load)
        but_load.clicked.connect(self.but_load_clicked)

        but_new_f = QPushButton('New tag collections', self)
        but_new_f.setIcon(QApplication.style().standardIcon(
            QStyle.SP_FileIcon))
        but_new_f.setToolTip('New tag collections')
        menu_layout.addWidget(but_new_f)
        but_new_f.clicked.connect(self.but_new_tree_clicked)
        layout.addLayout(menu_layout)
        ## Menu Layout END

        tv = QTreeView(self)
        tv.setDragDropMode(QAbstractItemView.InternalMove)
        tv.setDragEnabled(True)
        tv.setAcceptDrops(True)
        tv.setDropIndicatorShown(True)
        tv.setContextMenuPolicy(Qt.CustomContextMenu)
        tv.customContextMenuRequested.connect(
            self.customContextMenuRequestedForTreeView)

        tv.setModel(model)
        #tv.setAlternatingRowColors(True)
        layout.addWidget(tv)
        self._widget_tv = tv

        but = QPushButton('Remove Selected', self)
        but.setIcon(QApplication.style().standardIcon(QStyle.SP_TrashIcon))
        layout.addWidget(but)
        but.clicked.connect(self.but_remove_clicked)

        but_add = QPushButton('Add', self)
        layout.addWidget(but_add)
        but_add.clicked.connect(self.but_add_clicked)

        but_add_to_root = QPushButton('Add new element to root')
        but_add_to_root.setToolTip("Add new element to root of tree.")
        layout.addWidget(but_add_to_root)
        but_add_to_root.clicked.connect(self.but_add_to_root_clicked)

        but_item_up = QPushButton("Up")
        but_item_up.setToolTip("Up selected item.")
        but_item_up.clicked.connect(self.but_item_up_clicked)
        layout.addWidget(but_item_up)

        but_item_down = QPushButton("Down")
        but_item_down.setToolTip("Down selected item.")
        but_item_down.clicked.connect(self.but_item_down_clicked)
        layout.addWidget(but_item_down)

        but_font = QPushButton('Set Font', self)
        layout.addWidget(but_font)
        but_font.clicked.connect(self.but_font_dialog_clicked)
        self.layout().deleteLater()  # Remove default layout
        self.setLayout(layout)
예제 #7
0
class Editor(QMainWindow):
    """This is the main class.
    """

    FORMATS = ("Aiken (*.txt);;Cloze (*.cloze);;GIFT (*.gift);;JSON (*.json)"
               ";;LaTex (*.tex);;Markdown (*.md);;PDF (*.pdf);;XML (*.xml)")

    SHORTCUTS = {
        "Create file": Qt.CTRL + Qt.Key_N,
        "Find questions": Qt.CTRL + Qt.Key_F,
        "Read file": Qt.CTRL + Qt.Key_O,
        "Read folder": Qt.CTRL + Qt.SHIFT + Qt.Key_O,
        "Save": Qt.CTRL + Qt.Key_S,
        "Save as": Qt.CTRL + Qt.SHIFT + Qt.Key_S,
        "Add hint": Qt.CTRL + Qt.SHIFT + Qt.Key_H,
        "Remove hint": Qt.CTRL + Qt.SHIFT + Qt.Key_Y,
        "Add answer": Qt.CTRL + Qt.SHIFT + Qt.Key_A,
        "Remove answer": Qt.CTRL + Qt.SHIFT + Qt.Key_Q,
        "Open datasets": Qt.CTRL + Qt.SHIFT + Qt.Key_D
    }

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.setWindowTitle("QAS Editor GUI")

        self._items: List[QWidget] = []
        self._main_editor = None
        self.path: str = None
        self.top_quiz = Category()
        self.cxt_menu = QMenu(self)
        self.cxt_item: QStandardItem = None
        self.cxt_data: _Question | Category= None
        self.cur_question: _Question = None
        self.tagbar: GTagBar = None
        self.main_editor: GTextEditor = None
        self.is_open_find = self.is_open_dataset = False

        with resources.open_text("qas_editor.gui", "stylesheet.css") as ifile:
            self.setStyleSheet(ifile.read())

        self._add_menu_bars()

        # Left side
        self.data_view = QTreeView()
        self.data_view.setIconSize(QSize(18, 18))
        xframe_vbox = self._block_datatree()
        left = QWidget()
        left.setLayout(xframe_vbox)

        # Right side
        self.cframe_vbox = QVBoxLayout()
        self._block_general_data()
        self._block_answer()
        self._block_hints()
        self._block_units()
        self._block_zones()
        self._block_solution()
        self._block_template()
        self.cframe_vbox.addStretch()
        self.cframe_vbox.setSpacing(5)
        for value in self._items:
            value.setEnabled(False)

        frame = QFrame()
        frame.setLineWidth(2)
        frame.setLayout(self.cframe_vbox)
        right = QScrollArea()
        right.setWidget(frame)
        right.setWidgetResizable(True)

        # Create main window divider for the splitter
        splitter = QSplitter()
        splitter.addWidget(left)
        splitter.addWidget(right)
        splitter.setStretchFactor(1, 1)
        splitter.setSizes([250, 100])
        self.setCentralWidget(splitter)

        # Create lower status bar.
        status = QStatusBar()
        self.setStatusBar(status)
        self.cat_name = QLabel()
        status.addWidget(self.cat_name)

        self._update_tree_item(self.top_quiz, self.root_item)
        self.data_view.expandAll()
        self.setGeometry(50, 50, 1200, 650)
        self.show()

    def _debug_me(self):
        self.path = "./test_lib/datasets/moodle/all.xml"
        self.top_quiz = Category.read_files(["./test_lib/datasets/moodle/all.xml"])
        gtags = {}
        self.top_quiz.get_tags(gtags)
        self.tagbar.set_gtags(gtags)
        self.root_item.clear()
        self._update_tree_item(self.top_quiz, self.root_item)
        self.data_view.expandAll()

    def _add_menu_bars(self):
        file_menu = self.menuBar().addMenu("&File")
        tmp = QAction("New file", self)
        tmp.setStatusTip("New file")
        tmp.triggered.connect(self._create_file)
        tmp.setShortcut(self.SHORTCUTS["Create file"])
        file_menu.addAction(tmp)
        tmp = QAction("Open file", self)
        tmp.setStatusTip("Open file")
        tmp.triggered.connect(self._read_file)
        tmp.setShortcut(self.SHORTCUTS["Read file"])
        file_menu.addAction(tmp)
        tmp = QAction("Open folder", self)
        tmp.setStatusTip("Open folder")
        tmp.triggered.connect(self._read_folder)
        tmp.setShortcut(self.SHORTCUTS["Read folder"])
        file_menu.addAction(tmp)
        tmp = QAction("Save", self)
        tmp.setStatusTip("Save top category to specified file on disk")
        tmp.triggered.connect(lambda: self._write_file(False))
        tmp.setShortcut(self.SHORTCUTS["Save"])
        file_menu.addAction(tmp)
        tmp = QAction("Save As...", self)
        tmp.setStatusTip("Save top category to specified file on disk")
        tmp.triggered.connect(lambda: self._write_file(True))
        tmp.setShortcut(self.SHORTCUTS["Save as"])
        file_menu.addAction(tmp)

        file_menu = self.menuBar().addMenu("&Edit")
        tmp = QAction("Shortcuts", self)
        #tmp.setShortcut(self.SHORTCUTS["Read file"])
        file_menu.addAction(tmp)
        tmp = QAction("Datasets", self)
        tmp.triggered.connect(self._open_dataset_popup)
        tmp.setShortcut(self.SHORTCUTS["Open datasets"])
        file_menu.addAction(tmp)
        tmp = QAction("Find Question", self)
        tmp.triggered.connect(self._open_find_popup)
        tmp.setShortcut(self.SHORTCUTS["Find questions"])
        file_menu.addAction(tmp)
        
        self.toolbar = GTextToolbar(self)
        self.addToolBar(Qt.TopToolBarArea, self.toolbar)

    def _add_new_category(self):
        popup = PopupName(self, True)
        popup.show()
        if not popup.exec():
            return
        self.cxt_data.add_subcat(popup.data)
        self._new_item(popup.data, self.cxt_item, "question")

    def _add_new_question(self):
        popup = PopupQuestion(self, self.cxt_data)
        popup.show()
        if not popup.exec():
            return
        self._new_item(popup.question, self.cxt_item, "question")

    @action_handler
    def _append_category(self):
        path, _ = QFileDialog.getOpenFileName(self, "Open file", "",
                                              self.FORMATS)
        if not path:
            return
        quiz = Category.read_files([path], path.rsplit("/", 1)[-1])
        self.cxt_data[quiz.name] = quiz
        self._update_tree_item(quiz, self.cxt_item)

    def _block_answer(self) -> None:
        frame = GCollapsible(self, "Answers")
        self.cframe_vbox.addLayout(frame)
        self._items.append(GOptions(self.toolbar, self.main_editor))
        _shortcut = QShortcut(self.SHORTCUTS["Add answer"], self)
        _shortcut.activated.connect(self._items[-1].add)
        _shortcut = QShortcut(self.SHORTCUTS["Remove answer"], self)
        _shortcut.activated.connect(self._items[-1].pop)
        frame.setLayout(self._items[-1])

    def _block_datatree(self) -> QVBoxLayout:
        self.data_view.setStyleSheet("margin: 5px 5px 0px 5px")
        self.data_view.setHeaderHidden(True)
        self.data_view.doubleClicked.connect(self._update_item)
        self.data_view.setContextMenuPolicy(Qt.CustomContextMenu)
        self.data_view.customContextMenuRequested.connect(self._data_view_cxt)
        self.data_view.setDragEnabled(True)
        self.data_view.setAcceptDrops(True)
        self.data_view.setDropIndicatorShown(True)
        self.data_view.setDragDropMode(QAbstractItemView.InternalMove)
        self.data_view.original_dropEvent = self.data_view.dropEvent
        self.data_view.dropEvent = self._dataview_dropevent
        self.root_item = QStandardItemModel(0, 1)
        self.root_item.setHeaderData(0, Qt.Horizontal, "Classification")
        self.data_view.setModel(self.root_item)

        xframe_vbox = QVBoxLayout()
        xframe_vbox.addWidget(self.data_view)
        return xframe_vbox

    def _block_general_data(self) -> None:
        clayout = GCollapsible(self, "Question Header")
        self.cframe_vbox.addLayout(clayout, 1)

        grid = QVBoxLayout()    # No need of parent. It's inside GCollapsible
        grid.setSpacing(2)

        self.main_editor = GTextEditor(self.toolbar, "question")
        self._items.append(self.main_editor)
        self._items[-1].setToolTip("Question's description text")
        self._items[-1].setMinimumHeight(200)
        grid.addWidget(self._items[-1], 1)
        self.tagbar = GTagBar(self)
        self.tagbar.setToolTip("List of tags used by the question.")
        self._items.append(self.tagbar)
        grid.addWidget(self._items[-1], 0)

        others = QHBoxLayout()  # No need of parent. It's inside GCollapsible
        grid.addLayout(others, 0)

        group_box = QGroupBox("General", self)
        _content = QVBoxLayout(group_box)
        _content.setSpacing(5)
        _content.setContentsMargins(5, 3, 5, 3)
        self._items.append(GField("dbid", self, int))
        self._items[-1].setToolTip("Optional ID for the question.")
        self._items[-1].setFixedWidth(50)
        _content.addWidget(self._items[-1], 0)
        self._items.append(GField("default_grade", self, int))
        self._items[-1].setToolTip("Default grade.")
        self._items[-1].setFixedWidth(50)
        self._items[-1].setText("1.0")
        _content.addWidget(self._items[-1], 0)
        self._items.append(GField("penalty", self, str))
        self._items[-1].setToolTip("Penalty")
        self._items[-1].setFixedWidth(50)
        self._items[-1].setText("0.0")
        _content.addWidget(self._items[-1], 0)
        _content.addStretch()

        others.addWidget(group_box, 0)

        group_box = QGroupBox("Unit Handling", self)
        _content = QVBoxLayout(group_box)
        _content.setSpacing(5)
        _content.setContentsMargins(5, 3, 5, 3)
        self._items.append(GDropbox("grading_type", self, Grading))
        self._items[-1].setToolTip("Grading")
        self._items[-1].setMinimumWidth(80)
        _content.addWidget(self._items[-1], 0)
        self._items.append(GDropbox("show_units", self, ShowUnits))
        self._items[-1].setToolTip("Show units")
        _content.addWidget(self._items[-1], 0)
        self._items.append(GField("unit_penalty", self, float))
        self._items[-1].setToolTip("Unit Penalty")
        self._items[-1].setText("0.0")
        _content.addWidget(self._items[-1], 0)
        self._items.append(GCheckBox("left", "Left side", self))
        _content.addWidget(self._items[-1], 0)
        others.addWidget(group_box, 1)

        group_box = QGroupBox("Multichoices", self)
        _content = QVBoxLayout(group_box)
        _content.setSpacing(5)
        _content.setContentsMargins(5, 3, 5, 3)
        self._items.append(GDropbox("numbering", self, Numbering))
        self._items[-1].setToolTip("How options will be enumerated")
        _content.addWidget(self._items[-1], 0)
        self._items.append(GCheckBox("show_instr", "Instructions", self))
        self._items[-1].setToolTip("If the structions 'select one (or more "
                                   " options)' should be shown")
        _content.addWidget(self._items[-1], 0)
        self._items.append(GCheckBox("single", "Multi answer", self))
        self._items[-1].setToolTip("If there is just a single or multiple "
                                   "valid answers")
        _content.addWidget(self._items[-1], 0)
        self._items.append(GCheckBox("shuffle", "Shuffle", self))
        self._items[-1].setToolTip("If answers should be shuffled (e.g. order "
                                   "of options will change each time)")
        _content.addWidget(self._items[-1], 0)
        others.addWidget(group_box, 1)

        group_box = QGroupBox("Documents", self)
        _content = QGridLayout(group_box)
        _content.setSpacing(5)
        _content.setContentsMargins(5, 3, 5, 3)
        self._items.append(GDropbox("rsp_format", self, ResponseFormat))
        self._items[-1].setToolTip("The format to be used in the reponse.")
        _content.addWidget(self._items[-1], 0, 0, 1, 2)
        self._items.append(GCheckBox("rsp_required",
                                     "Required", self))
        self._items[-1].setToolTip("Require the student to enter some text.")
        _content.addWidget(self._items[-1], 0, 2)
        self._items.append(GField("min_words", self, int))
        self._items[-1].setToolTip("Minimum word limit")
        self._items[-1].setText("0")
        _content.addWidget(self._items[-1], 1, 0)
        self._items.append(GField("max_words", self, int))
        self._items[-1].setToolTip("Maximum word limit")
        self._items[-1].setText("10000")
        _content.addWidget(self._items[-1], 2, 0)
        self._items.append(GField("attachments", self, int))
        self._items[-1].setToolTip("Number of attachments allowed. 0 is none."
                                   " -1 is unlimited. Should be bigger than "
                                   "field below.")
        self._items[-1].setText("-1")
        _content.addWidget(self._items[-1], 1, 1)
        self._items.append(GField("atts_required", self, int))
        self._items[-1].setToolTip("Number of attachments required. 0 is none."
                                   " -1 is unlimited. Should be smaller than "
                                   "field above.")
        self._items[-1].setText("0")
        _content.addWidget(self._items[-1], 2, 1)
        self._items.append(GField("lines", self, int))
        self._items[-1].setToolTip("Input box size.")
        self._items[-1].setText("15")
        _content.addWidget(self._items[-1], 1, 2)
        self._items.append(GField("max_bytes", self, int))
        self._items[-1].setToolTip("Maximum file size.")
        self._items[-1].setText("1Mb")
        _content.addWidget(self._items[-1], 2, 2)
        self._items.append(GField("file_types", self, str))
        self._items[-1].setToolTip("Accepted file types (comma separeted).")
        self._items[-1].setText(".txt, .pdf")
        _content.addWidget(self._items[-1], 3, 0, 1, 3)
        others.addWidget(group_box, 1)

        _wrapper = QVBoxLayout()  # No need of parent. It's inside GCollapsible
        group_box = QGroupBox("Random", self)
        _wrapper.addWidget(group_box)
        _content = QVBoxLayout(group_box)
        _content.setSpacing(5)
        _content.setContentsMargins(5, 3, 5, 3)
        self._items.append(GCheckBox("subcats", "Subcats", self))
        self._items[-1].setToolTip("If questions wshould be choosen from "
                                   "subcategories too.")
        _content.addWidget(self._items[-1])
        self._items.append(GField("choose", self, int))
        self._items[-1].setToolTip("Number of questions to select.")
        self._items[-1].setText("5")
        self._items[-1].setFixedWidth(85)
        _content.addWidget(self._items[-1])

        group_box = QGroupBox("Fill-in", self)
        _wrapper.addWidget(group_box)
        _content = QVBoxLayout(group_box)
        _content.setContentsMargins(5, 3, 5, 3)
        self._items.append(GCheckBox("use_case", "Match case", self))
        self._items[-1].setToolTip("If text is case sensitive.")
        _content.addWidget(self._items[-1])

        others.addLayout(_wrapper, 0)

        group_box = QGroupBox("Datasets", self)
        _content = QGridLayout(group_box)
        _content.setSpacing(5)
        _content.setContentsMargins(5, 3, 5, 3)
        self._items.append(GList("datasets", self))
        self._items[-1].setFixedHeight(70)
        self._items[-1].setToolTip("List of datasets used by this question.")
        _content.addWidget(self._items[-1], 0, 0, 1, 2)
        self._items.append(GDropbox("synchronize", self, Synchronise))
        self._items[-1].setToolTip("How should the databases be synchronized.")
        self._items[-1].setMinimumWidth(70)
        _content.addWidget(self._items[-1], 1, 0)
        _gen = QPushButton("Gen", self)
        _gen.setToolTip("Generate new items based on the max, min and decimal "
                        "values of the datasets, and the current solution.")
        _gen.clicked.connect(self._gen_items)
        _content.addWidget(_gen, 1, 1)
        others.addWidget(group_box, 2)

        others.addStretch()
        clayout.setLayout(grid)
        clayout._toggle()

    def _block_hints(self) -> None:
        clayout = GCollapsible(self, "Hints")
        self.cframe_vbox.addLayout(clayout)
        self._items.append(GHintsList(None, self.toolbar))
        _shortcut = QShortcut(self.SHORTCUTS["Add hint"], self)
        _shortcut.activated.connect(self._items[-1].add)
        _shortcut = QShortcut(self.SHORTCUTS["Remove hint"], self)
        _shortcut.activated.connect(self._items[-1].pop)
        clayout.setLayout(self._items[-1])

    def _block_solution(self) -> None:
        collapsible = GCollapsible(self, "Solution and Feedback")
        self.cframe_vbox.addLayout(collapsible)
        layout = QVBoxLayout()
        collapsible.setLayout(layout)
        self._items.append(GTextEditor(self.toolbar, "feedback"))
        self._items[-1].setMinimumHeight(100)
        self._items[-1].setToolTip("General feedback for the question. May "
                                   "also be used to describe solutions.")
        layout.addWidget(self._items[-1])
        sframe = QFrame(self)
        sframe.setStyleSheet(".QFrame{border:1px solid rgb(41, 41, 41);"
                             "background-color: #e4ebb7}")
        layout.addWidget(sframe)
        _content = QGridLayout(sframe)
        self._items.append(GTextEditor(self.toolbar, "if_correct"))
        self._items[-1].setToolTip("Feedback for correct answer")
        _content.addWidget(self._items[-1], 0, 0)
        self._items.append(GTextEditor(self.toolbar, "if_incomplete"))
        self._items[-1].setToolTip("Feedback for incomplete answer")
        _content.addWidget(self._items[-1], 0, 1)
        self._items.append(GTextEditor(self.toolbar, "if_incorrect"))
        self._items[-1].setToolTip("Feedback for incorrect answer")
        _content.addWidget(self._items[-1], 0, 2)
        self._items.append(GCheckBox("show_num", "Show the number of correct "
                                     "responses once the question has finished"
                                     , self))
        _content.addWidget(self._items[-1], 2, 0, 1, 3)
        _content.setColumnStretch(3, 1)

    def _block_template(self) -> None:
        collapsible = GCollapsible(self, "Templates")
        self.cframe_vbox.addLayout(collapsible)
        layout = QVBoxLayout()
        collapsible.setLayout(layout)
        self._items.append(GTextEditor(self.toolbar, "template"))
        self._items[-1].setMinimumHeight(70)
        self._items[-1].setToolTip("Text displayed in the response input box "
                                    "when a new attempet is started.")
        layout.addWidget(self._items[-1])
        self._items.append(GTextEditor(self.toolbar, "grader_info"))
        self._items[-1].setMinimumHeight(50)
        self._items[-1].setToolTip("Information for graders.")
        layout.addWidget(self._items[-1])

    def _block_units(self):
        collapsible = GCollapsible(self, "Units")
        self.cframe_vbox.addLayout(collapsible)

    def _block_zones(self):
        collapsible = GCollapsible(self, "Background and Zones")
        self.cframe_vbox.addLayout(collapsible)

    @action_handler
    def _clone_shallow(self) -> None:
        new_data = copy.copy(self.cxt_data)
        self._new_item(new_data, self.cxt_item.parent(), "question")

    @action_handler
    def _clone_deep(self) -> None:
        new_data = copy.deepcopy(self.cxt_data)
        self._new_item(new_data, self.cxt_itemparent(), "question")

    @action_handler
    def _create_file(self, *_):
        self.top_quiz = Category()
        self.path = None
        self.root_item.clear()
        self._update_tree_item(self.top_quiz, self.root_item)

    @action_handler
    def _dataview_dropevent(self, event: QDropEvent):
        from_obj = self.data_view.selectedIndexes()[0].data(257)
        to_obj = self.data_view.indexAt(event.pos()).data(257)
        if isinstance(to_obj, Category):
            if isinstance(from_obj, _Question):
                to_obj.add_subcat(from_obj)
            else:
                to_obj.add_question(from_obj)
        else:
            event.ignore()
        self.data_view.original_dropEvent(event)

    def _data_view_cxt(self, event):
        model_idx = self.data_view.indexAt(event)
        self.cxt_item = self.root_item.itemFromIndex(model_idx)
        self.cxt_data = model_idx.data(257)
        self.cxt_menu.clear()
        rename = QAction("Rename", self)
        rename.triggered.connect(self._rename_category)
        self.cxt_menu.addAction(rename)
        if self.cxt_item != self.root_item.item(0):
            tmp = QAction("Delete", self)
            tmp.triggered.connect(self._delete_item)
            self.cxt_menu.addAction(tmp)
            tmp = QAction("Clone (Shallow)", self)
            tmp.triggered.connect(self._clone_shallow)
            self.cxt_menu.addAction(tmp)
            tmp = QAction("Clone (Deep)", self)
            tmp.triggered.connect(self._clone_deep)
            self.cxt_menu.addAction(tmp)
        if isinstance(self.cxt_data, Category):
            tmp = QAction("Save as", self)
            tmp.triggered.connect(lambda: self._write_quiz(self.cxt_data, True))
            self.cxt_menu.addAction(tmp)
            tmp = QAction("Append", self)
            tmp.triggered.connect(self._append_category)
            self.cxt_menu.addAction(tmp)
            tmp = QAction("Sort", self)
            #tmp.triggered.connect(self._add_new_category)
            self.cxt_menu.addAction(tmp)
            tmp = QAction("New Question", self)
            tmp.triggered.connect(self._add_new_question)
            self.cxt_menu.addAction(tmp)
            tmp = QAction("New Category", self)
            tmp.triggered.connect(self._add_new_category)
            self.cxt_menu.addAction(tmp)
        self.cxt_menu.popup(self.data_view.mapToGlobal(event))

    @action_handler
    def _delete_item(self, *_):
        self.cxt_item.parent().removeRow(self.cxt_item.index().row())
        cat = self.cxt_data.parent
        if isinstance(self.cxt_data, _Question):
            cat.pop_question(self.cxt_data)
        elif isinstance(self.cxt_data, Category):
            cat.pop_subcat(self.cxt_data)

    @action_handler
    def _gen_items(self, _):
        pass

    def _new_item(self, data: Category, parent: QStandardItem, title: str):

        name = f"{data.__class__.__name__}_icon.png".lower()
        item = None
        with resources.path("qas_editor.images", name) as path:
            item = QStandardItem(QIcon(path.as_posix()), data.name)
            item.setEditable(False)
            item.setData(QVariant(data))
            parent.appendRow(item)
        return item

    @action_handler
    def _open_dataset_popup(self, _):
        if not self.is_open_dataset:
            popup = PopupDataset(self, self.top_quiz)
            popup.show()
            self.is_open_dataset = True

    @action_handler
    def _open_find_popup(self, _):
        if not self.is_open_find:
            popup = PopupFind(self, self.top_quiz, self.tagbar.cat_tags)
            popup.show()
            self.is_open_find = True

    @action_handler
    def _read_file(self, _):
        files, _ = QFileDialog.getOpenFileNames(self, "Open file", "",
                                                self.FORMATS)
        if not files:
            return
        if len(files) == 1:
            self.path = files[0]
        self.top_quiz = Category.read_files(files)
        gtags = {}
        self.top_quiz.get_tags(gtags)
        self.tagbar.set_gtags(gtags)
        self.root_item.clear()
        self._update_tree_item(self.top_quiz, self.root_item)
        self.data_view.expandAll()

    @action_handler
    def _read_folder(self, _):
        dialog = QFileDialog(self)
        dialog.setFileMode(QFileDialog.FileMode.Directory)
        if not dialog.exec():
            return
        self.top_quiz = Category()
        self.path = None
        for folder in dialog.selectedFiles():
            cat = folder.rsplit("/", 1)[-1]
            quiz = Category.read_files(glob.glob(f"{folder}/*"), cat)
            self.top_quiz.add_subcat(quiz)
        gtags = {}
        self.top_quiz.get_tags(gtags)
        self.tagbar.set_gtags(gtags)
        self.root_item.clear()
        self._update_tree_item(self.top_quiz, self.root_item)
        self.data_view.expandAll()

    @action_handler
    def _rename_category(self, *_):
        popup = PopupName(self, False)
        popup.show()
        if not popup.exec():
            return
        self.cxt_data.name = popup.data
        self.cxt_item.setText(popup.data)

    @action_handler
    def _update_item(self, model_index: QModelIndex) -> None:
        item = model_index.data(257)
        if isinstance(item, _Question):
            for key in self._items:
                attr = key.get_attr()
                if attr in item.__dict__:
                    key.setEnabled(True)
                    key.from_obj(item)
                else:
                    key.setEnabled(False)
            self.cur_question = item
        path = [f" ({item.__class__.__name__})"]
        while item.parent:
            path.append(item.name)
            item = item.parent
        path.append(item.name)
        path.reverse()
        self.cat_name.setText(" > ".join(path[:-1]) + path[-1])

    def _update_tree_item(self, data: Category, parent: QStandardItem) -> None:
        item = self._new_item(data, parent, "category")
        for k in data.questions:
            self._new_item(k, item, "question")
        for k in data:
            self._update_tree_item(data[k], item)

    @action_handler
    def _write_quiz(self, quiz: Category, save_as: bool):
        if save_as or self.path is None:
            path, _ = QFileDialog.getSaveFileName(self, "Save file", "",
                                                  self.FORMATS)
            if not path:
                return None
        else:
            path = self.path
        ext = path.rsplit('.', 1)[-1]
        getattr(quiz, quiz.SERIALIZERS[ext][1])(path)
        return path

    def _write_file(self, save_as: bool) -> None:
        path = self._write_quiz(self.top_quiz, save_as)
        if path:
            self.path = path
예제 #8
0
class OpenedFileExplorer(DockWidget):
    """Opened File Explorer is list widget with list of opened files.
    It implements switching current file, files sorting. Uses _OpenedFileModel internally.
    Class instance created by Workspace.
    """
    def __init__(self, workspace):
        DockWidget.__init__(self, workspace, "&Opened Files",
                            QIcon(":/enkiicons/filtered.png"), "Alt+O")

        self._workspace = workspace

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

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

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

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

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

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

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

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

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

        # 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)
예제 #9
0
class FileBrowserWidget(QWidget):
    on_open = pyqtSignal(str)

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

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

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

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

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

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

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

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

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

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

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

    def restoreState(self, state):
        try:
            self.setRoot(state["root"])
        except:
            pass
        try:
            idx = self.model.index(state["sel"])
            if idx.isValid():
                self.tree.expand(idx)
                self.tree.setCurrentIndex(idx)
                self.tree.scrollTo(idx, QAbstractItemView.PositionAtCenter)
        except:
            pass
예제 #10
0
class App(QWidget):
    def __init__(self):
        super().__init__()
        self.title = 'Whenson\'s Custom INI Merger'
        self.geo = (200, 200, 800, 600)
        self.initGUI()

    def initGUI(self):
        # windows title and geometry
        self.setGeometry(*(self.geo))
        self.setWindowTitle(self.title)
        self.show()

        # master layout and widget definition
        self.layout = QHBoxLayout()

        self.file_box = QVBoxLayout()

        self.list_box = QVBoxLayout()
        self.btn_group = QVBoxLayout()

        # deploy layout
        self.setLayout(self.layout)
        self.layout.addLayout(self.file_box)

        self.layout.addLayout(self.list_box)
        self.layout.addLayout(self.btn_group)

        # file box
        self.file_box.addWidget(QLabel('File Browser'))
        self.folder_tree = QTreeView()
        self.file_box.addWidget(self.folder_tree)
        self.file_list = QTreeView()
        self.file_box.addWidget(self.file_list)

        # list box

        self.normal_list = DropListWidget()
        self.demo_list = DropListWidget()

        self.list_box.addWidget(QLabel('Output File'))
        self.list_box.addWidget(QLineEdit())

        self.list_box.addWidget(QLabel('Module List'))
        self.list_box.addWidget(self.normal_list)

        temp = QHBoxLayout()
        self.up_btn = QPushButton()
        self.up_btn.setText('ᐱ')
        self.down_btn = QPushButton()
        self.down_btn.setText('ᐯ')
        self.list_box.addLayout(temp)
        temp.addWidget(self.up_btn)
        temp.addWidget(self.down_btn)

        self.list_box.addWidget(QLabel('With Demostration'))
        self.list_box.addWidget(self.demo_list)

        # button group
        self.merge_btn = QPushButton('Merge')
        self.btn_group.addWidget(self.merge_btn)

        self.load_btn = QPushButton('Load')
        self.btn_group.addWidget(self.load_btn)

        self.init_file_tree()
        self.init_drag()

    def init_file_tree(self):
        self.dir_model = QFileSystemModel()
        self.dir_model.setRootPath('C:/')
        self.dir_model.setFilter(QDir.AllDirs | QDir.NoDotAndDotDot)
        self.folder_tree.setModel(self.dir_model)
        self.folder_tree.hideColumn(1)
        self.folder_tree.hideColumn(2)
        self.folder_tree.hideColumn(3)

        self.file_model = QFileSystemModel()
        self.file_model.setFilter(QDir.NoDotAndDotDot | QDir.Files)
        self.file_list.setModel(self.file_model)
        self.file_list.setRootIndex(self.file_model.setRootPath('C:/'))
        self.file_model.setNameFilters(['*.txt', '*.ini', '*.map'])
        self.file_model.setNameFilterDisables(False)
        self.folder_tree.clicked.connect(self.tree_on_clicked)
        self.file_list.setSelectionMode(QAbstractItemView.ExtendedSelection)

    def init_drag(self):
        self.file_list.setDragEnabled(True)
        self.file_list.setDropIndicatorShown(True)
        # self.file_list.setDefaultDropAction(Qt.MoveAction)

    def tree_on_clicked(self, index):
        path = self.dir_model.fileInfo(index).absoluteFilePath()
        self.file_list.setRootIndex(self.file_model.setRootPath(path))