Фреймворк Qt для C++ разработчика: сигналы, слоты, виджеты и QML

Фреймворк Qt для C++ разработчика: сигналы, слоты, виджеты и QML Полезное

Qt — это кроссплатформенный фреймворк на C++ для приложений с графическим интерфейсом (GUI) и не только: в нем есть модули для сети, работы с файлами, потоков, баз данных и мультимедиа. Один и тот же исходный код собирается под Windows, Linux и macOS, а с дополнительными сборками — под Android, iOS и встраиваемые системы.

Ключевое отличие Qt от «просто библиотеки классов» — собственная объектная система поверх C++: базовый класс QObject, механизм сигналов и слотов и метаобъектный компилятор moc. Ниже разберем, как это устроено, чем виджеты отличаются от QML, как собрать проект и что важно знать про лицензию Qt 6.

Что такое Qt и что в него входит

Qt существует с середины 1990-х и вырос из библиотеки для GUI в большой набор модулей. На нем построена рабочая среда KDE Plasma, десктоп-клиент Telegram, продукты Autodesk и множество встраиваемых интерфейсов.

Различать стоит две вещи. Qt как библиотека — это классы C++ (QString, QWidget, QNetworkAccessManager и другие). Qt как SDK — это библиотека плюс инструменты вокруг нее:

  • Qt Creator — IDE для разработки, отладки и профилирования; работает на Windows, Linux и macOS.
  • Qt Designer — визуальный редактор форм для виджетов; раскладку сохраняет в .ui (XML).
  • Qt Linguist — инструмент локализации приложений на разные языки.
  • qmake и поддержка CMake — системы сборки проекта.

Для других языков есть привязки к библиотеке Qt: для Python это PyQt и PySide (официальная привязка от разработчиков Qt). Привязки для Ruby, Java и PHP исторически существовали, но давно почти не поддерживаются, поэтому для продакшена ориентироваться на них не стоит.

Объектная система: QObject и moc

Многие возможности Qt держатся на классе QObject. Если ваш класс наследует QObject и в теле стоит макрос Q_OBJECT, класс получает метаинформацию: имена методов, свойства, сигналы и слоты, доступные во время выполнения.

Эту метаинформацию генерирует не компилятор C++, а отдельный шаг сборки — moc (Meta-Object Compiler, метаобъектный компилятор). Он читает заголовки с макросом Q_OBJECT, находит signals: и slots: и генерирует дополнительный .cpp-файл, который собирается вместе с проектом. Поэтому сигналы и слоты — это не std::function и не шаблоны стандартной библиотеки, а расширение, встроенное через кодогенерацию.

#include <QObject>

class Counter : public QObject
{
    Q_OBJECT            // включаем метаобъектную систему: код добавит moc
public:
    int value() const { return m_value; }

public slots:
    void setValue(int v);   // слот - обычный метод, вызывается по сигналу

signals:
    void valueChanged(int newValue);   // объявляем, тело генерирует moc

private:
    int m_value = 0;
};

Слот — это обычный метод: у него есть тело, его можно вызвать напрямую. Сигнал — только объявление; его реализацию дописывает moc, а наш код лишь «испускает» сигнал через emit:

void Counter::setValue(int v)
{
    if (v == m_value)
        return;
    m_value = v;
    emit valueChanged(m_value);   // уведомляем всех подключенных слушателей
}

Сигналы и слоты: как связать объекты

Сигналы и слоты — это способ развязать объекты: отправитель сигнала не знает, кто его слушает. Связь задается функцией QObject::connect. В Qt 5 и Qt 6 рекомендуется синтаксис с указателями на методы — его проверяет компилятор:

Counter a, b;
QObject::connect(&a, &Counter::valueChanged,
                 &b, &Counter::setValue);

a.setValue(10);
// a испускает valueChanged(10) -> вызывается b.setValue(10)
// теперь b.value() == 10

Старый строковый синтаксис SIGNAL(...)/SLOT(...) тоже работает, но ошибки в именах он ловит только во время выполнения, поэтому для нового кода его не берут. Один сигнал можно соединить с несколькими слотами и наоборот — это удобно для реакции на события интерфейса.

Минимальное приложение на виджетах

Теперь соберем самое маленькое рабочее GUI-приложение на модуле Qt Widgets. Его можно скопировать целиком:

#include <QApplication>
#include <QPushButton>

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);   // создаем приложение и цикл событий

    QPushButton button("Нажми меня");
    button.resize(200, 60);
    button.show();

    // сигнал clicked кнопки соединяем со слотом quit приложения
    QObject::connect(&button, &QPushButton::clicked,
                     &app, &QApplication::quit);

    return app.exec();   // запускаем цикл обработки событий
}

Что произойдет: откроется окно с кнопкой «Нажми меня», нажатие на кнопку испускает сигнал clicked, тот вызывает слот quit, и приложение завершается. Ключевой момент — app.exec(): он запускает цикл событий, который слушает мышь, клавиатуру и таймеры. Без него окно мелькнет и сразу закроется.

Виджеты против QML: два способа делать интерфейс

В Qt два разных подхода к интерфейсу, и их важно не путать.

Qt Widgets — классический набор готовых элементов (QPushButton, QLineEdit, QTableView), описываемых на C++ или в Qt Designer. Хорошо подходит для десктопных приложений в стиле операционной системы: формы, таблицы, меню.

Qt Quick — более новый стек для гибких, анимированных интерфейсов. Разметку в нем пишут не на C++, а на декларативном языке QML с вкраплениями JavaScript для логики. Часто применяется в мобильных и встраиваемых интерфейсах, где нужны плавные анимации и нестандартный вид.

Признак Qt Widgets Qt Quick / QML
Язык интерфейса C++ (или Qt Designer) QML + JavaScript
Стиль нативный вид ОС кастомный, анимированный
Типичная область десктоп, формы, таблицы мобильные, встраиваемые, авто
Логика C++ C++ через регистрацию типов

Оба подхода могут работать в одном приложении: тяжелую логику пишут на C++, а интерфейс — на QML или виджетах. Выбор простой: нужен строгий десктоп в стиле системы — берут виджеты; нужен гибкий или мобильный интерфейс — берут Qt Quick.

Сборка проекта: qmake и CMake

Исторически Qt-проекты собирались через qmake и файл .pro. В Qt 6 основной и рекомендуемой системой сборки стал CMake — он лучше интегрируется со сторонними библиотеками и современными тулчейнами. qmake по-прежнему поддерживается для существующих проектов, но новые обычно начинают на CMake.

cmake_minimum_required(VERSION 3.16)
project(hello LANGUAGES CXX)

find_package(Qt6 REQUIRED COMPONENTS Widgets)
qt_standard_project_setup()   # включает автозапуск moc и др.

qt_add_executable(hello main.cpp)
target_link_libraries(hello PRIVATE Qt6::Widgets)

Функция qt_standard_project_setup() автоматически подключает запуск moc, uic (обработка .ui) и rcc (ресурсы), поэтому вручную вызывать метаобъектный компилятор не нужно.

Лицензирование Qt 6

Qt распространяется под несколькими лицензиями сразу, и это влияет на то, как его можно применять в коммерческом продукте:

  • LGPLv3 — открытая лицензия; позволяет использовать Qt в закрытом приложении при выполнении условий (в первую очередь — динамическая линковка библиотек и возможность их замены пользователем).
  • GPLv3 — открытая лицензия с требованием открывать исходный код производного продукта.
  • Коммерческая — платная лицензия без обязательств открывать код; включает часть модулей и инструментов, недоступных в открытой версии.

Практический вывод: для проприетарного приложения обычно выбирают LGPL с динамической линковкой либо коммерческую лицензию. Состав модулей меняется между версиями, поэтому условия сверяют с текущим текстом лицензии Qt.

Выводы

  • Qt — кроссплатформенный C++ фреймворк: один исходник собирается под настольные, мобильные и встраиваемые платформы, и это не только GUI, но и сеть, потоки, БД, мультимедиа.
  • Основа Qt — класс QObject, макрос Q_OBJECT и метаобъектный компилятор moc; сигналы и слоты реализованы через кодогенерацию, а не через std::function.
  • Сигналы и слоты развязывают объекты: QObject::connect с указателями на методы — предпочтительный синтаксис в Qt 5 и Qt 6.
  • Виджеты и QML — два разных стека: виджеты для нативного десктопа на C++, Qt Quick/QML для гибких и мобильных интерфейсов.
  • В Qt 6 основная система сборки — CMake; лицензирование тройное (LGPL/GPL/коммерческая), условия зависят от способа линковки и модулей.

Где применяется / связь с практикой

Qt закрывает задачи, где нужен быстрый нативный интерфейс поверх C++: настольные инструменты (Qt Creator, продукты Autodesk, десктоп Telegram), встраиваемые панели в медтехнике и промышленности, приборные интерфейсы в автомобилях. Во всех случаях сначала требуется уверенный C++: классы, наследование, память, сборка — на этом фундаменте стоят QObject, сигналы и слоты.

Освойте тему на практике

Если хочется дойти до рабочего Qt-приложения, начинать логично с языка. На курсе C++ Basic разбирают синтаксис, классы и сборку — базу, без которой Qt воспринимается как магия. Посмотреть формат и темы вживую можно на открытых уроках: там показывают код и отвечают на вопросы в прямом эфире.

FAQ

Нужно ли ставить QML, если я делаю обычное десктоп-приложение?
Нет. Для классического десктопа достаточно модуля Qt Widgets на C++. QML и Qt Quick берут, когда нужен кастомный вид, анимации или мобильный интерфейс. Оба стека ставятся вместе с Qt, но использовать можно только нужный.

Можно ли писать на Qt без глубокого знания C++?
Через привязки PyQt или PySide интерфейс делают на Python. Но сам фреймворк проектировался под C++, и большинство документации, примеров и вакансий завязаны на него, поэтому C++ остается базовым навыком для Qt.

Зачем нужен moc и почему нельзя обойтись стандартным C++?
Сигналы, слоты и свойства требуют метаинформации об объекте во время выполнения, которой в стандартном C++ нет. moc генерирует эту информацию как дополнительный код на этапе сборки; при использовании qmake или CMake он вызывается автоматически.

OTUS Журнал
Скидка 5% 14-20 сентября на курсы (popup)