Каждый разработчик сталкивался с этим: текст в интерфейсе «плывёт», обрезается, выглядит по-разному на разных платформах или внезапно ломает аккуратную верстку. Шрифт кажется мелочью — до тех пор, пока именно он не начинает портить впечатление от всего приложения.
Эта глава обнаружит, что работа со шрифтами — это не про «выбрать Times или Arial». Здесь раскроем, как Qt на самом деле рассчитывает размеры текста, почему одни строки идеально вписываются в интерфейс, а другие — нет, и какой скрытый механизм позволяет получать предсказуемый результат без ручных подгонок. Вы узнаете секрет, который профессиональные разработчики используют, чтобы интерфейсы выглядели аккуратно и стабильно на Windows, Linux и macOS.
В главе задействованы 3 ключевых класса Qt для работы со шрифтами, показаны реальные измерения текста в пикселях, а также практические приёмы выравнивания, переноса и усечения строк — в том числе с интеллектуальным разрывом в середине. Результат: меньше костылей и до 3 раз меньше ручных правок UI.
Пропуск этой главы означает одно: вы продолжите гадать, почему текст ведёт себя «странно», вместо того чтобы точно управлять им.
В этой главе вы найдёте готовые к использованию примеры кода.
Самопроверка по главе
Почему масштабируемая гарнитура называется «идеальным математическим описанием» и как это влияет на вывод шрифта?Ответ
Правильный ответ: Она позволяет отображать шрифт без искажений в любом размере, а функции растеризации автоматически преобразуют математическое представление в растровую матрицу без дополнительных усилий разработчика.
Зачем в методе QFont("XYZ Font, Arial, Helvetica, sans-serif") указывать несколько шрифтов через запятую?Ответ
Правильный ответ: Это обеспечивает систему запасных шрифтов: если XYZ Font недоступен, Qt интеллектуально выберет Arial, затем Helvetica, затем любой sans-serif шрифт, что делает поведение более предсказуемым.
В чем разница между методами width() и horizontalAdvance() класса QFontMetrics?Ответ
Правильный ответ: Метод width() может принимать часть строки и количество символов для измерения, в то время как horizontalAdvance() измеряет полный размер всей переданной строки с учетом всех её символов.
Зачем использовать метод boundingRect() перед отображением текста?Ответ
Правильный ответ: Он возвращает объект QRect с размерами прямоугольной области, необходимой для отображения текста, что позволяет заранее определить геометрию и корректно спланировать размещение текста в интерфейсе.
Что означают значения, возвращаемые методами ascent() и descent(), и почему они важны?Ответ
Правильный ответ: ascent() возвращает максимальную высоту символа над базовой линией (включая диакритические знаки), а descent() — максимальное значение ниже базовой линии; они необходимы для точного вертикального выравнивания текста.
Почему флаг TextDontClip может быть проблематичным при выводе длинного текста?Ответ
Правильный ответ: Этот флаг гарантирует, что текст не будет обрезан даже при выходе за границы заданной области, что может привести к наложению текста на другие элементы интерфейса.
Как залить текст градиентом и почему градиент передается через объект QPen, а не QBrush?Ответ
Правильный ответ: Создается QLinearGradient с переходами цветов, затем передается в конструктор QPen, который устанавливается через setPen(). Перо используется, так как текст в QPainter рисуется как контур, а не заливается как фигура.
В каких практических ситуациях метод elidedText() предпочтительнее обрезки или переноса текста?Ответ
Правильный ответ: При отображении путей каталогов, длинных названий файлов или заголовков в ограниченном пространстве, где важно показать пользователю, что текст урезан, сохранив при этом начало и/или конец строки.
Почему метод elidedText() возвращает новую строку вместо изменения исходной?Ответ
Правильный ответ: Это сохраняет исходный текст неизменным для возможного последующего использования и соответствует принципам неизменяемости данных; отображаемое представление отделено от хранимых данных.
В чем разница между setFont() для виджета и QApplication::setFont()?Ответ
Правильный ответ: QWidget::setFont() устанавливает шрифт только для конкретного виджета, а статический метод QApplication::setFont() устанавливает шрифт по умолчанию для всего приложения.
Почему механизм мнемоник TextShowMnemonic работает по-разному на разных платформах?Ответ
Правильный ответ: Qt корректно обрабатывает амперсанды и назначает клавиши доступа, но видимость подчёркивания определяется платформой: в Windows 10/11 оно скрыто по умолчанию, в Linux зависит от темы, а в macOS мнемоники вообще не используются.
Что произойдет, если использовать флаг TextWordWrap вместе с TextSingleLine?Ответ
Правильный ответ: Возникнет конфликт флагов: TextSingleLine игнорирует символы новой строки, а TextWordWrap пытается перенести текст на новую строку, что приведет к непредсказуемому результату — следует использовать только один из этих флагов.
Для чего могут использоваться методы leftBearing() и rightBearing() класса QFontMetrics?Ответ
Правильный ответ: Они возвращают левое и правое пространство буквы в пикселах, что важно для точного позиционирования символов, особенно при создании кастомного текстового рендеринга или анимации отдельных букв.
Практические задания
Простой уровень
Информация о системных шрифтах
Создайте приложение, которое отображает список всех установленных в системе шрифтов в виджете QTextEdit. Для каждого шрифта выведите его название, используя сам этот шрифт. Размер шрифта должен быть 14pt.
Подсказки: Используйте класс QFontDatabase и метод families() для получения списка шрифтов. Для каждого шрифта создайте HTML-строку с тегом <font face=”…”>. Вставьте результат в QTextEdit методом setHtml().
Средний уровень
Анализатор метрик шрифта
Разработайте виджет, который позволяет пользователю выбрать шрифт и его размер (через комбобоксы), ввести текст в текстовое поле, и увидеть визуализацию метрик этого текста: базовую линию, ascent, descent, ширину, высоту. Используйте разные цвета для отображения различных характеристик. Выведите числовые значения метрик.
Подсказки: Создайте кастомный виджет с переопределенным paintEvent(). Используйте QFontMetrics для получения всех необходимых метрик. Рисуйте горизонтальные линии для базовой линии, ascent и descent разными цветами. Используйте QPainter::drawLine() и drawText() для визуализации.
Сложный уровень
Адаптивный текстовый виджет с эффектами
Создайте кастомный виджет для отображения текста, который автоматически адаптируется к размеру окна: при достаточном пространстве показывает полный текст с градиентной заливкой, при уменьшении размера применяет elided text (с выбором позиции разрыва), а при критически малом размере переключается на многострочный режим с переносом слов. Добавьте возможность выбора стиля градиента и позиции разрыва через контекстное меню.
Подсказки: Переопределите paintEvent() и resizeEvent(). В paintEvent() проверяйте текущую ширину виджета и применяйте соответствующую стратегию отображения. Для градиента используйте QLinearGradient с динамическими координатами, зависящими от размера виджета. Используйте fontMetrics().horizontalAdvance() для определения пороговых значений ширины. Добавьте contextMenuEvent() для меню настроек.
💬 Присоединяйтесь к обсуждению!
Разобрались с метриками шрифтов и нюансами отображения текста в Qt? Возникли вопросы о выборе между различными методами drawText() или о работе с градиентной заливкой?
Поделитесь своими находками в работе со шрифтами, расскажите о нестандартных решениях или помогите другим читателям понять тонкости QFontMetrics!