Russian Qt Forum
Май 02, 2024, 07:04 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1]   Вниз
  Печать  
Автор Тема: QTextEdit + Изменение регистра символов  (Прочитано 10612 раз)
gil9red
Administrator
Джедай : наставник для всех
*****
Offline Offline

Сообщений: 1805



Просмотр профиля WWW
« : Август 12, 2012, 17:32 »

Здраствуйте!
Кому не лень, помогите Улыбающийся
делаю панельку форматирования текста, связанную с textEdit, в качестве основы,
взял пример Text Edit
Захотелось добавить возможность поменять регистр выделенных символов, для этого использую функции:
Код:
QString UTextEdit::makeCaseSensitiveUpper(QString InputString)
{
    return InputString.toUpper();
}
QString UTextEdit::makeCaseSensitiveLower(QString InputString)
{
    return InputString.toLower();
}
QString UTextEdit::makeCaseSensitiveNegative(QString InputString)
{
    for (int i = 0; i < InputString.size(); i++)
    {
        if(InputString[i].isLower())
            InputString[i] = InputString[i].toUpper();
        else
            InputString[i] = InputString[i].toLower();
    }
    return InputString;
}

для изменения выделенных символов делаю следующее:
Код:
enum UTypesOfCaseSensitive
{
    UpperCaseSensitive,
    LowerCaseSensitive,
    NegativeCaseSensitive
};

void UTextEdit::textCaseSensitiveEdit(UTypesOfCaseSensitive typeOfCaseSensitive)
{
    QTextCursor cursor = this->textCursor();

    if(!cursor.selectedText().isEmpty())
    {
        cursor.beginEditBlock();

        if(typeOfCaseSensitive == UpperCaseSensitive)
        {
            QTextDocumentFragment textFragment = cursor.selection();
            QString text          = textFragment.toPlainText();
            QString changeText = makeCaseSensitiveUpper(text);
            this->setText(this->toPlainText().replace(QString(text),
                                                 QString(changeText));

        }else if(typeOfCaseSensitive == LowerCaseSensitive)
        {
            QTextDocumentFragment textFragment = cursor.selection();
            QString text          = textFragment.toPlainText();
            QString changeText = makeCaseSensitiveLower(text);
            this->setText(this->toHtml().replace(QString(text),
                                                 QString(changeText));
        }else if(typeOfCaseSensitive == NegativeCaseSensitive)
        {
            QTextDocumentFragment textFragment = cursor.selection();
            QString text          = textFragment.toPlainText();
            QString changeText = makeCaseSensitiveNegative(text);
            this->setText(this->toHtml().replace(QString(text),
                                                 QString(changeText));
        }

        cursor.endEditBlock();
     }
}

Вот до этого смог додуматься, но есть проблемы:
Код:
this->setText(this->toHtml().replace(QString(text),
                                                 QString(changeText));
    • такой код не годится - будет весело, если в тексте будет что то повторяться
    • при таком коде, если среди выделенных символов есть отличающиеся шрифтом, то замены не будет

    можно замену делать вот так:
    [/list]
    Код:
    this->setText(this->toPlainText().replace(QString(text),
                                                     QString(changeText));
    тогда:
    • после замены, если текст располагался на нескольких строках, все они окажутся на первой
    • форматирование текста (цвет, выделение, подчеркивания) пропадает

    Можно вместо QString & QString::replace ( const QString & before, const QString & after, Qt::CaseSensitivity cs = Qt::CaseSensitive ),
    использовать QString & QString::replace ( int position, int n, const QString & after ),
    тогда проблема в том что указание позиции курсора текста, в качестве параметра не катит,
    ведь мне нужно чтобы была поддержка rich text, а это html текст, а курсор в тексте не подходит для использования в html тексте, т.е. что то вроде этого не прокатит:
    Код:
                QTextDocumentFragment textFragment = cursor.selection();
                QString text          = textFragment.toPlainText();
                QString changeText = makeCaseSensitiveNegative(text);
                this->setText(this->toHtml().replace(cursor.position(),
                                                     text.size(),
                                                     changeText);

    Помогите с написанием кода, который бы заменял выделенный текст в TextEdit, на тот который с измененным регистром, и при этом изменяемый текст сохранял бы свой шрифт:
    до: Hello World! после: HELLO WORLD!
    Спасибо Улыбающийся
    « Последнее редактирование: Август 14, 2012, 13:36 от gil9red » Записан

    Swa
    Самовар
    **
    Offline Offline

    Сообщений: 170


    Просмотр профиля
    « Ответ #1 : Август 13, 2012, 10:14 »

    Во-первых:
    Код:
    QTextDocumentFragment textFragment = cursor.selection();
    QString text          = textFragment.toPlainText();
    - для этого есть метод QString QTextCursor::selectedText () const

    Во-вторых: Зачем это?
    Код:
    this->setText(this->toPlainText().replace(QString(text),QString(changeText));
    Вы уже нашли валидный курсор с выделенным текстом, а затем выполняете поиск по всему документу. Чтобы изменить выделенный текст используйте
    void QTextCursor::insertText ( const QString & text )
    Он заменит выделенный текст на text.
    Записан
    gil9red
    Administrator
    Джедай : наставник для всех
    *****
    Offline Offline

    Сообщений: 1805



    Просмотр профиля WWW
    « Ответ #2 : Август 13, 2012, 10:52 »

    Я выбрал cursor.selection() потому что в документации было написано что это в отличии от selectedText() используется для работы с rich text, потом использовал textFragment.toHTML(), но оно не прокатило, поэтому перешел на toPlainText().

    Сделал с insertText(), оно работает, только не годится оно для rich text - если например использую следующий код:
    Код:
                QString text       = cursor.selectedText();
                QString changeText = makeCaseSensitiveUpper(text);
                cursor.insertText(changeText);
    только есть проблемка с QTextCharFormat: cursor.insertText(), использует тот QTextCharFormat, который находится перед позицией курсора, например:
    до: helloo выделяем "llo", теперь в зависимости от того с какого края выделяли: слева-направо или наоборот будет разный результат:
    слева-направо: llo
    справо-налево: llo

    Ну как то так=)
    Записан

    gil9red
    Administrator
    Джедай : наставник для всех
    *****
    Offline Offline

    Сообщений: 1805



    Просмотр профиля WWW
    « Ответ #3 : Август 13, 2012, 11:11 »

    вот если бы можно было получить QTextCharFormat для каждого символа выделенного текста и скажем сохранить их например в контейнер, то:
    Код:
                QString text       = cursor.selectedText();
                QString changeText = makeCaseSensitiveUpper(text);
                for(int i = 0; i < changeText.size(); i++)
                    cursor.insertText(changeText.data()[i]);
    и в функции insertText(), в качестве второго параметра передавать QTextCharFormat данного символа, то что это сработает показывает код снизу:
    Код:
       
                QTextCharFormat charFormat;
                charFormat.setForeground(Qt::yellow);
                cursor.insertText(changeText.data()[0], charFormat);
    Записан

    Swa
    Самовар
    **
    Offline Offline

    Сообщений: 170


    Просмотр профиля
    « Ответ #4 : Август 13, 2012, 12:09 »

    Вот держите:
    Код:
    QTextCursor cursor = this->textCursor();
    int start = cursor.selectionStart();
    int end = cursor.selectionEnd();
    int cStart = 0;
    int cEnd = 0;
    QTextBlock block = document()->findBlock(start);

    while (true) {
    if (!block.isValid()) {break;}
    if (block.position() > end) {break;}
    QTextBlock::iterator it;
    for(it = block.begin(); !(it.atEnd()); ++it) {
    QTextFragment currentFragment = it.fragment();
    if (!currentFragment.isValid()) {continue;}
    int fs = currentFragment.position();
    int fe = currentFragment.position() + currentFragment.length();

    if (end < fs || start > fe) {continue;}

    QTextCursor temp(document());

    if (start < fs) {
    cStart = fs;
    } else {
    cStart = start;
    }

    if (end < fe) {
    cEnd = end;
    } else {
    cEnd = fe;
    }

    temp.setPosition(cStart);
    temp.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, cEnd - cStart);
    QString text = temp.selectedText();
    text = text.toUpper();
    temp.beginEditBlock();
        temp.insertText(text);
    temp.endEditBlock();
    }
    block = block.next();
    }
    Записан
    gil9red
    Administrator
    Джедай : наставник для всех
    *****
    Offline Offline

    Сообщений: 1805



    Просмотр профиля WWW
    « Ответ #5 : Август 13, 2012, 13:47 »

    даааа...вот так работать с text edit и курсором я пока не умею... Грустный
    Спасибо, Swa за рабочий пример, буду изучать Улыбающийся
    Записан

    gil9red
    Administrator
    Джедай : наставник для всех
    *****
    Offline Offline

    Сообщений: 1805



    Просмотр профиля WWW
    « Ответ #6 : Август 13, 2012, 14:50 »

    Кое что конечно не понятно: Улыбающийся

    Код:
    QTextCursor cursor = this->textCursor();

    int start = cursor.selectionStart();
    int end = cursor.selectionEnd();     
    int cStart = 0;
    int cEnd = 0;
    // получем текстовый блок используя позицию курсора
    QTextBlock block = document()->findBlock(start);

    while (true) {
    if (!block.isValid()) {break;} // если блок не существует
    if (block.position() > end) {break;} // если позиция первого символа в блоке > end (а это может быть??)
    QTextBlock::iterator it;

    // перебираем символы блока с начала до конца
    for(it = block.begin(); !(it.atEnd()); ++it) {
    // текстовый фрагмент - это ведь слова, символы, отделенные друг от друга разделителем (пробел например)?
    QTextFragment currentFragment = it.fragment();
    if (!currentFragment.isValid()) {continue;} // если не существует
    int fs = currentFragment.position(); // нач позиция
    int fe = currentFragment.position() + currentFragment.length(); // конечная

    if (end < fs || start > fe) {continue;} // еще одна проверка

    QTextCursor temp(document()); // а почему получаем именно от документа курсор, а не от text edit?

                          // определяем нач и кон позицию
                          // передвижения курсора по документу
    if (start < fs) {
    cStart = fs;
    } else {
    cStart = start;
    }

    if (end < fe) {
    cEnd = end;
    } else {
    cEnd = fe;
    }

    // курсор будет находиться в позиции cStart
                          temp.setPosition(cStart);
                          // сдвигаем вправо, выделяя пройденные символы
                          temp.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, cEnd - cStart);

    QString text = temp.selectedText();

    text = text.toUpper();

                           //помещаем вставку в этот блок, чтобы можно было использовать после
    //undo и redo
                          temp.beginEditBlock();
        temp.insertText(text);
    temp.endEditBlock();
    }
                // смотрим следующий блок
    block = block.next();
    }
    Записан

    Bepec
    Гость
    « Ответ #7 : Август 13, 2012, 14:52 »

    QTextEdit - это представление. Данные/цвет/позиции курсора/шрифт, всё это хранится в QDocument - модели.
    Записан
    gil9red
    Administrator
    Джедай : наставник для всех
    *****
    Offline Offline

    Сообщений: 1805



    Просмотр профиля WWW
    « Ответ #8 : Август 13, 2012, 15:01 »

    А можно будет облегчить код, не используя блоки текста и фрагменты, а получая курсор от документа, и у выделенных курсором символов поменять регистр, при этом не потеряв форматирования символов (шрифт, выделение, почеркивание, цвет)?
    Записан

    Swa
    Самовар
    **
    Offline Offline

    Сообщений: 170


    Просмотр профиля
    « Ответ #9 : Август 13, 2012, 15:19 »

    Насколько я понимаю, это - минимальный код для этой задачи.
    Тут дело немного сложнее, чем кажется. Если вы повнимательнее почитаете документацию, то узнаете, что формат текста хранится в QTextFragment, то есть фрагмент - это кусок текста с одинаковым форматированием. Формат не хранится для каждого символа. Например, в тексте "раз два три" находятся три фрагмента. Чтобы изменить текст фрагмента, не потеряв его форматирования, нужно установить курсор внутри фрагмента.
    Записан
    gil9red
    Administrator
    Джедай : наставник для всех
    *****
    Offline Offline

    Сообщений: 1805



    Просмотр профиля WWW
    « Ответ #10 : Август 13, 2012, 15:25 »

    Swa, спасибо за такой ясный и понятный ответ Улыбающийся
    все, вопросов нет Улыбающийся
    Записан

    gil9red
    Administrator
    Джедай : наставник для всех
    *****
    Offline Offline

    Сообщений: 1805



    Просмотр профиля WWW
    « Ответ #11 : Август 14, 2012, 13:47 »

    Наткнулся на ошибку Грустный
    для моего случая, изменил добавил немного код:
    Код:
    void UTextEdit::textCaseSensitiveEdit(UTypesOfCaseSensitive typeOfCaseSensitive)
    {
        QTextCursor cursor = this->textCursor();

        int startPosition = cursor.selectionStart();
        int endPosition   = cursor.selectionEnd();
        int cursorStart   = 0;
        int cursorEnd     = 0;

        QTextBlock block = document()->findBlock(startPosition);

        while(true)
        {
            if(!block.isValid())
                break;

            if(block.position() > endPosition)
                break;

            QTextBlock::iterator it;

            for(it = block.begin(); !(it.atEnd()); ++it)
            {
                QTextFragment currentFragment = it.fragment();
                if (!currentFragment.isValid())
                    continue;

                int fragmentLength = currentFragment.length();

                int fragmentStart  = currentFragment.position();
                int fragmentEnd    = fragmentStart + fragmentLength;

                if (endPosition < fragmentStart || startPosition > fragmentEnd)
                    continue;

                QTextCursor temp(document());

                if(startPosition < fragmentStart)
                    cursorStart = fragmentStart;
                else
                    cursorStart = startPosition;

                if(endPosition < fragmentEnd)
                    cursorEnd = endPosition;
                else
                    cursorEnd = fragmentEnd;

                temp.setPosition(cursorStart);
                temp.movePosition(QTextCursor::Right,
                                  QTextCursor::KeepAnchor,
                                  cursorEnd - cursorStart);
                QString text = temp.selectedText();

                if(typeOfCaseSensitive == UpperCaseSensitive)
                {
                    text = makeCaseSensitiveUpper(text);
                }else if(typeOfCaseSensitive == LowerCaseSensitive)
                {
                    text = makeCaseSensitiveLower(text);
                }else if(typeOfCaseSensitive == NegativeCaseSensitive)
                {
                    text = makeCaseSensitiveNegative(text);
                }

                temp.beginEditBlock();
                temp.insertText(text);
                temp.endEditBlock();
            }
            block = block.next();
        }
    }

    Если при выделении текста началом выделения будет первый символ абзаца, то при повторном использовании функции программа падает, посмотрел в отладке, и он указывает на строку с циклом for, и похоже проблема именно в ++it
    Помогите исправить это Улыбающийся
    « Последнее редактирование: Август 14, 2012, 14:07 от gil9red » Записан

    Страниц: [1]   Вверх
      Печать  
     
    Перейти в:  


    Страница сгенерирована за 0.053 секунд. Запросов: 22.