def _cursor_moved(self): tc = QTextCursor(self.Editor.textCursor()) # copy of cursor self.ColNumber.setText( str( tc.positionInBlock() ) ) tb = tc.block() if tb == self.last_text_block : return # still on same line, nothing more to do # Fill in line-number widget, line #s are origin-1 self.LineNumber.setText( str( tb.blockNumber()+1 ) ) # Fill in the image name and folio widgets pn = self.page_model.page_index(tc.position()) if pn is not None : # the page model has info on this position self.ImageFilename.setText(self.page_model.filename(pn)) self.Folio.setText(self.page_model.folio_string(pn)) else: # no image data, or cursor is above page 1 self.ImageFilename.setText('') self.Folio.setText('') # Change the current-line "extra selection" to the new current line. # Note: the cursor member of the extra selection may not have a # selection. If it does, the current line highlight disappears. The # cursor "tc" may or may not have a selection; to make sure, we clone # it and remove any selection from it. cltc = QTextCursor(tc) cltc.setPosition(tc.position(),QTextCursor.MoveAnchor) # Set the cloned cursor into the current line extra selection object. self.current_line_sel.cursor = cltc # Re-assign the list of extra selections to force update of display. self.Editor.setExtraSelections(self.extra_sel_list)
def _cursor_moved(self): tc = QTextCursor(self.Editor.textCursor()) # copy of cursor self.ColNumber.setText(str(tc.positionInBlock())) tb = tc.block() if tb == self.last_text_block: return # still on same line, nothing more to do # Fill in line-number widget, line #s are origin-1 self.LineNumber.setText(str(tb.blockNumber() + 1)) # Fill in the image name and folio widgets pn = self.page_model.page_index(tc.position()) if pn is not None: # the page model has info on this position self.ImageFilename.setText(self.page_model.filename(pn)) self.Folio.setText(self.page_model.folio_string(pn)) else: # no image data, or cursor is above page 1 self.ImageFilename.setText('') self.Folio.setText('') # Change the current-line "extra selection" to the new current line. # Note: the cursor member of the extra selection may not have a # selection. If it does, the current line highlight disappears. The # cursor "tc" may or may not have a selection; to make sure, we clone # it and remove any selection from it. cltc = QTextCursor(tc) cltc.setPosition(tc.position(), QTextCursor.MoveAnchor) # Set the cloned cursor into the current line extra selection object. self.current_line_sel.cursor = cltc # Re-assign the list of extra selections to force update of display. self.Editor.setExtraSelections(self.extra_sel_list)
def unindent(self): cursor = self._neditor.textCursor() start_block = self._neditor.document().findBlock( cursor.selectionStart()) end_block = self._neditor.document().findBlock(cursor.selectionEnd()) position = cursor.position() if position == 0: return if start_block != end_block: # Unindent multiple lines block = start_block stop_block = end_block.next() while block != stop_block: indentation = self.__block_indentation(block) if indentation.endswith(self.text()): cursor = QTextCursor(block) cursor.setPosition(block.position() + len(indentation)) cursor.setPosition(cursor.position() - self.WIDTH, QTextCursor.KeepAnchor) cursor.removeSelectedText() block = block.next() else: # Unindent one line indentation = self.__block_indentation(start_block) cursor = QTextCursor(start_block) cursor.setPosition(start_block.position() + len(indentation)) cursor.setPosition(cursor.position() - self.WIDTH, QTextCursor.KeepAnchor) cursor.removeSelectedText()
def unindent(self): cursor = self._neditor.textCursor() start_block = self._neditor.document().findBlock( cursor.selectionStart()) end_block = self._neditor.document().findBlock( cursor.selectionEnd()) position = cursor.position() if position == 0: return if start_block != end_block: # Unindent multiple lines block = start_block stop_block = end_block.next() while block != stop_block: indentation = self.__block_indentation(block) if indentation.endswith(self.text()): cursor = QTextCursor(block) cursor.setPosition(block.position() + len(indentation)) cursor.setPosition(cursor.position() - self.WIDTH, QTextCursor.KeepAnchor) cursor.removeSelectedText() block = block.next() else: # Unindent one line indentation = self.__block_indentation(start_block) cursor = QTextCursor(start_block) cursor.setPosition(start_block.position() + len(indentation)) cursor.setPosition(cursor.position() - self.WIDTH, QTextCursor.KeepAnchor) cursor.removeSelectedText()
def insert_snippet(text, cursor, variables): """Inserts a normal text snippet. After the insert, the cursor points to the end of the inserted snippet. If this function returns a cursor it must be set as the cursor for the view after the snippet has been inserted. """ exp_base = expand.Expander(cursor) evs = [] # make a list of events, either text or a constant for text, key in snippets.expand(text): if text: evs.append(text) if key == '$': evs.append('$') elif key: # basic variables func = getattr(exp_base, key, None) if func: evs.append(func()) selectionUsed = expand.SELECTION in evs # do the padding if 'selection: strip;' is used if selectionUsed and 'strip' in variables.get('selection', ''): space = '\n' if '\n' in cursor.selection().toPlainText() else ' ' # change whitespace in previous and next piece of text i = evs.index(expand.SELECTION) for j in range(i-1, -i, -1): if evs[j] not in expand.constants: evs[j] = evs[j].rstrip() + space break for j in range(i+1, len(evs)): if evs[j] not in expand.constants: evs[j] = space + evs[j].lstrip() break # now insert the text ins = QTextCursor(cursor) selectionUsed and ins.setPosition(cursor.selectionStart()) a, c = -1, -1 for e in evs: if e == expand.ANCHOR: a = ins.position() elif e == expand.CURSOR: c = ins.position() elif e == expand.SELECTION: ins.setPosition(cursor.selectionEnd()) else: ins.insertText(e) cursor.setPosition(ins.position()) # return a new cursor if requested if (a, c) != (-1, -1): new = QTextCursor(cursor) if a != -1: new.setPosition(a) if c != -1: new.setPosition(c, QTextCursor.KeepAnchor if a != -1 else QTextCursor.MoveAnchor) return new
def comentar_linha(self): """ comenta a linha :return: None """ editor = self.widget_abas.widget(self.widget_abas.currentIndex()) caminho = editor.get_caminho() # Testa se a aba eh a de boas vindas if caminho == 0: return cursor_atual = editor.textCursor() posicao = cursor_atual.position() bloco_atual = cursor_atual.block() cursor = QTextCursor(bloco_atual) editor.setTextCursor(cursor) texto = bloco_atual.text() if texto.strip().startswith("//"): cursor = self.selecionar_texto(cursor, '/', cursor.position(), 2) cursor.removeSelectedText() cursor.setPosition(posicao - 2) editor.setTextCursor(cursor) else: editor.insertPlainText('//') cursor.setPosition(posicao + 2) editor.setTextCursor(cursor)
def actionTriggered(self, name): # convert arpeggio_normal to arpeggioNormal, etc. name = _arpeggioTypes[name] cursor = self.mainwindow().textCursor() # which arpeggio type is last used? lastused = '\\arpeggioNormal' types = set(_arpeggioTypes.values()) block = cursor.block() while block.isValid(): s = types.intersection(tokeniter.tokens(block)) if s: lastused = s.pop() break block = block.previous() # where to insert c = lydocument.cursor(cursor) c.select_end_of_block() with cursortools.compress_undo(cursor): for item in ly.rhythm.music_items(c, partial=ly.document.OUTSIDE): c = QTextCursor(cursor.document()) c.setPosition(item.end) c.insertText('\\arpeggio') if name != lastused: cursortools.strip_indent(c) indent = c.block().text()[:c.position() - c.block().position()] c.insertText(name + '\n' + indent) # just pick the first place return
def actionTriggered(self, name): # convert arpeggio_normal to arpeggioNormal, etc. name = _arpeggioTypes[name] cursor = self.mainwindow().textCursor() # which arpeggio type is last used? lastused = '\\arpeggioNormal' types = set(_arpeggioTypes.values()) block = cursor.block() while block.isValid(): s = types.intersection(tokeniter.tokens(block)) if s: lastused = s.pop() break block = block.previous() # where to insert c = lydocument.cursor(cursor) c.select_end_of_block() with cursortools.compress_undo(cursor): for item in ly.rhythm.music_items(c, partial=ly.document.OUTSIDE): c = QTextCursor(cursor.document()) c.setPosition(item.end) c.insertText('\\arpeggio') if name != lastused: cursortools.strip_indent(c) indent = c.block().text()[:c.position()-c.block().position()] c.insertText(name + '\n' + indent) # just pick the first place return
def _append_log(self, record): """Internal method to insert a displayed record into the underlying :class:`QPlainTextEdit`.""" self.infobar.add_record(record) self._rec_lines.append(record.text.count('\n') + 1) # NOTE: For some reason, we must use `setPosition` in order to # guarantee a absolute, fixed selection (at least on linux). It seems # almost if `movePosition(End)` will be re-evaluated at any time the # cursor/selection is used and therefore always point to the end of # the document. cursor = QTextCursor(self.textctrl.document()) cursor.movePosition(QTextCursor.End) pos0 = cursor.position() cursor.insertText(record.text + '\n') pos1 = cursor.position() cursor = QTextCursor(self.textctrl.document()) cursor.setPosition(pos0) cursor.setPosition(pos1, QTextCursor.KeepAnchor) selection = QTextEdit.ExtraSelection() selection.format = self.formats.get(record.domain, self.default_format) selection.cursor = cursor selections = self.textctrl.extraSelections() if selections: # Force the previous selection to end at the current block. # Without this, all previous selections are be updated to span # over the rest of the document, which dramatically impacts # performance because it means that all selections need to be # considered even if showing only the end of the document. selections[-1].cursor.setPosition(pos0, QTextCursor.KeepAnchor) selections.append(selection) self.textctrl.setExtraSelections(selections[-self.maxlen:]) self.textctrl.ensureCursorVisible() if self.maxlen: # setMaximumBlockCount() must *not* be in effect while inserting # the text, because it will mess with the cursor positions and # make it nearly impossible to create a proper ExtraSelection! num_lines = sum(self._rec_lines) self.textctrl.setMaximumBlockCount(num_lines + 1) self.textctrl.setMaximumBlockCount(0)
def change_cursor_position(self, value): # изменение положения курсора в текстовом поле if pd.notnull(value): # если указано перемещение курсора place = QTextCursor(self.text.textCursor()) # запоминаем копию текущего курсора place.setPosition(place.position() + int(value)) # изменяем позицию курсора на value self.text.setTextCursor(place) # устанавливаем новый курсор if value < 0: # запоминаем переход внутрь скобок self.inside = -value else: self.inside = 0
def keep_selection(cursor, edit=None): """Performs operations inside the selection and restore the selection afterwards. If edit is given, call setTextCursor(cursor) on the Q(Plain)TextEdit afterwards. """ start, end, pos = cursor.selectionStart(), cursor.selectionEnd(), cursor.position() cur2 = QTextCursor(cursor) cur2.setPosition(end) try: yield finally: if pos == start: cursor.setPosition(cur2.position()) cursor.setPosition(start, QTextCursor.KeepAnchor) else: cursor.setPosition(start) cursor.setPosition(cur2.position(), QTextCursor.KeepAnchor) if edit: edit.setTextCursor(cursor)
def start_complete_process(self, event: QtGui.QKeyEvent, tc: QtGui.QTextCursor): if (event.text().isalnum() and not self.document().characterAt(tc.position() + 1).isalnum()): tc.movePosition(QtGui.QTextCursor.Left, QtGui.QTextCursor.MoveAnchor, 1) tc.select(QtGui.QTextCursor.WordUnderCursor) if len(tc.selectedText()) <= 1: return self.completer.setCompletionPrefix(tc.selectedText()) popup = self.completer.popup() popup.setCurrentIndex(self.completer.completionModel().index(0, 0)) cr = self.cursorRect() cr.setWidth( self.completer.popup().sizeHintForColumn(0) + self.completer.popup().verticalScrollBar().sizeHint().width()) self.completer.complete(cr) else: self.completer.popup().hide()
def read_pages(self, section, value, version): valid_rule = {C.FolioRuleAdd1,C.FolioRuleSet,C.FolioRuleSkip} valid_fmt = {C.FolioFormatArabic,C.FolioFormatLCRom,C.FolioFormatUCRom,C.FolioFormatSame} last_fmt = C.FolioFormatArabic last_pos = -1 # ensure monotonically increasing positions self.clear() if not isinstance(value, list) : pagedata_logger.error('{} metadata must be a list of lists, ignoring it'.format(C.MD_PT)) return for row in value: try: # throws exception if not exactly 6 items [P, fn, pfrs, rule, fmt, nbr] = row P = int(P) # exception if not valid numeric tc = QTextCursor(self.document) tc.setPosition(P) # no effect if P negative or too big if (tc.position() != P) or (P < last_pos) : raise ValueError("Invalid document position") last_pos = P rule = int(rule) # exceptions if not numeric fmt = int(fmt) nbr = int(nbr) if not ( (rule in valid_rule) and (fmt in valid_fmt) and (nbr >= 0) ) : raise ValueError("Invalid folio info") # All looks good, do permanent things self.cursor_list.append(tc) self.filename_list.append(fn) self.folio_list.append( [rule, fmt, nbr] ) if fmt != C.FolioFormatSame : self.explicit_formats.add(len(self.folio_list)-1) # get list of proofer strings, dropping opening null string # due to leading backslash. If it is only '\\' the result # is the list ['']. plist = pfrs.split('\\')[1:] self.proofers_list.append(plist) except Exception as thing: pagedata_logger.error('Invalid row of page metadata: '+thing.args[0]) pagedata_logger.error(' ignoring {}'.format(row)) if 0 < len(self.filename_list) : self._active = True self._add_stopper() self.PagesUpdated.emit()
def insert_snippet(self): cursor = self._editor.word_under_cursor() prefix = cursor.selectedText() # pos = cursor.position() # cursor.movePosition(QTextCursor.StartOfWord) # start_pos = cursor.position() # cursor.setPosition(pos, QTextCursor.KeepAnchor) copy = QTextCursor(cursor) copy.movePosition(QTextCursor.StartOfWord) start = copy.position() self._current_snippet = self.snippets.get(prefix) if self._current_snippet is not None: self.active = True cursor.removeSelectedText() cursor.insertText(self._current_snippet.body) self._highlight(start) else: self.active = False
def _cursor_moved(self): tc = QTextCursor(self.Editor.textCursor()) # copy of cursor self.ColNumber.setText( str( tc.positionInBlock() ) ) tb = tc.block() if tb == self.last_text_block : return # still on same line, nothing more to do # Fill in line-number widget, line #s are origin-1 self.LineNumber.setText( str( tb.blockNumber()+1 ) ) # Fill in the image name and folio widgets pn = self.page_model.page_index(tc.position()) if pn is not None : # the page model has info on this position self.ImageFilename.setText(self.page_model.filename(pn)) self.Folio.setText(self.page_model.folio_string(pn)) else: # no image data, or cursor is above page 1 self.ImageFilename.setText('') self.Folio.setText('') # Change the extra selection to the current line. The cursor needs # to have no selection. Yes, we are here because the cursor moved, # but that doesn't mean no-selection; it might have moved because # of a shift-click extending the selection. #xtc.clearSelection() self.current_line_sel.cursor = tc self.Editor.setExtraSelections(self.extra_sel_list)
def read_pages(self, stream, sentinel, vers, parm): valid_rule = {C.FolioRuleAdd1,C.FolioRuleSet,C.FolioRuleSkip} valid_fmt = {C.FolioFormatArabic,C.FolioFormatLCRom,C.FolioFormatUCRom,C.FolioFormatSame} last_fmt = C.FolioFormatArabic last_pos = -1 # ensure monotonically increasing positions for line in metadata.read_to(stream, sentinel): try: # throws exception if not exactly 6 items (P, fn, pfrs, rule, fmt, nbr) = line.split(' ') P = int(P) # throws exception if not valid int string tc = QTextCursor(self.document) tc.setPosition(P) # no effect if P negative or too big if (tc.position() != P) or (P < last_pos) : raise ValueError("Invalid document position") last_pos = P rule = int(rule) fmt = int(fmt) nbr = int(nbr) if not ( (rule in valid_rule) and (fmt in valid_fmt) and (nbr >= 0) ) : raise ValueError("Invalid folio info") # All looks good, do permanent things self.cursor_list.append(tc) self.filename_list.append(fn) self.folio_list.append( [rule, fmt, nbr] ) if fmt != C.FolioFormatSame : self.explicit_formats.add(len(self.folio_list)-1) # get list of proofer strings, dropping opening null string # due to leading backslash. If it is only '\\' the result # is the list ['']. plist = pfrs.replace(C.UNICODE_EN_SPACE,' ').split('\\')[1:] self.proofers_list.append(plist) except Exception as thing: pagedata_logger.error('invalid line of page metadata: '+thing.args[0]) pagedata_logger.error(' ignoring "'+line+'"') if 0 < len(self.filename_list) : self._active = True self._add_stopper()
class ParsingInput(QWidget): edit_complete = pyqtSignal(Task) def __init__(self): super().__init__(flags=Qt.WindowTitleHint | Qt.WindowCloseButtonHint | Qt.WindowMinimizeButtonHint | Qt.WindowMaximizeButtonHint) self.setFocusPolicy(Qt.StrongFocus) self.setMinimumSize(28, 28) self.setCursor(Qt.IBeamCursor) self.text = [] self.character_offsets = [5] self.parsed_blocks = [] self.qt_text = QTextDocument("") self.cursor = QTextCursor(self.qt_text) self.cursor.setPosition(0) self.cursor_visible = True self.cursor_timer = QTimer() self.cursor_timer.timeout.connect(self.blink_cursor) self.set_cursor_visible() def paintEvent(self, e): qp = QPainter() qp.begin(self) qp.setRenderHint(QPainter.Antialiasing) qp.setRenderHint(QPainter.HighQualityAntialiasing) self.draw(qp) qp.end() def draw(self, qp): size = self.size() w = size.width() h = size.height() font = QFont('Sans-Serif', 12, QFont.Normal) qp.setFont(font) qp.setPen(QColor(128, 128, 128)) qp.setBrush(QColor(255, 255, 255)) qp.drawRoundedRect(0, 0, w, h, 5, 5) qp.setPen(QColor(0, 0, 0)) font_metrics = qp.fontMetrics() c_start_position = 5 cursor_pixel_position = c_start_position self.character_offsets = [cursor_pixel_position] for i, c in enumerate(self.text): start_of_parsed_block = False end_of_parsed_block = False inside_parsed_block = False for start, end in self.parsed_blocks: if start == i: block_width = 4 for char in self.text[start:end]: block_width += font_metrics.width(char["char"]) qp.setPen(QColor(0, 0, 0)) qp.setBrush(QColor(0, 0, 0)) qp.drawRoundedRect(c_start_position + 2, 4, block_width, 20, 2, 2) start_of_parsed_block = True if end == i: end_of_parsed_block = True if start <= i < end: inside_parsed_block = True if end_of_parsed_block: c_start_position += 4 if start_of_parsed_block: c_start_position += 4 c_width = font_metrics.width(c["char"]) if self.cursor.hasSelection() and self.cursor.selectionStart( ) <= i < self.cursor.selectionEnd(): if start_of_parsed_block or end_of_parsed_block: sel_rect_start = c_start_position - 2 sel_rect_width = font_metrics.width(c["char"]) + 4 else: sel_rect_start = c_start_position sel_rect_width = font_metrics.width(c["char"]) if i != self.cursor.selectionStart(): sel_rect_start += 2 if i + 1 != self.cursor.selectionEnd(): sel_rect_width += 2 else: sel_rect_width -= 2 if not inside_parsed_block: selection_color = QColor(100, 100, 255) else: selection_color = QColor(60, 60, 168) qp.setBrush(selection_color) qp.setPen(selection_color) qp.drawRect(sel_rect_start, 6, sel_rect_width, 16) qp.setPen( QColor(inside_parsed_block * 255, inside_parsed_block * 255, inside_parsed_block * 255)) qp.setBrush( QColor(255 - inside_parsed_block * 255, 255 - inside_parsed_block * 255, 255 - inside_parsed_block * 255)) qp.drawText(c_start_position, 20, c["char"]) c_start_position += c_width if i == self.cursor.position() - 1: cursor_pixel_position = c_start_position self.character_offsets.append(c_start_position) if self.hasFocus() and self.cursor_visible: qp.setPen(QColor(0, 0, 0)) for start, end in self.parsed_blocks: if start < self.cursor.position() <= end: qp.setPen(QColor(255, 255, 255)) qp.drawLine(cursor_pixel_position, 4, cursor_pixel_position, 28 - 4) def keyPressEvent(self, event): key = event.key() if key == Qt.Key_Left: if event.modifiers() & Qt.CTRL: new_pos = max( 0, self.get_text_str().rfind( " ", 0, max(0, self.cursor.position() - 1))) else: new_pos = self.cursor.position() - 1 if event.modifiers() & Qt.SHIFT: if self.cursor.hasSelection(): self.cursor.setPosition(max(0, new_pos), mode=QTextCursor.KeepAnchor) else: self.cursor.setPosition(self.cursor.position(), mode=QTextCursor.MoveAnchor) self.cursor.setPosition(max(0, new_pos), mode=QTextCursor.KeepAnchor) else: self.cursor.setPosition(max(0, new_pos)) self.set_cursor_visible() self.update() elif key == Qt.Key_Right: if event.modifiers() & Qt.CTRL: next_space = self.get_text_str().find( " ", self.cursor.position() + 1, len(self.text)) if next_space < 0: new_pos = len(self.text) else: new_pos = next_space else: new_pos = self.cursor.position() + 1 if event.modifiers() & Qt.SHIFT: if self.cursor.hasSelection(): self.cursor.setPosition(max(0, new_pos), mode=QTextCursor.KeepAnchor) else: self.cursor.setPosition(self.cursor.position(), mode=QTextCursor.MoveAnchor) self.cursor.setPosition(max(0, new_pos), mode=QTextCursor.KeepAnchor) else: self.cursor.setPosition(min(len(self.text), new_pos)) self.set_cursor_visible() self.update() elif key == Qt.Key_Home: if event.modifiers() & Qt.SHIFT: if self.cursor.hasSelection(): self.cursor.setPosition(0, mode=QTextCursor.KeepAnchor) else: self.cursor.setPosition(self.cursor.position(), mode=QTextCursor.MoveAnchor) self.cursor.setPosition(0, mode=QTextCursor.KeepAnchor) else: self.cursor.setPosition(0) self.set_cursor_visible() self.update() elif key == Qt.Key_End: if event.modifiers() & Qt.SHIFT: if self.cursor.hasSelection(): self.cursor.setPosition(len(self.text), mode=QTextCursor.KeepAnchor) else: self.cursor.setPosition(self.cursor.position(), mode=QTextCursor.MoveAnchor) self.cursor.setPosition(len(self.text), mode=QTextCursor.KeepAnchor) else: self.cursor.setPosition(len(self.text)) self.set_cursor_visible() self.update() elif key == Qt.Key_Escape: self.cursor.clearSelection() self.set_cursor_visible() self.update() elif key == Qt.Key_Backspace: something_changed = False if self.cursor.hasSelection(): self.delete_selected_text() something_changed = True else: if self.cursor.position() > 0: self.text.pop(self.cursor.position() - 1) self.cursor.setPosition(self.cursor.position() - 1) something_changed = True if something_changed: self.parse_text() self.set_cursor_visible() self.update() elif key == Qt.Key_Delete: something_changed = False if self.cursor.hasSelection(): self.delete_selected_text() something_changed = True else: if self.cursor.position() < len(self.text): self.text.pop(self.cursor.position()) something_changed = True if something_changed: self.parse_text() self.set_cursor_visible() self.update() elif key == Qt.Key_C and event.modifiers() & Qt.CTRL: selected_text = self.get_selected_text() print("Selected Text: {}".format(selected_text)) if len(selected_text) > 0: QApplication.clipboard().setText(selected_text) elif key == Qt.Key_V and event.modifiers() & Qt.CTRL: self.delete_selected_text() clipboard_text = QApplication.clipboard().text() self.text = self.text[:self.cursor.position()] + \ [{"char": c, "parse": True} for c in clipboard_text] + \ self.text[self.cursor.position():] self.parse_text() self.cursor.setPosition(self.cursor.position() + len(clipboard_text)) self.set_cursor_visible() self.update() elif key == Qt.Key_Enter or key == Qt.Key_Return: self.edit_complete.emit(self.get_task()) self.text = [] self.cursor.setPosition(0) self.parse_text() self.set_cursor_visible() self.update() else: if len(event.text()) > 0: typed_text = "" for c in event.text(): if c in ALL_CHARACTERS: typed_text += c if len(typed_text): self.delete_selected_text() self.text = self.text[:self.cursor.position()] + \ [{"char": c, "parse": True} for c in typed_text] + \ self.text[self.cursor.position():] self.parse_text() self.cursor.setPosition(self.cursor.position() + len(typed_text)) self.set_cursor_visible() self.update() def get_selected_text(self): if self.cursor.hasSelection(): return "".join([ c["char"] for c in self.text[self.cursor.selectionStart():self. cursor.selectionEnd()] ]) else: return "" def delete_selected_text(self): if self.cursor.hasSelection(): start_pos = self.cursor.selectionStart() self.text = self.text[:start_pos] + self.text[self.cursor. selectionEnd():] self.cursor.clearSelection() self.cursor.setPosition(start_pos) def get_min_dist_pos(self, click_x): min_dist_pos = 0 min_dist = abs(self.character_offsets[0] - click_x) for i, offset in enumerate(self.character_offsets[1:]): d = abs(offset - click_x) if d < min_dist: min_dist = d min_dist_pos = i + 1 return min_dist_pos def mousePressEvent(self, event): self.cursor.select(QTextCursor.BlockUnderCursor) self.cursor.setPosition(self.get_min_dist_pos(event.pos().x()), mode=QTextCursor.MoveAnchor) self.update() def mouseMoveEvent(self, event): self.cursor.setPosition(self.get_min_dist_pos(event.pos().x()), mode=QTextCursor.KeepAnchor) self.update() def mouseReleaseEvent(self, event): self.cursor.setPosition(self.get_min_dist_pos(event.pos().x()), mode=QTextCursor.KeepAnchor) if self.cursor.selectionStart() == self.cursor.selectionEnd(): self.cursor.clearSelection() self.update() def blink_cursor(self): self.cursor_visible = not self.cursor_visible self.update() def set_cursor_visible(self): self.cursor_visible = True self.cursor_timer.stop() self.cursor_timer.start(500) def get_text_str(self): return "".join([c["char"] for c in self.text]) @staticmethod def generate_date(day, hour=None, minute=None): fuzziness = timedelta(days=1) date = datetime.now() if day in ["Morgen", "Tomorrow"]: date = datetime.now() + timedelta(days=1) try: if hour is not None and 0 <= int(hour) <= 24: if minute is not None and 0 <= int(minute) < 60: date = datetime(year=date.year, month=date.month, day=date.day, hour=int(hour), minute=int(minute)) else: date = datetime(year=date.year, month=date.month, day=date.day, hour=int(hour), minute=0) fuzziness = 0 except ValueError: print("Unexpected Values for hour ({}) and minute ({}). Ignoring.". format(hour, minute)) return date, fuzziness def parse_text(self): regexes = [ (re.compile(r"(Morgen|Tomorrow) (?:at |um )?(\d\d?):(\d\d?)"), self.generate_date), (re.compile(r"(Morgen|Tomorrow)"), self.generate_date) ] text = self.get_text_str() saved_cursor_pos = self.cursor.position() self.qt_text.setPlainText(text) self.cursor.setPosition(saved_cursor_pos) self.parsed_blocks = [] for regex, generator in regexes: matches = regex.finditer(text) for match in matches: if match.end() > 0 and self.text[match.end() - 1]["parse"]: print(match.groups()) print(generator(*match.groups())) text = text[:match.start()] + " " * ( match.end() - match.start()) + text[match.end():] self.parsed_blocks.append((match.start(), match.end())) def get_task(self): new_task = Task() new_task.text = self.get_text_str() return new_task
def character_at(cursor: QtGui.QTextCursor) -> str: return cursor.document().characterAt(cursor.position())
def move_to_next_word(self, doc: QTextDocument, cursor: QTextCursor): """Moves cursor to next word""" while not cursor.isNull() and not cursor.atEnd(): if doc.characterAt(cursor.position()) not in self.ignored_chars: return cursor.movePosition(QTextCursor.NextCharacter)
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 insert_snippet(text, cursor, variables): """Inserts a normal text snippet. After the insert, the cursor points to the end of the inserted snippet. If this function returns a cursor it must be set as the cursor for the view after the snippet has been inserted. """ exp_base = expand.Expander(cursor) evs = [] # make a list of events, either text or a constant for text, key in snippets.expand(text): if text: evs.append(text) if key == '$': evs.append('$') elif key: # basic variables func = getattr(exp_base, key, None) if func: evs.append(func()) selectionUsed = expand.SELECTION in evs # do the padding if 'selection: strip;' is used if selectionUsed and 'strip' in variables.get('selection', ''): space = '\n' if '\n' in cursor.selection().toPlainText() else ' ' # change whitespace in previous and next piece of text i = evs.index(expand.SELECTION) for j in range(i - 1, -i, -1): if evs[j] not in expand.constants: evs[j] = evs[j].rstrip() + space break for j in range(i + 1, len(evs)): if evs[j] not in expand.constants: evs[j] = space + evs[j].lstrip() break # now insert the text ins = QTextCursor(cursor) selectionUsed and ins.setPosition(cursor.selectionStart()) a, c = -1, -1 for e in evs: if e == expand.ANCHOR: a = ins.position() elif e == expand.CURSOR: c = ins.position() elif e == expand.SELECTION: ins.setPosition(cursor.selectionEnd()) else: ins.insertText(e) cursor.setPosition(ins.position()) # return a new cursor if requested if (a, c) != (-1, -1): new = QTextCursor(cursor) if a != -1: new.setPosition(a) if c != -1: new.setPosition( c, QTextCursor.KeepAnchor if a != -1 else QTextCursor.MoveAnchor) return new