Хабрахабр

[Перевод] Портируем код с Qt 1.0 на Qt 5.11

Недавно вышел Qt 5.11 и мне подумалось, что сейчас самое время обновить до него кое-какие мои проектики на Qt 1.0… Ладно, шучу 🙂 На самом деле мне стало интересно, насколько хорошо за все эти годы развития фреймворка Qt нам удавалось сохранять обратную совместимость кода.

Вы не должны переписывать (или даже перекомпилировать) свой код при переходе на другую минорную версию Qt. Qt гарантирует совместимость на уровне кода и бинарников при обновлении между минорными версиями фреймворка (и мы серьёзно относимся к этому обещанию). С релиза Qt 1. Однако переходы между мажорными версиями требовали от нас идти на некоторые жертвы ради прогресса. 0, 3. 0 в 1996 году мы ломали совместимость кода четыре раза: в версиях 2. 0 (ох, это было болезненно!) и 5. 0, 4. 0.

Отсюда возникает вопрос: насколько сложно портировать приложение, написанное во времена Qt 1. Мы старались даже в мажорных версиях сломать как можно меньше всего, но всё же это приходилось делать. 11? 0 до современного Qt 5.

0 и постарался собрать его с помощью Qt 5. Для ответа на этот вопрос я взял пример кода, который поставлялся с документацией на Qt 1. 41, так что мне пришлось изрядно покопаться в дрейнейшей истории, пройти через логи четырёх разных систем контроля версий… но это я уже отвлекаюсь. Наши публичные архивы содержат изменения начиная с версии 1. Проект, который я планирую собрать, называется «t14» — поскольку это иллюстрация к 14-ой (и последней) главе оригинального руководства.

И вот, что мне пришлось проделать для его сборки.

Импортируем оригинальный проект: 10 files changed, 798 insertions(+)
Чистое произведение искусства: так в 1996-ом году официально рекомендовалось писать программы на Qt.

Он был написан на Perl. Переходим на qmake: 3 files changed, 2 insertions(+), 148 deletions(-)
image
До выхода qmake был tmake. Кроме того, tmake не был публично описанной частью фреймворка, так что для релизов мы готовили make-файлы. Базовый синтаксис был похож, только tmake ещё позволял включать прямо в файл проекта код на Perl, чего (к счастью!) уже нельзя сделать в qmake. Наличие совершенно разных внутренней и внешней системы сборки кода всегда добавляло в релизы элемент неожиданности.

Мы, конечно, могли ориентироваться на Unix и сделать нормальные имена, но, если код должен был быть портируемым — приходилось использовать «восхитительные» названия файлов типа «qscrbar.h» и «qbttngrp.h». Исправляем include-файлы: 4 files changed, 8 insertions(+), 8 deletions(-)
image
В те древние времена, когда писался данный пример, операционная система Windows ограничивала длину имени файла до 8 символов.

Добавляем отсутствующие include-файлы: 1 file changed, 3 insertions(+)
Зависимости от неявных инклюдов были и остаются проблемой.

Счастья своего не осознают! Меняем TRUE/FALSE на true/false: 1 file changed, 13 insertions(+), 13 deletions(-)
image
Ох уж эти сегодняшние дети. Мы были вынуждены писать свой собственный булевый тип данных, поскольку встроенного в язык у нас не было!

Да, классом, поскольку у нас тогда не было пространств имён. Исправляем ссылки на сущности, которые переехали в пространство имён «Qt»: 3 files changed, 15 insertions(+), 15 deletions(-)
image
«Qt» было добавлено в 1998 году и являлось тогда… классом.

Убираем аргумент «name»: 6 files changed, 26 insertions(+), 26 deletions(-)
image
Все конструкторы подклассов QObject принимали параметром имя объекта.

Использование индивидуальных сеттеров — значительно лучшая практика, чем конструктор, принимающий 7 аргументов. API класса QScrollBar изменился: 1 file changed, 5 insertions(+), 5 deletions(-)
image
Иногда нам приходилось избавляться от старых, плохо спроектированных API.

Раньше это была 8-битная строка в кодировке Latin1 с автоматическим преобразованием к const char*, так что кое-где API использовало аргументы типа const char*. Используем QString вместо const char*: 2 files changed, 2 insertions(+), 2 deletions(-)
image
QString существует в Qt с 1994 года. 0. Поддержка Unicode появилась в Qt 2.

Ну, разве что, если они начинаются на «Q». warning() теперь называется qWarning(): 1 file changed, 1 insertion(+), 1 deletion(-)
Мы вообще-то избегаем добавлять сущности в глобальное пространство имён. Эта буква принадлежит нам.

А в 1996 году многие дисплеи могли отображать лишь 8 бит на пиксель, так что для показа более 256 цветов нужно было ещё постараться. Убираем ненужные вызовы старых методов QApplication: 1 file changed, 2 deletions(-)
image
Qt сегодня многое делает сам, хорошо и автоматически.

А ещё его название — не аббревиатура. Заменяем QAccel на QShortcut: 1 file changed, 4 insertions(+), 3 deletions(-)
image
QShortcut — одновременно мощнее и проще. QShortcut был добавлено в 2004 году.

Сегодня у нас есть буферизация и композиция, так что всё становится и сложнее, и проще одновременно. Исправляем логику перерисовки: 1 file changed, 7 insertions(+), 7 deletions(-)
image
В 90-ые мы могли прямо рисовать на виджетах, когда захотим. Чуть больше кода, но намного логичнее. Мы отправляем запрос на изменение и позже, когда получим сигнал на перерисовку, рисуем.

Она убивала все таймеры объекта, включая и использовавшиеся внутри Qt. QObject::killTimers() больше не существует: 2 files changed, 3 insertions(+), 2 deletions(-)
image
Эта функция была слишком опасной. Сегодня вы должны убивать таймеры осознанно и индивидуально.

QWMatrix теперь называется QMatrix: 1 file changed, 2 insertions(+), 2 deletions(-)
Просто изменение названия класса.

Кроме того, дочерние виджеты теперь по-умолчанию прозрачны. Метод QWidget::setBackgroundColor() был удалён: 1 file changed, 3 insertions(+), 1 deletion(-)
image
Цвет фона виджета — уже не столь простая сущность: он инкапсулирован внутри QPalette вместе с другими свойствами. Мы должны рассказать Qt, как именно хотим рисовать фон.

Не получается заполнить pixmap содержимым виджета: 1 file changed, 1 insertion(+), 1 deletion(-)
image
Я использовал для этого прозрачный pixmap, поскольку теперь Qt их поддерживает.

Начиная с Qt 4. Рисование прямоугольников изменилось: 1 file changed, 1 insertion(+), 1 deletion(-)
image
Это, наверное, наиболее кардинальное изменение из всех вышеописанных. Таким образом, нам необходимо вычесть ширину пера (в нашем случае это 1) перед передачей прямоугольника в QPainter. 0 метод QPainter::drawRect() был изменён таким образом, чтобы «штриховочный прямоугольник имел размер rectangle.size() + ширина пера».

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

image

Выяснилось, что это произошло из-за жестко прописанных в коде размеров и позиций елементов, а размеры шрифтов, оказывается, поменялись с 1996 года. Ой… Кое-где текст выглядит обрезанным. 0 (первая версия появилась в Qt 1. Решением будет использование QLayout, который не был доступен во времена Qt 1. и была полностью переписана к выходу Qt 2. 1. 0).

Используем QLayout вместо жестко прописанных размеров: 4 files changed, 29 insertions(+), 24 deletions(-)
image

И вот с этим, последним, изменением всё, наконец, выглядит так, как и должно:

image

У нас получилось (и даже не заняло много времени) портировать код 22-летней давности с Qt 1. В чём же мораль этой истории? 11. 0 на Qt 5. У меня, наверное, больше времени заняло написание данной статьи, чем правки самого кода. Это возможно. Некоторые из них остались неизменными, другие были расширены и доработаны, но всё же сохранили узнаваемость. Большинство API всё ещё живы в последних версиях Qt даже спустя столько лет и версий. Все изменения были направлены на улучшения читабельности, безопасности, поддерживаемости и производительности приложений на Qt.

Теги
Показать больше

Похожие статьи

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

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

Кнопка «Наверх»
Закрыть