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)
class Demo(QWidget): def __init__(self): super(Demo, self).__init__() self.resize(600, 300) self.model = QDirModel(self) # 1 self.model.setReadOnly(False) self.model.setSorting(QDir.Name | QDir.IgnoreCase) self.tree = QTreeView(self) # 2 self.tree.setModel(self.model) self.tree.clicked.connect(self.show_info) self.index = self.model.index(QDir.currentPath()) self.tree.expand(self.index) self.tree.scrollTo(self.index) self.info_label = QLabel(self) # 3 self.v_layout = QVBoxLayout() self.v_layout.addWidget(self.tree) self.v_layout.addWidget(self.info_label) self.setLayout(self.v_layout) def show_info(self): # 4 index = self.tree.currentIndex() file_name = self.model.fileName(index) file_path = self.model.filePath(index) file_info = 'File Name: {}\nFile Path: {}'.format(file_name, file_path) self.info_label.setText(file_info)
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))
class Dialog_ImageFolder(): def __init__(self, parent, title, init_path): self.w = QDialog(parent) self.parent = parent self.left = 300 self.top = 300 self.width = 600 self.height = 400 self.title = title self.dirModel = QFileSystemModel() self.dirModel.setRootPath(init_path) self.dirModel.setFilter(QDir.NoDotAndDotDot | QDir.AllDirs) self.treeview = QTreeView() self.treeview.setModel(self.dirModel) self.treeview.setRootIndex(self.dirModel.index("")) self.treeview.clicked.connect(self.on_clicked) #--- Hide All Header Sections Except First ---- header = self.treeview.header() for sec in range(1, header.count()): header.setSectionHidden(sec, True) #--- ---- ---- ---- ---- ---- ---- ---- ---- -- focus_index = self.dirModel.index(init_path) self.treeview.setCurrentIndex(focus_index) self.current_row_changed() self.listview = QListView() self.listview.setViewMode(QListView.IconMode) self.listview.setIconSize(QSize(192, 192)) targetfiles1 = glob.glob(os.path.join(init_path, '*.png')) targetfiles2 = glob.glob(os.path.join(init_path, '*.tif')) targetfiles3 = glob.glob(os.path.join(init_path, '*.tiff')) targetfiles = targetfiles1 + targetfiles2 + targetfiles3 lm = _MyListModel(targetfiles, self.parent) self.listview.setModel(lm) self.sub_layout = QHBoxLayout() self.sub_layout.addWidget(self.treeview) self.sub_layout.addWidget(self.listview) self.buttonBox = QDialogButtonBox(QDialogButtonBox.Open | QDialogButtonBox.Cancel) self.buttonBox.accepted.connect(self.accept) self.buttonBox.rejected.connect(self.reject) self.main_layout = QVBoxLayout() self.main_layout.addLayout(self.sub_layout) self.main_layout.addWidget(self.buttonBox) self.w.setGeometry(self.left, self.top, self.width, self.height) self.w.setWindowTitle(self.title) self.w.setWindowIcon(QIcon(os.path.join(icon_dir, 'Mojo2_16.png'))) self.w.setLayout(self.main_layout) def current_row_changed(self): index = self.treeview.currentIndex() self.treeview.scrollTo(index, QAbstractItemView.EnsureVisible) self.treeview.resizeColumnToContents(0) def on_clicked(self, index): path = self.dirModel.fileInfo(index).absoluteFilePath() targetfiles1 = glob.glob(os.path.join(path, '*.png')) targetfiles2 = glob.glob(os.path.join(path, '*.tif')) targetfiles3 = glob.glob(os.path.join(path, '*.tiff')) targetfiles = targetfiles1 + targetfiles2 + targetfiles3 lm = _MyListModel(targetfiles, self.parent) self.listview.setModel(lm) def accept(self): index = self.treeview.currentIndex() self.newdir = self.dirModel.filePath(index) self.w.done(1) def reject(self): self.w.done(0) def GetValue(self): index = self.treeview.currentIndex() self.newdir = self.dirModel.filePath(index) return self.newdir
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))
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)
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)
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)
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
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")