Alen
Alen

Reputation: 1788

Qt C++ QTextEdit count words while typing

I need to implement method in Qt C++ which counts number of words in QTextEdit while user type. Also when user type multiple spaces they shouldn't be treated as word. I know how to do this on already typed text, but I need to update total number of words all the time. Can you help me with this.

Upvotes: 5

Views: 5598

Answers (5)

Alen
Alen

Reputation: 1788

This is how I done it, I made combination out of some answers on this thread. As I noticed this solution is really fast and it doesn't waste resources if not needed. It checks only parts of text while you type, only if you paste text then it checks whole text. I would like someone to check this code and post some feedback. This is the code:

size = 0;
counter = 0;
start = 0;
end = 0;
void MainWindow::count()
{
    text = ui->textEdit->toPlainText();
    if(text.length()-size == 1){
    if(text.length() == 1){
    if(!text.at(0).isSpace()){
        counter++;
        ui->label_2->setText(QString::number(counter));
    }}

    if(text.length()>2){
    start = text.length()-1;
    end = text.length();
    if(text.at(start-1).isSpace() && !text.at(end-1).isSpace()){
        counter++;
        ui->label_2->setText(QString::number(counter));
    }}}
    else if(text.length()-size > 1){
    counter = 0;
    if(!text.at(0).isSpace()) counter++;
        for(int i = 1; i<text.length();i++){
            if(!text.at(i).isSpace() && text.at(i-1).isSpace())
                counter++;

        }
        ui->label_2->setText(QString::number(counter));
    }
    size = text.length();
}

Upvotes: 0

Marek R
Marek R

Reputation: 37607

If you want calculate how many words document contains from the beginning the best solution is:

void MainWindow::docChanged()
{
    QTextDocument *doc = qobject_cast<QTextDocument *>(sender());
    QRegExp re("\\b\\w+\\b");
    int wordCount = 0;
    QTextCursor c = doc->find(re);
    while (!c.isNull()) {
        wordCount++;
        c = doc->find(re, c);
    }
    ui->labelWordCount->setText(QString::number(wordCount));
}

Upvotes: 0

Marek R
Marek R

Reputation: 37607

Edit:
I thought I remembered well how QTextDocument works (I thought that contentsChange(int position, int charsRemoved, int charsAdded) is invoked before actual change). I was wrong or maybe behavior in new version has changed. Anyway with current version (5.0.1) I don't see how to calculate correction to word count. It looks like it is impossible. At least I don't see a way to calculate how many words has been removed.

Listen to two signals from a QTextDocument: contentsChange(int position, int charsRemoved, int charsAdded) and contentsChanged(). First is fired before document modification and second after. Use this information: position, charsRemoved, charsAdded; to calculate correction to total count of words.

Upvotes: 0

Amartel
Amartel

Reputation: 4286

I would suggest you to connect on void QTextEdit::textChanged () [signal] and use something like this:

void onTextChanged()
{
    int wordCount = textEdit->toPlainText().split(QRegExp("(\\s|\\n|\\r)+")
                                                  , QString::SkipEmptyParts).count();
}

Upvotes: 1

dtech
dtech

Reputation: 49289

You will pretty much have to parse it yourself, splitting the string with a single splitter is not a good solution for production. Here is a simple parser I've written some time ago that does exactly this:

void processChar(const QChar &ch)
{
    // if char is not separator
    if (!sp[ch.unicode()]){
        if (!isWord){ // set where word begins
            wordBegin = counter;
            isWord = 1;
        }
        ++textChar;
    }
    // if char is separator
    else{
        // if there is a current word
        if (isWord){
            QString tempWord(text.mid(wordBegin, counter - wordBegin));
            tempWord = tempWord.toLower();
            // if word does not exist add with value one
            if (!words.contains(tempWord)){
                words.insert(tempWord, 1);
                ++uniqueWords;
            }
            // if word exists find its value and increment by one
            else {
                words.insert(tempWord, words.value(tempWord) + 1);
            }
            ++totalWords;
            isWord = 0;
        }
        // else skip
        ++sepChar;
    }
}

You call processChar() for each character of your text.

It is pretty fast, it uses a lookup table (char array) sp that includes all characters and marks those that are separators. Here is how you populate the table from a QString of all separators.

void indexChars()
{
    // zero everything
    for (unsigned i = 0; i < 65536; ++i) sp[i] = 0;

    // set every separator index to 1
    for (int i = 0; i < separators.length(); ++i) {
        sp[separators.at(i).unicode()] = 1;
    }
}

The table is quite big because I wanted to be able to process 16 bit characters as well.

How fast is it - despite doing extra work, it is bout 20 times faster than the word count of MS Word, plus it actually creates a list of every unique word in the text plus the number of occurrences, stored in the QMap words. It tracks text characters textChar, separator characters sepChar, total and unique words.

Functionality might be a bit overkill, but you can strip it down if you don't need all the extra stuff. It will get even faster if you remove what you don't need.

You can use those snippets, and either use a timer to periodically count the words if text has changed since last count, which is the better solution than the alternative - to count on every typed character of the text field.

Upvotes: 0

Related Questions