Example #1
0
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)
Example #2
0
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()

        r_inx = self.model.setRootPath(".")
        self.tree = QTreeView()
        self.tree.setModel(self.model)
        self.tree.setAnimated(False)
        self.tree.setIndentation(15)
        self.tree.setSortingEnabled(True)
        self.tree.setWindowTitle("Dir View")
        self.tree.resize(640, 480)
        windowLayout = QVBoxLayout()
        windowLayout.addWidget(self.tree)
        self.setLayout(windowLayout)
        print(self.model.rootPath())
        print(self.model.filePath(self.model.parent(r_inx)))
        index_temp = self.model.index(0, 0, QModelIndex())
        print(self.model.fileName(index_temp))
        self.show()
Example #3
0
class FolderView(QWidget):
    movieFound = pyqtSignal(list)

    def __init__(self, config):
        super().__init__()
        self.config = config['FolderView']
        self.resize(200, 400)
        self.setMaximumWidth(200)
        self.setMinimumWidth(150)

        layout = QVBoxLayout()
        layout.setContentsMargins(0, 0, 0, 0)

        self.currPath = QLineEdit()
        self.model = QFileSystemModel()
        self.model.rootPathChanged.connect(self.currPath.setText)
        self.folderList = FolderList(self.config, parent=self)
        self.folderList.setModel(self.model)
        self.folderList.changeDir(self.config.get('currdir', ''))

        layout.addWidget(self.currPath)
        layout.addWidget(self.folderList)
        self.setLayout(layout)

    def setCurrentIndex(self, index):
        self.folderList.setCurrentIndex(index)
        self.folderList.onListClicked(index)

    def getPath(self, index = None):
        if not index: index = self.folderList.currentIndex()
        return self.model.filePath(index)

    def rootPath(self):
        return self.model.rootPath()

    def getSelectedIndexes(self):
        return self.folderList.selectedIndexes()

    def getFiles(self, index):
        return self.folderList.getFiles(index)

    def playFile(self):
        index = self.folderList.currentIndex()
        if not index.isValid():
            return

        import glob
        exts = ('*.mp4', '*.mkv', '*.avi', '*.wmv')
        files = []
        path = self.model.filePath(index)
        #print(path)
        for ext in exts:
            files.extend(glob.glob('%s/%s'%(path, ext)))
        if len(files) < 1: return

        files.sort()
        os.startfile(files[0])
Example #4
0
class E5FileCompleter(QCompleter):
    """
    Class implementing a completer for file names.
    """
    def __init__(self,
                 parent=None,
                 completionMode=QCompleter.PopupCompletion,
                 showHidden=False):
        """
        Constructor
        
        @param parent parent widget of the completer (QWidget)
        @keyparam completionMode completion mode of the
            completer (QCompleter.CompletionMode)
        @keyparam showHidden flag indicating to show hidden entries as well
            (boolean)
        """
        super(E5FileCompleter, self).__init__(parent)
        self.__model = QFileSystemModel(self)
        if showHidden:
            self.__model.setFilter(
                QDir.Filters(QDir.Dirs | QDir.Files | QDir.Drives
                             | QDir.AllDirs | QDir.Hidden))
        else:
            self.__model.setFilter(
                QDir.Filters(QDir.Dirs | QDir.Files | QDir.Drives
                             | QDir.AllDirs))
        self.__model.setRootPath("")
        self.setModel(self.__model)
        self.setCompletionMode(completionMode)
        if isWindowsPlatform():
            self.setCaseSensitivity(Qt.CaseInsensitive)
        if parent:
            parent.setCompleter(self)

    def setRootPath(self, path):
        """
        Public method to set the root path of the model.
        
        @param path root path for the model
        @type str
        """
        if not os.path.isdir(path):
            path = os.path.dirname(path)
        self.__model.setRootPath(path)

    def rootPath(self):
        """
        Public method to get the root path of the model.
        
        @return root path of the model
        @rtype str
        """
        return self.__model.rootPath()
Example #5
0
class E5FileCompleter(QCompleter):
    """
    Class implementing a completer for file names.
    """
    def __init__(self, parent=None,
                 completionMode=QCompleter.PopupCompletion,
                 showHidden=False):
        """
        Constructor
        
        @param parent parent widget of the completer (QWidget)
        @keyparam completionMode completion mode of the
            completer (QCompleter.CompletionMode)
        @keyparam showHidden flag indicating to show hidden entries as well
            (boolean)
        """
        super(E5FileCompleter, self).__init__(parent)
        self.__model = QFileSystemModel(self)
        if showHidden:
            self.__model.setFilter(
                QDir.Filters(QDir.Dirs | QDir.Files | QDir.Drives |
                             QDir.AllDirs | QDir.Hidden))
        else:
            self.__model.setFilter(QDir.Filters(
                QDir.Dirs | QDir.Files | QDir.Drives | QDir.AllDirs))
        self.__model.directoryLoaded.connect(self.complete)
        self.__model.setRootPath("")
        self.setModel(self.__model)
        self.setCompletionMode(completionMode)
        if isWindowsPlatform():
            self.setCaseSensitivity(Qt.CaseInsensitive)
        if parent:
            parent.setCompleter(self)
    
    def setRootPath(self, path):
        """
        Public method to set the root path of the model.
        
        @param path root path for the model
        @type str
        """
        if not os.path.isdir(path):
            path = os.path.dirname(path)
        self.__model.setRootPath(path)
    
    def rootPath(self):
        """
        Public method to get the root path of the model.
        
        @return root path of the model
        @rtype str
        """
        return self.__model.rootPath()
Example #6
0
    def load_dir(self, dir_path):
        if dir_path == self.dir_path:
            return

        self.dir_path = dir_path

        model = QFileSystemModel()
        model.setRootPath(dir_path)
        self.setModel(model)

        index_root = model.index(model.rootPath())
        self.setRootIndex(index_root)

        # hide unwanted info
        self.hideColumn(1)
        self.hideColumn(2)
        self.hideColumn(3)
        self.setHeaderHidden(True)
Example #7
0
class Example(QMainWindow):
    def __init__(self):
        super().__init__()

        self.initUI()

    def initUI(self):

        self.central_widget = QWidget()
        self.setCentralWidget(self.central_widget)

        self.folderLayout = QWidget()

        self.pathRoot = QDir.rootPath()

        self.dirmodel = QFileSystemModel(self)
        self.dirmodel.setRootPath(QDir.currentPath())

        self.indexRoot = self.dirmodel.index(self.dirmodel.rootPath())

        self.folder_view = QTreeView()
        self.folder_view.setDragEnabled(True)
        self.folder_view.setModel(self.dirmodel)
        self.folder_view.setRootIndex(self.indexRoot)

        self.selectionModel = self.folder_view.selectionModel()

        self.left_layout = QVBoxLayout()
        self.left_layout.addWidget(self.folder_view)

        self.folderLayout.setLayout(self.left_layout)

        splitter_filebrowser = QSplitter(Qt.Horizontal)
        splitter_filebrowser.addWidget(self.folderLayout)
        splitter_filebrowser.addWidget(Figure_Canvas(self))
        splitter_filebrowser.setStretchFactor(1, 1)

        hbox = QHBoxLayout(self)
        hbox.addWidget(splitter_filebrowser)

        self.centralWidget().setLayout(hbox)

        self.setWindowTitle('Simple drag & drop')
        self.setGeometry(0, 0, 1000, 500)
    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
Example #9
0
    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
Example #10
0
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')
Example #11
0
class FileSystemTable(QTableView, TableType):
    Q_ENUMS(TableType)

    transferFileRequest = pyqtSignal(str)
    rootChanged = pyqtSignal(str)

    def __init__(self, parent=None):
        super(FileSystemTable, self).__init__(parent)

        self._table_type = TableType.Local

        # This prevents doing unneeded initialization
        # when QtDesginer loads the plugin.
        if parent is None:
            return

        self.parent = parent
        self.path_data = dict()
        self.doubleClicked.connect(self.changeRoot)
        self.selected_row = None
        self.clipboard = QApplication.clipboard()

        self.model = QFileSystemModel()
        self.model.setReadOnly(True)
        self.model.setFilter(QDir.AllDirs | QDir.NoDotAndDotDot
                             | QDir.AllEntries)

        self.setModel(self.model)

        self.verticalHeader().hide()
        self.horizontalHeader().setStretchLastSection(True)
        self.setAlternatingRowColors(True)
        self.setSelectionBehavior(QAbstractItemView.SelectRows)

        self.info = Info()
        self._nc_file_dir = self.info.getProgramPrefix()
        self.setRootPath(self._nc_file_dir)

    def showEvent(self, event=None):
        self.rootChanged.emit(self._nc_file_dir)

    def changeRoot(self, index):

        path = self.model.filePath(self.rootIndex())
        new_path = self.model.filePath(index)

        absolute_path = os.path.join(path, new_path)

        file_info = QFileInfo(absolute_path)
        if file_info.isDir():
            self.model.setRootPath(absolute_path)
            self.setRootIndex(self.model.index(absolute_path))

            self.rootChanged.emit(absolute_path)

    @pyqtSlot()
    def newFile(self):
        path = self.model.filePath(self.rootIndex())
        new_file = QFile(os.path.join(path, "New File"))
        new_file.open(QIODevice.ReadWrite)

    @pyqtSlot()
    def deleteFile(self):
        index = self.selectionModel().currentIndex()
        path = self.model.filePath(index)
        if path:
            fileInfo = QFileInfo(path)
            if fileInfo.isFile():
                if not self.ask_dialog(
                        "Do you wan't to delete the selected file?"):
                    return
                file = QFile(path)
                file.remove()

            elif fileInfo.isDir():
                if not self.ask_dialog(
                        "Do you wan't to delete the selected directory?"):
                    return
                directory = QDir(path)
                directory.removeRecursively()

    @pyqtSlot()
    def createDirectory(self):
        path = self.model.filePath(self.rootIndex())
        directory = QDir()
        directory.setPath(path)
        directory.mkpath("New Folder")

    @pyqtSlot(str)
    def setRootPath(self, root_path):

        self.rootChanged.emit(root_path)
        self.model.setRootPath(root_path)
        self.setRootIndex(self.model.index(root_path))

        return True

    @pyqtSlot()
    def goUP(self):

        path = self.model.filePath(self.rootIndex())

        file_info = QFileInfo(path)
        directory = file_info.dir()
        new_path = directory.absolutePath()

        currentRoot = self.rootIndex()

        self.model.setRootPath(new_path)
        self.setRootIndex(currentRoot.parent())
        self.rootChanged.emit(new_path)

    @pyqtSlot()
    def doFileTransfer(self):
        index = self.selectionModel().currentIndex()
        path = self.model.filePath(index)
        self.transferFileRequest.emit(path)

    @pyqtSlot(str)
    def transferFile(self, src_path):
        dest_path = self.model.filePath(self.rootIndex())

        src_file = QFile()
        src_file.setFileName(src_path)

        src_file_info = QFileInfo(src_path)

        dst_path = os.path.join(dest_path, src_file_info.fileName())

        src_file.copy(dst_path)

    @pyqtSlot()
    def getSelected(self):
        return self.selected_row

    @pyqtSlot()
    def getCurrentDirectory(self):
        return self.model.rootPath()

    @pyqtProperty(TableType)
    def tableType(self):
        return self._table_type

    @tableType.setter
    def tableType(self, table_type):
        self._table_type = table_type
        if table_type == TableType.Local:
            self.setRootPath(self._nc_file_dir)
        else:
            self.setRootPath('/media/')

    def ask_dialog(self, message):
        box = QMessageBox.question(self.parent, 'Are you sure?', message,
                                   QMessageBox.Yes, QMessageBox.No)
        if box == QMessageBox.Yes:
            return True
        else:
            return False
Example #12
0
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')
Example #13
0
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)
Example #14
0
class MainWindow(QObject):
    """
    Main class that is responsible for the graphical user interface.
    """

    message = pyqtSignal(str)
    login_ok = False

    def __init__(self):
        super(self).__init__()
        self.app = QApplication([])
        self.main_window = QWidget()
        self.api = StravaApi(startWithGui=True)

        self.main_window.setMinimumSize(500, 500)
        self.main_window.setWindowTitle("Strava API")
        self.api.apiMessage.connect(self.write_to_logbox, )

        # Log in --------
        user_info_layout = QHBoxLayout()
        user_info_grid_layout = QGridLayout()
        func_button_layout = QVBoxLayout()

        self.name_label = QLabel()
        self.username_label = QLabel()
        self.gender_label = QLabel()
        self.city_label = QLabel()
        self.country_label = QLabel()
        user_info_grid_layout.addWidget(QLabel("Name: "), 0, 0, Qt.AlignLeft)
        user_info_grid_layout.addWidget(self.name_label, 0, 1, Qt.AlignLeft)
        user_info_grid_layout.addWidget(QLabel("Username: "******"Gender: "), 2, 0, Qt.AlignLeft)
        user_info_grid_layout.addWidget(self.gender_label, 2, 1, Qt.AlignLeft)
        user_info_grid_layout.addWidget(QLabel("City: "), 3, 0, Qt.AlignLeft)
        user_info_grid_layout.addWidget(self.city_label, 3, 1, Qt.AlignLeft)
        user_info_grid_layout.addWidget(QLabel("Country: "), 4, 0,
                                        Qt.AlignLeft)
        user_info_grid_layout.addWidget(self.country_label, 4, 1, Qt.AlignLeft)

        self.client_id = QLineEdit()
        self.pass_edit = QLineEdit()

        athlete_info = QPushButton("Fetch Athlete information")
        athlete_info.clicked.connect(self.populate_athlete_labels)
        latest_run = QPushButton("Latest run")
        latest_run.clicked.connect(self.on_get_latest_activity_clicked)
        summary = QPushButton("Workout summary")
        summary.clicked.connect(self.on_summary_clicked)
        upload_activities = QPushButton("Upload Activities")
        upload_activities.clicked.connect(self.on_upload_activities_clicked)

        func_button_layout.addWidget(athlete_info)
        func_button_layout.addWidget(latest_run)
        func_button_layout.addWidget(summary)
        func_button_layout.addWidget(upload_activities)

        user_info_layout.addLayout(user_info_grid_layout)
        user_info_layout.addLayout(func_button_layout)

        self.activity_directory_input_box = QLineEdit()
        self.file_model = QFileSystemModel()
        self.file_model.setNameFilters(["*.gpx"])
        self.file_model.setNameFilterDisables(False)
        self.file_list_view = QListView()

        # Logger window --------
        logger_group_box = self.build_logger_window()

        self.main_layout = QVBoxLayout()
        self.main_layout.addLayout(user_info_layout)
        self.main_layout.addWidget(logger_group_box)

        self.main_window.setLayout(self.main_layout)
        self.main_window.show()

        self.login_dialog()

    def login_dialog(self):
        try:
            dialog = QDialog()
            dialog.setWindowTitle("Strava authorization")
            layout = QVBoxLayout(dialog)
            login_group_box = self.build_credentials_box()
            layout.addWidget(login_group_box)

            url_label = QLabel()
            url_label.setText(
                "Paste the link in an url and then use the \"code\" value in the response and paste that value in the empty box below. "
            )
            self.url_edit = QTextEdit()
            self.url_edit.setReadOnly(True)
            layout.addWidget(url_label)
            layout.addWidget(self.url_edit)
            login_input = QLineEdit()
            layout.addWidget(login_input)
            button = QPushButton("Ok")
            layout.addWidget(button)

            button.clicked.connect(dialog.accept)
            if (dialog.exec() == QDialog.Accepted):
                self.api.authorize(login_input.text().strip())
                self.write_to_logbox("Login successful!")
        except ValueError:
            error = sys.exc_info()[0]
            self.api.apiMessage(error)

    def build_credentials_box(self):
        login_group_box = QGroupBox("Log in")
        box_layout = QHBoxLayout()
        line_layout = QGridLayout()
        line_layout.addWidget(QLabel("Client Id: "), 0, 0)
        line_layout.addWidget(self.client_id, 0, 1)
        line_layout.addWidget(QLabel("Client Secret: "), 1, 0)
        self.pass_edit.setEchoMode(QLineEdit.Password)
        credential_button = QPushButton("Verify Credentials")
        credential_button.clicked.connect(self.on_credential_button_clicked)
        line_layout.addWidget(self.pass_edit, 1, 1)
        line_layout.addWidget(credential_button, 2, 1)

        cred_tuple = self.api.readDefaultCredentialsFromConfig()
        self.client_id.insert(str(cred_tuple[0]))
        self.pass_edit.insert(str(cred_tuple[1]))

        box_layout.addItem(line_layout)
        login_group_box.setLayout(box_layout)

        return login_group_box

    def on_credential_button_clicked(self):
        self.api.readCredentialsFromGui(self.client_id.text().strip(),
                                        self.pass_edit.text().strip())
        self.url_edit.setText(self.api.buildAuthUrl())

    def build_logger_window(self):
        logger_group_box = QGroupBox("Log")

        self.logBox = QTextEdit()
        self.logBox.setMinimumSize(150, 300)
        self.logBox.setReadOnly(True)
        self.logBox.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        bottom_line_layout = QVBoxLayout()
        bottom_line_layout.addWidget(self.logBox, Qt.AlignBottom)
        logger_group_box.setLayout(bottom_line_layout)
        self.message.connect(self.write_to_logbox)

        return logger_group_box

    def write_to_logbox(self, message):
        self.logBox.moveCursor(QTextCursor.End)
        self.logBox.insertPlainText(message + "\n")
        self.logBox.moveCursor(QTextCursor.End)

    def populate_athlete_labels(self):
        athlete_info = self.api.currentAthlete()
        self.name_label.setText(athlete_info.name)
        self.username_label.setText(athlete_info.username)
        self.gender_label.setText(athlete_info.gender)
        self.city_label.setText(athlete_info.city)
        self.country_label.setText(athlete_info.country)

    def on_summary_clicked(self):
        self.api.athleteSummary()

    def on_get_latest_activity_clicked(self):
        self.api.getLatestActivity()

    def on_upload_activities_clicked(self):
        dialog = QDialog()
        box = QDialogButtonBox(dialog)
        box.addButton("Upload", QDialogButtonBox.AcceptRole)
        box.addButton("Cancel", QDialogButtonBox.RejectRole)

        box.accepted.connect(dialog.accept)
        box.rejected.connect(dialog.reject)
        dialog.setMinimumSize(500, 500)
        dialog.setWindowTitle("Upload Activities")
        layout = QVBoxLayout()
        input_layout = QHBoxLayout()

        label = QLabel("Location: ")
        input_push_button = QPushButton("...")
        input_push_button.setMaximumWidth(30)
        input_layout.addWidget(label, Qt.AlignLeft)
        input_layout.addWidget(self.activity_directory_input_box, Qt.AlignLeft)
        input_layout.addWidget(input_push_button, Qt.AlignRight)

        input_push_button.clicked.connect(
            self.open_activity_directory_selection_dialog)

        layout.addLayout(input_layout)

        self.file_list_view.setModel(self.file_model)
        self.file_list_view.setRootIndex(
            self.file_model.index(QDir.currentPath()))

        layout.addWidget(self.file_list_view)
        layout.addWidget(box)

        dialog.setLayout(layout)

        conclusion = dialog.exec()

        if conclusion == QDialog.Accepted:
            self.api.uploadActivitiesFromDirectory(self.file_model.rootPath())

    def activity_dir_selected(self, directoryStr, model):
        model.setRootPath(directoryStr)

    def open_activity_directory_selection_dialog(self):
        dialog = QFileDialog()
        dialog.setFileMode(QFileDialog.DirectoryOnly)
        dirPath = dialog.getExistingDirectory()

        self.file_model.setRootPath(dirPath)
        self.file_list_view.setRootIndex(self.file_model.index(dirPath))
        self.activity_directory_input_box.setText(dirPath)

    def start_gui(self):
        self.app.exec_()
Example #15
0
class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self._ui = Ui_MainWindow()
        self._ui.setupUi(self)

        self._model = QFileSystemModel()
        self._model.setRootPath('/')

        self._setUpViews(self._ui.leftView)
        self._setUpViews(self._ui.rightView)

        self._setUpActions()

        self._setUpButtons()

    def _setUpViews(self, view):
        view.setModel(self._model)
        view.setRootIndex(self._model.index(self._model.rootPath()))
        view.doubleClicked.connect(self._view_doubleClicked)

    def _setUpActions(self):
        self._ui.actionNewFile.triggered.connect(self._newFile_triggered)
        self._ui.actionNewFolder.triggered.connect(self._newFolder_triggered)
        self._ui.actionProperties.triggered.connect(self._properties_triggered)
        self._ui.actionMove.triggered.connect(self._move_triggered)
        self._ui.actionCopy.triggered.connect(self._copy_triggered)
        self._ui.actionToTrash.triggered.connect(self._toTrash_triggered)
        self._ui.actionDelete.triggered.connect(self._delete_triggered)
        self._ui.actionOpenDefaultApp.triggered.connect(
            self._defaultApp_triggered)

    def _setUpButtons(self):
        self._ui.leftBackButton.clicked.connect(self._leftBack_clicked)
        self._ui.rightBackButton.clicked.connect(self._rightBack_clicked)
        self._ui.leftHomeButton.clicked.connect(self._leftHome_clicked)
        self._ui.rightHomeButton.clicked.connect(self._rightHome_clicked)

    def _newFile_triggered(self):
        print('newFile')
        path = self._getCurrentLocation()
        newFile, confirmed = QInputDialog.getText(self, 'Create new file',
                                                  'File name: ')

        if confirmed:
            try:
                filecommands.newFile(path + '/' + newFile, changeIfExists)
            except CreateFileError:
                throwError(f'Can NOT create file {newFile}')

    def _newFolder_triggered(self):
        print('newFolder')
        path = self._getCurrentLocation()
        newFolder, confirmed = QInputDialog.getText(self, 'Create new folder',
                                                    'Folder name: ')

        if confirmed:
            try:
                filecommands.newFolder(path + '/' + newFolder, changeIfExists)
            except CreateFolderError:
                throwError(f'Can NOT create folder {newFolder}')

    def _properties_triggered(self):
        print('properties')
        paths = self._getSelectedItemsPath()

        for path in paths:
            dialog = PropertiesDialog(path, changeIfExists)
            dialog.exec_()

    def _move_triggered(self):
        print('move')
        paths = self._getSelectedItemsPath()
        dst = self._getDestination()

        for path in paths:
            try:
                filecommands.move(path, dst, changeIfExists)
            except MoveFileError:
                throwError(f'Can NOT move {path} to {dst}')

    def _copy_triggered(self):
        print('copy')
        paths = self._getSelectedItemsPath()
        dst = self._getDestination()

        for path in paths:
            try:
                filecommands.copy(path, dst, changeIfExists)
            except CopyFileError:
                throwError(f'Can NOT copy {path} to {dst}')

    def _toTrash_triggered(self):
        print('toTrash')
        paths = self._getSelectedItemsPath()

        for path in paths:
            try:
                filecommands.moveToTrash(path)
            except MoveToTrashError:
                throwError(f'Can NOT move {path} to trash')

    def _delete_triggered(self):
        print('delete')
        paths = self._getSelectedItemsPath()

        for path in paths:
            try:
                filecommands.delete(path)
            except DeleteFileError:
                throwError(f'Can NOT delete {path}')

    def _defaultApp_triggered(self):
        print('default')
        paths = self._getSelectedItemsPath()

        for path in paths:
            try:
                filecommands.openWithDefaultApp(path)
            except OpenFileError:
                throwError(f'Can NOT open {path} with default app')

    def _leftBack_clicked(self):
        self._goBack(self._ui.leftView)

    def _rightBack_clicked(self):
        self._goBack(self._ui.rightView)

    def _leftHome_clicked(self):
        self._goHome(self._ui.leftView)

    def _rightHome_clicked(self):
        self._goHome(self._ui.rightView)

    def _view_doubleClicked(self, index):
        view = self.sender()
        firstColumnIndex = index.siblingAtColumn(0)
        fileInfo = self._model.fileInfo(firstColumnIndex)

        if fileInfo.isDir():
            if fileInfo.isReadable():
                view.setRootIndex(firstColumnIndex)
            else:
                message = QMessageBox(
                    QMessageBox.Critical, 'Permission denied',
                    f'Folder {fileInfo.fileName()} is not readable!')
                message.exec()
        elif fileInfo.isFile():
            try:
                filecommands.openWithDefaultApp(fileInfo.absoluteFilePath())
            except OpenFileError:
                message = QMessageBox(QMessageBox.Critical,
                                      'Can NOT open file',
                                      f'Can not open {fileInfo.fileName()}')
                message.exec()

    def _goBack(self, view):
        index = view.rootIndex()
        if index != self._model.index(filecommands.ROOT_PATH):
            view.setRootIndex(index.parent())

    def _goHome(self, view):
        view.setRootIndex(self._model.index(filecommands.ROOT_PATH))

    def _getSelectedItemsPath(self):
        return {
            self._model.fileInfo(index).absoluteFilePath()
            for index in self._ui.leftView.selectedIndexes()
        }

    def _getCurrentLocation(self):
        return self._model.fileInfo(
            self._ui.leftView.rootIndex()).absoluteFilePath()

    def _getDestination(self):
        return self._model.fileInfo(
            self._ui.rightView.rootIndex()).absoluteFilePath()
Example #16
0
class MainWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

        # style window 
        self.setWindowTitle("Waifu File Sort")
        app_icon = QtGui.QIcon()
        app_icon.addFile("./icons/waifu_sort.png", QtCore.QSize(256, 256))
        self.setWindowIcon(app_icon)

        # store important data
        self.path_hotkey_dict = {}
        self.hotkey_path_dict = {}
        self._shortcut_list = []
        self.undo_list = []
        self.delete_folder = str(pathlib.Path(find_data_file("delete")))
        self.current_file_folder = ""
        self.pic_ext_list = [".jpg", ".png", ".webp", ".JPEG", ".PNG"]
        self.default_palette = QtGui.QGuiApplication.palette()

        # initialize source directory
        self.model = QFileSystemModel()
        self.model.setNameFilters(
            ["*.jpg", "*.png", "*.webp", "*.JPEG", "*.PNG"])
        self.model.setNameFilterDisables(False)
        self.ui.treeView.setModel(self.model)
        self.ui.treeView.setSelectionMode(QAbstractItemView.SingleSelection)
        self.ui.tableWidget.setSelectionMode(
            QtWidgets.QAbstractItemView.SingleSelection
        )
        self.ui.treeView.selectionModel().selectionChanged.connect(
            self.update_image_label
        )

        # hotkeys
        self.delShortcut = QShortcut(QKeySequence("Delete"), self)
        self.delShortcut.activated.connect(
            partial(self.move_cb, None, self.delete_folder)
        )

        self.undoShortcut = QShortcut(QKeySequence("Ctrl+Z"), self)
        self.undoShortcut.activated.connect(self.undo_cb)


        # callbacks init
        self.ui.browseBtn.clicked.connect(self.browse_source_click)
        self.ui.addDest.clicked.connect(self.add_dest_to_table)
        self.ui.removeDest.clicked.connect(self.remove_destination)
        self.ui.actionAbout.triggered.connect(self.show_about_dialog)
        self.ui.actionSave_Preset.triggered.connect(self.save_preset_cb)
        self.ui.actionLoad_Preset.triggered.connect(self.load_preset_cb)
        self.ui.actionClear_Delete_Folder.triggered.connect(self.clear_deleted_folder)
        self.ui.unmoveBtn.clicked.connect(self.undo_cb)
        self.ui.checkDeletedBtn.clicked.connect(self.check_deleted_btn_cb)
        self.ui.actionFancy.triggered.connect(self.set_fancy_style)
        self.ui.actionLight.triggered.connect(self.set_light_style)
        self.ui.actionDark.triggered.connect(self.set_dark_style)
        self.ui.comboMode.currentTextChanged.connect(self.change_file_type)
        self.ui.actionOrange.triggered.connect(self.set_orange_style)
        self.ui.actionRemove_Duplicates.triggered.connect(
            self.remove_duplicate_pictures)
        self.ui.actionRemove_Duplicates_Recursively.triggered.connect(
            partial(self.remove_duplicate_pictures, recursive_delete=True))

        self.set_dark_style()

    def remove_duplicate_pictures(self, recursive_delete=False):
        root_path = self.model.rootPath()
        if root_path == ".":
            return  # if source destination wasnt chosen

        # check for missclick
        msg = "Are you sure you want to delete (trash bin) duplicate pictures from source folder?"
        reply = QtWidgets.QMessageBox.question(self, 'Message',
                                               msg, QtWidgets.QMessageBox.Yes,
                                               QtWidgets.QMessageBox.No)

        if reply != QtWidgets.QMessageBox.Yes:
            return

        # gather pictures from root path
        all_pictures = []
        if recursive_delete:  # recursive search
            for path in pathlib.Path(root_path).glob(r'**/*'):
                if path.suffix in self.pic_ext_list:
                    all_pictures.append(path)

        else:  # non recursive
            for path in pathlib.Path(root_path).glob(r'*'):
                if path.suffix in self.pic_ext_list:
                    all_pictures.append(path)

        # add phash of picture to dictionary, replace with shorter filename if same hash found
        result_pics = {}
        for path in all_pictures:
            with Image.open(path) as img:
                img_hash = str(imagehash.phash(img))
                if img_hash in result_pics:
                    dict_fname = result_pics[img_hash].stem
                    if len(path.stem) < len(dict_fname):
                        result_pics[img_hash] = path
                else:
                    result_pics[img_hash] = path

        result_pics = {value: key for key, value in result_pics.items()}
        # delete all pictures that are not in a result_pics dict
        for path in all_pictures:
            if path not in result_pics:
                try:
                    shutil.move(str(path), self.delete_folder)
                except shutil.Error:
                    send2trash(str(path))

        QtWidgets.QMessageBox.about(self, "Info", "Done")

    def add_text_to_buttons(self):
        self.ui.addDest.setText("Add")
        self.ui.removeDest.setText("Remove")
        self.ui.browseBtn.setText("Browse")
        self.ui.checkDeletedBtn.setText("Deleted")
        self.ui.unmoveBtn.setText("Undo")

    def remove_text_from_buttons(self):
        self.ui.addDest.setText("")
        self.ui.removeDest.setText("")
        self.ui.browseBtn.setText("")
        self.ui.checkDeletedBtn.setText("")
        self.ui.unmoveBtn.setText("")

    def set_orange_style(self):
        QtGui.QGuiApplication.setPalette(self.default_palette)
        with open("./styles/orange.css") as f:
            style_text = f.read()
            self.setStyleSheet(style_text)

        self.add_text_to_buttons()

    def set_fancy_style(self):
        QtGui.QGuiApplication.setPalette(self.default_palette)
        with open("./styles/fancy.css") as f:
            style_text = f.read()
            self.setStyleSheet(style_text)

        self.remove_text_from_buttons()

    def set_light_style(self):
        QtGui.QGuiApplication.setPalette(self.default_palette)
        self.setStyleSheet(" ")
        self.add_text_to_buttons()

    def set_dark_style(self):
        self.setStyleSheet(" ")
        self.add_text_to_buttons()

        dark_palette = QPalette()
        dark_palette.setColor(QPalette.Window, QColor(53, 53, 53))
        dark_palette.setColor(QPalette.WindowText, Qt.white)
        dark_palette.setColor(QPalette.Base, QColor(25, 25, 25))
        dark_palette.setColor(QPalette.AlternateBase, QColor(53, 53, 53))
        dark_palette.setColor(QPalette.ToolTipBase, Qt.white)
        dark_palette.setColor(QPalette.ToolTipText, Qt.white)
        dark_palette.setColor(QPalette.Text, Qt.white)
        dark_palette.setColor(QPalette.Button, QColor(53, 53, 53))
        dark_palette.setColor(QPalette.ButtonText, Qt.white)
        dark_palette.setColor(QPalette.BrightText, Qt.red)
        dark_palette.setColor(QPalette.Link, QColor(42, 130, 218))
        dark_palette.setColor(QPalette.Highlight, QColor(42, 130, 218))
        dark_palette.setColor(QPalette.HighlightedText, Qt.black)
        QtGui.QGuiApplication.setPalette(dark_palette)

    def change_file_type(self):
        """
        Change source directory display between pictures and files
        """
        mode = self.ui.comboMode.currentText()
        if mode == "Files":
            self.model.setNameFilters(["*.*"])

        elif mode == "Pictures":
            self.model.setNameFilters(
                ["*.jpg", "*.png", "*.webp", ".JPEG", ".PNG"])

    def check_deleted_btn_cb(self):
        """
        This is supposed to change model view to the deleted folder,
        and second press is supposed to bring you back to the previous 
        folder, but it doesnt work if you dont select an image in deleted folder.
        """
        ind = self.ui.treeView.currentIndex()
        file_path = self.model.filePath(ind)
        try:
            file_path = pathlib.Path(file_path).parents[0].resolve()
        except IndexError:
            return

        if file_path != pathlib.Path(self.delete_folder).resolve():
            self.model.setRootPath(self.delete_folder)
            self.ui.treeView.setRootIndex(self.model.index(self.delete_folder))
        else:
            self.model.setRootPath(self.current_file_folder)
            self.ui.treeView.setRootIndex(
                self.model.index(self.current_file_folder))

    def clear_deleted_folder(self):
        msg = "Are you sure you want to clear folder with deleted files?"
        reply = QtWidgets.QMessageBox.question(self, 'Message',
                                               msg, QtWidgets.QMessageBox.Yes,
                                               QtWidgets.QMessageBox.No)

        if reply == QtWidgets.QMessageBox.Yes:
            p = pathlib.Path(self.delete_folder)
            for filename in p.glob("*"):
                send2trash(str(filename))
            QtWidgets.QMessageBox.about(
                self, "Delete folder cleared", "Delete folder cleared"
            )

    def undo_cb(self):
        """
        Store actions in a list, revert them 1 by 1
        """
        try:
            last_operation = self.undo_list[-1]
        except IndexError:
            return
        pic_path, dest_path = last_operation
        pic_path = pathlib.Path(pic_path)
        dest_path = pathlib.Path(dest_path, pic_path.name)
        pic_path, dest_path = dest_path, pic_path

        # print(pic_path.parents[0], dest_path)
        try:
            shutil.move(pic_path, str(dest_path))
        except shutil.Error:
            QtWidgets.QMessageBox.warning(
                self, "Warning", "File already exists")
        except AttributeError:
            return
        except FileNotFoundError:
            return
        del self.undo_list[-1]

    def load_preset_cb(self):
        """
        Load user settings from file
        """
        dialog = QFileDialog()
        dialog.setFilter(dialog.filter() | QtCore.QDir.Hidden)
        dialog.setDefaultSuffix("json")
        dialog.setAcceptMode(QFileDialog.AcceptOpen)
        dialog.setNameFilters(["JSON (*.json)"])
        if dialog.exec_() == QDialog.Accepted:
            preset_path = dialog.selectedFiles()[0]
            self.path_hotkey_dict = load_json(preset_path)
            self.path_hotkey_dict = {
                k: v for k, v in self.path_hotkey_dict.items() if v is not None
            }
            print("loaded dict: ", self.path_hotkey_dict)
            self.hotkey_path_dict = {
                value: key for key, value in self.path_hotkey_dict.items()
            }
            self.restore_table_from_dict()
        else:
            print("Cancelled")

    def save_preset_cb(self):
        """
        Save user settings to file
        """
        dialog = QFileDialog()
        dialog.setFilter(dialog.filter() | QtCore.QDir.Hidden)
        dialog.setDefaultSuffix("json")
        dialog.setAcceptMode(QFileDialog.AcceptSave)
        dialog.setNameFilters(["JSON (*.json)"])
        if dialog.exec_() == QDialog.Accepted:
            preset_path = dialog.selectedFiles()[0]
            save_json(self.path_hotkey_dict, preset_path)
            QtWidgets.QMessageBox.information(
                self, "Saved", f"Saved hotkey preset: {preset_path}"
            )
        else:
            print("Cancelled")

    def show_about_dialog(self):
        text = (
            "<center>"
            "<h1>Waifu File Sort</h1>"
            "&#8291;"
            "</center>"
            f"<p>Version {get_version()}<br/>"
        )
        QtWidgets.QMessageBox.about(self, "About Waifu File Sort", text)

    def restore_table_from_dict(self):
        self.clear_table_widget()
        row_counter = 0

        for shortcut in self._shortcut_list:
            shortcut.setEnabled(False)

        self._shortcut_list = []

        for path, hotkey in self.path_hotkey_dict.items():
            path = pathlib.Path(path)
            self.add_dest_to_table(dest_path=path.name, hotkey=hotkey)
            self.ui.tableWidget.item(row_counter, 0).setToolTip(str(path))
            shortcut = QShortcut(QKeySequence(hotkey), self)
            shortcut.activated.connect(
                lambda mypath=path: self.move_cb(input_path=mypath))
            self._shortcut_list.append(shortcut)
            row_counter += 1

    def clear_table_widget(self):
        self.ui.tableWidget.clearContents()
        self.ui.tableWidget.setRowCount(0)

    def remove_destination(self):
        # get selected row or return
        # delete info from both dicts
        # reconstruct table widget from dict

        current_row = self.ui.tableWidget.currentRow()
        # print(f"{current_row=}")
        try:
            dest_path = self.ui.tableWidget.item(current_row, 0).toolTip()
        except AttributeError:
            return
        hotkey = self.ui.tableWidget.cellWidget(current_row, 2).text()
        # print("deleting hotkey: ", hotkey)
        self.delete_hotkey(hotkey)
        try:
            del self.path_hotkey_dict[dest_path]
        except KeyError:
            pass
        try:
            del self.hotkey_path_dict[hotkey]
        except KeyError:
            pass

        self.restore_table_from_dict()

    def delete_hotkey(self, name):
        for shortcut in self._shortcut_list:
            key_name = shortcut.key().toString()
            # print("k-name: ", key_name)
            if key_name == name.upper():
                # print("DELETED hotkey: ", name)
                shortcut.setEnabled(False)

    def add_dest_to_table(self, dest_path=None, hotkey=None):
        self.ui.tableWidget.setEditTriggers(
            self.ui.tableWidget.NoEditTriggers
        )  # disable editing and sorting
        self.ui.tableWidget.setSortingEnabled(False)

        row_counter = self.ui.tableWidget.rowCount()
        self.ui.tableWidget.insertRow(row_counter)
        ########################################################
        # add path label
        dest_path = QtWidgets.QTableWidgetItem(
            dest_path or "Press browse to specify destination directory"
        )
        self.ui.tableWidget.setItem(row_counter, 0, dest_path)
        ########################################################
        # add browse button
        browse_btn = QtWidgets.QPushButton("Browse")
        browse_btn.clicked.connect(
            lambda *args, row_ind=row_counter: self.browse_dest_click(row_ind)
        )
        self.ui.tableWidget.setCellWidget(row_counter, 1, browse_btn)
        ########################################################
        # add hotkey line edit
        hotkey_line = QtWidgets.QLineEdit()
        hotkey_line.setPlaceholderText("Add hotkey")
        hotkey_line.setText(hotkey)
        hotkey_line.setMaxLength(1)

        hotkey_line.textChanged.connect(
            lambda *args, row_ind=row_counter: self.hotkey_line_text_changed_cb(
                hotkey_line, row_ind
            )
        )
        self.ui.tableWidget.setCellWidget(row_counter, 2, hotkey_line)
        ########################################################
        # add send button
        send_btn = QtWidgets.QPushButton("Send")
        send_btn.clicked.connect(
            lambda *args, row_ind=row_counter: self.move_cb(row=row_ind)
        )
        self.ui.tableWidget.setCellWidget(row_counter, 3, send_btn)

    def move_cb(self, row=None, input_path=None):
        ind = self.ui.treeView.currentIndex()
        pic_path = self.model.filePath(ind)
        dest_path = input_path or self.ui.tableWidget.item(row, 0).toolTip()
        dest_path = pathlib.Path(dest_path)

        if dest_path.is_dir() and str(dest_path) != ".":
            try:
                shutil.move(pic_path, str(dest_path))
            except shutil.Error:
                QtWidgets.QMessageBox.warning(
                    self, "Warning", "File already exists")
                return
            self.undo_list.append((pic_path, str(dest_path)))
        else:
            # notify user
            QtWidgets.QMessageBox.warning(
                self, "Warning", "Press Browse to add destination folder"
            )

    def hotkey_line_text_changed_cb(self, hotkey_line, row_ind):
        hotkey = hotkey_line.text()
        path = self.ui.tableWidget.item(row_ind, 0).toolTip()
        if not path and len(hotkey) > 0:
            QtWidgets.QMessageBox.warning(
                self, "Warning", "Press Browse to add destination folder, add hotkey after"
            )
            hotkey_line.clear()
            hotkey_line.clearFocus()
            return

        # check if hotkey line edit is empty and delete hotkey
        if len(hotkey) == 0 and path != "":
            hotkey_to_del = self.path_hotkey_dict[path]
            self.delete_hotkey(hotkey_to_del)

        self.path_hotkey_dict[path] = hotkey
        self.hotkey_path_dict = {
            value: key for key, value in self.path_hotkey_dict.items()
        }
        shortcut = QShortcut(QKeySequence(hotkey), self)
        # self._shortcut_list.append(shortcut.key().toString())
        self._shortcut_list.append(shortcut)
        dest_path = self.hotkey_path_dict[hotkey]
        shortcut.activated.connect(lambda: self.move_cb(input_path=dest_path))
        if len(hotkey) > 0:
            hotkey_line.clearFocus()

    def browse_dest_click(self, caller_row):
        # print(caller_row)
        dialog = QFileDialog()
        folder_path = dialog.getExistingDirectory(None, "Select Folder")
        p = pathlib.Path(folder_path)

        if folder_path:
            self.ui.tableWidget.item(caller_row, 0).setText(p.name)
            self.ui.tableWidget.item(caller_row, 0).setToolTip(str(p))
            self.path_hotkey_dict[str(p)] = self.ui.tableWidget.cellWidget(
                caller_row, 2
            ).text()

    def browse_source_click(self):
        dialog = QFileDialog()
        folder_path = dialog.getExistingDirectory(None, "Select Folder")
        if folder_path:
            self.model.setRootPath(folder_path)
            self.ui.treeView.setRootIndex(self.model.index(folder_path))

    def update_image_label(self):
        ind = self.ui.treeView.currentIndex()
        file_path = self.model.filePath(ind)

        # keep track of current folder for check button return location
        try:
            path_to_current_folder = pathlib.Path(file_path).parents[0]
        except IndexError:
            return # fix click on C drive crash

        if str(path_to_current_folder.resolve()) != str(
            pathlib.Path(self.delete_folder).resolve()
        ):
            self.current_file_folder = str(path_to_current_folder)

        pixmap = QtGui.QPixmap(file_path)
        pixmap = pixmap.scaled(
            self.ui.imageLabel.width(),
            self.ui.imageLabel.height(),
            QtCore.Qt.KeepAspectRatio,
        )
        self.ui.imageLabel.setPixmap(pixmap)
        self.ui.imageLabel.setAlignment(QtCore.Qt.AlignCenter)
Example #17
0
class MainView(QMainWindow):
    def __init__(self, model, controller):
        self.settings = SettingsModel()
        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()

        self.model.subscribe_update_func(self.update_ui_from_model)

    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)

        # 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.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(['*.jpg', '*.png', '*.jpeg'])
        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, 200)
        self.ui.treeView.setColumnWidth(1, 200)
        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.button_open_parent.clicked.connect(self.on_open_parent)

        # Shortcuts
        _translate = QtCore.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.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.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))

    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())

    # On resize
    def resizeEvent(self, resizeEvent):
        self.main_controller.update_canvas(self.ui.graphicsView.width(),
                                           self.ui.graphicsView.height(),
                                           self.model.get_image())

    # Additional static shortcuts
    def keyPressEvent(self, e):
        if e.key() == QtCore.Qt.Key_Escape and self.model.is_fullscreen:
            self.main_controller.toggle_fullscreen()

        # Redefine shortcuts when hiding menubar
        # somehow not working 18/2/2015
        if self.model.is_fullscreen and self.ui.menubar.isHidden():
            if e.key() == QKeySequence(self.settings.get('Hotkeys', 'Exit')):
                self.on_close()
            elif e.key() == QKeySequence(self.settings.get('Hotkeys', 'Next')):
                self.on_next_item()
            elif e.key() == QKeySequence(
                    self.settings.get('Hotkeys', 'Previous')):
                self.on_previous_item()
            elif e.key() == QKeySequence(
                    self.settings.get('Hotkeys', 'Directory')):
                self.on_change_directory()
            elif e.key() == QKeySequence(
                    self.settings.get('Hotkeys', 'Zoom in')):
                self.on_zoom_in()
            elif e.key() == QKeySequence(
                    self.settings.get('Hotkeys', 'Zoom out')):
                self.on_zoom_out()
            elif e.key() == QKeySequence(
                    self.settings.get('Hotkeys', 'Zoom original')):
                self.on_zoom_original()
            elif e.key() == QKeySequence(
                    self.settings.get('Hotkeys', '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)

    def on_dir_list_activated(self, index):
        if self.file_model.hasChildren(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)

    def on_dir_list_clicked(self, index):
        self.main_controller.open_image(self.ui.graphicsView.width(),
                                        self.ui.graphicsView.height(),
                                        self.file_model.filePath(index))

    # File menu
    def on_set_as_wallpaper(self):
        from view.WallpaperView import WallpaperDialog
        from 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):
        import subprocess
        # Windows
        subprocess.Popen(r'explorer /select,' + self.model.get_image_path())

    def on_options(self):
        from view.OptionsView import OptionDialog
        self.dialog = OptionDialog(self, self.ui)
        self.dialog.show()

    def on_close(self):
        self.close()

    # Folder menu
    def on_next_item(self):
        index = self.ui.treeView.moveCursor(QAbstractItemView.MoveDown,
                                            Qt.NoModifier)
        self.ui.treeView.setCurrentIndex(index)
        self.main_controller.open_image(self.ui.graphicsView.width(),
                                        self.ui.graphicsView.height(),
                                        self.file_model.filePath(index))

    def on_previous_item(self):
        index = self.ui.treeView.moveCursor(QAbstractItemView.MoveUp,
                                            Qt.NoModifier)
        self.ui.treeView.setCurrentIndex(index)
        self.main_controller.open_image(self.ui.graphicsView.width(),
                                        self.ui.graphicsView.height(),
                                        self.file_model.filePath(index))

    def on_change_directory(self):
        self.main_controller.change_directory()

    # View menu
    def on_zoom_in(self):
        self.canvas_controller.update_canvas(self.ui.graphicsView.width(),
                                             self.ui.graphicsView.height(),
                                             self.model.get_image(), 1, 1.1)

    def on_zoom_out(self):
        self.canvas_controller.update_canvas(self.ui.graphicsView.width(),
                                             self.ui.graphicsView.height(),
                                             self.model.get_image(), 1, 0.9)

    def on_zoom_original(self):
        self.canvas_controller.update_canvas(self.ui.graphicsView.width(),
                                             self.ui.graphicsView.height(),
                                             self.model.get_image(), 4)

    def on_scale_image_to_width(self):
        self.canvas_controller.update_canvas(self.ui.graphicsView.width(),
                                             self.ui.graphicsView.height(),
                                             self.model.get_image(), 2)

    def on_scale_image_to_height(self):
        self.canvas_controller.update_canvas(self.ui.graphicsView.width(),
                                             self.ui.graphicsView.height(),
                                             self.model.get_image(), 3)

    def on_toggle_filelist(self):
        if self.ui.actionFile_list.isChecked():
            self.ui.fileWidget.show()
        else:
            self.ui.fileWidget.hide()

    def on_fullscreen(self):
        self.main_controller.toggle_fullscreen()

    # Favorite menu
    def on_add_to_favorites(self):
        self.main_controller.add_to_favorites()
        self.load_favorites()

    def on_remove_from_favorites(self):
        self.main_controller.remove_from_favorites()
        self.load_favorites()

    # Help menu
    def on_changelog(self):
        webbrowser.open('https://gimu.org/hitagi-reader/docs')

    def on_about(self):
        from view.AboutView import AboutDialog
        dialog = AboutDialog(self, None, None)
        dialog.show()

    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))

        #if self.model.image_path is not None:
        # self.ui.statusbar.showMessage(str(self.model.image_path) + "    " + str(self.model.image_index + 1) + " of " + str(len(self.model.image_paths)))
        self.ui.graphicsView.setScene(self.canvas.scene)

        # Fullscreen mode switching
        if self.model.is_fullscreen:
            self.showFullScreen()
            if self.settings.get('Misc', 'hide_menubar') == 'True':
                self.ui.menubar.hide()
            if self.settings.get('Misc', 'hide_statusbar') == 'True':
                self.ui.statusbar.hide()
        else:
            self.showNormal()
            if self.settings.get('Misc', 'hide_menubar') == 'True':
                self.ui.menubar.show()
            if self.settings.get('Misc', 'hide_statusbar') == 'True':
                self.ui.statusbar.show()
class MainWindow(QtWidgets.QMainWindow):
    connection_status = 0
    file_path = ''
    ftp = ftplib.FTP('')

    def __init__(self):
        super(MainWindow, self).__init__()
        self.ui = uic.loadUi('ftp.ui', self)
        self.model = QFileSystemModel()
        self.setFixedSize(810, 563)
        self.init_ui()
        self.bind_event()
        self.set_validation_type()

    def init_ui(self):
        self.populate_local_tree_view()
        self.show()

    def populate_local_tree_view(self):
        self.fsm = QFileSystemModel(self)
        self.fsm.setRootPath('')
        self.fsm.setReadOnly(True)
        # self.fsm.setFilter(QDir.AllDirs | QDir.NoDotAndDotDot)
        self.ui.local_tree_view.setModel(self.fsm)
        self.local_tree_view.setRootIndex(self.fsm.index(self.fsm.rootPath()))
        self.ui.local_tree_view.setAnimated(False)
        self.ui.local_tree_view.setIndentation(20)
        self.ui.local_tree_view.setSortingEnabled(True)

    def bind_event(self):
        self.ui.btn_login.clicked.connect(self.on_login_click)
        self.ui.local_tree_view.clicked.connect(self.on_tree_view_clicked)
        # self.ui.tableView.doubleClicked.connect(self.on_click)

    def on_tree_view_clicked(self, index):
        index_item = self.fsm.index(index.row(), 0, index.parent())
        path = str(self.fsm.filePath(index_item))
        self.selected_file_path = path

    def set_validation_type(self):
        only_int = QIntValidator()
        self.ui.input_port_number.setValidator(
            only_int)  # allowing only integer input
        self.ui.input_password.setEchoMode(QLineEdit.Password)

    @pyqtSlot()
    def on_login_click(self):
        if self.connection_status == 0:
            host_name = self.ui.input_host_name.text().strip()
            usr = self.ui.input_username.text().strip()
            password = self.ui.input_password.text().strip()
            port_number = self.ui.input_port_number.text().strip()

            if is_not_blank(host_name) and is_not_blank(usr) and is_not_blank(
                    password) and is_not_blank(port_number):
                port_number = int(port_number)
                try:
                    self.ftp.connect(host_name, port_number)
                    self.ftp.login(usr, password)

                    self.add_item_to_log_list_widget("login successful")
                    self.ui.btn_login.setText("Disconnect")
                    self.connection_status = 1
                    self.get_remote_file()
                except ftplib.all_errors:
                    self.add_item_to_log_list_widget(
                        "logged in incorrect credentials")
                    show_error_dialog("Please check your credentials")
            else:
                show_error_dialog("Please enter all credentials")
        else:
            self.logout()

    def get_remote_file(self):
        self.model = QStandardItemModel()
        self.model.setColumnCount(3)
        self.model.setHorizontalHeaderLabels(['Name', 'Size', 'Last Modified'])

        files = self.ftp.nlst()
        i = 0
        for filename in files:
            self.add_row_item(i, 0, self.model, filename)
            try:
                self.ftp.voidcmd('TYPE I')
                size = str(self.ftp.size(filename))
                size = size + " KB"
                self.add_row_item(i, 1, self.model, size)
                modified_time = self.ftp.sendcmd('MDTM ' + filename)
                formatted_time = datetime.strptime(
                    modified_time[4:],
                    "%Y%m%d%H%M%S").strftime("%d %B %Y %H:%M:%S")
                self.add_row_item(i, 2, self.model, formatted_time)
                self.add_item_to_log_list_widget(
                    "remote directory listing successful")
            except:
                item = QStandardItem(filename)
                item.setTextAlignment(Qt.AlignVCenter)
                item.setEditable(False)
                self.model.setItem(i, 0, item)
                item.setIcon(QIcon("static/ic_folder.png"))
            i += 1
        self.ui.tableView.setModel(self.model)
        self.ui.tableView.setSelectionBehavior(QTableView.SelectRows)
        self.set_property()

    @staticmethod
    def add_row_item(row, column, model, text):
        item = QStandardItem(text)
        item.setTextAlignment(Qt.AlignVCenter)
        item.setEditable(False)
        model.setItem(row, column, item)

    def set_property(self):
        self.ui.tableView.setColumnWidth(0, 120)
        self.ui.tableView.setColumnWidth(1, 120)
        self.ui.tableView.setColumnWidth(2, 160)
        self.ui.tableView.setShowGrid(False)
        header = self.ui.tableView.horizontalHeader()
        header.setDefaultAlignment(Qt.AlignVCenter)

    def contextMenuEvent(self, event):
        self.menu = QMenu(self)
        rename_action = QAction('Rename', self)
        rename_action.triggered.connect(lambda: self.rename_slot(event))
        self.menu.addAction(rename_action)

        delete_action = QAction('Delete', self)
        delete_action.triggered.connect(lambda: self.delete_slot(event))
        self.menu.addAction(delete_action)

        download_action = QAction('Download', self)
        download_action.triggered.connect(lambda: self.download_slot(event))
        self.menu.addAction(download_action)

        create_dir_action = QAction('New Dir', self)
        create_dir_action.triggered.connect(
            lambda: self.create_dir_slot(event))
        self.menu.addAction(create_dir_action)

        upload_file_action = QAction('Upload', self)
        upload_file_action.triggered.connect(
            lambda: self.upload_file_slot(event))
        self.menu.addAction(upload_file_action)

        self.menu.popup(QCursor.pos())

    @pyqtSlot()
    def rename_slot(self, event):
        # indexes = self.ui.tableView.selectionModel().selectedRows()
        # for index in sorted(indexes):
        # print('Row %d is selected' % index.row())
        for index in sorted(self.ui.tableView.selectionModel().selectedRows()):
            selected_filename = self.model.data(
                self.model.index(index.row(), 0))
            new_name = show_input_dialog(self, "Rename", "New Name")
            if new_name != '' and len(new_name) != 0:
                try:
                    self.ftp.rename(selected_filename, new_name)
                    self.get_remote_file()
                    self.add_item_to_log_list_widget(selected_filename +
                                                     " change with " +
                                                     new_name +
                                                     " successfully on remote")
                except:
                    show_error_dialog("Unexpected error occurred")
            else:
                show_error_dialog("Invalid file name")
            break  # just rename one file

    @pyqtSlot()
    def delete_slot(self, event):
        for index in sorted(self.ui.tableView.selectionModel().selectedRows()):
            try:
                selected_filename = self.model.data(
                    self.model.index(index.row(), 0))
                self.ftp.rmd(selected_filename)
                self.add_item_to_log_list_widget(selected_filename +
                                                 " deleted on remote")
            except:
                print("")

    @pyqtSlot()
    def download_slot(self, event):
        for index in sorted(self.ui.tableView.selectionModel().selectedRows()):
            selected_filename = str(
                self.model.data(self.model.index(index.row(), 0)))
            try:
                local_file = open(selected_filename, 'wb')
                self.ftp.retrbinary('RETR ' + selected_filename,
                                    local_file.write, 1024)
                local_file.close()
                self.add_item_to_log_list_widget(
                    selected_filename + "file is downloaded successfully")
            except Exception as e:
                print(e)  # show_error_dialog("Unexpected error occurred")

    @pyqtSlot()
    def create_dir_slot(self, event):
        self.add_item_to_log_list_widget(
            "test file is downloaded successfully")
        file_name = show_input_dialog(self, "New Directory", "Directory Name")
        try:
            if file_name != '' and len(file_name) != 0:
                self.ftp.mkd(file_name)
                self.get_remote_file()
                self.add_item_to_log_list_widget(file_name +
                                                 " created successfully")
            else:
                show_error_dialog("Invalid directory name")
        except:
            print("")

    @pyqtSlot()
    def upload_file_slot(self, event):
        try:
            if os.path.isfile(self.file_path):
                file_name = os.path.basename(self.file_path)
                self.ftp.storbinary('STOR ' + file_name,
                                    open(self.file_path, 'rb'))
                self.get_remote_file()
                self.add_item_to_log_list_widget(file_name +
                                                 " uploaded successfully")
        except:
            show_error_dialog("Please choose only one file")

    @pyqtSlot()
    def on_delete_file_click(self):
        files = list(self.ftp.nlst())
        for f in files:
            self.ftp.delete(f)

    def get_size(self, directory):
        size = 0
        for file in self.ftp.nlst(directory):
            size += self.ftp.size(file)
        return size

    def add_item_to_log_list_widget(self, log):
        self.ui.log_list_widget.addItem("status\t" + log)

    def logout(self):
        self.ftp.close()
        self.add_item_to_log_list_widget("logout successfully")
        self.connection_status = 0
        self.ui.btn_login.setText("Connect")
Example #19
0
class FileManager(QWidget, _HalWidgetBase):
    def __init__(self, parent=None):
        super(FileManager, self).__init__(parent)
        self.title = 'Qtvcp File System View'
        self.left = 10
        self.top = 10
        self.width = 640
        self.height = 480
        self.media_path = (os.path.join(os.path.expanduser('~'),
                                        'linuxcnc/nc_files'))
        user = os.path.split(os.path.expanduser('~'))[-1]
        self.user_path = (os.path.join('/media', user))
        self.currentPath = None
        self.currentFolder = None
        self.initUI()

    def initUI(self):
        self.setWindowTitle(self.title)
        self.setGeometry(self.left, self.top, self.width, self.height)

        pasteBox = QHBoxLayout()
        self.textLine = QLineEdit()
        self.textLine.setToolTip('Current Director/selected File')
        self.pasteButton = QToolButton()
        self.pasteButton.setEnabled(False)
        self.pasteButton.setText('Paste')
        self.pasteButton.setToolTip(
            'Copy file from copy path to current directory/file')
        self.pasteButton.clicked.connect(self.paste)
        self.pasteButton.hide()
        pasteBox.addWidget(self.textLine)
        pasteBox.addWidget(self.pasteButton)

        self.copyBox = QFrame()
        hbox = QHBoxLayout()
        hbox.setContentsMargins(0, 0, 0, 0)
        self.copyLine = QLineEdit()
        self.copyLine.setToolTip('File path to copy from, when pasting')
        self.copyButton = QToolButton()
        self.copyButton.setText('Copy')
        self.copyButton.setToolTip('Record current file as copy path')
        self.copyButton.clicked.connect(self.recordCopyPath)
        hbox.addWidget(self.copyButton)
        hbox.addWidget(self.copyLine)
        self.copyBox.setLayout(hbox)
        self.copyBox.hide()

        self.model = QFileSystemModel()
        self.model.setRootPath(QDir.currentPath())
        self.model.setFilter(QDir.AllDirs | QDir.NoDot | QDir.Files)
        self.model.setNameFilterDisables(False)
        self.model.rootPathChanged.connect(self.folderChanged)

        self.list = QListView()
        self.list.setModel(self.model)
        self.updateDirectoryView(self.media_path)
        self.list.resize(640, 480)
        self.list.clicked[QModelIndex].connect(self.listClicked)
        self.list.activated.connect(self._getPathActivated)
        self.list.setAlternatingRowColors(True)

        self.cb = QComboBox()
        self.cb.currentIndexChanged.connect(self.filterChanged)
        self.fillCombobox(INFO.PROGRAM_FILTERS_EXTENSIONS)
        self.cb.setMinimumHeight(30)
        self.cb.setSizePolicy(QSizePolicy(QSizePolicy.Fixed,
                                          QSizePolicy.Fixed))

        self.button2 = QToolButton()
        self.button2.setText('Media')
        self.button2.setSizePolicy(
            QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed))
        self.button2.setMinimumSize(60, 30)
        self.button2.setToolTip('Jump to Media directory')
        self.button2.clicked.connect(self.onJumpClicked)

        SettingMenu = QMenu(self)
        self.settingMenu = SettingMenu
        for i in ('Media', 'User'):
            axisButton = QAction(QIcon.fromTheme('user-home'), i, self)
            # weird lambda i=i to work around 'function closure'
            axisButton.triggered.connect(
                lambda state, i=i: self.jumpTriggered(i))
            SettingMenu.addAction(axisButton)
        self.button2.setMenu(SettingMenu)

        self.button3 = QToolButton()
        self.button3.setText('Add Jump')
        self.button3.setSizePolicy(
            QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed))
        self.button3.setMinimumSize(60, 30)
        self.button3.setToolTip('Add current directory to jump button list')
        self.button3.clicked.connect(self.onActionClicked)

        hbox = QHBoxLayout()
        hbox.addWidget(self.button2)
        hbox.addWidget(self.button3)
        hbox.insertStretch(2, stretch=0)
        hbox.addWidget(self.cb)

        windowLayout = QVBoxLayout()
        windowLayout.addLayout(pasteBox)
        windowLayout.addWidget(self.copyBox)
        windowLayout.addWidget(self.list)
        windowLayout.addLayout(hbox)
        self.setLayout(windowLayout)
        self.show()

    def _hal_init(self):
        if self.PREFS_:
            last_path = self.PREFS_.getpref('last_loaded_directory',
                                            self.media_path, str,
                                            'BOOK_KEEPING')
            self.updateDirectoryView(last_path)
            LOG.debug("lAST FILE PATH: {}".format(last_path))
        else:
            LOG.debug("lAST FILE PATH: {}".format(self.media_path))
            self.updateDirectoryView(self.media_path)

    #########################
    # callbacks
    #########################

    # add shown text and hidden filter data from the INI
    def fillCombobox(self, data):
        for i in data:
            self.cb.addItem(i[0], i[1])

    def folderChanged(self, data):
        self.currentFolder = data
        self.textLine.setText(data)

    def updateDirectoryView(self, path):
        self.list.setRootIndex(self.model.setRootPath(path))

    # retrieve selected filter (it's held as QT.userData)
    def filterChanged(self, index):
        userdata = self.cb.itemData(index)
        self.model.setNameFilters(userdata)

    def listClicked(self, index):
        # the signal passes the index of the clicked item
        dir_path = self.model.filePath(index)
        if self.model.fileInfo(index).isFile():
            self.currentPath = dir_path
            self.textLine.setText(self.currentPath)
            return
        root_index = self.model.setRootPath(dir_path)
        self.list.setRootIndex(root_index)

    def onUserClicked(self):
        self.showUserDir()

    def onMediaClicked(self):
        self.showMediaDir()

    def onJumpClicked(self):
        data = self.button2.text()
        if data == 'Media':
            self.showMediaDir()
        elif data == 'User':
            self.showUserDir()
        else:
            self.updateDirectoryView(self.button2.text())

    def jumpTriggered(self, data):
        if data == 'Media':
            self.button2.setText('{}'.format(data))
            self.button2.setToolTip('Jump to Media directory')
            self.showMediaDir()
        elif data == 'User':
            self.button2.setText('{}'.format(data))
            self.button2.setToolTip('Jump to User directory')
            self.showUserDir()
        else:
            self.button2.setText('{}'.format(data))
            self.button2.setToolTip('Jump to directory: {}'.format(data))
            self.updateDirectoryView(self.button2.text())

    def onActionClicked(self):
        i = self.currentFolder
        button = QAction(QIcon.fromTheme('user-home'), i, self)
        # weird lambda i=i to work around 'function closure'
        button.triggered.connect(lambda state, i=i: self.jumpTriggered(i))
        self.settingMenu.addAction(button)

    # get current selection and update the path
    # then if the path is good load it into linuxcnc
    # record it in the preference file if available
    def _getPathActivated(self):
        row = self.list.selectionModel().currentIndex()
        self.listClicked(row)

        fname = self.currentPath
        if fname is None:
            return
        if fname:
            self.load(fname)

    def recordCopyPath(self):
        data, isFile = self.getCurrentSelected()
        if isFile:
            self.copyLine.setText(os.path.normpath(data))
            self.pasteButton.setEnabled(True)
        else:
            self.copyLine.setText('')
            self.pasteButton.setEnabled(False)
            STATUS.emit('error', OPERATOR_ERROR,
                        'Can only copy a file, not a folder')

    def paste(self):
        res = self.copyFile(self.copyLine.text(), self.textLine.text())
        if res:
            self.copyLine.setText('')
            self.pasteButton.setEnabled(False)

    ########################
    # helper functions
    ########################

    def showCopyControls(self, state):
        if state:
            self.copyBox.show()
            self.pasteButton.show()
        else:
            self.copyBox.hide()
            self.pasteButton.hide()

    def showMediaDir(self):
        self.updateDirectoryView(self.user_path)

    def showUserDir(self):
        self.updateDirectoryView(self.media_path)

    def copyFile(self, s, d):
        try:
            shutil.copy(s, d)
            return True
        except Exception as e:
            LOG.error("Copy file error: {}".format(e))
            STATUS.emit('error', OPERATOR_ERROR,
                        "Copy file error: {}".format(e))
            return False

    # moves the selection up
    # used with MPG scrolling
    def up(self):
        self.select_row('up')

    # moves the selection down
    # used with MPG scrolling
    def down(self):
        self.select_row('down')

    def select_row(self, style='down'):
        style = style.lower()
        selectionModel = self.list.selectionModel()
        row = selectionModel.currentIndex().row()
        self.rows = self.model.rowCount(self.list.rootIndex())

        if style == 'last':
            row = self.rows
        elif style == 'up':
            if row > 0:
                row -= 1
            else:
                row = 0
        elif style == 'down':
            if row < self.rows:
                row += 1
            else:
                row = self.rows
        else:
            return
        top = self.model.index(row, 0, self.list.rootIndex())
        selectionModel.setCurrentIndex(
            top, QItemSelectionModel.Select | QItemSelectionModel.Rows)
        selection = QItemSelection(top, top)
        selectionModel.clearSelection()
        selectionModel.select(selection, QItemSelectionModel.Select)

    # returns the current highlighted (selected) path as well as
    # whether it's a file or not.
    def getCurrentSelected(self):
        selectionModel = self.list.selectionModel()
        index = selectionModel.currentIndex()
        dir_path = self.model.filePath(index)
        if self.model.fileInfo(index).isFile():
            return (dir_path, True)
        else:
            return (dir_path, False)

    # This can be class patched to do something else
    def load(self, fname=None):
        if fname is None:
            self._getPathActivated()
            return
        self.recordBookKeeping()
        ACTION.OPEN_PROGRAM(fname)
        STATUS.emit('update-machine-log', 'Loaded: ' + fname, 'TIME')

    # This can be class patched to do something else
    def recordBookKeeping(self):
        fname = self.currentPath
        if fname is None:
            return
        if self.PREFS_:
            self.PREFS_.putpref('last_loaded_directory', self.model.rootPath(),
                                str, 'BOOK_KEEPING')
            self.PREFS_.putpref('RecentPath_0', fname, str, 'BOOK_KEEPING')
Example #20
0
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 MainWindow(QMainWindow, Ui_MainWindow):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent=parent)

        # 初始化工作目录
        self.workdir = os.getcwd() + os.sep + get_default_workdir()

        # 初始化UI
        self._initUI()

        # 初始化文件管理器
        self.model = QFileSystemModel()

        # 初始化两个数组,用于向鼠标追踪方法传递数据
        self.axis_y_data_arr = []
        self.axis_x_dict_arr = []

        # 判断程序所在目录下data文件夹是否存在
        if os.path.isdir(self.workdir):
            # 存在,设置路径提示文本框
            self.lineEdit.setText(self.workdir)
            # 设置工作目录
            self.changeworkdir(self.workdir)
        else:
            # 首次启动,默认工作目录未找到,询问用户是否设置工作目录
            self.set_work_dir(True)

        # 状态栏提示欢迎语
        self.statusbar.showMessage("RSA:Welcome to Rock-Spectrum-Assistant")

        # 唤醒窗口,把窗口提到最前方
        self.raise_()

    def _initUI(self):
        # 载入UI.py
        self.setupUi(self)

        # 关联菜单栏到方法
        self.aboutwin = aboutDialog()
        self.Help.triggered.connect(self.helpmanual)
        self.helpwin = helpdialog()
        self.About.triggered.connect(self.aboutthisprogram)
        self.notepad = Notepad(self.workdir)
        self.Notepad.triggered.connect(lambda: self.addfile(True))

        self.findpeaksdialog = findpeaksdialog()
        self.Findpeaks.triggered.connect(self.findpeakswin)
        # 把子窗体plot、clear按钮点击交给RSA主窗体处理
        self.findpeaksdialog.findpeaksbutton.clicked.connect(
            self.findpeaksplot)
        self.findpeaksdialog.clearbutton.clicked.connect(self.clearfindpeaks)

        self.searchdialog = searchdialog(self.workdir)
        # 把搜索子窗体双击事件连接到RSA主窗体进行处理
        self.searchdialog.listWidget.itemDoubleClicked.connect(
            self.searchdialogitemdoubleclicked)

        # checkbox相关设置
        self.crosshaircheckbox.stateChanged.connect(
            lambda: self.crosshaircheckboxstateChanged(self.crosshaircheckbox))
        self.showgridcheckbox.stateChanged.connect(
            lambda: self.showgridcheckboxstateChanged(self.showgridcheckbox))

        # treeView右键菜单关联
        self.treeView.setContextMenuPolicy(Qt.CustomContextMenu)
        self.treeView.customContextMenuRequested.connect(self.context_menu)

        # 绘图板鼠标追踪鼠标跟踪
        self.pyqtgraph.setMouseTracking(True)
        self.pyqtgraph.scene().sigMouseMoved.connect(self.mouseMoved)

        # 获取绘图板绘图对象
        self.plotItem = self.pyqtgraph.getPlotItem()

        # 设置x、y坐标轴文字
        self.plotItem.setLabel(axis='bottom', text=get_x_axis_lable())
        self.plotItem.setLabel(axis='left', text=get_y_axis_lable())

    def changeworkdir(self, path, isdialogflag=False):
        # 设置treeview工作目录,代码顺序不能颠倒
        self.model.setRootPath(path)
        self.treeView.setModel(self.model)
        self.treeView.setRootIndex(self.model.index(path))
        # 重设工作目录
        self.workdir = self.model.rootPath()
        # 更换目录做一次清空,仅限不是由子窗口调用的时候
        if not isdialogflag:
            self.on_clearbutton_clicked()

    def context_menu(self):
        # 设定鼠标点击位置的指针
        index = self.treeView.currentIndex()
        self.mouseindex = self.model.filePath(index)

        # 添加右键菜单
        menu = QMenu()

        plotfile = menu.addAction("plot")
        plotfile.triggered.connect(self.plotfile)
        addfile = menu.addAction("add")
        addfile.triggered.connect(self.addfile)
        editfile = menu.addAction("edit")
        editfile.triggered.connect(self.editfile)
        removefile = menu.addAction("remove")
        removefile.triggered.connect(self.removefile)
        opendirectory = menu.addAction("open directory")
        opendirectory.triggered.connect(self.opendirectory)

        cursor = QCursor()
        menu.exec_(cursor.pos())

    def plotfile(self):
        # 绘图板上图形过多,提示用户
        if self.plotcount == get_plot_limit():
            QMessageBox.information(
                self, "温馨提示",
                "绘图板上已经超过" + str(get_plot_limit()) + "个图形,过多绘图会导致无法分辨,请清除绘图板",
                QMessageBox.Close, QMessageBox.Close)

        # 判断是文件指针指向的是一个目录还是文件
        if os.path.isdir(self.mouseindex) or self.mouseindex == "":
            # 指向一个目录,提示用户
            QMessageBox.information(self, "温馨提示", "选择的是一个目录,请选择一个数据文件",
                                    QMessageBox.Close, QMessageBox.Close)

            # 指向一个文件,尝试读取数据
        elif os.path.isfile(self.mouseindex):

            try:
                self.plot(self.mouseindex)
            # 捕获异常
            except Exception as e:
                QMessageBox.information(self, "警告",
                                        "文件打开失败\n请检查数据格式\n{}".format(e),
                                        QMessageBox.Close, QMessageBox.Close)
                self.statusbar.showMessage(
                    "RSA:please check the data file format")

    def plot(self, file):

        # 选取画笔颜色,防止溢出
        colorindex = self.plotcount
        if self.plotcount > len(color) - 1:
            colorindex = self.plotcount % len(color)

        # 载入数据,如果数据格式有变化这里会报错
        data = load_data(file)
        filepath, fullfilename = os.path.split(file)
        filename, extension = os.path.splitext(fullfilename)
        # 记录数据给findpeaks使用
        self.lastplotdata = data

        # 获取用来设置x轴坐标轴文字的数据
        x_dict = self.get_axix_x_data(data)

        # 将数据保存到数组用于鼠标追踪调用
        self.axis_y_data_arr.append(data)
        self.axis_x_dict_arr.append(x_dict)
        self.pyqtgraph.plot(x=list(x_dict.keys()),
                            y=data.iloc[:, 0].values,
                            pen=color[colorindex],
                            name=filename,
                            antialias=True)

        # 用绘图板上的数据数组长度来给绘图计数器赋值
        DataItem_list = self.plotItem.listDataItems()
        self.plotcount = len(DataItem_list)

        # 根据UI文件默认配置,添加十字丝和网格
        self.crosshaircheckboxstateChanged(self.crosshaircheckbox)
        self.showgridcheckboxstateChanged(self.showgridcheckbox)

        # 添加图例,仅第二次绘图运行
        if self.plotcount == 2 and self.plotItem.legend is None:
            self.plotItem.addLegend()
            for item in DataItem_list:
                self.plotItem.legend.addItem(item=item, name=item.name())

        # 在状态栏上显示当前绘图的文件和绘图总数
        self.statusbar.showMessage("RSA:plot /" + file.lstrip(self.workdir) +
                                   " ,当前绘图总数 " + str(self.plotcount))

    # TODO:恢复默认坐标值不生效
    def get_axix_x_data(self, data=None):
        stringaxis = self.plotItem.getAxis(name='bottom')
        # 如果没有数据传入,坐标值设为默认值
        if data is None:
            stringaxis.setTickSpacing()
        else:
            # 将数据表索引(波长)转换为绘图时的坐标轴数据
            x_dict = dict(enumerate(data.index))
            axis_x_data = [
                (i, list(data.index)[i])
                for i in range(0, len(data.index), get_ticks_spacing())
            ]
            stringaxis.setTicks([axis_x_data, x_dict.items()])
            return x_dict

    # 打开记事本与新建文件都是进行同一个操作,合并两个方法
    def addfile(self, flag=False):
        if flag is True:
            self.statusbar.showMessage("RSA:start notepad")
        else:
            self.statusbar.showMessage("RSA:add file")
            self.notepad.newFile(get_testdata())
        self.notepad.show()

    def editfile(self):
        if os.path.isdir(self.mouseindex):
            # 打开的是一个目录而不是一个文件时提示用户
            QMessageBox.information(self, "温馨提示", "选择的是一个目录,请选择一个数据文件",
                                    QMessageBox.Close, QMessageBox.Close)
        elif os.path.isfile(self.mouseindex):
            # 分割文件拓展名,并与默认数据文件拓展名作比较
            file_extension = os.path.splitext(self.mouseindex)[1]
            if file_extension == get_default_data_file_extension():
                self.edit(self.mouseindex)
            else:
                QMessageBox.information(self, "温馨提示", "文件拓展名与数据库默认拓展名不相符",
                                        QMessageBox.Close, QMessageBox.Close)

    def edit(self, file):
        self.statusbar.showMessage("RSA:edit /" + file.lstrip(self.workdir))
        self.notepad.openFileEvent(file)
        self.notepad.show()

    def removefile(self):
        if os.path.isdir(self.mouseindex):
            QMessageBox.question(self, "温馨提示",
                                 "您所选择的是一个文件夹\n出于数据安全考虑\n本程序不提供删除文件夹功能",
                                 QMessageBox.Close, QMessageBox.Close)
        elif os.path.isfile(self.mouseindex):
            filepath, filename = os.path.split(self.mouseindex)
            reply = QMessageBox.warning(self, "温馨提示", "是否确定删除文件" + filename,
                                        QMessageBox.Yes | QMessageBox.Cancel,
                                        QMessageBox.Cancel)
            if reply == QMessageBox.Yes:
                # 用户点击Yes,删除文件
                os.remove(self.mouseindex)
                self.statusbar.showMessage(
                    "RSA:remove /" + self.mouseindex.lstrip(self.workdir))
            if reply == QMessageBox.Cancel:
                pass

    def opendirectory(self):
        # 防止用户点到空白目录无响应
        if self.mouseindex == "":
            path = self.workdir
        else:
            path = self.mouseindex
        # 判断指针点击的是文件还是目录并打开
        if os.path.isfile(path):
            path = os.path.dirname(path)
        self.statusbar.showMessage("RSA:open directory /" +
                                   path.lstrip(self.workdir))
        os.startfile(path)

    @pyqtSlot()
    def on_clearbutton_clicked(self):
        self.plotItem.getAxis('bottom').setTicks(ticks=None)
        self.plotItem.getAxis('right').setTicks(ticks=None)
        # 清空图例栏的内容
        if self.plotItem.legend is not None:
            DataItems_list = self.plotItem.listDataItems()
            for item in DataItems_list:
                self.plotItem.legend.removeItem(name=item.name())
        if self.findpeaksdialog.isVisible() is True:
            self.clearfindpeaks()
            self.findpeaksdialog.close()
        self.axis_y_data_arr.clear()
        self.axis_x_dict_arr.clear()
        self.pyqtgraph.clear()
        self.plotcount = len(self.axis_y_data_arr)
        self.statusbar.showMessage("RSA:reset plot board")

    @pyqtSlot()
    def on_browsebutton_clicked(self, path=None, isdialogflag=False):
        if path is None:
            path = self.get_path_from_user()

        # TODO:当窗口返回的是工作目录是空字符串,重复要求用户设置
        if path == '':
            self.set_work_dir(False)

        else:
            self.changeworkdir(path, isdialogflag)
            self.lineEdit.setText(path)
            # 重设记事本模块工作目录
            self.notepad.changeworkdir(self.workdir)
            self.statusbar.showMessage("RSA:change work dir to /" + path)

    def set_work_dir(self, firstworkdirflag=False):
        if firstworkdirflag is True:
            text = "没有找到默认数据文件夹,是否浏览目录设置"
        else:
            text = "未选择工作目录,是否浏览目录设置"
        self.lineEdit.setText("Working directory")
        reply = QMessageBox.question(self, "温馨提示", text,
                                     QMessageBox.Yes | QMessageBox.Cancel,
                                     QMessageBox.Cancel)
        if reply == QMessageBox.Yes:
            # 用户点击Yes,设置工作目录,模拟用户点击浏览按钮
            self.on_browsebutton_clicked()
        if reply == QMessageBox.Cancel:
            pass

    # 我是不想搞这个额外的方法的,但是如果不做分离上面两个函数不正常
    def get_path_from_user(self):
        filedialog = QFileDialog()
        filedialog.setViewMode(QFileDialog.Detail)
        path = QFileDialog.getExistingDirectory(
            self, '请选择数据文件夹',
            os.environ['userprofile'] + os.path.sep + 'desktop')
        return path

    @pyqtSlot()
    def on_searchbutton_clicked(self):
        self.statusbar.showMessage("RSA:search dir is " + self.workdir)
        self.searchdialog.show()

    def searchdialogitemdoubleclicked(self, event):
        index = event.text()
        if os.path.isdir(index):
            self.on_browsebutton_clicked(index, isdialogflag=True)
            self.searchdialog.close()
        elif os.path.isfile(index):
            path = os.path.dirname(index)
            self.on_browsebutton_clicked(path, isdialogflag=True)
            self.mouseindex = index
            self.plotfile()
            self.searchdialog.close()

    def aboutthisprogram(self):
        self.statusbar.showMessage("RSA:about this program")
        self.aboutwin.show()

    def helpmanual(self):
        self.statusbar.showMessage("RSA:start help manual")
        self.helpwin.show()

    def findpeakswin(self):
        # 检查绘图板上是否存在图形
        if self.plotcount == 1:
            self.statusbar.showMessage("RSA:start find peaks /" +
                                       self.mouseindex.lstrip(self.workdir))
            self.findpeaksdialog.show()
        else:
            self.statusbar.showMessage(
                "RSA:plotcount is {},find peaks will not run".format(
                    self.plotcount))
            QMessageBox.information(
                self, "提示",
                "findpeas仅能在绘图板上有一个图形时运行\n当前绘图数为{}".format(self.plotcount),
                QMessageBox.Close, QMessageBox.Close)

    def findpeaksplot(self):
        data = self.lastplotdata.iloc[:, 0].values
        # 这个值用来对齐绘图板显示的x轴坐标和返回的峰x轴坐标的差异
        firstindex = int(self.lastplotdata.index[0])
        peaks = self.findpeaksdialog.find_peaks(data)
        self.findpeaksdialog.textEdit.clear()
        self.findpeaksdialog.textEdit.setEnabled(True)
        self.findpeaksdialog.textEdit.append("peak index,peak values")
        # 添加元素前清空一次
        self.clearpeaksmark()
        for peak in peaks:
            if self.findpeaksdialog.peakmarkcheckbox.isChecked():
                arrow = pg.ArrowItem(pos=(peak, data[peak]), angle=-90)
                self.plotItem.addItem(arrow)
                self.findpeaksdialog.peaksmarklist.append(arrow)
            if self.findpeaksdialog.peaktextchebox.isChecked():
                text = pg.TextItem("peak")
                text.setPos(peak, data[peak])
                self.plotItem.addItem(text)
                self.findpeaksdialog.peaksmarklist.append(text)
            self.findpeaksdialog.textEdit.append("[" + str(peak + firstindex) +
                                                 "," + str(data[peak]) + "]")

    def clearfindpeaks(self):
        self.clearpeaksmark()
        self.findpeaksdialog.clear()

    def clearpeaksmark(self):
        for mark in self.findpeaksdialog.peaksmarklist:
            self.plotItem.removeItem(mark)

    def crosshaircheckboxstateChanged(self, checkbox):
        # 防止未绘图连续点击两次checkbox程序崩溃
        if self.plotcount == 0:
            pass
        else:
            # 十字光标相关设置,添加元素到绘图元件中
            if checkbox.isChecked():
                self.mouseTrackingLabel = pg.TextItem()  # 创建一个文本项
                self.plotItem.addItem(self.mouseTrackingLabel)  # 在图形部件中添加文本项
                self.vLine = pg.InfiniteLine(
                    angle=90,
                    movable=False,
                )  # 创建一个垂直线条
                self.hLine = pg.InfiniteLine(
                    angle=0,
                    movable=False,
                )  # 创建一个水平线条
                self.plotItem.addItem(self.vLine,
                                      ignoreBounds=True)  # 在图形部件中添加垂直线条
                self.plotItem.addItem(self.hLine,
                                      ignoreBounds=True)  # 在图形部件中添加水平线条
                self.showgridcheckboxstateChanged(
                    self.showgridcheckbox)  # 显示网格

            # 如果用户取消了checkbox的状态,那就删除这三个item
            # 判断元素是否存在,因为三个元素是统一添加、统一删除的,判断一个元素是不是存在就行
            flag = True
            try:
                self.mouseTrackingLabel
            except AttributeError:
                flag = False
            if checkbox.isChecked() is False and flag is True:
                self.plotItem.removeItem(self.hLine)
                self.plotItem.removeItem(self.vLine)
                self.plotItem.removeItem(self.mouseTrackingLabel)

    def showgridcheckboxstateChanged(self, checkbox):
        if checkbox.checkState():
            self.plotItem.showGrid(x=True, y=True, alpha=get_grid_alpha())
        else:
            self.plotItem.showGrid(x=False, y=False)

    def mouseMoved(self, event):
        # 必须勾选crosshair的checkbox且绘图板上有图形才会进行鼠标追踪
        if self.crosshaircheckbox.isChecked() and self.plotcount >= 1:
            if event is None:
                pass
            else:
                pos = event  # 获取事件的鼠标位置
                try:
                    # 如果鼠标位置在绘图部件中
                    if self.plotItem.sceneBoundingRect().contains(pos):
                        mousePoint = self.plotItem.vb.mapSceneToView(
                            pos)  # 转换鼠标坐标
                        index = int(mousePoint.x())  # 鼠标所处的X轴坐标
                        # pos_y = int(mousePoint.y())  # 鼠标所处的Y轴坐标
                        # TODO:如果多次载入数据长度不一样,这里可能会引发错误
                        if -1 < index < len(self.axis_y_data_arr[0].index):
                            # 在label中写入HTML
                            self.mouseTrackingLabel.setHtml(
                                self.generate_mousetracking_label(
                                    self.plotcount, index))
                            self.mouseTrackingLabel.setPos(
                                mousePoint.x(), mousePoint.y())  # 设置label的位置
                        # 设置垂直线条和水平线条的位置组成十字光标
                        self.vLine.setPos(mousePoint.x())
                        self.hLine.setPos(mousePoint.y())
                except Exception as e:
                    QMessageBox.information(self, "提示", "鼠标追踪错误\n{}".format(e),
                                            QMessageBox.Close,
                                            QMessageBox.Close)

    def generate_mousetracking_label(self, count, index):
        labletext = ""
        for i in range(count):
            labletext = labletext + "<p style='color:white'><strong>{}:{} {}:{}</strong></p>"\
                .format(get_x_axis_lable(), self.axis_x_dict_arr[i][index], get_y_axis_lable(), self.axis_y_data_arr[i].iloc[index].values)
        return labletext

    def closeEvent(self, QCloseEvent):
        # 退出程序确认,使用QMessageBox提示
        reply = QMessageBox.warning(self, "温馨提示", "即将退出RSA, 确定?",
                                    QMessageBox.Yes | QMessageBox.Cancel,
                                    QMessageBox.Yes)
        if reply == QMessageBox.Yes:
            QCloseEvent.accept()
            # 关闭所有子窗体
            self.aboutwin.close()
            self.helpwin.close()
            self.searchdialog.close()
            self.findpeaksdialog.close()
            self.notepad.close()
        if reply == QMessageBox.Cancel:
            QCloseEvent.ignore()
Example #22
0
class Explorer(QDialog):
    def __init__(self,
                 parent,
                 window_title=_("Select resources"),
                 subtitle=_("Select files and/or folders to include")):
        super().__init__(parent)
        self.logger = logging.getLogger(__name__)
        self.setModal(True)
        self.setSizeGripEnabled(True)
        self.setWindowTitle(window_title)
        self.subtitle = subtitle
        self.file_set = set()
        self.config = Configuration()
        self.__init_ui__()
        self.filename_filter = FilenameFilter()
        #self.show()

    def __init_ui__(self):
        # layout
        vert = QVBoxLayout(self)
        vert.setContentsMargins(0, 0, 0, 0)

        resource_dir = self.config.cfg_resource_dir()

        p_top = QVBoxLayout()
        lb_subtitle = QLabel(self.subtitle)
        lb_subtitle.setContentsMargins(10, 10, 10, 0)
        p_top.addWidget(lb_subtitle)

        self.model = QFileSystemModel()
        self.model.setRootPath(resource_dir)

        self.view = QTreeView()
        self.view.setModel(self.model)
        self.view.setRootIndex(self.model.index(self.model.rootPath()))
        self.view.setAlternatingRowColors(True)
        self.view.setSelectionMode(QAbstractItemView.MultiSelection)
        self.view.selectionModel().selectionChanged.connect(
            self.selection_changed)
        self.view.collapsed.connect(self.item_collapsed)
        self.view.expanded.connect(self.item_expanded)
        p_top.addWidget(self.view)

        p_info = QHBoxLayout()
        lb_resource = QLabel(_("resource dir") + ": " + resource_dir)
        lb_resource.setContentsMargins(10, 0, 10, 0)

        self.lb_selection_count = QLabel(
            str(self.selected_file_count()) + " " + _("resources selected"))
        self.lb_selection_count.setContentsMargins(10, 0, 10, 0)

        p_info.addWidget(self.lb_selection_count)
        p_info.addStretch(1)
        p_info.addWidget(lb_resource)

        p_top.addLayout(p_info)

        p_bottom = QHBoxLayout()

        self.pb_deselect = QPushButton(_("Deselect all"))
        self.pb_deselect.clicked.connect(self.pb_deselect_clicked)
        self.pb_deselect.setEnabled(self.selected_file_count() > 0)
        p_bottom.addWidget(self.pb_deselect)

        p_bottom.addStretch(1)
        self.pb_ok = QPushButton(_("OK"))
        self.pb_ok.setAutoDefault(True)
        self.pb_ok.clicked.connect(self.accept)
        p_bottom.addWidget(self.pb_ok)

        pb_cancel = QPushButton(_("Cancel"))
        pb_cancel.clicked.connect(self.reject)
        p_bottom.addWidget(pb_cancel)

        vert.addLayout(p_top)
        vert.addLayout(p_bottom)

        self.setLayout(vert)
        self.resize(self.config.explorer_width(),
                    self.config.explorer_height())
        width = self.view.width() - 50
        self.view.setColumnWidth(0, width / 2)
        self.view.setColumnWidth(1, width / 6)
        self.view.setColumnWidth(2, width / 6)
        self.view.setColumnWidth(3, width / 6)

    def __persist__(self):
        # persist properties of the explorer
        self.config.set_explorer_width(self.width())
        self.config.set_explorer_height(self.height())
        self.config.persist()

    def __compute_filenames__(self, item_selection):
        # item_selection: a QItemSelection
        # return corresponding absolute filenames as a set, including filenames in underlying folders
        s = set()
        for index in item_selection.indexes():
            # we have an index for each column in the model
            if index.column() == 0:
                path = index.model().filePath(index)
                if os.path.isdir(path):
                    for root, directories, filenames in os.walk(path):
                        for filename in filenames:
                            if self.filename_filter.accept(filename):
                                s.add(os.path.join(root, filename))
                elif os.path.isfile(path):
                    s.add(path)
                else:
                    self.logger.warn("isUnknownThing", path)
        return s

    def showEvent(self, QShowEvent):
        #self.pb_ok.setFocus()
        pass

    def set_filename_filter(self, filename_filter):
        # set the FilenameFilter
        self.filename_filter = filename_filter

    def selected_file_count(self):
        return len(self.file_set)

    def selected_file_set(self):
        return frozenset(self.file_set)

    def selection_changed(self, selected, deselected):
        # selected, deselected: PyQt5.QtCore.QItemSelection
        selected_filenames = self.__compute_filenames__(selected)
        self.file_set.update(selected_filenames)
        deselected_filenames = self.__compute_filenames__(deselected)
        self.file_set.difference_update(deselected_filenames)

        self.pb_deselect.setEnabled(self.selected_file_count() > 0)
        self.lb_selection_count.setText(
            str(self.selected_file_count()) + " " + _("resources selected"))

    def item_expanded(self, index):
        # index: a QModelIndex
        # show all child items selected/deselected in accordance with state of parent folder
        pass

    def item_collapsed(self, index):
        pass

    def pb_deselect_clicked(self):
        self.view.selectionModel().clear()

    def hideEvent(self, QHideEvent):
        self.__persist__()
Example #23
0
class Explorer(QDialog):
    def __init__(
        self, parent, window_title=_("Select resources"), subtitle=_("Select files and/or folders to include")
    ):
        super().__init__(parent)
        self.logger = logging.getLogger(__name__)
        self.setModal(True)
        self.setSizeGripEnabled(True)
        self.setWindowTitle(window_title)
        self.subtitle = subtitle
        self.file_set = set()
        self.config = Configuration()
        self.__init_ui__()
        self.filename_filter = FilenameFilter()
        # self.show()

    def __init_ui__(self):
        # layout
        vert = QVBoxLayout(self)
        vert.setContentsMargins(0, 0, 0, 0)

        resource_dir = self.config.cfg_resource_dir()

        p_top = QVBoxLayout()
        lb_subtitle = QLabel(self.subtitle)
        lb_subtitle.setContentsMargins(10, 10, 10, 0)
        p_top.addWidget(lb_subtitle)

        self.model = QFileSystemModel()
        self.model.setRootPath(resource_dir)

        self.view = QTreeView()
        self.view.setModel(self.model)
        self.view.setRootIndex(self.model.index(self.model.rootPath()))
        self.view.setAlternatingRowColors(True)
        self.view.setSelectionMode(QAbstractItemView.MultiSelection)
        self.view.selectionModel().selectionChanged.connect(self.selection_changed)
        self.view.collapsed.connect(self.item_collapsed)
        self.view.expanded.connect(self.item_expanded)
        p_top.addWidget(self.view)

        p_info = QHBoxLayout()
        lb_resource = QLabel(_("resource dir") + ": " + resource_dir)
        lb_resource.setContentsMargins(10, 0, 10, 0)

        self.lb_selection_count = QLabel(str(self.selected_file_count()) + " " + _("resources selected"))
        self.lb_selection_count.setContentsMargins(10, 0, 10, 0)

        p_info.addWidget(self.lb_selection_count)
        p_info.addStretch(1)
        p_info.addWidget(lb_resource)

        p_top.addLayout(p_info)

        p_bottom = QHBoxLayout()

        self.pb_deselect = QPushButton(_("Deselect all"))
        self.pb_deselect.clicked.connect(self.pb_deselect_clicked)
        self.pb_deselect.setEnabled(self.selected_file_count() > 0)
        p_bottom.addWidget(self.pb_deselect)

        p_bottom.addStretch(1)
        self.pb_ok = QPushButton(_("OK"))
        self.pb_ok.setAutoDefault(True)
        self.pb_ok.clicked.connect(self.accept)
        p_bottom.addWidget(self.pb_ok)

        pb_cancel = QPushButton(_("Cancel"))
        pb_cancel.clicked.connect(self.reject)
        p_bottom.addWidget(pb_cancel)

        vert.addLayout(p_top)
        vert.addLayout(p_bottom)

        self.setLayout(vert)
        self.resize(self.config.explorer_width(), self.config.explorer_height())
        width = self.view.width() - 50
        self.view.setColumnWidth(0, width / 2)
        self.view.setColumnWidth(1, width / 6)
        self.view.setColumnWidth(2, width / 6)
        self.view.setColumnWidth(3, width / 6)

    def __persist__(self):
        # persist properties of the explorer
        self.config.set_explorer_width(self.width())
        self.config.set_explorer_height(self.height())
        self.config.persist()

    def __compute_filenames__(self, item_selection):
        # item_selection: a QItemSelection
        # return corresponding absolute filenames as a set, including filenames in underlying folders
        s = set()
        for index in item_selection.indexes():
            # we have an index for each column in the model
            if index.column() == 0:
                path = index.model().filePath(index)
                if os.path.isdir(path):
                    for root, directories, filenames in os.walk(path):
                        for filename in filenames:
                            if self.filename_filter.accept(filename):
                                s.add(os.path.join(root, filename))
                elif os.path.isfile(path):
                    s.add(path)
                else:
                    self.logger.warn("isUnknownThing", path)
        return s

    def showEvent(self, QShowEvent):
        # self.pb_ok.setFocus()
        pass

    def set_filename_filter(self, filename_filter):
        # set the FilenameFilter
        self.filename_filter = filename_filter

    def selected_file_count(self):
        return len(self.file_set)

    def selected_file_set(self):
        return frozenset(self.file_set)

    def selection_changed(self, selected, deselected):
        # selected, deselected: PyQt5.QtCore.QItemSelection
        selected_filenames = self.__compute_filenames__(selected)
        self.file_set.update(selected_filenames)
        deselected_filenames = self.__compute_filenames__(deselected)
        self.file_set.difference_update(deselected_filenames)

        self.pb_deselect.setEnabled(self.selected_file_count() > 0)
        self.lb_selection_count.setText(str(self.selected_file_count()) + " " + _("resources selected"))

    def item_expanded(self, index):
        # index: a QModelIndex
        # show all child items selected/deselected in accordance with state of parent folder
        pass

    def item_collapsed(self, index):
        pass

    def pb_deselect_clicked(self):
        self.view.selectionModel().clear()

    def hideEvent(self, QHideEvent):
        self.__persist__()