class MyGridFilesWidget(QWidget): def __init__(self, parent): super(QWidget, self).__init__(parent) start_dir = QStringListModel() start_dir = 'C:/ROBOCZY' self.model = QFileSystemModel() self.model.setFilter(QDir.AllDirs | QDir.NoDotAndDotDot | QDir.AllEntries) self.model.setNameFilters() self.model.setNameFilterDisables(0) #self.model.setRootPath(start_dir) #self.model.setRootPath(start_dir) self.tree = QTreeView() self.tree.setRootIndex(self.model.index(start_dir)) self.tree.setModel(self.model) self.tree.setAnimated(False) self.tree.setIndentation(20) self.tree.setSortingEnabled(True) self.tree.setWindowTitle("Dir View") self.tree.resize(640, 480) windowLayout = QVBoxLayout() windowLayout.addWidget(self.tree) self.setLayout(windowLayout)
class PangoFileWidget(PangoDockWidget): def __init__(self, title, parent=None): super().__init__(title, parent) self.setFixedWidth(160) self.file_model = QFileSystemModel() self.file_model.setFilter(QDir.Files | QDir.NoDotAndDotDot) self.file_model.setNameFilters(["*.jpg", "*.png"]) self.file_model.setNameFilterDisables(False) self.th_provider = ThumbnailProvider() self.file_model.setIconProvider(self.th_provider) self.file_view = QListView() self.file_view.setModel(self.file_model) self.file_view.setViewMode(QListView.IconMode) self.file_view.setFlow(QListView.LeftToRight) self.file_view.setIconSize(QSize(150, 150)) self.setWidget(self.file_view) def select_next_image(self): c_idx = self.file_view.currentIndex() idx = c_idx.siblingAtRow(c_idx.row()+1) if idx.row() != -1: self.file_view.setCurrentIndex(idx) def select_prev_image(self): c_idx = self.file_view.currentIndex() idx = c_idx.siblingAtRow(c_idx.row()-1) if idx.row() != -1: self.file_view.setCurrentIndex(idx)
class FileChooser(QWidget): fileOpened = pyqtSignal(str) def __init__(self, parent=None): super().__init__(parent) self.folderBox = QComboBox(self) self.explorerTree = FileTreeView(self) self.explorerTree.doubleClickCallback = self._fileOpened self.explorerModel = QFileSystemModel(self) self.explorerModel.setFilter( QDir.AllDirs | QDir.Files | QDir.NoDotAndDotDot) self.explorerModel.setNameFilters(["*.py"]) self.explorerModel.setNameFilterDisables(False) self.explorerTree.setModel(self.explorerModel) for index in range(1, self.explorerModel.columnCount()): self.explorerTree.hideColumn(index) self.setCurrentFolder() self.folderBox.currentIndexChanged[int].connect( self.updateCurrentFolder) layout = QVBoxLayout(self) layout.addWidget(self.folderBox) layout.addWidget(self.explorerTree) layout.setContentsMargins(5, 5, 0, 0) def _fileOpened(self, modelIndex): path = self.explorerModel.filePath(modelIndex) if os.path.isfile(path): self.fileOpened.emit(path) def currentFolder(self): return self.explorerModel.rootPath() def setCurrentFolder(self, path=None): if path is None: app = QApplication.instance() path = app.getScriptsDirectory() else: assert os.path.isdir(path) self.explorerModel.setRootPath(path) self.explorerTree.setRootIndex(self.explorerModel.index(path)) self.folderBox.blockSignals(True) self.folderBox.clear() style = self.style() dirIcon = style.standardIcon(style.SP_DirIcon) self.folderBox.addItem(dirIcon, os.path.basename(path)) self.folderBox.insertSeparator(1) self.folderBox.addItem(self.tr("Browse…")) self.folderBox.setCurrentIndex(0) self.folderBox.blockSignals(False) def updateCurrentFolder(self, index): if index < self.folderBox.count() - 1: return path = QFileDialog.getExistingDirectory( self, self.tr("Choose Directory"), self.currentFolder(), QFileDialog.ShowDirsOnly) if path: QSettings().setValue("scripting/path", path) self.setCurrentFolder(path)
class FileTree(QWidget): def __init__(self, defaultfolder=r'c:\Zen_Output'): super(QWidget, self).__init__() filter = ['*.czi', '*.ome.tiff', '*ome.tif' '*.tiff' '*.tif'] # define the style for the FileTree via s style sheet self.setStyleSheet(""" QTreeView::item { background-color: rgb(38, 41, 48); font-weight: bold; } QTreeView::item::selected { background-color: rgb(38, 41, 48); color: rgb(0, 255, 0); } QTreeView QHeaderView:section { background-color: rgb(38, 41, 48); color: rgb(255, 255, 255); } """) self.model = QFileSystemModel() self.model.setRootPath(defaultfolder) self.model.setFilter(QtCore.QDir.AllDirs | QDir.Files | QtCore.QDir.NoDotAndDotDot) self.model.setNameFilterDisables(False) self.model.setNameFilters(filter) self.tree = QTreeView() self.tree.setModel(self.model) self.tree.setRootIndex(self.model.index(defaultfolder)) self.tree.setAnimated(True) self.tree.setIndentation(20) self.tree.setSortingEnabled(False) header = self.tree.header() header.setSectionResizeMode(QtWidgets.QHeaderView.ResizeToContents) windowLayout = QVBoxLayout() windowLayout.addWidget(self.tree) self.setLayout(windowLayout) self.tree.clicked.connect(self.on_treeView_clicked) @pyqtSlot() def on_treeView_clicked(self, index): indexItem = self.model.index(index.row(), 0, index.parent()) filename = self.model.fileName(indexItem) filepath = self.model.filePath(indexItem) # open the file when clicked print('Opening ImageFile : ', filepath) open_image_stack(filepath)
def directory(app): #app = QApplication(sys.argv) QCoreApplication.setApplicationVersion(QT_VERSION_STR) parser = QCommandLineParser() parser.setApplicationDescription("File Directory") parser.addHelpOption() parser.addVersionOption() dontUseCustomDirectoryIconsOption = QCommandLineOption( 'C', "Set QFileIconProvider.DontUseCustomDirectoryIcons") parser.addOption(dontUseCustomDirectoryIconsOption) parser.addPositionalArgument('', "The directory to start in.") parser.process(app) try: rootPath = parser.positionalArguments().pop(0) except IndexError: rootPath = None model = QFileSystemModel() model.setRootPath('') filter = ['*.db'] #filtering out just by db model.setNameFilters(filter) model.setNameFilterDisables(0) #Only show the filtered .db paths #filename = model.filePath() #print(filename) if parser.isSet(dontUseCustomDirectoryIconsOption): model.iconProvider().setOptions( QFileIconProvider.DontUseCustomDirectoryIcons) tree = QTreeView() tree.setModel(model) if rootPath is not None: rootIndex = model.index(QDir.cleanPath(rootPath)) if rootIndex.isValid(): tree.setRootIndex(rootIndex) # Demonstrating look and feel features. tree.setAnimated(False) tree.setIndentation(20) tree.setSortingEnabled(True) availableSize = QApplication.desktop().availableGeometry(tree).size() tree.resize(availableSize / 2) tree.setColumnWidth(0, tree.width() / 3) tree.setWindowTitle("Directory View") tree.show() sys.exit(app.exec_())
class DirTreeView(QTreeView): """ A widget class used to display the contents of an Informatic project source directory. """ newSelection = pyqtSignal([list]) def __init__(self, parent=None, rootdir=None): """ The rootdir keyword argument is a filepath for a directory initialized as the root directory whose contents are displayed by the widget. """ # Invoke the QTreeView constructor super().__init__(parent) self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred) # The widget's contents are based on a file system model self.dirTree = QFileSystemModel() # Display only files with filename extensions commonly used for Inform 6 # source files self.dirTree.setNameFilterDisables(False) self.dirTree.setNameFilters(['*.inf', '*.i6', '*.h']) self.dirTree.setRootPath(rootdir) self.setModel(self.dirTree) self.cd(rootdir) # Hide all but the first column, which holds the filename for column in range(1, self.dirTree.columnCount()): self.hideColumn(column) def cd(self, path): """ Takes one argument, path, a directory filepath, and changes the root directory displayed by the widget to the directory at that filepath. """ self.setRootIndex(self.dirTree.index(path)) def selectionChanged(self, selected, deselected): """ Emits the newSelection signal with a list of selected items whenever the selection of items in the file tree is changed. """ self.newSelection.emit(selected.indexes())
class CommanderFileManager(object): """docstring for FileManager""" def __init__(self): self.files_tree_model = QFileSystemModel() self.set_filter() self.files_tree_model.setRootPath(os.path.abspath(__file__)) self._file = None def set_filter(self, filter_list=['*.lua']): self.files_tree_model.setFilter(QDir.AllDirs | QDir.NoDotAndDotDot | QDir.AllEntries) self.files_tree_model.setNameFilters(filter_list) self.files_tree_model.setNameFilterDisables(0) def get_path(self, index): return self.files_tree_model.filePath(index) def open(self, path): try: with open(path, 'rt') as f: text = f.read() self._file = path return text except Exception as e: print(e) return None def save(self, fp, text): """ save file document """ try: with open(fp, 'wt') as f: f.write(text) except Exception as e: return False return True @property def file(self): return self._file @file.setter def file(self, f): self._file = f
def browserFile(self): global Browser, Model self.browser = QTreeView() model = QFileSystemModel() model.setNameFilters(['*.nii']) model.setNameFilterDisables(False) model.setReadOnly(True) self.browser.setModel(model) self.browser.expandAll() self.browser.setColumnWidth(0, 400) self.browser.selectionModel().selectionChanged.connect(self.select) Browser = self.browser Model = model
def open_project(self, project): project_path = project.path qfsm = None # Should end up having a QFileSystemModel if project_path not in self.__projects: qfsm = QFileSystemModel() project.model = qfsm qfsm.setRootPath(project_path) qfsm.setFilter(QDir.AllDirs | QDir.Files | QDir.NoDotAndDotDot) # If set to true items that dont match are displayed disabled qfsm.setNameFilterDisables(False) pext = ["*{0}".format(x) for x in project.extensions] logger.debug(pext) qfsm.setNameFilters(pext) self.__projects[project_path] = project self.__check_files_for(project_path) self.projectOpened.emit(project_path) else: qfsm = self.__projects[project_path] return qfsm
class App(QWidget): def __init__(self): super().__init__() self.title = 'PyQt5 file system view - pythonspot.com' self.left = 10 self.top = 10 self.width = 640 self.height = 480 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('/home/rob/Muziek') filter = ["*.wav", "*.ogg"] self.model.setNameFilters(filter) self.model.setNameFilterDisables(0) root = self.model.setRootPath('/home/rob/Muziek') #print(root) self.tree = QTreeView() self.tree.setModel(self.model) self.tree.setRootIndex(root) self.tree.setAnimated(False) self.tree.setIndentation(20) self.tree.setSortingEnabled(True) self.tree.doubleClicked.connect(self.test) self.tree.setWindowTitle("Dir View") self.tree.resize(640, 480) windowLayout = QVBoxLayout() windowLayout.addWidget(self.tree) self.setLayout(windowLayout) self.show() def test(self, signal): file_path = self.model.filePath(signal) print(file_path)
def load_tree(self, project): """Load the tree view on the right based on the project selected.""" qfsm = QFileSystemModel() qfsm.setRootPath(project.path) load_index = qfsm.index(qfsm.rootPath()) qfsm.setFilter(QDir.AllDirs | QDir.NoDotAndDotDot) qfsm.setNameFilterDisables(False) pext = ["*{0}".format(x) for x in project.extensions] qfsm.setNameFilters(pext) self._tree.setModel(qfsm) self._tree.setRootIndex(load_index) t_header = self._tree.header() t_header.setHorizontalScrollMode(QAbstractItemView.ScrollPerPixel) t_header.setSectionResizeMode(0, QHeaderView.Stretch) t_header.setStretchLastSection(False) t_header.setClickable(True) self._tree.hideColumn(1) # Size self._tree.hideColumn(2) # Type self._tree.hideColumn(3) # Modification date
def load_tree(self, project): """Load the tree view on the right based on the project selected.""" qfsm = QFileSystemModel() qfsm.setRootPath(project.path) load_index = qfsm.index(qfsm.rootPath()) qfsm.setFilter(QDir.AllDirs | QDir.NoDotAndDotDot) qfsm.setNameFilterDisables(False) pext = ["*{0}".format(x) for x in project.extensions] qfsm.setNameFilters(pext) self._tree.setModel(qfsm) self._tree.setRootIndex(load_index) t_header = self._tree.header() t_header.setHorizontalScrollMode(QAbstractItemView.ScrollPerPixel) t_header.setSectionResizeMode(0, QHeaderView.Stretch) t_header.setStretchLastSection(False) t_header.setSectionsClickable(True) self._tree.hideColumn(1) # Size self._tree.hideColumn(2) # Type self._tree.hideColumn(3) # Modification date
def setupModel(self): self.list_view_imported_symbols.setModel( self._parent_controller.get_instruments_model()) fileName = "G:/Programming/Projects/QtStatisticCalculator/^spx_y_test.csv" name = AkFunctions.getShortName(fileName) headers, data = AkFunctions.loadCSV(fileName) xPeriod = AkPeriod(1, data, headers=headers) #instrument = AkInstrument(name, [xPeriod]) analysis_list = [ AkAnalysisType.Calendar, AkAnalysisType.Period, AkAnalysisType.Series ] instrument_ = AkInstrument(name, sources=[data], analysis_types=analysis_list, method=AkSelectionMethod.CC, precision=3) self._parent_controller.get_instruments_model().insertRows( 0, len([instrument_]), [instrument_]) fileSystemModel = QFileSystemModel() fileSystemModel.setFilter(QDir.AllDirs | QDir.NoDotAndDotDot | QDir.AllEntries) filters = ["*.csv"] fileSystemModel.setNameFilters(filters) fileSystemModel.setNameFilterDisables(False) fileSystemModel.setRootPath(QDir.currentPath()) self.tree_view_windows_files.setModel(fileSystemModel) self.tree_view_windows_files.hideColumn(1) self.tree_view_windows_files.hideColumn(2) self.tree_view_windows_files.hideColumn(3)
class App(QWidget): def __init__(self): super().__init__() self.title = 'PyQt5 file system view - pythonspot.com' self.left = 10 self.top = 10 self.width = 640 self.height = 480 self.initUI() def tree_cilcked(self, Qmodelidx): print(self.model.filePath(Qmodelidx)) print(self.model.fileName(Qmodelidx)) print(self.model.fileInfo(Qmodelidx)) 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()) # 这里过滤,只显示 py 文件 mf = self.model.setNameFilters(['*.py']) self.model.setNameFilterDisables(False) # 这里做展示 self.tree = QTreeView() self.tree.setModel(self.model) # self.tree.setRootIndex(self.model.index(QDir.currentPath())) self.tree.setRootIndex(self.model.index('g:\l')) self.tree.doubleClicked.connect(self.tree_cilcked) # 这里隐藏了目录信息展示 for i in [1, 2, 3]: self.tree.setColumnHidden(i, True) # 缩进 self.tree.setIndentation(10) self.tree.setWindowTitle("Dir View") self.tree.resize(640, 480) windowLayout = QVBoxLayout() windowLayout.addWidget(self.tree) self.setLayout(windowLayout) self.show()
class Ui(QtWidgets.QMainWindow): # this safely gets a widget reference def getUI(self, type, name): x = self.findChild(type, name) if x is None: raise Exception('cannot find widget ' + name) return x def clear(self): if not self.capturing: self.canvas.clear() self.stage = 0 self.data = None self.done = True # we've not done anything yet # set image for input to processing # input: numpy w x h x 3 image, RGB 0-255 def setImage(self, img): # cv is bgr, qt (and sensible things) are rgb img = cv.cvtColor(img, cv.COLOR_BGR2RGB) # crop to ROI and resize # img = cropSquare(img,340,430,100) img = cv.resize(img, dsize=(600, 600), interpolation=cv.INTER_CUBIC) self.img = img self.clear() self.canvas.display(0, self.img) self.done = False # input: filename # output: numpy w x h x 3 image, RGB 0-255 def loadFile(self, fname): img = cv.imread(fname) print("Image read") if img is None: raise Exception('cannot read image') self.setImage(img) # open file, get ROI and convert to grey def openFileAction(self): fname, _ = QFileDialog.getOpenFileName(self, 'Open file', '.', "Image files (*.jpg *.gif)") if fname is None or fname == '': return self.loadFile(fname) def nextStage(self): if self.done: return print("Stage {0}, image {1} ".format(self.stage, self.img.shape)) start = time.perf_counter() # perform the next stage - the type of the image depends on the stage. # At input it's a 24-bit image. self.img, self.data, self.done, text = ellipse_blob.stage( self.stage, (self.img, self.data)) self.stage = self.stage + 1 self.canvas.display(self.stage, self.img) print("Time taken {0} ".format(time.perf_counter() - start)) if isinstance(text, tuple): text, status = text st = self.getUI(QtWidgets.QPlainTextEdit, 'status') st.appendPlainText(status) self.canvas.text(self.stage, text) def findEllipsesAction(self): while True: print("Stage ", self.done, self.stage) self.nextStage() if self.done: break def liveCaptureAction(self): self.capturing = not self.capturing b = self.getUI(QtWidgets.QPushButton, 'liveCaptureButton') if self.capturing: s = "End live" b.setStyleSheet('QPushButton {background-color:#ff8080;}') else: s = "Begin live" b.setStyleSheet('QPushButton {}') b.setText(s) def fileClickedAction(self, idx): if not self.dirModel.isDir(idx): item = self.dirModel.filePath(idx) self.loadFile(item) # confirm a quit menu action def confirmQuitAction(self): # reply = QMessageBox.question(self, # 'Confirm', # 'Really quit?', QMessageBox.Yes | QMessageBox.No, QMessageBox.No) # if reply == QMessageBox.Yes: # app.quit() app.quit() def tick(self): if self.capturing: if self.done: if self.cam is None: self.cam = cv.VideoCapture(self.camNo) self.cam.set(cv.CAP_PROP_FRAME_WIDTH, 640) self.cam.set(cv.CAP_PROP_FRAME_HEIGHT, 480) self.cam.set(cv.CAP_PROP_BUFFERSIZE, 1) ret, img = self.cam.read() if not ret: # no cam, should turn off button and capturing self.liveCaptureAction() else: self.setImage(img) self.done = False else: self.nextStage() def __init__(self, *args, **kwargs): super(Ui, self).__init__( *args, **kwargs) # Call the inherited classes __init__ method uic.loadUi('test.ui', self) # Load the .ui file # now we get references to the widgets we want and connect # things up. Brackets here to make the line break work. (self.getUI(QtWidgets.QAction, 'actionQuit').triggered.connect(self.confirmQuitAction)) (self.getUI(QtWidgets.QAction, 'actionOpen').triggered.connect(self.openFileAction)) (self.getUI(QtWidgets.QAction, 'actionLive').triggered.connect(self.liveCaptureAction)) (self.getUI(QtWidgets.QPushButton, 'findEllipsesButton').clicked.connect( self.findEllipsesAction)) (self.getUI(QtWidgets.QPushButton, 'liveCaptureButton').clicked.connect( self.liveCaptureAction)) # set up the file tree self.dirModel = QFileSystemModel() self.dirModel.setRootPath(QDir.currentPath()) self.dirModel.setNameFilters(["*.jpg", "*.png"]) tree = self.getUI(QtWidgets.QTreeView, 'treeView') tree.setModel(self.dirModel) tree.setRootIndex(self.dirModel.index(QDir.currentPath())) tree.setIndentation(10) tree.setSortingEnabled(True) tree.setColumnWidth(0, tree.width() / 1.5) tree.setColumnHidden(1, True) tree.setColumnHidden(2, True) tree.doubleClicked.connect(self.fileClickedAction) self.treeView = tree # set up the live camera system (to null) self.capturing = False self.cam = None # set up the drawing area and the timer which updates it self.canvas = self.getUI(QtWidgets.QWidget, 'view') self.timer = QTimer() self.timer.timeout.connect(self.tick) self.timer.start(100) self.clear() # set up internal state self.show() # Show the GUI
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 MainView(QMainWindow): resizeCompleted = pyqtSignal() def __init__(self, model, controller, image_path): self.settings = SettingsModel() self.slideshow = SlideshowModel() self.model = model self.canvas = self.model.canvas self.main_controller = controller self.canvas_controller = CanvasController(self.canvas) super(MainView, self).__init__() self.build_ui() self.center_ui() # Resize timer to prevent laggy updates self.resize_timer = None self.resizeCompleted.connect(self.resize_completed) # Slideshow if self.settings.get('Slideshow', 'reverse') == 'True': self.slideshow.updateSignal.connect(self.on_previous_item) else: self.slideshow.updateSignal.connect(self.on_next_item) self.model.subscribe_update_func(self.update_ui_from_model) self.arguments = { 'image_path': image_path } def build_ui(self): self.ui = Ui_Hitagi() self.ui.setupUi(self) # File menu self.ui.actionSet_as_wallpaper.triggered.connect(self.on_set_as_wallpaper) self.ui.actionCopy_to_clipboard.triggered.connect(self.on_clipboard) self.ui.actionOpen_current_directory.triggered.connect(self.on_current_dir) self.ui.actionOptions.triggered.connect(self.on_options) self.ui.actionExit.triggered.connect(self.on_close) # Folder menu self.ui.actionOpen_next.triggered.connect(self.on_next_item) self.ui.actionOpen_previous.triggered.connect(self.on_previous_item) self.ui.actionChange_directory.triggered.connect(self.on_change_directory) self.ui.actionSlideshow.triggered.connect(self.on_slideshow) # View menu self.ui.actionZoom_in.triggered.connect(self.on_zoom_in) self.ui.actionZoom_out.triggered.connect(self.on_zoom_out) self.ui.actionOriginal_size.triggered.connect(self.on_zoom_original) self.ui.actionRotate_clockwise.triggered.connect(self.on_rotate_clockwise) self.ui.actionRotate_counterclockwise.triggered.connect(self.on_rotate_counterclockwise) self.ui.actionFlip_horizontally.triggered.connect(self.on_flip_horizontal) self.ui.actionFlip_vertically.triggered.connect(self.on_flip_vertical) self.ui.actionFit_image_width.triggered.connect(self.on_scale_image_to_width) self.ui.actionFit_image_height.triggered.connect(self.on_scale_image_to_height) self.ui.actionFile_list.triggered.connect(self.on_toggle_filelist) self.ui.actionFullscreen.triggered.connect(self.on_fullscreen) # Favorite menu self.ui.actionAdd_to_favorites.triggered.connect(self.on_add_to_favorites) self.ui.actionRemove_from_favorites.triggered.connect(self.on_remove_from_favorites) # Help menu self.ui.actionChangelog.triggered.connect(self.on_changelog) self.ui.actionAbout.triggered.connect(self.on_about) # Load stylesheet stylesheet_dir = "resources/hitagi.stylesheet" with open(stylesheet_dir, "r") as sh: self.setStyleSheet(sh.read()) # File listing self.file_model = QFileSystemModel() self.file_model.setFilter(QDir.NoDotAndDotDot | QDir.AllDirs | QDir.Files) self.file_model.setNameFilters(['*.bmp', '*.gif', '*.jpg', '*.jpeg', '*.png', '*.png', '*.pbm', '*.pgm', '*.ppm', '*.xbm', '*.xpm']) self.file_model.setNameFilterDisables(False) self.file_model.setRootPath(self.settings.get('Directory', 'default')) self.ui.treeView.setModel(self.file_model) self.ui.treeView.setColumnWidth(0, 120) self.ui.treeView.setColumnWidth(1, 120) self.ui.treeView.hideColumn(1) self.ui.treeView.hideColumn(2) # Double click self.ui.treeView.activated.connect(self.on_dir_list_activated) # Update file list self.ui.treeView.clicked.connect(self.on_dir_list_clicked) # Open parent self.ui.pushButton_open_parent.clicked.connect(self.on_open_parent) self.ui.pushButton_favorite.clicked.connect(self.on_manage_favorite) # Shortcuts _translate = QCoreApplication.translate self.ui.actionExit.setShortcut(_translate("Hitagi", self.settings.get('Hotkeys', 'Exit'))) self.ui.actionOpen_next.setShortcut(_translate("Hitagi", self.settings.get('Hotkeys', 'Next'))) self.ui.actionOpen_previous.setShortcut(_translate("Hitagi", self.settings.get('Hotkeys', 'Previous'))) self.ui.actionChange_directory.setShortcut(_translate("Hitagi", self.settings.get('Hotkeys', 'Directory'))) self.ui.actionAdd_to_favorites.setShortcut(_translate("Hitagi", self.settings.get('Hotkeys', 'Add to favorites'))) self.ui.actionRemove_from_favorites.setShortcut(_translate("Hitagi", self.settings.get('Hotkeys', 'Remove from favorites'))) self.ui.actionSlideshow.setShortcut(_translate("Hitagi", self.settings.get('Hotkeys', 'Slideshow'))) self.ui.actionZoom_in.setShortcut(_translate("Hitagi", self.settings.get('Hotkeys', 'Zoom in'))) self.ui.actionZoom_out.setShortcut(_translate("Hitagi", self.settings.get('Hotkeys', 'Zoom out'))) self.ui.actionOriginal_size.setShortcut(_translate("Hitagi", self.settings.get('Hotkeys', 'Zoom original'))) self.ui.actionRotate_clockwise.setShortcut(_translate("Hitagi", self.settings.get('Hotkeys', 'Rotate clockwise'))) self.ui.actionRotate_counterclockwise.setShortcut(_translate("Hitagi", self.settings.get('Hotkeys', 'Rotate counterclockwise'))) self.ui.actionFlip_horizontally.setShortcut(_translate("Hitagi", self.settings.get('Hotkeys', 'Flip horizontal'))) self.ui.actionFlip_vertically.setShortcut(_translate("Hitagi", self.settings.get('Hotkeys', 'Flip vertical'))) self.ui.actionFit_image_width.setShortcut(_translate("Hitagi", self.settings.get('Hotkeys', 'Fit to width'))) self.ui.actionFit_image_height.setShortcut(_translate("Hitagi", self.settings.get('Hotkeys', 'Fit to height'))) self.ui.actionFile_list.setShortcut(_translate("Hitagi", self.settings.get('Hotkeys', 'Toggle filelist'))) self.ui.actionFullscreen.setShortcut(_translate("Hitagi", self.settings.get('Hotkeys', 'Fullscreen'))) # Load favorites in UI self.load_favorites() # Background self.ui.graphicsView.setBackgroundBrush(QBrush(QColor(self.settings.get('Look', 'background')), Qt.SolidPattern)) # Save current height for fullscreen mode self.default_menubar_height = self.ui.menubar.height() # Save current width for file list self.default_filelist_width = self.ui.fileWidget.width() def load_favorites(self): self.favorites = FavoritesModel() self.ui.menuFavorites.clear() for item in self.favorites.items(): self.ui.menuFavorites.addAction(item).triggered.connect((lambda item: lambda: self.on_open_favorite(item))(item)) def on_open_favorite(self, path): self.main_controller.change_directory(path) def center_ui(self): ui_geometry = self.frameGeometry() center_point = QDesktopWidget().availableGeometry().center() ui_geometry.moveCenter(center_point) self.move(ui_geometry.topLeft()) # Qt show event def showEvent(self, event): self.main_controller.start(self.arguments['image_path']) # Arguments and starting behaviour # Start in fullscreen mode according to settings if self.settings.get('Misc', 'fullscreen_mode') == 'True': self.on_fullscreen() # Initialize container geometry to canvas self.canvas_controller.update(self.ui.graphicsView.width(), self.ui.graphicsView.height()) self.main_controller.update_canvas() def update_resize_timer(self, interval=None): if self.resize_timer is not None: self.killTimer(self.resize_timer) if interval is not None: self.resize_timer = self.startTimer(interval) else: self.resize_timer = None # Qt resize event def resizeEvent(self, event): self.update_resize_timer(300) # Qt timer event def timerEvent(self, event): if event.timerId() == self.resize_timer: self.update_resize_timer() self.resizeCompleted.emit() def resize_completed(self): self.canvas_controller.update(self.ui.graphicsView.width(), self.ui.graphicsView.height()) self.main_controller.update_canvas() # Additional static shortcuts def keyPressEvent(self, e): if e.key() == Qt.Key_Escape and self.model.is_fullscreen: self.main_controller.toggle_fullscreen() def on_open_parent(self): parent_index = self.file_model.parent(self.file_model.index(self.file_model.rootPath())) self.file_model.setRootPath(self.file_model.filePath(parent_index)) self.ui.treeView.setRootIndex(parent_index) # Update directory path self.model.directory = self.file_model.filePath(parent_index) self.update_ui_from_model() def on_dir_list_activated(self, index): if self.file_model.isDir(index) is not False: self.file_model.setRootPath(self.file_model.filePath(index)) self.ui.treeView.setRootIndex(index) # Save current path self.model.directory = self.file_model.filePath(index) self.update_ui_from_model() def on_dir_list_clicked(self, index): self.main_controller.open_image(self.file_model.filePath(index)) # File menu def on_set_as_wallpaper(self): from hitagilib.view.WallpaperView import WallpaperDialog from hitagilib.controller.wallpaper import WallpaperController image = self.model.get_image() if image is not None: dialog = WallpaperDialog(self, None, WallpaperController(self.model), image) dialog.show() def on_clipboard(self): self.main_controller.copy_to_clipboard() def on_current_dir(self): if not self.main_controller.open_in_explorer(): self.show_explorer_error() def on_options(self): from hitagilib.view.OptionsView import OptionDialog self.dialog = OptionDialog(self) self.dialog.show() def on_close(self): if self.slideshow.isRunning(): self.slideshow.exit() self.close() # Folder menu def on_next_item(self): current_index = self.ui.treeView.currentIndex() # Slideshow restart - determine if we are at the end of our file list if self.slideshow.is_running and self.settings.get('Slideshow', 'restart') == 'True' and not self.ui.treeView.indexBelow(current_index).isValid(): self.main_controller.open_image(self.file_model.filePath(current_index)) self.on_slideshow_restart(0) # Restart slideshow elif self.slideshow.is_running and self.settings.get('Slideshow', 'random') == 'True': # Random index - moveCursor expects constants @http://doc.qt.io/qt-5/qabstractitemview.html#CursorAction-enum index = self.ui.treeView.moveCursor(randint(0,9), Qt.NoModifier) self.ui.treeView.setCurrentIndex(index) self.main_controller.open_image(self.file_model.filePath(index)) else: # Proceed normally, scroll down index = self.ui.treeView.moveCursor(QAbstractItemView.MoveDown, Qt.NoModifier) self.ui.treeView.setCurrentIndex(index) self.main_controller.open_image(self.file_model.filePath(index)) def on_previous_item(self): current_index = self.ui.treeView.currentIndex() # Slideshow restart (reverse) - determine if we are the the top of our file list if self.slideshow.is_running and self.settings.get('Slideshow', 'restart') == 'True' and not self.ui.treeView.indexAbove(current_index).isValid(): self.main_controller.open_image(self.file_model.filePath(current_index)) self.on_slideshow_restart(1) # Restart slideshow elif self.slideshow.is_running and self.settings.get('Slideshow', 'random') == 'True': # Random index index = self.ui.treeView.moveCursor(randint(0,9), Qt.NoModifier) self.ui.treeView.setCurrentIndex(index) self.main_controller.open_image(self.file_model.filePath(index)) else: # Proceed normally, scroll up index = self.ui.treeView.moveCursor(QAbstractItemView.MoveUp, Qt.NoModifier) self.ui.treeView.setCurrentIndex(index) self.main_controller.open_image(self.file_model.filePath(index)) def on_slideshow(self): if self.ui.actionSlideshow.isChecked(): self.slideshow.start() self.slideshow.is_running = True else: self.slideshow.is_running = False self.slideshow.exit() def on_slideshow_restart(self, direction): # 0: Restart from top to bottom # 1: Restart from bottom to top if direction == 0: index = self.ui.treeView.moveCursor(QAbstractItemView.MoveHome, Qt.NoModifier) self.main_controller.open_image(self.file_model.filePath(index)) else: index = self.ui.treeView.moveCursor(QAbstractItemView.MoveEnd, Qt.NoModifier) self.main_controller.open_image(self.file_model.filePath(index)) self.ui.treeView.setCurrentIndex(index) def on_change_directory(self): self.main_controller.change_directory() # View menu def on_zoom_in(self): self.canvas_controller.scale_image(1.1) def on_zoom_out(self): self.canvas_controller.scale_image(0.9) def on_rotate_clockwise(self): self.canvas_controller.rotate_image(90) def on_rotate_counterclockwise(self): self.canvas_controller.rotate_image(-90) def on_flip_horizontal(self): self.canvas_controller.flip_image(0) def on_flip_vertical(self): self.canvas_controller.flip_image(1) def on_scale_image_to_width(self): self.canvas_controller.update_image(1) def on_scale_image_to_height(self): self.canvas_controller.update_image(2) def on_zoom_original(self): self.canvas_controller.update_image(3) def on_toggle_filelist(self): if self.ui.fileWidget.isHidden(): self.ui.fileWidget.show() else: self.ui.fileWidget.hide() self.update_resize_timer(300) def on_fullscreen(self): self.main_controller.toggle_fullscreen() if self.model.is_fullscreen: self.showFullScreen() if self.settings.get('Misc', 'hide_menubar') == 'True': self.ui.menubar.setMaximumHeight(0) # Workaround to preserve shortcuts else: self.showNormal() if self.settings.get('Misc', 'hide_menubar') == 'True': self.ui.menubar.setMaximumHeight(self.default_menubar_height) self.canvas_controller.update(self.ui.graphicsView.width(), self.ui.graphicsView.height()) self.main_controller.update_canvas() # Favorite button def on_manage_favorite(self): if self.main_controller.check_favorites(self.model.directory): self.on_remove_from_favorites() else: self.on_add_to_favorites() # Favorite menu def on_add_to_favorites(self): self.main_controller.add_to_favorites() self.load_favorites() self.update_ui_from_model() def on_remove_from_favorites(self): self.main_controller.remove_from_favorites() self.load_favorites() self.update_ui_from_model() # Help menu def on_changelog(self): webbrowser.open('https://github.com/gimu/hitagi-reader/releases') def on_about(self): from hitagilib.view.AboutView import AboutDialog dialog = AboutDialog(self, None, None) dialog.show() def on_fileWidget_visibilityChanged(self, visible): """On file list hide/show and de/attachment""" if visible: self.ui.actionFile_list.setChecked(True) else: self.ui.actionFile_list.setChecked(False) self.update_resize_timer(300) def show_explorer_error(self): notify = QMessageBox() notify.setWindowTitle("Error") notify.setText(QCoreApplication.translate('Hitagi', "Couldn't open the current directory with an appropriate filemanager!")) notify.exec_() def update_ui_from_model(self): """Update UI from model.""" self.settings = SettingsModel() # On changing directory self.file_model.setRootPath(self.model.directory) self.ui.treeView.setRootIndex(self.file_model.index(self.model.directory)) # Update favorite button if self.main_controller.check_favorites(self.model.directory): self.ui.pushButton_favorite.setText(QCoreApplication.translate('Hitagi', "Unfavorite")) else: self.ui.pushButton_favorite.setText(QCoreApplication.translate('Hitagi', "Favorite")) # Canvas update self.ui.graphicsView.setScene(self.canvas.scene)
class FileRenamer(QWidget): def __init__(self, parent=None): QWidget.__init__(self, parent=parent) self.help_text = '' self.in_context = False self.build_ui() self.connect_events() self.cache = {} def build_ui(self): self.ui = UI_file_renamer.Ui_Form() self.ui.setupUi(self) self.setAcceptDrops(True) self.original_model = QFileSystemModel() self.original_model.setNameFilters(["*.wav", "*.zc"]) self.original_model.setNameFilterDisables(False) self.ui.treeview_original.setModel(self.original_model) for i in range(6, 0, -1): self.ui.treeview_original.setColumnHidden(i, True) self.changed_model = RenamedFileSystemModel( rename_function=self.change_fname_function) self.changed_model.setNameFilters(["*.wav", "*.zc"]) self.changed_model.setNameFilterDisables(False) self.ui.treeview_changed.setModel(self.changed_model) for i in range(6, 0, -1): self.ui.treeview_changed.setColumnHidden(i, True) icon = QIcon( utils.resource_path('../resources/icons/nabat_circle_color.ico')) self.setWindowIcon(icon) self.ui.progressBar.setVisible(False) self.ui.progressBar_tarball.setVisible(False) def connect_events(self): self.ui.btn_browse.clicked.connect(self.browse) self.ui.btn_browse_output.clicked.connect(self.browse_output) self.ui.directory_name.textChanged.connect(self.load_directory) self.ui.btn_save.clicked.connect(self.save) self.ui.btn_tarball.clicked.connect(self.tarball) self.ui.treeview_original.expanded.connect(self.expand_changed) self.ui.treeview_original.collapsed.connect(self.collapse_changed) self.ui.treeview_changed.expanded.connect(self.expand_original) self.ui.treeview_changed.collapsed.connect(self.collapse_original) self.sb_original = self.ui.treeview_original.verticalScrollBar() self.sb_original.valueChanged.connect(self.sync_scroll_right) self.sb_changed = self.ui.treeview_changed.verticalScrollBar() self.sb_changed.valueChanged.connect(self.sync_scroll_left) self.ui.chk_auto_rename.stateChanged.connect(self.update_filenames) self.ui.chk_replace.stateChanged.connect(self.update_filenames) self.ui.replace_from.textChanged.connect(self.update_filenames) self.ui.replace_to.textChanged.connect(self.update_filenames) self.ui.chk_replace.stateChanged.connect(self.enable_chk_replace) self.ui.chk_replace2.stateChanged.connect(self.enable_chk_replace2) self.ui.chk_folderGRTS.stateChanged.connect(self.enable_chk_grtsfolder) self.ui.chk_sitename_folder.stateChanged.connect( self.enable_chk_sitefolder) self.ui.site_text.textChanged.connect(self.update_filenames) self.ui.grts_txt.textChanged.connect(self.update_filenames) self.ui.replace_from.textChanged.connect(self.update_filenames) self.ui.replace_to.textChanged.connect(self.update_filenames) self.ui.replace_from2.textChanged.connect(self.update_filenames) self.ui.replace_to2.textChanged.connect(self.update_filenames) self.ui.grts_folder.toggled.connect(self.grts_parent_change) self.ui.grts_folder2x.toggled.connect(self.grts_parent_change) self.ui.site_folder.toggled.connect(self.site_parent_change) self.ui.site_folder2x.toggled.connect(self.site_parent_change) def grts_parent_change(self): self.ui.grts_txt.setText('') self.update_filenames() def site_parent_change(self): self.ui.site_text.setText('') self.update_filenames() def enable_chk_replace(self): checked = self.ui.chk_replace.isChecked() self.ui.replace_from.setEnabled(checked) self.ui.replace_to.setEnabled(checked) self.ui.chk_use_re.setEnabled(checked) self.ui.label_2.setEnabled(checked) self.update_filenames() def enable_chk_replace2(self): checked = self.ui.chk_replace2.isChecked() self.ui.replace_from2.setEnabled(checked) self.ui.replace_to2.setEnabled(checked) self.ui.chk_use_re2.setEnabled(checked) self.ui.label_3.setEnabled(checked) self.update_filenames() def enable_chk_grtsfolder(self): checked = self.ui.chk_folderGRTS.isChecked() self.ui.grts_folder.setEnabled(checked) self.ui.grts_folder2x.setEnabled(checked) self.ui.grts_folder3x.setEnabled(checked) self.ui.grts_txt.setEnabled(checked) self.update_filenames() def enable_chk_sitefolder(self): checked = self.ui.chk_sitename_folder.isChecked() self.ui.site_folder.setEnabled(checked) self.ui.site_folder2x.setEnabled(checked) self.ui.site_folder3x.setEnabled(checked) self.ui.site_text.setEnabled(checked) self.update_filenames() def sync_scroll_right(self, int): self.sb_changed.setValue(self.sb_original.value()) def sync_scroll_left(self, int): self.sb_original.setValue(self.sb_changed.value()) def update_filenames(self): self.load_directory() def expand_changed(self, index): self.ui.treeview_changed.setExpanded( self.changed_model.index(self.original_model.filePath(index)), True) def collapse_changed(self, index): self.ui.treeview_changed.setExpanded( self.changed_model.index(self.original_model.filePath(index)), False) def expand_original(self, index): self.ui.treeview_original.setExpanded( self.original_model.index(self.changed_model.filePath(index)), True) def collapse_original(self, index): self.ui.treeview_original.setExpanded( self.original_model.index(self.changed_model.filePath(index)), False) def browse(self): settings = QSettings('USGS', 'guanoeditor') last_data_fname = str(Path(settings.value('lastDataDname', '')).parent) dname = QFileDialog.getExistingDirectory(self, "Open a folder", last_data_fname) if dname: settings.setValue('lastDataDname', dname) self.ui.directory_name.setText(dname) self.load_directory() def browse_output(self): settings = QSettings('USGS', 'guanoeditor') last_data_fname = str(Path(settings.value('lastDataDname', '')).parent) dname = QFileDialog.getExistingDirectory(self, "Select an output folder", last_data_fname) if dname: settings.setValue('lastDataDname', dname) self.ui.directory_name_output.setText(dname) def dragEnterEvent(self, e): if e.mimeData().hasUrls() and e.mimeData().urls()[0].isLocalFile(): url = e.mimeData().urls()[0].toLocalFile() if url.endswith('.wav'): e.accept() else: e.ignore() else: e.ignore() def dropEvent(self, e): """ Updates the form with the contents of an xml node dropped onto it. Parameters ---------- e : qt event Returns ------- None """ try: e.setDropAction(Qt.CopyAction) e.accept() url = e.mimeData().urls()[0].toLocalFile() self.ui.file_name.setText(url) # self.from_xml(element) except: e = sys.exc_info()[0] print('problem drop', e) def change_fname_function(self, fname, index=None, f=None): renamed = fname if self.ui.chk_auto_rename.isChecked(): try: renamed = utils.clean_name(fname) except: renamed = fname try: if self.ui.chk_replace.isChecked(): if self.ui.chk_use_re.isChecked(): renamed = re.sub(self.ui.replace_from.text(), self.ui.replace_to.text(), renamed) else: renamed = renamed.replace(self.ui.replace_from.text(), self.ui.replace_to.text()) except: pass try: if self.ui.chk_replace2.isChecked(): if self.ui.chk_use_re2.isChecked(): renamed = re.sub(self.ui.replace_from2.text(), self.ui.replace_to2.text(), renamed) else: renamed = renamed.replace(self.ui.replace_from2.text(), self.ui.replace_to2.text()) except: pass if not self.ui.chk_folderGRTS.isChecked() and \ not self.ui.chk_sitename_folder.isChecked(): return renamed try: parts = renamed.split('_') time_str = parts[-1] date_str = parts[-2] if len(parts) == 4: grts_str, site_str = parts[:2] elif len(parts) == 3: grts_str = parts[0] site_str = 'UnknownSiteName' except: return renamed if self.ui.chk_folderGRTS.isChecked(): grts_str = self.ui.grts_txt.text() if not grts_str: if self.ui.grts_folder.isChecked(): if index is not None: grts_str = self.changed_model.parent(index).data() elif f is not None: grts_str = f.parent() elif self.ui.grts_folder2x.isChecked(): if index is not None: grts_str = self.changed_model.parent( index).parent().data() elif f is not None: grts_str = f.parent().parent() elif self.ui.grts_folder3x.isChecked(): if index is not None: grts_str = self.changed_model.parent( index).parent().parent().data() elif f is not None: grts_str = f.parent().parent().parent() if self.ui.chk_sitename_folder.isChecked(): site_str = self.ui.site_text.text() if not site_str: if self.ui.site_folder.isChecked(): if index is not None: site_str = self.changed_model.parent(index).data() elif f is not None: site_str = f.parent() elif self.ui.site_folder2x.isChecked(): if index is not None: site_str = self.changed_model.parent( index).parent().data() elif f is not None: site_str = f.parent().parent() elif self.ui.site_folder3x.isChecked(): if index is not None: site_str = self.changed_model.parent( index).parent().parent().data() elif f is not None: site_str = f.parent().parent().parent() try: parts = [grts_str, site_str, date_str, time_str] renamed = "_".join(parts) except: return renamed return renamed def load_directory(self): self.ui.treeview_original.setRootIndex( self.original_model.index(self.ui.directory_name.text())) self.original_model.setRootPath(self.ui.directory_name.text()) self.ui.treeview_changed.setRootIndex( self.changed_model.index(self.ui.directory_name.text())) self.changed_model.setRootPath(self.ui.directory_name.text()) def save(self): d = Path(self.ui.directory_name.text()) wavs = list(d.glob('**\*.wav')) wavs += list(d.glob('**\*.zc')) self.ui.progressBar.setVisible(True) self.ui.progressBar.setMinimum(1) self.ui.progressBar.setMaximum(len(wavs)) self.ui.progressBar.setVisible(1) if self.ui.directory_name_output.text(): if not Path(self.ui.directory_name_output.text()).exists(): msq = r"The output directory specified does not exist! Please point to an existing directory." QMessageBox.warning(self, "Output directory does not exist", msg) return None out_dir = Path(self.ui.directory_name_output.text()) make_copy = True else: out_dir = Path(self.ui.directory_name.text()) make_copy = False for f in wavs: original_fname = f.name new_fname = self.change_fname_function(original_fname, f=f) full_name = f.parent.joinpath(new_fname) full_name = str(full_name).replace(str(Path(self.ui.directory_name.text())), \ str(out_dir)) if not Path(full_name).exists(): Path(full_name).parent.mkdir(parents=True, exist_ok=True) if make_copy: shutil.copy(str(f), full_name) else: f.rename(full_name) if original_fname != new_fname: try: g = GuanoFile(full_name) g['Original Filename'] = original_fname g.write(make_backup=False) except: pass self.ui.progressBar.setValue(self.ui.progressBar.value() + 1) msg = f"Finished renaming files in directory:\n\n{out_dir}" QMessageBox.information(self, "Renaming Process Complete", msg) self.ui.progressBar.setVisible(False) def tarball(self): if self.ui.directory_name_output.text(): if not Path(self.ui.directory_name_output.text()).exists(): msq = r"The output directory specified does not exist! Please point to an existing directory." QMessageBox.warning(self, "Output directory does not exist", msg) return None out_dir = Path(self.ui.directory_name_output.text()) else: out_dir = Path(self.ui.directory_name.text()) wavs = list(out_dir.glob('**\*.wav')) wavs += list(out_dir.glob('**\*.zc')) self.ui.progressBar_tarball.setVisible(True) self.ui.progressBar_tarball.setMinimum(1) self.ui.progressBar_tarball.setMaximum(len(wavs)) self.ui.progressBar_tarball.setVisible(1) tar_name = out_dir.joinpath(out_dir.name + '.tar.gz') with tarfile.open(tar_name, "w:gz") as tar_handle: for f in wavs: tar_handle.add(str(f), arcname=f.name) self.ui.progressBar_tarball.setValue( self.ui.progressBar.value() + 1) self.ui.progressBar_tarball.setValue( self.ui.progressBar_tarball.maximum()) msg = f"Finished creating tar.gz archive of directory contents.:\n\n{tar_name}" QMessageBox.information(self, "Tarball Process Complete", msg) self.ui.progressBar_tarball.setVisible(False)
class TeamtoolBrowser(QWidget, design.Ui_Form): def __init__(self, parent=None): super(TeamtoolBrowser, self).__init__(parent) self.setupUi(self) self.browser = QWebEngineView() self.profile = QWebEngineProfile("somestorage", self.browser) self.webpage = QWebEnginePage(self.profile, self.browser) self.browser.setPage(self.webpage) self.initUI() self.eventUI() def eventUI(self): self.btnRefresh.clicked.connect(lambda: self.browse(refresh=True)) self.codeEdit.textChanged.connect(lambda: self.browse(offline=True)) self.fileView.doubleClicked.connect(self.selectFile) self.urlBar.returnPressed.connect(lambda: self.browse()) self.webpage.loadStarted.connect(lambda: self.startLoading()) self.webpage.loadFinished.connect(lambda: self.onLoadFinished()) self.btnPrev.clicked.connect(lambda: self.prev()) self.btnNext.clicked.connect(lambda: self.next()) def prev(self): ''' History: go back ''' self.webpage.page().triggerAction(QWebEnginePage.Back) self.urlBar.setText(self.webpage.url().toString()) def next(self): ''' History: move forward ''' self.webpage.page().triggerAction(QWebEnginePage.Forward) self.urlBar.setText(self.webpage.url().toString()) def startLoading(self): ''' When page starts to load ''' self.loadingAnimation.start() self.labelLoading.setVisible(True) def onLoadFinished(self): ''' When page has finished to load ''' self.loadingAnimation.stop() self.labelLoading.setVisible(False) if self.webpage.history().canGoBack(): self.btnPrev.setEnabled(True) else: self.btnPrev.setEnabled(False) if self.webpage.history().canGoForward(): self.btnNext.setEnabled(True) else: self.btnNext.setEnabled(False) self.urlBar.setText(self.webpage.url().toString()) def browse(self, refresh=False, offline=False): ''' Browse to urlBar URL or render a text/html string ''' # Offline mode, load plaintext html code if offline: self.webpage.setHtml(self.codeEdit.toPlainText()) return # Online mode if refresh: url = self.browser.reload() return url = self.urlBar.text() if not re.match('http://|https://', url, re.I): url = f'http://{url}' # print('===============>', os.path.join('statics/button', 'lock-ssl.png'), QUrl(url).scheme()) if QUrl(url).scheme() == 'https': print('HTTPS') self.httpsicon.setPixmap(self.pixmap_ssl) else: print('HTTP') self.httpsicon.setPixmap(self.pixmap_nossl) print(f'Browse to this URL: {url}') if isinstance(url, str) and url != '': self.webpage.setUrl(QUrl(url)) def selectFile(self, index): ''' Select a file in file system ''' if not self.fileModel.isDir(index) and index.data().lower().endswith( ('.html', '.txt')): with open(self.fileModel.fileInfo(index).absoluteFilePath(), 'r', encoding='utf-8') as f: self.webpage.setHtml(f.read()) else: print('Can not read this file...') def initUI(self): ''' Configuration of widgets ''' self.gLayoutBrowser.addWidget(self.browser) self.loadingAnimation = QMovie('loading.gif') self.loadingAnimation.setScaledSize(QtCore.QSize(24, 24)) self.labelLoading.setMovie(self.loadingAnimation) self.labelLoading.setVisible(False) self.fileModel = QFileSystemModel() self.fileModel.setRootPath(QtCore.QDir.currentPath()) self.fileView.setModel(self.fileModel) self.fileModel.setNameFilters(('.html', '.txt')) self.fileView.setColumnWidth(0, 170) self.fileView.setColumnWidth(1, 50) self.fileView.setColumnWidth(2, 50) self.splitterMain.setStretchFactor(1, 3) self.splitterSidebar.setStretchFactor(0, 1) self.urlBar.setPlaceholderText('Tapez ici votre URL') self.loadPage(online=True) self.pixmap_ssl = QPixmap(os.path.join('static/button', 'lock-ssl.png')) self.pixmap_nossl = QPixmap( os.path.join('static/button', 'lock-nossl.png')) print(f'w: {self.pixmap_ssl.width()} | h: {self.pixmap_ssl.height()}') self.httpsicon.setPixmap(self.pixmap_nossl) self.setGeometry(300, 300, 1280, 720) self.updateTitle() # self.codeEdit = TxtInput([QPushButton('Hello'), QPushButton('World')]) # self.editor = CustomTextEditor(txtInput=self.codeEdit) self.codeEdit = QPlainTextEdit() self.splitterSidebar.addWidget(self.codeEdit) self.show() def loadPage(self, online=True): ''' Load home page ''' if online: self.browser.setUrl(QUrl("https://www.google.com")) return with open('home.html', 'r') as f: html = f.read() self.browser.setHtml(html) def updateTitle(self): title = self.browser.page().title() self.setWindowTitle(f'{title}')
class Explorer(QWidget): def __init__(self, rootdir=QDir.rootPath()): QWidget.__init__(self) self.treeview = QTreeView() self.listview = QListView() self.path = rootdir self.filename = '' self.filepath = rootdir self.canvas = None self.col_selector = None self.header = '' self.xcol = [1] self.ycol = [1] self.ncols = 0 self.dirModel = QFileSystemModel() self.dirModel.setRootPath(self.path) self.dirModel.setFilter(QDir.NoDotAndDotDot | QDir.AllDirs) self.fileModel = QFileSystemModel() self.fileModel.setRootPath(self.filepath) self.fileModel.setFilter(QDir.NoDotAndDotDot | QDir.Files) self.fileModel.setNameFilters(['*.txt']) self.fileModel.setNameFilterDisables(0) self.treeview.setModel(self.dirModel) self.listview.setModel(self.fileModel) for i in [1, 2, 3]: self.treeview.setColumnHidden(i, True) self.treeview.setHeaderHidden(True) self.treeview.setRootIndex(self.dirModel.index(self.path)) self.listview.setRootIndex(self.fileModel.index(self.path)) self.treeview.clicked.connect(self.on_clicked) self.listview.selectionModel().currentChanged.connect(self.file_selected) self.listview.selectionModel().currentChanged.connect(lambda: self.canvas.update_plot(self)) self.listview.selectionModel().currentChanged.connect(lambda: self.col_selector.update_range(self.ncols)) def on_clicked(self, index): self.path = self.dirModel.fileInfo(index).absoluteFilePath() self.listview.setRootIndex(self.fileModel.setRootPath(self.path)) def file_selected(self, index): self.filename = self.fileModel.fileName(index) self.filepath = self.fileModel.filePath(index) self.load_file() def load_file(self): try: if self.filepath.endswith('.txt'): with open(self.filepath, 'r') as file: self.header, self.xcol, self.ycol = '', [], [] for ln in file: if ln.startswith('#'): self.header += ln[2:] else: cols = ln.split('\t') self.xcol.append(float(cols[0])) self.ycol.append(float(cols[self.col_selector.sp.value()])) self.ncols = len(cols) self.col_selector.update_range(self.ncols) except: self.header, self.xcol, self.ycol = '', [0], [0] def update_rootdir(self, rootdir): self.path = rootdir self.treeview.setRootIndex(self.dirModel.index(self.path)) self.listview.setRootIndex(self.fileModel.index(self.path))
class Broswer_Img(QMainWindow): """ 选择图片文件并且预览,自动匹配多图的情况和背景图片 """ Close_Signal = pyqtSignal(list) def __init__(self, *args, **kwargs): QMainWindow.__init__(self, *args, **kwargs) self.Current_Dir = QDir.home().absolutePath() #self.Current_Dir = Wk_Dir self.setWindowTitle("Select Imags") self.setWindowModality(Qt.ApplicationModal) self.Left_Dock_Code() self.Central_Frame_Code() self.Right_Dock_Code() self.connect_Signals() self.wb_nav_left.setEnabled(False) self.wb_nav_right.setEnabled(False) self.bkgd_nav_left.setEnabled(False) self.bkgd_nav_right.setEnabled(False) #self.setGeometry(200, 200, 1000, 600) #self.setMaximumSize(QSize(1000, 600)) def Left_Dock_Code(self): self.Left_Frame = QFrame(self) self.Model = QFileSystemModel() self.Model.setNameFilterDisables(False) self.Model.setRootPath(self.Current_Dir) #self.Model.setSorting(QDir.DirsFirst | QDir.IgnoreCase | QDir.Name) self.Model.setFilter(QDir.NoDotAndDotDot | QDir.AllDirs | QDir.AllEntries) self.Model.setNameFilters(['*.tif']) self.Tree = QTreeView(self.Left_Frame) self.Tree.setModel(self.Model) self.Tree.setRootIndex(self.Model.index(self.Current_Dir)) self.Tree.expandAll() self.Dir_Select = QPushButton("Select a Folder", self.Left_Frame) layout = QVBoxLayout() layout.addWidget(self.Tree) layout.addWidget(self.Dir_Select) self.Left_Frame.setLayout(layout) self.Left_Dock = QDockWidget('Broswer Images', self) self.Left_Dock.setWidget(self.Left_Frame) self.Left_Dock.setFeatures(QDockWidget.NoDockWidgetFeatures) self.Dir_Select.clicked.connect(self.dir_selection) self.addDockWidget(Qt.LeftDockWidgetArea, self.Left_Dock) def Central_Frame_Code(self): self.Central_Frame = QFrame(self) layout = QGridLayout() self.wb_label = QLabel(self.Central_Frame) self.bkgd_label = QLabel(self.Central_Frame) #self.wb_label.setMaximumHeight(30) self.wb_label.setWordWrap(True) self.wb = QLabel(self.Central_Frame) self.wb.setScaledContents(True) self.bkgd = QLabel(self.Central_Frame) self.bkgd.setScaledContents(True) self.wb.setMaximumSize(QSize(300, 300)) self.bkgd.setMaximumSize(QSize(300, 300)) self.wb_navigator = QFrame(self.Central_Frame) self.wb_nav_left = QPushButton('<--', self.wb_navigator) self.wb_nav_right = QPushButton('-->', self.wb_navigator) nav_layout = QHBoxLayout() nav_layout.addWidget(self.wb_nav_left) nav_layout.addWidget(self.wb_nav_right) self.wb_navigator.setLayout(nav_layout) self.wb_navigator.setMaximumHeight(60) self.bkgd_navigator = QFrame(self.Central_Frame) self.bkgd_nav_left = QPushButton('<--', self.bkgd_navigator) self.bkgd_nav_right = QPushButton('-->', self.bkgd_navigator) nav_layout2 = QHBoxLayout() nav_layout2.addWidget(self.bkgd_nav_left) nav_layout2.addWidget(self.bkgd_nav_right) self.bkgd_navigator.setLayout(nav_layout2) self.bkgd_navigator.setMaximumHeight(60) self.btns = QFrame(self.Central_Frame) self.btns.setMaximumHeight(60) self.Btn_Add = QPushButton('Add', self.btns) self.Btn_Close = QPushButton('Close', self.btns) btn_layout = QHBoxLayout() btn_layout.addWidget(self.Btn_Add) btn_layout.addWidget(self.Btn_Close) self.btns.setLayout(btn_layout) # 根据具体的传入参数构建不同的视图 layout.addWidget(self.wb_label, 0, 0) layout.addWidget(self.bkgd_label, 0, 1) layout.addWidget(self.wb, 1, 0) layout.addWidget(self.bkgd, 1, 1) layout.addWidget(self.wb_navigator, 2, 0) layout.addWidget(self.bkgd_navigator, 2, 1) layout.addWidget(self.btns, 3, 0, 2, 0) layouts = QVBoxLayout() layouts.addLayout(layout) self.Central_Frame.setLayout(layouts) self.setCentralWidget(self.Central_Frame) #self.setStyleSheet('border:1px solid red') def Right_Dock_Code(self): self.Added_Img_tree = Img_Tree([], self) self.Right_Dock = QDockWidget('Selected Images', self) self.Right_Dock.setWidget(self.Added_Img_tree) self.Right_Dock.setFeatures(QDockWidget.NoDockWidgetFeatures) self.addDockWidget(Qt.RightDockWidgetArea, self.Right_Dock) def connect_Signals(self): self.Tree.clicked.connect(self.Load_Img_to_Central_Frame) self.wb_nav_left.clicked.connect(self.change_img_index) self.wb_nav_right.clicked.connect(self.change_img_index) self.bkgd_nav_left.clicked.connect(self.change_img_index) self.bkgd_nav_right.clicked.connect(self.change_img_index) self.Btn_Add.clicked.connect(self.Add_Btn_Action) self.Btn_Close.clicked.connect(self.Close_Btn_Action) def Load_Img_to_Central_Frame(self, Index): select_file = self.Tree.model().filePath(Index) _, ext = os.path.splitext(select_file) if ext in ['.tif', '.jpeg', '.png', '.jpg']: self.Related_Imgs = BioRad_Imgs(select_file) self.set_Central_Frame() def set_Central_Frame(self): self.wb_nav_left.setEnabled(False) self.wb_nav_right.setEnabled(False) self.bkgd_nav_left.setEnabled(False) self.bkgd_nav_right.setEnabled(False) self.current_wb = self.Related_Imgs.WB_list[self.Related_Imgs.wb_index] self.current_bkgd = self.Related_Imgs.BKGD_list[ self.Related_Imgs.bkgd_index] self.wb_label.setText( self.current_wb.replace(self.Related_Imgs.Dir, '.')) self.bkgd_label.setText( self.current_bkgd.replace(self.Related_Imgs.Dir, '.')) wb, _ = CV_Img_to_QImage(cv2.imread(self.current_wb)) bkgd, _ = CV_Img_to_QImage(cv2.imread(self.current_bkgd)) self.wb.setPixmap(wb) self.wb.setScaledContents(True) self.bkgd.setPixmap(bkgd) wb_len = len(self.Related_Imgs.WB_list) bkgd_len = len(self.Related_Imgs.BKGD_list) if self.Related_Imgs.wb_index > 0: self.wb_nav_left.setEnabled(True) if wb_len - self.Related_Imgs.wb_index > 1: self.wb_nav_right.setEnabled(True) if self.Related_Imgs.bkgd_index > 0: self.bkgd_nav_left.setEnabled(True) if bkgd_len - self.Related_Imgs.bkgd_index > 1: self.bkgd_nav_right.setEnabled(True) def change_img_index(self): sender = self.sender() if sender == self.wb_nav_left: self.Related_Imgs.wb_index = self.Related_Imgs.wb_index - 1 if sender == self.wb_nav_right: self.Related_Imgs.wb_index = self.Related_Imgs.wb_index + 1 if sender == self.bkgd_nav_left: self.Related_Imgs.bkgd_index = self.Related_Imgs.bkgd_index - 1 if sender == self.bkgd_nav_right: self.Related_Imgs.bkgd_index = self.Related_Imgs.bkgd_index + 1 self.set_Central_Frame() def Add_Btn_Action(self): list = [{'wb': self.current_wb, 'bkgd': self.current_bkgd}] self.Added_Img_tree.Add_top_Level_Item(list) print(self.Added_Img_tree.imgs) def Close_Btn_Action(self): self.close() def closeEvent(self, event): self.Close_Signal.emit(self.Added_Img_tree.imgs) def dir_selection(self): global Wk_Dir dir = QFileDialog.getExistingDirectory(self, "Choose a Directory", Wk_Dir) self.Current_Dir = dir Wk_Dir = dir self.Tree.setRootIndex(self.Model.index(self.Current_Dir)) self.Left_Dock.setWindowTitle(dir)
class Script(QWidget, form_class): def __init__(self, parent=None): super(Script, self).__init__(parent) self.setupUi(self) # private variable self.__currentpath = "" self.__modfiedstate = False self._PyQtSignalConnect = console.PyQtSignalConnect() # treeView Size self.splitter.setSizes([ Config().getint('SIZE', 'QtScriptFileExplorer'), (self.size().width()) - Config().getint('SIZE', 'QtScriptFileExplorer') ]) # treeView model create self.model = QFileSystemModel() self.model.setNameFilters(["*.py"]) # treeView setting self.treeView.setModel(self.model) self.treeView.setRootIndex(self.model.setRootPath('./')) self.treeView.setAnimated(True) self.treeView.setSortingEnabled(False) [self.treeView.hideColumn(ii) for ii in range(1, 5)] self.treeView.header().setStretchLastSection(False) self.treeView.header().setSectionResizeMode(0, QHeaderView.Stretch) # signal connect self.treeView.activated.connect(lambda e : \ self._open_script(self.model.filePath(e))) self.btnSave.clicked.connect(lambda: self._save_script()) self.btnNew.clicked.connect(lambda: self._new_script()) self.btnRun.clicked.connect(lambda: self._run_script()) self.btnOpen.clicked.connect(lambda: self._btnOpen_clicked()) self.fontComboBox.currentFontChanged.connect( lambda x: self.plainTextEdit.setFont(x)) self.plainTextEdit.keyPressEvent = self._plainTextEdit_keyPressEvent self.plainTextEdit.wheelEvent = self._plainTextEdit_wheelEvent self.plainTextEdit.setAcceptDrops(True) self.plainTextEdit.dropEvent = lambda e: self._open_script(e.mimeData( ).urls()[0].toLocalFile()) self.plainTextEdit.modificationChanged.connect( lambda x: self._change_modified_state(x)) self._PyQtSignalConnect.script_run.connect( lambda: self.btnRun.animateClick()) def getstate(self): return self.__modfiedstate def _change_modified_state(self, param): self.__modfiedstate = param def _btnOpen_clicked(self): options = QFileDialog.Options() fileName, _ = QFileDialog.getOpenFileName( self, "QFileDialog.getOpenFileName()", "", "All Files (*);;Python Files (*.py)", options=options) if (os.path.isfile(fileName) == True): self._open_script(fileName) else: pass @ismodified def _new_script(self): self.plainTextEdit.clear() self._change_modified_state(False) def _save_script(self): options = QFileDialog.Options() fileName, _ = QFileDialog.getSaveFileName( self, "QFileDialog.getSaveFileName()", self.__currentpath, "Python Files (*.py)", options=options) if fileName == "": pass else: with open(fileName, "w") as f: f.write(self.plainTextEdit.toPlainText()) self.__currentpath = fileName # when re save occurs, it will be used in default path self._change_modified_state(False) return fileName @ismodified def _open_script(self, path): try: with open(path) as f: self.__currentpath = path self.plainTextEdit.clear() self.plainTextEdit.setPlainText(f.read()) except UnicodeDecodeError as e: pass except PermissionError as e: pass def _change_root(self): text = self.lineEdit.displayText() self.treeView.setRootIndex(self.model.setRootPath(text)) self.lineEdit.clear() def _plainTextEdit_keyPressEvent(self, event): # this is monkey....... # how to change tab to 4 space ? if (event.key() == Qt.Key_Tab): self.plainTextEdit.insertPlainText(" " * 4) else: QPlainTextEdit.keyPressEvent(self.plainTextEdit, event) def _plainTextEdit_wheelEvent(self, event): if (event.modifiers() & Qt.ControlModifier): self._plainTextEdit_zoom(event.angleDelta().y()) else: QPlainTextEdit.wheelEvent(self.plainTextEdit, event) def _plainTextEdit_zoom(self, delta): if delta < 0: self.plainTextEdit.zoomOut(2) elif delta > 0: self.plainTextEdit.zoomIn(2) def _run_script(self): self.btnRun.setEnabled(False) console.cexec(self.plainTextEdit.toPlainText(), isfile=True) self.btnRun.setEnabled(True)
class ReTextWindow(QMainWindow): def __init__(self, parent=None): QMainWindow.__init__(self, parent) self.resize(950, 700) screenRect = QDesktopWidget().screenGeometry() if globalSettings.windowGeometry: self.restoreGeometry(globalSettings.windowGeometry) else: self.move((screenRect.width() - self.width()) // 2, (screenRect.height() - self.height()) // 2) if not screenRect.contains(self.geometry()): self.showMaximized() if sys.platform.startswith('darwin'): # https://github.com/retext-project/retext/issues/198 searchPaths = QIcon.themeSearchPaths() searchPaths.append('/opt/local/share/icons') searchPaths.append('/usr/local/share/icons') QIcon.setThemeSearchPaths(searchPaths) setIconThemeFromSettings() if QFile.exists(getBundledIcon('retext')): self.setWindowIcon(QIcon(getBundledIcon('retext'))) elif QFile.exists('/usr/share/pixmaps/retext.png'): self.setWindowIcon(QIcon('/usr/share/pixmaps/retext.png')) else: self.setWindowIcon(QIcon.fromTheme('retext', QIcon.fromTheme('accessories-text-editor'))) self.splitter = QSplitter(self) self.treeView = QTreeView(self.splitter) self.treeView.doubleClicked.connect(self.treeItemSelected) self.tabWidget = QTabWidget(self.splitter) self.initTabWidget() self.splitter.setSizes([self.width() / 5, self.width() * 4 / 5]) self.initDirectoryTree(globalSettings.showDirectoryTree, globalSettings.directoryPath) self.setCentralWidget(self.splitter) self.tabWidget.currentChanged.connect(self.changeIndex) self.tabWidget.tabCloseRequested.connect(self.closeTab) self.toolBar = QToolBar(self.tr('File toolbar'), self) self.addToolBar(Qt.TopToolBarArea, self.toolBar) self.editBar = QToolBar(self.tr('Edit toolbar'), self) self.addToolBar(Qt.TopToolBarArea, self.editBar) self.searchBar = QToolBar(self.tr('Search toolbar'), self) self.addToolBar(Qt.BottomToolBarArea, self.searchBar) self.toolBar.setVisible(not globalSettings.hideToolBar) self.editBar.setVisible(not globalSettings.hideToolBar) self.actionNew = self.act(self.tr('New'), 'document-new', self.createNew, shct=QKeySequence.New) self.actionNew.setPriority(QAction.LowPriority) self.actionOpen = self.act(self.tr('Open'), 'document-open', self.openFile, shct=QKeySequence.Open) self.actionOpen.setPriority(QAction.LowPriority) self.actionSetEncoding = self.act(self.tr('Set encoding'), trig=self.showEncodingDialog) self.actionSetEncoding.setEnabled(False) self.actionReload = self.act(self.tr('Reload'), 'view-refresh', lambda: self.currentTab.readTextFromFile()) self.actionReload.setEnabled(False) self.actionSave = self.act(self.tr('Save'), 'document-save', self.saveFile, shct=QKeySequence.Save) self.actionSave.setEnabled(False) self.actionSave.setPriority(QAction.LowPriority) self.actionSaveAs = self.act(self.tr('Save as'), 'document-save-as', self.saveFileAs, shct=QKeySequence.SaveAs) self.actionNextTab = self.act(self.tr('Next tab'), 'go-next', lambda: self.switchTab(1), shct=Qt.CTRL+Qt.Key_PageDown) self.actionPrevTab = self.act(self.tr('Previous tab'), 'go-previous', lambda: self.switchTab(-1), shct=Qt.CTRL+Qt.Key_PageUp) self.actionCloseCurrentTab = self.act(self.tr('Close tab'), 'window-close', lambda: self.closeTab(self.ind), shct=QKeySequence.Close) self.actionPrint = self.act(self.tr('Print'), 'document-print', self.printFile, shct=QKeySequence.Print) self.actionPrint.setPriority(QAction.LowPriority) self.actionPrintPreview = self.act(self.tr('Print preview'), 'document-print-preview', self.printPreview) self.actionViewHtml = self.act(self.tr('View HTML code'), 'text-html', self.viewHtml) self.actionChangeEditorFont = self.act(self.tr('Change editor font'), trig=self.changeEditorFont) self.actionChangePreviewFont = self.act(self.tr('Change preview font'), trig=self.changePreviewFont) self.actionSearch = self.act(self.tr('Find text'), 'edit-find', self.search, shct=QKeySequence.Find) self.actionGoToLine = self.act(self.tr('Go to line'), trig=self.goToLine, shct=Qt.CTRL+Qt.Key_G) self.searchBar.visibilityChanged.connect(self.searchBarVisibilityChanged) self.actionPreview = self.act(self.tr('Preview'), shct=Qt.CTRL+Qt.Key_E, trigbool=self.preview) if QIcon.hasThemeIcon('document-preview'): self.actionPreview.setIcon(QIcon.fromTheme('document-preview')) elif QIcon.hasThemeIcon('preview-file'): self.actionPreview.setIcon(QIcon.fromTheme('preview-file')) elif QIcon.hasThemeIcon('x-office-document'): self.actionPreview.setIcon(QIcon.fromTheme('x-office-document')) else: self.actionPreview.setIcon(QIcon(getBundledIcon('document-preview'))) self.actionLivePreview = self.act(self.tr('Live preview'), shct=Qt.CTRL+Qt.Key_L, trigbool=self.enableLivePreview) menuPreview = QMenu() menuPreview.addAction(self.actionLivePreview) self.actionPreview.setMenu(menuPreview) self.actionInsertTable = self.act(self.tr('Insert table'), trig=lambda: self.insertFormatting('table')) self.actionTableMode = self.act(self.tr('Table editing mode'), shct=Qt.CTRL+Qt.Key_T, trigbool=lambda x: self.currentTab.editBox.enableTableMode(x)) self.actionInsertImages = self.act(self.tr('Insert images by file path'), trig=lambda: self.insertImages()) if ReTextFakeVimHandler: self.actionFakeVimMode = self.act(self.tr('FakeVim mode'), shct=Qt.CTRL+Qt.ALT+Qt.Key_V, trigbool=self.enableFakeVimMode) if globalSettings.useFakeVim: self.actionFakeVimMode.setChecked(True) self.enableFakeVimMode(True) self.actionFullScreen = self.act(self.tr('Fullscreen mode'), 'view-fullscreen', shct=Qt.Key_F11, trigbool=self.enableFullScreen) self.actionFullScreen.setChecked(self.isFullScreen()) self.actionFullScreen.setPriority(QAction.LowPriority) self.actionConfig = self.act(self.tr('Preferences'), icon='preferences-system', trig=self.openConfigDialog) self.actionConfig.setMenuRole(QAction.PreferencesRole) self.actionSaveHtml = self.act('HTML', 'text-html', self.saveFileHtml) self.actionPdf = self.act('PDF', 'application-pdf', self.savePdf) self.actionOdf = self.act('ODT', 'x-office-document', self.saveOdf) self.getExportExtensionsList() self.actionQuit = self.act(self.tr('Quit'), 'application-exit', shct=QKeySequence.Quit) self.actionQuit.setMenuRole(QAction.QuitRole) self.actionQuit.triggered.connect(self.close) self.actionUndo = self.act(self.tr('Undo'), 'edit-undo', lambda: self.currentTab.editBox.undo(), shct=QKeySequence.Undo) self.actionRedo = self.act(self.tr('Redo'), 'edit-redo', lambda: self.currentTab.editBox.redo(), shct=QKeySequence.Redo) self.actionCopy = self.act(self.tr('Copy'), 'edit-copy', lambda: self.currentTab.editBox.copy(), shct=QKeySequence.Copy) self.actionCut = self.act(self.tr('Cut'), 'edit-cut', lambda: self.currentTab.editBox.cut(), shct=QKeySequence.Cut) self.actionPaste = self.act(self.tr('Paste'), 'edit-paste', lambda: self.currentTab.editBox.paste(), shct=QKeySequence.Paste) self.actionPasteImage = self.act(self.tr('Paste image'), 'edit-paste', lambda: self.currentTab.editBox.pasteImage(), shct=Qt.CTRL+Qt.SHIFT+Qt.Key_V) self.actionMoveUp = self.act(self.tr('Move line up'), 'go-up', lambda: self.currentTab.editBox.moveLineUp(), shct=Qt.ALT+Qt.Key_Up) self.actionMoveDown = self.act(self.tr('Move line down'), 'go-down', lambda: self.currentTab.editBox.moveLineDown(), shct=Qt.ALT+Qt.Key_Down) self.actionUndo.setEnabled(False) self.actionRedo.setEnabled(False) self.actionCopy.setEnabled(False) self.actionCut.setEnabled(False) qApp = QApplication.instance() qApp.clipboard().dataChanged.connect(self.clipboardDataChanged) self.clipboardDataChanged() if enchant is not None: self.actionEnableSC = self.act(self.tr('Enable'), trigbool=self.enableSpellCheck) self.actionSetLocale = self.act(self.tr('Set locale'), trig=self.changeLocale) self.actionWebKit = self.act(self.tr('Use WebKit renderer'), trigbool=self.enableWebKit) if ReTextWebKitPreview is None: globalSettings.useWebKit = False self.actionWebKit.setEnabled(False) self.actionWebKit.setChecked(globalSettings.useWebKit) self.actionWebEngine = self.act(self.tr('Use WebEngine (Chromium) renderer'), trigbool=self.enableWebEngine) if ReTextWebEnginePreview is None: globalSettings.useWebEngine = False self.actionWebEngine.setChecked(globalSettings.useWebEngine) self.actionShow = self.act(self.tr('Show directory'), 'system-file-manager', self.showInDir) self.actionFind = self.act(self.tr('Next'), 'go-next', self.find, shct=QKeySequence.FindNext) self.actionFindPrev = self.act(self.tr('Previous'), 'go-previous', lambda: self.find(back=True), shct=QKeySequence.FindPrevious) self.actionReplace = self.act(self.tr('Replace'), 'edit-find-replace', lambda: self.find(replace=True)) self.actionReplaceAll = self.act(self.tr('Replace all'), trig=self.replaceAll) menuReplace = QMenu() menuReplace.addAction(self.actionReplaceAll) self.actionReplace.setMenu(menuReplace) self.actionCloseSearch = self.act(self.tr('Close'), 'window-close', lambda: self.searchBar.setVisible(False), shct=QKeySequence.Cancel) self.actionCloseSearch.setPriority(QAction.LowPriority) self.actionHelp = self.act(self.tr('Get help online'), 'help-contents', self.openHelp) self.aboutWindowTitle = self.tr('About ReText') self.actionAbout = self.act(self.aboutWindowTitle, 'help-about', self.aboutDialog) self.actionAbout.setMenuRole(QAction.AboutRole) self.actionAboutQt = self.act(self.tr('About Qt')) self.actionAboutQt.setMenuRole(QAction.AboutQtRole) self.actionAboutQt.triggered.connect(qApp.aboutQt) availableMarkups = markups.get_available_markups() if not availableMarkups: print('Warning: no markups are available!') if len(availableMarkups) > 1: self.chooseGroup = QActionGroup(self) markupActions = [] for markup in availableMarkups: markupAction = self.act(markup.name, trigbool=self.markupFunction(markup)) if markup.name == globalSettings.defaultMarkup: markupAction.setChecked(True) self.chooseGroup.addAction(markupAction) markupActions.append(markupAction) self.actionBold = self.act(self.tr('Bold'), shct=QKeySequence.Bold, trig=lambda: self.insertFormatting('bold')) self.actionItalic = self.act(self.tr('Italic'), shct=QKeySequence.Italic, trig=lambda: self.insertFormatting('italic')) self.actionUnderline = self.act(self.tr('Underline'), shct=QKeySequence.Underline, trig=lambda: self.insertFormatting('underline')) self.usefulTags = ('header', 'italic', 'bold', 'underline', 'numbering', 'bullets', 'image', 'link', 'inline code', 'code block', 'blockquote', 'table') self.usefulChars = ('deg', 'divide', 'euro', 'hellip', 'laquo', 'larr', 'lsquo', 'mdash', 'middot', 'minus', 'nbsp', 'ndash', 'raquo', 'rarr', 'rsquo', 'times') self.formattingBox = QComboBox(self.editBar) self.formattingBox.addItem(self.tr('Formatting')) self.formattingBox.addItems(self.usefulTags) self.formattingBox.activated[str].connect(self.insertFormatting) self.symbolBox = QComboBox(self.editBar) self.symbolBox.addItem(self.tr('Symbols')) self.symbolBox.addItems(self.usefulChars) self.symbolBox.activated.connect(self.insertSymbol) self.updateStyleSheet() menubar = self.menuBar() menuFile = menubar.addMenu(self.tr('&File')) menuEdit = menubar.addMenu(self.tr('&Edit')) menuHelp = menubar.addMenu(self.tr('&Help')) menuFile.addAction(self.actionNew) menuFile.addAction(self.actionOpen) self.menuRecentFiles = menuFile.addMenu(self.tr('Open recent')) self.menuRecentFiles.aboutToShow.connect(self.updateRecentFiles) menuFile.addAction(self.actionShow) menuFile.addAction(self.actionSetEncoding) menuFile.addAction(self.actionReload) menuFile.addSeparator() menuFile.addAction(self.actionSave) menuFile.addAction(self.actionSaveAs) menuFile.addSeparator() menuFile.addAction(self.actionNextTab) menuFile.addAction(self.actionPrevTab) menuFile.addAction(self.actionCloseCurrentTab) menuFile.addSeparator() menuExport = menuFile.addMenu(self.tr('Export')) menuExport.addAction(self.actionSaveHtml) menuExport.addAction(self.actionOdf) menuExport.addAction(self.actionPdf) if self.extensionActions: menuExport.addSeparator() for action, mimetype in self.extensionActions: menuExport.addAction(action) menuExport.aboutToShow.connect(self.updateExtensionsVisibility) menuFile.addAction(self.actionPrint) menuFile.addAction(self.actionPrintPreview) menuFile.addSeparator() menuFile.addAction(self.actionQuit) menuEdit.addAction(self.actionUndo) menuEdit.addAction(self.actionRedo) menuEdit.addSeparator() menuEdit.addAction(self.actionCut) menuEdit.addAction(self.actionCopy) menuEdit.addAction(self.actionPaste) menuEdit.addAction(self.actionPasteImage) menuEdit.addSeparator() menuEdit.addAction(self.actionMoveUp) menuEdit.addAction(self.actionMoveDown) menuEdit.addSeparator() if enchant is not None: menuSC = menuEdit.addMenu(self.tr('Spell check')) menuSC.addAction(self.actionEnableSC) menuSC.addAction(self.actionSetLocale) menuEdit.addAction(self.actionSearch) menuEdit.addAction(self.actionGoToLine) menuEdit.addAction(self.actionChangeEditorFont) menuEdit.addAction(self.actionChangePreviewFont) menuEdit.addSeparator() if len(availableMarkups) > 1: self.menuMode = menuEdit.addMenu(self.tr('Default markup')) for markupAction in markupActions: self.menuMode.addAction(markupAction) menuFormat = menuEdit.addMenu(self.tr('Formatting')) menuFormat.addAction(self.actionBold) menuFormat.addAction(self.actionItalic) menuFormat.addAction(self.actionUnderline) if ReTextWebKitPreview is not None or ReTextWebEnginePreview is None: menuEdit.addAction(self.actionWebKit) else: menuEdit.addAction(self.actionWebEngine) menuEdit.addSeparator() menuEdit.addAction(self.actionViewHtml) menuEdit.addAction(self.actionPreview) menuEdit.addAction(self.actionInsertTable) menuEdit.addAction(self.actionTableMode) menuEdit.addAction(self.actionInsertImages) if ReTextFakeVimHandler: menuEdit.addAction(self.actionFakeVimMode) menuEdit.addSeparator() menuEdit.addAction(self.actionFullScreen) menuEdit.addAction(self.actionConfig) menuHelp.addAction(self.actionHelp) menuHelp.addSeparator() menuHelp.addAction(self.actionAbout) menuHelp.addAction(self.actionAboutQt) self.toolBar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) self.toolBar.addAction(self.actionNew) self.toolBar.addSeparator() self.toolBar.addAction(self.actionOpen) self.toolBar.addAction(self.actionSave) self.toolBar.addAction(self.actionPrint) self.toolBar.addSeparator() self.toolBar.addAction(self.actionPreview) self.toolBar.addAction(self.actionFullScreen) self.editBar.addAction(self.actionUndo) self.editBar.addAction(self.actionRedo) self.editBar.addSeparator() self.editBar.addAction(self.actionCut) self.editBar.addAction(self.actionCopy) self.editBar.addAction(self.actionPaste) self.editBar.addSeparator() self.editBar.addWidget(self.formattingBox) self.editBar.addWidget(self.symbolBox) self.searchEdit = QLineEdit(self.searchBar) self.searchEdit.setPlaceholderText(self.tr('Search')) self.searchEdit.returnPressed.connect(self.find) self.replaceEdit = QLineEdit(self.searchBar) self.replaceEdit.setPlaceholderText(self.tr('Replace with')) self.replaceEdit.returnPressed.connect(self.find) self.csBox = QCheckBox(self.tr('Case sensitively'), self.searchBar) self.searchBar.addWidget(self.searchEdit) self.searchBar.addWidget(self.replaceEdit) self.searchBar.addSeparator() self.searchBar.addWidget(self.csBox) self.searchBar.addAction(self.actionFindPrev) self.searchBar.addAction(self.actionFind) self.searchBar.addAction(self.actionReplace) self.searchBar.addAction(self.actionCloseSearch) self.searchBar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) self.searchBar.setVisible(False) self.autoSaveEnabled = globalSettings.autoSave if self.autoSaveEnabled: timer = QTimer(self) timer.start(60000) timer.timeout.connect(self.saveAll) self.ind = None if enchant is not None: self.sl = globalSettings.spellCheckLocale try: enchant.Dict(self.sl or None) except enchant.errors.Error as e: warnings.warn(str(e), RuntimeWarning) globalSettings.spellCheck = False if globalSettings.spellCheck: self.actionEnableSC.setChecked(True) self.fileSystemWatcher = QFileSystemWatcher() self.fileSystemWatcher.fileChanged.connect(self.fileChanged) def restoreLastOpenedFiles(self): for file in readListFromSettings("lastFileList"): self.openFileWrapper(file) # Show the tab of last opened file lastTabIndex = globalSettings.lastTabIndex if lastTabIndex >= 0 and lastTabIndex < self.tabWidget.count(): self.tabWidget.setCurrentIndex(lastTabIndex) def iterateTabs(self): for i in range(self.tabWidget.count()): yield self.tabWidget.widget(i) def updateStyleSheet(self): self.ss = None if globalSettings.styleSheet: sheetfile = QFile(globalSettings.styleSheet) sheetfile.open(QIODevice.ReadOnly) self.ss = QTextStream(sheetfile).readAll() sheetfile.close() def initTabWidget(self): def dragEnterEvent(e): e.acceptProposedAction() def dropEvent(e): fn = bytes(e.mimeData().data('text/plain')).decode().rstrip() if fn.startswith('file:'): fn = QUrl(fn).toLocalFile() self.openFileWrapper(fn) self.tabWidget.setTabsClosable(True) self.tabWidget.setAcceptDrops(True) self.tabWidget.setMovable(True) self.tabWidget.dragEnterEvent = dragEnterEvent self.tabWidget.dropEvent = dropEvent self.tabWidget.setTabBarAutoHide(globalSettings.tabBarAutoHide) def initDirectoryTree(self, visible, path): if visible: self.fileSystemModel = QFileSystemModel(self.treeView) self.fileSystemModel.setRootPath(path) supportedExtensions = ['.txt'] for markup in markups.get_all_markups(): supportedExtensions += markup.file_extensions filters = ["*" + s for s in supportedExtensions] self.fileSystemModel.setNameFilters(filters) self.fileSystemModel.setNameFilterDisables(False) self.treeView.setModel(self.fileSystemModel) self.treeView.setRootIndex(self.fileSystemModel.index(path)) self.treeView.setColumnHidden(1, True) self.treeView.setColumnHidden(2, True) self.treeView.setColumnHidden(3, True) self.treeView.setHeaderHidden(True) self.treeView.setVisible(visible) def treeItemSelected(self, signal): file_path = self.fileSystemModel.filePath(signal) if os.path.isdir(file_path): return self.openFileWrapper(file_path) def act(self, name, icon=None, trig=None, trigbool=None, shct=None): if not isinstance(shct, QKeySequence): shct = QKeySequence(shct) if icon: action = QAction(self.actIcon(icon), name, self) else: action = QAction(name, self) if trig: action.triggered.connect(trig) elif trigbool: action.setCheckable(True) action.triggered[bool].connect(trigbool) if shct: action.setShortcut(shct) return action def actIcon(self, name): return QIcon.fromTheme(name, QIcon(getBundledIcon(name))) def printError(self): import traceback print('Exception occurred while parsing document:', file=sys.stderr) traceback.print_exc() def updateTabTitle(self, ind, tab): changed = tab.editBox.document().isModified() if changed and not self.autoSaveActive(tab): title = tab.getBaseName() + '*' else: title = tab.getBaseName() self.tabWidget.setTabText(ind, title) def tabFileNameChanged(self, tab): ''' Perform all UI state changes that need to be done when the filename of the current tab has changed. ''' if tab == self.currentTab: if tab.fileName: self.setWindowTitle("") if globalSettings.windowTitleFullPath: self.setWindowTitle(tab.fileName + '[*]') self.setWindowFilePath(tab.fileName) self.updateTabTitle(self.ind, tab) self.tabWidget.setTabToolTip(self.ind, tab.fileName) QDir.setCurrent(QFileInfo(tab.fileName).dir().path()) else: self.setWindowFilePath('') self.setWindowTitle(self.tr('New document') + '[*]') canReload = bool(tab.fileName) and not self.autoSaveActive(tab) self.actionSetEncoding.setEnabled(canReload) self.actionReload.setEnabled(canReload) def tabActiveMarkupChanged(self, tab): ''' Perform all UI state changes that need to be done when the active markup class of the current tab has changed. ''' if tab == self.currentTab: markupClass = tab.getActiveMarkupClass() dtMarkdown = (markupClass == markups.MarkdownMarkup) dtMkdOrReST = dtMarkdown or (markupClass == markups.ReStructuredTextMarkup) self.formattingBox.setEnabled(dtMarkdown) self.symbolBox.setEnabled(dtMarkdown) self.actionUnderline.setEnabled(dtMarkdown) self.actionBold.setEnabled(dtMkdOrReST) self.actionItalic.setEnabled(dtMkdOrReST) def tabModificationStateChanged(self, tab): ''' Perform all UI state changes that need to be done when the modification state of the current tab has changed. ''' if tab == self.currentTab: changed = tab.editBox.document().isModified() if self.autoSaveActive(tab): changed = False self.actionSave.setEnabled(changed) self.updateTabTitle(self.ind, tab) self.setWindowModified(changed) def createTab(self, fileName): previewStatesByName = { 'editor': PreviewDisabled, 'normal-preview': PreviewNormal, 'live-preview': PreviewLive, } previewState = previewStatesByName.get(globalSettings.defaultPreviewState, PreviewDisabled) if previewState == PreviewNormal and not fileName: previewState = PreviewDisabled # Opening empty document in preview mode makes no sense self.currentTab = ReTextTab(self, fileName, previewState) self.currentTab.fileNameChanged.connect(lambda: self.tabFileNameChanged(self.currentTab)) self.currentTab.modificationStateChanged.connect(lambda: self.tabModificationStateChanged(self.currentTab)) self.currentTab.activeMarkupChanged.connect(lambda: self.tabActiveMarkupChanged(self.currentTab)) self.tabWidget.addTab(self.currentTab, self.tr("New document")) self.currentTab.updateBoxesVisibility() if previewState > 0: QTimer.singleShot(500, self.currentTab.triggerPreviewUpdate) def closeTab(self, ind): if self.maybeSave(ind): if self.tabWidget.count() == 1: self.createTab("") closedTab = self.tabWidget.widget(ind) if closedTab.fileName: self.fileSystemWatcher.removePath(closedTab.fileName) self.tabWidget.removeTab(ind) closedTab.deleteLater() def changeIndex(self, ind): ''' This function is called when a different tab is selected. It changes the state of the window to mirror the current state of the newly selected tab. Future changes to this state will be done in response to signals emitted by the tab, to which the window was subscribed when the tab was created. The window is subscribed to all tabs like this, but only the active tab will logically generate these signals. Aside from the above this function also calls the handlers for the other changes that are implied by a tab switch: filename change, modification state change and active markup change. ''' self.currentTab = self.tabWidget.currentWidget() editBox = self.currentTab.editBox previewState = self.currentTab.previewState self.actionUndo.setEnabled(editBox.document().isUndoAvailable()) self.actionRedo.setEnabled(editBox.document().isRedoAvailable()) self.actionCopy.setEnabled(editBox.textCursor().hasSelection()) self.actionCut.setEnabled(editBox.textCursor().hasSelection()) self.actionPreview.setChecked(previewState >= PreviewLive) self.actionLivePreview.setChecked(previewState == PreviewLive) self.actionTableMode.setChecked(editBox.tableModeEnabled) self.editBar.setEnabled(previewState < PreviewNormal) self.ind = ind editBox.setFocus(Qt.OtherFocusReason) self.tabFileNameChanged(self.currentTab) self.tabModificationStateChanged(self.currentTab) self.tabActiveMarkupChanged(self.currentTab) def changeEditorFont(self): font, ok = QFontDialog.getFont(globalSettings.editorFont, self) if ok: self.setEditorFont(font) def setEditorFont(self, font): globalSettings.editorFont = font for tab in self.iterateTabs(): tab.editBox.updateFont() def changePreviewFont(self): font, ok = QFontDialog.getFont(globalSettings.font, self) if ok: self.setPreviewFont(font) def setPreviewFont(self, font): globalSettings.font = font for tab in self.iterateTabs(): tab.triggerPreviewUpdate() def preview(self, viewmode): self.currentTab.previewState = viewmode * 2 self.actionLivePreview.setChecked(False) self.editBar.setDisabled(viewmode) self.currentTab.updateBoxesVisibility() self.currentTab.triggerPreviewUpdate() def enableLivePreview(self, livemode): self.currentTab.previewState = int(livemode) self.actionPreview.setChecked(livemode) self.editBar.setEnabled(True) self.currentTab.updateBoxesVisibility() self.currentTab.triggerPreviewUpdate() def enableWebKit(self, enable): globalSettings.useWebKit = enable globalSettings.useWebEngine = False for tab in self.iterateTabs(): tab.rebuildPreviewBox() def enableWebEngine(self, enable): globalSettings.useWebKit = False globalSettings.useWebEngine = enable for tab in self.iterateTabs(): tab.rebuildPreviewBox() def enableCopy(self, copymode): self.actionCopy.setEnabled(copymode) self.actionCut.setEnabled(copymode) def enableFullScreen(self, yes): if yes: self.showFullScreen() else: self.showNormal() def openConfigDialog(self): dlg = ConfigDialog(self) dlg.setWindowTitle(self.tr('Preferences')) dlg.show() def enableFakeVimMode(self, yes): globalSettings.useFakeVim = yes if yes: FakeVimMode.init(self) for tab in self.iterateTabs(): tab.editBox.installFakeVimHandler() else: FakeVimMode.exit(self) def enableSpellCheck(self, yes): try: dict = enchant.Dict(self.sl or None) except enchant.errors.Error as e: QMessageBox.warning(self, '', str(e)) self.actionEnableSC.setChecked(False) yes = False self.setAllDictionaries(dict if yes else None) globalSettings.spellCheck = yes def setAllDictionaries(self, dictionary): for tab in self.iterateTabs(): hl = tab.highlighter hl.dictionary = dictionary hl.rehighlight() def changeLocale(self): localedlg = LocaleDialog(self, defaultText=self.sl) if localedlg.exec() != QDialog.Accepted: return sl = localedlg.localeEdit.text() try: enchant.Dict(sl or None) except enchant.errors.Error as e: QMessageBox.warning(self, '', str(e)) else: self.sl = sl or None self.enableSpellCheck(self.actionEnableSC.isChecked()) if localedlg.checkBox.isChecked(): globalSettings.spellCheckLocale = sl def search(self): self.searchBar.setVisible(True) self.searchEdit.setFocus(Qt.ShortcutFocusReason) def goToLine(self): line, ok = QInputDialog.getInt(self, self.tr("Go to line"), self.tr("Type the line number")) if ok: self.currentTab.goToLine(line-1) def searchBarVisibilityChanged(self, visible): if visible: self.searchEdit.setFocus(Qt.ShortcutFocusReason) def find(self, back=False, replace=False): flags = QTextDocument.FindFlags() if back: flags |= QTextDocument.FindBackward if self.csBox.isChecked(): flags |= QTextDocument.FindCaseSensitively text = self.searchEdit.text() replaceText = self.replaceEdit.text() if replace else None found = self.currentTab.find(text, flags, replaceText=replaceText) self.setSearchEditColor(found) def replaceAll(self): text = self.searchEdit.text() replaceText = self.replaceEdit.text() found = self.currentTab.replaceAll(text, replaceText) self.setSearchEditColor(found) def setSearchEditColor(self, found): palette = self.searchEdit.palette() palette.setColor(QPalette.Active, QPalette.Base, Qt.white if found else QColor(255, 102, 102)) self.searchEdit.setPalette(palette) def showInDir(self): if self.currentTab.fileName: path = QFileInfo(self.currentTab.fileName).path() QDesktopServices.openUrl(QUrl.fromLocalFile(path)) else: QMessageBox.warning(self, '', self.tr("Please, save the file somewhere.")) def moveToTopOfRecentFileList(self, fileName): if fileName: files = readListFromSettings("recentFileList") if fileName in files: files.remove(fileName) files.insert(0, fileName) recentCount = globalSettings.recentDocumentsCount if len(files) > recentCount: del files[recentCount:] writeListToSettings("recentFileList", files) def createNew(self, text=None): self.createTab("") self.ind = self.tabWidget.count()-1 self.tabWidget.setCurrentIndex(self.ind) if text: self.currentTab.editBox.textCursor().insertText(text) def switchTab(self, shift=1): self.tabWidget.setCurrentIndex((self.ind + shift) % self.tabWidget.count()) def updateRecentFiles(self): self.menuRecentFiles.clear() self.recentFilesActions = [] filesOld = readListFromSettings("recentFileList") files = [] for f in filesOld: if QFile.exists(f): files.append(f) self.recentFilesActions.append(self.act(f, trig=self.openFunction(f))) writeListToSettings("recentFileList", files) for action in self.recentFilesActions: self.menuRecentFiles.addAction(action) def markupFunction(self, markup): return lambda: self.setDefaultMarkup(markup) def openFunction(self, fileName): return lambda: self.openFileWrapper(fileName) def extensionFunction(self, data): return lambda: \ self.runExtensionCommand(data['Exec'], data['FileFilter'], data['DefaultExtension']) def getExportExtensionsList(self): extensions = [] for extsprefix in datadirs: extsdir = QDir(extsprefix+'/export-extensions/') if extsdir.exists(): for fileInfo in extsdir.entryInfoList(['*.desktop', '*.ini'], QDir.Files | QDir.Readable): extensions.append(self.readExtension(fileInfo.filePath())) locale = QLocale.system().name() self.extensionActions = [] for extension in extensions: try: if ('Name[%s]' % locale) in extension: name = extension['Name[%s]' % locale] elif ('Name[%s]' % locale.split('_')[0]) in extension: name = extension['Name[%s]' % locale.split('_')[0]] else: name = extension['Name'] data = {} for prop in ('FileFilter', 'DefaultExtension', 'Exec'): if 'X-ReText-'+prop in extension: data[prop] = extension['X-ReText-'+prop] elif prop in extension: data[prop] = extension[prop] else: data[prop] = '' action = self.act(name, trig=self.extensionFunction(data)) if 'Icon' in extension: action.setIcon(self.actIcon(extension['Icon'])) mimetype = extension['MimeType'] if 'MimeType' in extension else None except KeyError: print('Failed to parse extension: Name is required', file=sys.stderr) else: self.extensionActions.append((action, mimetype)) def updateExtensionsVisibility(self): markupClass = self.currentTab.getActiveMarkupClass() for action in self.extensionActions: if markupClass is None: action[0].setEnabled(False) continue mimetype = action[1] if mimetype is None: enabled = True elif markupClass == markups.MarkdownMarkup: enabled = (mimetype in ("text/x-retext-markdown", "text/x-markdown", "text/markdown")) elif markupClass == markups.ReStructuredTextMarkup: enabled = (mimetype in ("text/x-retext-rst", "text/x-rst")) else: enabled = False action[0].setEnabled(enabled) def readExtension(self, fileName): extFile = QFile(fileName) extFile.open(QIODevice.ReadOnly) extension = {} stream = QTextStream(extFile) while not stream.atEnd(): line = stream.readLine() if '=' in line: index = line.index('=') extension[line[:index].rstrip()] = line[index+1:].lstrip() extFile.close() return extension def openFile(self): supportedExtensions = ['.txt'] for markup in markups.get_all_markups(): supportedExtensions += markup.file_extensions fileFilter = ' (' + str.join(' ', ['*'+ext for ext in supportedExtensions]) + ');;' fileNames = QFileDialog.getOpenFileNames(self, self.tr("Select one or several files to open"), QDir.currentPath(), self.tr("Supported files") + fileFilter + self.tr("All files (*)")) for fileName in fileNames[0]: self.openFileWrapper(fileName) @pyqtSlot(str) def openFileWrapper(self, fileName): if not fileName: return fileName = QFileInfo(fileName).canonicalFilePath() exists = False for i, tab in enumerate(self.iterateTabs()): if tab.fileName == fileName: exists = True ex = i if exists: self.tabWidget.setCurrentIndex(ex) elif QFile.exists(fileName): noEmptyTab = ( (self.ind is None) or self.currentTab.fileName or self.currentTab.editBox.toPlainText() or self.currentTab.editBox.document().isModified() ) if noEmptyTab: self.createTab(fileName) self.ind = self.tabWidget.count()-1 self.tabWidget.setCurrentIndex(self.ind) if fileName: self.fileSystemWatcher.addPath(fileName) self.currentTab.readTextFromFile(fileName) self.moveToTopOfRecentFileList(self.currentTab.fileName) def showEncodingDialog(self): if not self.maybeSave(self.ind): return codecsSet = set(bytes(QTextCodec.codecForName(alias).name()) for alias in QTextCodec.availableCodecs()) encoding, ok = QInputDialog.getItem(self, '', self.tr('Select file encoding from the list:'), [bytes(b).decode() for b in sorted(codecsSet)], 0, False) if ok: self.currentTab.readTextFromFile(None, encoding) def saveFileAs(self): self.saveFile(dlg=True) def saveAll(self): for tab in self.iterateTabs(): if (tab.fileName and tab.editBox.document().isModified() and QFileInfo(tab.fileName).isWritable()): tab.saveTextToFile() def saveFile(self, dlg=False): fileNameToSave = self.currentTab.fileName if (not fileNameToSave) or dlg: proposedFileName = "" markupClass = self.currentTab.getActiveMarkupClass() if (markupClass is None) or not hasattr(markupClass, 'default_extension'): defaultExt = self.tr("Plain text (*.txt)") ext = ".txt" else: defaultExt = self.tr('%s files', 'Example of final string: Markdown files') \ % markupClass.name + ' (' + str.join(' ', ('*'+extension for extension in markupClass.file_extensions)) + ')' if markupClass == markups.MarkdownMarkup: ext = globalSettings.markdownDefaultFileExtension elif markupClass == markups.ReStructuredTextMarkup: ext = globalSettings.restDefaultFileExtension else: ext = markupClass.default_extension if fileNameToSave is not None: proposedFileName = fileNameToSave fileNameToSave = QFileDialog.getSaveFileName(self, self.tr("Save file"), proposedFileName, defaultExt)[0] if fileNameToSave: if not QFileInfo(fileNameToSave).suffix(): fileNameToSave += ext # Make sure we don't overwrite a file opened in other tab for tab in self.iterateTabs(): if tab is not self.currentTab and tab.fileName == fileNameToSave: QMessageBox.warning(self, "", self.tr("Cannot save to file which is open in another tab!")) return False self.actionSetEncoding.setDisabled(self.autoSaveActive()) if fileNameToSave: if self.currentTab.saveTextToFile(fileNameToSave): self.moveToTopOfRecentFileList(self.currentTab.fileName) return True else: QMessageBox.warning(self, '', self.tr("Cannot save to file because it is read-only!")) return False def saveHtml(self, fileName): if not QFileInfo(fileName).suffix(): fileName += ".html" try: _, htmltext, _ = self.currentTab.getDocumentForExport(webenv=True) except Exception: return self.printError() htmlFile = QFile(fileName) result = htmlFile.open(QIODevice.WriteOnly) if not result: QMessageBox.warning(self, '', self.tr("Cannot save to file because it is read-only!")) return html = QTextStream(htmlFile) if globalSettings.defaultCodec: html.setCodec(globalSettings.defaultCodec) html << htmltext htmlFile.close() def textDocument(self, title, htmltext): td = QTextDocument() td.setMetaInformation(QTextDocument.DocumentTitle, title) td.setHtml(htmltext) td.setDefaultFont(globalSettings.font) return td def saveOdf(self): title, htmltext, _ = self.currentTab.getDocumentForExport() try: document = self.textDocument(title, htmltext) except Exception: return self.printError() fileName = QFileDialog.getSaveFileName(self, self.tr("Export document to ODT"), self.currentTab.getBaseName() + ".odt", self.tr("OpenDocument text files (*.odt)"))[0] if not QFileInfo(fileName).suffix(): fileName += ".odt" writer = QTextDocumentWriter(fileName) writer.setFormat(b"odf") writer.write(document) def saveFileHtml(self): fileName = QFileDialog.getSaveFileName(self, self.tr("Save file"), self.currentTab.getBaseName() + ".html", self.tr("HTML files (*.html *.htm)"))[0] if fileName: self.saveHtml(fileName) def getDocumentForPrint(self, title, htmltext, preview): if globalSettings.useWebKit: return preview try: return self.textDocument(title, htmltext) except Exception: self.printError() def standardPrinter(self, title): printer = QPrinter(QPrinter.HighResolution) printer.setDocName(title) printer.setCreator('ReText %s' % app_version) if globalSettings.paperSize: pageSize = self.getPageSizeByName(globalSettings.paperSize) if pageSize is not None: printer.setPaperSize(pageSize) else: QMessageBox.warning(self, '', self.tr('Unrecognized paperSize setting "%s".') % globalSettings.paperSize) return printer def getPageSizeByName(self, pageSizeName): """ Returns a validated PageSize instance corresponding to the given name. Returns None if the name is not a valid PageSize. """ pageSize = None lowerCaseNames = {pageSize.lower(): pageSize for pageSize in self.availablePageSizes()} if pageSizeName.lower() in lowerCaseNames: pageSize = getattr(QPagedPaintDevice, lowerCaseNames[pageSizeName.lower()]) return pageSize def availablePageSizes(self): """ List available page sizes. """ sizes = [x for x in dir(QPagedPaintDevice) if type(getattr(QPagedPaintDevice, x)) == QPagedPaintDevice.PageSize] return sizes def savePdf(self): fileName = QFileDialog.getSaveFileName(self, self.tr("Export document to PDF"), self.currentTab.getBaseName() + ".pdf", self.tr("PDF files (*.pdf)"))[0] if fileName: if not QFileInfo(fileName).suffix(): fileName += ".pdf" title, htmltext, preview = self.currentTab.getDocumentForExport() if globalSettings.useWebEngine and hasattr(preview.page(), "printToPdf"): pageSize = self.getPageSizeByName(globalSettings.paperSize) if pageSize is None: pageSize = QPageSize(QPageSize.A4) margins = QMarginsF(20, 20, 13, 20) # left, top, right, bottom (in millimeters) layout = QPageLayout(pageSize, QPageLayout.Portrait, margins, QPageLayout.Millimeter) preview.page().printToPdf(fileName, layout) # Available since Qt 5.7 return printer = self.standardPrinter(title) printer.setOutputFormat(QPrinter.PdfFormat) printer.setOutputFileName(fileName) document = self.getDocumentForPrint(title, htmltext, preview) if document != None: document.print(printer) def printFile(self): title, htmltext, preview = self.currentTab.getDocumentForExport() printer = self.standardPrinter(title) dlg = QPrintDialog(printer, self) dlg.setWindowTitle(self.tr("Print document")) if (dlg.exec() == QDialog.Accepted): document = self.getDocumentForPrint(title, htmltext, preview) if document != None: document.print(printer) def printPreview(self): title, htmltext, preview = self.currentTab.getDocumentForExport() document = self.getDocumentForPrint(title, htmltext, preview) if document is None: return printer = self.standardPrinter(title) preview = QPrintPreviewDialog(printer, self) preview.paintRequested.connect(document.print) preview.exec() def runExtensionCommand(self, command, filefilter, defaultext): import shlex of = ('%of' in command) html = ('%html' in command) if of: if defaultext and not filefilter: filefilter = '*'+defaultext fileName = QFileDialog.getSaveFileName(self, self.tr('Export document'), '', filefilter)[0] if not fileName: return if defaultext and not QFileInfo(fileName).suffix(): fileName += defaultext else: fileName = 'out' + defaultext basename = '.%s.retext-temp' % self.currentTab.getBaseName() if html: tmpname = basename+'.html' self.saveHtml(tmpname) else: tmpname = basename + self.currentTab.getActiveMarkupClass().default_extension self.currentTab.writeTextToFile(tmpname) command = command.replace('%of', shlex.quote(fileName)) command = command.replace('%html' if html else '%if', shlex.quote(tmpname)) try: Popen(str(command), shell=True).wait() except Exception as error: errorstr = str(error) QMessageBox.warning(self, '', self.tr('Failed to execute the command:') + '\n' + errorstr) QFile(tmpname).remove() def autoSaveActive(self, tab=None): tab = tab if tab else self.currentTab return bool(self.autoSaveEnabled and tab.fileName and QFileInfo(tab.fileName).isWritable()) def clipboardDataChanged(self): mimeData = QApplication.instance().clipboard().mimeData() if mimeData is not None: self.actionPaste.setEnabled(mimeData.hasText()) self.actionPasteImage.setEnabled(mimeData.hasImage()) def insertFormatting(self, formatting): if formatting == 'table': dialog = InsertTableDialog(self) dialog.show() self.formattingBox.setCurrentIndex(0) return cursor = self.currentTab.editBox.textCursor() text = cursor.selectedText() moveCursorTo = None def c(cursor): nonlocal moveCursorTo moveCursorTo = cursor.position() def ensurenl(cursor): if not cursor.atBlockStart(): cursor.insertText('\n\n') toinsert = { 'header': (ensurenl, '# ', text), 'italic': ('*', text, c, '*'), 'bold': ('**', text, c, '**'), 'underline': ('<u>', text, c, '</u>'), 'numbering': (ensurenl, ' 1. ', text), 'bullets': (ensurenl, ' * ', text), 'image': ('![', text or self.tr('Alt text'), c, '](', self.tr('URL'), ')'), 'link': ('[', text or self.tr('Link text'), c, '](', self.tr('URL'), ')'), 'inline code': ('`', text, c, '`'), 'code block': (ensurenl, ' ', text), 'blockquote': (ensurenl, '> ', text), } if formatting not in toinsert: return cursor.beginEditBlock() for token in toinsert[formatting]: if callable(token): token(cursor) else: cursor.insertText(token) cursor.endEditBlock() self.formattingBox.setCurrentIndex(0) # Bring back the focus on the editor self.currentTab.editBox.setFocus(Qt.OtherFocusReason) if moveCursorTo: cursor.setPosition(moveCursorTo) self.currentTab.editBox.setTextCursor(cursor) def insertSymbol(self, num): if num: self.currentTab.editBox.insertPlainText('&'+self.usefulChars[num-1]+';') self.symbolBox.setCurrentIndex(0) def fileChanged(self, fileName): tab = None for testtab in self.iterateTabs(): if testtab.fileName == fileName: tab = testtab if tab is None: self.fileSystemWatcher.removePath(fileName) return if not QFile.exists(fileName): self.tabWidget.setCurrentWidget(tab) tab.editBox.document().setModified(True) QMessageBox.warning(self, '', self.tr( 'This file has been deleted by other application.\n' 'Please make sure you save the file before exit.')) elif not tab.editBox.document().isModified(): # File was not modified in ReText, reload silently tab.readTextFromFile() else: self.tabWidget.setCurrentWidget(tab) text = self.tr( 'This document has been modified by other application.\n' 'Do you want to reload the file (this will discard all ' 'your changes)?\n') if self.autoSaveEnabled: text += self.tr( 'If you choose to not reload the file, auto save mode will ' 'be disabled for this session to prevent data loss.') messageBox = QMessageBox(QMessageBox.Warning, '', text) reloadButton = messageBox.addButton(self.tr('Reload'), QMessageBox.YesRole) messageBox.addButton(QMessageBox.Cancel) messageBox.exec() if messageBox.clickedButton() is reloadButton: tab.readTextFromFile() else: self.autoSaveEnabled = False tab.editBox.document().setModified(True) if fileName not in self.fileSystemWatcher.files(): # https://github.com/retext-project/retext/issues/137 self.fileSystemWatcher.addPath(fileName) def maybeSave(self, ind): tab = self.tabWidget.widget(ind) if self.autoSaveActive(tab): tab.saveTextToFile() return True if not tab.editBox.document().isModified(): return True self.tabWidget.setCurrentIndex(ind) ret = QMessageBox.warning(self, '', self.tr("The document has been modified.\nDo you want to save your changes?"), QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel) if ret == QMessageBox.Save: return self.saveFile(False) elif ret == QMessageBox.Cancel: return False return True def closeEvent(self, closeevent): for ind in range(self.tabWidget.count()): if not self.maybeSave(ind): return closeevent.ignore() if globalSettings.saveWindowGeometry: globalSettings.windowGeometry = self.saveGeometry() if globalSettings.openLastFilesOnStartup: files = [tab.fileName for tab in self.iterateTabs()] writeListToSettings("lastFileList", files) globalSettings.lastTabIndex = self.tabWidget.currentIndex() closeevent.accept() def viewHtml(self): htmlDlg = HtmlDialog(self) try: _, htmltext, _ = self.currentTab.getDocumentForExport(includeStyleSheet=False) except Exception: return self.printError() winTitle = self.currentTab.getBaseName() htmlDlg.setWindowTitle(winTitle+" ("+self.tr("HTML code")+")") htmlDlg.textEdit.setPlainText(htmltext.rstrip()) htmlDlg.hl.rehighlight() htmlDlg.show() htmlDlg.raise_() htmlDlg.activateWindow() def insertImages(self): supportedExtensions = ['.png', '.jpg', '.jpeg', '.gif', '.bmp'] fileFilter = ' (%s);;' % ' '.join('*' + ext for ext in supportedExtensions) fileNames, _selectedFilter = QFileDialog.getOpenFileNames(self, self.tr("Select one or several images to open"), QDir.currentPath(), self.tr("Supported files") + fileFilter + self.tr("All files (*)")) cursor = self.currentTab.editBox.textCursor() imagesMarkup = '\n'.join( self.currentTab.editBox.getImageMarkup(fileName) for fileName in fileNames) cursor.insertText(imagesMarkup) self.formattingBox.setCurrentIndex(0) self.currentTab.editBox.setFocus(Qt.OtherFocusReason) def openHelp(self): QDesktopServices.openUrl(QUrl('https://github.com/retext-project/retext/wiki')) def aboutDialog(self): QMessageBox.about(self, self.aboutWindowTitle, '<p><b>' + (self.tr('ReText %s (using PyMarkups %s)') % (app_version, markups.__version__)) +'</b></p>' + self.tr('Simple but powerful editor' ' for Markdown and reStructuredText') +'</p><p>'+self.tr('Author: Dmitry Shachnev, 2011').replace('2011', '2011–2020') +'<br><a href="https://github.com/retext-project/retext">'+self.tr('Website') +'</a> | <a href="http://daringfireball.net/projects/markdown/syntax">' +self.tr('Markdown syntax') +'</a> | <a href="http://docutils.sourceforge.net/docs/user/rst/quickref.html">' +self.tr('reStructuredText syntax')+'</a></p>') def setDefaultMarkup(self, markupClass): globalSettings.defaultMarkup = markupClass.name for tab in self.iterateTabs(): if not tab.fileName: tab.updateActiveMarkupClass()
class Window(QMainWindow): save_crop_signal = pyqtSignal() samling_ratio = 60 class State(Enum): Initial = 1 OneCursorPlaced = 2 TwoCursorPlaced = 3 def __init__(self): super().__init__() self.player = QMediaPlayer() self.ui = Ui_MainWindow() # setup main Window's UI self.ui.setupUi(self) self._setup_connections() self._setup_matplotlib() self._setup_directory_browser() self.play_timer = QTimer(self) self.play_timer.timeout.connect(self.plot_animator) self.ymax = 30000 self.state = None self.crop_line_1_pos = None self.crop_line_2_pos = None self.play_limit = (0, 0) self.current_zoom = 100 self.seconds_to_prefetch = 60 self.minimum_file_file_length = 2.5 self.save_crop_signal.connect(self.save_crop_to_file) def _setup_matplotlib(self): self.figure = Figure() self.canvas = FigureCanvas(self.figure) layout = self.ui.graph_widget.layout() layout.addWidget(self.canvas) self.canvas_click_connection = None self.canvas_scroll_connection = None self.subplot = self.figure.add_subplot(111) self.audio_plot, = self.subplot.plot([0], [0], '-', color=(0.70, 0.70, 0.70)) self.playIndicator, = self.subplot.plot([0], [0], '^') self.crop_line_1, = self.subplot.plot([], [], '-r') self.crop_line_2, = self.subplot.plot([], [], '-r') self.ui.vertical_splitter.setSizes([2, 12]) def _setup_directory_browser(self): curdir = os.path.abspath(os.path.curdir) self.file_system_model = QFileSystemModel(self) self.file_system_model.setReadOnly(True) self.file_system_model.setFilter(QDir.AllDirs | QDir.AllEntries | QDir.NoDotAndDotDot) self.file_system_model.setNameFilters(['*.wav', '*.txt']) self.file_system_model.setNameFilterDisables(False) self.ui.directory_view.setModel(self.file_system_model) self.file_system_model.setRootPath(curdir) index = self.file_system_model.index(curdir) self.ui.directory_view.setRootIndex(index) self.ui.directory_view.hideColumn(1) self.ui.directory_view.hideColumn(2) self.ui.directory_view.hideColumn(3) # index: QModelIndex = model.index(os.path.abspath(os.path.curdir)) # self.ui.directory_view.expand(index); # self.ui.directory_view.scrollTo(index) # self.ui.directory_view.setCurrentIndex(index) self.ui.directory_view.doubleClicked.connect(self.file_selected) def plot(self, ydata_byte, start_pos=0): ''' plot some random stuff ''' ploty = numpy.fromstring(ydata_byte, numpy.int16) plotdatay = ploty[0:len(ploty):Window.samling_ratio] plotdatax = [ x for x in range(start_pos, len(ploty) + start_pos, Window.samling_ratio) ] self.set_view_range(start_pos, plotdatax[-1]) self.view_limit_range = (start_pos, plotdatax[-1]) _max = max(plotdatay) self.figure.get_axes()[0].set_ylim(-_max, _max) print("The real plot limit", (start_pos, plotdatax[-1])) self.audio_plot.set_data(plotdatax, plotdatay) # refresh canvas self.canvas.draw() def set_view_range(self, start, end): self.figure.get_axes()[0].set_xlim(start, end) self.current_view = [start, end] def plot_animator(self): # current_music_time = self.play_started_audio_time + diff current_time_ms = self.player.position() current_music_frame = int( (current_time_ms * self.wave.getframerate()) / 1000) self.playIndicator.set_data([current_music_frame], [0]) self.canvas.draw() if current_time_ms >= self.play_limit[1]: self.player.pause() self.play_timer.stop() self.playIndicator.set_data(self.crop_line_2_pos, 0) self.canvas.draw() def _setup_connections(self): self.player.stateChanged.connect(self.player_status_changed) self.player.positionChanged.connect(self.playback_progress_change) def toggle_pause(self): if self.playing: self.player.pause() self.play_timer.stop() else: self.player.play() self.play_timer.start() self.playing = not self.playing def seek(self, pos): self.player.setPosition(pos) self.player.play() if self.player.state() != QMediaPlayer.PlayingState: self.player.play() if not self.play_timer.isActive(): self.play_timer.start() def seek_frame(self, frame_pos): self.seek(int((frame_pos * 1000) / (self.wave.getframerate()))) # self.seek(int(frame_pos / (self.wave.getframerate() * 1000))) def canvas_click_listener(self, event): if not event.xdata: return currentframe = event.xdata current_time_in_milli = int(currentframe / self.wave.getframerate() * 1000) if (current_time_in_milli < 0): current_time_in_milli = 0 current_x = int(event.xdata) if (current_x < 0): current_x = 0 if self.state == Window.State.Initial: self.crop_line_1.set_data([current_x, current_x], [-self.ymax, self.ymax]) self.crop_line_1_pos = current_x elif self.state == Window.State.OneCursorPlaced: if current_x > self.crop_line_1_pos: self.crop_line_2_pos = current_x self.crop_line_2.set_data([current_x, current_x], [-self.ymax, self.ymax]) self.canvas.draw() self.seek(current_time_in_milli) def canvas_scroll_listener(self, event): current_range = self.current_view[1] - self.current_view[0] if current_range < 0: return if event.button == 'down': new_range = current_range / 1.3 else: new_range = current_range * 1.3 new_view = [event.xdata - new_range / 2, event.xdata + new_range / 2] count = 0 if new_view[0] < self.view_limit_range[0]: new_view[1] += self.view_limit_range[0] - new_view[0] new_view[0] = self.view_limit_range[0] if new_view[1] > self.view_limit_range[1]: new_view[1] = self.view_limit_range[1] elif new_view[1] > self.view_limit_range[1]: new_view[0] -= new_view[1] - self.view_limit_range[1] new_view[1] = self.view_limit_range[1] if new_view[0] < self.view_limit_range[0]: new_view[0] = self.view_limit_range[0] if new_view[0] > new_view[1]: return self.figure.get_axes()[0].set_xlim(new_view[0], new_view[1]) self.current_view = new_view self.canvas.draw() def eventFilter(self, event): print("New Event", event.type()) return False def keyPressEvent(self, event): key = event.key() if key == Qt.Key_Space: self.toggle_pause() if key == Qt.Key_Return: if self.state == Window.State.Initial: if self.crop_line_1_pos: self.state = Window.State.OneCursorPlaced self.crop_line_1.set_data( [self.crop_line_1_pos, self.crop_line_1_pos], [-self.ymax, self.ymax]) self.crop_line_1.set_color("green") self.canvas.draw() elif self.state == Window.State.OneCursorPlaced: if self.crop_line_2_pos: self.crop_line_2.set_color("green") self.zoom_to_crop() self.state = Window.State.TwoCursorPlaced self.ui.statusbar.showMessage( "Press Enter to save the clip to file", 3000) elif self.state == Window.State.TwoCursorPlaced: self.crop_to_save = self.data[self.crop_line_1_pos * 2:self.crop_line_2_pos * 2] self.crop_nchannel = self.wave.getnchannels() self.crop_stampwidth = self.wave.getsampwidth() self.crop_framerate = self.wave.getframerate() self.save_crop_signal.emit() self.update_cropped_plot() if key == Qt.Key_Backspace: if self.state == Window.State.OneCursorPlaced: self.state = Window.State.Initial self.crop_line_1_pos = None self.crop_line_1.set_color("red") self.crop_line_2.set_data([], []) self.canvas.draw() elif self.state == Window.State.TwoCursorPlaced: self.play_limit = self.play_limit_bak self.state = Window.State.OneCursorPlaced self.crop_line_2_pos = None self.crop_line_2.set_color("red") self.canvas.draw() @pyqtSlot(QMediaPlayer.State) def player_status_changed(self, x): if x == QMediaPlayer.StoppedState: self.play_timer.stop() pass elif x == QMediaPlayer.PlayingState: pass elif x == QMediaPlayer.PausedState: pass @pyqtSlot('qint64') def playback_progress_change(self, position): pass # self.ui.progressBar.setValue(int(position / self.player.duration() * 100)) def read_file(self, filename): # reset the cursors to default values self.wave = wave.open(filename, 'rb') self.channels = self.wave.getnchannels() self.rate = self.wave.getframerate() nframes = self.seconds_to_prefetch * self.wave.getframerate() self.playing = False self.data = self.wave.readframes(nframes) self.total_frames_read = len(self.data) // 2 if self.total_frames_read / self.wave.getframerate( ) < self.minimum_file_file_length: self.ui.statusbar.showMessage("The file is too short.") return self.player.setMedia(QMediaContent(QUrl.fromLocalFile(filename))) self.plot(self.data) nframes = min(self.wave.getnframes(), nframes) self.data_shift = 0 self.play_limit = (0, (nframes * 1000) / self.wave.getframerate()) self.crop_count = 0 self.current_open_file_name = filename[:-4] self.toggle_pause() if not self.canvas_scroll_connection: self.canvas_click_connection = self.canvas.mpl_connect( "button_press_event", self.canvas_click_listener) self.canvas_scroll_connection = self.canvas.mpl_connect( "scroll_event", self.canvas_scroll_listener) self.crop_line_1_pos = None self.crop_line_2_pos = None self.state = Window.State.Initial self.crop_line_2.set_data([], []) self.crop_line_2.set_color("red") self.crop_line_1.set_data([], []) self.crop_line_1.set_color("red") def zoom_to_crop(self): ##TODO : make crop from line1 and line2 position self.set_view_range(self.crop_line_1_pos - 1000, self.crop_line_2_pos + 1000) # cropped_data = self.data[self.crop_line_1_pos:self.crop_line_2_pos] self.play_limit_bak = self.play_limit self.play_limit = ((self.crop_line_1_pos * 1000) / self.wave.getframerate(), (1000 * self.crop_line_2_pos) / self.wave.getframerate()) self.seek_frame(self.crop_line_1_pos) self.canvas.draw() def update_cropped_plot(self): # frames remain in the total sound clip remaining_frames = (self.wave.getnframes()) - int(self.crop_line_2_pos) # time remain for compleliton of sound clip remaining_ms = (remaining_frames * 1000) / self.wave.getframerate() if remaining_ms < 3000: return self.crop_completed(remaining_ms) # the no of frames that have been loaded into memory frames_in_memory = int(self.total_frames_read - self.crop_line_2_pos) data_pos = int(self.crop_line_2_pos * 2 - self.data_shift) self.data_shift = self.crop_line_2_pos * 2 # all the data from sound have been read into the memory if frames_in_memory == remaining_frames: self.data = self.data[data_pos:len(self.data)] else: # the no of maximum frames that will be showed in preview total_frames_required = self.seconds_to_prefetch * self.wave.getframerate( ) # the no of frames that needs to be read from disk frames_to_read = total_frames_required - frames_in_memory # the file may not have that many frames, so it's the minimun of frames to read and frames in disk remain # to read frames_that_will_be_read = min( self.wave.getnframes() - self.total_frames_read, frames_to_read) self.total_frames_read += frames_that_will_be_read self.data = self.data[data_pos:len( self.data)] + self.wave.readframes(frames_that_will_be_read) self.plot(self.data, self.crop_line_2_pos) # frames_remain_to_read = self.wave.getnframes() - self.total_frames_read self.state = Window.State.Initial self.play_limit = ((self.crop_line_2_pos * 1000) / self.wave.getframerate(), (self.total_frames_read * 1000) / self.wave.getframerate()) self.view_limit_range = (self.crop_line_2_pos, self.total_frames_read) self.seek_frame(self.crop_line_2_pos) self.crop_line_1.set_data([], []) self.crop_line_2.set_data([], []) self.crop_line_1.set_color("red") self.crop_line_2.set_color("red") self.crop_line_1_pos = None self.crop_line_2_pos = None @pyqtSlot(QModelIndex) def file_selected(self, index): filename = self.file_system_model.filePath(index) if filename.endswith('.txt'): self.text_file = QFile(filename) if not self.text_file.open(QFile.ReadOnly | QFile.Text): QMessageBox.warning(self, "Application", "Cannot read file", self.text_file.errorString()) return in_stream = QTextStream(self.text_file) # self.ui.text_edit.setPlainText(in_stream.readAll()) # font: QFont = self.ui.text_browser.font() # font.setPixelSize(40) # self.ui.text_browser.setFont(font) data = in_stream.readAll() self.ui.text_browser.setPlainText(data) self.ui.text_edit.setPlainText(data) else: try: self.read_file(filename) except: traceback.print_exc(2) self.ui.statusbar.showMessage("Reading the file failed", 300) @pyqtSlot() def save_crop_to_file(self): self.crop_count += 1 wave_file = wave.open( self.current_open_file_name + "_crop_" + str(self.crop_count) + '.wav', 'wb') wave_file.setnchannels(self.crop_nchannel) wave_file.setsampwidth(self.crop_stampwidth) wave_file.setframerate(self.crop_framerate) wave_file.writeframes(self.crop_to_save) wave_file.close() def crop_completed(self, remaining_ms): self.state = Window.State.Initial self.crop_line_1.set_data([], []) self.crop_line_2.set_data([], []) self.crop_line_1.set_color("red") self.crop_line_2.set_color("red") self.crop_line_1_pos = None self.crop_line_2_pos = None self.audio_plot.set_data([], []) self.ui.statusbar.showMessage("Cropping this file has been completed") self.canvas.mpl_disconnect(self.canvas_click_connection) self.canvas.mpl_disconnect(self.canvas_scroll_connection) self.canvas_scroll_connection = None self.canvas_click_connection = None self.canvas.draw() self.player.stop() self.play_timer.stop() self.wave.close() self.ui.statusbar.showMessage( "Only %f seconds left thus this file is considered completed" % (remaining_ms / 1000))
class FileSelect(QMainWindow, QWidget): def __init__(self): super(FileSelect, self).__init__() self.setWindowTitle("Printing Booth Systems") global file, pagecount, total, enabler, copies, colormode, page file = "" pagecount = 0 total = 0.0 copies = "1" colormode = "" page = "" widget = QWidget() layout = QVBoxLayout() self.index = QModelIndex() self.model = QFileSystemModel() self.tree = QTreeView() self.model.setRootPath(QDir.currentPath()) #ignore self.model.setNameFilters(["*.pdf"]) idx = self.model.index("/media/pi/") self.tree.setModel(self.model) self.tree.setRootIndex(idx) self.tree.setColumnWidth(0, 250) self.tree.setAlternatingRowColors(True) layout.addWidget(self.tree) widget.setLayout(layout) self.setCentralWidget(widget) widget.setFixedWidth(600) self.tree.setFont(QFont('Arial', 30)) self.tree.hideColumn(1) self.tree.hideColumn(2) self.tree.hideColumn(3) self.tree.clicked.connect(self.on_treeView_clicked) self.btn1 = QPushButton('Select', self) self.btn1.setGeometry(620, 120, 150, 90) self.btn1.clicked.connect(self.slot_btn1_function) self.btn2 = QPushButton('Cancel', self) self.btn2.setGeometry(620, 300, 150, 90) self.btn2.clicked.connect(self.slot_btn2_function) def slot_btn1_function(self): if file.lower().endswith('.pdf'): self.p = Preview() self.p.showFullScreen() self.hide() else: pass def slot_btn2_function(self): self.mw = MainWindow() self.mw.showFullScreen() self.hide() def on_treeView_clicked(self, index): global file indexItem = self.model.index(index.row(), 0, index.parent()) filePath = self.model.filePath(indexItem) file = filePath
class NProject(QObject): """Project representation. SIGNALS: @projectPropertiesUpdated() """ projectPropertiesUpdated = pyqtSignal() projectNameUpdated = pyqtSignal(str) def __init__(self, path): super(NProject, self).__init__() project = json_manager.read_ninja_project(path) self.path = path self._name = project.get('name', '') if self._name == '': self._name = file_manager.get_basename(path) self.project_type = project.get('project-type', '') self.description = project.get('description', '') if self.description == '': self.description = translations.TR_NO_DESCRIPTION self.url = project.get('url', '') self.license = project.get('license', '') self.main_file = project.get('mainFile', '') self.pre_exec_script = project.get('preExecScript', '') self.post_exec_script = project.get('postExecScript', '') self.indentation = project.get('indentation', settings.INDENT) self.use_tabs = project.get('use-tabs', settings.USE_TABS) self.extensions = project.get('supported-extensions', settings.SUPPORTED_EXTENSIONS) self.python_exec = project.get('pythonExec', settings.PYTHON_EXEC) self.python_path = project.get('PYTHONPATH', '') self.additional_builtins = project.get('additional_builtins', []) self.program_params = project.get('programParams', '') self.venv = project.get('venv', '') self.related_projects = project.get('relatedProjects', []) self.added_to_console = False self.is_current = False #Model is a QFileSystemModel to be set on runtime self.__model = QFileSystemModel() self.__model.setRootPath(path) def _get_name(self): return self._name def _set_name(self, name): if name == '': self._name = file_manager.get_basename(self.path) else: self._name = name self.projectNameUpdated.emit(self._name) name = property(_get_name, _set_name) def save_project_properties(self): #save project properties project = {} project['name'] = self._name project['description'] = self.description project['url'] = self.url project['license'] = self.license project['mainFile'] = self.main_file project['project-type'] = self.project_type project['supported-extensions'] = self.extensions project['indentation'] = self.indentation project['use-tabs'] = self.use_tabs project['pythonExec'] = self.python_exec # FIXME project['PYTHONPATH'] = self.python_path project['additional_builtins'] = self.additional_builtins project['preExecScript'] = self.pre_exec_script project['postExecScript'] = self.post_exec_script project['venv'] = self.venv project['programParams'] = self.program_params project['relatedProjects'] = self.related_projects if file_manager.file_exists(self.path, self._name + '.nja'): file_manager.delete_file(self.path, self._name + '.nja') json_manager.create_ninja_project(self.path, self._name, project) #TODO: update project tree on extensions changed @property def full_path(self): ''' Returns the full path of the project ''' project_file = json_manager.get_ninja_project_file(self.path) if not project_file: # FIXME: If we dont have a project file project_file = '' # we should do SOMETHING! like kill zombies! return os.path.join(self.path, project_file) @property def python_exec_command(self): ''' Returns the python exec command of the project ''' if self.venv is '': return self.venv return self.python_exec @property def model(self): return self.__model @model.setter def model(self, model): # lint:ok self.__model = model self.__model.setFilter(QDir.AllDirs | QDir.NoDotAndDotDot | QDir.AllEntries) self.__model.setNameFilters(self.extensions) @model.deleter def model(self): # lint:ok del(self.__model)
class AnalyzeTab(QWidget): analyzeDone = pyqtSignal('PyQt_PyObject', 'PyQt_PyObject', 'PyQt_PyObject', 'PyQt_PyObject', 'PyQt_PyObject', 'PyQt_PyObject', 'PyQt_PyObject') def __init__(self): super().__init__() # Layouts self.mainLayout = QHBoxLayout() self.lVbox = QVBoxLayout() self.lHbox = QHBoxLayout() self.lHbox_top = QHBoxLayout() self.rVbox = QVBoxLayout() self.rHbox = QHBoxLayout() self.rVbox2 = QVBoxLayout() self.stack = QStackedWidget() self.stack_Vbox = QVBoxLayout() self.stack_Hbox1 = QHBoxLayout() self.stack_Hbox2 = QHBoxLayout() self.hSplit = QSplitter(Qt.Horizontal) self.hSplit.setFrameShape(QFrame.StyledPanel) self.vSplit = QSplitter(Qt.Vertical) self.vSplit.setFrameShape(QFrame.StyledPanel) self.mainLayout.addLayout(self.lVbox, 1) self.mainLayout.addLayout(self.rVbox, 3) # Setup file browser self.fileModel = QFileSystemModel() self.fileModel.setNameFilters(['*.wav']) self.fileModel.setRootPath(QDir.currentPath()) self.fileTree = QTreeView() self.fileTree.setModel(self.fileModel) self.fileTree.setRootIndex(self.fileModel.index(r'./')) self.fileTree.setSelectionMode(QAbstractItemView.SingleSelection) self.fileTree.setColumnHidden(2, True) self.fileTree.setColumnHidden(1, True) self.rootDirEdit = QLineEdit(os.path.dirname(__file__)) self.rootDirEdit.returnPressed.connect(self.on_edit_root) self.browseBtn = QPushButton('Browse') self.browseBtn.clicked.connect(self.on_browse) self.lHbox_top.addWidget(self.rootDirEdit, 3) self.lHbox_top.addWidget(self.browseBtn, 1) # Setup Canvas self.canvas = PlotCanvas(self) self.analyzeDone.connect(self.canvas.plot) self._analyze = lambda _: self.analyze(self.fileTree.selectedIndexes()) self.analyzeBtn = QPushButton('Analyze') self.analyzeBtn.clicked.connect(self._analyze) ## BATCH ANALYSIS CONTROLS ## self.batchAnalyzeChk = QCheckBox('Batch Analysis') self.dataTable = QTableWidget() self.batchCtrlBox = QGroupBox("Batch Analysis") self.batchCtrlBox.setLayout(self.stack_Vbox) # Analysis Mode self.modeGroup = QButtonGroup() self.modeBox = QGroupBox('Analysis Mode') self.modeBox.setLayout(self.stack_Hbox1) self.stack_Vbox.addWidget(self.modeBox) self.wavAnalysisChk = QCheckBox('Wav analysis') self.wavAnalysisChk.setChecked(True) self.calibrationLocationBox = QComboBox() self.calibrationLocationBox.addItems([str(n) for n in range(1, 11)]) self.calibrationCurveChk = QCheckBox('Calibration Curve') self.calibrationCurveChk.toggled.connect( lambda state: self.calibrationLocationBox.setEnabled(state)) self.calibrationCurveChk.setChecked(False) self.stack_Hbox1.addWidget(self.wavAnalysisChk, 3) self.stack_Hbox1.addWidget(self.calibrationCurveChk, 3) self.stack_Hbox1.addWidget(QLabel('Location: '), 1) self.stack_Hbox1.addWidget(self.calibrationLocationBox, 1) self.stack_Vbox.addLayout(self.stack_Hbox1) self.modeGroup.addButton(self.wavAnalysisChk) self.modeGroup.addButton(self.calibrationCurveChk) self.modeGroup.setExclusive(True) # Outputs self.outputCtrlBox = QGroupBox('Outputs') self.outputCtrlBox.setLayout(self.stack_Hbox2) self.stack_Vbox.addWidget(self.outputCtrlBox) self.toCSVchk = QCheckBox('.csv') self.toJSONchk = QCheckBox('.json') self.toCSVchk.stateChanged.connect(lambda _: self.update_settings( 'output', 'toCSV', self.toCSVchk.isChecked())) self.toJSONchk.stateChanged.connect(lambda _: self.update_settings( 'output', 'toJSON', self.toJSONchk.isChecked())) self.stack_Hbox2.addWidget(self.toCSVchk) self.stack_Hbox2.addWidget(self.toJSONchk) self.stack_Vbox.addLayout(self.stack_Hbox2) self.stack.addWidget(self.dataTable) self.stack.addWidget(self.batchCtrlBox) self.stack.setCurrentWidget(self.dataTable) self.stack.show() self.batchAnalyzeChk.stateChanged.connect(self.toggle_stack) self.batchAnalyzeChk.setChecked(False) self.stack_Vbox.addStretch() ## PROCESSING CONTROLS ## self.processControls = QGroupBox('Signal Processing') self.tOffsetSlider = QSlider(Qt.Horizontal, ) self.tOffsetSlider.setMinimum(1) self.tOffsetSlider.setMaximum(100) self.tOffsetSlider.setValue(100) self.tOffsetSlider.setTickPosition(QSlider.TicksBelow) self.tOffsetSlider.setTickInterval(10) self.tOffsetSlider.valueChanged.connect( lambda val: self.update_settings('processing', 'tChop', val)) self.tOffsetLayout = QHBoxLayout() self.tOffsetSlider_Box = QGroupBox( f'Chop Signal - {self.tOffsetSlider.value()}%') self.tOffsetSlider.valueChanged.connect( lambda val: self.tOffsetSlider_Box.setTitle(f'Chop Signal - {val}%' )) self.tOffsetSlider_Box.setLayout(self.tOffsetLayout) self.tOffsetLayout.addWidget(self.tOffsetSlider) self.nFFTSlider = QSlider(Qt.Horizontal, ) self.nFFTSlider.setMinimum(1) self.nFFTSlider.setMaximum(16) self.nFFTSlider.setValue(1) self.nFFTSlider.setTickPosition(QSlider.TicksBelow) self.nFFTSlider.setTickInterval(2) self.nFFTSlider.valueChanged.connect( lambda val: self.update_settings('processing', 'detail', val)) self.nFFTLayout = QHBoxLayout() self.nFFTSlider.valueChanged.connect( lambda val: self.nFFTSlider_Box.setTitle(f'FFT Size - {val*65536}' )) self.nFFTSlider_Box = QGroupBox( f'FFT Size - {self.nFFTSlider.value()*65536}') self.nFFTSlider_Box.setLayout(self.nFFTLayout) self.nFFTLayout.addWidget(self.nFFTSlider) self.rVbox2.addWidget(self.tOffsetSlider_Box) self.rVbox2.addWidget(self.nFFTSlider_Box) self.processControls.setLayout(self.rVbox2) self.lVbox.addLayout(self.lHbox_top, 1) self.lVbox.addWidget(self.fileTree, 7) self.lVbox.addLayout(self.lHbox, 1) self.lHbox.addWidget(self.analyzeBtn, 2) self.lHbox.addWidget(self.batchAnalyzeChk, 1) self.vSplit.addWidget(self.canvas) self.vSplit.addWidget(self.hSplit) self.rVbox.addWidget(self.vSplit) self.hSplit.addWidget(self.stack) self.hSplit.addWidget(self.processControls) self.settings = { 'processing': { 'tChop': self.tOffsetSlider.value(), 'detail': self.nFFTSlider.value() }, 'output': { 'toCSV': self.toCSVchk.isChecked(), 'toJSON': self.toJSONchk.isChecked() } } self.setLayout(self.mainLayout) def on_browse(self): # Browse to file tree root directory options = QFileDialog.Options() path = QFileDialog.getExistingDirectory( self, caption="Choose root directory", options=options) self.rootDirEdit.setText(path) self.fileTree.setRootIndex(self.fileModel.index(path)) def on_edit_root(self): # Update the file tree root directory self.fileTree.setRootIndex( self.fileModel.index(self.rootDirEdit.text())) def update_settings(self, category, setting, value): # Update settings and reprocess FFT if in single analysis mode self.settings[category][setting] = value if category == 'processing' and self.fileTree.selectedIndexes(): self.analyze(self.fileTree.selectedIndexes()) def toggle_stack(self, state): if state == 2: self.stack.setCurrentWidget(self.batchCtrlBox) self.fileTree.setSelectionMode(QAbstractItemView.MultiSelection) else: self.stack.setCurrentWidget(self.dataTable) self.fileTree.setSelectionMode(QAbstractItemView.SingleSelection) def analyze(self, filePaths): if self.batchAnalyzeChk.isChecked(): if self.wavAnalysisChk.isChecked(): self.batch_analyze_wav( [self.fileModel.filePath(path) for path in filePaths[::4]]) if self.calibrationCurveChk.isChecked(): self.generate_calibration_curve( [self.fileModel.filePath(path) for path in filePaths[::4]]) else: if os.path.isdir(self.fileModel.filePath( filePaths[0])) or len(filePaths) > 4: QMessageBox.information( self, 'Error', 'Please select only 1 file for single analysis.') return self.single_analyze_wav(self.fileModel.filePath(filePaths[0])) def single_analyze_wav(self, filePath): """ Do an FFT and find peaks on a single wav file :param filePath: file path to .wav file """ tChopped, vChopped, fVals,\ powerFFT, peakFreqs, peakAmps = Utils.AnalyzeFFT(filePath, tChop=self.settings['processing']['tChop'], detail=self.settings['processing']['detail']) self.analyzeDone.emit(tChopped, vChopped, fVals, powerFFT, peakFreqs, peakAmps, filePath) self.update_table(peakFreqs, peakAmps) def batch_analyze_wav(self, filePaths): """ Perform a batch analysis of many .wav files. Outputs FFTs and peaks in .csv or .json format :param filePaths: A list of folders containing the .wav files to be analyzed """ toCSV = self.settings['output']['toCSV'] toJSON = self.settings['output']['toJSON'] start = time.time() fileTotal = 0 for path in filePaths: if os.path.isdir(path): blockName = os.path.basename(path) print(f'Block: {blockName}') files = [ os.path.join(path, file) for file in os.listdir(path) if '.wav' in file ] fileTotal += len(files) if toCSV: if not os.path.exists(os.path.join(path, 'fft_results_csv')): os.makedirs(os.path.join(path, 'fft_results_csv')) resultFilePath = os.path.join(path, 'fft_results_csv') print('Processing FFTs...') with multiprocessing.Pool(processes=4) as pool: results = pool.starmap( Utils.AnalyzeFFT, zip(files, itertools.repeat(True), itertools.repeat(True))) results = [ result for result in results if result is not None ] peaks = [result[0] for result in results] ffts = [result[1] for result in results] print('Writing to .csv...') resultFileName = os.path.join(resultFilePath, f'{blockName}_Peaks.csv') peakFrames = pd.concat(peaks) peakFrames.to_csv(resultFileName, index=False, header=True) with concurrent.futures.ThreadPoolExecutor( max_workers=16) as executor: executor.map(self.multi_csv_write, ffts) if toJSON: if not os.path.exists( os.path.join(path, 'fft_results_json')): os.makedirs(os.path.join(path, 'fft_results_json')) print(os.path.join(path, 'fft_results_json')) print('Processing FFTs...') with multiprocessing.Pool(processes=4) as pool: results = pool.starmap( Utils.AnalyzeFFT, zip(files, itertools.repeat(True), itertools.repeat(False), itertools.repeat(True))) results = [ result for result in results if result is not None ] print('Writing to .json...') with concurrent.futures.ThreadPoolExecutor( max_workers=16) as executor: executor.map(self.multi_json_write, results) end = time.time() print( f'**Done!** {len(filePaths)} blocks with {fileTotal} files took {round(end-start, 1)}s' ) def generate_calibration_curve(self, filePaths): """ Attempt to fit an exponential function to a set of data points (x: Peak Frequency, y: Compressive strength) provided in JSON format. ex:{ "shape": "2-Hole", "testData": { "location": "1", "strength": 3.092453552, "peaks": [ { "frequency": 1134.5561082797967, "magnitude": 0.349102384777402 }] }, "waveData": [...], "freqData": [...] } Plot the curve, data points and give the function if successful. ** NOTE ** This function is still experimental and a bit buggy. Sometimes the scipy.optimize curve_fit won't converge with the initial guess given for the coeffecients. You're probably better off writing your own code. :param filePaths: A list of folders containing .jsons """ # Strike Location location = self.calibrationLocationBox.currentText() # Function to fit to the data exp_f = lambda x, a, b, c: a * np.exp(b * x) + c # Threaded method for opening all the .jsons and fitting calibCurve = ThreadedCalibrationCurve(filePaths, location, exp_f) progressDialog = QProgressDialog( f'Gettings samples for location: {location}', None, 0, len(filePaths), self) progressDialog.setModal(True) calibCurve.blocksSearched.connect(progressDialog.setValue) try: peakFreqs, strengths, popt, pcov, fitX = calibCurve.run() except Exception as e: QMessageBox.information(self, 'Error', e) return # Calculate R Squared residuals = strengths - exp_f(peakFreqs, *popt) ss_res = np.sum(residuals**2) ss_tot = np.sum((strengths - np.mean(strengths))**2) r_squared = 1 - (ss_res / ss_tot) # Plot Results fig = Figure() plt.scatter(peakFreqs, strengths) plt.plot(fitX, exp_f(fitX, *popt), '-k') ax = plt.gca() plt.text( 0.05, 0.9, f'y = {round(popt[0],3)}*exp({round(popt[1], 5)}x) + {round(popt[2], 3)}\n', ha='left', va='center', transform=ax.transAxes) plt.text(0.05, 0.85, f'R^2 = {round(r_squared,3)}', ha='left', va='center', transform=ax.transAxes) plt.title(f'Calibration Curve, Location: {location}') plt.xlabel('Frequency (Hz)') plt.ylabel('Compressive Strength (MPa)') plt.show() def multi_csv_write(self, frameTuple): frame = frameTuple[1] wavPath = frameTuple[0] resultFileDir = os.path.join(os.path.dirname(wavPath), 'fft_results_csv') resultFileName = os.path.basename(wavPath) + '_fft.csv' resultFilePath = os.path.join(resultFileDir, resultFileName) frame.to_csv(resultFilePath, index=False, header=True) def multi_json_write(self, results): data = results[0] wavPath = results[1] jsonFileDir = os.path.join(os.path.dirname(wavPath), 'fft_results_json') resultFileName = os.path.basename(wavPath) + '_fft.json' resultFilePath = os.path.join(jsonFileDir, resultFileName) # blockName = os.path.basename(os.path.dirname(wavPath)) # blockDir = os.path.join(jsonFileDir, blockName) # if not os.path.exists(blockDir): # os.makedirs(blockDir) # print(resultFilePath) with open(resultFilePath, 'w') as f: json.dump(data, f, indent=2) def update_table(self, peakFreqs, peakAmps): """ :param peakFreqs: :param peakAmps: :return: """ self.dataTable.setRowCount(2) self.dataTable.setColumnCount(len(peakFreqs) + 1) self.dataTable.setItem(0, 0, QTableWidgetItem("Frequencies: ")) self.dataTable.setItem(1, 0, QTableWidgetItem("Powers: ")) for col, freq in enumerate(peakFreqs, start=1): self.dataTable.setItem(0, col, QTableWidgetItem(str(round(freq)))) for col, power in enumerate(peakAmps, start=1): item = QTableWidgetItem(str(round(power, 3))) if power > 0.7: item.setBackground(QColor(239, 81, 28)) elif power >= 0.4: item.setBackground(QColor(232, 225, 34)) elif power < 0.4: item.setBackground(QColor(113, 232, 34)) self.dataTable.setItem(1, col, item)
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 = '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 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)
def displayFiles( self ): #show the xls files on the treeview for the selected library """ called when a library is selected """ #check we have a selection if len(self.tableView.selectionModel().selectedRows()) == 0: return log = self.logger.getChild('displayFiles') #======================================================================= # retrieve selection #======================================================================= #get the selection index """should only allow 1 row.. but taking the first regardless""" sindex = self.tableView.selectionModel().selectedRows()[0] row = sindex.row() #get this value libName = self.dfModel.data(self.dfModel.index(row, 0)) #log.debug('user selected \'%s\''%libName) #======================================================================= # data setup #======================================================================= focus_dir = self.vdata_d[libName]['basedir'] #focus_dir = r'C:\LS\03_TOOLS\CanFlood\_git\canflood\_pars\vfunc' #======================================================================= # #build the model #======================================================================= assert os.path.exists(focus_dir) fsModel = QFileSystemModel() fsModel.setRootPath(focus_dir) fsModel.setNameFilters(['*.xls']) self.fsModel = fsModel #======================================================================= # #tree view #======================================================================= self.treeView.setModel(fsModel) self.treeView.setRootIndex(fsModel.index(focus_dir)) log.debug('connected treeView to QFileSystemModel w/: \n %s' % focus_dir) #adjust columns header = self.treeView.header() header.setSectionResizeMode(0, QHeaderView.ResizeToContents) header.setStretchLastSection(False) #self.treeView.resizeColumnToContents(0) #======================================================================= # connect it #======================================================================= self.treeView.selectionModel().selectionChanged.connect( self.dislpayCsDetails) """ if not self.dfModel3 is None: self.dfModel3.clear() #self.tableView_bottomRight.clearSpans() #clear the table view until next trigger """ try: #cleanup the model self.tableView_bottomRight.setModel(pandasModel( pd.DataFrame())) #set a dummy model del self.dfModel3 except: pass
class ApplicationWindow(QtWidgets.QMainWindow): def __init__(self): super(ApplicationWindow, self).__init__() self.setAttribute(QtCore.Qt.WA_DeleteOnClose, True) self.ui = Ui_MainWindow() self.ui.setupUi(self) self._translate = self.ui.retranslateUi(self) self.ui.tabWidget.clear() self.model = QFileSystemModel() self.model.setRootPath(QtCore.QDir().rootPath()) # self.model.setFilter(QtCore.QDir().AllDirs|QtCore.QDir().NoDot) self.model.setNameFilters(filters) source = self.model.index(QtCore.QDir().homePath()) self.proxyModel = QtCore.QSortFilterProxyModel(self) self.proxyModel.setSourceModel(self.model) self.proxyModel.setDynamicSortFilter(True) index = self.proxyModel.mapFromSource(source) self.ui.treeView.setModel(self.model) self.ui.treeView.setRootIndex(source) # self.ui.treeView.setModel(self.proxyModel) # self.ui.treeView.setRootIndex(index) # self.ui.treeView.setRootIndex(self.model.index(user_home())) # self.ui.treeView.setModel(self.proxyModel) # self.ui.treeView.setRootIndex(self.proxyModel) [self.ui.treeView.setColumnHidden(cols, True) for cols in range(1, 4)] self.ui.treeView.doubleClicked.connect(self.onClick) self.ui.treeView.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) self.ui.treeView.customContextMenuRequested.connect( self.create_popup_menu) self.ui.filedirsearch.textChanged.connect(self.textFilter) self.ui.actionOpenProject.triggered.connect(self.openProjDialog) self.ui.actionOpenFile.triggered.connect(self.openFileDialog) self.ui.actionAbout_Qt.triggered.connect(self.aboutQt) self.ui.actionQuit.triggered.connect(self.close) self.ui.action_Save.triggered.connect(self.save) self.ui.tabWidget.tabCloseRequested.connect(self.removeTab) self.textFilter() def add_cb(self): print "add callback" def new_file(self): pass def load_yaml_to_gui(self, path): self.ui.tabWidget.addTab(self.addXCCDFTab(path), os.path.basename(path)) data = open_yaml(path) if data is not None: if "title" in data: self.ui.txtTitle.setText(data['title']) if "description" in data: self.ui.txtDesc.setPlainText(data['description']) if path.endswith(".rule"): if "severity" in data: self.ui.severityComboBox.setCurrentText( data['severity'].title()) if "ocil_clause" in data: self.ui.txtOCILclause.setText(data['ocil_clause']) if "ocil" in data: self.ui.txtOCIL.setPlainText(data['ocil']) if "rationale" in data: self.ui.txtRationale.setPlainText(data['rationale']) header = self.ui.identsTable header.setHorizontalHeaderLabels(["OS", "CCE"]) header.setColumnCount(3) header.verticalHeader().hide() header.setHorizontalHeaderItem( 0, QtWidgets.QTableWidgetItem("Identifier Type")) header.setHorizontalHeaderItem( 1, QtWidgets.QTableWidgetItem("Operating System")) header.setHorizontalHeaderItem( 2, QtWidgets.QTableWidgetItem("Identifier")) header.setColumnWidth(0, 137) header.setColumnWidth(1, 160) header.setColumnWidth(2, 137) header.insertRow(header.rowCount()) if "identifiers" in data: for row, keys in enumerate(data["identifiers"].keys()): val = data["identifiers"][keys] if "@" in keys: ident_type = keys.split("@")[0] os_type = keys.split("@")[1] else: ident_type = keys os_type = "All" header.setItem(row, 0, QtWidgets.QTableWidgetItem(ident_type)) header.setItem(row, 1, QtWidgets.QTableWidgetItem(os_type)) header.setItem(row, 2, QtWidgets.QTableWidgetItem(val)) header.insertRow(header.rowCount()) # header.doubleClicked.connect() self.ui.severityComboBox.activated.connect(self.onChange) self.ui.txtOCIL.textChanged.connect(self.onChange) self.ui.txtOCILclause.textChanged.connect(self.onChange) self.ui.txtRationale.textChanged.connect(self.onChange) # if path.endswith(".profile"): # tv = self.ui.tableView # header = ['rule_selection'] # tm = MyTableModel(self.tabledata, header, self) # tv.setModel(tm) # # # hide grid ## tv.setShowGrid(True) # # # set the font # font = QFont("Courier New", 8) # tv.setFont(font) # # # hide vertical header # vh = tv.verticalHeader() # vh.setVisible(False) # # # set horizontal header properties # hh = tv.horizontalHeader() # hh.setStretchLastSection(True) # # # set column width to fit contents # tv.resizeColumnsToContents() # # # set row height # nrows = len(self.tabledata) # for row in xrange(nrows): # tv.setRowHeight(row, 18) # ## # enable sorting # tv.setSortingEnabled(True) # # self.model = QtGui.QStandardItemModel(parent=self) # self.model.setHorizontalHeaderLabels(['Source', 'Destination', 'Protoco', 'Info']) # self.setModel(self.model) # self.setColumnWidth(0, 120) # self.setColumnWidth(1, 120) # self.setColumnWidth(2, 100) # self.setColumnWidth(3, 350) # self.setAlternatingRowColors(True) # self.setAutoScroll(True) # self.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows) # self.setEditTriggers(QtGui.QTableView.NoEditTriggers) # self.setSelectionMode(QtGui.QTableView.SingleSelection) # for selection in data["selections"]: # tv.setItem(selection) # # # self.ui.txtDesc.textChanged.connect(self.onChange) self.ui.txtTitle.textChanged.connect(self.onChange) def addXCCDFTab(self, path): self.ui.tab = QtWidgets.QWidget() _translate = QtCore.QCoreApplication.translate self.ui.tab.setObjectName("tab") self.ui.formLayoutWidget = QtWidgets.QWidget(self.ui.tab) self.ui.formLayoutWidget.setGeometry(QtCore.QRect(10, 10, 501, 541)) self.ui.formLayoutWidget.setObjectName("formLayoutWidget") self.ui.scap = QtWidgets.QFormLayout(self.ui.formLayoutWidget) self.ui.scap.setContentsMargins(1, 1, 1, 1) self.ui.scap.setObjectName("scap") self.ui.lblscap = QtWidgets.QLabel(self.ui.formLayoutWidget) self.ui.lblscap.setObjectName("lblscap") self.ui.scap.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.ui.lblscap) self.ui.txtTitle = QtWidgets.QLineEdit(self.ui.formLayoutWidget) self.ui.txtTitle.setObjectName("txtTitle") self.ui.scap.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.ui.txtTitle) self.ui.lbldesc = QtWidgets.QLabel(self.ui.formLayoutWidget) self.ui.lbldesc.setObjectName("lbldesc") self.ui.scap.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.ui.lbldesc) self.ui.txtDesc = QtWidgets.QPlainTextEdit(self.ui.formLayoutWidget) self.ui.txtDesc.setObjectName("txtDesc") self.ui.scap.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.ui.txtDesc) self.ui.lblscap.setText(_translate("MainWindow", "Title")) self.ui.lbldesc.setText(_translate("MainWindow", "Description")) if path.endswith(".rule"): self.ui.severityLabel = QtWidgets.QLabel(self.ui.formLayoutWidget) self.ui.severityLabel.setObjectName("severityLabel") self.ui.scap.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.ui.severityLabel) self.ui.severityComboBox = QtWidgets.QComboBox( self.ui.formLayoutWidget) self.ui.severityComboBox.setObjectName("severityComboBox") self.ui.scap.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.ui.severityComboBox) self.ui.identsLabel = QtWidgets.QLabel(self.ui.formLayoutWidget) self.ui.identsLabel.setObjectName("identsLabel") self.ui.scap.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.ui.identsLabel) self.ui.identsTable = QtWidgets.QTableWidget( self.ui.formLayoutWidget) self.ui.identsTable.setObjectName("identsTable") self.ui.scap.setWidget(3, QtWidgets.QFormLayout.FieldRole, self.ui.identsTable) self.ui.oCILClauseLabel = QtWidgets.QLabel( self.ui.formLayoutWidget) self.ui.oCILClauseLabel.setObjectName("oCILClauseLabel") self.ui.scap.setWidget(4, QtWidgets.QFormLayout.LabelRole, self.ui.oCILClauseLabel) self.ui.txtOCILclause = QtWidgets.QLineEdit( self.ui.formLayoutWidget) self.ui.txtOCILclause.setObjectName("txtOCILclause") self.ui.scap.setWidget(4, QtWidgets.QFormLayout.FieldRole, self.ui.txtOCILclause) self.ui.oCILLabel = QtWidgets.QLabel(self.ui.formLayoutWidget) self.ui.oCILLabel.setObjectName("oCILLabel") self.ui.scap.setWidget(5, QtWidgets.QFormLayout.LabelRole, self.ui.oCILLabel) self.ui.txtOCIL = QtWidgets.QPlainTextEdit( self.ui.formLayoutWidget) self.ui.txtOCIL.setObjectName("txtOCIL") self.ui.scap.setWidget(5, QtWidgets.QFormLayout.FieldRole, self.ui.txtOCIL) self.ui.oRationaleLabel = QtWidgets.QLabel( self.ui.formLayoutWidget) self.ui.oRationaleLabel.setFrameShape(QtWidgets.QFrame.NoFrame) self.ui.oRationaleLabel.setObjectName("oRationaleLabel") self.ui.scap.setWidget(6, QtWidgets.QFormLayout.LabelRole, self.ui.oRationaleLabel) self.ui.txtRationale = QtWidgets.QPlainTextEdit( self.ui.formLayoutWidget) self.ui.txtRationale.setObjectName("txtRationale") self.ui.scap.setWidget(6, QtWidgets.QFormLayout.FieldRole, self.ui.txtRationale) self.ui.identsLabel.setText( _translate("MainWindow", "Indentifiers")) self.ui.severityLabel.setText(_translate("MainWindow", "Severity")) self.ui.oCILClauseLabel.setText( _translate("MainWindow", "OCIL Clause")) self.ui.oCILLabel.setText(_translate("MainWindow", "OCIL")) self.ui.oRationaleLabel.setText( _translate("MainWindow", "Rationale")) [self.ui.severityComboBox.addItem(sevs) for sevs in severity] if path.endswith(".profile"): self.ui.selectionLabel = QtWidgets.QLabel(self.ui.formLayoutWidget) self.ui.selectionLabel.setObjectName("selectionLabel") self.ui.scap.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.ui.selectionLabel) self.ui.selectionLabel.setText( _translate("MainWindow", "Rule Selection")) return self.ui.tab def onClick(self, index): path = self.sender().model().filePath(index) if not self.sender().model().isDir(index): self.load_yaml_to_gui(path) else: # if path.endswith(".."): # path = path.rsplit("/", 2)[0] # print index self.ui.treeView.setRootIndex(index) def openProjDialog(self): dirname = QFileDialog() dirname.setFileMode(QFileDialog.Directory) dirname = QFileDialog.getExistingDirectory( self, "Open Project Folder", options=QFileDialog.DontUseNativeDialog | QFileDialog.HideNameFilterDetails | QFileDialog.ShowDirsOnly) if dirname != "": self.setDirectory(dirname) else: self.setDirectory(QtCore.QDir().homePath()) def openFileDialog(self): fname = QFileDialog() fname.setFileMode(QFileDialog.Directory) fname = QFileDialog.getOpenFileNames( self, "Select File to Open", QtCore.QDir().homePath(), "All (*.rule *.group *.profile);;Rules ( *.rule);;Groups (*.group);;Profiles (*.profile)", options=QFileDialog.DontUseNativeDialog) path = "".join(fname[0]) if path: self.load_yaml_to_gui(path) def saveFileDialog(self, path): quit_msg = "Save changes before closing?" reply = QtWidgets.QMessageBox.question(self, 'Message', quit_msg, QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.No) if reply == QtWidgets.QMessageBox.Yes: yaml_dict = UnsortableOrderedDict([ ("documentation_complete", "true"), ]) yaml_dict["title"] = self.ui.txtTitle.text() yaml_dict["description"] = self.ui.txtDesc.toPlainText() if path.endswith("*.rule"): yaml_dict["severity"] = self.ui.severityComboBox.currentText( ).lower() yaml_dict["ocil_clause"] = self.ui.txtOCILclause.text() yaml_dict["ocil"] = self.ui.txtOCIL.toPlainText() yaml_dict["rationale"] = self.ui.txtRationale.toPlainText() write_yaml(yaml_dict, path.strip("*")) def save(self, index): index = self.ui.tabWidget.currentIndex() filename = self.ui.tabWidget.tabText(index) yaml_dict = UnsortableOrderedDict([ ("documentation_complete", "true"), ]) yaml_dict["title"] = self.ui.txtTitle.text() yaml_dict["description"] = self.ui.txtDesc.toPlainText() if filename.endswith(".rule"): yaml_dict["severity"] = self.ui.severityComboBox.currentText( ).lower() yaml_dict["ocil_clause"] = self.ui.txtOCILclause.text() yaml_dict["ocil"] = self.ui.txtOCIL.toPlainText() yaml_dict["rationale"] = self.ui.txtRationale.toPlainText() write_yaml(yaml_dict, self.ui.tabWidget.tabText(index).strip("*")) self.ui.tabWidget.setTabText(index, filename.strip("*")) def setDirectory(self, directory): self.ui.treeView.setModel(self.model) self.ui.treeView.setRootIndex(self.model.index(directory)) def create_popup_menu(self, pos): self.popup_menu = QtWidgets.QMenu() self.popup_menu.addAction("New", self.add_cb) self.popup_menu.addAction("Rename", self.add_cb) self.popup_menu.addSeparator() self.popup_menu.addAction("Delete", self.add_cb) self.popup_menu.exec_(self.ui.treeView.viewport().mapToGlobal(pos)) def on_context_menu(self, pos): node = self.treeWidget.mapToGlobal(pos) self.popup_menu.exec_(self.treeWidget.mapToGlobal(pos)) def textFilter(self): regExp = QtCore.QRegExp(self.ui.filedirsearch.text(), QtCore.Qt.CaseInsensitive) self.proxyModel.setFilterRegExp(regExp) def removeTab(self, index): if self.ui.tabWidget.tabText(index).startswith("*"): self.saveFileDialog(self.ui.tabWidget.tabText(index)) self.ui.tabWidget.removeTab(index) def onChange(self): index = self.ui.tabWidget.currentIndex() filename = self.ui.tabWidget.tabText(index) if not filename.startswith("*"): filename = "*" + filename self.ui.tabWidget.setTabText(index, filename) def aboutQt(self): QMessageBox.aboutQt(self)
class FileChooser(QWidget): fileOpened = pyqtSignal(str) def __init__(self, parent=None): super().__init__(parent) # TODO: migrate to FolderComboBox? self.folderBox = QComboBox(self) self.explorerTree = FileTreeView(self) self.explorerTree.doubleClickCallback = self._fileOpened self.explorerModel = QFileSystemModel(self) self.explorerModel.setFilter(QDir.AllDirs | QDir.Files | QDir.NoDotAndDotDot) self.explorerModel.setNameFilters(["*.py"]) self.explorerModel.setNameFilterDisables(False) self.explorerTree.setModel(self.explorerModel) for index in range(1, self.explorerModel.columnCount()): self.explorerTree.hideColumn(index) self.setCurrentFolder() self.folderBox.currentIndexChanged[int].connect( self.updateCurrentFolder) layout = QVBoxLayout(self) layout.addWidget(self.folderBox) layout.addWidget(self.explorerTree) layout.setContentsMargins(5, 5, 0, 0) def _fileOpened(self, modelIndex): path = self.explorerModel.filePath(modelIndex) if os.path.isfile(path): self.fileOpened.emit(path) def currentFolder(self): return self.explorerModel.rootPath() def setCurrentFolder(self, path=None): if path is None: app = QApplication.instance() path = app.getScriptsDirectory() else: assert os.path.isdir(path) self.explorerModel.setRootPath(path) self.explorerTree.setRootIndex(self.explorerModel.index(path)) self.folderBox.blockSignals(True) self.folderBox.clear() style = self.style() dirIcon = style.standardIcon(style.SP_DirIcon) self.folderBox.addItem(dirIcon, os.path.basename(path)) self.folderBox.insertSeparator(1) self.folderBox.addItem(self.tr("Browse…")) self.folderBox.setCurrentIndex(0) self.folderBox.blockSignals(False) def updateCurrentFolder(self, index): if index < self.folderBox.count() - 1: return path = QFileDialog.getExistingDirectory(self, self.tr("Choose Directory"), self.currentFolder(), QFileDialog.ShowDirsOnly) if path: QSettings().setValue("scripting/path", path) self.setCurrentFolder(path)
class Chapterize(QMainWindow): def __init__(self): super().__init__() self.ui = Ui_ChapterizeWindow() self.ui.setupUi(self) self.dir = QDir.homePath() self.chaptersTableModel = ChaptersTableModel() self.ui.chapterTable.setModel(self.chaptersTableModel) self.ui.chapterTable.setItemDelegate(TimestampDelegate()) self.ui.chapterTable.contextMenuEvent = self.onChapterContextMenu self.mp3ListModel = QFileSystemModel() self.mp3ListModel.setNameFilters(['*.mp3']) self.mp3ListModel.setFilter(QDir.Files) self.mp3ListModel.setRootPath(self.dir) self.mp3ListModel.setNameFilterDisables(False) self.ui.mp3List.setModel(self.mp3ListModel) self.ui.mp3List.setRootIndex(self.mp3ListModel.index(self.dir)) self.ui.mp3List.selectionModel().selectionChanged.connect(self.onMp3Select) self.ui.actionChangeDir.triggered.connect(self.onDirectoryChange) self.ui.actionExit.triggered.connect(self.close) self.ui.actionSave.triggered.connect(self.chaptersTableModel.save) @pyqtSlot() def onDirectoryChange(self): chosen_dir = QFileDialog.getExistingDirectory(self, "Change Directory...", self.dir, QFileDialog.ShowDirsOnly) if chosen_dir != '': self.dir = chosen_dir self.mp3ListModel.setRootPath(self.dir) self.ui.mp3List.setRootIndex(self.mp3ListModel.index(self.dir)) @pyqtSlot(QItemSelection, QItemSelection) def onMp3Select(self, selected, deselected): sel_idx = selected.indexes()[0] pth = self.mp3ListModel.fileInfo(sel_idx).absoluteFilePath() self.chaptersTableModel.set_file(pth) @pyqtSlot(QContextMenuEvent) def onChapterContextMenu(self, e): idx = self.ui.chapterTable.indexAt(e.pos()) row = idx.row() if row < 0: row = self.chaptersTableModel.rowCount() menu = QMenu(self) insertAction = QAction('&Insert', menu) insertAction.triggered.connect(lambda: self.chaptersTableModel.insertRow(row)) menu.addAction(insertAction) deleteAction = QAction('&Delete', menu) deleteAction.triggered.connect(lambda: self.chaptersTableModel.removeRow(row)) menu.addAction(deleteAction) menu.exec_(e.globalPos())