|  | Home · Examples

# Text Finder Example

The Text Finder example demonstrates how to dynamically process forms using the QtUiTools module. Dynamic form processing enables a form to be processed at run-time only by changing the .ui file for the project. The program allows the user to look up a particular word within the contents of a text file. This text file is included in the project's resource and is loaded into the display at startup.

## Setting Up The Resource File

The resources required for Text Finder are:

- textfinder.ui - the user interface file created in QtDesigner
- input.txt - a text file containing some text to be displayed in the QTextEdit

textfinder.ui contains all the necessary QWidget objects for the Text Finder. A QLineEdit is used for the user input, a QTextEdit is used to display the contents of input.txt, a QLabel is used to display the text "Keyword", and a QPushButton is used for the "Find" button. The screenshot below shows the preview obtained in QtDesigner.
A textfinder.qrc file is used to store both the textfinder.ui and input.txt in the application's executable. The file contains the following code:

    <!DOCTYPE RCC><RCC version="1.0">
    <qresource>
        <file>forms/textfinder.ui</file>
        <file>forms/input.txt</file>
    </qresource>
    </RCC>

For more information on resource files, see The Qt Resource System.

To generate a form at run-time, the example is linked against the QtUiTools module library. This is done in the `textfinder.pro` file that contains the following lines:

    CONFIG      += uitools
    HEADERS     = textfinder.h
    RESOURCES   = textfinder.qrc
    SOURCES     = textfinder.cpp main.cpp

## TextFinder Class Definition

The `TextFinder` class is a subclass of QWidget and it hosts the QWidgets we need to access in the user interface. The QLabel in the user interface is not declared here as we do not need to access it.

    class TextFinder : public QWidget
    {
        Q_OBJECT

    public:
        TextFinder(QWidget *parent = 0);

    private slots:
        void on_findButton_clicked();

    private:
        QWidget* loadUiFile();
        void loadTextFile();

        QPushButton *ui_findButton;
        QTextEdit *ui_textEdit;
        QLineEdit *ui_lineEdit;
        bool isFirstTime;
    };

The slot `on_find_Button_clicked()` is a slot named according to the Automatic Connection naming convention required by `uic`.

## TextFinder Class Implementation

The `TextFinder` class's constructor calls the `loadUiFile()` function and then uses `qFindChild()` to access the user interface's QWidgets.
The following code example is written in c++.

    TextFinder::TextFinder(QWidget *parent)
        : QWidget(parent)
    {
        QWidget *formWidget = loadUiFile();

        ui_findButton = qFindChild<QPushButton*>(this, "findButton");
        ui_textEdit = qFindChild<QTextEdit*>(this, "textEdit");
        ui_lineEdit = qFindChild<QLineEdit*>(this, "lineEdit");

We then use QMetaObject's system to enable signal and slot connections.
The following code example is written in c++.

        QMetaObject::connectSlotsByName(this);

The loadTextFile() function is called to load input.txt into QTextEdit to displays its contents. The `TextFinder`'s layout and window title is set and `isFirstTime` is set to true.
The following code example is written in c++.

        loadTextFile();

        QVBoxLayout *layout = new QVBoxLayout;
        layout->addWidget(formWidget);
        setLayout(layout);
        setWindowTitle(tr("Text Finder"));

        isFirstTime = true;
    }

`isFirstTime` is used as a flag to indicate whether the search operation has been performed more than once. This is further explained with the `on_findButton_clicked()` function.

The `loadUiFile()` function is used to load the user interface file previously created in QtDesigner. The QUiLoader class is instantiated and its `load()` function is used to load the form into `formWidget` that acts as a place holder for the user interface. The function then returns `formWidget` to its caller.
The following code example is written in c++.

    QWidget* TextFinder::loadUiFile()
    {
        QUiLoader loader;

        QFile file(":/forms/textfinder.ui");
        file.open(QFile::ReadOnly);

        QWidget *formWidget = loader.load(&file, this);
        file.close();

        return formWidget;
    }

As mentioned earlier, the loadTextFile() function loads input.txt into QTextEdit to display its contents. Data is read using QTextStream into a QString object, `line` with the QTextStream::readAll() function. The contents of `line` are then appended to `ui_textEdit`.
The following code example is written in c++.

    void TextFinder::loadTextFile()
    {
        QFile inputFile(":/forms/input.txt");
        inputFile.open(QIODevice::ReadOnly);
        QTextStream in(&inputFile);
        QString line = in.readAll();
        inputFile.close();

        ui_textEdit->append(line);
        ui_textEdit->setUndoRedoEnabled(false);
        ui_textEdit->setUndoRedoEnabled(true);
    }

The `on_findButton_clicked()` function is a slot that is connected to `ui_findButton`'s `clicked()` signal. The `searchString` is extracted from the `ui_lineEdit` and the `document` is extracted from `textEdit`. In event there is an empty `searchString`, a QMessageBox is used, requesting the user to enter a word. Otherwise, we traverse through the words in `ui_textEdit`, and highlight all ocurrences of the `searchString` . Two QTextCursor objects are used: One to traverse through the words in `line` and another to keep track of the edit blocks.
The following code example is written in c++.

    void TextFinder::on_findButton_clicked()
    {
        QString searchString = ui_lineEdit->text();
        QTextDocument *document = ui_textEdit->document();

        bool found = false;

        if (isFirstTime == false)
            document->undo();

        if (searchString == "") {
            QMessageBox::information(this, tr("Empty Search Field"),
                    "The search field is empty. Please enter a word and click Find.");
        } else {

            QTextCursor highlightCursor(document);
            QTextCursor cursor(document);

            cursor.beginEditBlock();

            QTextCharFormat plainFormat(highlightCursor.charFormat());
            QTextCharFormat colorFormat = plainFormat;
            colorFormat.setForeground(Qt::red);

            while (!highlightCursor.isNull() && !highlightCursor.atEnd()) {
                highlightCursor = document->find(searchString, highlightCursor, QTextDocument::FindWholeWords);

                if (!highlightCursor.isNull()) {
                    found = true;
                    highlightCursor.movePosition(QTextCursor::WordRight,
                                           QTextCursor::KeepAnchor);
                    highlightCursor.mergeCharFormat(colorFormat);
                }
            }

            cursor.endEditBlock();

The `isFirstTime` flag is set to false the moment `findButton` is clicked. This is necessary to undo the previous text highlight before highlighting the user's next search string. Also, the `found` flag is used to indicate if the `searchString` was found within the contents of `ui_textEdit`. If it was not found, a QMessageBox is used to inform the user.
The following code example is written in c++.

            isFirstTime = false;

            if (found == false) {
                QMessageBox::information(this, tr("Word Not Found"),
                    "Sorry, the word cannot be found.");
            }
        }
    }

## main() Function

`main()`
The following code example is written in c++.

    int main(int argc, char *argv[])
    {
        Q_INIT_RESOURCE(textfinder);
        QApplication app(argc, argv);

        TextFinder *textFinder = new TextFinder;
        textFinder->show();

        return app.exec();
    }

The `main()` function initialises the textfinder.qrc resource file and instantiates as well as displays `TextFinder`.

See also:
  Calculator Builder Example, and World Time Clock Builder Example.