def __init__(self, parent=None, subfolders=False, status=None): QTreeView.__init__(self, parent) self._load = False # If True a loadFiles signal is emitted when # an index is clicked. See selectionChanged. dirmodel = QDirModel() dirmodel.setSorting(QDir.IgnoreCase) dirmodel.setFilter(QDir.Dirs | QDir.NoDotAndDotDot) dirmodel.setReadOnly(False) dirmodel.setLazyChildCount(False) dirmodel.setResolveSymlinks(False) header = PuddleHeader(Qt.Horizontal, self) self.setHeader(header) self.header().setSectionResizeMode(QHeaderView.ResizeToContents) self.setModel(dirmodel) [self.hideColumn(column) for column in range(1, 4)] self.header().hide() self.subfolders = subfolders self.setSelectionMode(self.ExtendedSelection) self._lastselection = 0 # If > 0 appends files. See selectionChanged self._load = True self.setDragEnabled(False) self.setAcceptDrops(True) self.setDropIndicatorShown(True) self._dropaction = Qt.MoveAction self._threadRunning = False self._select = True self.expanded.connect(lambda discarded: self.resizeColumnToContents(0))
def __init__(self, mainWindow, parent = None): super().__init__(parent) self.mDirectoryEdit = QLineEdit() self.mMapsView = MapsView(mainWindow) self.setObjectName("MapsDock") widget = QWidget(self) layout = QVBoxLayout(widget) layout.setContentsMargins(5, 5, 5, 5) dirLayout = QHBoxLayout() # QDirModel is obsolete, but I could not get QFileSystemModel to work here model = QDirModel(self) model.setFilter(QDir.AllDirs | QDir.Dirs | QDir.Drives | QDir.NoDotAndDotDot) completer = QCompleter(model, self) self.mDirectoryEdit.setCompleter(completer) button = QPushButton(self.tr("Browse...")) dirLayout.addWidget(self.mDirectoryEdit) dirLayout.addWidget(button) layout.addWidget(self.mMapsView) layout.addLayout(dirLayout) self.setWidget(widget) self.retranslateUi() button.clicked.connect(self.browse) prefs = preferences.Preferences.instance() prefs.mapsDirectoryChanged.connect(self.onMapsDirectoryChanged) self.mDirectoryEdit.setText(prefs.mapsDirectory()) self.mDirectoryEdit.returnPressed.connect(self.editedMapsDirectory)
def show_dir(self): model = QDirModel() model.setFilter(QDir.AllDirs | QDir.Files | QDir.NoDotAndDotDot) model.setNameFilters(["*.pickle", '*.pickle.gz']) _dir = self.lineEdit.text() if not _dir: _dir = '.' self.treeView.setModel(model) self.treeView.setRootIndex(model.index(_dir))
class SearchWidget(QFrame): """Widget, appeared, when Ctrl+F pressed. Has different forms for different search modes """ Normal = 'normal' Good = 'good' Bad = 'bad' Incorrect = 'incorrect' visibilityChanged = pyqtSignal(bool) """ visibilityChanged(visible) **Signal** emitted, when widget has been shown or hidden """ # pylint: disable=W0105 searchInDirectoryStartPressed = pyqtSignal(type(re.compile('')), list, str) """ searchInDirectoryStartPressed(regEx, mask, path) **Signal** emitted, when 'search in directory' button had been pressed """ # pylint: disable=W0105 searchInDirectoryStopPressed = pyqtSignal() """ searchInDirectoryStopPressed() **Signal** emitted, when 'stop search in directory' button had been pressed """ # pylint: disable=W0105 replaceCheckedStartPressed = pyqtSignal(str) """ replaceCheckedStartPressed(replText) **Signal** emitted, when 'replace checked' button had been pressed """ # pylint: disable=W0105 replaceCheckedStopPressed = pyqtSignal() """ replaceCheckedStartPressed() **Signal** emitted, when 'stop replacing checked' button had been pressed """ # pylint: disable=W0105 searchRegExpChanged = pyqtSignal(type(re.compile(''))) """ searchRegExpValidStateChanged(regEx) **Signal** emitted, when search regexp has been changed. If reg exp is invalid - regEx object contains empty pattern """ # pylint: disable=W0105 searchNext = pyqtSignal() """ searchNext() **Signal** emitted, when 'Search Next' had been pressed """ # pylint: disable=W0105 searchPrevious = pyqtSignal() """ searchPrevious() **Signal** emitted, when 'Search Previous' had been pressed """ # pylint: disable=W0105 replaceFileOne = pyqtSignal(str) """ replaceFileOne(replText) **Signal** emitted, when 'Replace' had been pressed """ # pylint: disable=W0105 replaceFileAll = pyqtSignal(str) """ replaceFileAll(replText) **Signal** emitted, when 'Replace All' had been pressed """ # pylint: disable=W0105 def __init__(self, plugin): QFrame.__init__(self, core.workspace()) self._mode = None self.plugin = plugin uic.loadUi(os.path.join(os.path.dirname(__file__), 'SearchWidget.ui'), self) self.cbSearch.setCompleter(None) self.cbReplace.setCompleter(None) self.cbMask.setCompleter(None) self.fsModel = QDirModel(self.cbPath.lineEdit()) self.fsModel.setFilter(QDir.AllDirs | QDir.NoDotAndDotDot) self.cbPath.lineEdit().setCompleter(QCompleter(self.fsModel, self.cbPath.lineEdit())) self._pathBackspaceShortcut = QShortcut(QKeySequence("Ctrl+Backspace"), self.cbPath, self._onPathBackspace) self._pathBackspaceShortcut.setContext(Qt.WidgetWithChildrenShortcut) # TODO QDirModel is deprecated but QCompleter does not yet handle # QFileSystemodel - please update when possible.""" self.cbSearch.setCompleter(None) self.pbSearchStop.setVisible(False) self.pbReplaceCheckedStop.setVisible(False) self._progress = QProgressBar(self) self._progress.setAlignment(Qt.AlignCenter) self._progress.setToolTip(self.tr("Search in progress...")) self._progress.setMaximumSize(QSize(80, 16)) core.mainWindow().statusBar().insertPermanentWidget(1, self._progress) self._progress.setVisible(False) # cd up action self.tbCdUp = QToolButton(self.cbPath.lineEdit()) self.tbCdUp.setIcon(QIcon(":/enkiicons/go-up.png")) self.tbCdUp.setCursor(Qt.ArrowCursor) self.tbCdUp.installEventFilter(self) # for drawing button self.cbSearch.installEventFilter(self) # for catching Tab and Shift+Tab self.cbReplace.installEventFilter(self) # for catching Tab and Shift+Tab self.cbPath.installEventFilter(self) # for catching Tab and Shift+Tab self.cbMask.installEventFilter(self) # for catching Tab and Shift+Tab self._closeShortcut = QShortcut(QKeySequence("Esc"), self) self._closeShortcut.setContext(Qt.WidgetWithChildrenShortcut) self._closeShortcut.activated.connect(self.hide) # connections self.cbSearch.lineEdit().textChanged.connect(self._onSearchRegExpChanged) self.cbSearch.lineEdit().returnPressed.connect(self._onReturnPressed) self.cbReplace.lineEdit().returnPressed.connect(self._onReturnPressed) self.cbPath.lineEdit().returnPressed.connect(self._onReturnPressed) self.cbMask.lineEdit().returnPressed.connect(self._onReturnPressed) self.cbRegularExpression.stateChanged.connect(self._onSearchRegExpChanged) self.cbCaseSensitive.stateChanged.connect(self._onSearchRegExpChanged) self.cbWholeWord.stateChanged.connect(self._onSearchRegExpChanged) self.tbCdUp.clicked.connect(self._onCdUpPressed) self.pbNext.pressed.connect(self.searchNext) self.pbPrevious.pressed.connect(self.searchPrevious) self.pbSearchStop.pressed.connect(self.searchInDirectoryStopPressed) self.pbReplaceCheckedStop.pressed.connect(self.replaceCheckedStopPressed) core.mainWindow().hideAllWindows.connect(self.hide) core.workspace().escPressed.connect(self.hide) core.workspace().currentDocumentChanged.connect( lambda old, new: self.setVisible(self.isVisible() and new is not None)) def show(self): """Reimplemented function. Sends signal """ super(SearchWidget, self).show() self.visibilityChanged.emit(self.isVisible()) def hide(self): """Reimplemented function. Sends signal, returns focus to workspace """ super(SearchWidget, self).hide() core.workspace().focusCurrentDocument() self.visibilityChanged.emit(self.isVisible()) def setVisible(self, visible): """Reimplemented function. Sends signal """ super(SearchWidget, self).setVisible(visible) self.visibilityChanged.emit(self.isVisible()) def _regExEscape(self, text): """Improved version of re.escape() Doesn't escape space, comma, underscore. Escapes \n and \t """ text = re.escape(text) # re.escape escapes space, comma, underscore, but, it is not necessary and makes text not readable for symbol in (' ,_=\'"/:@#%&'): text = text.replace('\\' + symbol, symbol) text = text.replace('\\\n', '\\n') text = text.replace('\\\t', '\\t') return text def _makeEscapeSeqsVisible(self, text): """Replace invisible \n and \t with escape sequences """ text = text.replace('\\', '\\\\') text = text.replace('\t', '\\t') text = text.replace('\n', '\\n') return text def setMode(self, mode): """Change search mode. i.e. from "Search file" to "Replace directory" """ if self._mode == mode and self.isVisible(): if core.workspace().currentDocument() is not None and \ not core.workspace().currentDocument().hasFocus(): self.cbSearch.lineEdit().selectAll() self.cbSearch.setFocus() self._mode = mode # Set Search and Replace text document = core.workspace().currentDocument() if document is not None and \ document.hasFocus() and \ document.qutepart.selectedText: searchText = document.qutepart.selectedText self.cbReplace.setEditText(self._makeEscapeSeqsVisible(searchText)) if self.cbRegularExpression.checkState() == Qt.Checked: searchText = self._regExEscape(searchText) self.cbSearch.setEditText(searchText) if not self.cbReplace.lineEdit().text() and \ self.cbSearch.lineEdit().text() and \ not self.cbRegularExpression.checkState() == Qt.Checked: replaceText = self.cbSearch.lineEdit().text().replace('\\', '\\\\') self.cbReplace.setEditText(replaceText) # Move focus to Search edit self.cbSearch.setFocus() self.cbSearch.lineEdit().selectAll() # Set search path if mode & MODE_FLAG_DIRECTORY and \ not (self.isVisible() and self.cbPath.isVisible()): try: searchPath = os.path.abspath(str(os.path.curdir)) except OSError: # current directory might have been deleted pass else: self.cbPath.setEditText(searchPath) # Set widgets visibility flag according to state widgets = (self.wSearch, self.pbPrevious, self.pbNext, self.pbSearch, self.wReplace, self.wPath, self.pbReplace, self.pbReplaceAll, self.pbReplaceChecked, self.wOptions, self.wMask) # wSear pbPrev pbNext pbSear wRepl wPath pbRep pbRAll pbRCHK wOpti wMask visible = \ {MODE_SEARCH: (1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0,), MODE_REPLACE: (1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0,), MODE_SEARCH_DIRECTORY: (1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1,), MODE_REPLACE_DIRECTORY: (1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1,), MODE_SEARCH_OPENED_FILES: (1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1,), MODE_REPLACE_OPENED_FILES: (1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1,)} for i, widget in enumerate(widgets): widget.setVisible(visible[mode][i]) # Search next button text if mode == MODE_REPLACE: self.pbNext.setText('Next') else: self.pbNext.setText('Next↵') # Finaly show all with valid size self.show() # show before updating widgets and labels self._updateLabels() self._updateWidgets() def eventFilter(self, object_, event): """ Event filter for mode switch tool button Draws icons in the search and path lineEdits """ if event.type() == QEvent.Paint and object_ is self.tbCdUp: # draw CdUp button in search path QLineEdit toolButton = object_ lineEdit = self.cbPath.lineEdit() lineEdit.setContentsMargins(lineEdit.height(), 0, 0, 0) height = lineEdit.height() availableRect = QRect(0, 0, height, height) if toolButton.rect() != availableRect: toolButton.setGeometry(availableRect) painter = QPainter(toolButton) toolButton.icon().paint(painter, availableRect) return True elif event.type() == QEvent.KeyPress: # Tab and Shift+Tab in QLineEdits if event.key() == Qt.Key_Tab: self._moveFocus(1) return True elif event.key() == Qt.Key_Backtab: self._moveFocus(-1) return True return QFrame.eventFilter(self, object_, event) def _onReturnPressed(self): """Return or Enter pressed on widget. Search next or Replace next """ if self.pbReplace.isVisible(): self.pbReplace.click() elif self.pbNext.isVisible(): self.pbNext.click() elif self.pbSearch.isVisible(): self.pbSearch.click() elif self.pbSearchStop.isVisible(): self.pbSearchStop.click() def _onPathBackspace(self): """Ctrl+Backspace pressed on path. Remove 1 path level. Default behavior would be to remove one word on Linux or all on Windows """ path = self.cbPath.currentText() if path.endswith('/') or \ path.endswith('\\'): path = path[:-1] head, tail = os.path.split(path) if head and \ head != path: if not head.endswith(os.sep): head += os.sep self.cbPath.lineEdit().setText(head) def _moveFocus(self, step): """Move focus forward or backward according to step. Standard Qt Keyboard focus algorithm doesn't allow circular navigation """ allFocusableWidgets = (self.cbSearch, self.cbReplace, self.cbPath, self.cbMask) visibleWidgets = [widget for widget in allFocusableWidgets if widget.isVisible()] try: focusedIndex = visibleWidgets.index(QApplication.focusWidget()) except ValueError: print('Invalid focused widget in Search Widget', file=sys.stderr) return nextFocusedIndex = (focusedIndex + step) % len(visibleWidgets) visibleWidgets[nextFocusedIndex].setFocus() visibleWidgets[nextFocusedIndex].lineEdit().selectAll() def _updateLabels(self): """Update 'Search' 'Replace' 'Path' labels geometry """ width = 0 if self.lSearch.isVisible(): width = max(width, self.lSearch.minimumSizeHint().width()) if self.lReplace.isVisible(): width = max(width, self.lReplace.minimumSizeHint().width()) if self.lPath.isVisible(): width = max(width, self.lPath.minimumSizeHint().width()) self.lSearch.setMinimumWidth(width) self.lReplace.setMinimumWidth(width) self.lPath.setMinimumWidth(width) def _updateWidgets(self): """Update geometry of widgets with buttons """ width = 0 if self.wSearchRight.isVisible(): width = max(width, self.wSearchRight.minimumSizeHint().width()) if self.wReplaceRight.isVisible(): width = max(width, self.wReplaceRight.minimumSizeHint().width()) if self.wPathRight.isVisible(): width = max(width, self.wPathRight.minimumSizeHint().width()) self.wSearchRight.setMinimumWidth(width) self.wReplaceRight.setMinimumWidth(width) self.wPathRight.setMinimumWidth(width) def updateComboBoxes(self): """Update comboboxes with last used texts """ searchText = self.cbSearch.currentText() replaceText = self.cbReplace.currentText() maskText = self.cbMask.currentText() # search if searchText: index = self.cbSearch.findText(searchText) if index == -1: self.cbSearch.addItem(searchText) # replace if replaceText: index = self.cbReplace.findText(replaceText) if index == -1: self.cbReplace.addItem(replaceText) # mask if maskText: index = self.cbMask.findText(maskText) if index == -1: self.cbMask.addItem(maskText) def _searchPatternTextAndFlags(self): """Get search pattern and flags """ pattern = self.cbSearch.currentText() pattern = pattern.replace('\u2029', '\n') # replace unicode paragraph separator with habitual \n if not self.cbRegularExpression.checkState() == Qt.Checked: pattern = re.escape(pattern) if self.cbWholeWord.checkState() == Qt.Checked: pattern = r'\b' + pattern + r'\b' flags = 0 if not self.cbCaseSensitive.checkState() == Qt.Checked: flags = re.IGNORECASE return pattern, flags def getRegExp(self): """Read search parameters from controls and present it as a regular expression """ pattern, flags = self._searchPatternTextAndFlags() return re.compile(pattern, flags) def isSearchRegExpValid(self): """Try to compile search pattern to check if it is valid Returns bool result and text error """ pattern, flags = self._searchPatternTextAndFlags() try: re.compile(pattern, flags) except re.error as ex: return False, str(ex) return True, None def _getSearchMask(self): """Get search mask as list of patterns """ mask = [s.strip() for s in self.cbMask.currentText().split(' ')] # remove empty mask = [_f for _f in mask if _f] return mask def setState(self, state): """Change line edit color according to search result """ widget = self.cbSearch.lineEdit() color = {SearchWidget.Normal: QApplication.instance().palette().color(QPalette.Base), SearchWidget.Good: QColor(Qt.green), SearchWidget.Bad: QColor(Qt.red), SearchWidget.Incorrect: QColor(Qt.darkYellow)} stateColor = color[state] if state != SearchWidget.Normal: stateColor.setAlpha(100) pal = widget.palette() pal.setColor(widget.backgroundRole(), stateColor) widget.setPalette(pal) def setSearchInProgress(self, inProgress): """Search thread started or stopped """ self.pbSearchStop.setVisible(inProgress) self.pbSearch.setVisible(not inProgress) self._updateWidgets() self._progress.setVisible(inProgress) def onSearchProgressChanged(self, value, total): """Signal from the thread, progress changed """ self._progress.setValue(value) self._progress.setMaximum(total) def setReplaceInProgress(self, inProgress): """Replace thread started or stopped """ self.pbReplaceCheckedStop.setVisible(inProgress) self.pbReplaceChecked.setVisible(not inProgress) self._updateWidgets() def setSearchInFileActionsEnabled(self, enabled): """Set enabled state for Next, Prev, Replace, ReplaceAll """ for button in (self.pbNext, self.pbPrevious, self.pbReplace, self.pbReplaceAll): button.setEnabled(enabled) def _onSearchRegExpChanged(self): """User edited search text or checked/unchecked checkboxes """ valid, error = self.isSearchRegExpValid() if valid: self.setState(self.Normal) core.mainWindow().statusBar().clearMessage() self.pbSearch.setEnabled(len(self.getRegExp().pattern) > 0) else: core.mainWindow().statusBar().showMessage(error, 3000) self.setState(self.Incorrect) self.pbSearch.setEnabled(False) self.searchRegExpChanged.emit(re.compile('')) return self.searchRegExpChanged.emit(self.getRegExp()) def _onCdUpPressed(self): """User pressed "Up" button, need to remove one level from search path """ text = self.cbPath.currentText() if not os.path.exists(text): return editText = os.path.normpath(os.path.join(text, os.path.pardir)) self.cbPath.setEditText(editText) def on_pbSearch_pressed(self): """Handler of click on "Search" button (for search in directory) """ self.setState(SearchWidget.Normal) self.searchInDirectoryStartPressed.emit(self.getRegExp(), self._getSearchMask(), self.cbPath.currentText()) def on_pbReplace_pressed(self): """Handler of click on "Replace" (in file) button """ self.replaceFileOne.emit(self.cbReplace.currentText()) def on_pbReplaceAll_pressed(self): """Handler of click on "Replace all" (in file) button """ self.replaceFileAll.emit(self.cbReplace.currentText()) def on_pbReplaceChecked_pressed(self): """Handler of click on "Replace checked" (in directory) button """ self.replaceCheckedStartPressed.emit(self.cbReplace.currentText()) def on_pbBrowse_pressed(self): """Handler of click on "Browse" button. Explores FS for search directory path """ path = QFileDialog.getExistingDirectory(self, self.tr("Search path"), self.cbPath.currentText()) if path: self.cbPath.setEditText(path)
class ComboBox(QComboBox): """File browser combo box. Widget and functionality """ def __init__(self, fileBrowser): QComboBox.__init__(self, fileBrowser) self._fileBrowser = fileBrowser self.setAttribute(Qt.WA_MacShowFocusRect, False) self.setAttribute(Qt.WA_MacSmallSize) self.setEditable(True) self.setMinimumContentsLength(1) self.setSizeAdjustPolicy(QComboBox.AdjustToMinimumContentsLengthWithIcon) self.lineEdit().setReadOnly(False) self._completionModel = QDirModel(self.lineEdit()) self._completionModel.setFilter(QDir.AllDirs | QDir.NoDotAndDotDot) self.lineEdit().setCompleter(QCompleter(self._completionModel, self.lineEdit())) # TODO QDirModel is deprecated but QCompleter does not yet handle # QFileSystemModel - please update when possible. self._count = 0 # Show popup action self._showPopupAction = QAction(QIcon(':enkiicons/filtered.png'), "File browser history", self) self._showPopupAction.setShortcut('Ctrl+H') core.actionManager().addAction("mNavigation/mFileBrowser/aMenuShow", self._showPopupAction) self._showPopupAction.triggered.connect(self._onShowPopup) # reconnected in self.updateComboItems() self.currentIndexChanged[int].connect(self._onItemSelected) def terminate(self): """Explicitly called destructor """ core.actionManager().removeAction(self._showPopupAction) def _onShowPopup(self, triggered): """Handler for self._showPopupAction """ self._fileBrowser.show() self.showPopup() def _onItemSelected(self, index): """Handler of item selection in the combo box """ if self.count() > self._count: # It is user input path = self.itemText(index) if os.path.isdir(path): self._fileBrowser.setCurrentPath(path) else: path = self.itemData(index) self._fileBrowser.setCurrentPath(path) def updateItems(self, items): """Update items in the combo box according to current history """ self.currentIndexChanged[int].disconnect() self.clear() # Current text self.addItem(os.path.normpath(self._fileBrowser.currentPath())) self.setItemData(0, self._fileBrowser.currentPath()) self.insertSeparator(self.count()) for index, path in enumerate(items): self.addItem(os.path.normpath(path)) self.setItemData(index + 2, path) self._count = self.count() self.currentIndexChanged[int].connect(self._onItemSelected)
class MyMainWindow(QWidget, Ui_Form): def __init__(self, proPath, parent=None): super(MyMainWindow, self).__init__(parent) # self.setCentralWidget(self.widget) self.proPath = proPath #************** 初始化窗口 self.setupUi(self) # 设置窗口的标题 self.setWindowTitle('ftpFilesys') # 设置窗口的图标,引用当前目录下的web.png图片 self.setWindowIcon(QIcon(self.proPath + '/Icon/LOGO.jpg')) self.setSignal() self.downing = True #************** 初始化按键 # self.Bt_down.setEnabled(False) self.Bt_up.setEnabled(False) # 创建SSH对象 self.ssh = paramiko.SSHClient() # 允许连接不在know_hosts文件中的主机,否则可能报错:paramiko.ssh_exception.SSHException: Server '192.168.43.140' not found in known_hosts self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) self.ftp_root = "/home/sd/ftp/biaodingCloud" #************** 初始化变量 """FTP窗口""" self.ftp = myFTP() # 实例化FTP self.ftp.encoding = "utf-8" self.select_file = "" # listView中选择的文件名 self.file_list = [] # 存放查询FTP返回的当前目录所有文件列表 self.ftp_tip = [] #存储当前登陆信息 """本地窗口""" # self.sysfile = QFileSystemModel() # 获取本地文件系统 # self.sysfile.setRootPath('') # self.treeView_local.setModel(self.sysfile) self.model = QDirModel() self.model.setFilter(QtCore.QDir.Dirs | QtCore.QDir.NoDotAndDotDot) self.treeView_local.header().hide() # 隐藏表头 self.treeView_local.setModel(self.model) for col in range(1, 4): self.treeView_local.setColumnHidden(col, True) self.save_path = "" # self.treeView_local.setRootIndex(self.model.index(self.save_path)) self.save_name = "" self.lineEdit_ip.setText("192.168.200.11") self.lineEdit_port.setText("21") self.lineEdit_user.setText("admin") self.lineEdit_pwd.setText("123") # 信号绑定设置 def setSignal(self): self.Bt_link.clicked.connect(self.ftp_connect) self.Bt_down.clicked.connect(self.Bt_down_run) # self.Bt_up.clicked.connect(self.ftp_connect) # 任意输入框改变就可以重新使能连接按键 self.lineEdit_ip.editingFinished.connect(self.BtEnabled) self.lineEdit_port.editingFinished.connect(self.BtEnabled) self.lineEdit_user.editingFinished.connect(self.BtEnabled) self.lineEdit_pwd.editingFinished.connect(self.BtEnabled) self.treeWidget_master.itemClicked.connect(self.select_item_ftp) # self.treeView_local.doubleClicked.connect(self.select_item_local) self.treeView_local.expanded.connect(self.select_item_local) self.treeView_local.clicked.connect(self.select_item_local) # self.treeWidget_master.doubleClicked.connect(self.cd_button) # 按键使能 def BtEnabled(self): self.Bt_link.setEnabled(True) self.Bt_down.setEnabled(False) self.Bt_up.setEnabled(False) # ftp 连接登录 def ftp_connect(self): self.Text_log.append("linking...") self.treeWidget_master.clear() host = self.lineEdit_ip.text() # 获取IP地址框内容 port = int(self.lineEdit_port.text()) # 获取端口号,注意要转换为int usr = self.lineEdit_user.text() # 获取用户名 pwd = self.lineEdit_pwd.text() # 获取密码 self.ftp_tip.append(host) self.ftp_tip.append(port) self.ftp_tip.append(usr) self.ftp_tip.append(pwd) try: self.ftp.connect(host, port, timeout=10) # 连接FTP except: logging.warning('network connect time out') # 打印日志信息 self.Text_log.append("network connect time out") try: self.ftp.login(usr, pwd) # 登录FTP except: logging.warning("username or password error") # 打印日志信息 self.Text_log.append("username or password error") self.file_list = self.ftp.nlst() # 查询当前目录的所有文件列表 self.Bt_link.setEnabled(False) self.root = QTreeWidgetItem(self.treeWidget_master) self.root.setText(0, '/') # print(self.setItemIcon('/')) self.root.setIcon(0, QIcon(self.setItem_Icon('/'))) self.root.setToolTip(0, '/') # self.dirItem(self.file_list,self.root) self.dirItem(self.root) self.cursor = self.Text_log.textCursor() self.Text_log.moveCursor(self.cursor.End) self.Text_log.append("link success!") # 连接服务器 result = self.ssh.connect(hostname=b'192.168.200.11', port=22, username=b'fs_001', password=b'123') self.Text_log.append(result) print("link success!") self.treeWidget_master.itemExpanded.connect(self.dirItem_new) # for col in range(len(self.file_list)): # print(col) # self.treeWidget_master.setColumnHidden(col, True) # print(self.ftp.dir('/DetectMask.zip')) # 判断数据类型,设置图标 def setItem_Icon(self, obj): if "." in obj: # 是文件则不能进入 icon = self.proPath + "/Icon/file.png" else: # 是文件夹则可以进入 icon = self.proPath + "/Icon/folder.png" if ".jpg" in obj or ".jpeg" in obj or ".png" in obj: # 是文件则不能进入 icon = self.proPath + "/Icon/file_img.png" elif ".zip" in obj or ".rar" in obj or ".7z" in obj: icon = self.proPath + "/Icon/file_zip.png" elif ".xls" in obj or ".xlsx" in obj: icon = self.proPath + "/Icon/file_excel.png" elif ".ppt" in obj or "pptx" in obj: icon = self.proPath + "/Icon/file_ppt.png" elif ".doc" in obj or ".docx" in obj or ".7z" in obj: icon = self.proPath + "/Icon/file_word.png" elif ".pdf" in obj: icon = self.proPath + "/Icon/file_pdf.png" elif ".py" in obj: icon = self.proPath + "/Icon/file_python.png" return icon # 递归操作,遍历ftp服务器所有文件 def dirItem(self, item): # print(item.toolTip((0))) list = self.ftp.nlst(str(item.toolTip(0))) # for name1 in list: # print(" ",name1) #self.showMessage("加载... ", QtCore.Qt.AlignHCenter | QtCore.Qt.AlignBottom, QtCore.Qt.black) for i, path in enumerate(list): name = path.split('/')[-1] child = QTreeWidgetItem(item) item.addChild(child) child.setIcon(0, QIcon(self.setItem_Icon(name))) child.setText(0, name) # print(self.ftp.pwd()) child.setToolTip(0, path) # print(child.toolTip(0)) # print(this,name) # 动态加载dirItem def dirItem_new(self, item): # print(item.toolTip((0))) # list=self.ftp.nlst(str(item.toolTip(0))) child_num = item.childCount() for i in range(child_num): cc_num = item.child(i).childCount() item.child(i).setExpanded(False) if cc_num >= 0: for j in range(cc_num): item.child(i).removeChild(item.child(i).child(j)) if self.checkFileDir(self.ftp, item.child(i).toolTip(0)) == "dir": self.dirItem(item.child(i)) # time.sleep(10) """递归操作,耗费资源+加载时间久,放弃""" # 递归操作,遍历ftp服务器所有文件 # def dirItem(self,list,item): # # list=self.ftp.nlst(str(item.toolTip(0))) # # self.showMessage("加载... ", QtCore.Qt.AlignHCenter | QtCore.Qt.AlignBottom, QtCore.Qt.black) # for i,name in enumerate(list): # child = QTreeWidgetItem(item) # item.addChild(child) # child.setIcon(0,QIcon(self.setItem_Icon(name))) # child.setText(0,name) # child.setToolTip(0,self.ftp.pwd()) # # print(child.toolTip(0)) # # print(this,name) # this = self.checkFileDir(self.ftp, name) # if this == "dir": # # print(self.ftp.pwd(), name) # self.ftp.cwd(name) # filelist=self.ftp.nlst() # # for name1 in filelist: # # print(" ",name1) # self.dirItem(filelist,child) # self.ftp.cwd("..") # 判断是否为文件 def checkFileDir(self, ftp, file_name): """ 判断当前目录下的文件与文件夹 :param ftp: 实例化的FTP对象 :param file_name: 文件名/文件夹名 :return:返回字符串“File”为文件,“Dir”问文件夹,“Unknow”为无法识别 """ rec = "" try: rec = ftp.cwd(file_name) # 需要判断的元素 ftp.cwd("..") # 如果能通过路径打开必为文件夹,在此返回上一级 except ftplib.error_perm as fe: rec = fe # 不能通过路劲打开必为文件,抓取其错误信息 finally: # print(file_name,rec) if "550" in str(rec)[:3]: return "file" elif "250" in str(rec)[:3]: return "dir" else: return "unknow" # 单击选中,使能下载按键 def select_item_ftp(self, item): # print(item.text(0),item.columnCount()) self.select_item = item self.select_file = item.toolTip(0) # print(self.select_file) if '.' in self.select_file: # 如果是文件,则可下载 self.Bt_down.setEnabled(True) else: # 否则是文件夹,不能下载 self.Bt_down.setEnabled(False) # 选择文件保存目录 def select_item_local(self, obj): self.save_path = self.model.filePath(obj) # 更新主窗口显示 def handleDisplay(self, data): if "ERROR" in data: self.Text_log.append(data) self.Bt_link.setEnabled(True) else: self.cursor = self.Text_log.textCursor() self.cursor.select(QTextCursor.LineUnderCursor) self.cursor.removeSelectedText() # self.Text_log.moveCursor(QtGui.QTextCursor.StartOfLine, QtGui.QTextCursor.MoveAnchor) self.Text_log.insertPlainText(data) if data == "100%": self.downing = True self.Text_log.append("Download Success!") self.thread.quit() # 执行命令 # stdin:标准输入(就是你输入的命令);stdout:标准输出(就是命令执行结果);stderr:标准错误(命令执行过程中如果出错了就把错误打到这里),stdout和stderr仅会输出一个 self.mv_file() self.Bt_down.setEnabled(False) # 移动文件 def mv_file(self): old_file = False path, file = os.path.split(self.select_file) print(self.ftp_root + path + "/old_file/") # s = self.ftp.mkd(path + "/old_file/") stdin, stdout, stderr = self.ssh.exec_command( "mkdir %s" % (self.ftp_root + path + "/old_file/")) stdin, stdout, stderr = self.ssh.exec_command( "mv %s %s" % (self.ftp_root + self.select_file, self.ftp_root + path + "/old_file/" + file)) # # self.Text_log.append(str(stdout)) parent = self.select_item.parent() print(parent.toolTip(0)) parent.setExpanded(False) parent.removeChild(self.select_item) child_num = parent.childCount() for i in range(child_num): if self.checkFileDir(self.ftp, parent.child(i).toolTip(0)) == "dir": if parent.child(i).text(0) == "old_file": old_file = True if old_file == False: item_name = "old_file" child = QTreeWidgetItem(parent) parent.addChild(child) child.setIcon(0, QIcon(self.setItem_Icon(item_name))) child.setText(0, item_name) # print(self.ftp.pwd()) child.setToolTip(0, path + "/old_file") # else: # child_num = parent.childCount() # for i in range(child_num): # parent.removeChild(parent.child(i)) # self.dirItem(parent) parent.setExpanded(True) self.treeWidget_master.update() # self.select_item.setExpanded(True) # 下载晚间操作 def Bt_down_run(self): if self.save_path == "": self.Text_log.append("未选择保存路径") self.downing = True reply = QMessageBox.warning(self, "警告", "未选择保存路径", QMessageBox.Ok) return if self.select_file == "": self.Text_log.append("未选择下载文件") self.downing = True reply = QMessageBox.warning(self, "警告", "未选择下载文件", QMessageBox.Ok) return if self.downing == False: reply = QMessageBox.warning(self, "警告", "正在下载,请等待。。。", QMessageBox.Ok) return self.downing = False self.save_name = self.save_path + "/%s" % self.select_file.split( '/')[-1] # print(self.select_file, self.save_name) self.Text_log.append("开始下载,\"%s\"将文件下载到\"%s\"" % (self.select_file, self.save_name)) if self.checkFileDir(self.ftp, self.select_file) == "dir": self.Text_log.append("这是文件夹,不能下载") return print(self.save_name) if os.path.exists(self.save_name): reply = QMessageBox.warning(self, "文件已存在", "是否覆盖?", QMessageBox.Yes | QMessageBox.No) if reply == QMessageBox.Yes: os.remove(self.save_name) else: self.downing = True return # 创建线程 self.down = down_Thread(self.ftp_tip, self.select_file, self.save_name) # self.down = down_Thread(self) # thread = MyThread(target=self.tcp_run, args=(self.ftp_tip,self.select_file,self.save_name)) # target = self.ftp.retrbinary, args = ("RETR %s" % self.select_file, open(self.save_name, 'wb').write) # 连接信号 self.down.update_date.connect(self.handleDisplay) self.thread = QThread() self.Text_log.append("0%") self.down.moveToThread(self.thread) # 开始线程 self.thread.started.connect(self.down.run) self.thread.start()
class SearchWidget(QFrame): """Widget, appeared, when Ctrl+F pressed. Has different forms for different search modes """ Normal = 'normal' Good = 'good' Bad = 'bad' Incorrect = 'incorrect' visibilityChanged = pyqtSignal(bool) """ visibilityChanged(visible) **Signal** emitted, when widget has been shown or hidden """ # pylint: disable=W0105 searchInDirectoryStartPressed = pyqtSignal(type(re.compile('')), list, str) """ searchInDirectoryStartPressed(regEx, mask, path) **Signal** emitted, when 'search in directory' button had been pressed """ # pylint: disable=W0105 searchInDirectoryStopPressed = pyqtSignal() """ searchInDirectoryStopPressed() **Signal** emitted, when 'stop search in directory' button had been pressed """ # pylint: disable=W0105 replaceCheckedStartPressed = pyqtSignal(str) """ replaceCheckedStartPressed(replText) **Signal** emitted, when 'replace checked' button had been pressed """ # pylint: disable=W0105 replaceCheckedStopPressed = pyqtSignal() """ replaceCheckedStartPressed() **Signal** emitted, when 'stop replacing checked' button had been pressed """ # pylint: disable=W0105 searchRegExpChanged = pyqtSignal(type(re.compile(''))) """ searchRegExpValidStateChanged(regEx) **Signal** emitted, when search regexp has been changed. If reg exp is invalid - regEx object contains empty pattern """ # pylint: disable=W0105 searchNext = pyqtSignal() """ searchNext() **Signal** emitted, when 'Search Next' had been pressed """ # pylint: disable=W0105 searchPrevious = pyqtSignal() """ searchPrevious() **Signal** emitted, when 'Search Previous' had been pressed """ # pylint: disable=W0105 replaceFileOne = pyqtSignal(str) """ replaceFileOne(replText) **Signal** emitted, when 'Replace' had been pressed """ # pylint: disable=W0105 replaceFileAll = pyqtSignal(str) """ replaceFileAll(replText) **Signal** emitted, when 'Replace All' had been pressed """ # pylint: disable=W0105 def __init__(self, plugin): QFrame.__init__(self, core.workspace()) self._mode = None self.plugin = plugin uic.loadUi(os.path.join(os.path.dirname(__file__), 'SearchWidget.ui'), self) self.cbSearch.setCompleter(None) self.cbReplace.setCompleter(None) self.cbMask.setCompleter(None) self.fsModel = QDirModel(self.cbPath.lineEdit()) self.fsModel.setFilter(QDir.AllDirs | QDir.NoDotAndDotDot) self.cbPath.lineEdit().setCompleter( QCompleter(self.fsModel, self.cbPath.lineEdit())) self._pathBackspaceShortcut = QShortcut(QKeySequence("Ctrl+Backspace"), self.cbPath, self._onPathBackspace) self._pathBackspaceShortcut.setContext(Qt.WidgetWithChildrenShortcut) # TODO QDirModel is deprecated but QCompleter does not yet handle # QFileSystemodel - please update when possible.""" self.cbSearch.setCompleter(None) self.pbSearchStop.setVisible(False) self.pbReplaceCheckedStop.setVisible(False) self._progress = QProgressBar(self) self._progress.setAlignment(Qt.AlignCenter) self._progress.setToolTip(self.tr("Search in progress...")) self._progress.setMaximumSize(QSize(80, 16)) core.mainWindow().statusBar().insertPermanentWidget(1, self._progress) self._progress.setVisible(False) # cd up action self.tbCdUp = QToolButton(self.cbPath.lineEdit()) self.tbCdUp.setIcon(QIcon(":/enkiicons/go-up.png")) self.tbCdUp.setCursor(Qt.ArrowCursor) self.tbCdUp.installEventFilter(self) # for drawing button self.cbSearch.installEventFilter( self) # for catching Tab and Shift+Tab self.cbReplace.installEventFilter( self) # for catching Tab and Shift+Tab self.cbPath.installEventFilter(self) # for catching Tab and Shift+Tab self.cbMask.installEventFilter(self) # for catching Tab and Shift+Tab self._closeShortcut = QShortcut(QKeySequence("Esc"), self) self._closeShortcut.setContext(Qt.WidgetWithChildrenShortcut) self._closeShortcut.activated.connect(self.hide) # connections self.cbSearch.lineEdit().textChanged.connect( self._onSearchRegExpChanged) self.cbSearch.lineEdit().returnPressed.connect(self._onReturnPressed) self.cbReplace.lineEdit().returnPressed.connect(self._onReturnPressed) self.cbPath.lineEdit().returnPressed.connect(self._onReturnPressed) self.cbMask.lineEdit().returnPressed.connect(self._onReturnPressed) self.cbRegularExpression.stateChanged.connect( self._onSearchRegExpChanged) self.cbCaseSensitive.stateChanged.connect(self._onSearchRegExpChanged) self.cbWholeWord.stateChanged.connect(self._onSearchRegExpChanged) self.tbCdUp.clicked.connect(self._onCdUpPressed) self.pbNext.pressed.connect(self.searchNext) self.pbPrevious.pressed.connect(self.searchPrevious) self.pbSearchStop.pressed.connect(self.searchInDirectoryStopPressed) self.pbReplaceCheckedStop.pressed.connect( self.replaceCheckedStopPressed) core.mainWindow().hideAllWindows.connect(self.hide) core.workspace().escPressed.connect(self.hide) core.workspace().currentDocumentChanged.connect( lambda old, new: self.setVisible(self.isVisible() and new is not None)) def show(self): """Reimplemented function. Sends signal """ super(SearchWidget, self).show() self.visibilityChanged.emit(self.isVisible()) def hide(self): """Reimplemented function. Sends signal, returns focus to workspace """ super(SearchWidget, self).hide() core.workspace().focusCurrentDocument() self.visibilityChanged.emit(self.isVisible()) def setVisible(self, visible): """Reimplemented function. Sends signal """ super(SearchWidget, self).setVisible(visible) self.visibilityChanged.emit(self.isVisible()) def _regExEscape(self, text): """Improved version of re.escape() Doesn't escape space, comma, underscore. Escapes \n and \t """ text = re.escape(text) # re.escape escapes space, comma, underscore, but, it is not necessary and makes text not readable for symbol in (' ,_=\'"/:@#%&'): text = text.replace('\\' + symbol, symbol) text = text.replace('\\\n', '\\n') text = text.replace('\\\t', '\\t') return text def _makeEscapeSeqsVisible(self, text): """Replace invisible \n and \t with escape sequences """ text = text.replace('\\', '\\\\') text = text.replace('\t', '\\t') text = text.replace('\n', '\\n') return text def setMode(self, mode): """Change search mode. i.e. from "Search file" to "Replace directory" """ if self._mode == mode and self.isVisible(): if core.workspace().currentDocument() is not None and \ not core.workspace().currentDocument().hasFocus(): self.cbSearch.lineEdit().selectAll() self.cbSearch.setFocus() self._mode = mode # Set Search and Replace text document = core.workspace().currentDocument() if document is not None and \ document.hasFocus() and \ document.qutepart.selectedText: searchText = document.qutepart.selectedText self.cbReplace.setEditText(self._makeEscapeSeqsVisible(searchText)) if self.cbRegularExpression.checkState() == Qt.Checked: searchText = self._regExEscape(searchText) self.cbSearch.setEditText(searchText) if not self.cbReplace.lineEdit().text() and \ self.cbSearch.lineEdit().text() and \ not self.cbRegularExpression.checkState() == Qt.Checked: replaceText = self.cbSearch.lineEdit().text().replace('\\', '\\\\') self.cbReplace.setEditText(replaceText) # Move focus to Search edit self.cbSearch.setFocus() self.cbSearch.lineEdit().selectAll() # Set search path if mode & MODE_FLAG_DIRECTORY and \ not (self.isVisible() and self.cbPath.isVisible()): try: searchPath = os.path.abspath(str(os.path.curdir)) except OSError: # current directory might have been deleted pass else: self.cbPath.setEditText(searchPath) # Set widgets visibility flag according to state widgets = (self.wSearch, self.pbPrevious, self.pbNext, self.pbSearch, self.wReplace, self.wPath, self.pbReplace, self.pbReplaceAll, self.pbReplaceChecked, self.wOptions, self.wMask) # wSear pbPrev pbNext pbSear wRepl wPath pbRep pbRAll pbRCHK wOpti wMask visible = \ {MODE_SEARCH: (1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0,), MODE_REPLACE: (1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0,), MODE_SEARCH_DIRECTORY: (1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1,), MODE_REPLACE_DIRECTORY: (1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1,), MODE_SEARCH_OPENED_FILES: (1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1,), MODE_REPLACE_OPENED_FILES: (1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1,)} for i, widget in enumerate(widgets): widget.setVisible(visible[mode][i]) # Search next button text if mode == MODE_REPLACE: self.pbNext.setText('Next') else: self.pbNext.setText('Next↵') # Finaly show all with valid size self.show() # show before updating widgets and labels self._updateLabels() self._updateWidgets() def eventFilter(self, object_, event): """ Event filter for mode switch tool button Draws icons in the search and path lineEdits """ if event.type( ) == QEvent.Paint and object_ is self.tbCdUp: # draw CdUp button in search path QLineEdit toolButton = object_ lineEdit = self.cbPath.lineEdit() lineEdit.setContentsMargins(lineEdit.height(), 0, 0, 0) height = lineEdit.height() availableRect = QRect(0, 0, height, height) if toolButton.rect() != availableRect: toolButton.setGeometry(availableRect) painter = QPainter(toolButton) toolButton.icon().paint(painter, availableRect) return True elif event.type( ) == QEvent.KeyPress: # Tab and Shift+Tab in QLineEdits if event.key() == Qt.Key_Tab: self._moveFocus(1) return True elif event.key() == Qt.Key_Backtab: self._moveFocus(-1) return True return QFrame.eventFilter(self, object_, event) def _onReturnPressed(self): """Return or Enter pressed on widget. Search next or Replace next """ if self.pbReplace.isVisible(): self.pbReplace.click() elif self.pbNext.isVisible(): self.pbNext.click() elif self.pbSearch.isVisible(): self.pbSearch.click() elif self.pbSearchStop.isVisible(): self.pbSearchStop.click() def _onPathBackspace(self): """Ctrl+Backspace pressed on path. Remove 1 path level. Default behavior would be to remove one word on Linux or all on Windows """ path = self.cbPath.currentText() if path.endswith('/') or \ path.endswith('\\'): path = path[:-1] head, tail = os.path.split(path) if head and \ head != path: if not head.endswith(os.sep): head += os.sep self.cbPath.lineEdit().setText(head) def _moveFocus(self, step): """Move focus forward or backward according to step. Standard Qt Keyboard focus algorithm doesn't allow circular navigation """ allFocusableWidgets = (self.cbSearch, self.cbReplace, self.cbPath, self.cbMask) visibleWidgets = [ widget for widget in allFocusableWidgets if widget.isVisible() ] try: focusedIndex = visibleWidgets.index(QApplication.focusWidget()) except ValueError: print('Invalid focused widget in Search Widget', file=sys.stderr) return nextFocusedIndex = (focusedIndex + step) % len(visibleWidgets) visibleWidgets[nextFocusedIndex].setFocus() visibleWidgets[nextFocusedIndex].lineEdit().selectAll() def _updateLabels(self): """Update 'Search' 'Replace' 'Path' labels geometry """ width = 0 if self.lSearch.isVisible(): width = max(width, self.lSearch.minimumSizeHint().width()) if self.lReplace.isVisible(): width = max(width, self.lReplace.minimumSizeHint().width()) if self.lPath.isVisible(): width = max(width, self.lPath.minimumSizeHint().width()) self.lSearch.setMinimumWidth(width) self.lReplace.setMinimumWidth(width) self.lPath.setMinimumWidth(width) def _updateWidgets(self): """Update geometry of widgets with buttons """ width = 0 if self.wSearchRight.isVisible(): width = max(width, self.wSearchRight.minimumSizeHint().width()) if self.wReplaceRight.isVisible(): width = max(width, self.wReplaceRight.minimumSizeHint().width()) if self.wPathRight.isVisible(): width = max(width, self.wPathRight.minimumSizeHint().width()) self.wSearchRight.setMinimumWidth(width) self.wReplaceRight.setMinimumWidth(width) self.wPathRight.setMinimumWidth(width) def updateComboBoxes(self): """Update comboboxes with last used texts """ searchText = self.cbSearch.currentText() replaceText = self.cbReplace.currentText() maskText = self.cbMask.currentText() # search if searchText: index = self.cbSearch.findText(searchText) if index == -1: self.cbSearch.addItem(searchText) # replace if replaceText: index = self.cbReplace.findText(replaceText) if index == -1: self.cbReplace.addItem(replaceText) # mask if maskText: index = self.cbMask.findText(maskText) if index == -1: self.cbMask.addItem(maskText) def _searchPatternTextAndFlags(self): """Get search pattern and flags """ pattern = self.cbSearch.currentText() pattern = pattern.replace( '\u2029', '\n') # replace unicode paragraph separator with habitual \n if not self.cbRegularExpression.checkState() == Qt.Checked: pattern = re.escape(pattern) if self.cbWholeWord.checkState() == Qt.Checked: pattern = r'\b' + pattern + r'\b' flags = 0 if not self.cbCaseSensitive.checkState() == Qt.Checked: flags = re.IGNORECASE return pattern, flags def getRegExp(self): """Read search parameters from controls and present it as a regular expression """ pattern, flags = self._searchPatternTextAndFlags() return re.compile(pattern, flags) def isSearchRegExpValid(self): """Try to compile search pattern to check if it is valid Returns bool result and text error """ pattern, flags = self._searchPatternTextAndFlags() try: re.compile(pattern, flags) except re.error as ex: return False, str(ex) return True, None def _getSearchMask(self): """Get search mask as list of patterns """ mask = [s.strip() for s in self.cbMask.currentText().split(' ')] # remove empty mask = [_f for _f in mask if _f] return mask def setState(self, state): """Change line edit color according to search result """ widget = self.cbSearch.lineEdit() color = { SearchWidget.Normal: QApplication.instance().palette().color(QPalette.Base), SearchWidget.Good: QColor(Qt.green), SearchWidget.Bad: QColor(Qt.red), SearchWidget.Incorrect: QColor(Qt.darkYellow) } stateColor = color[state] if state != SearchWidget.Normal: stateColor.setAlpha(100) pal = widget.palette() pal.setColor(widget.backgroundRole(), stateColor) widget.setPalette(pal) def setSearchInProgress(self, inProgress): """Search thread started or stopped """ self.pbSearchStop.setVisible(inProgress) self.pbSearch.setVisible(not inProgress) self._updateWidgets() self._progress.setVisible(inProgress) def onSearchProgressChanged(self, value, total): """Signal from the thread, progress changed """ self._progress.setValue(value) self._progress.setMaximum(total) def setReplaceInProgress(self, inProgress): """Replace thread started or stopped """ self.pbReplaceCheckedStop.setVisible(inProgress) self.pbReplaceChecked.setVisible(not inProgress) self._updateWidgets() def setSearchInFileActionsEnabled(self, enabled): """Set enabled state for Next, Prev, Replace, ReplaceAll """ for button in (self.pbNext, self.pbPrevious, self.pbReplace, self.pbReplaceAll): button.setEnabled(enabled) def _onSearchRegExpChanged(self): """User edited search text or checked/unchecked checkboxes """ valid, error = self.isSearchRegExpValid() if valid: self.setState(self.Normal) core.mainWindow().statusBar().clearMessage() self.pbSearch.setEnabled(len(self.getRegExp().pattern) > 0) else: core.mainWindow().statusBar().showMessage(error, 3000) self.setState(self.Incorrect) self.pbSearch.setEnabled(False) self.searchRegExpChanged.emit(re.compile('')) return self.searchRegExpChanged.emit(self.getRegExp()) def _onCdUpPressed(self): """User pressed "Up" button, need to remove one level from search path """ text = self.cbPath.currentText() if not os.path.exists(text): return editText = os.path.normpath(os.path.join(text, os.path.pardir)) self.cbPath.setEditText(editText) def on_pbSearch_pressed(self): """Handler of click on "Search" button (for search in directory) """ self.setState(SearchWidget.Normal) self.searchInDirectoryStartPressed.emit(self.getRegExp(), self._getSearchMask(), self.cbPath.currentText()) def on_pbReplace_pressed(self): """Handler of click on "Replace" (in file) button """ self.replaceFileOne.emit(self.cbReplace.currentText()) def on_pbReplaceAll_pressed(self): """Handler of click on "Replace all" (in file) button """ self.replaceFileAll.emit(self.cbReplace.currentText()) def on_pbReplaceChecked_pressed(self): """Handler of click on "Replace checked" (in directory) button """ self.replaceCheckedStartPressed.emit(self.cbReplace.currentText()) def on_pbBrowse_pressed(self): """Handler of click on "Browse" button. Explores FS for search directory path """ path = QFileDialog.getExistingDirectory(self, self.tr("Search path"), self.cbPath.currentText()) if path: self.cbPath.setEditText(path)
def __init__(self): super(GeneralPreferences, self).__init__() grid = QGridLayout(self) grid.setSpacing(20) grid.setColumnStretch(1, 10) # directory auto completer completer = QCompleter(self) dirs = QDirModel(self) dirs.setFilter(QDir.AllDirs | QDir.NoDotAndDotDot) completer.setModel(dirs) completer.setCaseSensitivity(Qt.CaseInsensitive) completer.setCompletionMode(QCompleter.PopupCompletion) label = QLabel( "<b>Ingresá el directorio donde descargar los videos...</b>") label.setTextFormat(Qt.RichText) grid.addWidget(label, 0, 0, 1, 2) grid.addWidget(QLabel("Descargar en:"), 1, 0, 2, 1) prv = config.get('downloaddir', '') self.downloaddir_entry = QLineEdit(prv) self.downloaddir_entry.setCompleter(completer) self.downloaddir_entry.setPlaceholderText('Ingresá un directorio') grid.addWidget(self.downloaddir_entry, 1, 1, 2, 2) self.downloaddir_buttn = QPushButton("Elegir un directorio") self.downloaddir_buttn.clicked.connect(self._choose_dir) grid.addWidget(self.downloaddir_buttn, 2, 1, 3, 2) self.autoreload_checkbox = QCheckBox( "Recargar automáticamente la lista de episodios al iniciar") self.autoreload_checkbox.setToolTip( "Cada vez que arranca el programa refrescar la lista de episodios." ) prv = config.get('autorefresh', False) self.autoreload_checkbox.setChecked(prv) grid.addWidget(self.autoreload_checkbox, 3, 0, 4, 2) self.shownotifs_checkbox = QCheckBox( "Mostrar una notificación cuando termina cada descarga") self.shownotifs_checkbox.setToolTip( "Hacer que el escritorio muestre una notificación cada vez que una descarga " "se complete.") prv = config.get('notification', True) self.shownotifs_checkbox.setChecked(prv) grid.addWidget(self.shownotifs_checkbox, 4, 0, 5, 2) self.cleanfnames_checkbox = QCheckBox( "Limpiar nombres para que se pueda guardar en cualquier lado") self.cleanfnames_checkbox.setToolTip( "Convertir caracteres extraños en títulos para que el archivo se pueda grabar en " "cualquier disco o pendrive.") prv = config.get('clean-filenames', False) self.cleanfnames_checkbox.setChecked(prv) grid.addWidget(self.cleanfnames_checkbox, 5, 0, 6, 2) lq = QLabel( "<b>Ingrese la Calidad de Video Preferida para las Descargas:</b>") lq.setTextFormat(Qt.RichText) grid.addWidget(lq, 8, 0, 7, 2) lqd = QLabel("* En caso de no existir se eligirá la más conveniente.") lqd.setTextFormat(Qt.RichText) grid.addWidget(lqd, 9, 0, 8, 2) self.select_quality = QComboBox() self.select_quality.setGeometry(QRect()) self.select_quality.setObjectName("Calidad de Video Preferida") self.select_quality.addItem("1080p") # HD self.select_quality.addItem("720p") # ALTA self.select_quality.addItem("480p") # MEDIA self.select_quality.addItem("360p") # BAJA self.select_quality.addItem("240p") # MALA self.select_quality.activated[str].connect(self.selected) prv = config.get('quality', '720p') self.select_quality.setCurrentText(prv) grid.addWidget(self.select_quality, 10, 0, 9, 2)