Text Finder Example¶
Dynamically loading .ui files using QUiLoader
.
The TextFinder example shows how to load and setup a .ui
file dynamically using the QUiLoader
class that is part of the Qt UI Tools library.
The program allows the user to look up a particular word within the contents of a text. The visual elements and layout of the user interface is loaded at runtime, from a the program resources.
Setting Up The Resource File¶
The resources required for the example are:
textfinder.ui
- the user interface file created in Qt Designer
input.txt
- a text file containing some text to be displayed in aQTextEdit
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. Note that all the widgets have sensible objectName
's assigned. These are used in code to identify them.
The screenshot below shows the preview obtained in Qt Designer .
In this example, we store both resources in the applicaton’s executable by including the textfinder.qrc
file. Alternatively, the files could also be loaded at runtime from the file system, or from an external binary resource .rcc
file. For more information on resource files, see The Qt Resource System .
The textfinder.qrc
file lists all files that should be included as a resource:
<Code snippet "/tmp/snapshot-qt5full-6.2/qt5/qtbase/textfinder/textfinder.qrc" not found>
To generate a form at run-time, the example is linked against the Qt Ui Tools library. This is done in the textfinder.pro
file:
<Code snippet "textfinder/textfinder.pro:0" not found>
TextFinder Class Definition¶
The TextFinder
class contains the main user interface. It declares pointers to the QPushButton
, QTextEdit
and QLineEdit
elements described above. The QLabel
in the user interface is not declared here as we do not need to access it from code.
class TextFinder(QWidget): Q_OBJECT # public TextFinder = explicit(QWidget parent = None) slots: = private() def on_findButton_clicked(): # private ui_findButton = QPushButton() ui_textEdit = QTextEdit() ui_lineEdit = QLineEdit()
The slot on_findButton_clicked()
is a slot named according to the Automatic Connection naming convention required by uic
.
Loading the Resources¶
We use QFile
to load the data from the program resources at runtime. The code for this is in two method methods on top of textfinder.cpp
: loadUiFile
and loadTextFile
.
The loadUiFile
function loads the user interface file previously created in Qt Designer . First, the content of the textfinder.ui
file is loaded from the resource system. Then a QUiLoader
instance is created, and the load()
function is called, with the first argument being the open file, and the second argument being the pointer of the widget that should be set as the parent. The created QWidget
is returned.
loadUiFile = QWidget(QWidget parent) file = QFile(":/forms/textfinder.ui") file.open(QIODevice.ReadOnly) loader = QUiLoader() return loader.load(file, parent)
In a similar vein, the loadTextFile
function loads input.txt
from the resources. Data is read using QTextStream
into a QString
with the readAll()
function. We explicitly set the encoding to UTF-8, because QTextStream
by default uses the current system locale. Finally, the loaded text is returned.
loadTextFile = QString() inputFile = QFile(":/forms/input.txt") inputFile.open(QIODevice.ReadOnly) in = QTextStream(inputFile) return in.readAll()
TextFinder Class Implementation¶
The TextFinder
class’s constructor does not instantiate any child widgets directly. Instead, it calls the loadUiFile()
function, and then uses findChild()
to locate the created QWidget
s by object name.
def __init__(self, parent): QWidget.__init__(self, parent) formWidget = loadUiFile(self) ui_findButton = findChild<QPushButton*>("findButton") ui_textEdit = findChild<QTextEdit*>("textEdit") ui_lineEdit = findChild<QLineEdit*>("lineEdit")
We then use connectSlotsByName()
to enable the automatic calling of the on_findButton_clicked()
slot.
QMetaObject.connectSlotsByName(self)
The loadTextFile
function is called to get the text to be shown in the QTextEdit
.
ui_textEdit.setText(loadTextFile())
The dynamically loaded user interface in formWidget
is now properly set up. We now embed formWidget
through a QVBoxLayout
.
layout = QVBoxLayout() layout.addWidget(formWidget) setLayout(layout)
At the end of the constructor we set a window title.
setWindowTitle(tr("Text Finder"))
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 ui_textEdit
. If 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.
def on_findButton_clicked(self): searchString = ui_lineEdit.text() document = ui_textEdit.document() found = False() # undo previous change (if any) document.undo() if (searchString.isEmpty()) { QMessageBox.information(self, tr("Empty Search Field"), tr("The search field is empty. " "Please enter a word and click Find.")) else: highlightCursor = QTextCursor(document) cursor = QTextCursor(document) cursor.beginEditBlock() plainFormat = QTextCharFormat(highlightCursor.charFormat()) colorFormat = plainFormat colorFormat.setForeground(Qt.red) while (not highlightCursor.isNull() and not highlightCursor.atEnd()) { highlightCursor = document.find(searchString, highlightCursor, QTextDocument.FindWholeWords) if (not highlightCursor.isNull()) { found = True highlightCursor.movePosition(QTextCursor.WordRight, QTextCursor.KeepAnchor) highlightCursor.mergeCharFormat(colorFormat) cursor.endEditBlock()
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.
if (found == False) { QMessageBox.information(self, tr("Word Not Found"), tr("Sorry, the word cannot be found."))
`` main()``
Function¶
The main()
function instantiates and shows TextFinder
.
if __name__ == "__main__": app = QApplication([]) textFinder = TextFinder() textFinder.show() sys.exit(app.exec())
There are various approaches to include forms into applications. Using QUILoader is just one of them. See Using a Designer UI File in Your Application for more information on the other approaches available.
© 2022 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.