Как отменить при использовании QScintilla setText?

Позвольте мне начать с публикации некоторых небольших вспомогательных функций, которые я буду использовать, чтобы сформулировать свои вопросы:

import textwrap
import sys
from pathlib import Path

from PyQt5.Qsci import QsciScintilla
from PyQt5.Qt import *  # noqa


def set_style(sci):
    # Set default font
    sci.font = QFont()
    sci.font.setFamily('Consolas')
    sci.font.setFixedPitch(True)
    sci.font.setPointSize(8)
    sci.font.setBold(True)
    sci.setFont(sci.font)
    sci.setMarginsFont(sci.font)
    sci.setUtf8(True)

    # Set paper
    sci.setPaper(QColor(39, 40, 34))

    # Set margin defaults
    fontmetrics = QFontMetrics(sci.font)
    sci.setMarginsFont(sci.font)
    sci.setMarginWidth(0, fontmetrics.width("000") + 6)
    sci.setMarginLineNumbers(0, True)
    sci.setMarginsForegroundColor(QColor(128, 128, 128))
    sci.setMarginsBackgroundColor(QColor(39, 40, 34))
    sci.setMarginType(1, sci.SymbolMargin)
    sci.setMarginWidth(1, 12)

    # Set indentation defaults
    sci.setIndentationsUseTabs(False)
    sci.setIndentationWidth(4)
    sci.setBackspaceUnindents(True)
    sci.setIndentationGuides(True)
    sci.setFoldMarginColors(QColor(39, 40, 34), QColor(39, 40, 34))

    # Set caret defaults
    sci.setCaretForegroundColor(QColor(247, 247, 241))
    sci.setCaretWidth(2)

    # Set edge defaults
    sci.setEdgeColumn(80)
    sci.setEdgeColor(QColor(221, 221, 221))
    sci.setEdgeMode(sci.EdgeLine)

    # Set folding defaults (http://www.scintilla.org/ScintillaDoc.html#Folding)
    sci.setFolding(QsciScintilla.CircledFoldStyle)

    # Set wrapping
    sci.setWrapMode(sci.WrapNone)

    # Set selection color defaults
    sci.setSelectionBackgroundColor(QColor(61, 61, 52))
    sci.resetSelectionForegroundColor()

    # Set scrollwidth defaults
    sci.SendScintilla(QsciScintilla.SCI_SETSCROLLWIDTHTRACKING, 1)

    # Current line visible with special background color
    sci.setCaretLineBackgroundColor(QColor(255, 255, 224))

    # Set multiselection defaults
    sci.SendScintilla(QsciScintilla.SCI_SETMULTIPLESELECTION, True)
    sci.SendScintilla(QsciScintilla.SCI_SETMULTIPASTE, 1)
    sci.SendScintilla(QsciScintilla.SCI_SETADDITIONALSELECTIONTYPING, True)


def set_state1(sci):
    sci.clear_selections()
    base = "line{} state1"
    view.setText("\n".join([base.format(i) for i in range(10)]))
    for i in range(0, 10, 2):
        region = (len(base) * i, len(base) * (i + 1) - 1)
        if i == 0:
            view.set_selection(region)
        else:
            view.add_selection(region)


def set_state2(sci):
    base = "line{} state2"
    view.setText("\n".join([base.format(i) for i in range(10)]))
    for i in range(1, 10, 2):
        region = (len(base) * i, len(base) * (i + 1) - 1)
        if i == 1:
            view.set_selection(region)
        else:
            view.add_selection(region)


class Editor(QsciScintilla):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        set_style(self)

    def clear_selections(self):
        sci = self
        sci.SendScintilla(sci.SCI_CLEARSELECTIONS)

    def set_selection(self, r):
        sci = self
        sci.SendScintilla(sci.SCI_SETSELECTION, r[1], r[0])

    def add_selection(self, r):
        sci = self
        sci.SendScintilla(sci.SCI_ADDSELECTION, r[1], r[0])

    def sel(self):
        sci = self
        regions = []

        for i in range(sci.SendScintilla(sci.SCI_GETSELECTIONS)):
            regions.append(
                sci.SendScintilla(sci.SCI_GETSELECTIONNSTART, i),
                sci.SendScintilla(sci.SCI_GETSELECTIONNEND, i)
            )

        return sorted(regions)

Собственно у меня пара вопросов:

Вопрос 1)

if __name__ == '__main__':
    app = QApplication(sys.argv)

    view = Editor()
    set_state1(view)
    view.move(1000, 100)
    view.resize(800, 300)
    view.show()
    app.exec_()

Я получу это (вы можете увидеть вопрос на снимке ниже):

введите здесь описание изображения

Вопрос 2)

if __name__ == '__main__':
    app = QApplication(sys.argv)

    view = Editor()
    set_state1(view)
    set_state2(view)
    view.move(1000, 100)
    view.resize(800, 300)
    view.show()
    app.exec_()

Как я могу изменить код, чтобы я мог восстановить состояние 1 при нажатии ctrl+z?

Прямо сейчас при использовании ctrl+z вы не сможете получить state1:

введите здесь описание изображения

в основном из-за того, как ведет себя setText:

Заменяет весь текущий текст текстом. Обратите внимание, что эта функция очищает историю отмен/повторов.

Я уже пробовал некоторые функции, опубликованные в документах отменить и повторить, но не удачи пока.

Например, одна из моих попыток заключалась в том, чтобы сначала выделить весь текст, а затем использовать replaceSelectedText и, наконец, восстановление выбора из предыдущего состояния вручную, результат был уродливым (я не хочу, чтобы прокрутка редактора испортилась при отмене/повторении)... В принципе, я хотел бы получить то же чувство, что и SublimeText .

Кстати, это небольшой минимальный пример, но в реальном случае я буду накапливать кучу операций без фиксации на scintilla очень часто... поэтому мне интересно выяснить, как откатиться к предыдущему состоянию при использовании невыполнимый setText... Другими словами, я хотел бы избежать использования функций Scintilla, таких как insertAt, replaceSelectedText или подобные... поскольку я использую встроенные функции строки python для внутреннего изменения буфера.

РЕДАКТИРОВАТЬ:

Я почти уверен, что beginUndoAction и endUndoAction не помогут мне ответить на вопрос 2, но... как насчет SCI_ADDUNDOACTION? Хотя документы довольно запутаны... :/


person BPL    schedule 09.02.2019    source источник


Ответы (1)


Вопрос 1. Последняя добавленная выборка автоматически устанавливается как выборка Main. Чтобы удалить его, добавьте строку sci.SendScintilla(sci.SCI_SETMAINSELECTION, -1) в конец функции set_state1.

Вопрос 2:

  • То, как вы описали это, сохраняя выбор, используя replaceSelectedText, а затем используя setCursorPosition / reselecting all selections и setFirstVisibleLine для восстановления положения прокрутки, - это один из способов.
  • Глядя на источник С++ функции setText:
// Set the given text.
void QsciScintilla::setText(const QString &text)
{
    bool ro = ensureRW();

    SendScintilla(SCI_SETTEXT, ScintillaBytesConstData(textAsBytes(text)));
    SendScintilla(SCI_EMPTYUNDOBUFFER);

    setReadOnly(ro);
}

Вы можете попробовать установить текст с помощью sci.SendScintilla(sci.SCI_SETTEXT, b"some text"), который не сбрасывает буфер отмены/повтора.

person Matic Kukovec    schedule 09.02.2019