Визуальный дизайн можно делать быстро, а код — оставлять чистым и расширяемым. Вы узнаете секрет, как отделить интерфейс от логики так, чтобы изменения в форме не ломали архитектуру и экономили часы на каждом итерационном шаге.
Будут разобраны 4 способа подключения ui-форм (от прямого до динамической загрузки через QUiLoader), плюс работа с Property Editor, компоновками (layout), tab order и быстрым связыванием signals/slots в режиме F4.
Если этот инструмент уже есть в вашем арсенале — вы перестанете “рисовать UI кодом” там, где это не нужно. Если нет — есть риск продолжать делать вчерашнюю работу вчерашними средствами.
В этой главе вы найдёте готовые к использованию примеры кода.
Самопроверка по главе
Почему при использовании прямого способа (direct approach) невозможно реализовать дополнительную логику, например слот для кнопки Reset?Ответ
Правильный ответ: Потому что форма создаётся через setupUi() напрямую без создания собственного класса, и нет места для добавления пользовательских методов и слотов.
Какое главное преимущество множественного наследования перед обычным наследованием при работе с формами?Ответ
Правильный ответ: Возможность обращаться к виджетам формы напрямую (например, m_sld) вместо использования префикса m_ui (m_ui.m_sld), что упрощает код.
Зачем нужно использовать Spacer (заполнитель пространства) при создании компоновки?Ответ
Правильный ответ: Чтобы сохранить свободное пространство между виджетами и обеспечить корректное масштабирование при изменении размеров окна, не допуская растягивания виджетов на всю доступную область.
Почему при использовании QUiLoader необходимо явно проверять возвращаемый указатель на null?Ответ
Правильный ответ: Метод load() вернёт нулевой указатель при ошибках в XML-данных ui-файла, и использование невалидного указателя приведёт к краху приложения.
Что произойдёт, если не указать ui-файл в секции FORMS qmake-проекта или не добавить его в список исходников CMake?Ответ
Правильный ответ: Система сборки не создаст автоматически заголовочный файл ui_*.h из ui-файла, что сделает невозможным использование формы в коде.
В чём разница между режимами Edit Widgets и Edit Signals/Slots в Qt Designer?Ответ
Правильный ответ: Edit Widgets предназначен для добавления виджетов и изменения их свойств, а Edit Signals/Slots — для визуального соединения сигналов виджетов со слотами других виджетов.
Почему важно задавать имена виджетов в поле objectName в Qt Designer?Ответ
Правильный ответ: Эти имена становятся именами атрибутов класса в сгенерированном коде, через которые происходит доступ к виджетам для соединения с сигналами/слотами и управления ими.
Какой практический смысл применения компоновок (layouts) вместо фиксированного позиционирования виджетов?Ответ
Правильный ответ: Компоновки автоматически пересчитывают размеры и позиции виджетов при изменении размера окна, обеспечивая адаптивность интерфейса и корректное отображение на разных разрешениях экрана.
Как функция Promote to… решает проблему использования пользовательских виджетов в Qt Designer?Ответ
Правильный ответ: Она заменяет стандартный виджет (например, QWidget) на пользовательский класс в сгенерированном коде, подключая соответствующий заголовочный файл без необходимости интеграции виджета в Qt Designer.
Почему динамическая загрузка формы через QUiLoader не требует включения ui-файла в секцию FORMS?Ответ
Правильный ответ: Потому что ui-файл используется как ресурс напрямую во время выполнения, а QUiLoader интерпретирует XML на лету, создавая виджеты без предварительной генерации заголовочного файла.
Зачем устанавливать флажок “Show signals and slots inherited from QWidget” при соединении кнопки Quit со слотом close()?Ответ
Правильный ответ: Слот close() не принадлежит непосредственно форме, а унаследован от базового класса QWidget, поэтому без этого флажка он не отображается в списке доступных слотов.
В каком сценарии имеет смысл выбрать динамическую загрузку формы вместо трёх статических способов?Ответ
Правильный ответ: Когда нужна возможность изменять интерфейс без перекомпиляции приложения, например, для загрузки разных версий форм или создания редактируемых пользователем интерфейсов.
Почему метод findChild<Тип*>(“ИмяОбъекта”) критически важен при динамической загрузке форм?Ответ
Правильный ответ: При динамической загрузке нет прямого доступа к виджетам через атрибуты класса, и findChild() — единственный способ получить указатели на виджеты по их именам для дальнейшей работы.
Практические задания
Простой уровень
Калькулятор с историей операций
Создайте в Qt Designer форму калькулятора с двумя полями ввода (QLineEdit), четырьмя кнопками операций (+, -, *, /) и меткой (QLabel) для отображения результата. Реализуйте приложение используя способ наследования (inheritance approach), где каждая кнопка соединена со слотом, выполняющим соответствующую операцию.
Подсказки: Используйте вертикальную компоновку для полей ввода, горизонтальную — для кнопок операций. Примените методы text() и setText() для работы с текстом. Преобразуйте строки в числа через toDouble(). В классе реализуйте слоты calculate() для каждой операции.
Средний уровень
Редактор настроек с валидацией
Создайте форму настроек приложения с использованием множественного наследования. Форма должна содержать: поле ввода имени пользователя, спиннер для возраста (QSpinBox), комбобокс выбора языка (QComboBox), чекбокс “Запомнить настройки” и две кнопки “Применить” и “Отмена”. Используйте функцию Promote to… для интеграции пользовательского виджета валидации email. Реализуйте проверку корректности данных перед применением.
Подсказки: Создайте класс EmailValidator, наследованный от QLineEdit, с методом isValid(). В Qt Designer разместите QLineEdit и примените Promote to EmailValidator. Используйте setRange() для QSpinBox (18-100). Кнопка “Применить” должна быть активна только при валидных данных. Для этого соедините сигналы textChanged() и valueChanged() со слотом проверки.
Сложный уровень
Система динамической загрузки плагинов интерфейса
Разработайте приложение, которое динамически загружает различные ui-формы из ресурсов или файловой системы через QUiLoader. Создайте главное окно с QListWidget, отображающим доступные формы-плагины. При выборе элемента списка соответствующая форма должна загружаться и отображаться в QStackedWidget. Реализуйте автоматическое обнаружение всех виджетов загруженной формы и вывод их списка в QTreeWidget с иерархией. Добавьте возможность динамического соединения сигналов виджетов формы с универсальным слотом-логгером.
Подсказки: Храните ui-файлы в ресурсах (qrc) или читайте из директории. Используйте QUiLoader::load() для создания виджетов. Метод findChildren() поможет найти все дочерние виджеты рекурсивно. Для иерархии используйте QObject::parent(). Динамическое соединение сигналов реализуйте через QMetaObject::connectSlotsByName() или вручную через connect() с QMetaMethod. Не забудьте обработать ошибки загрузки ui-файлов.
💬 Присоединяйтесь к обсуждению!
Освоили Qt Designer и способы интеграции форм в проекты? Какой подход показался вам наиболее удобным — множественное наследование или динамическая загрузка?
Возможно, у вас возникли вопросы о том, когда использовать QUiLoader, а когда компилировать формы статически? Или вы нашли интересные применения функции Promote to… для своих виджетов?
Поделитесь своим опытом, задайте вопросы или помогите другим читателям разобраться с практическими заданиями!