Быстрый старт с Qt C++ в Visual Studio (Quickstart with Qt C++ in Visual Studio)

Автор: | 28.06.2019

Введение
Инсталляция библиотеки Qt в  Visual Studio
Qt GUI приложение «Hello, World!»
Signals и slots. Обработка событий
Qt Desiner. Создание GUI в интерактивном режиме
Полезные ссылки

Введение

Сегодня практически невозможно представить приложение, в котором нет графического интерфейса пользователя (GUI -Graphical User Interface). Windows API обладает необходимыми инструментами для создания GUI. Однако их использование требует больших затрат времени и практического опыта. Даже библиотека MFC, которая призвана облегчить процесс написания GUI приложений на С++ для ОС Windows, не дает той простоты и легкости создания программ, как хотелось бы. Но самый большой недостаток этих инструментов и библиотек — это платформозависимость. В отличие от MFC библиотека Qt кроссплатформенная. При этом она предоставляет возможности создавать GUI в интерактивном режиме.

Библиотека Qt — это множество классов (более 500), которые определены в модулях, например — QtCore, QtGui, QtWidgets, QtOpenGL. Модуль QtCore в основном отвечает за связь с ОС и управление событиями. Модули QtGui и QtWidgets содержит классы для программирования GUI. Модуль QtOpenGL делает возможным использование OpenGL.

Инсталляция библиотеки Qt в  Visual Studio

  1. Если у вас еще не установлен Visual Studio, тогда загрузите онлайн-инсталлятор Visual Studio 2017 Community Edition: visualstudio.com/downloads   и с помощью него установите VS (подробнее см. Установка Visual Studio 2017 )
  2. Загружаем файл qt-opensource-windows-x86-5.9.1.exe с официального сайта Qt и запускаем его (подробнее см. Installing Visual Studio 2017 + Qt Interface Library). Библиотека Qt установится в директории  C:/Qt.
  3. Обеспечиваем интеграцию Qt в Visual Studio. Для этого запускаем Visual Studio, выбираем кнопки Tools>Extensions and Updates…  , в открывшемся окне — кнопку Online. В поиске набираем qt, нажимаем кнопку Download, затем кнопку Install (подробнее см.  Как программировать на Qt в Visual Studio 2015  ).
  4. Перезапускаем VS, в меню появилась кнопка Qt VS Tools. Выбираем кнопки Qt VS Tools>Qt Options>Add. В окошко Path копируем путь C:\Qt\Qt5.9.1\5.9.1\msvc2017_64.
  5. Создаем проект (File>New>Project), откроется окно для выбора типа проекта, выбираем Qt Console Application:

В открывшемся окне для выбора модулей нажимаем кнопки Next>Finish (модули по умолчанию). В результате создается шаблон проекта  со следующим  кодом в файле main.cpp:

Запускаем программу (клавиша F5). Тест пройден успешно, если открывается окно консоли:

Если при запуске возникают ошибки, вероятнее всего это связано с отсутствием каких-либо необходимых компонентов в VS. Запустите снова онлайн-инсталлятор Visual Studio 2017 и доустановите их в соответствии с рекомендациями (см. Установка Visual Studio 2017 и  Installing Visual Studio 2017 + Qt Interface Library).

Давайте распечатаем «Hello World», изменив исходный файл:

#include <QCoreApplication>
#include <QDebug>
int main (int argc, char * argv [])
{
    QCoreApplication a (argc, argv); 
    qDebug () << "Hello World";
    return a.exec ();
}

Класс QCoreApplication предоставляет цикл обработки событий для консольных приложений Qt.  Для приложений с графическим интерфейсом мы будем использовать QApplication. При вызове a.exec () запускается цикл обработки событий.

Qt GUI приложение «Hello, World!»

Создаем проект Qt GUI приложения (File>New>Project>Visual C++>Qt>Qt GUI  Application>OK>Next>Next>Finish). Получаем шаблон приложения с заготовленным кодом. Запускаем его, открывается окно:

Посмотрим на исходный код в файлах проекта:

Файл main.cpp

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

В первой строке подключается заголовочный файл QtGuiApplication2.h, который  расположен непосредственно в созданном проекте.В второй строке подключается заголовочный файл QApplication, который  расположен в модуле QtWidgets:

 

В файле main.cpp сначала создается объект класса QApplication, который осуществляет контроль и управление приложением. Для его создания в конструктор этого класса необходимо передать два аргумента. Первый аргумент представляет собой информацию о количестве аргументов в командной строке, с которой происходит обращение к программе, а второй — это указатель на массив символьных строк, содержащих аргументы, по одному в строке. Любая использующая Qt-программа с графическим интерфейсом должна создавать только один объект этого класса, и он должен быть создан до использования операций, связанных с пользовательским интерфейсом.  После создания элементы управления Qt по умолчанию невидимы, и для их отображения необходимо вызвать метод show().

В последней строке программы приложение запускается вызовом QApplication::exec(). С его запуском приводится в действие цикл обработки событий, определенный в классе QCoreApplication, являющимся базовым для QApplication. Этот цикл передает получаемые от системы события на обработку соответствующим объектам. Он продолжается до тех пор, пока либо не будет вызван статический метод
QCoreApplication::exit(), либо не закроется окно последнего элемента управления. По завершению работы приложения метод QApplication::exec() возвращает значение целого типа, содержащее код, информирующий о его завершении.

Файл QtGuiApplication2.h

#pragma once
#include <QtWidgets/QMainWindow>
#include "ui_QtGuiApplication2.h"
class QtGuiApplication2 : public QMainWindow
{
 Q_OBJECT
public:
 QtGuiApplication2(QWidget *parent = Q_NULLPTR);
private:
 Ui::QtGuiApplication2Class ui;
};

В первой строке подключается заголовочный файл QMainWindow, который  расположен в модуле QtWidgets.

В файле QtGuiApplication2.h описывается класс QtGuiApplication2, производный от класса QMainWindow, предоставляемого системой Qt.  В разделе public объявляется конструктор.  В разделе private создается переменная ui, через которую устанавливается пользовательский интерфейс.

Макрос Q_OBJECT должен появляться в закрытом разделе описания класса, который объявляет свои собственные signals и slots (см. Signals и slots. Обработка событий) или использует другие сервисы, предоставляемые системой Qt.

Файл QtGuiApplication2.cpp

#include "QtGuiApplication2.h"
QtGuiApplication2::QtGuiApplication2(QWidget *parent):QMainWindow(parent)
{
 ui.setupUi(this);
}

В файле QtGuiApplication2.cpp определяется конструктор с передачей параметра parent конструктору базового класса.  Функция setupUi  берет на себя все действия по созданию этого (this) окна .

Изменяем код в файле main.cpp на следующий:

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

Запускаем приложение, получаем результат:

Изменяем код в файле main.cpp на следующий:

#include "QtGuiApplication2.h"
#include <qlabel.h>

int main(int argc, char *argv[])
{
 QApplication a(argc, argv);
 QLabel label("Hello, World!");
 label.show();
 QtGuiApplication2 w;
 w.setWindowTitle("Hello world");
 w.show();
 return a.exec();
}

Запускаем приложение, получаем результат:

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

Файл main.cpp

#include "QtGuiApplication2.h"
int main(int argc, char *argv[])
{
 QApplication a(argc, argv);
 QtGuiApplication2 w;
 w.setWindowTitle("Hello world");
 w.show();
 return a.exec();
 }

Файл QtGuiApplication2.h

#pragma once
#include <QMainWindow>
#include <qlabel.h>
#include "ui_QtGuiApplication2.h"
class QtGuiApplication2 : public QMainWindow
{
 Q_OBJECT
public:
 QtGuiApplication2(QWidget *parent = Q_NULLPTR);
private:
 QLabel *label;
 Ui::QtGuiApplication2Class ui;
};

Файл QtGuiApplication2.cpp

#include "QtGuiApplication2.h"
QtGuiApplication2::QtGuiApplication2(QWidget *parent):QMainWindow(parent)
{
 label = new QLabel("Hello world", this);
 //label->show();
 //ui.setupUi(this);
}

Запускаем приложение, получаем результат:

Signals и slots. Обработка событий

Для программирования GUI на языке С++  в ОС Windows обычно используется библиотека MFC.  При этом обработка событий реализуется через специальные макросы — так называемые карты сообщений, которые не имеют ничего общего с ООП и весьма неудобны. Проблема обработки событий решена в Qt более эффективно — при помощи механизма сигналов (signals) и слотов (slots).

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

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

В модели события участвуют три участника:

  • источник события (event source) — объект, состояние которого изменяется, он генерирует события;
  • объект события (event object) — инкапсулирует изменения состояния в источнике события;
  • слушатель события (event target) — объект, который хочет получить уведомление.

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

Создадим проект приложения под названием Click и изменим код файлов проекта.

Файл main.cpp

#include "Click.h"
int main(int argc, char *argv[])
{
 QApplication app(argc, argv);
 Click window;
 window.resize(250, 150);
 window.setWindowTitle("Click");
 window.show();
 return app.exec();
}

Файл Click.h

// #include <QPushButton>
// #include <QApplication>
// #include <QHBoxLayout>
#include <QtWidgets>
//#include "ui_Click.h"

class Click : public QMainWindow
{
 //Q_OBJECT
public:
 Click(QWidget *parent = Q_NULLPTR);
private:
 QHBoxLayout *hbox;
 QPushButton *quitBtn;
 //Ui::ClickClass ui;
};

Обратите внимание, что вместо трех заголовочных файлов (они закомментированы) можно подключать только  лишь модуль QtWidgets, в котором  эти файлы находятся. При большем количестве подключаемых файлов читаемость программы заметно ухудшается. Кроме того, подключение модулей дает возможность ускорить компиляцию самой программы за счет предварительно откомпилированных заголовочных файлов (Precompiled Headers) в том случае, если ваш компилятор позволяет это делать.

Файл Click.cpp

#include "Click.h"
Click::Click(QWidget *parent):QMainWindow(parent)
{
 //ui.setupUi(this);
 hbox = new QHBoxLayout(this);
 hbox->setSpacing(5);
 quitBtn = new QPushButton("Quit", this);
 hbox->addWidget(quitBtn, 0, Qt::AlignLeft | Qt::AlignTop);
 connect(quitBtn, &QPushButton::clicked, qApp, &QApplication::quit);
}

Метод connect () подключает сигнал QPushButton::clicked к слоту QApplication::quit. Здесь QApp — это глобальный указатель на объект приложения. Он определен в заголовочном файле <QApplication>

В общем виде, вызов метода connect() выглядит следующим образом:

QObject::connect(const QObject* sender,
                                 const char* signal,
                                 const QObject* receiver,
                                 const char* slot,
                                Qt::ConnectionType type = Qt::AutoConnection
);

Ему передаются пять следующих параметров:

  1. sender — указатель на объект, отправляющий сигнал;
  2. signal — это сигнал, с которым осуществляется соединение;
  3. receiver — указатель на объект, который имеет слот для обработки сигнала;
  4. slot — слот, который вызывается при получении сигнала.
  5.  type — управляет режимом обработки.

Имеется три возможных значения параметра type: Qt::DirectConnection — сигнал обрабатывается сразу вызовом соответствующего метода слота; Qt::QueuedConnection — сигнал преобразуется в событие и ставится в общую очередь для обработки; Qt::AutoConnection — (значение по умолчанию) это автоматический режим, который действует следующим образом: если отсылающий сигнал объект находится в одном потоке с принимающим его объектом, то устанавливается режим режим Qt::DirectConnection, в противном случае — режим Qt::QueuedConnection.

Далее см. примеры в статье Events and signals in Qt5 :

  • KeyPress
  • QMoveEvent
  • Disconnecting a signal
  • Timer

Qt Desiner. Создание GUI в интерактивном режиме

Создаем проект Qt GUI приложения (File>New>Project>Visual C++>Qt>Qt GUI  Application>OK>Next>Next>Finish). Получаем шаблон приложения с заготовленным кодом. В окне Solution Explore выполняем dblclick на файле QtGuiApplicatin3.ui.

Откроется редактор Qt Desiner. С панели виджетов перетаскиваем на заготовку окна две кнопки Push Button и меняем надписи на кнопках (через dblclick):

Пиктограммой переключаемся из режима работы с виджетами в режим «Изменение сигналов/слотов»

Выполняем настройки соединения сигнала clicked() кнопки Quit со слотом close() через окно настроек. Окно открывается, когда отпускаем левую кнопку мышки  после движения курсора от кнопки Quit.

В окне «Редактор Сигналов/Слотов» (окно расположено справа внизу редактора Qt Desiner) отражается результат соединения сигнала и слота

Сохраняем результаты роботы в редакторе Qt Desiner (Файл>Сохранить), возвращаемся в редактор VS и запускаем приложение:

Нажимаем кнопку «Quit», окно приложения закрывается.

Кнопка «Hello» пока еще не соединена ни с каким слотом. Переключаемся снова в редактор Qt Desiner и подготавливаем новый (нестандартный) слот:

Подключаем новый слот к кнопке «Hello»

Теперь нужно добавить реализацию этого слота (метода slot1 ()) в программном коде:

 

Изменяем код файлов проекта:

Файл QtGuiApplication3.h

#pragma once

#include <QtWidgets/QMainWindow>
#include "ui_QtGuiApplication3.h"
#include <qmessagebox.h>
#include <memory>

class QtGuiApplication3 : public QMainWindow
{
 Q_OBJECT
 std::unique_ptr<QMessageBox>gmb;
public:
 QtGuiApplication3(QWidget *parent = Q_NULLPTR);
private slots:
 void slot1()
 {
 gmb->show();
 }
private:
 Ui::QtGuiApplication3Class ui;
};

std::unique_ptr – «умный указатель», который получает единоличное владение объектом через его указатель, и разрушает объект через его указатель, когда unique_ptr выходит из области видимости.

Файл QtGuiApplication2.cpp

#include "QtGuiApplication3.h"

QtGuiApplication3::QtGuiApplication3(QWidget *parent)
 : QMainWindow(parent)
{
 gmb = std::make_unique<QMessageBox>(this);
 gmb->setText("Hello, Qt Desiner");
 ui.setupUi(this);
}

Запускаем программу, нажимаем кнопку «Hello» и получаем результат:

Полезные ссылки:

 

 

Автор: Николай Свирневский

 

 

 

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

Ваш e-mail не будет опубликован. Обязательные поля помечены *