UseDevTools:/blog/sozdanie_app_vs2017_qt59

В настоящее время, Qt является распространённым кроссплатформенным фреймворком для разработки программного обеспечения на языке программирования C++.

Библиотека появилась в далёком 1996 году, и, за время своего существования, легла в основу многих програмных проектов.

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

В этой статье мы рассмотрим создание простого приложения с помощью которого возможно кодирование текста и файлов в base64 формат. В качестве примера создадим приложение для операционной системы Windows.

Средой разработки будем использовать IDE Microsoft Visual Studio 2017 и Qt фреймворк версии 5.9.

Создание приложения рассмотрим с точки зрения новичка в Qt, который только начинает знакомиться с фреймворком, но который имеет некоторые навыки в обращении с MS Visual Studio (здесь и далее VS).

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

Полностью исходные и исполняемый файл приложения вы можете скачать и посмотреть с нашего сайта здесь.

По всем замечаниям и предложениям относительно данной статьи вы можете писать на почтовый ящик admin@usetoolz.ru или в раздел отзывы внизу статьи.

С официального сайта Qt скачаем бесплатную версию фреймворка для разработки программ с открытыми исходными кодами (лицензия GNU GPL).

В процессе установки выберем стабильную версию Qt5.9.1 и компоненты для MS VisualStudio 2017. Здесь мы будем использовать 64-битную версию компонент, для создания 64-битных приложений.

Далее, необходимо скачать и установить Addin к Visual Studio, для интеграции Qt в VS.

Addin можно поставить непосредственно из VS (выбираем в меню Tools > Extensions and Updates…) или скачать его с сайта Qt, последняя версия (beta) на момент написания статьи была от 3 августа (qt-vsaddin-msvc2017-2.1.2-beta-03.08.2017.vsix).

В конечном счете в Visual Studio должно появиться соответствующее установленное расширение:

Приступим к созданию нашего проекта.

Здесь мы имем три варианта, для дальнейшего продвижения:

1. Создание проекта по шаблону
2. Конвертация проекта, созданного в QtCreator’е (.pro)
3. Создание пустого проекта, с добавлением в него библиотек Qt

Первый вариант наиболее заманчивый, однако мне так и не удалось им воспользоваться.

После выбора шаблона Qt,

мы попадаем в Qt GUI Application Wizard,

выбираем компоненты которые хотим использовать.

Описываем наш новый класс;

И... получаем что то типа этого

И совершенно пустой проект.

В итоге создать проект этим способом у меня не получилось. Возможно на моей машине не совсем корректно установлена Visual Studio или продукт находящийся в стадии beta тестирования подходит не для всех конфигураций систем. В любом случае если вы сталкивались или столкнётесь с этой проблемой и победите её – пишите, буду очень благодарен.

Второй вариант создания проекта позволяет создавать проекты для visual studio на основе .pro файлов – проектов созданных в QtCreator’е. После многих попыток открытия проектов Qt из Examples таким способом, я пришёл к выводу что всё таки beta addin не совсем доработан до реалий VS 2017. Многие проекты не открывались вообще, а те, что открылись, начинали выдавать ошибки в самых неожиданных местах. То файлов нужных нет, то .h файл не подключен и т.п.

В общем, если мы хотим полностью работать в среде Qt то мы должны использовать стандартный QtCreator. Наша же статья именно об интеграции Qt с IDE Visual Studio.

Поэтому, перейдем к третьему варианту. Создадим проект, непосредственно в Visual Studio, и подключим к нему библиотеки Qt. Создать можно пустой проект, или консольное приложение. В последнем случае студия создаст необходимые файлы автоматичеки, которые в первом случае придётся добавлять руками.

Не забудьте, что тип платформы должен быть 64-битный, т.к. ранее мы установили 64-битную версию компонент Qt для VisualStudio.

 

В итоге проект должен выглядить так:

Основной файл приложения Base64Coder.cpp будет содержать строки:

#include "stdafx.h"
int main(int argc, char *argv[])
{
return 0;
}

На данном этапе приложение ничего не делает.

На моей машине Qt установлен по адресу C:\Qt, поэтому все пути к файлам в дальнейшем будут указываться от него.

Укажем Visual Studio, где находятся заголовочные файлы (.h) и файлы библиотек (.lib) фреймворка.

Линковщику скажем какие именно библиотеки буду использоваться при сборке приложения.

Наше приложение будет использовать четыре Qt библиотеки, Qt5Core.lib, Qt5Gui.lib, Qt5Widgets.lib и qtmaind.lib.

Обратите внимание, что настроки проекта для различной конфигурации могут быть разными. В Qt существуют разные наборы библиотек для Debug и Release версий. Если в названии библиотеки последний символ – d, то библиотека должна использоваться в Debug версии. В релизной версии эта буква отсутствует. Такое же правило верно и для qt-библиотечных .dll файлов, испозуемых приложением во время работы.

Так-же линковщику укажем, что подсистема нашего приложения – стандартное оконное windows приложение: SubSystem: Windows (/SUBSYSTEM:WINDOWS).

В случае, если мы создавали проект от консольного приложения, в этом поле будет указано: Console (/SUBSYSTEM:CONSOLE), и перед запуском основного окна приложения, появится консольное окно, что впрочем полезно при отладке, так как в это окно будут выводиться все ошибки и предупреждения.

Для того, что бы создать визуальное окно приложения, вы можете пойти классическим путём. В начале создаёте файлы класса окна (.cpp и .h), затем прописываете в них добавление всех визуальных контролов, и в завершение пишите код управления их поведением.

В противовес этому подходу, Qt имеет собственное средство для визуального создания окон – «QtDesigner». Здесь вы можете визуально накидать на форму элементов управления, задать модели их поведения и сохранить всё это в файл.

Создадим основное окно нашего приложения при помощи дизайнера. Из меню «Qt VS Tools > Launch Qt Designer» запустим дизайнер.

Из шаблонов выбираем «MainWindow» и жмём кнопку создать.

Далее сохраняем получившиеся окно в файл с именем MainWindow.ui, и добавляем его в проект.

Затем, добавим в проект класс главного окна. Обратите внимание, в качестве родительского класса укажем «QMainWindow» – класс библиотеки Qt:

Добавив вызовы конструктора родительского класса, получим следующий код:

MainWindow.h

#pragma once
class MainWindow :
public QMainWindow
{
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
};

MainWindow.cpp

#include "stdafx.h"
#include "MainWindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent)
{
}
MainWindow::~MainWindow()
{
}

Изменим файл Base64Coder.cpp, добавив в него инициализацию и активацию нашего окна:

#include "stdafx.h"
#include "mainwindow.h"
#include <QtWidgets/QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}

В конце функции мы запускаем на исполнение наше Qt приложение.

Собираем проект и стартуем его (Ctrl+F5).

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

Для преобразование .ui файлов в компилируемый вид воспользуемся утилитой «uic.exe» из набора Qt библиотеки.

В нашем случае этот файл находится в директории C:\Qt\5.9.1\msvc2017_64\bin\

Запустим её с параметрами для нашего класса главного окна
C:\Qt\5.9.1\msvc2017_64\bin\uic.exe -o ui_mainwindow.h MainWindow.ui

Получившийся файл ui_mainwindow.h включим в проект.

Информация о нарисованной нами в дизайнере форме будет добавлена в сборку. Добавим указание на её использование нашему классу главного окна:

MainWindow.h

#pragma once
namespace Ui {
class MainWindow;
}
class MainWindow :
public QMainWindow
{
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private:
Ui::MainWindow *ui;
};

MainWindow.cpp

#include "stdafx.h"
#include "MainWindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
MainWindow::~MainWindow()
{
}

Теперь, при запуске, наше приложение будет иметь следующий вид:

Внизу виден элемент StatusBar, т.е. мы успешно подключили наше окно, созданное в Qt дизайнере.

В дальнейшем мы ещё не раз будем корректировать внешний вид нашего окна в qt-дизайнере, и каждый раз запускать утилиту преобразования файла .ut в .h из командной строки не очень удобно, поэтому создадим отдельный .bat файл и пропишем в нём команду преобразования.

Файл назовём, например, prebuild.bat:

C:\Qt\5.9.1\msvc2017_64\bin\uic.exe -o ui_mainwindow.h MainWindow.ui

Далее укажем его имя в поле Pre-Build Event, в свойствах нашего проекта.

Теперь исполнение нашего .bat файла будет всегда вызываться перед сборкой проекта. В дальнейшем, этот файл нам ещё не раз пригодится.

Поговорим немного о компоновке элементов управления на форме окна (layouts).

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

Компоновка доступна по следующим типам:

по горизонтали,
по вертикали,
по сетке,
в два столбца (в виде формы)

Для примера выберем компоновку по сетке:

Видим, что компоновщик наложил на форму сетку размером три на пять и разложил наши кнопки по её ячейкам (как смог). Теперь при изменении размеров окна, кнопки будут пропорционально менять своё расположение внутри него.

Для того, чтобы ещё дальше разделить выбранную ячейку, нам необходимо положить в неё отдельный элемент – компоновщик (layout), взяв его c панели виджетов qt-дизайнера. И, в зависимости от типа этого элемента, все внутренние компоненты будут располагаться уже по его правилам.

Как и компоновщик на форме, компоновщик элемент, также может быть четырёх типов: горизонтальный, вертикальный, по сетке и по форме.

Существуют еще два специальных элемента управления, которые занимаются тем, что раздвигают ячейку сетки компоновщика по горизонтали или по вертикали – так называемые спейсеры (spacer).

На примере разберем как можно создать форму с двумя кнопками справа – снизу, которые будут находится там вне зависимости от размеров окна.

Для этого нам потребуется два компоновщика (один на форме – вертикальный, и один отдельный – горизонтальный) и два спейсера – также горизонтальный и вертикальный.

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

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

Вот как это выглядит в дизайнере:

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

После того, как мы нарисовали интерфейс нашего главного окна, нам необходимо настроить действия, которые можно будет совершать выбирая пункты меню или нажимая кнопки на панели инструментов (ToolBar).

В редакторе действий опишем некоторые стандартные действия над нашим окном, а затем поместим их простым перетягиванием мышкой в меню и на панель инструментов.

Нашим созданным «действиям» мы можем поставить в соответствие иконку, которая будет появляться в меню – слево от надписи, или на кнопке в панели инструментов.

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

Для простоты поместим все иконки, которые будут использованы в приложении в отдельную директорию проекта, например - /ico, и создадим ресурсный файл, в котором опишем местонахождение наших иконок и назовём его mainwindow.qrc:

<RCC>
<qresource prefix="/res">
<file>ico/about.png</file>
<file>ico/encode.png</file>
<file>ico/decode.png</file>
<file>ico/exit.png</file>
<file>ico/file.png</file>
<file>ico/qtlogo.png</file>
<file>ico/base64coder_logo.png</file>
</qresource>
</RCC>

В дизайнере, в «обозревателе ресурсов», прикрепим его к форме (изменить/открыть файл ресурсов). После этого мы можем просмотреть наш ресурс и увидеть в нём наши иконки:

Для прикрепления иконки к элементу действия, в его свойстве icon мы выбираем пункт: «выбрать ресурс», и выбираем нужную нам икноку.

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

Для этого воспользуемся утилитой rcc.exe из набора Qt библиотеки.

Добавим следующую строчку в наш файл prebuild.bat, для автоматического преобразования ресурсного файла во время сборки проекта:

C:\Qt\5.9.1\msvc2017_64\bin\rcc.exe -name ApplicationResources mainwindow.qrc -o qrc_mainwindow.h

А файл qrc_mainwindow.h включим в проект и поместим на него ссылку в файле главного окна (mainwindow.cpp):

#include "qrc_mainwindow.h"

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

В Qt, для управлениями событиями, существует система сигналов и слотов. Сигнал генерирует сообщение, а слот принимает его. Между ними устанавливается соединение, которое указывает по активации какого сигнала сообщение должно быть направлено в какой слот.

Существуют три типа перенаправления сигнала – прямой, очередь, и очередь с блокировкой.

Первый тип сигнала устанавливается тогда, когда нужно передать сообщение слоту, который находится в том же потоке что и сигнал.В этом случае происходит просто вызов конечной функции слота (Qt::DirectConnection).

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

В случае, когда такая рассинхронизация неуместна, устанавливается тип перенаправления – очередь с блокировкой. Здесь управление к потоку сигнала вернётся только после того, как слот получит сообщение (Qt::BlockingQueuedConnection).

Можно сказать, что очередь - это асинхронная передача сообщения, а прямой вызов и очередь с блокировкой – синхронная.

В Qt-дизайнере существует возможность управлениями сигналами и слотами внутри окна.

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

Поэтому мы вынесем описание соединений в реализацию конструктора нашего окна mainwindow.cpp:

MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
QObject::connect(ui->Exit, &QAction::triggered, this, &MainWindow::close);
QObject::connect(ui->Encode, &QAction::triggered, this, &MainWindow::handleEncode);
QObject::connect(ui->Decode, &QAction::triggered, this, &MainWindow::handleDecode);

Начиная с пятой версии, в Qt был изменён способ соединения сигналов и слотов.

В более ранних версиях это было возможно сделать только используя макросы:

QObject::connect(ui->Exit, SIGNAL(triggered()), this, SLOT(close()));

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

В версии Qt5+ появилась возможность передавать сигналы и слоты через указатели:

QObject::connect(ui->Exit, &QAction::triggered, this, &MainWindow::close);

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

На данном этапе создания приложения, добавим в класс окна слоты handleEncode и handleDecode.

В файл «mainwindow.h» внесем следующие изменения:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QtWidgets/QMainWindow>
#include <QtGui/QClipboard>
#include <QtCore/QTextCodec>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
private:
int lineChunk72;
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
void handleEncode();
void handleDecode();
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H

Файл mainwindow.cpp будет выглядеть так:

#include "stdafx.h"
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "moc_mainwindow.h"
#include "qrc_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
lineChunk72 = 72;
QObject::connect(ui->Exit, &QAction::triggered, this, &MainWindow::close);
QObject::connect(ui->Encode, &QAction::triggered, this, &MainWindow::handleEncode);
QObject::connect(ui->Decode, &QAction::triggered, this, &MainWindow::handleDecode);
QObject::connect(ui->EncodeButton, &QPushButton::clicked, this, &MainWindow::handleEncode);
QObject::connect(ui->DecodeButton, &QPushButton::clicked, this, &MainWindow::handleDecode);
ui->tabWidget->setCurrentIndex(0);
}
MainWindow::~MainWindow()
{
emit cancelTaskRequired();
delete ui;
}
void MainWindow::handleEncode()
{
QString text = ui->inputTextEdit->toPlainText();
if (text.count() == 0)
{
return;
}
text = base64Encode(text);
if (ui->cb72->checkState() == Qt::Checked) {
int pie = lineChunk72, idx = 0;
while (idx < text.count() - pie) {
idx += pie;
text.insert(idx++, QString("\n"));
}
}
ui->outputTextEdit->setPlainText(text);
setStatusDone();
}
void MainWindow::handleDecode()
{
QString text = ui->inputTextEdit->toPlainText();
if (text.count() == 0)
{
return;
}
ui->outputTextEdit->setPlainText(base64Decode(text));
setStatusDone();
}
QString MainWindow::base64Encode(QString text) {
QByteArray buffer;
buffer.append(text);
return buffer.toBase64();
}
QString MainWindow::base64Decode(QString text) {
QByteArray buffer;
buffer.append(text);
return QByteArray::fromBase64(buffer);
}

В Qt каждый класс, который содержит сигналы и слоты должен быть унаследован от класса QObject и содержать в себе макрос «Q_OBJECT»

Это необходимо для поддержки работоспособности механизма сигналов и слотов.

Добавим этот макрос к нашему классу:

class MainWindow : public QMainWindow
{
Q_OBJECT

Если мы сейчас попробуем скомпилировать наш проект, то получим несколько ошибок сборки - линковщик не сможет разрешить некоторые символы нашего класса:

Это происходит потому, что макрос Q_OBJECT лишь декларирует поддержку системы сигналов-слотов. Что бы эта система заработала нужна её имплементация.

Для этого создадим .moc файл при помощи утилиты moc.exe из набора Qt библиотеки.

В этом файле будет описано взаимодействие наших сигналов и слотов из файла класса с системой Qt.

Добавим следующую строчку в наш файл prebuild.bat, для автоматического создания .moc файла, назовём его moc_mainwindow.h:

C:\Qt\5.9.1\msvc2017_64\bin\moc.exe -i mainwindow.h -o moc_mainwindow.h

Файл moc_mainwindow.h включим в проект и поместим на него ссылку в файле главного окна (mainwindow.cpp):

#include "moc_mainwindow.h"

Теперь компиляция нашего приложения пройдет успешно.

Если в своём приложении мы будем использовать символы отличные от латиницы, то, в общем случае, на форме мы увидем черные ромбики, вместо привычных нам слов.

Парадигма Qt предполагает использования модуля перевода совместно с программой «Qt Linguist» для локализации приложения.

В исходном коде своей программы, все строковые константы мы должны использовать в функции транслятора tr(“label”), затем создать файл с такими строками, и при помощи лигнвиста перевести их на конечный язык.

В этой статье мы не будем рассматривать данный подход, т.к. наше приложение небольшое, а модуль лингвиста удобно использовать совместно с Qt-креатором.

Вместо этого мы преобразуем текст из кодировки исходных файлов (наши файлы написаны в кодировке Windows-1251) в UNICODE, который используется при выводе конечных сообщений.

Для этого определим текстовый кодек и функцию преобразования:

Mainwindow.h

class MainWindow : public QMainWindow
{
Q_OBJECT
private:
QTextCodec *codec;
public:
QString ru(const char* chars);

Mainwindow.cpp

MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
codec = QTextCodec::codecForName("Windows-1251");
QString MainWindow::ru(const char* chars)
{
return codec->toUnicode(chars);
}

После этого, если мы будем использовать функцию ru() – например так: ru("Выберите файл для кодирования"), мы будем видеть привычный нам текст на русском языке.

Итак, мы написали приложение, которое конвертирует текст в строку base64 и обратно. Теперь мы хотим добавить возможность конвертировать файл в строку base64.

Само по себе это достаточно просто сделать:

QFile hInputFile(inputFileName),
QFile hOutputFile(outputFileName),
if (hInputFile.open(QIODevice::ReadOnly))
{
if (hOutputFile.open(QIODevice::WriteOnly | QIODevice::Truncate))
{
QByteArray arr = readInputBytes(hInputFile.size());
hOutputFile.write(arr.toBase64());
hOutputFile.close();
}
hInputFile.close();
}

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

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

Для этого создадим класс ProcessingFile, а в качестве родительских классов укажем QRunnable для операций в параллельном потоке и QObject для подключения системы сигнал-слот.

processfile.cpp

#ifndef PROCESSINGFILE_H
#define PROCESSINGFILE_H
#include <QtCore/QObject>
#include <QtCore/QRunnable>
#include <QtCore/QFile>
#include <QtCore/QTime>
#include <QtCore/QDateTime>
static const qulonglong DEFAULT_CHUNK_SIZE = 1000000;
class ProcessingFile : public QObject, public QRunnable
{
Q_OBJECT
public:
enum Result {
RESULT_OK,
RESULT_FAILED,
RESULT_CANCELLED
};
public:
ProcessingFile(const QString& inputFileName, const QString& outputFileName);
ProcessingFile(const QString& inputFileName, const QString& outputFileName, qulonglong chunkSize);
~ProcessingFile();
void run();
QByteArray readInputBytes(int count);
public slots:
void cancel();
signals:
void progressChanged(int allSize, int indexSize);
void progressFinished(Result result);
protected:
virtual QByteArray transform(QByteArray);
protected:
QFile hInputFile, hOutputFile;
qulonglong mChunkSize;
QAtomicInt mCancelledMarker;
};
#endif // PROCESSINGFILE_H

Функцию работы с файлом напишем так:

void ProcessingFile::run()
{
Result failProcess = RESULT_OK;
if (hInputFile.open(QIODevice::ReadOnly))
{
if (hOutputFile.open(QIODevice::WriteOnly | QIODevice::Truncate))
{
int fileSize = hInputFile.size();
emit progressChanged(fileSize, hInputFile.pos());
qint64 startTime = QDateTime::currentMSecsSinceEpoch();
while(true)
{
if (mCancelledMarker.testAndSetAcquire(true, true)) {
failProcess = RESULT_CANCELLED;
break;
}
QByteArray arr = readInputBytes(mChunkSize);
if (hInputFile.error() != QFile::NoError) {
failProcess = RESULT_FAILED;
break;
}
if (arr.count() == 0)
break;
arr = transform(arr);
if (hInputFile.error() != QFile::NoError) {
failProcess = RESULT_FAILED;
break;
}
hOutputFile.write(arr);
if (hOutputFile.error() != QFile::NoError) {
failProcess = RESULT_FAILED;
break;
}
if (QDateTime::currentMSecsSinceEpoch() - startTime > 100) {
emit progressChanged(fileSize, hInputFile.pos());
startTime = QDateTime::currentMSecsSinceEpoch();
}
}
emit progressChanged(fileSize, fileSize);
hOutputFile.close();
}
else
failProcess = RESULT_FAILED;
hInputFile.close();
}
else
failProcess = RESULT_FAILED;
emit progressFinished(failProcess);
}

Здесь последовательно читается часть файла, преобразовывается и пишется в другой.

После преобразования следующей части файла, мы посылаем сигнал о том, что в основном потоке необходимо обновить ход процесса преобразования (обычно это progressbar).

Обратите внимание, что если размер части файла невелик, то перерисовка progressbar’а будет происходить слишком часто, и все ресурсы процессора пойдут именно на его перерисовку, а не на полезные действия. Для этого мы ограничиваем количество перерисовок десятью за секунду:

if (QDateTime::currentMSecsSinceEpoch() - startTime > 100) {
emit progressChanged(fileSize, hInputFile.pos());
startTime = QDateTime::currentMSecsSinceEpoch();
}

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

Вот в принципе и всё что я хотел написать в этой статье.

В завершение, хочу добавить, как включить собственную иконку приложения в проект.

Для этого создадим ресурсный файл «base64coder.rc» и добавим в него следующую строку, указывающую на нашу иконку:

IDI_ICON1 ICON "base64coder.ico"

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

В данной статье мы рассмотрели создание приложения под Windows на C++ с использованием IDE Visual Studio 2017 и Qt фреймворка версии 5.9.

Мы создали проект на VS, настроили его на работу с библиотеками Qt, создали форму при помощи QtDesigner. Познали основы работы с системой обмена сообщениями «сигнал-слот». Подключили ресурсы к приложению. Научились работать с большими файлами.

Исходные файлы приложения вы можете скачать с нашего сайта здесь.

Так-же на нашем портале вы можете воспользоваться online версией перекодировщика в формат base64.

Все замечания и предложения по данной статье и тематики в целом, вы можете присылать на почтовый ящик admin@usetoolz.ru или оставить в качестве отзыва ниже.

04.10.2017

Добавить комментарий