class FilenamePrompt(_BasePrompt): """A prompt for a filename.""" def __init__(self, question, parent=None): super().__init__(question, parent) self._init_texts(question) self._init_fileview() self._set_fileview_root(question.default) self._lineedit = LineEdit(self) if question.default: self._lineedit.setText(question.default) self._lineedit.textEdited.connect(self._set_fileview_root) self._vbox.addWidget(self._lineedit) self.setFocusProxy(self._lineedit) self._init_key_label() if config.get('ui', 'prompt-filebrowser'): self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred) @pyqtSlot(str) def _set_fileview_root(self, path, *, tabbed=False): """Set the root path for the file display.""" separators = os.sep if os.altsep is not None: separators += os.altsep dirname = os.path.dirname(path) try: if not path: pass elif path in separators and os.path.isdir(path): # Input "/" -> don't strip anything pass elif path[-1] in separators and os.path.isdir(path): # Input like /foo/bar/ -> show /foo/bar/ contents path = path.rstrip(separators) elif os.path.isdir(dirname) and not tabbed: # Input like /foo/ba -> show /foo contents path = dirname else: return except OSError: log.prompt.exception("Failed to get directory information") return root = self._file_model.setRootPath(path) self._file_view.setRootIndex(root) @pyqtSlot(QModelIndex) def _insert_path(self, index, *, clicked=True): """Handle an element selection. Args: index: The QModelIndex of the selected element. clicked: Whether the element was clicked. """ path = os.path.normpath(self._file_model.filePath(index)) if clicked: path += os.sep else: # On Windows, when we have C:\foo and tab over .., we get C:\ path = path.rstrip(os.sep) log.prompt.debug('Inserting path {}'.format(path)) self._lineedit.setText(path) self._lineedit.setFocus() self._set_fileview_root(path, tabbed=True) if clicked: # Avoid having a ..-subtree highlighted self._file_view.setCurrentIndex(QModelIndex()) def _init_fileview(self): self._file_view = QTreeView(self) self._file_model = QFileSystemModel(self) self._file_view.setModel(self._file_model) self._file_view.clicked.connect(self._insert_path) if config.get('ui', 'prompt-filebrowser'): self._vbox.addWidget(self._file_view) else: self._file_view.hide() # Only show name self._file_view.setHeaderHidden(True) for col in range(1, 4): self._file_view.setColumnHidden(col, True) # Nothing selected initially self._file_view.setCurrentIndex(QModelIndex()) # The model needs to be sorted so we get the correct first/last index self._file_model.directoryLoaded.connect( lambda: self._file_model.sort(0)) def accept(self, value=None): text = value if value is not None else self._lineedit.text() text = downloads.transform_path(text) if text is None: message.error("Invalid filename") return False self.question.answer = text return True def item_focus(self, which): # This duplicates some completion code, but I don't see a nicer way... assert which in ['prev', 'next'], which selmodel = self._file_view.selectionModel() parent = self._file_view.rootIndex() first_index = self._file_model.index(0, 0, parent) row = self._file_model.rowCount(parent) - 1 last_index = self._file_model.index(row, 0, parent) if not first_index.isValid(): # No entries return assert last_index.isValid() idx = selmodel.currentIndex() if not idx.isValid(): # No item selected yet idx = last_index if which == 'prev' else first_index elif which == 'prev': idx = self._file_view.indexAbove(idx) else: assert which == 'next', which idx = self._file_view.indexBelow(idx) # wrap around if we arrived at beginning/end if not idx.isValid(): idx = last_index if which == 'prev' else first_index selmodel.setCurrentIndex( idx, QItemSelectionModel.ClearAndSelect | QItemSelectionModel.Rows) self._insert_path(idx, clicked=False) def _allowed_commands(self): return [('prompt-accept', 'Accept'), ('leave-mode', 'Abort')]
class FilenamePrompt(_BasePrompt): """A prompt for a filename.""" def __init__(self, question, parent=None): super().__init__(question, parent) self._init_texts(question) self._init_key_label() self._lineedit = LineEdit(self) if question.default: self._lineedit.setText(question.default) self._lineedit.textEdited.connect(self._set_fileview_root) self._vbox.addWidget(self._lineedit) self.setFocusProxy(self._lineedit) self._init_fileview() self._set_fileview_root(question.default) if config.val.prompt.filebrowser: self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred) self._to_complete = '' @pyqtSlot(str) def _set_fileview_root(self, path, *, tabbed=False): """Set the root path for the file display.""" separators = os.sep if os.altsep is not None: separators += os.altsep dirname = os.path.dirname(path) basename = os.path.basename(path) if not tabbed: self._to_complete = '' try: if not path: pass elif path in separators and os.path.isdir(path): # Input "/" -> don't strip anything pass elif path[-1] in separators and os.path.isdir(path): # Input like /foo/bar/ -> show /foo/bar/ contents path = path.rstrip(separators) elif os.path.isdir(dirname) and not tabbed: # Input like /foo/ba -> show /foo contents path = dirname self._to_complete = basename else: return except OSError: log.prompt.exception("Failed to get directory information") return root = self._file_model.setRootPath(path) self._file_view.setRootIndex(root) @pyqtSlot(QModelIndex) def _insert_path(self, index, *, clicked=True): """Handle an element selection. Args: index: The QModelIndex of the selected element. clicked: Whether the element was clicked. """ if index == QModelIndex(): path = os.path.join(self._file_model.rootPath(), self._to_complete) else: path = os.path.normpath(self._file_model.filePath(index)) if clicked: path += os.sep else: # On Windows, when we have C:\foo and tab over .., we get C:\ path = path.rstrip(os.sep) log.prompt.debug('Inserting path {}'.format(path)) self._lineedit.setText(path) self._lineedit.setFocus() self._set_fileview_root(path, tabbed=True) if clicked: # Avoid having a ..-subtree highlighted self._file_view.setCurrentIndex(QModelIndex()) def _init_fileview(self): self._file_view = QTreeView(self) self._file_model = QFileSystemModel(self) self._file_view.setModel(self._file_model) self._file_view.clicked.connect(self._insert_path) if config.val.prompt.filebrowser: self._vbox.addWidget(self._file_view) else: self._file_view.hide() # Only show name self._file_view.setHeaderHidden(True) for col in range(1, 4): self._file_view.setColumnHidden(col, True) # Nothing selected initially self._file_view.setCurrentIndex(QModelIndex()) # The model needs to be sorted so we get the correct first/last index self._file_model.directoryLoaded.connect( lambda: self._file_model.sort(0)) def accept(self, value=None, save=False): self._check_save_support(save) text = value if value is not None else self._lineedit.text() text = downloads.transform_path(text) if text is None: message.error("Invalid filename") return False self.question.answer = text return True def item_focus(self, which): # This duplicates some completion code, but I don't see a nicer way... assert which in ['prev', 'next'], which selmodel = self._file_view.selectionModel() parent = self._file_view.rootIndex() first_index = self._file_model.index(0, 0, parent) row = self._file_model.rowCount(parent) - 1 last_index = self._file_model.index(row, 0, parent) if not first_index.isValid(): # No entries return assert last_index.isValid() idx = selmodel.currentIndex() if not idx.isValid(): # No item selected yet idx = last_index if which == 'prev' else first_index elif which == 'prev': idx = self._file_view.indexAbove(idx) else: assert which == 'next', which idx = self._file_view.indexBelow(idx) # wrap around if we arrived at beginning/end if not idx.isValid(): idx = last_index if which == 'prev' else first_index idx = self._do_completion(idx, which) selmodel.setCurrentIndex( idx, QItemSelectionModel.ClearAndSelect | # type: ignore[arg-type] QItemSelectionModel.Rows) self._insert_path(idx, clicked=False) def _do_completion(self, idx, which): filename = self._file_model.fileName(idx) while not filename.startswith(self._to_complete) and idx.isValid(): if which == 'prev': idx = self._file_view.indexAbove(idx) else: assert which == 'next', which idx = self._file_view.indexBelow(idx) filename = self._file_model.fileName(idx) return idx def _allowed_commands(self): return [('prompt-accept', 'Accept'), ('leave-mode', 'Abort')]
class FileManager(QWidget, _HalWidgetBase): def __init__(self, parent=None): super(FileManager, self).__init__(parent) self.title = 'PyQt5 file system view - pythonspot.com' self.left = 10 self.top = 10 self.width = 640 self.height = 480 self.default_path = (os.path.join(os.path.expanduser('~'), 'linuxcnc/nc_files/examples')) self.user_path = (os.path.join('/media')) self.currentPath = None self.EXT = INFO.PROGRAM_FILTERS_EXTENSIONS self.initUI() def initUI(self): self.setWindowTitle(self.title) self.setGeometry(self.left, self.top, self.width, self.height) self.model = QFileSystemModel() self.model.setRootPath(QDir.currentPath()) self.model.setFilter(QDir.AllDirs | QDir.NoDot | QDir.Files) self.model.setNameFilterDisables(False) self.model.setNameFilters(self.EXT) self.list = QListView() self.list.setModel(self.model) self.updateDirectoryView(self.default_path) self.list.setWindowTitle("Dir View") self.list.resize(640, 480) self.list.clicked[QModelIndex].connect(self.clicked) self.list.activated.connect(self._getPathActivated) #self.list.currentChanged = self.currentChanged self.list.setAlternatingRowColors(True) self.cb = QComboBox() self.cb.currentTextChanged.connect(self.filterChanged) self.cb.addItems(self.EXT) #self.cb.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)) self.button = QPushButton() self.button.setText('Media') self.button.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)) self.button.setToolTip('Jump to Media directory') self.button.clicked.connect(self.onMediaClicked) self.button2 = QPushButton() self.button2.setText('User') self.button2.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)) self.button2.setToolTip('Jump to linuxcnc directory') self.button2.clicked.connect(self.onUserClicked) hbox = QHBoxLayout() hbox.addWidget(self.button) hbox.addWidget(self.button2) hbox.addWidget(self.cb) windowLayout = QVBoxLayout() windowLayout.addWidget(self.list) windowLayout.addLayout(hbox) self.setLayout(windowLayout) self.show() # this could return the current/previous selected as it's selected. # need to uncomment monkey patch of self.list.currentChanged above # so far this is not needed def currentChanged(self,c,p): dir_path = self.model.filePath(c) print('-> ',dir_path) def updateDirectoryView(self, path): self.list.setRootIndex(self.model.setRootPath(path)) def filterChanged(self, text): self.model.setNameFilters([text]) def clicked(self, index): # the signal passes the index of the clicked item dir_path = self.model.filePath(index) if self.model.fileInfo(index).isFile(): self.currentPath = dir_path return root_index = self.model.setRootPath(dir_path) self.list.setRootIndex(root_index) def onMediaClicked(self): self.updateDirectoryView(self.user_path) def onUserClicked(self): self.updateDirectoryView(self.default_path) def select_row(self, style): style = style.lower() selectionModel = self.list.selectionModel() row = selectionModel.currentIndex().row() self.rows = self.model.rowCount(self.list.rootIndex()) if style == 'last': row = self.rows elif style == 'up': if row > 0: row -= 1 else: row = 0 elif style == 'down': if row < self.rows: row += 1 else: row = self.rows else: return top = self.model.index(row, 0, self.list.rootIndex()) selectionModel.setCurrentIndex(top, QItemSelectionModel.Select | QItemSelectionModel.Rows) selection = QItemSelection(top, top) selectionModel.clearSelection() selectionModel.select(selection, QItemSelectionModel.Select) # returns the current highlighted (selected) path as well as # whether it's a file or not. def getCurrentSelected(self): selectionModel = self.list.selectionModel() index = selectionModel.currentIndex() dir_path = self.model.filePath(index) if self.model.fileInfo(index).isFile(): return (dir_path, True) else: return (dir_path, False) def _hal_init(self): if self.PREFS_: last_path = self.PREFS_.getpref('last_loaded_directory', self.default_path, str, 'BOOK_KEEPING') self.updateDirectoryView(last_path) LOG.debug("lAST FILE PATH: {}".format(last_path)) else: LOG.debug("lAST FILE PATH: {}".format(self.default_path)) self.updateDirectoryView(self.default_path) # get current selection and update the path # then if the path is good load it into linuxcnc # record it in the preference file if available def _getPathActivated(self): row = self.list.selectionModel().currentIndex() self.clicked(row) fname = self.currentPath if fname is None: return if fname: self.load(fname) # this can be class patched to do something else def load(self, fname=None): if fname is None: self._getPathActivated() return self.recordBookKeeping() ACTION.OPEN_PROGRAM(fname) STATUS.emit('update-machine-log', 'Loaded: ' + fname, 'TIME') # this can be class patched to do something else def recordBookKeeping(self): fname = self.currentPath if fname is None: return if self.PREFS_: self.PREFS_.putpref('last_loaded_directory', self.model.rootPath(), str, 'BOOK_KEEPING') self.PREFS_.putpref('RecentPath_0', fname, str, 'BOOK_KEEPING') # moves the selection up # used with MPG scrolling def up(self): self.select_row('up') # moves the selection down # used with MPG scrolling def down(self): self.select_row('down')
class FileManager(QWidget, _HalWidgetBase): def __init__(self, parent=None): super(FileManager, self).__init__(parent) self.title = 'PyQt5 file system view - pythonspot.com' self.left = 10 self.top = 10 self.width = 640 self.height = 480 self.default_path = (os.path.join(os.path.expanduser('~'), 'labvcnc/nc_files/examples')) self.user_path = (os.path.join('/media')) self.currentPath = None self.initUI() def initUI(self): self.setWindowTitle(self.title) self.setGeometry(self.left, self.top, self.width, self.height) self.model = QFileSystemModel() self.model.setRootPath(QDir.currentPath()) self.model.setFilter(QDir.AllDirs | QDir.NoDot | QDir.Files) self.model.setNameFilterDisables(False) self.model.setNameFilters(["*.ngc", '*.py']) self.list = QListView() self.list.setModel(self.model) self.updateDirectoryView(self.default_path) self.list.setWindowTitle("Dir View") self.list.resize(640, 480) self.list.clicked[QModelIndex].connect(self.clicked) self.list.activated.connect(self.load) self.list.setAlternatingRowColors(True) self.cb = QComboBox() self.cb.currentTextChanged.connect(self.filterChanged) self.cb.addItems(sorted({'*.ngc', '*.py', '*'})) #self.cb.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)) self.button = QPushButton() self.button.setText('Media') self.button.setSizePolicy( QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)) self.button.setToolTip('Jump to Media directory') self.button.clicked.connect(self.onMediaClicked) self.button2 = QPushButton() self.button2.setText('User') self.button2.setSizePolicy( QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)) self.button2.setToolTip('Jump to labvcnc directory') self.button2.clicked.connect(self.onUserClicked) hbox = QHBoxLayout() hbox.addWidget(self.button) hbox.addWidget(self.button2) hbox.addWidget(self.cb) windowLayout = QVBoxLayout() windowLayout.addWidget(self.list) windowLayout.addLayout(hbox) self.setLayout(windowLayout) self.show() def updateDirectoryView(self, path): self.list.setRootIndex(self.model.setRootPath(path)) def filterChanged(self, text): self.model.setNameFilters([text]) def clicked(self, index): # the signal passes the index of the clicked item dir_path = self.model.filePath(index) if self.model.fileInfo(index).isFile(): self.currentPath = dir_path return root_index = self.model.setRootPath(dir_path) self.list.setRootIndex(root_index) def onMediaClicked(self): self.updateDirectoryView(self.user_path) def onUserClicked(self): self.updateDirectoryView(self.default_path) def select_row(self, style): style = style.lower() selectionModel = self.list.selectionModel() row = selectionModel.currentIndex().row() self.rows = self.model.rowCount(self.list.rootIndex()) if style == 'last': row = self.rows elif style == 'up': if row > 0: row -= 1 else: row = 0 elif style == 'down': if row < self.rows: row += 1 else: row = self.rows else: return top = self.model.index(row, 0, self.list.rootIndex()) selectionModel.setCurrentIndex( top, QItemSelectionModel.Select | QItemSelectionModel.Rows) selection = QItemSelection(top, top) selectionModel.clearSelection() selectionModel.select(selection, QItemSelectionModel.Select) def _hal_init(self): if self.PREFS_: last_path = self.PREFS_.getpref('last_file_path', self.default_path, str, 'BOOK_KEEPING') self.updateDirectoryView(last_path) LOG.debug("lAST FILE PATH: {}".format(last_path)) else: LOG.debug("lAST FILE PATH: {}".format(self.default_path)) self.updateDirectoryView(self.default_path) # get current selection and update the path # then if the path is good load it into labvcnc # record it in the preference file if available def load(self): row = self.list.selectionModel().currentIndex() self.clicked(row) fname = self.currentPath if fname is None: return if fname: if self.PREFS_: self.PREFS_.putpref('last_file_path', fname, str, 'BOOK_KEEPING') ACTION.OPEN_PROGRAM(fname) STATUS.emit('update-machine-log', 'Loaded: ' + fname, 'TIME') def up(self): self.select_row('up') def down(self): self.select_row('down')
class Ui(QtWidgets.QMainWindow): """Main Class of the simple DMS user interface. Arguments: nothing Returns: nothing """ def __init__(self): """Initialize variables and connect actions with functions.""" super(Ui, self).__init__() uic.loadUi(os.path.join(CURRDIR, "ui", "main_simpledms.ui"), self) self.loadpref() self.rules = rules.Rules(self.pref["dmsroot"]) self.currentselectedrulesfolder = None self.currentselectedsearchfolder = None self.current_monitorfolder_index = None self.parent_index = None self.filemodelmonitor = QFileSystemModel() self.rulesfoldermodel = QFileSystemModel() self.resultfoldermodel = QFileSystemModel() self.searchfoldermodel = QFileSystemModel() self.textEdit_tags = MyTextEdit(self.textEdit_tags) self.updateui_settings() self.updateui_pdfrename() self.show() # Connect Widget Toolbar Actions self.actionScan.triggered.connect(self.select_widget) self.actionPdf.triggered.connect(self.select_widget) self.actionSettings.triggered.connect(self.select_widget) self.actionAbout.triggered.connect(self.show_ui_about) self.actionExit.triggered.connect(self.select_widget) # Connect Preferences self.pushButton_setmonitorfolder.clicked.connect( self.browse_monitor_folder) self.pushButton_setdmsroot.clicked.connect(self.browse_dms_root) self.treeView_rulesfolders.clicked.connect(self.rulesfolderselected) self.treeView_rules.doubleClicked.connect(self.ruledoubleclicked) self.pushButton_addrule.clicked.connect(self.addruleclicked) self.pushButton_deleterule.clicked.connect(self.deleteruleclicked) # Connect page pdf renaming self.listView_monitorfiles.clicked.connect( self.listView_monitorfiles_clicked) self.treeView_output.clicked.connect(self.treeView_output_clicked) self.pushButton_ok.clicked.connect(self.pushButton_ok_clicked) self.listView_monitorfiles.doubleClicked.connect( self.listView_monitorfiles_doubleclicked) self.lineEdit_outputfilename.textChanged.connect(self.readyforstorage) self.pushButton_addDate.clicked.connect( self.pushButton_addDate_clicked) # -------- Settings page ----------- def rulesfolderselected(self, signal): """Update ui if a folder in settings -> rules is selected.""" self.currentselectedrulesfolder = self.rulesfoldermodel.filePath( signal) self.updateui_settings() self.pushButton_addrule.setEnabled(True) def ruledoubleclicked(self): """Open ui for rule adaption of double clicked rule.""" selectedrule = self.treeView_rules_model.itemData( self.treeView_rules.selectedIndexes()[0]) rule = self.rules.returnruleofkeywords([selectedrule[0]], self.currentselectedrulesfolder) rulesdialog = MyRulesWidget( keywords=rule[0][1], booleanoperator=rule[0][2], tags=rule[0][3], doctitle=rule[0][4], indexertags=set(self.rules.returnalltags()), ) rulesdialog.setAttribute(QtCore.Qt.WA_DeleteOnClose) if rulesdialog.exec_(): self.rules.replacerule( rule[0][0], rulesdialog.keywords, rulesdialog.booleanoperator, rulesdialog.tags, rulesdialog.doctitle, self.currentselectedrulesfolder, ) self.updateui_settings() def deleteruleclicked(self): """Delete selected rule and update ui.""" if self.treeView_rules.selectedIndexes(): selectedrule = self.treeView_rules_model.itemData( self.treeView_rules.selectedIndexes()[0]) self.rules.delrule([selectedrule[0]], self.currentselectedrulesfolder) self.updateui_settings() def addruleclicked(self): """Add rule to database if it does not exist yet.""" rulesdialog = MyRulesWidget( indexertags=set(self.rules.returnalltags())) rulesdialog.setAttribute(QtCore.Qt.WA_DeleteOnClose) if rulesdialog.exec_(): if self.rules.returnruleofkeywords( rulesdialog.keywords, self.currentselectedrulesfolder): QtWidgets.QMessageBox.information( self, "Error", "A rule with these keywords already exists.") else: self.rules.addrule( rulesdialog.keywords, rulesdialog.booleanoperator, rulesdialog.tags, rulesdialog.doctitle, self.currentselectedrulesfolder, ) self.updateui_settings() def loadpref(self): """Load preferences: root of dms and monitorfolder.""" if os.path.isfile("pref.json"): with open("pref.json") as f: self.pref = json.load(f) if not os.path.isdir(self.pref["dmsroot"]): os.makedirs(self.pref["dmsroot"]) QtWidgets.QMessageBox.information( self, "Attention!", "Stored path of dmsroot does not exist") if not os.path.isdir(self.pref["monitorfolder"]): os.makedirs(self.pref["monitorfolder"]) QtWidgets.QMessageBox.information( self, "Attention!", "Stored path of monitorfolder does not exist.", ) else: # If pref.json file does not exist if not os.path.isdir( os.path.join(os.path.expanduser("~"), "paperwork")): os.makedirs(os.path.join(os.path.expanduser("~"), "paperwork")) QtWidgets.QMessageBox.information( self, "Attention!", "Standard path for file cabinet" "was created. If " "needed, please change.", ) if not os.path.isdir( os.path.join(os.path.expanduser("~"), "paperwork_open")): os.makedirs( os.path.join(os.path.expanduser("~"), "paperwork_open")) QtWidgets.QMessageBox.information( self, "Attention!", "Standard path for monitor folder" "was created. If " "needed, please change.", ) self.pref = { "dmsroot": os.path.join(os.path.expanduser("~"), "paperwork"), "monitorfolder": os.path.join(os.path.expanduser("~"), "paperwork_open"), } self.savepref() def savepref(self): """Save preferences to pref.json.""" with open("pref.json", "w") as f: json.dump(self.pref, f) def browse_monitor_folder(self): """Select monitor folder.""" # execute getExistingDirectory dialog and set the directory variable to be equal # to the user selected directory directory = QFileDialog.getExistingDirectory( self, "Select a monitor folder with files to be " "processed/imported") # if user didn't pick a directory don't continue if directory: self.pref["monitorfolder"] = directory self.savepref() self.updateui_settings() self.updateui_pdfrename() def browse_dms_root(self): """Select dms root folder.""" # execute getExistingDirectory dialog and set the directory variable to be equal # to the user selected directory directory = QFileDialog.getExistingDirectory( self, "Select a root directory of the filing cabinet") # if user didn't pick a directory don't continue if directory: if not len(self.rules.returnallrules()) == 0: result = QtWidgets.QMessageBox.question( self, "Attention", "If the root directory is changed, the current rules are " "deleted! Are you sure and want to proceed?", ) if result == QtWidgets.QMessageBox.No: return self.rules.resetdb(directory) self.pref["dmsroot"] = directory self.savepref() # self.indexer.__init__(directory) self.updateui_settings() def updateui_settings(self): """Update ui elements of settings page.""" self.label_monitorfolder.setText(self.pref["monitorfolder"]) self.label_monitordir.setText( ".." + os.sep + os.path.basename(os.path.normpath(self.pref["monitorfolder"]))) self.label_dmsroot.setText(self.pref["dmsroot"]) self.rulesfoldermodel.setRootPath(self.pref["dmsroot"]) self.rulesfoldermodel.setFilter(QtCore.QDir.NoDotAndDotDot | QtCore.QDir.Dirs) self.treeView_rulesfolders.setModel(self.rulesfoldermodel) self.treeView_rulesfolders.setRootIndex( self.rulesfoldermodel.index(self.pref["dmsroot"])) self.treeView_rulesfolders.hideColumn(1) self.treeView_rulesfolders.hideColumn(2) self.treeView_rulesfolders.hideColumn(3) self.treeView_rules_model = QtGui.QStandardItemModel() self.treeView_rules.setModel(self.treeView_rules_model) self.treeView_rules_model.setHorizontalHeaderLabels( ["Rules (keywords)"]) rulesoffolder = self.rules.returnrulesoffolder( self.currentselectedrulesfolder) if rulesoffolder is not None: for i in rulesoffolder: rule = QtGui.QStandardItem(i[1]) self.treeView_rules_model.appendRow(rule) # -------- Action Bar ----------- def select_widget(self): """Select index of stacked widget based on toolbox actions.""" sender = self.sender() if sender.text() == "Scan": pass elif sender.text() == "Import": self.stackedWidget.setCurrentIndex(0) elif sender.text() == "Settings": self.stackedWidget.setCurrentIndex(1) elif sender.text() == "Exit": QtWidgets.QApplication.instance().quit() def show_ui_about(self): """Show about page.""" dialog = QDialog() dialog.ui = ui.about.Ui_Dialog() dialog.ui.setupUi(dialog) dialog.setAttribute(QtCore.Qt.WA_DeleteOnClose) dialog.exec_() # -------- pdf renaming page ----------- def listView_monitorfiles_clicked(self, index): """Show preview of pdf, extract date and words and propose tags and directory in dms root.""" self.current_monitorfolder_index = index pdffile = os.path.join(self.pref["monitorfolder"], self.filemodelmonitor.itemData(index)[0]) # Check if file is really a pdf. if "PDF" not in magic.from_file(pdffile): QtWidgets.QMessageBox.information(self, "Attention!", "Document is not a pdf.") return self.listView_monitorfiles.setEnabled(False) self.setCursor(QtCore.Qt.BusyCursor) self.statusbar.showMessage("Reading pdf...") self.pdfhandler = PdfHandler(pdffile) self.pdfhandler.thumbheight = ( self.listView_monitorfiles.frameGeometry().height()) self.pdfhandler.createthumbnail() pixmap = QtGui.QPixmap(self.pdfhandler.thumbfpath) self.thumbnail.setPixmap(pixmap) self.thumbnail.resize(pixmap.width(), pixmap.height()) self.analyze_text() if self.readyforstorage(): self.statusbar.showMessage("Automatic renaming and moving file...") # self.pushButton_ok_clicked() self.listView_monitorfiles.setEnabled(True) self.setCursor(QtCore.Qt.ArrowCursor) self.statusbar.showMessage("Ready") def listView_monitorfiles_doubleclicked(self, index): """Open doubleclicked file. Arguments: index: index from pyqt5 listview which element was clicked. Returns: nothing """ file2open = os.path.join(self.pref["monitorfolder"], self.filemodelmonitor.itemData(index)[0]) webbrowser.open(file2open) def listWidget_pdfthumbnails_doubleclicked(self): """Open file in browser.""" file2open = self.pdfhandler.filepath if os.path.isfile(file2open): webbrowser.open(file2open) else: QtWidgets.QMessageBox.information(self, "Attention!", "File does not exist!") def analyze_text(self): """Analyze the found text.""" text = self.pdfhandler.gettext() dateext = DateExtractor(text) date = dateext.getdate() # If Date not found in text, search for date in filename if not date: dateext = DateExtractor(self.pdfhandler.filepath) date = dateext.getdate() result = self.rules.applyrule(text) if result["doctitle"] is not None: if date is not None: self.lineEdit_outputfilename.setText( date.strftime("%Y-%m-%d") + " " + result["doctitle"]) else: self.lineEdit_outputfilename.setText(result["doctitle"]) else: if date is not None: self.lineEdit_outputfilename.setText( date.strftime("%Y-%m-%d") + " ") else: self.lineEdit_outputfilename.clear() if result["tags"] is not None: self.textEdit_tags.setText(result["tags"]) else: self.textEdit_tags.clear() self.destination = result["destination"] self.treeView_output.setCurrentIndex( self.resultfoldermodel.index(self.destination)) if not self.readyforstorage(): self.lineEdit_outputfilename.setFocus() else: self.pushButton_ok.setFocus() self.statusbar.showMessage("Ready") self.setCursor(QtCore.Qt.ArrowCursor) def readyforstorage(self) -> bool: """Check if file infos like date, text, tags and folder are set.""" if (self.lineEdit_outputfilename.text() and re.match( r"(\d{4}-\d{2}-\d{2}\s\S+)", self.lineEdit_outputfilename.text(), re.I | re.UNICODE, ) and self.resultfoldermodel.filePath( self.treeView_output.currentIndex())): self.pushButton_ok.setEnabled(True) return True else: self.pushButton_ok.setEnabled(False) return False def pushButton_ok_clicked(self): """Store document with new metadata in target directory.""" self.setCursor(QtCore.Qt.BusyCursor) self.statusbar.showMessage("Storing...") doctitle = self.lineEdit_outputfilename.text()[11:] date = self.lineEdit_outputfilename.text()[0:10] tags = self.textEdit_tags.toPlainText() tags = tags.strip(", ") path = self.resultfoldermodel.filePath( self.treeView_output.currentIndex()) self.pdfhandler.update_and_move(path, doctitle, tags, date) self.updateui_pdfrename() self.setCursor(QtCore.Qt.ArrowCursor) row = self.current_monitorfolder_index.row() n_elements = self.filemodelmonitor.rowCount(self.parent_index) if row + 1 < n_elements and n_elements > 0: self.listView_monitorfiles_clicked( self.current_monitorfolder_index.sibling(row + 1, 0)) if row + 1 >= n_elements and n_elements > 1: self.listView_monitorfiles_clicked( self.current_monitorfolder_index.sibling(row - 1, 0)) self.updateui_pdfrename() self.statusbar.showMessage("ready") def pushButton_addDate_clicked(self): """Add current date to document name field.""" if self.lineEdit_outputfilename.text(): if re.match(r"(\d{4}-\d{2}-\d{2})", self.lineEdit_outputfilename.text()): text = (str(datetime.date.today()) + self.lineEdit_outputfilename.text()[10:]) self.lineEdit_outputfilename.setText(text) else: self.lineEdit_outputfilename.setText( str(datetime.date.today()) + " ") else: self.lineEdit_outputfilename.setText( str(datetime.date.today()) + " ") self.lineEdit_outputfilename.setFocus() def treeView_output_clicked(self): """Check if all input is available after the target directory was selected.""" self.readyforstorage() def updateui_pdfrename(self): """Update ui of page pdfrename.""" self.filemodelmonitor.setFilter(QtCore.QDir.NoDotAndDotDot | QtCore.QDir.Files) self.filemodelmonitor.setNameFilters(["*.pdf"]) self.filemodelmonitor.setNameFilterDisables(False) self.parent_index = self.filemodelmonitor.setRootPath( self.pref["monitorfolder"]) self.listView_monitorfiles.setModel(self.filemodelmonitor) self.listView_monitorfiles.setRootIndex( self.filemodelmonitor.index(self.pref["monitorfolder"])) self.resultfoldermodel.setRootPath(self.pref["dmsroot"]) self.resultfoldermodel.setFilter(QtCore.QDir.NoDotAndDotDot | QtCore.QDir.Dirs) self.treeView_output.setModel(self.resultfoldermodel) self.treeView_output.setRootIndex( self.resultfoldermodel.index(self.pref["dmsroot"])) self.treeView_output.hideColumn(1) self.treeView_output.hideColumn(2) self.treeView_output.hideColumn(3) indexertags = set(self.rules.returnalltags()) completer = MyDictionaryCompleter(myKeywords=indexertags) self.textEdit_tags.setCompleter(completer)
class FileManager(QWidget, _HalWidgetBase): def __init__(self, parent=None): super(FileManager, self).__init__(parent) self.title = 'Qtvcp File System View' self.left = 10 self.top = 10 self.width = 640 self.height = 480 self._last = 0 if INFO.PROGRAM_PREFIX is not None: self.user_path = os.path.expanduser(INFO.PROGRAM_PREFIX) else: self.user_path = (os.path.join(os.path.expanduser('~'), 'linuxcnc/nc_files')) user = os.path.split(os.path.expanduser('~'))[-1] self.media_path = (os.path.join('/media', user)) temp = [('User', self.user_path), ('Media', self.media_path)] self._jumpList = OrderedDict(temp) self.currentPath = None self.currentFolder = None self.PREFS_ = None self.initUI() def initUI(self): self.setWindowTitle(self.title) self.setGeometry(self.left, self.top, self.width, self.height) pasteBox = QHBoxLayout() self.textLine = QLineEdit() self.textLine.setToolTip('Current Director/selected File') self.pasteButton = QToolButton() self.pasteButton.setEnabled(False) self.pasteButton.setText('Paste') self.pasteButton.setToolTip( 'Copy file from copy path to current directory/file') self.pasteButton.clicked.connect(self.paste) self.pasteButton.hide() pasteBox.addWidget(self.textLine) pasteBox.addWidget(self.pasteButton) self.copyBox = QFrame() hbox = QHBoxLayout() hbox.setContentsMargins(0, 0, 0, 0) self.copyLine = QLineEdit() self.copyLine.setToolTip('File path to copy from, when pasting') self.copyButton = QToolButton() self.copyButton.setText('Copy') self.copyButton.setToolTip('Record current file as copy path') self.copyButton.clicked.connect(self.recordCopyPath) hbox.addWidget(self.copyButton) hbox.addWidget(self.copyLine) self.copyBox.setLayout(hbox) self.copyBox.hide() self.model = QFileSystemModel() self.model.setRootPath(QDir.currentPath()) self.model.setFilter(QDir.AllDirs | QDir.NoDot | QDir.Files) self.model.setNameFilterDisables(False) self.model.rootPathChanged.connect(self.folderChanged) self.list = QListView() self.list.setModel(self.model) self.list.resize(640, 480) self.list.clicked[QModelIndex].connect(self.listClicked) self.list.activated.connect(self._getPathActivated) self.list.setAlternatingRowColors(True) self.list.hide() self.table = QTableView() self.table.setModel(self.model) self.table.resize(640, 480) self.table.clicked[QModelIndex].connect(self.listClicked) self.table.activated.connect(self._getPathActivated) self.table.setAlternatingRowColors(True) header = self.table.horizontalHeader() header.setSectionResizeMode(0, QHeaderView.Stretch) header.setSectionResizeMode(1, QHeaderView.ResizeToContents) header.setSectionResizeMode(3, QHeaderView.ResizeToContents) header.swapSections(1, 3) header.setSortIndicator(1, Qt.AscendingOrder) self.table.setSortingEnabled(True) self.table.setColumnHidden(2, True) # type self.table.verticalHeader().setVisible(False) # row count header self.cb = QComboBox() self.cb.currentIndexChanged.connect(self.filterChanged) self.fillCombobox(INFO.PROGRAM_FILTERS_EXTENSIONS) self.cb.setMinimumHeight(30) self.cb.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)) self.button2 = QToolButton() self.button2.setText('User') self.button2.setSizePolicy( QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)) self.button2.setMinimumSize(60, 30) self.button2.setToolTip( 'Jump to User directory.\nLong press for Options.') self.button2.clicked.connect(self.onJumpClicked) self.button3 = QToolButton() self.button3.setText('Add Jump') self.button3.setSizePolicy( QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)) self.button3.setMinimumSize(60, 30) self.button3.setToolTip('Add current directory to jump button list') self.button3.clicked.connect(self.onActionClicked) self.settingMenu = QMenu(self) self.button2.setMenu(self.settingMenu) hbox = QHBoxLayout() hbox.addWidget(self.button2) hbox.addWidget(self.button3) hbox.insertStretch(2, stretch=0) hbox.addWidget(self.cb) windowLayout = QVBoxLayout() windowLayout.addLayout(pasteBox) windowLayout.addWidget(self.copyBox) windowLayout.addWidget(self.list) windowLayout.addWidget(self.table) windowLayout.addLayout(hbox) self.setLayout(windowLayout) self.show() def _hal_init(self): if self.PREFS_: last_path = self.PREFS_.getpref('last_loaded_directory', self.user_path, str, 'BOOK_KEEPING') LOG.debug("lAST FILE PATH: {}".format(last_path)) if not last_path == '': self.updateDirectoryView(last_path) else: self.updateDirectoryView(self.user_path) # get all the saved jumplist paths temp = self.PREFS_.getall('FILEMANAGER_JUMPLIST') self._jumpList.update(temp) else: LOG.debug("lAST FILE PATH: {}".format(self.user_path)) self.updateDirectoryView(self.user_path) # install jump paths into toolbutton menu for i in self._jumpList: self.addAction(i) # set recorded columns sort settings self.SETTINGS_.beginGroup("FileManager-{}".format(self.objectName())) sect = self.SETTINGS_.value('sortIndicatorSection', type=int) order = self.SETTINGS_.value('sortIndicatorOrder', type=int) self.SETTINGS_.endGroup() if not None in (sect, order): self.table.horizontalHeader().setSortIndicator(sect, order) # when qtvcp closes this gets called # record jump list paths def _hal_cleanup(self): if self.PREFS_: for i, key in enumerate(self._jumpList): if i in (0, 1): continue self.PREFS_.putpref(key, self._jumpList.get(key), str, 'FILEMANAGER_JUMPLIST') # record sorted columns h = self.table.horizontalHeader() self.SETTINGS_.beginGroup("FileManager-{}".format(self.objectName())) self.SETTINGS_.setValue('sortIndicatorSection', h.sortIndicatorSection()) self.SETTINGS_.setValue('sortIndicatorOrder', h.sortIndicatorOrder()) self.SETTINGS_.endGroup() ######################### # callbacks ######################### # add shown text and hidden filter data from the INI def fillCombobox(self, data): for i in data: self.cb.addItem(i[0], i[1]) def folderChanged(self, data): data = os.path.normpath(data) self.currentFolder = data self.textLine.setText(data) def updateDirectoryView(self, path, quiet=False): if os.path.exists(path): self.list.setRootIndex(self.model.setRootPath(path)) self.table.setRootIndex(self.model.setRootPath(path)) else: LOG.debug( "Set directory view error - no such path {}".format(path)) if not quiet: STATUS.emit( 'error', LOW_ERROR, "File Manager error - No such path: {}".format(path)) # retrieve selected filter (it's held as QT.userData) def filterChanged(self, index): userdata = self.cb.itemData(index) self.model.setNameFilters(userdata) def listClicked(self, index): # the signal passes the index of the clicked item dir_path = os.path.normpath(self.model.filePath(index)) if self.model.fileInfo(index).isFile(): self.currentPath = dir_path self.textLine.setText(self.currentPath) return root_index = self.model.setRootPath(dir_path) self.list.setRootIndex(root_index) self.table.setRootIndex(root_index) def onUserClicked(self): self.showUserDir() def onMediaClicked(self): self.showMediaDir() # jump directly to a saved path shown on the button def onJumpClicked(self): data = self.button2.text() if data.upper() == 'MEDIA': self.showMediaDir() elif data.upper() == 'USER': self.showUserDir() else: temp = self._jumpList.get(data) if temp is not None: self.updateDirectoryView(temp) else: STATUS.emit('error', linuxcnc.OPERATOR_ERROR, 'file jumopath: {} not valid'.format(data)) log.debug('file jumopath: {} not valid'.format(data)) # jump directly to a saved path from the menu def jumpTriggered(self, data): if data.upper() == 'MEDIA': self.button2.setText('{}'.format(data)) self.button2.setToolTip( 'Jump to Media directory.\nLong press for Options.') self.showMediaDir() elif data.upper() == 'USER': self.button2.setText('{}'.format(data)) self.button2.setToolTip( 'Jump to User directory.\nLong press for Options.') self.showUserDir() else: self.button2.setText('{}'.format(data)) self.button2.setToolTip('Jump to directory:\n{}'.format( self._jumpList.get(data))) self.updateDirectoryView(self._jumpList.get(data)) # add a jump list path def onActionClicked(self): i = self.currentFolder try: self._jumpList[i] = i except Exception as e: print(e) button = QAction(QIcon.fromTheme('user-home'), i, self) # weird lambda i=i to work around 'function closure' button.triggered.connect(lambda state, i=i: self.jumpTriggered(i)) self.settingMenu.addAction(button) # get current selection and update the path # then if the path is good load it into linuxcnc # record it in the preference file if available def _getPathActivated(self): if self.list.isVisible(): row = self.list.selectionModel().currentIndex() else: row = self.table.selectionModel().currentIndex() self.listClicked(row) fname = self.currentPath if fname is None: return if fname: self.load(fname) def recordCopyPath(self): data, isFile = self.getCurrentSelected() if isFile: self.copyLine.setText(os.path.normpath(data)) self.pasteButton.setEnabled(True) else: self.copyLine.setText('') self.pasteButton.setEnabled(False) STATUS.emit('error', OPERATOR_ERROR, 'Can only copy a file, not a folder') def paste(self): res = self.copyFile(self.copyLine.text(), self.textLine.text()) if res: self.copyLine.setText('') self.pasteButton.setEnabled(False) ######################## # helper functions ######################## def addAction(self, i): axisButton = QAction(QIcon.fromTheme('user-home'), i, self) # weird lambda i=i to work around 'function closure' axisButton.triggered.connect(lambda state, i=i: self.jumpTriggered(i)) self.settingMenu.addAction(axisButton) def showList(self, state=True): if state: self.table.hide() self.list.show() else: self.table.show() self.list.hide() def showTable(self, state=True): self.showList(not state) def showCopyControls(self, state): if state: self.copyBox.show() self.pasteButton.show() else: self.copyBox.hide() self.pasteButton.hide() def showMediaDir(self, quiet=False): self.updateDirectoryView(self.media_path, quiet) def showUserDir(self, quiet=False): self.updateDirectoryView(self.user_path, quiet) def copyFile(self, s, d): try: shutil.copy(s, d) return True except Exception as e: LOG.error("Copy file error: {}".format(e)) STATUS.emit('error', OPERATOR_ERROR, "Copy file error: {}".format(e)) return False @pyqtSlot(float) @pyqtSlot(int) def scroll(self, data): if data > self._last: self.up() elif data < self._last: self.down() self._last = data # moves the selection up # used with MPG scrolling def up(self): self.select_row('up') # moves the selection down # used with MPG scrolling def down(self): self.select_row('down') def select_row(self, style='down'): style = style.lower() if self.list.isVisible(): i = self.list.rootIndex() selectionModel = self.list.selectionModel() else: i = self.table.rootIndex() selectionModel = self.table.selectionModel() row = selectionModel.currentIndex().row() self.rows = self.model.rowCount(i) if style == 'last': row = self.rows elif style == 'up': if row > 0: row -= 1 else: row = 0 elif style == 'down': if row < self.rows - 1: row += 1 else: row = self.rows - 1 else: return top = self.model.index(row, 0, i) selectionModel.setCurrentIndex( top, QItemSelectionModel.Select | QItemSelectionModel.Rows) selection = QItemSelection(top, top) selectionModel.clearSelection() selectionModel.select(selection, QItemSelectionModel.Select) # returns the current highlighted (selected) path as well as # whether it's a file or not. def getCurrentSelected(self): if self.list.isVisible(): selectionModel = self.list.selectionModel() else: selectionModel = self.table.selectionModel() index = selectionModel.currentIndex() dir_path = os.path.normpath(self.model.filePath(index)) if self.model.fileInfo(index).isFile(): return (dir_path, True) else: return (dir_path, False) # This can be class patched to do something else def load(self, fname=None): try: if fname is None: self._getPathActivated() return self.recordBookKeeping() ACTION.OPEN_PROGRAM(fname) STATUS.emit('update-machine-log', 'Loaded: ' + fname, 'TIME') except Exception as e: LOG.error("Load file error: {}".format(e)) STATUS.emit('error', NML_ERROR, "Load file error: {}".format(e)) # This can be class patched to do something else def recordBookKeeping(self): fname = self.currentPath if fname is None: return if self.PREFS_: self.PREFS_.putpref('last_loaded_directory', self.model.rootPath(), str, 'BOOK_KEEPING') self.PREFS_.putpref('RecentPath_0', fname, str, 'BOOK_KEEPING')
class FileManager(QWidget, _HalWidgetBase): def __init__(self, parent=None): super(FileManager, self).__init__(parent) self.title = 'Qtvcp File System View' self.left = 10 self.top = 10 self.width = 640 self.height = 480 self.media_path = (os.path.join(os.path.expanduser('~'), 'linuxcnc/nc_files')) user = os.path.split(os.path.expanduser('~'))[-1] self.user_path = (os.path.join('/media', user)) self.currentPath = None self.currentFolder = None self.initUI() def initUI(self): self.setWindowTitle(self.title) self.setGeometry(self.left, self.top, self.width, self.height) pasteBox = QHBoxLayout() self.textLine = QLineEdit() self.textLine.setToolTip('Current Director/selected File') self.pasteButton = QToolButton() self.pasteButton.setEnabled(False) self.pasteButton.setText('Paste') self.pasteButton.setToolTip( 'Copy file from copy path to current directory/file') self.pasteButton.clicked.connect(self.paste) self.pasteButton.hide() pasteBox.addWidget(self.textLine) pasteBox.addWidget(self.pasteButton) self.copyBox = QFrame() hbox = QHBoxLayout() hbox.setContentsMargins(0, 0, 0, 0) self.copyLine = QLineEdit() self.copyLine.setToolTip('File path to copy from, when pasting') self.copyButton = QToolButton() self.copyButton.setText('Copy') self.copyButton.setToolTip('Record current file as copy path') self.copyButton.clicked.connect(self.recordCopyPath) hbox.addWidget(self.copyButton) hbox.addWidget(self.copyLine) self.copyBox.setLayout(hbox) self.copyBox.hide() self.model = QFileSystemModel() self.model.setRootPath(QDir.currentPath()) self.model.setFilter(QDir.AllDirs | QDir.NoDot | QDir.Files) self.model.setNameFilterDisables(False) self.model.rootPathChanged.connect(self.folderChanged) self.list = QListView() self.list.setModel(self.model) self.updateDirectoryView(self.media_path) self.list.resize(640, 480) self.list.clicked[QModelIndex].connect(self.listClicked) self.list.activated.connect(self._getPathActivated) self.list.setAlternatingRowColors(True) self.cb = QComboBox() self.cb.currentIndexChanged.connect(self.filterChanged) self.fillCombobox(INFO.PROGRAM_FILTERS_EXTENSIONS) self.cb.setMinimumHeight(30) self.cb.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)) self.button2 = QToolButton() self.button2.setText('Media') self.button2.setSizePolicy( QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)) self.button2.setMinimumSize(60, 30) self.button2.setToolTip('Jump to Media directory') self.button2.clicked.connect(self.onJumpClicked) SettingMenu = QMenu(self) self.settingMenu = SettingMenu for i in ('Media', 'User'): axisButton = QAction(QIcon.fromTheme('user-home'), i, self) # weird lambda i=i to work around 'function closure' axisButton.triggered.connect( lambda state, i=i: self.jumpTriggered(i)) SettingMenu.addAction(axisButton) self.button2.setMenu(SettingMenu) self.button3 = QToolButton() self.button3.setText('Add Jump') self.button3.setSizePolicy( QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)) self.button3.setMinimumSize(60, 30) self.button3.setToolTip('Add current directory to jump button list') self.button3.clicked.connect(self.onActionClicked) hbox = QHBoxLayout() hbox.addWidget(self.button2) hbox.addWidget(self.button3) hbox.insertStretch(2, stretch=0) hbox.addWidget(self.cb) windowLayout = QVBoxLayout() windowLayout.addLayout(pasteBox) windowLayout.addWidget(self.copyBox) windowLayout.addWidget(self.list) windowLayout.addLayout(hbox) self.setLayout(windowLayout) self.show() def _hal_init(self): if self.PREFS_: last_path = self.PREFS_.getpref('last_loaded_directory', self.media_path, str, 'BOOK_KEEPING') self.updateDirectoryView(last_path) LOG.debug("lAST FILE PATH: {}".format(last_path)) else: LOG.debug("lAST FILE PATH: {}".format(self.media_path)) self.updateDirectoryView(self.media_path) ######################### # callbacks ######################### # add shown text and hidden filter data from the INI def fillCombobox(self, data): for i in data: self.cb.addItem(i[0], i[1]) def folderChanged(self, data): self.currentFolder = data self.textLine.setText(data) def updateDirectoryView(self, path): self.list.setRootIndex(self.model.setRootPath(path)) # retrieve selected filter (it's held as QT.userData) def filterChanged(self, index): userdata = self.cb.itemData(index) self.model.setNameFilters(userdata) def listClicked(self, index): # the signal passes the index of the clicked item dir_path = self.model.filePath(index) if self.model.fileInfo(index).isFile(): self.currentPath = dir_path self.textLine.setText(self.currentPath) return root_index = self.model.setRootPath(dir_path) self.list.setRootIndex(root_index) def onUserClicked(self): self.showUserDir() def onMediaClicked(self): self.showMediaDir() def onJumpClicked(self): data = self.button2.text() if data == 'Media': self.showMediaDir() elif data == 'User': self.showUserDir() else: self.updateDirectoryView(self.button2.text()) def jumpTriggered(self, data): if data == 'Media': self.button2.setText('{}'.format(data)) self.button2.setToolTip('Jump to Media directory') self.showMediaDir() elif data == 'User': self.button2.setText('{}'.format(data)) self.button2.setToolTip('Jump to User directory') self.showUserDir() else: self.button2.setText('{}'.format(data)) self.button2.setToolTip('Jump to directory: {}'.format(data)) self.updateDirectoryView(self.button2.text()) def onActionClicked(self): i = self.currentFolder button = QAction(QIcon.fromTheme('user-home'), i, self) # weird lambda i=i to work around 'function closure' button.triggered.connect(lambda state, i=i: self.jumpTriggered(i)) self.settingMenu.addAction(button) # get current selection and update the path # then if the path is good load it into linuxcnc # record it in the preference file if available def _getPathActivated(self): row = self.list.selectionModel().currentIndex() self.listClicked(row) fname = self.currentPath if fname is None: return if fname: self.load(fname) def recordCopyPath(self): data, isFile = self.getCurrentSelected() if isFile: self.copyLine.setText(os.path.normpath(data)) self.pasteButton.setEnabled(True) else: self.copyLine.setText('') self.pasteButton.setEnabled(False) STATUS.emit('error', OPERATOR_ERROR, 'Can only copy a file, not a folder') def paste(self): res = self.copyFile(self.copyLine.text(), self.textLine.text()) if res: self.copyLine.setText('') self.pasteButton.setEnabled(False) ######################## # helper functions ######################## def showCopyControls(self, state): if state: self.copyBox.show() self.pasteButton.show() else: self.copyBox.hide() self.pasteButton.hide() def showMediaDir(self): self.updateDirectoryView(self.user_path) def showUserDir(self): self.updateDirectoryView(self.media_path) def copyFile(self, s, d): try: shutil.copy(s, d) return True except Exception as e: LOG.error("Copy file error: {}".format(e)) STATUS.emit('error', OPERATOR_ERROR, "Copy file error: {}".format(e)) return False # moves the selection up # used with MPG scrolling def up(self): self.select_row('up') # moves the selection down # used with MPG scrolling def down(self): self.select_row('down') def select_row(self, style='down'): style = style.lower() selectionModel = self.list.selectionModel() row = selectionModel.currentIndex().row() self.rows = self.model.rowCount(self.list.rootIndex()) if style == 'last': row = self.rows elif style == 'up': if row > 0: row -= 1 else: row = 0 elif style == 'down': if row < self.rows: row += 1 else: row = self.rows else: return top = self.model.index(row, 0, self.list.rootIndex()) selectionModel.setCurrentIndex( top, QItemSelectionModel.Select | QItemSelectionModel.Rows) selection = QItemSelection(top, top) selectionModel.clearSelection() selectionModel.select(selection, QItemSelectionModel.Select) # returns the current highlighted (selected) path as well as # whether it's a file or not. def getCurrentSelected(self): selectionModel = self.list.selectionModel() index = selectionModel.currentIndex() dir_path = self.model.filePath(index) if self.model.fileInfo(index).isFile(): return (dir_path, True) else: return (dir_path, False) # This can be class patched to do something else def load(self, fname=None): if fname is None: self._getPathActivated() return self.recordBookKeeping() ACTION.OPEN_PROGRAM(fname) STATUS.emit('update-machine-log', 'Loaded: ' + fname, 'TIME') # This can be class patched to do something else def recordBookKeeping(self): fname = self.currentPath if fname is None: return if self.PREFS_: self.PREFS_.putpref('last_loaded_directory', self.model.rootPath(), str, 'BOOK_KEEPING') self.PREFS_.putpref('RecentPath_0', fname, str, 'BOOK_KEEPING')
class TextRenamer(QtWidgets.QMainWindow): ui = None currentFullNames = None currentExtensions = None path = None currentIndex = None showExtension = False def __init__(self): # init user interface: super(TextRenamer, self).__init__() self.show() self.ui = uic.loadUi('textRenamer.ui', self) # TODO: remember last used path self.path = QDir.rootPath() # Set up files and folder views self.foldersModel = QFileSystemModel() self.foldersModel.setRootPath(self.path) self.foldersModel.setFilter(QDir.NoDotAndDotDot | QDir.AllDirs) self.filesModel = QFileSystemModel() self.filesModel.setFilter(QDir.NoDotAndDotDot | QDir.Files) self.ui.folderView.setModel(self.foldersModel) self.ui.filesView.setModel(self.filesModel) # Remove all columns but the folder name self.ui.folderView.setRootIndex(self.foldersModel.index(self.path)) self.ui.folderView.hideColumn(1) self.ui.folderView.hideColumn(2) self.ui.folderView.hideColumn(3) # Set path for start self.ui.filesView.setRootIndex(self.filesModel.index(self.path)) # Event handlers: self.ui.renameButton.clicked.connect(self.renameFiles) self.ui.folderView.clicked.connect(self.openFolder) self.filesModel.directoryLoaded.connect(self.loadedFolder) self.ui.extensionCheckbox.stateChanged.connect( self.extensionCheckboxChanged) def extensionCheckboxChanged(self, state): if state == 0: self.showExtension = False else: self.showExtension = True self.readCurrentNames() def loadedFolder(self, path): # Delay reading to get correct file order QTimer.singleShot(1, self.readCurrentNames) def readCurrentNames(self): # Reset self.currentFullNames = [] for i in range(self.filesModel.rowCount(self.currentIndex)): self.currentFullNames.append(self.currentIndex.child(i, 0).data()) self.currentNames = [ os.path.splitext(x)[0] for x in self.currentFullNames ] self.currentExtensions = [ os.path.splitext(x)[1] for x in self.currentFullNames ] if self.showExtension: fileListText = '\n'.join(self.currentFullNames) else: fileListText = '\n'.join(self.currentNames) self.ui.newText.setPlainText(fileListText) # Only enable the rename button once everything is loaded self.ui.renameButton.setEnabled(True) def openFolder(self, index): self.path = self.foldersModel.fileInfo(index).absoluteFilePath() self.currentIndex = self.filesModel.setRootPath(self.path) self.ui.filesView.setRootIndex(self.currentIndex) # Clear info text self.ui.errorLabel.setText("") def renameFiles(self): # Read new names (only use as many rows as we have currentFullNames): newNames = self.ui.newText.toPlainText().split( '\n')[:len(self.currentFullNames)] if self.showExtension: self.newFullNames = newNames else: # Add extension self.newFullNames = [ i + j for i, j in zip(newNames, self.currentExtensions) ] # TODO: Add temporary filenames for cases where a new name conflicts with a name that has not yet been renamed # Sanity checkes: enoughNames = False allNamesHaveCharacters = False allNamesUnique = False # Sanity check: All new names have characters if len(self.newFullNames) >= len(self.currentFullNames): enoughNames = True else: self.ui.errorLabel.setText("Error: Not enough names given") # Sanity chcek: All names have characters allNamesHaveCharacters = True for newName in self.newFullNames: if newName == "": allNamesHaveCharacters = False self.ui.errorLabel.setText("Error: Blank new name") break # Sanity chcek: All names unique if self.allUnique(self.newFullNames): allNamesUnique = True else: self.ui.errorLabel.setText("Error: All names have to be unique") renamedCount = 0 notChangedCount = 0 errorCount = 0 # Rename if enoughNames and allNamesHaveCharacters and allNamesUnique: for currentName, newName in zip(self.currentFullNames, self.newFullNames): print(self.path + "/" + currentName + " > " + newName) # Dont rename needlessly if not currentName == newName: currentFile = os.path.join(self.path, currentName).replace( os.sep, '/') newFileName = os.path.join(self.path, newName).replace(os.sep, '/') # Only rename if the current file exists if os.path.exists(currentFile): renamedOk = QtCore.QFile.rename( currentFile, newFileName) if not renamedOk: errorCount += 1 renamedCount += 1 else: notChangedCoint += 1 else: notChangedCount += 1 labelText = "Renamed: " + str(renamedCount) + " files" if notChangedCount > 0: labelText += ", No change: " + str(notChangedCount) if errorCount > 0: labelText += ", Error: " + str(errorCount) # Set info text self.ui.errorLabel.setText(labelText) # Early exit uniqueness checker, returns true if all items in x are unique def allUnique(self, x): seen = set() return not any(i in seen or seen.add(i) for i in x)