В заметке представлен способ сборки программ использующих библиотеку Qt. Рассмотрены вопросы локализации приложения и подключения ресурсов, например изображений.
Помимо файлов с примерами есть файл с шаблоном приложения, используя который можно быстро создать достаточно сложный проект.
Для лучшего понимания материала, рекомендую прочитать введение в систему сборки приложений CMake.
Для проекта я рекомендую использовать следующую структуру директорий:
build CMakeLists.txt resources images *.jpg *.gif ... translations *.ts *.qm ... resources.qrc [libraries] [sources] [*.cpp] [*.h] |
Таким образом в проекте существует директория build с файлом для системы сборки, также в этой директории создаются файлы для сборки, файлы генерируемые Qt и здесь же будет находится собранное приложение.
Второй важной директорией является директория resources, в корне которой находится XML файл resources.qrc содержащий список файлов с ресурсами включаемыми в приложение и директориями с ресурсами, например изображениями (images) и файлами с переводом на разные языки (translations).
Также в главной директории могут располагаться директории с подпроектами, файлы и директории с кодом. Под подпроектами я понимаю отдельные директории в корне проекта, содержащие код компилируемый в библиотеки и директорию build с файлом CMakeLists.txt, с помощью которого можно собрать данную библиотеку.
В файле для CMake придется сделать следующие вещи:
- Найти и подключить библиотеки Qt
- Из файлов кода сгенерировать MOC-файлы и включить их в компиляцию
- Указать, что необходимо включать ресурсы в приложение
- Проверить не были ли изменены файлы с переводом и в случае изменений сделать из них бинарные файлы, которые в свою очередь включить в проект
Поиск и подключение библиотек Qt
Поиск и подключение библиотек в CMake производится при помощи специальных расширений, рассмотрение которых выходит за рамки заметки, пока достаточно знать, что в стандартной поставке модуль для Qt уже есть и его можно использовать:
find_package (Qt4 REQUIRED) |
После этого будет предпринята попытка найти Qt и в случае успеха будет создан ряд переменных содержащих полезную информацию. Сейчас интерес для нас представляют QT_USE_FILE и QT_LIBRARIES, которые содержат путь к заголовочным файлам и библиотекам Qt:
include (${QT_USE_FILE}) ... target_link_libraries (${PROJECT} ${QT_LIBRARIES}) |
Генерация MOC-файлов
Воспользовавшись макросом qt4_wrap_cpp можно из списка исходных кодов получить список MOC-файлов, который в свою очередь установить в зависимость к собираемому проекту:
qt4_wrap_cpp (MOC_SOURCES ${HEADERS}) ... add_executable (${PROJECT} ${HEADERS} ${SOURCES} ${MOC_SOURCES}) |
В результате выполнения qt4_wrap_cpp переменная MOC_SOURCES будет содержать список сгенерированных MOC-файлов.
Генерация кода из файлов с ресурсами и включение их в приложение
Макрос qt4_add_resources из файла resources.qrc для каждого описанного там файла сгенерирует файлы с кодом, которые можно будет внедрить в приложение. Список этих файлов, как нетрудно догадаться, будет в первой переменной:
qt4_add_resources (QRC_SOURCES ${RESOURCES}) ... add_executable (${PROJECT} ${HEADERS} ${SOURCES} ${MOC_SOURCES} ${QRC_SOURCES}) |
Файлы с переводом
Механизм локализации приложений Qt предлагает создание текстового файла с переводом (*.ts), работать с которым могут переводчики с помощью программы Linguist и последующей генерации из этих файлов бинарных ресурсов (*.qm) внедряемых в приложение.
Я использую следующий подход:
- ts-файлы хранятся в репозитории
- Эти файлы обновляются вручную (цель translations)
- С ними работают переводчики, поэтому перед комитом кода в репозиторий, необходимо получить последнюю версию файлов, обновить файлы (make translations) и только после этого заливать свой код
- При изменении ts-файла автоматически должны обновляться qm-файлы
- qm-файлы должны быть указаны в resources.qrc
Для этих целей я использую следующий подход:
- В CMakeLists.txt определяем переменную содержащую список языков поддерживаемых приложением:
set (LANGUAGES rus eng)
- ts и qm файлы будут называться язык.ts и язык.qm, соответственно. Формируем данный список и тут же добавляем команды обновляющие qm-файл при изменении ts-файла:
foreach (LANGUAGE ${LANGUAGES}) set (TS ${TRANSLATIONS_PATH}/${LANGUAGE}.ts) set (QM ${TRANSLATIONS_PATH}/${LANGUAGE}.qm) set (TRANSLATIONS ${TRANSLATIONS} ${TS}) set (TRANSLATIONS_BINARY ${TRANSLATIONS_BINARY} ${QM}) add_custom_command ( OUTPUT ${QM} COMMAND ${QT_LRELEASE_EXECUTABLE} ${TS} MAIN_DEPENDENCY ${TS}) endforeach()
- Добавляем зависимость проекта от файлов перевода:
add_executable (${PROJECT} ${HEADERS} ${SOURCES} ${MOC_SOURCES} ${QRC_SOURCES} ${TRANSLATIONS})
- Добавляем отдельную цель create_translations необходимую для обновления ts-файлов. Отдельная цель сделана для того, чтобы не вызывать утилиту обновления ts-файлов при каждой сборке проекта и защитить ts-файл от неосторожного удаления при запуске make clean.
add_custom_target ( translations COMMAND ${QT_LUPDATE_EXECUTABLE} ${HEADERS} ${SOURCES} -ts ${TRANSLATIONS}) add_custom_command ( TARGET translations COMMAND ${QT_LRELEASE_EXECUTABLE} ${TRANSLATIONS})
Что получилось
cmake_minimum_required (VERSION 2.6) set (PROJECT demo) set (HEADERS ../main_window.h) set (SOURCES ../main_window.cpp ../main.cpp) set (LIBRARIES library) set (LANGUAGES rus eng) set (RESOURCE_PATH ../resources) set (RESOURCES ${RESOURCE_PATH}/resources.qrc) set (TRANSLATIONS_PATH ../resources/translations) project (${PROJECT}) include_directories (../) find_package (Qt4 REQUIRED) include (${QT_USE_FILE}) qt4_add_resources (QRC_SOURCES ${RESOURCES}) qt4_wrap_cpp (MOC_SOURCES ${HEADERS}) foreach (LANGUAGE ${LANGUAGES}) set (TS ${TRANSLATIONS_PATH}/${LANGUAGE}.ts) set (QM ${TRANSLATIONS_PATH}/${LANGUAGE}.qm) set (TRANSLATIONS ${TRANSLATIONS} ${TS}) set (TRANSLATIONS_BINARY ${TRANSLATIONS_BINARY} ${QM}) add_custom_command ( OUTPUT ${QM} COMMAND ${QT_LRELEASE_EXECUTABLE} ${TS} MAIN_DEPENDENCY ${TS}) endforeach() add_custom_target ( translations COMMAND ${QT_LUPDATE_EXECUTABLE} ${HEADERS} ${SOURCES} -ts ${TRANSLATIONS}) add_custom_command ( TARGET translations COMMAND ${QT_LRELEASE_EXECUTABLE} ${TRANSLATIONS}) foreach (LIBRARY ${LIBRARIES}) add_subdirectory (../${LIBRARY}/build bin/${LIBRARY}) endforeach () if (MSVC) add_definitions (/W3) elseif (CMAKE_COMPILER_IS_GNUCXX) add_definitions (-Wall -pedantic) else () message ("Unknown compiler") endif () source_group ("Header Files" FILES ${HEADERS}) source_group ("Source Files" FILES ${SOURCES}) source_group ("Generated Files" FILES ${MOC_SOURCES}) source_group ("Resource Files" FILES ${QRC_SOURCES}) add_executable (${PROJECT} ${HEADERS} ${SOURCES} ${MOC_SOURCES} ${QRC_SOURCES} ${TRANSLATIONS}) target_link_libraries (${PROJECT} ${LIBRARIES} ${QT_LIBRARIES}) |
Последовательность сборки проекта
При применении предложенного подхода процесс сборки проекта выглядит следующим образом:
- Получаем код проекта со всеми зависимостями из репозитория
- Переходим в директорию build и выполняем команду
cmake CMakeLists.txt
- Создаем qm-файлы командой
make translations
или открыв созданный проект с своей среде разработки и собрав подпроект translations
- Собираем приложение выполнив
make
или выбрав команду build в своей среде разработки
Шаблон проекта с использование библиотеки Qt и системы сборки CMake
Для удобства я написал шаблон для приложений использующих CMake. Он содержит несколько директорий, файл CMakeLists.txt, пустой ts-файл (default.ts) и resources.qrc. Для использования вам надо будет добавить в CMakeLists.txt путь к файлам с кодом, указать используемые библиотеки и языки. Все переменные вынесены в начало файла и выглядят следующим образом:
set (PROJECT имя_проекта) set (HEADERS заголовочные_файлы) set (SOURCES файлы_с_реализацией) set (LIBRARIES директории_с_библиотеками_проекта) set (LANGUAGES список_языков) |
Также необходимо добавить список файлов с ресурсами в resources.qrc и создать из пустого ts-файла столько файлов, сколько языков в списке, назвав их язык.ts
Убираем консольное окно в Windows
Если не приложить определенных усилий, приложение под Windows будет запускаться с консольным окном, что удобно при отладке, но не нужно в финальной версии. Чтобы его убрать нужно сделать две вещи:
- В команду add_executable добавить WIN32 после ${PROJECT} (на платформах отличных от Windows, это игнорируется, так что можно использовать смело не только под Windows)
- В список линкуемых библиотек target_link_libraries добавить ${QT_QTMAIN_LIBRARY}
Спасибо большое за стать. и за шаблон. Его обязательно попробую заюзать.Как раз стоит задача заюзать cmake вместо qmake. Да и троли вроде бы обещали перейти на cmake
Vernat
18 февраля 10 14:53
А как задать пути, в которых необходимо искать файлы? Я не хочу в перечислении файлов указывать у каждого файла путь.QMake это позволяет.Уверен что CMake тоже, не подскажите?!
Vernat
18 февраля 10 17:00
Вот так:
file(GLOB HEADERS ../*.h)
file(GLOB SOURCES ../*.cpp)
add_executable(${PROJECT} ${HEADERS} ${SOURCES})
Но на мой взгляд это в корне неверно, так как сиюминутное удобство может обернуться подключением ненужных файлов в сложном проекте.
Максим Тремпольцев
18 февраля 10 21:05
В шаблоне и в заметке не хватате про обработку ui форм, созданных qt-designer (qt4_wrap_ui)
Vernat
18 февраля 10 17:47
Да я их как-то не использую.. Не прижились они — руками проще.
Максим Тремпольцев
18 февраля 10 21:06
Когда сам делаешь , то наверно да, руками проще, а когда тебе достается чужой код и там весь ui сделан «типа ручками» не пойми как, то лучше ui от qt-designer-а.
Я тут поискал, оказалось просто :
прописываем формы set
( FORMS ../ui/mainwindow.ui )
подключаем текущую директорию в инклюдники, потому что сюда свалятся сгенеренные из *.ui ашники
include_directories(
${CMAKE_CURRENT_BINARY_DIR}
)
после генерации moc-файлов вставляем эту для генерации хедеров
qt4_wrap_ui( UI_HEADERS ${FORMS} )
и в саму цель добавить ${UI_HEADERS} ,будет выглядеть вот так:
add_executable (${PROJECT} ${UI_HEADERS} ${HEADERS} ${SOURCES} ${MOC_SOURCES} ${QRC_SOURCES} ${TRANSLATIONS})
И у меня все заработало на Вашем подправленном шаблоне. Большое спасибо!
Vernat
18 февраля 10 21:41
Спасибо за дополнение!
Максим Тремпольцев
18 февраля 10 21:44
Как говорит автор похожего программерского блога:»Идеи глупо держать под подушкой, там они гниют и тухнут». Я думаю это можно отнести не только к идеям, но и ко всем знаниям.
Vernat
18 февраля 10 22:40
А подскажи, плз, как сделать tак, что бы цель собиралась с ключами отладки, к примеру -g (-g2)
Serge
2 марта 10 15:01
Чтобы собрать с отладочной информацией надо выполнить:
cmake CMakeLists.txt -DCMAKE_BUILD_TYPE=debug
и соответственно без
cmake CMakeLists.txt -DCMAKE_BUILD_TYPE=release
Дополнительные ключи компилятору устанавливаются командой add_definitions()
Максим Тремпольцев
2 марта 10 21:28
А есть ли способ автоматически построить файл .TS по исходникам, использующим макрос (функцию?) tr()?
Спасибо!
Денис
4 марта 10 1:25
Есть такой способ — можно написать цель которая будет вызывать lupdate список_исходников -ts имя.ts, в результате чего будет создан ts-файл. А можно просто создать пустой ts-файл, а потом обновлять его методом описанным в статье.
Максим Тремпольцев
4 марта 10 9:55
>Да я их как-то не использую.. Не прижились они – руками проще.
Здравствуйте, Максим.
Если не использовать *.ui, то получается, что файл *.h будет напичкан include’ами для каждого интерфейсного элемента? Или тут тоже какая-то хитрость:)?
Спасибо, заранее за ответ.
Иван Русских
10 марта 10 8:54
Иван, в *.h пишем предварительные объявления, например:
class QPushButton;
class QLabel;
А уже в *.cpp можно одним махом подключить все:
#include <QtGui>
Максим Тремпольцев
10 марта 10 9:47
не подскажите, как указать директорию, куда будет складываться готовый бинарник?
Vernat
7 апреля 10 13:45
Для этого существует переменная EXECUTABLE_OUTPUT_PATH для исполняемых файлов и LIBRARY_OUTPUT_PATH для библиотек:
set (EXECUTABLE_OUTPUT_PATH ../bin)
Помимо этого для подключаемых подпроектов директория с бинарниками указывается прямо в команде:
add_subdirectory (../cool_library/build ../bin/cool_library)
Максим Тремпольцев
7 апреля 10 20:58
Добрый день максим! В qmake есть возможность просто подрубать подпроекты через include(…/*.pri), причем там могут быть тупо перечислены файлы (содержимое переменных подпроекта плюсуется к переменным главного проекта), либо либа, есть ли в cmake подобный элегантный способ, чтобы заюзать эти самые pri файлы либо их содержимое?
p.s может форум открыть?
Vernat
27 мая 10 16:33
Извините пожалуйста за ошибку в написании имени. Неплохо бы редактирование сообщений ввести
Vernat
27 мая 10 20:05
а что делать, когда Qt не лежит в дефолтных путях, а, например, собран у себя в хомяке (точнее стоит Qt4SDK).
сперва он вообще не находил, но после устанавливки: set (QT4_DIR /home/…./qt4/gcc)
стал выдавать:
====
Warning: QT_QMAKE_EXECUTABLE reported QT_INSTALL_LIBS as /usr/lib
Warning: But QtCore couldn’t be found. Qt must NOT be installed correctly, or it wasn’t found for cross compiling.
CMake Error at /usr/share/cmake-2.8/Modules/FindQt4.cmake:639 (MESSAGE):
Could NOT find QtCore.
====
cap
29 июля 11 19:34
Достаточно сделать чтобы был доступен qmake, самое простое — в /bin/ сделать символьную ссылку на него. После этого, если Qt был собран правильно, все будет работать.
Максим Тремпольцев
29 июля 11 19:58
одна (старая) версия Qt стоит из пакетов в /bin и /usr/lib, но она не нужна для проекта, а нужна та, которая была с установщиком QtSDK. А с последней никак не может справиться.
cap
2 августа 11 14:27
Здравствуйте.
Сразу скажу — впервые «вылезаю из-под windows-одеяла» ))
Так что продукты вроде autotools, cmake для меня в новинку.
Интересный момент при сборке для MSVC2010.
Этап генерации проходит гладко, если не считать сообщений типа
=========
Could not copy from: C:/Program Files/CMake 2.8/share/cmake-2.8/Templates/CMakeVSMacros2.vsmacros
to: C:/Documents and Settings/Admin/?юш фюъєьхэЄ?/Visual Studio 2010/Projects/VSMacros80/CMakeMacros/CMakeVSMacros2.vsmacros
=========
Я так понял, что cmake не в состоянии прочитать русское название каталога «Мои документы».
Тем не менее, Makefile генерится, и без проблем воспринимается IDE MSVC2010.
Но на этапе сборки в конфигурации Release пропадает картинка! ПРичем в Debug она присутствует.
Это как так???
Valerius
12 мая 12 7:03
В первом случае — не факт. Возможно просто окно вывода студии не может нормально отобразить русский текст, а проблема с правами на запись в директорию Admin. Надо смотреть.
Во втором случае, еще сложней что-либо посоветовать — мало информации. В каком формате картинка? Если, например gif, то надо смотреть подгружаются ли плагины.
Максим Тремпольцев
12 мая 12 10:04
>> В первом случае – не факт. …
Вряд ли. Речь идет о консольном окне локализованной винды. Русские буквы в нем нормально отображаются. Прав на запись в каталог там особых не требуется. Я через консольку дошел до каталога «Templates/», чередуя русские и английские нащвания в пути и проверил на запись, все в порядке.
>> Во втором случае, еще сложней что-либо посоветовать …
Речь идет о Вашем примере, размещенном в данной статье. Картинка там в гифе.
Компиляция идет без проблем, сборка тоже, причем, собираю и через ИДЕ, и посредством cmake —build -config Release / cmake —build -config Debug.
В обоих случаях в дебаге на форме отображен «робот с сердечком», а в релизе — нет.
Valerius
12 мая 12 14:18
Вот что я сделал для проверки подгрузки плагинов.
Я вообще использовал вместо gif и QMovie простенький jpg и QPixmap.
Проект, собранный qmake’ом в IDE QtCreator показал картинку и в дебуге и в релизе.
Проект, собранный CMake под MSVC-2010 так и не отображает картинку в Release-сборке.
Не могу пока разобраться, что за ерунда…
Valerius
12 мая 12 15:02
Ладно. Забил я на кривость [s]своих рук[/s]/CMakeLists.txt для MSVC-2010.
Пробую собрать другим генератором, как описано у Вас, а именно
cmake -G «MinGW Makefiles»
На этом шаге никаких ошибок.
Однако, на шаге
«Создаем qm-файлы командой make translations»
почему-то подключается MAKE от Borland, и ничего хорошего не происходит.
Походу, Максим, с учетом предыдущих корявок, описанных мной, никакой универсальности у Вашего рецепта (((
Valerius
4 июня 12 14:31
Здравствуйте.
Никто не подскажет, как в cmake задать такие переменные *.pro файла как VERSION, QMAKE_TARGET_COMPANY и т.д.
Нужно это для того, чтобы в Windows в свойствах файла отображалась корректная и актуальная информация.
Если кто-то умеет вместо этого использовать *.rc файл, тоже буду рад пояснениям.
Спасибо.
Александр
6 октября 15 12:57
Mercedes
MichaelBib
1 августа 19 5:30
Посоветуйте поставщика строительной техники, по всем предложениям пишите на почту. Заранее благодарны за вашу помощь!
Mpkrazvitienet
31 мая 20 20:19