перенос qt приложения с qt 4 на qt5
Портируем Qt4 приложение на Qt5
Где-то здесь не так давно был пост о нововведених в Qt5. Всё вроде выглядит замечательно, но как же обстоят дела на самом деле в отношении уже имеющихся приложений? В этой статье я рассмотрю пример портирования одного из своих проектов на Qt5 с сохранением совместимости исходников с Qt4.
Итак, как и следовало ожидать мое GUI приложение не собирается. И не собирается оно потому, что я использую стандартные Qt виджеты. После недолгих разбирательств выясняется, что виджеты сейчас в отдельном модуле и должны быть явно включены. Сделаем это совместимым с Qt4 способом — добавим следующий код в наш файл qmake проекта (*.pro|*.pri):
Также с этого момента благодаря добавленноу дефайну можно в любом месте своего кода вставить #ifdef HAVE_QT5 и далее написать код специфичный для Qt5. Того же эффекта с ifdef можно было добиться создав precompiled header с содержимым:
И опять мой проект не собирается. На этот раз причина в отсутствующих дефайнах Q_WS_*. С Q_WS_WIN и Q_WS_MAC вроде бы всё предельно ясно, просто меняем их на Q_OS_WIN и Q_OS_MAC соответственно. Но что же делать с Q_WS_X11? Довольно часто Q_WS_X11 используется как просто признак юникс-подобных систем за исключением Mac OS (X), но и бывает так, что под этим дефайном скрывается код специфичный для X11. Но в моем случае это в общем-то одно и тоже, так что можно просто заменить на какой-либо другой дефайн (просто переопределить Q_WS_X11 мы не можем, так как некоторые части Qt всё ещё используют это определение) и объявить его в положенном месте, в файле проекта например:
Идем дальше. Проект у меня довольно старый и в нем используется множество устаревших классов или методов классов или чего угодно, что уже выброшено или запрещено в Qt5. Сборка опять не идет. Часть из того, что устарело, можно найти в документации по Qt5. Как правило в той же документации сказано, что надо использовать взамен, хотя есть и исключения, которые я рассмотрю ниже. Если же в документации по Qt5 не найдено устарвших классов и их методов, то вероятно это остатки Qt3 и их можно найти в документации на Qt4. Из того на что я наткнулся стоит отметить: QIconSet, QMenuItem (qt3. меняем на QIcon и QAction соответственно), QAbstractItemModel::reset (используем beginResetModel/endResetModel), QKeySequence больше не кастуется в int (делаем цикл по элементам последовательности), часть QUrl перекочевала в новый класс QUrlQuery, Qt::escape удален (свой inline враппер над Qt::escape и QString::toHtmlEscaped), qInstallMsgHandler(qInstallMessageHandler)…
Не ручаюсь за логическую верность реализации, так как до конца написания статьи код ещё не был протестирован.
Также у меня имеются нативные обработчики событий, такие как QApplication::x11EventFilter например. В Qt5 конечно же их пришлось переписать. Для этого нам понадобится QAbstractNativeEventFilter и немного знаний программирования xcb (Xlib здесь не сработает так как QPA о нем ничего не знает). В Принципе, переход на xcb не слишком сложен ввиду схожести API этих двух библиотек, но мануалами запастись не помешает. В моем случае реализация была довольно тривиальна: в своем классе приложения рядом с x11EventFilter добавил ещё один метод под названием xcbEventFilter и несколько ifdefов для компиляции только нужного метода. Далее создал класс унаследованный от QAbstractNativeEventFilter и из него просто обработку всех xcb событий перенаправил на наш метод:
Если душе угодно инициализацию приложением можно заменить кастом сингелтона приложения в nativeEventFilter.
Далее плагины. В Qt5 они загружаются другим, несовместимым способом и объявлены должны быть тоже по-другому. Сделана такая неприятная поломка в целях оптимизации (сейчас необязательно грузить весь плагин как полноценную разделяемую библиотеку, что бы убедиться что это вообще Qt плагин и что бы получить из него какую-либо мета информацию, такую как, например, версия программы, с которой данный плагин совместим. подробнее здесь), но чинить тем не менее её всё равно надо.
Для начала, все макросы экспорта наших плагинов делаем условными примерно так:
Также в классе плагина условно добавляем новый специфичный для Qt5 макрос Q_PLUGIN_METADATA где-нибудь рядом с Q_INTERFACES но после Q_OBJECT:
Часть FILE «myplugin.json» необходима только если нам на самом деле нужны метаданные в плагине, а что касается интерфейса «tld.domain.Project.MyPluginInterface», то это тот же интерфейс, что и в Q_DECLARE_INTERFACE. В моем случае в метаданных будут храниться: версия плагина, минимальная версия самой программы и приоритет загрузки. Также надо не забыть добавить магию с объявлением HAVE_QT5 в файлы проектов плагинов, ну или, как быстрый вариант без магии, использовать #if QT_VERSION >= 0x050000.
В случае же статических плагинов, придется поменять вызов Q_IMPORT_PLUGIN макросов. В качестве параметра они сейчас принимают имя класса плагина, а не то, что было первым параметром в Q_EXPORT_PLUGIN2.
Итак, запуск! И, как следовало ождать, segfault. Мой код ожидает, что QMetaType::Void == 0, но в Qt5 это не так. Отлично, исправляем, запускаем и снова segfault. На этот раз проблема в том, что некоторые типы объявлены в одном месте, а Q_DECLARE_METATYPE для них — в другом. Из-за чего последний, даже при явно включенном хидере с типом, работает неправильно. Я не стал разбираться в чем именно загвоздка, просто перенес Q_DECLARE_METATYPE для типов в их заголовочные файлы. И снова запуск — работает!
Программа запустилась, но работы ещё много. Переход на xcb таки должен быть полноценным, т.е. мой класс X11Info должен быть переписать с использованием xcb. Также надо проверить на работоспособность всё, что было пропатчено, впрочем, как и не пропатченное. Но, я надеюсь, самое сложное уже позади!
Надеюсь мой опыт будет вам полезен. Ниже приведу несколько ссылок, которые мне помогли в решение Qt5 головоломок:
Переход с Qt4 на Qt5
Интересует вот какой вопрос: есть ли смысл переводить проект с Qt4 на Qt5.
Наиболее интересно:
— много ли геммороя будет с перевод?
— какие плюсы может дать перевод?
— если кто уже переводил проекты, то прошу отписаться какие подводные камни могут возникнуть?
Особенности проекта:
— Работа с COM-портами (сейчас через qextserialport и qserialdevice)
— работа с PostgreSQL (сейчас через pqxx)
— шифрование (qca)
— сборка под платформами Linux, Windows
— сериализация (json)
— работа с http (сейчас через QNetworkAccessManager)
— многопоточночть
Заранее спасибо отписавшимся.
Если собираетесь долго поддерживать, то переводить имеет смысл, но обязательно ли сейчас?
Если нет гуя, то из плюсов в первую очередь можно отметить новые, на порядок быстрые, сигналы/слоты.
Если есть гуй на виджетах и хотите перенести на QML, используя компоненты, аналогичные виджетовым то пока ещё можно повременить, так как нормального диалога сохранения файла там нет до сих пор.
Для com портов средства есть, то, что вы назвали, по-моему и в Qt5 работает, pqxx как работал, так и останется работать, qca также. В Qt5 есть свои методы для работы с JSON. Сетевой стек вроде претерпел некоторые изменения, точнее не скажу.
Со кросс-сборкой проблем не будет.
— Работа с COM-портами (сейчас через qextserialport и qserialdevice)
Строго рекомендуется перейти на QtSerialPort..
Если есть возможность использовать c++11, то смысл переходить имеет: новый механизм сигналов вместе с лямбдами существенно упрощает и укорачивает код. Проблем с переносом виджетов быть не должно. В остальном могут быть какие-то изменения.
А если используется стандартный Qt-шный гуй, есть проблемы?
Нет, оно еще слишком сырое и падучее.
При переводе, некоторые вещи стали неожиданно депрекейтед
К примеру, setCodecForCStrings, setCodecForTr и подобное.
Ну и с цмейком поменяли достаточно сильно.
А так, в общем, не доставило большого труда.
Спасибо всем отписавшимся. Для себя решил пока подождать.
Я не понимаю, а чего ждать то, когда рак на горе свистнет? 🙂
Иди когда будет некогда или поздно переводить?
OMG, как же дальше жить-то? альтернативы есть?
Утф во все поля. 🙂
Хотя я сам был в шоке.
Из замеченых — небольшое повышение производительности.
— если кто уже переводил проекты, то прошу отписаться какие подводные камни могут возникнуть?
Не заметил таких. Хотя проект не большой,
А ждать когда скажут: «Всё Qt5 полностью готов! Куча новых фич! Пофикшены все баги и тормоза!»
А серьезно ждать пока появиться свободное время. Я и так не слишком много плюсов видел в переходе, а многие говорят что пока сыровато. Так что в данный момент не хочу заморачиваться.
Если нет гуя, то из плюсов в первую очередь можно отметить новые, на порядок быстрые, сигналы/слоты.
Где почитать бенчмарки и с какой стати они стали быстрее? А то я на Qt5 но ими пока не пользуюсь. И что, даже в queued режиме между потоками они быстрее стали?
Мысли по поводу Qt 5
Qt 4.0 была выпущена в июне 2005 года, почти шесть лет назад. Многое изменилось в индустрии программного обеспечения за эти годы. Тогда разработка приложений шла в основном на настольных системах, сейчас же мобильные устройства, подключенные к сети, становятся все более популярными. Технология пользовательского интерфейса перешла от статических виджетов к плавным сенсорным. Начиная с Qt 4.0, мы выпустили семь минорных версий Qt, следуя потребностям разработчиков и пользователей, например, разработав Qt Quick. С растущей пользовательской базой Qt, растёт потребность во встроенных, мобильных приложениях и UI-разработчиках.
Кроме того, в будущем, чтобы быть ведущим фреймворком для разработчиков в нескольких отраслях, Qt необходимо непрерывно обновляться и развиваться. Qt 4 был эволюцией, поэтому я задумался о том, как могут выглядеть следующие версии Qt с технической точки зрения. Последние годы мы работали над созданием основы для следующей мажорной версии. Я вижу в ней Qt Quick, QML Scenegraph и проект Lighhouse в сочетании с усилением акцента на Qt Webkit как фундамент, который мы планируем использовать для перехода к новому мажорному релизу Qt.
Учитывая, что Qt управляется открыто, я хотел бы поделиться своими размышлениями с сообществом Qt, чтобы начать дискуссии о технической архитектуре Qt 5.
Цели следующей мажорной версии Qt (Qt 5)
Сделать несложным переход с Qt 4 на Qt 5
В Qt 5 мы планируем сделать некоторые изменения в API и оставить позади ограничения из прошлого, чтобы было лучше в будущем. Для тех из вас, кто был с нами при переходе Qt 3 на Qt 4, мы не планируем повторять подобные трудности перехода в Qt 5. Мы считаем, что можем сохранить совместимость в большинстве случаев, но потери бинарной совместимости не избежать. Мы сделаем все, чтобы избежать нарушения каких-либо основ и сделать переход с Qt 4 на Qt 5 очень простым для большинства приложений.
Qt 5 будет сосредоточено на небольшом наборе операционных систем/платформ (т.е. платформ Wayland и X11 на Linux, Mac и Windows). Общее число платформ будет зависеть от усилий открытого сообщества, вложенных в Qt. Другие операционные системы, в настоящее время поддерживаемые Qt 4 (особенно коммерческие системы UNIX), не будут в центре внимания для Nokia. Целью проекта Qt 5 является предоставление наилучшей функциональности на каждой платформе, что означает, что Qt начнёт предлагать более дифференцированные возможности на разных ОС, в то же время предлагая эффективное повторное использование большей части кода на разных платформах.
Открытая разработка вместе с вами при сильной поддержке Nokia
Другим важным изменением в Qt 5 будет модель разработки. Qt 4 был разработан в основном в Trolltech и Nokia, затем опубликован для сообщества разработчиков. Qt 5 мы планируем развивать открыто, как проект с изначально открытым исходным кодом. Не будет никаких различий между разработчиками, работающими в Nokia и другими участниками.
Если вы или ваша компания хотите принять участие в разработке Qt 5, пожалуйста, присоединяйтесь к саммиту разработчиков Qt в Берлине с 16 по 18 июня. Это основное место, где мы хотели бы обсудить вместе с вами планы и идеи для Qt 5.0 и 5.1. Некоторые из нас также будут на саммите разработчиков Ubuntu на этой неделе и конференции MeeGo в конце этого месяца.
Обзор
Qt 5 должна быть основой для нового способа разработки приложений. Пока вся мощность нативности Qt в использовании C++, предлагается сфокусироваться на переходе к модели, где C++ используется в основном для реализации модульного бэкэнда функциональности для Qt Quick.
В Qt 5 мы должны поместить Qt Quick в центр Qt без разрушительных последствий для существующего кода, разработанного для Qt 4. Это будет, скорее, реструктуризацией, что позволит нам изменить способ разработки приложений в будущем.
Хотя традиционные Qt/C++ приложения и будут продолжать работать с Qt 5, но произойдут некоторые фундаментальные изменения в том, КАК приложения могут быть и будут написаны.
Следует ожидать, что с течением времени все интерфейсы будут написаны на QML. JavaScript основным в сообществе Qt, и мы должны ожидать, что большая часть логики приложений и даже целые приложения будут написаны на JavaScript, а не C++. Ожидается, что многие разработчики приложений на уже сейчас начнут с QML и JavaScript, и будут реализовывать функции на C++ лишь тогда, когда это требуется. В некоторых случаях, вся мощь C++ API, предлагаемая Qt, может быть использована для реализации критичных по времени и сложных по функциональности приложений.
Хотя мы и предлагаем поддержку основанной на QWidget модели программирования и набор API для совместимости, в долгосрочной перспективе мы также видим QML как будущее пользовательских интерфейсов на рабочем столе. Решением здесь является основанные на QML наборы компонентов, которые интегрируются с нативными API стилизации на настольных платформах.
Четыре больших архитектурных изменения
Qt Components и Qt Mobility теперь станут неотъемлемой частью платформы Qt, а не модулями со специальным статусом.
Бета-версии доступны к концу 2011 года. Финальный релиз в 2012 году
То, что мы не хотим слишком изменить основы Qt и тот факт, что мы хотим сделать переход существующих приложений на Qt 5 простым, заставляют нас быть осторожными с количеством изменений и существующей базой кода. Большую часть изменений мы уже вам предложили, а также мы начали работы над реструктуризацией нашей кодовой базы в новую модульную структуру, где каждая динамическая библиотека находится в своем собственном репозитории. Мы считаем, что мы должны удалить некоторые очень редко используемые API, которые сохранены для совместимости, но останавливают последующее развитие. Мы также считаем, что бета-версии будут доступны к концу года и окончательный релиз Qt 5.0 будет выпущен в 2012 году.
На прошлой неделе был выпущен Qt SDK с обновлениями, которые планируется использовать в предстоящем году для целевых устройств с Nokia Symbian и MeeGo. Qt 5.0 сосредоточена вокруг следующего поколения приложений и пользовательских интерфейсов, но серьёзных сложностей с переходом на эту версию возникнуть не должно.
Помогите нам ускорить разработку
Для тех из вас, кому интересны подробности, вот ссылка на официальный документ, более подробно описывающий некоторые из идей. Ничего из этого документа не закреплено окончательно, но он отражает наши текущие направления и мысли.
Вы можете следить за нашей работой в хранилищах Qt. Мы намерены поддерживать master-ветку пригодной для использования в любое время, по крайней мере на Linux с Wayland и X11 (плагин xcb lighthouse).
Возможно, что некоторые функции не будут в полной мере доступны в Qt 5.0 и появятся лишь с течением времени в последующих версиях, но надеемся, что существенной регрессии функциональности для Qt 4.8 не будет (да, мы намерены выпустить еще одну промежуточную версию в серии Qt 4.x в течение следующих нескольких месяцев!). При разработке Qt 5 мы должны сделать все от нас зависящее, чтобы сохранить совместимость с Qt 4, так чтобы портирование приложений на Qt 5 было как можно более простым. Так что, если вы хотите помочь или поучаствовать в разработке Qt 5, мы ждем вас на саммите разработчиков Qt в Берлине в июне этого года.
Transition from Qt 4.x to Qt5
En Ar Bg De El Es Fa Fi Fr Hi Hu It Ja Kn Ko Ms Nl Pl Pt Ru Sq Th Tr Uk Zh
The transition from Qt 4.x to Qt 5 is not expected to be significant. However, the «modularization» of the Qt code base requires some amount of changes to project configuration, such as use of «headers», and configuration of project build settings (such as changes to the *.pro files).
Contents
QtWidgets as a Separate Module
example compile time errors
error: QMainWindow: No such file or directory error: QToolButton: No such file or directory error: QWidget: No such file or directory
Solution
Add this in your *.pro file:
Change all instances of
The code should work now, though sometimes you may require to be more explicit:
QtWebKitWidgets is also a separate module:
example compile time errors
error: invalid use of incomplete type ‘class QWebFrame’ error: forward declaration of ‘class QWebFrame’
Solution
Add this in your *.pro file:
Note: when you have QT += webkitwidgets you don’t need QT += widgets
In addition, replace all instances of
You can try this by porting a WYSISWYG html editor from Qt 4 to Qt 5.
QPrinter Doesn’t Work
If your code has the following lines:
add the following to your project file:
Again, sometimes it may not work and you would need to be explicit:
toAscii() and fromAscii() Methods are deprecated
Replace all instances of
to fromLatin1() toLatin1()
For example, given the Qt 4 code QByteArray configfileti = TMP_Config.toAscii();
you would change to QByteArray configfileti = TMP_Config.toLatin1();
QCoreApplication::UnicodeUTF8 is deprecated
This enum type used to define the 8-bit encoding of character string arguments to translate(). This enum is now obsolete and UTF-8 will be used in all cases. So remove all instances of QCoreApplication::UnicodeUTF8. For example:
Href_Gui->setWindowTitle(QApplication::translate(«Href_Gui», «Url / www», 0, QApplication::UnicodeUTF8)); label->setText(QApplication::translate(«Href_Gui», «Text:», 0, QApplication::UnicodeUTF8)); label_2->setText(QApplication::translate(«Href_Gui», «Url:», 0, QApplication::UnicodeUTF8)); label_3->setText(QApplication::translate(«Href_Gui», «Target / Name:», 0, QApplication::UnicodeUTF8));
Href_Gui->setWindowTitle(QApplication::translate(«Href_Gui», «Url / www», 0)); label->setText(QApplication::translate(«Href_Gui», «Text:», 0)); label_2->setText(QApplication::translate(«Href_Gui», «Url:», 0)); label_3->setText(QApplication::translate(«Href_Gui», «Target / Name:», 0));
QWorkspace is deprecated
This class is obsolete and was replaced by the QMdiArea class in Qt 4.3. In Qt 5 QWorkspace has been removed. The new class has a similar API to QWorkspace and porting it only involved changing the names of a few methods, signals, and slots.
QDrag Problems
Apps that have drop and drag functionality will need some tweaking. A line such as
QDrag *drag = new QDrag(event->widget());
in Qt 5 will generate the error
error: no matching function for call to ‘QDrag::QDrag(QWidget*)’
To fix this add among the includes:
qFindChildren is deprecated
An error will pop of this fashion: error: ‘qFindChildren’ was not declared in this scope
To solve this you replace qFindChildren with findChildren, for example in
toString(const QObject* obj, int indentLevel) const < […]
QList childlist = qFindChildren (obj, QString());
QList childlist = obj->findChildren (QString());
qInstallMsgHandler is deprecated
The 4.8 function qInstallMsgHandler has been replaced with qInstallMessageHandler. The functions are similar, but the function signature of the `handler` has also changed, adding a very useful QMessageLogContext and replacing the char array with a QString reference.
Your compiler will complain
error: ‘qInstallMsgHandler’: identifier not found
void myhandler(QtMsgType type, const char* msg) <
int main(int argc, char** argv) <
void myhandler(QtMsgType type, const QMessageLogContext& context, const QString& msg) < //
int main(int argc, char** argv) <
qVariantValue is deprecated
Your compiler will say error: ‘qVariantValue’ was not declared in this scope
This function is equivalent to QVariant::value (value). Therefore if given a QVariant val rewrite the line QTime t = qVariantValue (val);
This QTime enclosed in the angled brackets lets the compiler know what QVariant will return. However, if the variable is not a QVariable the type enclosed in the angled brackets should not be used(doing so will result in a vague compile time error). So given that m_color is of type QColor you will rewrite
s.setValue(«color/favorite», qVariantValue (m_color));
qVariantCanConvert is deprecated
Q_ASSERT(qVariantCanConvert (variant)); Q_ASSERT(qVariantCanConvert (variant)); Q_ASSERT(qVariantCanConvert (fontVariant));
Q_ASSERT(variant.canConvert(QMetaType::QString)); Q_ASSERT(variant.canConvert(QMetaType::QSize)); Q_ASSERT(fontVariant.canConvert(QMetaType::QFont));
Qt::escape is deprecated
error: ‘escape’ is not a member of ‘Qt’
So you would change the following block:
if (result == QString())
if (result == QString())
this procedure can be automated by a porting tool from KDAB.
QDesktopServices::storageLocation deprecated
error: ‘storageLocation’ is not a member of ‘QDesktopServices’ error: ‘DataLocation’ is not a member of ‘QDesktopServices’
QString path = s.value(«db.path», QDesktopServices::storageLocation(QDesktopServices::DataLocation)).toString();
QString path = s.value(«db.path», QStandardPaths::standardLocations(QStandardPaths::DataLocation)).toString();
CONFIG+=qtestlib is deprecated
If you have the above line in your project file the compiler will warn you in the compile window, nonetheless the code will still run as usual:
Project WARNING: CONFIG+=qtestlib is deprecated. Use QT+=testlib instead.
QWeakPointer quirks
quint64 decodedPointer = line.toULongLong(); MetaData* md = reinterpret_cast (decodedPointer); QWeakPointer wp(md);
error: no matching function for call to ‘QWeakPointer ::QWeakPointer(MetaData*&)’
To fix this add to the project file:
QtConcurrent Library is Missing?
C:\Qt\5.0.2\5.0.2\mingw47_32\include\QtConcurrent\qtconcurrentthreadengine.h:133: error: undefined reference to `_imp___ZN12QtConcurrent16ThreadEngineBaseD2Ev’
In Qt 4, QtConcurrent was part of QtCore, so there was no need to include specific headers. This is no longer the case with Qt 5. If your source code has lines like
m_current = QtConcurrent::blockingMappedReduced(slices, functor, stitchReduce, QtConcurrent::UnorderedReduce );
You will need to include the header:
and add the following line to your project file:
_Headers»> Fixing #include<> Headers
A Perl script «fixqt4headers.pl» exists in qtbase/bin/. that should be run on source code using Qt that corrects the #include<> directives for Qt components to also consider the module name.
Plugin loading
The Q_EXPORT_PLUGIN,Q_EXPORT_PLUGIN2 macros have been deprecated in favor of the new Q_PLUGIN_METADATA macro. The advantage of the new system is that it allows Qt to query the metadata for the plugin without actually dlopen’ing it. This greatly improves performance and reliability of the plugin system.
The new Q_PLUGIN_METADATA macro is included next to the Q_OBJECT macro in the QObject derived class that is returned when loading the plugin. It contains the plugins IID and a filename pointing to a json file containing the metadata for the plugin. The json file is compiled into the plugin and does not need to be installed.
An example on how to change your plugins can be found by looking at the patch that changes the Gif image format plugin, see http://code.qt.io/cgit/qt/qtbase.git/commit/?id=963b4c1647299fd023ddbe7c4a25ac404e303c5d
Deploying to systems without C++11
QTimer is no longer accurate to the millisecond by default
QTimer has now 3 accuracy types, with a new default behaviour:
QUrl addQueryItem moved to QUrlQuery
If you have: QUrl url; // … url.addQueryItem(key, value);
You will want to change it to QUrl url; QUrlQuery urlQuery(url); //initialize with url to append to existing query, if present // … urlQuery.addQueryItem(key, value);
The same applies to the QUrl::getQueryItem() methods. They have been moved to QUrlQuery.
QAbstractItemModel changes
void reset() void setRoleNames(const QHash & roleNames) both have changed and are now obsolete.
QHeaderView changes
Use void QHeaderView::setSectionResizeMode(QHeaderView::ResizeMode mode) instead of void QHeaderView::setResizeMode(ResizeMode mode) The latter has been removed.