class FileSystemTable(QTableView, TableType): if IN_DESIGNER: from PyQt5.QtCore import Q_ENUMS Q_ENUMS(TableType) gcodeFileSelected = Signal(bool) filePreviewText = Signal(str) fileNamePreviewText = Signal(str) transferFileRequest = Signal(str) rootChanged = Signal(str) def __init__(self, parent=None): super(FileSystemTable, self).__init__(parent) self._table_type = TableType.Local self._hidden_columns = '' # This prevents doing unneeded initialization # when QtDesginer loads the plugin. if parent is None: return self.parent = parent self.path_data = dict() self.doubleClicked.connect(self.openSelectedItem) self.selected_row = None self.clipboard = QApplication.clipboard() self.model = QFileSystemModel() self.model.setReadOnly(True) self.model.setFilter(QDir.AllDirs | QDir.NoDotAndDotDot | QDir.AllEntries) self.setModel(self.model) self.verticalHeader().hide() self.horizontalHeader().setStretchLastSection(True) self.setAlternatingRowColors(True) self.setSelectionBehavior(QAbstractItemView.SelectRows) self.selection_model = self.selectionModel() self.selection_model.selectionChanged.connect(self.onSelectionChanged) self.info = Info() self.editor = self.info.getEditor() self._nc_file_dir = self.info.getProgramPrefix() self.nc_file_exts = self.info.getProgramExtentions() self.setRootPath(self._nc_file_dir) def showEvent(self, event=None): self.rootChanged.emit(self._nc_file_dir) def onSelectionChanged(self, selected, deselected): if len(selected) == 0: return index = selected.indexes()[0] path = self.model.filePath(index) if os.path.isfile(path): self.gcodeFileSelected.emit(True) with open(path, 'r') as fh: content = fh.read() self.filePreviewText.emit(content) self.fileNamePreviewText.emit(path) else: self.gcodeFileSelected.emit(False) self.filePreviewText.emit('') self.fileNamePreviewText.emit('') @Slot() def openSelectedItem(self, index=None): """If ngc file, opens in LinuxCNC, if dir displays dir.""" if index is None: selection = self.getSelection() if selection is None: return index = selection[0] path = self.model.filePath(self.rootIndex()) name = self.model.filePath(index) absolute_path = os.path.join(path, name) file_info = QFileInfo(absolute_path) if file_info.isDir(): self.model.setRootPath(absolute_path) self.setRootIndex(self.model.index(absolute_path)) self.rootChanged.emit(absolute_path) elif file_info.isFile(): # if file_info.completeSuffix() not in self.nc_file_exts: # LOG.warn("Unsuported NC program type with extention .%s", # file_info.completeSuffix()) loadProgram(absolute_path) @Slot() def editSelectedFile(self): """Open the selected file in editor.""" selection = self.getSelection() if selection is not None: path = self.model.filePath(selection[0]) subprocess.Popen([self.editor, path]) return False @Slot() def loadSelectedFile(self): """Loads the selected file into LinuxCNC.""" selection = self.getSelection() if selection is not None: path = self.model.filePath(selection[0]) loadProgram(path) return True return False @Slot() def selectPrevious(self): """Select the previous item in the view.""" selection = self.getSelection() if selection is None: # select last item in view self.selectRow(self.model.rowCount(self.rootIndex()) - 1) else: self.selectRow(selection[0].row() - 1) return True @Slot() def selectNext(self): """Select the next item in the view.""" selection = self.getSelection() if selection is None: # select first item in view self.selectRow(0) else: self.selectRow(selection[-1].row() + 1) return True @Slot() def rename(self): """renames the selected file or folder""" index = self.selectionModel().currentIndex() path = self.model.filePath(index) if path: file_info = QFileInfo(path) if file_info.isFile(): filename = self.rename_dialog("file") if filename: q_file = QFile(path) file_info.absolutePath() new_path = os.path.join(file_info.absolutePath(), str(filename)) q_file.rename(new_path) elif file_info.isDir(): filename = self.rename_dialog("directory") if filename: directory = QDir(path) file_info.absolutePath() new_path = os.path.join(file_info.absolutePath(), str(filename)) directory.rename(path, new_path) @Slot() def newFile(self): """Create a new empty file""" path = self.model.filePath(self.rootIndex()) new_file_path = os.path.join(path, "New File.ngc") count = 1 while os.path.exists(new_file_path): new_file_path = os.path.join(path, "New File {}.ngc".format(count)) count += 1 new_file = QFile(new_file_path) new_file.open(QIODevice.ReadWrite) @Slot() def newFolder(self): path = self.model.filePath(self.rootIndex()) new_name = 'New Folder' count = 1 while os.path.exists(os.path.join(path, new_name)): new_name = "New Folder {}".format(count) count += 1 directory = QDir(path) directory.mkpath(new_name) directory.setPath(new_name) @Slot() @deprecated(replaced_by='newFolder', reason='for consistency with newFile method name') def createDirectory(self): self.newFolder() @Slot() def deleteItem(self): """Delete the selected item (either a file or folder).""" # ToDo: use Move2Trash, instead of deleting the file index = self.selectionModel().currentIndex() path = self.model.filePath(index) if path: file_info = QFileInfo(path) if file_info.isFile(): if not self.ask_dialog( "Do you wan't to delete the selected file?"): return q_file = QFile(path) q_file.remove() elif file_info.isDir(): if not self.ask_dialog( "Do you wan't to delete the selected directory?"): return directory = QDir(path) directory.removeRecursively() @Slot() @deprecated(replaced_by='deleteItem', reason='because of unclear method name') def deleteFile(self): self.deleteItem() @Slot(str) def setRootPath(self, root_path): """Sets the currently displayed path.""" self.rootChanged.emit(root_path) self.model.setRootPath(root_path) self.setRootIndex(self.model.index(root_path)) return True @Slot() def viewParentDirectory(self): """View the parent directory of the current view.""" path = self.model.filePath(self.rootIndex()) file_info = QFileInfo(path) directory = file_info.dir() new_path = directory.absolutePath() currentRoot = self.rootIndex() self.model.setRootPath(new_path) self.setRootIndex(currentRoot.parent()) self.rootChanged.emit(new_path) @Slot() @deprecated(replaced_by='viewParentDirectory') def goUP(self): self.viewParentDirecotry() @Slot() def viewHomeDirectory(self): self.setRootPath(os.path.expanduser('~/')) @Slot() def viewNCFilesDirectory(self): # ToDo: Make preset user definable path = os.path.expanduser('~/linuxcnc/nc_files') self.setRootPath(path) @Slot() def viewPresetDirectory(self): # ToDo: Make preset user definable preset = os.path.expanduser('~/linuxcnc/nc_files') self.setRootPath(preset) @Slot() def doFileTransfer(self): index = self.selectionModel().currentIndex() path = self.model.filePath(index) self.transferFileRequest.emit(path) @Slot(str) def transferFile(self, src_path): dest_path = self.model.filePath(self.rootIndex()) src_file = QFile() src_file.setFileName(src_path) src_file_info = QFileInfo(src_path) dst_path = os.path.join(dest_path, src_file_info.fileName()) src_file.copy(dst_path) @Slot() def getSelection(self): """Returns list of selected indexes, or None.""" selection = self.selection_model.selectedIndexes() if len(selection) == 0: return None return selection @Slot() def getCurrentDirectory(self): return self.model.rootPath() @Property(TableType) def tableType(self): return self._table_type @tableType.setter def tableType(self, table_type): self._table_type = table_type if table_type == TableType.Local: self.setRootPath(self._nc_file_dir) else: self.setRootPath('/media/') @Property(str) def hiddenColumns(self): """String of comma separated column numbers to hide.""" return self._hidden_columns @hiddenColumns.setter def hiddenColumns(self, columns): try: col_list = [int(c) for c in columns.split(',') if c != ''] except: return False self._hidden_columns = columns header = self.horizontalHeader() for col in range(4): if col in col_list: header.hideSection(col) else: header.showSection(col) def ask_dialog(self, message): box = QMessageBox.question(self.parent, 'Are you sure?', message, QMessageBox.Yes, QMessageBox.No) if box == QMessageBox.Yes: return True else: return False def rename_dialog(self, data_type): text, ok_pressed = QInputDialog.getText( self.parent, "Rename", "New {} name:".format(data_type), QLineEdit.Normal, "") if ok_pressed and text != '': return text else: return False
def __init__(self, settings, path=None): super(RootWidget, self).__init__() # --- setup the file system model --- model = QFileSystemModel() model.setRootPath('This PC') model.setReadOnly(False) model.setIconProvider( JSONFileIconProvider('file_view_icons.json') ) model.setFilter( QDir.AllEntries | QDir.NoDotAndDotDot | QDir.AllDirs | QDir.Hidden ) self._model = FileSystemProxyModel() self._model.setSourceModel(model) # --- setup the tree view --- self._view = QTreeView() self._view.setModel(self._model) self._view.setSelectionMode(QTreeView.ExtendedSelection) self._view.activated.connect(self._handle_activated_index) self._view.setSortingEnabled(True) self._view.setHeaderHidden(True) self._view.setExpandsOnDoubleClick(False) self._view.sortByColumn(0, Qt.AscendingOrder) # Setup drag and drop. self._view.setDragDropMode(QTreeView.DragDrop) self._view.setDefaultDropAction(Qt.MoveAction) # Setup the columns that show. self._view.hideColumn(1) self._view.hideColumn(2) self._view.hideColumn(3) # Note that the double click trigger cannot be in this list, otherwise it flashes in edit # mode before opening the file self._view.setEditTriggers( QTreeView.SelectedClicked | QTreeView.EditKeyPressed) # setup the context menu self._view.setContextMenuPolicy(Qt.CustomContextMenu) self._view.customContextMenuRequested.connect(self._context_menu) # Unselect items on collapse. self._view.collapsed.connect(self._view.clearSelection) # --- setup the current root path label --- self._root_edit = PathEdit() self._root_edit.new_path.connect(self._set_root_path) # --- setup the tool bar --- tool_bar = QToolBar() new_file_action = QAction('New File', self) tool_bar.addAction(new_file_action) tool_bar.widgetForAction(new_file_action).setObjectName('new_file') new_file_action.triggered.connect(self._create_file) new_directory_action = QAction('New Directory', self) tool_bar.addAction(new_directory_action) tool_bar.widgetForAction(new_directory_action).setObjectName('new_directory') new_directory_action.triggered.connect(self._create_directory) close_action = QAction('Close', self) tool_bar.addAction(close_action) self._close_widget = tool_bar.widgetForAction(close_action) self._close_widget.setObjectName('close_root') close_action.triggered.connect(self.close_request.emit) # --- setup the layout --- main_layout = QVBoxLayout() main_layout.setContentsMargins(0, 0, 0, 0) main_layout.setSpacing(0) path_layout = QHBoxLayout() path_layout.setContentsMargins(0, 0, 0, 0) main_layout.setSpacing(0) path_layout.addWidget(self._root_edit) path_layout.addWidget(tool_bar) main_layout.addLayout(path_layout) main_layout.addWidget(self._view) self.setLayout(main_layout) if path is not None: self._set_root_path(path) self._root_edit.update(path) self._settings = None self.update_settings(settings)