Программирование на языке C++ в среде Qt Creator

Структура проекта. Основные типы

Показывать лекцию целиком

12.1 Файлы проекта

Теперь давайте рассмотрим из чего состоит проект Qt. В общем, проект Qt имеет такую структуру:

  • файл проекта — описывает файлы, которые входят в проект и содержит необходимые настройки;
  • файлы, входящие в проект (или другие подпроекты, если проект разбит на несколько частей).
  • Ключевую роль имеет файл проекта с расширением .pro. Он содержит списки файлов: исходных кодов, файлов ресурсов, файлов локализации, форм, других файлов, которые входят в проект, а также файлов подпроектов, если проект состоит из нескольких частей. Этот файл также содержит некоторые настройки программы.

    Теперь рассмотрим создание своего проектного файла. Создадим новую папку, где будет размещаться проект (например: custom_project). Создайте файл (это будет файл проекта) введите его имя с расширением .pro (например: custom_project.pro). Наш файл пока что пустой, но его уже можно открыть в Qt Creator (воспользуйтесь главным меню: File->Open File or Project...).

    Создать пустой проект можно с помощью мастера построения проектов. Для этого надо воспользоваться главным меню File->New File or Project... либо комбинацией клавиш Ctrl+Shift+N. В окне мастера нужно выбрать раздел Other Project (Другой проект) и тип проекта — Empty Qt Project.

    После того, как мы открыли проект, Qt Creator предлагает выбрать комплект для его компиляции. В разделе Projects (Проекты) выберем комплект по умолчанию и нажмём Configure Project. В дереве проекта выберем и откроем файл проекта. Теперь настало время исследовать синтаксис проектных файлов Qt.

    Проектный файл обычно содержит несколько настроек в виде специальных переменных, каждая из которых играет свою особую роль. Среди большого количества настроек, которые задают в .pro-файле:

  • тип проекта (приложение, динамическая или статическая библиотека, проект, который состоит из подпроектов);
  • общие настройки проекта;
  • настройки компиляции;
  • путь, где будет размещён исполняемый файл, библиотека или бинарный файл во время процесса компиляции;
  • пути к файлам, библиотекам и другим частям проекта необходимым для компиляции;
  • файлы, входящие в проект;
  • дополнительные действия, которые будут выполняться в процессе компиляции проекта.
  • Откройте проектный файл и добавьте к нему содержимое. Обратите внимание: символ # можно использовать для обозначения комментариев.

    # Указываем тип проекта
    TEMPLATE = app # app - Application, прикладная программа
    # Используемые модули Qt
    QT -= gui # Удаляем из списка модуль gui
    # это означает отказ от использования графического интрефейса,
    # то есть - консольную программу
    CONFIG += console # Конфигурируем создание консольного проекта
    # (нужно только для консольных проектов в Windows, в Linux и Mac OS X не выполняет никаких
    действий)
    CONFIG -= app_bundle # Предотвращает создание Application bundle в Mac OS X
    # (нужно только для консольных проектов в Mac OS X)
    TARGET = custom_project # Название исполняемого файла

    Теперь нам осталось добавить в проект файл с текстом программы. Для этого мы снова можем воспользоваться мастером. В категории Files and Classes (Файлы и классы) выберем раздел C++ и выберем тип файла "C++ Source File" (Файл исходных текстов C++). Поскольку это будет главный файл программы, то дадим ему привычное для этого случая название: main.cpp. Текст программы является обычным:

    int main ( int lArg c, char * lArgv [ ] )
    {
    	return 0;
    }

    После создания main.cpp, вновь откроем файл проекта и обратим внимание на несколько дополнительных строк:

    SOURCES += \
    	main.cpp

    Переменная SOURCES хранит список .cpp файлов. В табл. 12.1 мы предоставляем список переменных, которые часто участвуют в описании проекта:

    Некоторые важные переменные для описания настроек проекта
    Переменная Описание Пример
    CONFIG Разнообразные настройки конфигурации проекта (например: режим отладки, вывод предупреждений, компиляция динамической библиотеки и т.п.).
    CONFIG += dll plugin \
    	warn_on release
    DEFINES Макроопределения в проекте. Работает так же, как директива препроцессора #define.
    DEFINES +=
    DEBUG_OUTPUT \
    	CUSTOM_DEFINE
    DESTDIR Путь к папке, где будет создан исполняемый файл.
    DESTDIR = ./bin
    INCLUDEPATH Путь к папкам с заголовочными файлами
    INCLUDEPATH += ./includes \
    	./my_header_files
    FORMS Файлы форм Qt Designer.
    FORMS += mainwindow.ui
    HEADERS Заголовочные файлы программы *.h.
    HEADERS += mainwindow.h
    LIBS Пути к динамическим библиотекам и статическим библиотекам, которые используются в программе.
    LIBS += -L./libs \
    	-L./my_libs \
    	-lmycustomlib
    QT Модули Qt, которые используются в программе.
    QT += core gui widgets \
    	network sql xml
    RESOURCES Файл ресурсов.
    RESOURCES = resources.qrc
    SOURCES Исходные тексты программы *.cpp.
    SOURCES += main.cpp \
    	mainwindow.cpp
    TARGET Название исполняемого файла или динамической библиотеки.
    TARGET = MyFirstProject
    TEMPLATE Тип проекта (приложение, библиотека, составленный из подпроектов ...)
    TEMPLATE = lib

    12.2 Компиляция проекта

    Компиляция проекта проходит в два этапа. Сначала выполняется предварительная обработка проекта с помощью программы qmake. Этот инструмент Qt несёт ответственность за весь процесс компиляции проекта. Он читает содержание проектного файла и генерирует необходимые промежуточные файлы (дополнительные файлы с исходным кодом и make-файлы для компиляции). Это необходимо для того, чтобы превратить все особые расширения Qt, которые были использованы в программе, в код на языке C++ и использовать дополнительные настройки для проекта, описанные в pro-файле. После этого проект готов к обработке компилятором. Вторым этапом является непосредственно процесс компиляции. Все эти действия выполняются автоматически в среде Qt Creator.

    (рис 12.1) Процесс компиляции проекта

    После успешной компиляции мы получаем исполняемый файл программы. Откройте папку проекта. Она будет содержать исполняемый файл и все промежуточные файлы, сгенерированные в процессе.

    Таким образом, процессом построения проекта руководит .pro-файл. При наличии исходных текстов программы и при отсутствии .pro-файла, его можно сгенерировать. Для этого из командной строки необходимо перейти в папку, которая содержит исходные тексты программы и вызвать qmake с параметром --project. Этим приёмом удобно воспользоваться, чтобы сгенерировать файл проекта и использовать оболочку QtCreator для работы над программой (даже для обычных программ на C++ без Qt).

    Раздел "Projects" (Проекты) содержит набор необходимых настроек для процесса компиляции и для настройки среды запуска проекта. Одной из таких настроек есть опция Shadow Build, которая позволяет включить режим при котором для промежуточных файлов, make-файлов и продуктов компиляции создаётся отдельная папка вне папки с исходным кодом проекта (настройки размещения для неё — в поле Build directory). Это позволяет построить и хранить одновременно несколько вариантов построенного проекта для различных инструментариев. Также это сохраняет папку с исходным кодом от засорения файлами, созданными в процессе построения проекта. При выключенном Shadow build промежуточные файлы и папка с построенной программой сохраняются в папке, которая содержит файл проекта.

    Конечно, созданные промежуточные файлы не являются непосредственной частью проекта. Они были сгенерированы, и будут перезаписываться при необходимости во время компиляции. Поэтому не стоит добавлять их к pro-файлу или делать любые изменения в них. Также не стоит их добавлять в систему контроля версий, если её используют при разработке.

    Иногда сгенерированные файлы вместе с объектными и make-файлами бывает необходимо удалить. Это необходимо делать перед тем как заархивировать проект для сохранения, поскольку сгенерированные файлы занимают довольно много места на диске по сравнению с объёмом исходного кода. Порой могут возникать проблемы с компиляцией, когда после значительных изменений в структуре программы промежуточные файлы не были достаточно хорошо заново сгенерированы. В таких случаях возникает необходимость очистить проект. Для этого выберите в главном меню Build->Clean Project (Сборка->Очистить проект). Это позволит удалить сгенерированные файлы, кроме скомпилированного исполняемого файла и make-файлов.

    Для того, чтобы очистить проект полностью, необходимо изменить некоторые настройки. Откройте раздел Projects (Проекты) и в разделе Clean Steps (Этапы очистки) нажмите кнопку Details (Подробнее) и измените параметр Make arguments (Аргументы make) с clean на distclean (рис. 12.2).

    (рис 12.2) Настройки очистки проекта

    Снова очистите проект — все сгенерованные файлы, включительно с исполняемым файлом и make-файлами, будут удалены.

    12.3 Консольный проект Qt. Вывод сообщений.

    Несмотря на то, что Qt почти всегда рассматривают как инструментарий для создания программ с графическим интерфейсом, его также можно использовать и в таких программах, которые работают как фоновые процессы, а также в консольных проектах. Для нескольких следующих примеров мы будем использовать последний созданный нами в предыдущем разделе проект.

    В таком консольном проекте можно использовать почти все привычные для Qt средства и классы. В следующих нескольких примерах мы рассмотрим работу с некоторыми важными типами Qt именно на примере консольного проекта. А пока что ограничимся только обзором средства, которое позволяет выводить в консоль сообщения и разнообразную информацию для отладки в процессе работы программы.

    Для вывода информации в консольном проекте можно использовать все привычные средства стандартной библиотеки C++. Но в Qt для этого есть удобный инструмент — функция qDebug(). Рассмотрим пример её использования:

    #include <QDebug>
    //Собственный тип данных — структура для комплексных чисел
    struct complex
    {
    	double re;
    	double im;
    };
    //Определение потокового оператора для поддержки вывода собственного типа
    //complex с помощью qDebug()
    QDebug operator<<(QDebug dbg, const complex c )
    {
    	dbg.nospace ( ) << " ( " << c.re << " + i * " << c.im << " ) ";
    	return dbg.space ( );
    }
    int main ( int lArg c, char * lArgv [ ] )
    {
    	//Вывод разнообразных типов данных
    	qDebug ( ) << " Hello, " << " this is debug output ";
    	qDebug ( ) << " Integer values : " << 1 << 10 << 100;
    	qDebug ( ) <<" Doubles and floats : "<<.1 << .123 << 0.112345;
    	qDebug ( ) << " Characters : " << " c " << " \ t " << " $ " << " \ n " << " newline ";
    	qDebug ( ) << " Booleans : " << true << false;
    	qDebug ( ) << " Pointers : " << lArgv;
    	qDebug ( ) << " and much more...";
    	//Вывод собственного типа данных
    	complex c;
    	c.re = 0.2;
    	c.im = 1.5;
    	qDebug ( ) << " including custom types : " << c;
    	return 0;
    }

    После выполнения программы в консоли увидим текст:

    Hello, this is debug output
    Integer values: 1 10 100
    Doubles and floats: 0.1 0.123 0.112345
    Characters: c $
    newline
    Booleans: true false
    Pointers: 0x3278fc8
    and much more...
    including custom types: (0.2 + i*1.5)

    Кроме qDebug() существуют другие функции для вывода сообщений разного уровня. Описание и примеры этих функций рассмотрим в табл. 12.2.

    Функции для вывода сообщений
    Функция Описание Особенности Пример
    qDebug() Вывод сообщений для отладки, разнообразной информации при работе программы. Сообщения могут быть выключены с помощью специального макроопределения QT_NO_DEBUG_OUTPUT например, в файле проекта:
    DEFINES +=
    	QT_NO_DEBUG_OUTPUT
    int error_num = 59;
    std::string error_string (
    	"uknown error" );
    qDebug ( " result : % d ,
    	description : % s " ,
    	error_num, error_string
    	. c_str ( ) );
    __________________
    #include <QDebug>
    ...
    qDebug ( )<<" result : "" <<
    	error_num <<" ,
    	description : " <<
    	error_string.c_str ( );
    qWarning() Вывод сообщений при работе программы. Сообщения могут быть выключены с помощью специального макроопределения QT_NO_WARNING_OUTPUT например, в файле проекта:
    DEFINES +=
    	QT_NO_WARNING_OUTPUT
    qWarning ( " warning : % d ,
    	description : % s " ,
    	error_num, error_string
    	.c_str ( ) );
    __________________
    #include <QDebug>
    ...
    qWarning ( )<<" warning : "
    	<<error_num <<" ,
    	description : " <<
    	error_string.c_str ( );
    qCritical() Вывод сообщений о критических ошибках.
    qCritical ( " critical error
    	: % d, description :% s " ,
    	error_num, error_string
    	.c_str ( ) );
    __________________
    #include <QDebug>
    ...
    qCritical ( )<<" critical
    	error : " <<error_num
    	<<", description : " <<
    	error_string.c_str ( );
    		
    qFatal() Вывод сообщений о фатальных для программы ошибках После вывода сообщения происходит аварийное завершение работы программы
    qFatal ( " fatal error : % d ,
    	description :% s " ,
    	error_num, error_string
    	.c_str ( ) );

    12.4 Работа с текстовыми строками в Qt. Класс QString. Списки строк QStringList.

    Обычные строки С довольно просты в использовании, но работать с ними не очень удобно в ряде случаев. Один из них, это поддержка выбора кодировок для текста. Ведь, как известно, существует много разных стандартов кодирования символов текста, которые отличаются поддержкой разного диапазона кодируемых символов.

    В Qt для работы со строками есть мощный и специализированный класс — QString. Он имеет поддержку Unicode, возможность преобразования текста между разными кодировками и в обычные строки С и std::string. А также он имеет хорошее быстродействие и богатый набор инструментов для работы. Поддержка Unicode позволяет работать с текстом на любом языке мира, что очень важно при локализации графического интерфейса программы.

    Рассмотрим методы работы с текстовыми строками в Qt. Перед началом работы с текстом в Qt нужно подключить файл описания QString:

    #include <QString>

    Как и почти для всех классов Qt, название класса совпадает с названием файла описания класса, который необходимо подключить с помощью директивы #include.

    Существует большое количество разных способов добавления строк и символов к существующей строке:

    QString lMainStr = " string "; // lMainStr == " string "
    lMainStr += " "; // lMainStr == " string "
    ( lMainStr += " is " ) += " "; // lMainStr == " string is "
    QString lHelperStr1 ( " composed " );
    lMainStr += lHelperStr1; // lMainStr == " string is composed"
    QString lHelperStr2 = + " " +QString( " from " ) + " ";
    lMainStr.append ( lHelperStr2 );
    // lMainStr == " string is composited from "
    lMainStr.push_back ( " fragments " );
    // lMainStr == " string is composited from fragments "
    lMainStr.prepend ( " This " );
    // lMainStr == "This string is composited from fragments "
    lMainStr.insert ( lMainStr.length ( ), "." );
    // lMainStr == "This string is composited from fragments."
    lMainStr += QString( 2, '.' );
    // lMainStr == "This string is composited from fragments..."
    lMainStr= lMainStr.rightJustified ( lMainStr.length ( ) + 8, ' ' );
    // lMainStr=="This string is composited from fragments..."

    Также есть возможность выделения части строки либо разделения её на части:

    QString lQuote= "This is sentence one. This is sentence two. ";
    //Новая строка из пяти символов
    QString lFragment1 = lQuote.left ( 5 ); // lFragment1 == "This "
    qDebug ( ) << " lFragment1 is : " << lFragment1;
    //Первое предложение: Все символы до первой точки
    QString lSentence= lQuote.section ( ".", 0, 0 );
    qDebug ( ) << " lSentenceis : " << lSentence;
    // lsentence == "This is sentence one"
    //Список слов в строке
    QStringList lWordsList = lSentence.split ( " ", QString::SkipEmptyParts );
    qDebug ( ) << lWordsList;
    // lWordsList == (" This ", " is ", " sentence ", "one ", "This " ,
    // " is ", " sentence ", " two ")

    Для проверки на пустую строку используют метод isEmpty(). Его не следует путать с методом isNull(), который возвращает значение true только для ещё не инициализированной строки. Например:

    QString( ).isNull ( ); // true (нулевая строка)
    QString( ).isEmpty ( ); // true (нулевая строка тоже пустая)
    QString( "" ).isNull ( ); // false (пустая строка не является нулевой)
    QString( "" ).isEmpty ( ); // true
    QString( "abc" ).isNull ( ); // false
    QString( "abc" ).isEmpty ( ); // false

    QString имеет инструменты для преобразования из std::string и наоборот. Например:

    QString lQtstringInitial = " I am a standard STL string. ";
    std::string lStdstring = lQtstringInitial.toStdString ( );
    QString lQtString = QString::fromStdString ( lStdString );

    Также QString имеет средства для работы с числовой информацией:

    //преобразование целого числа в строку
    int x = 16;
    QString lXStr = QString::number ( x );
    // x = 7; lXStr = 7
    //преобразование строки в целое число
    int y = lXStr.toInt ( );
    //преобразование дробного числа в строку
    double teta = 12099.10012021210102109991;
    QString lTetaStr = QString::number ( teta );
    // lTetaStr == 12099.1
    lTetaStr.setNum ( teta );
    // lTetaStr == 12099.1
    //вывод с 4-мя знаками после запятой
    lTetaStr = QString::number (teta, 'f', 4 );
    // lTetaStr == 12099.1001
    //форматирование с использованием символа "e"
    lTetaStr = QString::number (teta, 'e' );
    // lTetaStr == 1.209910e+04
    //Запись числа в строку в разных системах счисления
    lXStr = QString("int %1 is %L2 in decimal system, %L3 in binary system, and %
    	L4 in hexadecimal")
    		.arg( x )
    		.arg( x, 0, 10 )
    		.arg( x, 0, 2 )
    		.arg( x, 0, 16 );

    Для работы со списком строк в Qt предусмотрен специализированный тип QStringList. QStringList относят к контейнерным классам Qt. Подробнее классы-контейнеры мы рассмотрим в следующем параграфе.

    12.5 Контейнерные классы в Qt

    Список строк QStringList, который мы рассматривали, является представителем семейства контейнерных классов Qt, которые являются аналогом контейнеров STL. Но в то же время они имеют свои собственные различия в реализации и возможностях. Мы же будем в дальнейшем использовать в наших примерах исключительно контейнеры Qt.

    Простейшим контейнерным классом является QList. QList — список общего назначения. Для добавления элементов в начало и в конец списка используют методы prepend() и append(). Также можно добавить несколько элементов за раз используя потоковые операторы. Например:

    #include <QList>
    ...
    QList<QString> lList;
    QString lStr1 ( " string 1 " );
    QString lStr2 ( " string 2 " );
    lList << lStr1 << lStr2; //Добавляем несколько элементов за раз
    lList.prepend ( lStr1 ); //Добавляем элемент в начало (повторно)
    lList.append ( " string 3 " ); //Добавляем в конец списка
    lList << " string 4 "; //То же, что и lList.append (" string 4 ");

    Последние две строки возможны за счёт неявного преобразования char* в QString. Класс QStringList, которого мы коснулись в примерах предыдущего раздела посвящённого текстовым строкам, наследует от QList<QString> и реализует ряд дополнительных методов для работы со строками в списке.

    Для доступа к элементам используют метод at(), который принимает индекс элемента в списке в качестве аргумента. Количество элементов в списке возвращают методы size() и count().

    qDebug ( ) << lList.first ( );
    qDebug ( ) << lList.last ( );
    if ( lList.size ( ) >3) qDebug ( ) << lList.at ( 3 );

    Также стоит отметить два других важных разновидности контейнеров: хэш (QHash) и словарь (QMap). Хэш — контейнер, в котором элементы добавляют парами (ключ-значение). Значение в хэше находят по ключу, а для поиска используют хэш-функцию, которая преобразует ключ в значение. В словаре элементы также добавляют парами ключ-значение, но значение сортируют по ключу. Для доступа к значению, используют метод value(), который принимает два параметра: ключ и значение по умолчанию, которое метод вернёт, если значение не будет найдено. Например:

    #include <QMap>
    ...
    QMap<QString, QString> lSurnameByName;
    lSurnameByName.insert ( " Bill ", " Hunter " );
    lSurnameByName.insert ( " Marry ", " Lee " );
    //Поиск значения по ключу
    qDebug ( ) << " Bill " << lSurnameByName.value ( " Bill " );
    qDebug ( ) << " Marry " << lSurnameByName.value ( " Marry ", " Doe " );
    //Прибавляем другое значение с уже существующим ключом
    lSurnameByName.insert ( " Marry ", " Hunter " );
    qDebug ( ) << " Marry " << lSurnameByName.value ( " Marry " );
    //Ключи не существуют — вывод значений по умолчанию
    qDebug ( ) << " James " << lSurnameByName.value ( " James " );
    qDebug ( ) << " John " << lSurnameByName.value ( " John ", " Doe " );

    После выполнения получим вывод:

    Bill "Hunter"
    Marry "Lee"
    Marry "Hunter"
    James ""
    John "Doe"

    Обратите внимание: после того, как мы добавили ещё одно значение с тем же ключом (Marry), предыдущее значение было перезаписано новым значением. Для того, чтобы добавить несколько значений с одним и тем же ключом можно воспользоваться методом insertMulti().

    #include <QHash>
    ...
    QHash<QString, QString> lClassificationHash;
    //Добавляем несколько значений с одинаковыми ключами
    lClassificationHash.insertMulti ( " fruits ", " apple " );
    lClassificationHash.insertMulti ( " fruits ", " orange " );
    lClassificationHash.insertMulti ( " vegetables ", " potato " );
    lClassificationHash.insertMulti ( " vegetables ", " cabbage " );
    lClassificationHash.insertMulti ( " vegetables ", " tomato " );
    qDebug ( )<< lClassificationHash.value ( " fruits " ); //Вывод одного значения с ключом
    qDebug ( )<< lClassificationHash.values ( " fruits " ); //Вывод значений с ключом
    qDebug ( )<< lClassificationHash.values ( " vegetables " );

    Получим следующий вывод в консоль:

    "orange"
    ("orange", "apple")
    ("tomato", "cabbage", "potato")

    Для итерации по списку можно воспользоваться макросом foreach. Также можно воспользоваться итератором в стиле Java. Например:

    QList<int> lList; //Создаём список целых чисел
    lList.append ( 3 ); //Добавляем элементы
    lList.append ( 6 );
    lList.append ( 9 );
    QListIterator <int> lIt ( lList ); //Создаём итератор для списка
    while ( lIt.hasNext ( ) ) //Пока следующий элемент существует
    {
    	qDebug ( ) << lIt.next ( ); //...вывести следующий элемент
    }

    Другой пример — итерация в обратном направлении. На этот раз используем хеш.

    QHash<QString, int> lNumberByName;
    lNumberByName.insert ( " twelve ", 12 );
    lNumberByName.insert ( " thirty three ", 33 );
    lNumberByName.insert ( " one hundred an twenty five ", 125 );
    QHash It erator<QString, int> lHashIterator ( lNumberByName );
    lHashIterator.toBack ( ); //Перейти к концу контейнера — итератор указывает после
    //последнего элемента
    while ( lHashIterator.hasPrevious ( ) )
    {
    	lHashIterator.previous ( ); //Переходим к предыдущемму элементу
    	//Выводим ключ и значение
    	qDebug ( ) << lHashIterator.key ( )<< " - " << lHashIterator.value ( );
    }

    Следующий пример — с итератором в стиле STL.

    QHash<QString, int >::const_iterator lStlLikeIterator;
    for ( lStlLikeIterator = lNumberByName.begin ( );
    lStlLikeIterator != lNumberByName.end ( );
    lStlLikeIterator ++)
    {
    qDebug ( ) << lStlLikeIterator.key ( )<< " - "
    //Тоже самое, что и * lStlLikeIterator
    << lStlLikeIterator.value ( );
    }
    приведены разновидности контейнеров Qt.
    Переменная Описание особенностей
    QList Список общего назначения для использования в большинстве ситуаций, которые возникают при разработке. Имеет оптимальное быстродействие в большинстве случаев.
    QLinkedList Реализует связный список в Qt. Отсутствует операция доступа по индексу элемента (такая как at(int pos)).
    QVector Реализует вектор элементов в Qt.
    QStack Реализует стек. Стек размещает элементы по принципу LIFO (Last In, First Out) — элемент, добавленный первым будет последним элементом в стеке.
    QQueue Реализует очередь. Очередь размещает элементы по принципу FIFO (First In, First Out) — элемент, добавленный первым, будет первым элементом в очереди
    QSet Множество элементов. Гарантирует, что все элементы будут уникальными.
    QMap Контейнерный класс для словаря. Элементы добавляют парами: ключ-значение. Словарь всегда сортирует элементы по ключу. Позволяет найти элемент по ключу.
    QMultiMap Контейнерный класс словаря создан для удобной работы с тем, чтобы каждому ключу соответствовало несколько значений. Метод insert() не заменяет значение ключа, если ключ уже существует, а добавляет новую пару ключ-значение.
    QHash Контейнерный класс для хеша. Элементы добавляют парами: ключ — значение. Элементы хранятся в хеше в произвольном порядке. Позволяет выполнять очень быстрый поиск элемента по ключу.
    QMultiHash Контейнерный класс хеша, создан для удобной работы с тем, чтобы каждому ключу соответствовало несколько значений. Метод insert() не заменяет значение ключа, если ключ уже существует, а добавляет новую пару ключ-значение.

    12.6 Работа с файлами

    Инструментарий Qt содержит большое количество средств, которые позволяют разработчику абстрагироваться от деталей реализации на той или иной программной платформе. В этом разделе мы рассмотрим средства, Qt предоставляет для работы с файловой системой.

    Все устройства ввода/вывода в Qt наследуют от абстрактного класса QIODevice. Среди его потомков: буфер для данных (QBuffer), процесс — программа которая выполняется в системе (QProcess), сетевой сокет (QAbstractSocket) и другие. Мы же подробно рассмотрим работу с другим его потомком — классом для работы с файлом (QFile).

    Для работы с файлом необходимо создать объект класса QFile и задать для него путь к файлу (абсолютный или относительный), с которым вы будете работать. Путь и имя передают как параметр конструктора или с помощью метода setFileName().

    Далее файл необходимо открыть и задать режим доступа к нему. Метод open()принимает флаги доступа и возвращает true, если файл удалось открыть. Доступные флаги доступа:

  • QIODevice::ReadOnly — открыть для чтения;
  • QIODevice::WriteOnly — открыть для записи;
  • QIODevice::ReadWrite — открыть для чтения и записи;
  • QIODevice::Append — все данные будут добавляться в конец файла (после уже существующих данных);
  • QIODevice::Truncate — если возможно, стереть содержимое файла перед открытием;
  • QIODevice::Text — режим работы с текстовым файлом (важно для текстовых файлов для корректной обработки символов завершения строки в Windows и Linux).
  • Флаги (класс QFlags) часто используют в Qt для задания комбинации настроек. Для комбинации нескольких настроек, так же как и бинарной арифметике, используют операцию побитового OR.

    Для записи и чтения используют методы read() и write(), которые перегружены в нескольких вариантах. Для чтения одной строки текстового файла используют метод readLine(). Для чтения всего содержимого можно воспользоваться методом readAll(). Текущую позицию при чтении из файла определяют с помощью метода pos(). Установить позицию можно с помощью метода seek(). Метод atEnd() позволяет определить достигли ли мы конца файла при чтении. После завершения работы с файлом его нужно закрыть вызовом метода close(). Следующий пример демонстрирует чтение текстового файла и вывод его в консоль.

    #include <QDebug>
    #include <QFile>
    int main ( int arg c, char * argv [ ] )
    {
    	const QString lFileName ( " file.txt " );
    	//Проверяем существование файла
    	if ( ! QFile::exists ( lFileName ) )
    	{
    		qCritical ( " File % s does not exists." ,
    		qPrintable ( lFileName ) );
    		return 1;
    	}
    	QFile lFile;
    	//Устанавливаем имя файла
    	lFile.setFileName ( lFileName );
    	//Открываем файл — текстовый, только для чтения
    	if ( ! lFile.open ( QIODevice::ReadOnly | QIODevice::Text ) )
    	{
    		//Если открыть файл не удалось — выводим сообщение об ошибке
    		qCritical ( " Error % d : % s.", lFile.error ( ) ,
    		qPrintable ( lFile.errorString ( ) ) );
    		return 2;
    	}
    	//Пока можно прочесть строку
    	while ( ! lFile.atEnd ( ) )
    	{
    		// ... выводить её в консоль
    		qDebug ( ) << lFile.readLine ( );
    	}
    	//Заканчиваем работу с файлом
    	lFile.close ( );
    	return 0;
    }

    Рассмотрим работу с файлами в Qt на другом примере записи и чтения текстовой информации. В этом примере мы используем класс QTextStream для получения введённой пользователем информации в стандартный поток ввода. Конструктор QTextStream может принимать в качестве параметра указатель на потомок QIODevicе, указатель на QString или QByteArray, а также файловую переменную. В примере мы перенаправляем поток ввода в QТextStream. Далее мы читаем строку из потока ввода и записываем её в файл.

    #include <QDebug>
    #include <QIODevice>
    #include <QFile>
    #include <QTextStream>
    int main ( int lArg c, char * lArgv [ ] )
    {
    	QTextStream in ( stdin );
    	QFile lFile ( " in.txt " );
    	if ( lFile.open ( QIODevice::WriteOnly | QIODevice::Truncate ) )
    	{
    		QString lData = in.readLine ( );
    		lFile.write ( qPrintable ( lData ) );
    		lFile.close ( );
    	}
    	else
    	{
    		qDebug ( ) << " Cannot open file ! ";
    	}
    	return 0;
    }

    В следующем фрагменте демонстрируем обратный процесс — чтение строки из файла и вывод прочитанного строки в консоль. Опять же для чтения из файла мы используем QTextStream.

    QFile lFile ( " in.txt " );
    if ( lFile.open ( QIODevice::ReadOnly | QIODevice::Truncate ) )
    {
    	QTextStream in (lFile );
    	QString lData = in.readLine ( );
    	qDebug ( ) << lData;
    	lFile.close ( );
    }
    else
    {
    	qDebug ( ) << " Cannot open file ! ";
    }

    12.7 Задачи для самостоятельного решения

  • Создайте пустой консольный проект Qt и скомпилируйте его. Определите потоковый оператор для вывода класса Person, который имеет поля Name, Phone number, Address для вывода в консоль с помощью функции qDebug().
  • Создайте программу табуляции функции и записи в текстовый файл с использованием средств, предоставляемых Qt. Используйте классы QFile, QTextStream для записи. Адрес текстового файла жёстко задайте в тексте программы.
  • Создайте программу чтения значений протабулированной функции из текстового файла с использованием средств Qt и вывода значений в консоль. Используйте классы QFile, QTextStream для чтения. Адрес текстового файла жёстко задайте в тексте программы. Если файл невозможно открыть для чтения — выведите сообщение о критической ошибке с завершением работы программы.
  • Вернуться к учебному плану