Почему происходит ошибка переполнения стека

Преподаватель задал задание: Печатаешь в QPlainText какой либо текст, и он должен при обнаружение определенной группы символов, заменять на определенную фразу, например: @#RUB@ заменять на "рубли" и т.д.. Мной было принято решение, отслеживать событие изменения текста в QPlainText, то есть на каждый ввод нового символа, я буду брать весь текст из QPlainTextи прогонять через замену:

void MainWindow::on_plainTextEdit_textChanged()
{
    //ui->plainTextEdit->blockSignals(true);

    ui->plainTextEdit->setPlainText
    (
       ui->plainTextEdit->toPlainText()
           .replace("#@RUB@", "₽", Qt::CaseInsensitive).replace("#@РУБ@", "₽", Qt::CaseInsensitive)
           .replace("#@AUTH@", "©", Qt::CaseInsensitive).replace("#@АВТП@", "©", Qt::CaseInsensitive)
           .replace("#@PM@", "‰", Qt::CaseInsensitive).replace("#@ПМ@", "‰", Qt::CaseInsensitive)
           .replace("#@GS@", "®", Qt::CaseInsensitive).replace("#@ЗК@", "®", Qt::CaseInsensitive)
           .replace("#@EURO@", "€", Qt::CaseInsensitive).replace("#@ЕВРО@", "€", Qt::CaseInsensitive)
    );

    //ui->plainTextEdit->blockSignals(false);
}

Вопрос, почему крашит с событием переполнения стека? Я делал по методичке и в ней, при изменении текста, в событии textChanged()якобы не крашит, по крайней мере пример брал от туда, вот кусок кода:

void MainWindow::on_plainTextEdit_textChanged()
    {
       QString str = ui->plainTextEdit->toPlainText(); // получаем текст виджета
       if (parseText->change(str))                     // были ли примеры
       {
           ui->plainTextEdit->setPlainText(parseText->getText()); // заменяем текст примеры с результатами
       }
    }

Отключение сигнала в момент изменения содержимого QPlainText помогает, но тогда возникает проблема с позицией курсора - он постоянно прыгает в начало, при вводе нового символа. Где мои ошибки, пожалуйста объясните. Версия QT 5.15.


Ответы (1 шт):

Автор решения: Sergey Tatarincev

Вы своими руками загоняете в рекурсию

[signal]void QPlainTextEdit::textChanged() This signal is emitted whenever the document's content changes; for example, when text is inserted or deleted, or when formatting is applied.

Т.е. после замены текста (setPlainText()) сигнал вызовется по новой. Текст опять заменится (хоть и на тот же самый), снова вызовется сигнал. и т.д.

По решению проблемы. Можете попробовать заменять текст ТОЛЬКО если присутствуют ваши паттерны (пример для одного. остальное допишете). В идеале код надо переписать полность на использование курсоров БЕЗ полной замены текста (на больших текстах это будет работать не оптимально)

void MainWindow::on_plainTextEdit_textChanged()
{
    int cursorPos = ui->plainTextEdit->textCursor().position(); // при замене текста курсор сдвинется в начало, поэтому запоминайте заранее
    bool isEnd = ui->plainTextEdit->textCursor().atEnd(); // то же самое
    QTextCursor cur = ui->plainTextEdit->textCursor();
    QString s = ui->plainTextEdit->toPlainText();

    if(s.indexOf("#@RUB@",Qt::CaseInsensitive)!=-1){
        ui->plainTextEdit->setPlainText(s.replace("#@RUB@", "₽", Qt::CaseInsensitive));
        qApp->processEvents();
        if(isEnd){ // паттерн дописан в конец текста или в середину?
            cur.movePosition(QTextCursor::End); // курсор в конец текста
        }else{
            cur.movePosition(QTextCursor::Start); // перемещаем курсор в начало
            cur.movePosition(QTextCursor::Right,QTextCursor::MoveAnchor,cursorPos-5); // перемещаем вправо на cursorPos-5 (длина паттерна-1)
        }
        ui->plainTextEdit->setTextCursor(cur);
    }
}
→ Ссылка