def highlight_ascii(self) -> None: """Bidirectional highlighting from ascii""" # Create and get cursors for getting and setting selections. highlight_cursor = QTextCursor(self.main_text_area.document()) cursor = self.ascii_text_area.textCursor() # Clear any current selections and reset text color. highlight_cursor.select(QTextCursor.Document) highlight_cursor.setCharFormat(QTextCharFormat()) highlight_cursor.clearSelection() # Information about where selections and rows start. selected_text = cursor.selectedText() # The actual text selected. selection_start = cursor.selectionStart() ascii_text = self.ascii_text_area.toPlainText().replace('\n', '') main_start = self.__get_valuable_positions_length(ascii_text[:selection_start]) main_start = self.__positive_compensation(main_start) total_bytes = self.__get_valuable_positions_length(selected_text) # get all valuable positions # \n and word length compensation total_bytes = self.__positive_compensation(total_bytes) selection_end = main_start + total_bytes # Select text and highlight it. highlight_cursor.setPosition(main_start, QTextCursor.MoveAnchor) highlight_cursor.setPosition(selection_end, QTextCursor.KeepAnchor) highlight = QTextCharFormat() highlight.setBackground(Qt.red) highlight_cursor.setCharFormat(highlight) highlight_cursor.clearSelection()
def highlight(begin, end, color, qtextedit): form = QTextCharFormat() form.setBackground(Qt.red) cursor = QTextCursor(qtextedit.document()) cursor.setPosition(begin, QTextCursor.MoveAnchor) cursor.setPosition(end, QTextCursor.KeepAnchor) cursor.setCharFormat(form)
def _format_suggestion(self, index): suggestion = index.data(Qt.DisplayRole) self._doc.clear() cursor = QTextCursor(self._doc) cursor.setCharFormat(self._translation_char_format) cursor.insertText(escape_translation(suggestion.text) + ':') if not suggestion.steno_list: cursor.insertText(' ' + NO_SUGGESTIONS_STRING) return for strokes_list in suggestion.steno_list[:MAX_SUGGESTIONS_COUNT]: cursor.insertBlock() cursor.setCharFormat(self._strokes_char_format) cursor.insertText(' ' + '/'.join(strokes_list))
def html_copy(cursor, scheme='editor', number_lines=False): """Return a new QTextDocument with highlighting set as HTML textcharformats. The cursor is a cursor of a document.Document instance. If the cursor has a selection, only the selection is put in the new document. If number_lines is True, line numbers are added. """ data = textformats.formatData(scheme) doc = QTextDocument() doc.setDefaultFont(data.font) doc.setPlainText(cursor.document().toPlainText()) if metainfo.info(cursor.document()).highlighting: highlight(doc, mapping(data), ly.lex.state(documentinfo.mode(cursor.document()))) if cursor.hasSelection(): # cut out not selected text start, end = cursor.selectionStart(), cursor.selectionEnd() cur1 = QTextCursor(doc) cur1.setPosition(start, QTextCursor.KeepAnchor) cur2 = QTextCursor(doc) cur2.setPosition(end) cur2.movePosition(QTextCursor.End, QTextCursor.KeepAnchor) cur2.removeSelectedText() cur1.removeSelectedText() if number_lines: c = QTextCursor(doc) f = QTextCharFormat() f.setBackground(QColor('#eeeeee')) if cursor.hasSelection(): num = cursor.document().findBlock( cursor.selectionStart()).blockNumber() + 1 last = cursor.document().findBlock(cursor.selectionEnd()) else: num = 1 last = cursor.document().lastBlock() lastnum = last.blockNumber() + 1 padding = len(format(lastnum)) block = doc.firstBlock() while block.isValid(): c.setPosition(block.position()) c.setCharFormat(f) c.insertText('{0:>{1}d} '.format(num, padding)) block = block.next() num += 1 return doc
def html_copy(cursor, scheme='editor', number_lines=False): """Return a new QTextDocument with highlighting set as HTML textcharformats. The cursor is a cursor of a document.Document instance. If the cursor has a selection, only the selection is put in the new document. If number_lines is True, line numbers are added. """ data = textformats.formatData(scheme) doc = QTextDocument() doc.setDefaultFont(data.font) doc.setPlainText(cursor.document().toPlainText()) if metainfo.info(cursor.document()).highlighting: highlight(doc, mapping(data), ly.lex.state(documentinfo.mode(cursor.document()))) if cursor.hasSelection(): # cut out not selected text start, end = cursor.selectionStart(), cursor.selectionEnd() cur1 = QTextCursor(doc) cur1.setPosition(start, QTextCursor.KeepAnchor) cur2 = QTextCursor(doc) cur2.setPosition(end) cur2.movePosition(QTextCursor.End, QTextCursor.KeepAnchor) cur2.removeSelectedText() cur1.removeSelectedText() if number_lines: c = QTextCursor(doc) f = QTextCharFormat() f.setBackground(QColor('#eeeeee')) if cursor.hasSelection(): num = cursor.document().findBlock(cursor.selectionStart()).blockNumber() + 1 last = cursor.document().findBlock(cursor.selectionEnd()) else: num = 1 last = cursor.document().lastBlock() lastnum = last.blockNumber() + 1 padding = len(format(lastnum)) block = doc.firstBlock() while block.isValid(): c.setPosition(block.position()) c.setCharFormat(f) c.insertText('{0:>{1}d} '.format(num, padding)) block = block.next() num += 1 return doc
def highlightMain(self): # Create and get cursors for getting and setting selections. highlightCursor = QTextCursor(self.asciiTextArea.document()) # asciitextArea의 처음을 가르치는 커서를 구성 cursor = self.mainTextArea.textCursor() # 커서의 위치의 사본을 따서 복사 함 # Clear any current selections and reset text color. highlightCursor.select(QTextCursor.Document) highlightCursor.setCharFormat(QTextCharFormat()) highlightCursor.clearSelection() # Information about where selections and rows start. selectedText = cursor.selectedText() # The actual text selected. selectionStart = cursor.selectionStart() selectionEnd = cursor.selectionEnd() mainText = self.mainTextArea.toPlainText().replace('\n', 'A') #mainText = self.mainTextArea.toPlainText() totalBytes = 0 for char in mainText[selectionStart:selectionEnd]: if char is not ' ': totalBytes += len(char) asciiStart = 0 for char in mainText[:selectionStart]: if char is not ' ': asciiStart += len(char) totalBytes = round(totalBytes / self.byteWidth) asciiStart = round(asciiStart / self.byteWidth) asciiEnd = asciiStart + totalBytes asciiText = self.asciiTextArea.toPlainText() # Select text and highlight it. highlightCursor.setPosition(asciiStart, QTextCursor.MoveAnchor) highlightCursor.setPosition(asciiEnd, QTextCursor.KeepAnchor) highlight = QTextCharFormat() highlight.setBackground(Qt.darkCyan) highlightCursor.setCharFormat(highlight) highlightCursor.clearSelection()
def set_bytes(self, bs): self.pretty_mode = False self.data = bs chunks = HextEditor._split_by_printables(bs) self.clear() cursor = QTextCursor(self.textedit.document()) cursor.beginEditBlock() cursor.select(QTextCursor.Document) cursor.setCharFormat(QTextCharFormat()) cursor.clearSelection() for chunk in chunks: if chr(chunk[0]) in qtprintable: cursor.insertText(chunk.decode()) else: for b in chunk: self._insert_byte(cursor, b) cursor.endEditBlock()
def __highlight(self, positions, color=None, cancel=False): cursor = QTextCursor(self.document()) modified = self.document().isModified() for position in positions: if position > self.get_position('eof'): return cursor.setPosition(position) cursor.movePosition( QTextCursor.NextCharacter, QTextCursor.KeepAnchor) charformat = cursor.charFormat() pen = QPen(Qt.NoPen) if cancel else QPen(color) charformat.setTextOutline(pen) cursor.setCharFormat(charformat) if cancel: charformat = QTextCharFormat() cursor.movePosition( QTextCursor.NextCharacter, QTextCursor.KeepAnchor) cursor.setCharFormat(charformat) cursor.clearSelection() self.setCurrentCharFormat(charformat) self.document().setModified(modified)
def highlight(document, mapping=None, state=None): """Highlight a generic QTextDocument once. mapping is an optional Mapping instance, defaulting to the current configured editor highlighting formats (returned by highlight_mapping()). state is an optional ly.lex.State instance. By default the text type is guessed. """ if mapping is None: mapping = highlight_mapping() if state is None: state = ly.lex.guessState(document.toPlainText()) cursor = QTextCursor(document) block = document.firstBlock() while block.isValid(): for token in state.tokens(block.text()): f = mapping[token] if f: cursor.setPosition(block.position() + token.pos) cursor.setPosition(block.position() + token.end, QTextCursor.KeepAnchor) cursor.setCharFormat(f) block = block.next()
class ExceptionDialog(QDialog): def __init__(self, exc): super().__init__() self.setWindowTitle(_("Exception occured")) button_box = QDialogButtonBox(QDialogButtonBox.Ok) button_box.button(QDialogButtonBox.Ok).setText(_("Ok")) button_box.accepted.connect(self.accept) self.setMinimumSize(800, 100) layout = QVBoxLayout() self.__exception_text = QTextEdit(self) self.__exception_text.setReadOnly(True) self.__exception_text.setLineWrapMode(QTextEdit.NoWrap) layout.addWidget(self.__exception_text) layout.addWidget(button_box) self.setLayout(layout) self.__cursor = QTextCursor(self.__exception_text.document()) self.__normal_format = self.__cursor.charFormat() self.__separator_format = self.__cursor.charFormat() self.__separator_format.setFontWeight(QFont.Bold) self.__bold_format = self.__cursor.charFormat() self.__bold_format.setFontWeight(QFont.Bold) self.__file_format = self.__cursor.charFormat() self.__file_format.setFontUnderline(True) self.__file_format.setForeground(QColor(Qt.blue)) self.__lineno_format = self.__cursor.charFormat() self.__lineno_format.setForeground(QColor(Qt.darkGreen)) self.__code_format = self.__cursor.charFormat() font = QFontDatabase.systemFont(QFontDatabase.FixedFont) self.__code_format.setFontFamily(font.family()) self.__add_exception(exc) def __add_exception(self, exc): if isinstance(exc, Exception): exc = ExceptionInfo.from_exception(exc) if exc.cause is not None: self.__add_exception(exc.cause) self.__add_separator_line(traceback._cause_message.strip()) elif exc.context is not None: self.__add_exception(exc.context) self.__add_separator_line(traceback._context_message.strip()) for line in exc.traceback: if line.module is not None: self.__add_module_line(line.module, line.lineno, line.function) else: self.__add_file_line(line.filename, line.lineno, line.function) if line.text: self.__add_code_line(line.text) self.__cursor.insertText("\n") self.__add_exc_description_line(exc.type_name, exc.description) def __add_separator_line(self, text): self.__cursor.setCharFormat(self.__separator_format) self.__cursor.insertText("\n") self.__cursor.insertText(text) self.__cursor.insertText("\n") def __add_module_line(self, module, lineno, function): self.__cursor.setCharFormat(self.__bold_format) self.__cursor.insertText("Module ") self.__cursor.setCharFormat(self.__file_format) self.__cursor.insertText(module) self.__cursor.setCharFormat(self.__bold_format) self.__cursor.insertText(" line ") self.__cursor.setCharFormat(self.__lineno_format) self.__cursor.insertText(str(lineno)) self.__cursor.setCharFormat(self.__bold_format) self.__cursor.insertText(" in ") self.__cursor.setCharFormat(self.__normal_format) self.__cursor.insertText(function) self.__cursor.insertText("\n") def __add_file_line(self, filename, lineno, function): self.__cursor.setCharFormat(self.__bold_format) self.__cursor.insertText("File ") self.__cursor.setCharFormat(self.__file_format) self.__cursor.insertText(filename) self.__cursor.setCharFormat(self.__bold_format) self.__cursor.insertText(" line ") self.__cursor.setCharFormat(self.__lineno_format) self.__cursor.insertText(str(lineno)) self.__cursor.setCharFormat(self.__bold_format) self.__cursor.insertText(" in ") self.__cursor.setCharFormat(self.__normal_format) self.__cursor.insertText(function) self.__cursor.insertText("\n") def __add_code_line(self, code): self.__cursor.setCharFormat(self.__code_format) self.__cursor.insertText(" ") self.__cursor.insertText(code) self.__cursor.insertText("\n") def __add_exc_description_line(self, type, description): self.__cursor.setCharFormat(self.__bold_format) self.__cursor.insertText(type) self.__cursor.insertText(": ") self.__cursor.setCharFormat(self.__normal_format) self.__cursor.insertText(description) self.__cursor.insertText("\n")
class TextSectionEditor(QTextDocument): def __init__(self, sectionId: str, content="", pos=0, selectionStart=0, selectionEnd=0): super().__init__() self.dtb = DTB() self.sectionId = sectionId self.setDefaultStyleSheet(CSS) self.setHtml(content) self.s_start = selectionStart self.s_end = selectionEnd self.cur = QTextCursor(self) self.cur.setPosition(pos) self.result = { "text": "", "cursorPosition": self.pos, "eventAccepted": False } self.pending = False @property def len(self): return self.characterCount() @property def s_len(self): return abs(self.s_end - self.s_start) @property def pos(self): return self.cur.position() def onChange(self): self._update_ddb() self.setResponse(True) return self.result def onLoad(self): item = self.dtb.getDB("Section", self.sectionId) self.setHtml(item["text"]) self.setResponse(True, cur=self.len) return self.result def onMenu(self, style={}, **kwargs): backup = [self.pos, self.s_start, self.s_end] for k, v in style.items(): # un peut répétition mais uniquement sur des if alors ... if k == "fgColor": self._set_fg_color(v) elif k == "underline": # pragma: no branch self._set_underline(style["underline"]) self.cur.setPosition(backup[0]) self.s_start = backup[1] self.s_end = backup[2] if self.pending: # else: self._update_ddb() else: self.setResponse(False) return self.result def onKey(self, event): # on met en premier ceux à qui il faut passer l'event if event["key"] == Qt.Key_Return: self.do_key_return(event) elif event["modifiers"] == Qt.ControlModifier: self.do_control_modifier(event) else: self.setResponse(False) if self.pending: self._update_ddb() return self.result def do_control_modifier(self, event): if event == KeyW.KEY_1: self.do_key_1() elif event == KeyW.KEY_2: self.do_key_2() elif event == KeyW.KEY_3: self.do_key_3() elif event == KeyW.KEY_4: self.do_key_4() elif event["key"] == Qt.Key_U: # pragma: no branch self.do_key_u() def do_key_1(self): self._set_fg_color(BLACK) def do_key_2(self): self._set_fg_color(BLUE) def do_key_3(self): self._set_fg_color(GREEN) def do_key_4(self): self._set_fg_color(RED) def do_key_return(self, event): block = self.findBlock(self.pos) if event["modifiers"] == Qt.ControlModifier: self._appendEmptyBlock() elif event["modifiers"] == Qt.ShiftModifier: self._insertEmptyBlock() elif block.blockFormat().headingLevel(): self._appendEmptyBlock() else: if self._headerAutoFormat(): return else: self.setResponse(False) def do_key_u(self): self._set_underline("toggle") def setResponse(self, accepted, text=None, cur=None): self.result["text"] = text or self.toHtml() self.result["cursorPosition"] = cur or self.cur.position() self.result["eventAccepted"] = accepted def _appendEmptyBlock(self, section="p", pre_move=QTextCursor.EndOfBlock, set_response=True): self.cur.movePosition(pre_move) self.cur.insertBlock(blockFormat[section], blockCharFormat[section]) self.cur.insertFragment(QTextDocumentFragment.fromPlainText("")) self.pending = True if set_response: self.setResponse(True) def _insertEmptyBlock(self, section="p", pre_move=QTextCursor.StartOfBlock, set_response=True): old = self.cur.blockFormat().headingLevel() self._appendEmptyBlock(pre_move=pre_move, set_response=False) self._set_block_style(old) self.cur.movePosition(QTextCursor.PreviousBlock) self._set_block_style(section) self.pending = True if set_response: # pragma: no branch self.setResponse(True) def _headerAutoFormat(self): # on check les expressions régulières suivantes: # #, ##, ###, ####, ##### line = self.cur.block().text() matched = RE_AUTOPARAGRAPH_DEBUT.search(line) matched_at_start = False if not matched: matched = RE_AUTOPARAGRAPH_FIN.search(line) if not matched: return False else: matched_at_start = True # strip les # et applique les styles par défault level = len(matched.groups()[0]) if matched_at_start: text = self.cur.block().text()[level + 1:] else: text = self.cur.block().text()[:-(level + 1)] self.cur.beginEditBlock() self.cur.select(QTextCursor.LineUnderCursor) self.cur.setCharFormat(blockCharFormat[level]) self.cur.insertText(text) self.cur.setBlockFormat(blockFormat[level]) self.cur.endEditBlock() self._appendEmptyBlock() self.pending = True self.setResponse(True) return True @contextmanager def _merge_char_format(self): self._select_word_or_selection() f: QTextCharFormat = QTextCharFormat() yield f self.cur.mergeCharFormat(f) self.pending = True self.setResponse(True, cur=max(self.pos, self.s_start, self.s_end)) def _select_word_or_selection(self): if self.s_start < self.pos: self.cur.movePosition(QTextCursor.Left, QTextCursor.KeepAnchor, self.s_len) elif self.s_end > self.pos: self.cur.movePosition(QTextCursor.Right, QTextCursor.KeepAnchor, self.s_len) else: self.cur.select(QTextCursor.WordUnderCursor) def _set_block_style(self, level): self.cur.setBlockFormat(blockFormat[level]) self.cur.setBlockCharFormat(blockCharFormat[level]) def _set_fg_color(self, color): with self._merge_char_format() as f: f.setForeground(QBrush(QColor(color))) def _set_underline(self, value): with self._merge_char_format() as f: if value == "toggle": value = not self.cur.charFormat().fontUnderline() f.setFontUnderline(value) def _update_ddb(self): new_body = TextSectionFormatter(self.toHtml()).build_body() self.dtb.setDB("Section", self.sectionId, {"text": new_body}) return new_body
def load(self, constrained_text): # Get the rendered constrained text to display to the editor if not constrained_text.can_render(): raise Exception( "The constrained text to be displayed in the text editor is not properly configured. Either the data or the file template has not been loaded." ) raw_text = constrained_text.render_textlines() visible_text = raw_text.copy() # Make the text that is visible by striping out formatting rules for i, line in enumerate(visible_text): if line.strip().endswith(ConstrainedText.k_tag_ro): visible_text[i] = "%s\n" % line.split( ConstrainedText.k_tag_ro)[0] elif line.strip().endswith(ConstrainedText.k_tag_rw): visible_text[i] = "%s\n" % line.split( ConstrainedText.k_tag_rw)[0] elif line.strip().endswith(ConstrainedText.k_tag_map): visible_text[i] = "%s\n" % line.split( ConstrainedText.k_tag_map)[0] else: visible_text[i] = "%s\n" % visible_text[i] visible_text[-1] = visible_text[-1].split('\n')[0] self.setText("".join(visible_text)) # Connect to cursor position changed signal self.cursorPositionChanged.connect(self._alter_read_write) # Differentiate read only versus read-write lines of text by color current_cursor = self.textCursor() current_cursor.beginEditBlock() position = 0 for i, line in enumerate(raw_text): if line.strip().endswith( ConstrainedText.k_tag_rw): # Writeable line of text cursor = QTextCursor(self.document()) cursor.setPosition(position) cursor.setPosition(position + len(visible_text[i]), QTextCursor.KeepAnchor) char_format = self.currentCharFormat() char_format.setForeground(QColor("black")) cursor.setCharFormat(char_format) self.document().findBlockByLineNumber(i).setUserState( 0 ) # Add flag to denote that this line number is read-write elif line.strip().endswith( ConstrainedText.k_tag_map): # Mapped input line of text cursor = QTextCursor(self.document()) cursor.setPosition(position) cursor.setPosition(position + len(visible_text[i]), QTextCursor.KeepAnchor) char_format = self.currentCharFormat() char_format.setForeground(QColor("blue")) cursor.setCharFormat(char_format) self.document().findBlockByLineNumber(i).setUserState( 1) # Add flag to denote that this line number is read only elif line.strip().endswith( ConstrainedText.k_tag_ro): # Read only line of text cursor = QTextCursor(self.document()) cursor.setPosition(position) cursor.setPosition(position + len(visible_text[i]), QTextCursor.KeepAnchor) char_format = self.currentCharFormat() char_format.setForeground(QColor("black")) cursor.setCharFormat(char_format) self.document().findBlockByLineNumber(i).setUserState( 1) # Add flag to denote that this line number is read only else: # Writeable line of text cursor = QTextCursor(self.document()) cursor.setPosition(position) cursor.setPosition(position + len(visible_text[i]), QTextCursor.KeepAnchor) char_format = self.currentCharFormat() char_format.setForeground(QColor("black")) cursor.setCharFormat(char_format) self.document().findBlockByLineNumber(i).setUserState( 0) # Add flag to denote that this line number is writeable position += len(visible_text[i]) current_cursor.endEditBlock() self.document().clearUndoRedoStacks()