def silent_exec_method(self, code): """Silently execute a kernel method and save its reply The methods passed here **don't** involve getting the value of a variable but instead replies that can be handled by ast.literal_eval. To get a value see `get_value` Parameters ---------- code : string Code that contains the kernel method as part of its string See Also -------- handle_exec_method : Method that deals with the reply Note ---- This is based on the _silent_exec_callback method of RichJupyterWidget. Therefore this is licensed BSD """ # Generate uuid, which would be used as an indication of whether or # not the unique request originated from here local_uuid = to_text_string(uuid.uuid1()) code = to_text_string(code) msg_id = self.kernel_client.execute("", silent=True, user_expressions={local_uuid: code}) self._kernel_methods[local_uuid] = code self._request_info["execute"][msg_id] = self._ExecutionRequest(msg_id, "silent_exec_method")
def setup(self): iofuncs = self.get_internal_funcs()+self.get_3rd_party_funcs() load_extensions = {} save_extensions = {} load_funcs = {} save_funcs = {} load_filters = [] save_filters = [] load_ext = [] for ext, name, loadfunc, savefunc in iofuncs: filter_str = to_text_string(name + " (*%s)" % ext) if loadfunc is not None: load_filters.append(filter_str) load_extensions[filter_str] = ext load_funcs[ext] = loadfunc load_ext.append(ext) if savefunc is not None: save_extensions[filter_str] = ext save_filters.append(filter_str) save_funcs[ext] = savefunc load_filters.insert(0, to_text_string(_("Supported files")+" (*"+\ " *".join(load_ext)+")")) load_filters.append(to_text_string(_("All files (*.*)"))) self.load_filters = "\n".join(load_filters) self.save_filters = "\n".join(save_filters) self.load_funcs = load_funcs self.save_funcs = save_funcs self.load_extensions = load_extensions self.save_extensions = save_extensions
def test_recent_projects_menu_action(projects, tmpdir): """ Test that the actions of the submenu 'Recent Projects' in the 'Projects' main menu are working as expected. Regression test for Issue #8450. """ recent_projects_len = len(projects.recent_projects) # Create the directories. path0 = to_text_string(tmpdir.mkdir('project0')) path1 = to_text_string(tmpdir.mkdir('project1')) path2 = to_text_string(tmpdir.mkdir('project2')) # Open projects in path0, path1, and path2. projects.open_project(path=path0) projects.open_project(path=path1) projects.open_project(path=path2) assert (len(projects.recent_projects_actions) == recent_projects_len + 3 + 2) assert projects.get_active_project().root_path == path2 # Trigger project1 in the list of Recent Projects actions. projects.recent_projects_actions[1].trigger() assert projects.get_active_project().root_path == path1 # Trigger project0 in the list of Recent Projects actions. projects.recent_projects_actions[2].trigger() assert projects.get_active_project().root_path == path0
def goto_line(self, line, column=0, end_column=0, move=True, word=''): """ Moves the text cursor to the specified position. :param line: Number of the line to go to (0 based) :param column: Optional column number. Default is 0 (start of line). :param move: True to move the cursor. False will return the cursor without setting it on the editor. :param word: Highlight the word, when moving to the line. :return: The new text cursor :rtype: QtGui.QTextCursor """ line = min(line, self.line_count()) text_cursor = self._move_cursor_to(line) if column: text_cursor.movePosition(text_cursor.Right, text_cursor.MoveAnchor, column) if end_column: text_cursor.movePosition(text_cursor.Right, text_cursor.KeepAnchor, end_column) if move: block = text_cursor.block() self.unfold_if_colapsed(block) self._editor.setTextCursor(text_cursor) if self._editor.isVisible(): self._editor.centerCursor() else: self._editor.focus_in.connect( self._editor.center_cursor_on_next_focus) if word and to_text_string(word) in to_text_string(block.text()): self._editor.find(word, QTextDocument.FindCaseSensitively) return text_cursor
def run(self): html_text = self.html_text_no_doc doc = self.doc if doc is not None: if type(doc) is dict and 'docstring' in doc.keys(): try: context = generate_context(name=doc['name'], argspec=doc['argspec'], note=doc['note'], math=self.math_option, img_path=self.img_path) html_text = sphinxify(doc['docstring'], context) if doc['docstring'] == '': html_text += '<div class="hr"></div>' html_text += self.html_text_no_doc except Exception as error: self.error_msg.emit(to_text_string(error)) return elif self.context is not None: try: html_text = sphinxify(doc, self.context) except Exception as error: self.error_msg.emit(to_text_string(error)) return self.html_ready.emit(html_text)
def format_measure(measure): """Get format and units for data coming from profiler task.""" # Convert to a positive value. measure = abs(measure) # For number of calls if isinstance(measure, int): return to_text_string(measure) # For time measurements if 1.e-9 < measure <= 1.e-6: measure = u"{0:.2f} ns".format(measure / 1.e-9) elif 1.e-6 < measure <= 1.e-3: measure = u"{0:.2f} us".format(measure / 1.e-6) elif 1.e-3 < measure <= 1: measure = u"{0:.2f} ms".format(measure / 1.e-3) elif 1 < measure <= 60: measure = u"{0:.2f} sec".format(measure) elif 60 < measure <= 3600: m, s = divmod(measure, 3600) if s > 60: m, s = divmod(measure, 60) s = to_text_string(s).split(".")[-1] measure = u"{0:.0f}.{1:.2s} min".format(m, s) else: h, m = divmod(measure, 3600) if m > 60: m /= 60 measure = u"{0:.0f}h:{1:.0f}min".format(h, m) return measure
def data(self, index, role=Qt.DisplayRole): """Cell content""" if not index.isValid(): return to_qvariant() if role == Qt.DisplayRole or role == Qt.EditRole: column = index.column() row = index.row() if column == 0: return to_qvariant(to_text_string(self.df_index[row])) else: value = self.get_value(row, column-1) if isinstance(value, float): try: return to_qvariant(self._format % value) except (ValueError, TypeError): # may happen if format = '%d' and value = NaN; # see issue 4139 return to_qvariant(DEFAULT_FORMAT % value) else: try: return to_qvariant(to_text_string(value)) except UnicodeDecodeError: return to_qvariant(encoding.to_unicode(value)) elif role == Qt.BackgroundColorRole: return to_qvariant(self.get_bgcolor(index)) elif role == Qt.FontRole: return to_qvariant(get_font(font_size_delta=DEFAULT_SMALL_DELTA)) return to_qvariant()
def save_to_conf(self): """Save settings to configuration file""" for checkbox, (option, _default) in list(self.checkboxes.items()): self.set_option(option, checkbox.isChecked()) for radiobutton, (option, _default) in list(self.radiobuttons.items()): self.set_option(option, radiobutton.isChecked()) for lineedit, (option, _default) in list(self.lineedits.items()): self.set_option(option, to_text_string(lineedit.text())) for textedit, (option, _default) in list(self.textedits.items()): self.set_option(option, to_text_string(textedit.toPlainText())) for spinbox, (option, _default) in list(self.spinboxes.items()): self.set_option(option, spinbox.value()) for combobox, (option, _default) in list(self.comboboxes.items()): data = combobox.itemData(combobox.currentIndex()) self.set_option(option, from_qvariant(data, to_text_string)) for (fontbox, sizebox), option in list(self.fontboxes.items()): font = fontbox.currentFont() font.setPointSize(sizebox.value()) self.set_font(font, option) for clayout, (option, _default) in list(self.coloredits.items()): self.set_option(option, to_text_string(clayout.lineedit.text())) for (clayout, cb_bold, cb_italic), (option, _default) in list(self.scedits.items()): color = to_text_string(clayout.lineedit.text()) bold = cb_bold.isChecked() italic = cb_italic.isChecked() self.set_option(option, (color, bold, italic))
def update_browse_tabs_menu(self): """Update browse tabs menu""" self.browse_tabs_menu.clear() names = [] dirnames = [] for index in range(self.count()): if self.menu_use_tooltips: text = to_text_string(self.tabToolTip(index)) else: text = to_text_string(self.tabText(index)) names.append(text) if osp.isfile(text): # Testing if tab names are filenames dirnames.append(osp.dirname(text)) offset = None # If tab names are all filenames, removing common path: if len(names) == len(dirnames): common = get_common_path(dirnames) if common is None: offset = None else: offset = len(common)+1 if offset <= 3: # Common path is not a path but a drive letter... offset = None for index, text in enumerate(names): tab_action = create_action(self, text[offset:], icon=self.tabIcon(index), toggled=lambda state, index=index: self.setCurrentIndex(index), tip=self.tabToolTip(index)) tab_action.setChecked(index == self.currentIndex()) self.browse_tabs_menu.addAction(tab_action)
def setup_and_check(self, value): """Verify if TextEditor is able to display strings passed to it.""" try: to_text_string(value, 'utf8') return True except: return False
def create_folders_files(tmpdir, request): """A project directory with dirs and files for testing.""" project_dir = to_text_string(tmpdir.mkdir('project')) destination_dir = to_text_string(tmpdir.mkdir('destination')) top_folder = osp.join(project_dir, 'top_folder_in_proj') if not osp.exists(top_folder): os.mkdir(top_folder) list_paths = [] for item in request.param: if osp.splitext(item)[1]: if osp.split(item)[0]: dirs, fname = osp.split(item) dirpath = osp.join(top_folder, dirs) if not osp.exists(dirpath): os.makedirs(dirpath) item_path = osp.join(dirpath, fname) else: item_path = osp.join(top_folder, item) else: dirpath = osp.join(top_folder, item) if not osp.exists(dirpath): os.makedirs(dirpath) item_path = dirpath if not osp.isdir(item_path): with open(item_path, 'w') as fh: fh.write("File Path:\n" + str(item_path).replace(os.sep, '/')) list_paths.append(item_path) return list_paths, project_dir, destination_dir, top_folder
def test_fix_indentation(code_editor_indent_bot): """Test fix_indentation() method.""" editor, qtbot = code_editor_indent_bot # Contains tabs. original = ("\t\n" "class a():\t\n" "\tself.b = 1\n" "\tprint(self.b)\n" "\n" ) # Fix indentation replaces tabs with indent_chars spaces. fixed = (" \n" "class a(): \n" " self.b = 1\n" " print(self.b)\n" "\n" ) editor.set_text(original) editor.fix_indentation() assert to_text_string(editor.toPlainText()) == fixed assert editor.document().isModified() # Test that undo/redo works - issue 1754. editor.undo() assert to_text_string(editor.toPlainText()) == original assert not editor.document().isModified() editor.redo() assert to_text_string(editor.toPlainText()) == fixed assert editor.document().isModified()
def chdir(self, directory=None, browsing_history=False): """Set directory as working directory""" if directory is not None: directory = osp.abspath(to_text_string(directory)) if browsing_history: directory = self.history[self.histindex] elif directory in self.history: self.histindex = self.history.index(directory) else: if self.histindex is None: self.history = [] else: self.history = self.history[:self.histindex+1] if len(self.history) == 0 or \ (self.history and self.history[-1] != directory): self.history.append(directory) self.histindex = len(self.history)-1 directory = to_text_string(directory) if PY2: PermissionError = OSError try: os.chdir(directory) self.parent_widget.open_dir.emit(directory) self.refresh(new_path=directory, force_current=True) except PermissionError: QMessageBox.critical(self.parent_widget, "Error", _("You don't have the right permissions to " "open this directory"))
def __repr__(self): match = to_text_string(self.match).rstrip() font = get_font() _str = to_text_string("<b>{1}</b> ({2}): " "<span style='font-family:{0};" "font-size:75%;'>{3}</span>") return _str.format(font.family(), self.lineno, self.colno, match)
def create_new_folder(self, current_path, title, subtitle, is_package): """Create new folder""" if current_path is None: current_path = '' if osp.isfile(current_path): current_path = osp.dirname(current_path) name, valid = QInputDialog.getText(self, title, subtitle, QLineEdit.Normal, "") if valid: dirname = osp.join(current_path, to_text_string(name)) try: os.mkdir(dirname) except EnvironmentError as error: QMessageBox.critical(self, title, _("<b>Unable " "to create folder <i>%s</i></b>" "<br><br>Error message:<br>%s" ) % (dirname, to_text_string(error))) finally: if is_package: fname = osp.join(dirname, '__init__.py') try: with open(fname, 'wb') as f: f.write(to_binary_string('#')) return dirname except EnvironmentError as error: QMessageBox.critical(self, title, _("<b>Unable " "to create file <i>%s</i></b>" "<br><br>Error message:<br>%s" ) % (fname, to_text_string(error)))
def run(self): html_text = self.html_text_no_doc doc = self.doc if doc is not None: if type(doc) is dict and 'docstring' in doc.keys(): try: context = generate_context(name=doc['name'], argspec=doc['argspec'], note=doc['note'], math=self.math_option, img_path=self.img_path, css_path=self.css_path) html_text = sphinxify(doc['docstring'], context) if doc['docstring'] == '': if any([doc['name'], doc['argspec'], doc['note']]): msg = _("No further documentation available") html_text += '<div class="hr"></div>' else: msg = _("No documentation available") html_text += '<div id="doc-warning">%s</div>' % msg except Exception as error: self.error_msg.emit(to_text_string(error)) return elif self.context is not None: try: html_text = sphinxify(doc, self.context) except Exception as error: self.error_msg.emit(to_text_string(error)) return self.html_ready.emit(html_text)
def get_number_matches(self, pattern, source_text='', case=False, regexp=False): """Get the number of matches for the searched text.""" pattern = to_text_string(pattern) if not pattern: return 0 if not regexp: pattern = re.escape(pattern) if not source_text: source_text = to_text_string(self.toPlainText()) try: if case: regobj = re.compile(pattern) else: regobj = re.compile(pattern, re.IGNORECASE) except sre_constants.error: return None number_matches = 0 for match in regobj.finditer(source_text): number_matches += 1 return number_matches
def register_plugin(self): """Register plugin in Spyder's main window""" self.main.add_dockwidget(self) self.edit.connect(self.main.editor.load) self.removed.connect(self.main.editor.removed) self.removed_tree.connect(self.main.editor.removed_tree) self.renamed.connect(self.main.editor.renamed) self.main.editor.open_dir.connect(self.chdir) self.create_module.connect(self.main.editor.new) self.run.connect( lambda fname: self.main.open_external_console(to_text_string(fname), osp.dirname(to_text_string(fname)), '', False, False, True, '', False)) # Signal "set_explorer_cwd(QString)" will refresh only the # contents of path passed by the signal in explorer: self.main.workingdirectory.set_explorer_cwd.connect( lambda directory: self.refresh_plugin(new_path=directory, force_current=True)) self.open_dir.connect( lambda dirname: self.main.workingdirectory.chdir(dirname, refresh_explorer=False)) self.sig_open_file.connect(self.main.open_file) self.sig_new_file.connect(lambda t: self.main.editor.new(text=t))
def find_multiline_pattern(self, regexp, cursor, findflag): """Reimplement QTextDocument's find method Add support for *multiline* regular expressions""" pattern = to_text_string(regexp.pattern()) text = to_text_string(self.toPlainText()) try: regobj = re.compile(pattern) except sre_constants.error: return if findflag & QTextDocument.FindBackward: # Find backward offset = min([cursor.selectionEnd(), cursor.selectionStart()]) text = text[:offset] matches = [_m for _m in regobj.finditer(text, 0, offset)] if matches: match = matches[-1] else: return else: # Find forward offset = max([cursor.selectionEnd(), cursor.selectionStart()]) match = regobj.search(text, offset) if match: pos1, pos2 = match.span() fcursor = self.textCursor() fcursor.setPosition(pos1) fcursor.setPosition(pos2, QTextCursor.KeepAnchor) return fcursor
def get_options(self, all=False): # Getting options self.search_text.lineEdit().setStyleSheet("") self.exclude_pattern.lineEdit().setStyleSheet("") utext = to_text_string(self.search_text.currentText()) if not utext: return try: texts = [(utext.encode('utf-8'), 'utf-8')] except UnicodeEncodeError: texts = [] for enc in self.supported_encodings: try: texts.append((utext.encode(enc), enc)) except UnicodeDecodeError: pass text_re = self.edit_regexp.isChecked() exclude = to_text_string(self.exclude_pattern.currentText()) exclude_re = self.exclude_regexp.isChecked() case_sensitive = self.case_button.isChecked() python_path = False if not case_sensitive: texts = [(text[0].lower(), text[1]) for text in texts] file_search = self.path_selection_combo.is_file_search() path = self.path_selection_combo.get_current_searchpath() # Finding text occurrences if not exclude_re: exclude = fnmatch.translate(exclude) else: try: exclude = re.compile(exclude) except Exception: exclude_edit = self.exclude_pattern.lineEdit() exclude_edit.setStyleSheet(self.REGEX_INVALID) return None if text_re: try: texts = [(re.compile(x[0]), x[1]) for x in texts] except Exception: self.search_text.lineEdit().setStyleSheet(self.REGEX_INVALID) return None if all: search_text = [to_text_string(self.search_text.itemText(index)) for index in range(self.search_text.count())] exclude = [to_text_string(self.exclude_pattern.itemText(index)) for index in range(self.exclude_pattern.count())] path_history = self.path_selection_combo.get_external_paths() exclude_idx = self.exclude_pattern.currentIndex() more_options = self.more_options.isChecked() return (search_text, text_re, [], exclude, exclude_idx, exclude_re, python_path, more_options, case_sensitive, path_history) else: return (path, file_search, exclude, texts, text_re, case_sensitive)
def _find_parenthesis(self, position, forward=True): """ If 'forward' is True (resp. False), proceed forwards (resp. backwards) through the line that contains 'position' until an unmatched closing (resp. opening) parenthesis is found. Returns a tuple containing the position of this parenthesis (or -1 if it is not found) and the number commas (at depth 0) found along the way. """ commas = depth = 0 document = self._text_edit.document() char = to_text_string(document.characterAt(position)) # Search until a match is found or a non-printable character is # encountered. while category(char) != 'Cc' and position > 0: if char == ',' and depth == 0: commas += 1 elif char == ')': if forward and depth == 0: break depth += 1 elif char == '(': if not forward and depth == 0: break depth -= 1 position += 1 if forward else -1 char = to_text_string(document.characterAt(position)) else: position = -1 return position, commas
def rename_file(self, fname): """Rename file""" path, valid = QInputDialog.getText(self, _('Rename'), _('New name:'), QLineEdit.Normal, osp.basename(fname)) if valid: path = osp.join(osp.dirname(fname), to_text_string(path)) if path == fname: return if osp.exists(path): if QMessageBox.warning(self, _("Rename"), _("Do you really want to rename <b>%s</b> and " "overwrite the existing file <b>%s</b>?" ) % (osp.basename(fname), osp.basename(path)), QMessageBox.Yes|QMessageBox.No) == QMessageBox.No: return try: misc.rename_file(fname, path) self.parent_widget.renamed.emit(fname, path) return path except EnvironmentError as error: QMessageBox.critical(self, _("Rename"), _("<b>Unable to rename file <i>%s</i></b>" "<br><br>Error message:<br>%s" ) % (osp.basename(fname), to_text_string(error)))
def get_number_matches(self, pattern, source_text='', case=False): """Get the number of matches for the searched text.""" pattern = to_text_string(pattern) if not pattern: return 0 if not source_text: if WEBENGINE: self.page().toPlainText(self.set_source_text) source_text = to_text_string(self.source_text) else: source_text = to_text_string( self.page().mainFrame().toPlainText()) try: if case: regobj = re.compile(pattern) else: regobj = re.compile(pattern, re.IGNORECASE) except sre_constants.error: return number_matches = 0 for match in regobj.finditer(source_text): number_matches += 1 return number_matches
def update_btn_state(self): user = to_text_string(self.le_user.text()).strip() != '' password = to_text_string(self.le_password.text()).strip() != '' token = to_text_string(self.le_token.text()).strip() != '' enable = ((user and password and self.tabs.currentIndex() == 0) or (token and self.tabs.currentIndex() == 1)) self.bt_sign_in.setEnabled(enable)
def select_directory(self): """Select directory""" self.redirect_stdio.emit(False) directory = getexistingdirectory(self, _("Select directory"), self.path) if directory: directory = to_text_string(osp.abspath(to_text_string(directory))) self.redirect_stdio.emit(True) return directory
def start(self, wdir=None, args=None, pythonpath=None): filename = to_text_string(self.filecombo.currentText()) if wdir is None: wdir = self._last_wdir if wdir is None: wdir = osp.basename(filename) print(filename) if os.name == 'nt': # On Windows, one has to replace backslashes by slashes to avoid # confusion with escape characters (otherwise, for example, '\t' # will be interpreted as a tabulation): filename = osp.normpath(filename).replace(os.sep, '/') if args is None: args = self._last_args if args is None: args = [] if pythonpath is None: pythonpath = self._last_pythonpath self._last_wdir = wdir self._last_args = args self._last_pythonpath = pythonpath self.datelabel.setText(_('Running tests, please wait...')) self.process = QProcess(self) self.process.setProcessChannelMode(QProcess.SeparateChannels) self.process.setWorkingDirectory(filename) self.process.readyReadStandardOutput.connect(self.read_output) self.process.readyReadStandardError.connect( lambda: self.read_output(error=True)) self.process.finished.connect(self.finished) self.stop_button.clicked.connect(self.process.kill) if pythonpath is not None: env = [to_text_string(_pth) for _pth in self.process.systemEnvironment()] baseshell.add_pathlist_to_PYTHONPATH(env, pythonpath) self.process.setEnvironment(env) self.output = '' self.error_output = '' executable = "py.test" p_args = ['--junit-xml', self.DATAPATH] # executable = "nosetests" # p_args = ['--with-xunit', "--xunit-file=%s" % self.DATAPATH] if args: p_args.extend(programs.shell_split(args)) self.process.start(executable, p_args) running = self.process.waitForStarted() self.set_running_state(running) if not running: QMessageBox.critical(self, _("Error"), _("Process failed to start"))
def find_text(self, text, changed=True, forward=True, case=False, words=False, regexp=False): """Find text""" cursor = self.textCursor() findflag = QTextDocument.FindFlag() if not forward: findflag = findflag | QTextDocument.FindBackward if case: findflag = findflag | QTextDocument.FindCaseSensitively moves = [QTextCursor.NoMove] if forward: moves += [QTextCursor.NextWord, QTextCursor.Start] if changed: if to_text_string(cursor.selectedText()): new_position = min([cursor.selectionStart(), cursor.selectionEnd()]) cursor.setPosition(new_position) else: cursor.movePosition(QTextCursor.PreviousWord) else: moves += [QTextCursor.End] if regexp: text = to_text_string(text) else: text = re.escape(to_text_string(text)) if QT55_VERSION: pattern = QRegularExpression(u"\\b{}\\b".format(text) if words else text) if case: pattern.setPatternOptions( QRegularExpression.CaseInsensitiveOption) else: pattern = QRegExp(u"\\b{}\\b".format(text) if words else text, Qt.CaseSensitive if case else Qt.CaseInsensitive, QRegExp.RegExp2) for move in moves: cursor.movePosition(move) if regexp and '\\n' in text: # Multiline regular expression found_cursor = self.find_multiline_pattern(pattern, cursor, findflag) else: # Single line find: using the QTextDocument's find function, # probably much more efficient than ours found_cursor = self.document().find(pattern, cursor, findflag) if found_cursor is not None and not found_cursor.isNull(): self.setTextCursor(found_cursor) return True return False
def save_breakpoints(self): breakpoints = repr(self.breakpoints) filename = to_text_string(self.filename) breakpoints = to_text_string(breakpoints) filename = osp.normpath(osp.abspath(filename)) if breakpoints: breakpoints = eval(breakpoints) else: breakpoints = [] save_breakpoints(filename, breakpoints) self.editor.sig_breakpoints_saved.emit()
def append_to_history(self, filename, command): """ Append an entry to history filename Slot for append_to_history signal emitted by shell instance """ if not is_text_string(filename): # filename is a QString filename = to_text_string(filename.toUtf8(), "utf-8") command = to_text_string(command) index = self.filenames.index(filename) self.editors[index].append(command) if self.get_option("go_to_eof"): self.editors[index].set_cursor_position("eof") self.tabwidget.setCurrentIndex(index)
def get(self): self.runconf.args_enabled = self.clo_cb.isChecked() self.runconf.args = to_text_string(self.clo_edit.text()) self.runconf.wdir_enabled = self.wd_cb.isChecked() self.runconf.wdir = to_text_string(self.wd_edit.text()) self.runconf.current = self.current_radio.isChecked() self.runconf.systerm = self.systerm_radio.isChecked() self.runconf.interact = self.interact_cb.isChecked() self.runconf.show_kill_warning = self.show_kill_warning_cb.isChecked() self.runconf.post_mortem = self.post_mortem_cb.isChecked() self.runconf.python_args_enabled = self.pclo_cb.isChecked() self.runconf.python_args = to_text_string(self.pclo_edit.text()) return self.runconf.get()
def __move_line_or_selection(self, after_current_line=True): """Move current line or selected text""" cursor = self.textCursor() cursor.beginEditBlock() start_pos, end_pos = self.__save_selection() last_line = False # ------ Select text # Get selection start location cursor.setPosition(start_pos) cursor.movePosition(QTextCursor.StartOfBlock) start_pos = cursor.position() # Get selection end location cursor.setPosition(end_pos) if not cursor.atBlockStart() or end_pos == start_pos: cursor.movePosition(QTextCursor.EndOfBlock) cursor.movePosition(QTextCursor.NextBlock) end_pos = cursor.position() # Check if selection ends on the last line of the document if cursor.atEnd(): if not cursor.atBlockStart() or end_pos == start_pos: last_line = True # ------ Stop if at document boundary cursor.setPosition(start_pos) if cursor.atStart() and not after_current_line: # Stop if selection is already at top of the file while moving up cursor.endEditBlock() self.setTextCursor(cursor) self.__restore_selection(start_pos, end_pos) return cursor.setPosition(end_pos, QTextCursor.KeepAnchor) if last_line and after_current_line: # Stop if selection is already at end of the file while moving down cursor.endEditBlock() self.setTextCursor(cursor) self.__restore_selection(start_pos, end_pos) return # ------ Move text sel_text = to_text_string(cursor.selectedText()) cursor.removeSelectedText() if after_current_line: # Shift selection down text = to_text_string(cursor.block().text()) sel_text = os.linesep + sel_text[0:-1] # Move linesep at the start cursor.movePosition(QTextCursor.EndOfBlock) start_pos += len(text) + 1 end_pos += len(text) if not cursor.atEnd(): end_pos += 1 else: # Shift selection up if last_line: # Remove the last linesep and add it to the selected text cursor.deletePreviousChar() sel_text = sel_text + os.linesep cursor.movePosition(QTextCursor.StartOfBlock) end_pos += 1 else: cursor.movePosition(QTextCursor.PreviousBlock) text = to_text_string(cursor.block().text()) start_pos -= len(text) + 1 end_pos -= len(text) + 1 cursor.insertText(sel_text) cursor.endEditBlock() self.setTextCursor(cursor) self.__restore_selection(start_pos, end_pos)
if case: findflag = findflag | QWebEnginePage.FindCaseSensitively return self.findText(text, QWebEnginePage.FindFlags(findflag)) def get_selected_text(self): """Return text selected by current text cursor""" return self.selectedText() def set_source_text(self, source_text): """Set source text of the page. Callback for QWebEngineView.""" self.source_text = source_text def get_number_matches(self, pattern, source_text='', case=False): """Get the number of matches for the searched text.""" pattern = to_text_string(pattern) if not pattern: return 0 if not source_text: if WEBENGINE: self.page().toPlainText(self.set_source_text) source_text = to_text_string(self.source_text) else: source_text = to_text_string( self.page().mainFrame().toPlainText()) try: if case: regobj = re.compile(pattern) else: regobj = re.compile(pattern, re.IGNORECASE) except sre_constants.error:
def test_open_project_uses_visible_config(projects, tmpdir, value): """Test that when a project is opened, the project explorer is only opened if the config option visible_if_project_open is set.""" projects.set_option('visible_if_project_open', value) projects.open_project(path=to_text_string(tmpdir)) assert projects.dockwidget.isVisible() == value
def get_external_paths(self): """Returns a list of the external paths listed in the combobox.""" return [to_text_string(self.itemText(i)) for i in range(EXTERNAL_PATHS, self.count())]
def option_changed(self, option, value): """Handle when the value of an option has changed""" setattr(self, to_text_string(option), value) self.shellwidget.set_namespace_view_settings() if self.setup_in_progress is False: self.sig_option_changed.emit(option, value)
def dropEvent(self, event): """Reimplement Qt method""" event.ignore() action = event.dropAction() if action not in (Qt.MoveAction, Qt.CopyAction): return # QTreeView must not remove the source items even in MoveAction mode: # event.setDropAction(Qt.CopyAction) dst = self.get_filename(self.indexAt(event.pos())) yes_to_all, no_to_all = None, None src_list = [to_text_string(url.toString()) for url in event.mimeData().urls()] if len(src_list) > 1: buttons = QMessageBox.Yes|QMessageBox.YesToAll| \ QMessageBox.No|QMessageBox.NoToAll|QMessageBox.Cancel else: buttons = QMessageBox.Yes|QMessageBox.No for src in src_list: if src == dst: continue dst_fname = osp.join(dst, osp.basename(src)) if osp.exists(dst_fname): if yes_to_all is not None or no_to_all is not None: if no_to_all: continue elif osp.isfile(dst_fname): answer = QMessageBox.warning(self, _('Project explorer'), _('File <b>%s</b> already exists.<br>' 'Do you want to overwrite it?') % dst_fname, buttons) if answer == QMessageBox.No: continue elif answer == QMessageBox.Cancel: break elif answer == QMessageBox.YesToAll: yes_to_all = True elif answer == QMessageBox.NoToAll: no_to_all = True continue else: QMessageBox.critical(self, _('Project explorer'), _('Folder <b>%s</b> already exists.' ) % dst_fname, QMessageBox.Ok) event.setDropAction(Qt.CopyAction) return try: if action == Qt.CopyAction: if osp.isfile(src): shutil.copy(src, dst) else: shutil.copytree(src, dst) else: if osp.isfile(src): misc.move_file(src, dst) else: shutil.move(src, dst) self.parent_widget.removed.emit(src) except EnvironmentError as error: if action == Qt.CopyAction: action_str = _('copy') else: action_str = _('move') QMessageBox.critical(self, _("Project Explorer"), _("<b>Unable to %s <i>%s</i></b>" "<br><br>Error message:<br>%s" ) % (action_str, src, to_text_string(error)))
def test_filesystem_notifications(qtbot, projects, tmpdir): """ Test that filesystem notifications are emitted when creating, deleting and moving files and directories. """ # Create a directory for the project and some files. project_root = tmpdir.mkdir('project0') folder0 = project_root.mkdir('folder0') folder1 = project_root.mkdir('folder1') file0 = project_root.join('file0') file1 = folder0.join('file1') file2 = folder0.join('file2') file3 = folder1.join('file3') file0.write('') file1.write('') file3.write('ab') # Open the project projects.open_project(path=to_text_string(project_root)) # Get a reference to the filesystem event handler fs_handler = projects.watcher.event_handler # Test file creation with qtbot.waitSignal(fs_handler.sig_file_created, timeout=30000) as blocker: file2.write('') file_created, is_dir = blocker.args assert file_created == to_text_string(file2) assert not is_dir # Test folder creation with qtbot.waitSignal(fs_handler.sig_file_created, timeout=3000) as blocker: folder2 = project_root.mkdir('folder2') folder_created, is_dir = blocker.args assert folder_created == osp.join(to_text_string(project_root), 'folder2') # Test file move/renaming new_file = osp.join(to_text_string(folder0), 'new_file') with qtbot.waitSignal(fs_handler.sig_file_moved, timeout=3000) as blocker: shutil.move(to_text_string(file1), new_file) original_file, file_moved, is_dir = blocker.args assert original_file == to_text_string(file1) assert file_moved == new_file assert not is_dir # Test folder move/renaming new_folder = osp.join(to_text_string(project_root), 'new_folder') with qtbot.waitSignal(fs_handler.sig_file_moved, timeout=3000) as blocker: shutil.move(to_text_string(folder2), new_folder) original_folder, folder_moved, is_dir = blocker.args assert original_folder == to_text_string(folder2) assert folder_moved == new_folder assert is_dir # Test file deletion with qtbot.waitSignal(fs_handler.sig_file_deleted, timeout=3000) as blocker: os.remove(to_text_string(file0)) deleted_file, is_dir = blocker.args assert deleted_file == to_text_string(file0) assert not is_dir assert not osp.exists(to_text_string(file0)) # Test folder deletion with qtbot.waitSignal(fs_handler.sig_file_deleted, timeout=3000) as blocker: shutil.rmtree(to_text_string(folder0)) deleted_folder, is_dir = blocker.args assert to_text_string(folder0) in deleted_folder # For some reason this fails in macOS if not sys.platform == 'darwin': # Test file/folder modification with qtbot.waitSignal(fs_handler.sig_file_modified, timeout=3000) as blocker: file3.write('abc') modified_file, is_dir = blocker.args assert modified_file in to_text_string(file3)
def url_to_text(self, url): """Convert QUrl object to displayed text in combo box""" return osp.splitext(to_text_string(url.path()))[0][1:]
def get_source_code(self): """Return associated editor source code.""" return to_text_string(self.editor.toPlainText())
def replace_find(self, focus_replace_text=False, replace_all=False): """Replace and find""" if (self.editor is not None): replace_text = to_text_string(self.replace_text.currentText()) search_text = to_text_string(self.search_text.currentText()) re_pattern = None # Check regexp before proceeding if self.re_button.isChecked(): try: re_pattern = re.compile(search_text) # Check if replace_text can be substituted in re_pattern # Fixes issue #7177 re_pattern.sub(replace_text, '') except re.error: # Do nothing with an invalid regexp return case = self.case_button.isChecked() first = True cursor = None while True: if first: # First found seltxt = to_text_string(self.editor.get_selected_text()) cmptxt1 = search_text if case else search_text.lower() cmptxt2 = seltxt if case else seltxt.lower() if re_pattern is None: has_selected = self.editor.has_selected_text() if has_selected and cmptxt1 == cmptxt2: # Text was already found, do nothing pass else: if not self.find(changed=False, forward=True, rehighlight=False): break else: if len(re_pattern.findall(cmptxt2)) > 0: pass else: if not self.find(changed=False, forward=True, rehighlight=False): break first = False wrapped = False position = self.editor.get_position('cursor') position0 = position cursor = self.editor.textCursor() cursor.beginEditBlock() else: position1 = self.editor.get_position('cursor') if is_position_inf(position1, position0 + len(replace_text) - len(search_text) + 1): # Identify wrapping even when the replace string # includes part of the search string wrapped = True if wrapped: if position1 == position or \ is_position_sup(position1, position): # Avoid infinite loop: replace string includes # part of the search string break if position1 == position0: # Avoid infinite loop: single found occurrence break position0 = position1 if re_pattern is None: cursor.removeSelectedText() cursor.insertText(replace_text) else: seltxt = to_text_string(cursor.selectedText()) cursor.removeSelectedText() cursor.insertText(re_pattern.sub(replace_text, seltxt)) if self.find_next(): found_cursor = self.editor.textCursor() cursor.setPosition(found_cursor.selectionStart(), QTextCursor.MoveAnchor) cursor.setPosition(found_cursor.selectionEnd(), QTextCursor.KeepAnchor) else: break if not replace_all: break if cursor is not None: cursor.endEditBlock() if focus_replace_text: self.replace_text.setFocus()
def __init__(self, text, title='', font=None, parent=None, readonly=False): QDialog.__init__(self, parent) # Destroying the C++ object right after closing the dialog box, # otherwise it may be garbage-collected in another QThread # (e.g. the editor's analysis thread in Spyder), thus leading to # a segmentation fault on UNIX or an application crash on Windows self.setAttribute(Qt.WA_DeleteOnClose) self.text = None self.btn_save_and_close = None # Display text as unicode if it comes as bytes, so users see # its right representation if is_binary_string(text): self.is_binary = True text = to_text_string(text, 'utf8') else: self.is_binary = False self.layout = QVBoxLayout() self.setLayout(self.layout) # Text edit self.edit = QTextEdit(parent) self.edit.setReadOnly(readonly) self.edit.textChanged.connect(self.text_changed) self.edit.setPlainText(text) if font is None: font = get_font() self.edit.setFont(font) self.layout.addWidget(self.edit) # Buttons configuration btn_layout = QHBoxLayout() btn_layout.addStretch() if not readonly: self.btn_save_and_close = QPushButton(_('Save and Close')) self.btn_save_and_close.setDisabled(True) self.btn_save_and_close.clicked.connect(self.accept) btn_layout.addWidget(self.btn_save_and_close) self.btn_close = QPushButton(_('Close')) self.btn_close.setAutoDefault(True) self.btn_close.setDefault(True) self.btn_close.clicked.connect(self.reject) btn_layout.addWidget(self.btn_close) self.layout.addLayout(btn_layout) # Make the dialog act as a window if sys.platform == 'darwin': # See spyder-ide/spyder#12825 self.setWindowFlags(Qt.Tool) else: # Make the dialog act as a window self.setWindowFlags(Qt.Window) self.setWindowIcon(ima.icon('edit')) if title: try: unicode_title = to_text_string(title) except UnicodeEncodeError: unicode_title = u'' else: unicode_title = u'' self.setWindowTitle(_("Text editor") + \ u"%s" % (u" - " + unicode_title if unicode_title else u""))
def value_to_display(value, minmax=False): """Convert value for display purpose""" try: numeric_numpy_types = (int64, int32, float64, float32, complex128, complex64) if isinstance(value, recarray): fields = value.names display = 'Field names: ' + ', '.join(fields) elif isinstance(value, MaskedArray): display = 'Masked array' elif isinstance(value, ndarray): if minmax: try: display = 'Min: %r\nMax: %r' % (value.min(), value.max()) except (TypeError, ValueError): display = repr(value) else: display = repr(value) elif isinstance(value, (list, tuple, dict, set)): display = CollectionsRepr.repr(value) elif isinstance(value, Image): display = '%s Mode: %s' % (address(value), value.mode) elif isinstance(value, DataFrame): cols = value.columns if PY2 and len(cols) > 0: # Get rid of possible BOM utf-8 data present at the # beginning of a file, which gets attached to the first # column header when headers are present in the first # row. # Fixes Issue 2514 try: ini_col = to_text_string(cols[0], encoding='utf-8-sig') except: ini_col = to_text_string(cols[0]) cols = [ini_col] + [to_text_string(c) for c in cols[1:]] else: cols = [to_text_string(c) for c in cols] display = 'Column names: ' + ', '.join(list(cols)) elif isinstance(value, NavigableString): # Fixes Issue 2448 display = to_text_string(value) elif is_binary_string(value): try: display = to_text_string(value, 'utf8') except: display = value elif is_text_string(value): display = value elif isinstance(value, NUMERIC_TYPES) or isinstance(value, bool) or \ isinstance(value, datetime.date) or \ isinstance(value, numeric_numpy_types): display = repr(value) else: # Note: Don't trust on repr's. They can be inefficient and # so freeze Spyder quite easily # display = repr(value) type_str = to_text_string(type(value)) display = type_str[1:-1] except: type_str = to_text_string(type(value)) display = type_str[1:-1] # Truncate display at 80 chars to avoid freezing Spyder # because of large displays if len(display) > 80: display = display[:80].rstrip() + ' ...' return display
def save_history(self): """Save history to a text file in user home directory""" open(self.LOG_PATH, 'w').write("\n".join( \ [to_text_string(self.pydocbrowser.url_combo.itemText(index)) for index in range(self.pydocbrowser.url_combo.count())]))
def edit_script(self, filename, external_editor): filename = to_text_string(filename) if external_editor: self.external_editor(filename) else: self.parent().edit_script(filename)
def replace_find(self, focus_replace_text=False, replace_all=False): """Replace and find.""" if self.editor is None: return replace_text = to_text_string(self.replace_text.currentText()) search_text = to_text_string(self.search_text.currentText()) re_pattern = None case = self.case_button.isChecked() re_flags = re.MULTILINE if case else re.IGNORECASE | re.MULTILINE # Check regexp before proceeding if self.re_button.isChecked(): try: re_pattern = re.compile(search_text, flags=re_flags) # Check if replace_text can be substituted in re_pattern # Fixes spyder-ide/spyder#7177. re_pattern.sub(replace_text, '') except re.error: # Do nothing with an invalid regexp return first = True cursor = None while True: if first: # First found seltxt = to_text_string(self.editor.get_selected_text()) cmptxt1 = search_text if case else search_text.lower() cmptxt2 = seltxt if case else seltxt.lower() if re_pattern is None: has_selected = self.editor.has_selected_text() if has_selected and cmptxt1 == cmptxt2: # Text was already found, do nothing pass else: if not self.find(changed=False, forward=True, rehighlight=False): break else: if len(re_pattern.findall(cmptxt2)) > 0: pass else: if not self.find(changed=False, forward=True, rehighlight=False): break first = False wrapped = False position = self.editor.get_position('cursor') position0 = position cursor = self.editor.textCursor() cursor.beginEditBlock() else: position1 = self.editor.get_position('cursor') if is_position_inf( position1, position0 + len(replace_text) - len(search_text) + 1): # Identify wrapping even when the replace string # includes part of the search string wrapped = True if wrapped: if (position1 == position or is_position_sup(position1, position)): # Avoid infinite loop: replace string includes # part of the search string break if position1 == position0: # Avoid infinite loop: single found occurrence break position0 = position1 if re_pattern is None: cursor.removeSelectedText() cursor.insertText(replace_text) else: seltxt = to_text_string(cursor.selectedText()) # Note: If the selection obtained from an editor spans a line # break, the text will contain a Unicode U+2029 paragraph # separator character instead of a newline \n character. # See: spyder-ide/spyder#2675 eol_char = get_eol_chars(self.editor.toPlainText()) seltxt = seltxt.replace(u'\u2029', eol_char) cursor.removeSelectedText() cursor.insertText(re_pattern.sub(replace_text, seltxt)) if self.find_next(set_focus=False): found_cursor = self.editor.textCursor() cursor.setPosition(found_cursor.selectionStart(), QTextCursor.MoveAnchor) cursor.setPosition(found_cursor.selectionEnd(), QTextCursor.KeepAnchor) else: break if not replace_all: break if cursor is not None: cursor.endEditBlock() if focus_replace_text: self.replace_text.setFocus() else: self.editor.setFocus() if getattr(self.editor, 'document_did_change', False): self.editor.document_did_change()
def is_valid(self, qstr=None): """Return True if string is valid""" if qstr is None: qstr = self.currentText() return osp.isdir(to_text_string(qstr))
def find(self, changed=True, forward=True, rehighlight=True, start_highlight_timer=False, multiline_replace_check=True): """Call the find function""" # When several lines are selected in the editor and replace box is # activated, dynamic search is deactivated to prevent changing the # selection. Otherwise we show matching items. if multiline_replace_check and self.replace_widgets[0].isVisible(): sel_text = self.editor.get_selected_text() if len(to_text_string(sel_text).splitlines()) > 1: return None text = self.search_text.currentText() if len(text) == 0: self.search_text.lineEdit().setStyleSheet("") if not self.is_code_editor: # Clears the selection for WebEngine self.editor.find_text('') self.change_number_matches() return None else: case = self.case_button.isChecked() word = self.words_button.isChecked() regexp = self.re_button.isChecked() found = self.editor.find_text(text, changed, forward, case=case, word=word, regexp=regexp) stylesheet = self.STYLE[found] tooltip = self.TOOLTIP[found] if not found and regexp: error_msg = regexp_error_msg(text) if error_msg: # special styling for regexp errors stylesheet = self.STYLE['regexp_error'] tooltip = self.TOOLTIP['regexp_error'] + ': ' + error_msg self.search_text.lineEdit().setStyleSheet(stylesheet) self.search_text.setToolTip(tooltip) if self.is_code_editor and found: cursor = QTextCursor(self.editor.textCursor()) TextHelper(self.editor).unfold_if_colapsed(cursor) if rehighlight or not self.editor.found_results: self.highlight_timer.stop() if start_highlight_timer: self.highlight_timer.start() else: self.highlight_matches() else: self.clear_matches() number_matches = self.editor.get_number_matches(text, case=case, regexp=regexp, word=word) if hasattr(self.editor, 'get_match_number'): match_number = self.editor.get_match_number(text, case=case, regexp=regexp, word=word) else: match_number = 0 self.change_number_matches(current_match=match_number, total_matches=number_matches) return found
def truncate_result(self, line, start, end): ellipsis = '...' max_line_length = 80 max_num_char_fragment = 40 html_escape_table = { "&": "&", '"': """, "'": "'", ">": ">", "<": "<", } def html_escape(text): """Produce entities within text.""" return "".join(html_escape_table.get(c, c) for c in text) line = to_text_string(line) left, match, right = line[:start], line[start:end], line[end:] if len(line) > max_line_length: offset = (len(line) - len(match)) // 2 left = left.split(' ') num_left_words = len(left) if num_left_words == 1: left = left[0] if len(left) > max_num_char_fragment: left = ellipsis + left[-offset:] left = [left] right = right.split(' ') num_right_words = len(right) if num_right_words == 1: right = right[0] if len(right) > max_num_char_fragment: right = right[:offset] + ellipsis right = [right] left = left[-4:] right = right[:4] if len(left) < num_left_words: left = [ellipsis] + left if len(right) < num_right_words: right = right + [ellipsis] left = ' '.join(left) right = ' '.join(right) if len(left) > max_num_char_fragment: left = ellipsis + left[-30:] if len(right) > max_num_char_fragment: right = right[:30] + ellipsis line_match_format = to_text_string('{0}<b>{1}</b>{2}') left = html_escape(left) right = html_escape(right) match = html_escape(match) trunc_line = line_match_format.format(left, match, right) return trunc_line
def value_to_display(value, minmax=False, level=0): """Convert value for display purpose""" # To save current Numpy threshold np_threshold = FakeObject try: numeric_numpy_types = (int64, int32, int16, int8, uint64, uint32, uint16, uint8, float64, float32, float16, complex128, complex64, bool_) if ndarray is not FakeObject: # Save threshold np_threshold = get_printoptions().get('threshold') # Set max number of elements to show for Numpy arrays # in our display set_printoptions(threshold=10) if isinstance(value, recarray): if level == 0: fields = value.names display = 'Field names: ' + ', '.join(fields) else: display = 'Recarray' elif isinstance(value, MaskedArray): display = 'Masked array' elif isinstance(value, ndarray): if level == 0: if minmax: try: display = 'Min: %r\nMax: %r' % (value.min(), value.max()) except (TypeError, ValueError): if value.dtype.type in numeric_numpy_types: display = str(value) else: display = default_display(value) elif value.dtype.type in numeric_numpy_types: display = str(value) else: display = default_display(value) else: display = 'Numpy array' elif any([type(value) == t for t in [list, set, tuple, dict]]): display = collections_display(value, level + 1) elif isinstance(value, Image): if level == 0: display = '%s Mode: %s' % (address(value), value.mode) else: display = 'Image' elif isinstance(value, DataFrame): if level == 0: cols = value.columns if PY2 and len(cols) > 0: # Get rid of possible BOM utf-8 data present at the # beginning of a file, which gets attached to the first # column header when headers are present in the first # row. # Fixes Issue 2514 try: ini_col = to_text_string(cols[0], encoding='utf-8-sig') except: ini_col = to_text_string(cols[0]) cols = [ini_col] + [to_text_string(c) for c in cols[1:]] else: cols = [to_text_string(c) for c in cols] display = 'Column names: ' + ', '.join(list(cols)) else: display = 'Dataframe' elif isinstance(value, NavigableString): # Fixes Issue 2448 display = to_text_string(value) if level > 0: display = u"'" + display + u"'" elif isinstance(value, Index): if level == 0: display = value.summary() else: display = 'Index' elif is_binary_string(value): # We don't apply this to classes that extend string types # See issue 5636 if is_type_text_string(value): try: display = to_text_string(value, 'utf8') if level > 0: display = u"'" + display + u"'" except: display = value if level > 0: display = b"'" + display + b"'" else: display = default_display(value) elif is_text_string(value): # We don't apply this to classes that extend string types # See issue 5636 if is_type_text_string(value): display = value if level > 0: display = u"'" + display + u"'" else: display = default_display(value) elif (isinstance(value, datetime.date) or isinstance(value, datetime.timedelta)): display = str(value) elif (isinstance(value, NUMERIC_TYPES) or isinstance(value, bool) or isinstance(value, numeric_numpy_types)): display = repr(value) else: if level == 0: display = default_display(value) else: display = default_display(value, with_module=False) except: display = default_display(value) # Truncate display at 70 chars to avoid freezing Spyder # because of large displays if len(display) > 70: if is_binary_string(display): ellipses = b' ...' else: ellipses = u' ...' display = display[:70].rstrip() + ellipses # Restore Numpy threshold if np_threshold is not FakeObject: set_printoptions(threshold=np_threshold) return display
def populate_branch(self, editor, root_item, tree_cache=None): """ Generates an outline of the editor's content and stores the result in a cache. """ if tree_cache is None: tree_cache = {} for _l in list(tree_cache.keys()): # Checking if key is still in tree cache in case one of its # ancestors was deleted in the meantime (deleting all children): if _l not in tree_cache: continue # Removing deleted items if not tree_cache[_l][0].oedata.is_valid(): remove_from_tree_cache(tree_cache, line=_l) continue # Moving cached items whose line changed block_line = tree_cache[_l][0].line if _l != block_line: if block_line in tree_cache: remove_from_tree_cache(tree_cache, line=block_line) if _l in tree_cache: tree_cache[block_line] = tree_cache[_l] tree_cache.pop(_l) ancestors = [(root_item, 0)] cell_ancestors = [(root_item, 0)] previous_item = None previous_level = None prev_cell_level = None prev_cell_item = None for data in editor.outlineexplorer_data_list(): try: line_nb = data.get_block_number() if line_nb is None: continue line_nb += 1 except AttributeError: continue level = None if data is None else data.fold_level citem, _d = tree_cache.get(line_nb, (None, "")) if citem is not None: # Check if underlying C++ object has been deleted try: citem.text(0) except RuntimeError: tree_cache.pop(line_nb) citem, _d = (None, "") # Skip iteration if line is not the first line of a foldable block if level is None: if citem is not None: remove_from_tree_cache(tree_cache, line=line_nb) continue # Searching for class/function statements not_class_nor_function = data.is_not_class_nor_function() if not not_class_nor_function: class_name = data.get_class_name() if class_name is None: func_name = data.get_function_name() if func_name is None: if citem is not None: remove_from_tree_cache(tree_cache, line=line_nb) continue # Skip iteration for if/else/try/for/etc foldable blocks. if not_class_nor_function and not data.is_comment(): if citem is not None: remove_from_tree_cache(tree_cache, line=line_nb) continue if citem is not None: cname = to_text_string(citem.text(0)) cparent = citem.parent() clevel = citem.level() # Blocks for Cell Groups. if (data is not None and data.def_type == data.CELL and self.group_cells): preceding = (root_item if previous_item is None else previous_item) cell_level = data.cell_level if prev_cell_level is not None: if cell_level == prev_cell_level: pass elif cell_level > prev_cell_level: cell_ancestors.append( (prev_cell_item, prev_cell_level)) else: while (len(cell_ancestors) > 1 and cell_level <= prev_cell_level): cell_ancestors.pop(-1) _item, prev_cell_level = cell_ancestors[-1] parent, _level = cell_ancestors[-1] if citem is not None: if data.text == cname and level == clevel: previous_level = clevel previous_item = citem continue else: remove_from_tree_cache(tree_cache, line=line_nb) item = CellItem(data, parent, preceding) debug = "%s -- %s/%s" % (str( item.line).rjust(6), to_text_string( item.parent().text(0)), to_text_string(item.text(0))) tree_cache[line_nb] = (item, debug) ancestors = [(item, 0)] prev_cell_level = cell_level prev_cell_item = item previous_item = item continue # Blocks for Code Groups. if previous_level is not None: if level == previous_level: pass elif level > previous_level: ancestors.append((previous_item, previous_level)) else: while len(ancestors) > 1 and level <= previous_level: ancestors.pop(-1) _item, previous_level = ancestors[-1] parent, _level = ancestors[-1] preceding = root_item if previous_item is None else previous_item if not_class_nor_function and data.is_comment(): if not self.show_comments: if citem is not None: remove_from_tree_cache(tree_cache, line=line_nb) continue if citem is not None: if data.text == cname and level == clevel: previous_level = clevel previous_item = citem continue else: remove_from_tree_cache(tree_cache, line=line_nb) if data.def_type == data.CELL: item = CellItem(data, parent, preceding) else: item = CommentItem(data, parent, preceding) elif class_name is not None: if citem is not None: if (class_name == cname and level == clevel and parent is cparent): previous_level = clevel previous_item = citem continue else: remove_from_tree_cache(tree_cache, line=line_nb) item = ClassItem(data, parent, preceding) else: if citem is not None: if (func_name == cname and level == clevel and parent is cparent): previous_level = clevel previous_item = citem continue else: remove_from_tree_cache(tree_cache, line=line_nb) item = FunctionItem(data, parent, preceding) debug = "%s -- %s/%s" % (str( item.line).rjust(6), to_text_string( item.parent().text(0)), to_text_string(item.text(0))) tree_cache[line_nb] = (item, debug) previous_level = level previous_item = item return tree_cache
def is_valid(self, qstr=None): """Return True if string is valid""" if qstr is None: qstr = self.currentText() return is_module_or_package(to_text_string(qstr))
def handle_qbytearray(obj, encoding): """Qt/Python2/3 compatibility helper.""" if isinstance(obj, QByteArray): obj = obj.data() return to_text_string(obj, encoding=encoding)
def set_current_text(self, text): """Sets the text of the QLineEdit of the QComboBox.""" self.lineEdit().setText(to_text_string(text))
def url_combo_activated(self, valid): """Load URL from combo box first item""" text = to_text_string(self.url_combo.currentText()) self.go_to(self.text_to_url(text))
def get_options(self, all=False): # Getting options self.search_text.lineEdit().setStyleSheet("") self.exclude_pattern.lineEdit().setStyleSheet("") utext = to_text_string(self.search_text.currentText()) if not utext: return try: texts = [(utext.encode('utf-8'), 'utf-8')] except UnicodeEncodeError: texts = [] for enc in self.supported_encodings: try: texts.append((utext.encode(enc), enc)) except UnicodeDecodeError: pass text_re = self.edit_regexp.isChecked() exclude = to_text_string(self.exclude_pattern.currentText()) exclude_re = self.exclude_regexp.isChecked() case_sensitive = self.case_button.isChecked() python_path = False if not case_sensitive: texts = [(text[0].lower(), text[1]) for text in texts] file_search = self.path_selection_combo.is_file_search() path = self.path_selection_combo.get_current_searchpath() if not exclude_re: items = [fnmatch.translate(item.strip()) for item in exclude.split(",") if item.strip() != ''] exclude = '|'.join(items) # Validate regular expressions: try: if exclude: exclude = re.compile(exclude) except Exception: exclude_edit = self.exclude_pattern.lineEdit() exclude_edit.setStyleSheet(self.REGEX_INVALID) return None if text_re: try: texts = [(re.compile(x[0]), x[1]) for x in texts] except Exception: self.search_text.lineEdit().setStyleSheet(self.REGEX_INVALID) return None if all: search_text = [to_text_string(self.search_text.itemText(index)) for index in range(self.search_text.count())] exclude = [to_text_string(self.exclude_pattern.itemText(index)) for index in range(self.exclude_pattern.count())] path_history = self.path_selection_combo.get_external_paths() exclude_idx = self.exclude_pattern.currentIndex() more_options = self.more_options.isChecked() return (search_text, text_re, [], exclude, exclude_idx, exclude_re, python_path, more_options, case_sensitive, path_history) else: return (path, file_search, exclude, texts, text_re, case_sensitive)
def createWindow(self, webwindowtype): import webbrowser webbrowser.open(to_text_string(self.url().toString()))
def start(self, wdir=None, args=None, pythonpath=None): filename = to_text_string(self.filecombo.currentText()) if wdir is None: wdir = self._last_wdir if wdir is None: wdir = osp.basename(filename) if args is None: args = self._last_args if args is None: args = [] if pythonpath is None: pythonpath = self._last_pythonpath self._last_wdir = wdir self._last_args = args self._last_pythonpath = pythonpath self.datelabel.setText(_('Profiling, please wait...')) self.process = QProcess(self) self.process.setProcessChannelMode(QProcess.SeparateChannels) self.process.setWorkingDirectory(wdir) self.process.readyReadStandardOutput.connect(self.read_output) self.process.readyReadStandardError.connect( lambda: self.read_output(error=True)) self.process.finished.connect( lambda ec, es=QProcess.ExitStatus: self.finished(ec, es)) self.stop_button.clicked.connect(self.kill) if pythonpath is not None: env = [ to_text_string(_pth) for _pth in self.process.systemEnvironment() ] add_pathlist_to_PYTHONPATH(env, pythonpath) processEnvironment = QProcessEnvironment() for envItem in env: envName, separator, envValue = envItem.partition('=') processEnvironment.insert(envName, envValue) self.process.setProcessEnvironment(processEnvironment) self.output = '' self.error_output = '' self.stopped = False p_args = ['-m', 'cProfile', '-o', self.DATAPATH] if os.name == 'nt': # On Windows, one has to replace backslashes by slashes to avoid # confusion with escape characters (otherwise, for example, '\t' # will be interpreted as a tabulation): p_args.append(osp.normpath(filename).replace(os.sep, '/')) else: p_args.append(filename) if args: p_args.extend(shell_split(args)) executable = sys.executable if executable.endswith("spyder.exe"): # py2exe distribution executable = "python.exe" self.process.start(executable, p_args) running = self.process.waitForStarted() self.set_running_state(running) if not running: QMessageBox.critical(self, _("Error"), _("Process failed to start"))
def setup_and_check(self, data, title='', readonly=False, xlabels=None, ylabels=None): """ Setup ArrayEditor: return False if data is not supported, True otherwise """ self.data = data readonly = readonly or not self.data.flags.writeable is_record_array = data.dtype.names is not None is_masked_array = isinstance(data, np.ma.MaskedArray) if data.ndim > 3: self.error(_("Arrays with more than 3 dimensions are not " "supported")) return False if xlabels is not None and len(xlabels) != self.data.shape[1]: self.error(_("The 'xlabels' argument length do no match array " "column number")) return False if ylabels is not None and len(ylabels) != self.data.shape[0]: self.error(_("The 'ylabels' argument length do no match array row " "number")) return False if not is_record_array: dtn = data.dtype.name if dtn == 'object': # If the array doesn't have shape, we can't display it if data.shape == (): self.error(_("Object arrays without shape are not " "supported")) return False # We don't know what's inside these arrays, so we can't handle # edits self.readonly = readonly = True elif (dtn not in SUPPORTED_FORMATS and not dtn.startswith('str') and not dtn.startswith('unicode')): arr = _("%s arrays") % data.dtype.name self.error(_("%s are currently not supported") % arr) return False self.layout = QGridLayout() self.setLayout(self.layout) self.setWindowIcon(ima.icon('arredit')) if title: title = to_text_string(title) + " - " + _("NumPy array") else: title = _("Array editor") if readonly: title += ' (' + _('read only') + ')' self.setWindowTitle(title) self.resize(600, 500) # Stack widget self.stack = QStackedWidget(self) if is_record_array: for name in data.dtype.names: self.stack.addWidget(ArrayEditorWidget(self, data[name], readonly, xlabels, ylabels)) elif is_masked_array: self.stack.addWidget(ArrayEditorWidget(self, data, readonly, xlabels, ylabels)) self.stack.addWidget(ArrayEditorWidget(self, data.data, readonly, xlabels, ylabels)) self.stack.addWidget(ArrayEditorWidget(self, data.mask, readonly, xlabels, ylabels)) elif data.ndim == 3: pass else: self.stack.addWidget(ArrayEditorWidget(self, data, readonly, xlabels, ylabels)) self.arraywidget = self.stack.currentWidget() if self.arraywidget: self.arraywidget.model.dataChanged.connect( self.save_and_close_enable) self.stack.currentChanged.connect(self.current_widget_changed) self.layout.addWidget(self.stack, 1, 0) # Buttons configuration btn_layout = QHBoxLayout() if is_record_array or is_masked_array or data.ndim == 3: if is_record_array: btn_layout.addWidget(QLabel(_("Record array fields:"))) names = [] for name in data.dtype.names: field = data.dtype.fields[name] text = name if len(field) >= 3: title = field[2] if not is_text_string(title): title = repr(title) text += ' - '+title names.append(text) else: names = [_('Masked data'), _('Data'), _('Mask')] if data.ndim == 3: # QSpinBox self.index_spin = QSpinBox(self, keyboardTracking=False) self.index_spin.valueChanged.connect(self.change_active_widget) # QComboBox names = [str(i) for i in range(3)] ra_combo = QComboBox(self) ra_combo.addItems(names) ra_combo.currentIndexChanged.connect(self.current_dim_changed) # Adding the widgets to layout label = QLabel(_("Axis:")) btn_layout.addWidget(label) btn_layout.addWidget(ra_combo) self.shape_label = QLabel() btn_layout.addWidget(self.shape_label) label = QLabel(_("Index:")) btn_layout.addWidget(label) btn_layout.addWidget(self.index_spin) self.slicing_label = QLabel() btn_layout.addWidget(self.slicing_label) # set the widget to display when launched self.current_dim_changed(self.last_dim) else: ra_combo = QComboBox(self) ra_combo.currentIndexChanged.connect(self.stack.setCurrentIndex) ra_combo.addItems(names) btn_layout.addWidget(ra_combo) if is_masked_array: label = QLabel(_("<u>Warning</u>: changes are applied separately")) label.setToolTip(_("For performance reasons, changes applied "\ "to masked array won't be reflected in "\ "array's data (and vice-versa).")) btn_layout.addWidget(label) btn_layout.addStretch() if not readonly: self.btn_save_and_close = QPushButton(_('Save and Close')) self.btn_save_and_close.setDisabled(True) self.btn_save_and_close.clicked.connect(self.accept) btn_layout.addWidget(self.btn_save_and_close) self.btn_close = QPushButton(_('Close')) self.btn_close.setAutoDefault(True) self.btn_close.setDefault(True) self.btn_close.clicked.connect(self.reject) btn_layout.addWidget(self.btn_close) self.layout.addLayout(btn_layout, 2, 0) self.setMinimumSize(400, 300) # Make the dialog act as a window self.setWindowFlags(Qt.Window) return True
def translate_dumb(x): if not is_unicode(x): return to_text_string(x, "utf-8") return x
def get_search_score(query, choice, ignore_case=True, apply_regex=True, template='{}'): """Returns a tuple with the enriched text (if a template is provided) and a score for the match. Parameters ---------- query : str String with letters to search in choice (in order of appearance). choice : str Sentence/words in which to search for the 'query' letters. ignore_case : bool, optional Optional value perform a case insensitive search (True by default). apply_regex : bool, optional Optional value (True by default) to perform a regex search. Useful when this function is called directly. template : str, optional Optional template string to surround letters found in choices. This is useful when using a rich text editor ('{}' by default). Examples: '<b>{}</b>', '<code>{}</code>', '<i>{}</i>' Returns ------- results : tuple Tuples where the first item is the text (enriched if a template was used) and the second item is a search score. Notes ----- The score is given according the following precedence (high to low): - Letters in one word and no spaces with exact match. Example: 'up' in 'up stroke' - Letters in one word and no spaces with partial match. Example: 'up' in 'upstream stroke' - Letters in one word but with skip letters. Example: 'cls' in 'close up' - Letters in two or more words Example: 'cls' in 'car lost' """ original_choice = to_text_string(choice, encoding='utf-8') result = (original_choice, NOT_FOUND_SCORE) # Handle empty string case if not query: return result query = to_text_string(query, encoding='utf-8') choice = to_text_string(choice, encoding='utf-8') if ignore_case: query = query.lower() choice = choice.lower() if apply_regex: pattern = get_search_regex(query, ignore_case=ignore_case) r = re.search(pattern, choice) if r is None: return result else: sep = u'-' # Matches will be replaced by this character let = u'x' # Nonmatches (except spaed) will be replaced by this score = 0 exact_words = [query == to_text_string(word, encoding='utf-8') for word in choice.split(u' ')] partial_words = [query in word for word in choice.split(u' ')] if any(exact_words) or any(partial_words): pos_start = choice.find(query) pos_end = pos_start + len(query) score += pos_start text = choice.replace(query, sep*len(query), 1) enriched_text = original_choice[:pos_start] +\ template.format(original_choice[pos_start:pos_end]) +\ original_choice[pos_end:] if any(exact_words): # Check if the query words exists in a word with exact match score += 1 elif any(partial_words): # Check if the query words exists in a word with partial match score += 100 else: # Check letter by letter text = [l for l in original_choice] if ignore_case: temp_text = [l.lower() for l in original_choice] else: temp_text = text[:] # Give points to start of string score += temp_text.index(query[0]) # Find the query letters and replace them by `sep`, also apply # template as needed for enricching the letters in the text enriched_text = text[:] for char in query: if char != u'' and char in temp_text: index = temp_text.index(char) enriched_text[index] = template.format(text[index]) text[index] = sep temp_text = [u' ']*(index + 1) + temp_text[index+1:] enriched_text = u''.join(enriched_text) patterns_text = [] for i, char in enumerate(text): if char != u' ' and char != sep: new_char = let else: new_char = char patterns_text.append(new_char) patterns_text = u''.join(patterns_text) for i in reversed(range(1, len(query) + 1)): score += (len(query) - patterns_text.count(sep*i))*100000 temp = patterns_text.split(sep) while u'' in temp: temp.remove(u'') if not patterns_text.startswith(sep): temp = temp[1:] if not patterns_text.endswith(sep): temp = temp[:-1] for pat in temp: score += pat.count(u' ')*10000 score += pat.count(let)*100 return original_choice, enriched_text, score