Qt для macOS — Особенности
На этой странице описаны основные проблемы, связанные с поддержкой macOS в Qt. Терминология macOS и конкретные процессы можно найти на сайте https://developer.apple.com/.
Aqua
Aqua — неотъемлемая часть платформы macOS. Как и Cocoa и Carbon, Qt предоставляет виджеты, которые выглядят так же, как описано в описаниях человеческого интерфейса. Виджеты Qt используют HIThemes для реализации внешнего вида. Другими словами, мы используем собственные API Apple для рендеринга. Дополнительную документацию об Aqua можно найти в Руководстве по разработке пользовательского интерфейса macOS.
На странице Галерея виджетов Qt содержатся примеры изображений приложений, использующих тему платформы macOS.
Атрибуты Qt для macOS
В следующем списке представлены набор полезных атрибутов, которые можно использовать для настройки приложений на macOS:
- Qt::AA_MacPluginApplication
- Qt::AA_DontUseNativeMenuBar
- Qt::AA_MacDontSwapCtrlAndMeta
- Qt::WA_MacNoClickThrough
- Qt::WA_MacOpaqueSizeGrip
- Qt::WA_MacShowFocusRect
- Qt::WA_MacNormalSize
- Qt::WA_MacSmallSize
- Qt::WA_MacMiniSize
- Qt::WA_MacVariableSize
- Qt::WA_MacBrushedMetal
- Qt::WA_MacAlwaysShowToolWindow
- Qt::WA_MacFrameworkScaled
- Qt::WA_MacNoShadow
- Qt::Sheet
- Qt::Drawer
- Qt::MacWindowToolBarButtonHint,
- QMainWindow::unifiedTitleAndToolBarOnMac
macOS всегда выполняет двойную буферизацию экрана, поэтому атрибут Qt::WA_PaintOnScreen не оказывает никакого эффекта. Также невозможно рисовать вне события рисования, поэтому Qt::WA_PaintOutsidePaintEvent также не оказывает никакого эффекта.
Правый щелчок мыши
Класс QContextMenuEvent обеспечивает поддержку правого щелчка мыши для приложений macOS. Это будет сопоставлено с событием контекстного меню, например, меню, которое отобразит всплывающее меню выбора. Это наиболее распространённое использование правого щелчка мыши и соответствует щелчку с нажатой клавишей Control на мышах macOS с поддержкой одноклавишной мыши.
Панель меню
Qt обнаруживает панели меню и преобразует их в родные панели меню Mac. Встраивание в существующие приложения Qt обычно происходит автоматически. Однако, если у вас есть особые потребности, текущая реализация Qt выбирает панель меню, начиная с активного окна (например, QGuiApplication::focusWindow()) и применяет следующие тесты:
- Если окно содержит QMenuBar, то оно используется.
- Если окно является модальным, то используется его панель меню. Если панель меню не указана, используется стандартная панель меню (как описано ниже).
- Если у окна нет родителя, используется стандартная панель меню (как описано ниже).
Эти тесты выполняются по всей цепочке родительских окон до тех пор, пока не будет удовлетворено одно из вышеперечисленных правил. Если ничего не подходит, создаётся стандартная панель меню. Стандартная панель меню Qt — это пустая панель меню. Однако вы можете создать другую стандартную панель меню, создав QMenuBar без родительского окна. Первое созданное окно будет назначено стандартной панелью меню и будет использоваться всякий раз, когда требуется стандартная панель меню.
Использование родных панелей меню вводит определённые ограничения для классов Qt. В разделе с списком ограничений ниже представлена дополнительная информация.
Qt поддерживает глобальную панель меню с помощью QMenuBar. Пользователи macOS ожидают видеть панель меню в верхней части экрана, и Qt это поддерживает.
Кроме того, ожидается соблюдение определённых соглашений, например, меню приложения должно содержать пункты О программе, Настройки, Выход и т. д. Qt обрабатывает эти соглашения, но не предоставляет механизма прямого взаимодействия с меню приложения.
Каждый QAction имеет свойство menuRole, которое управляет особым расположением элементов меню приложения; однако по умолчанию menuRole это TextHeuristicRole, что означает, что элементы меню будут автоматически распознаваться по их тексту.
Другие стандартные пункты меню, такие как Вырезать, Копировать, Вставить и Выделить всё, применимы как в вашем приложении, так и в некоторых встроенных диалогах, таких как QFileDialog. Важно создавать эти пункты меню со стандартными сочетаниями клавиш, чтобы соответствующие функции редактирования были включены в диалогах. В настоящее время для них нет идентификаторов MenuRole, но они будут распознаваться автоматически так же, как элементы меню приложения, когда QAction имеет значение по умолчанию TextHeuristicRole.
Специальные клавиши
Для обеспечения ожидаемого поведения приложений Qt на macOS значения перечислений Qt::Key_Meta, Qt::MetaModifier и Qt::META соответствуют клавишам Control на стандартной клавиатуре Apple, а значения перечислений Qt::Key_Control, Qt::ControlModifier и Qt::CTRL — клавишам Command.
Панель задач
Взаимодействие с панелью задач возможно. Иконка может быть установлена вызовом QWindow::setWindowIcon() из главного окна вашего приложения. Вызов setWindowIcon() может быть выполнен столько раз, сколько нужно, для обеспечения лёгкого обновления иконки.
Доступность
Многие пользователи взаимодействуют с macOS с помощью вспомогательных устройств. Qt стремится автоматизировать эту функциональность в вашем приложении, чтобы оно соответствовало принятой практике на данной платформе. Qt использует фреймворк доступности Apple для обеспечения доступа для пользователей с ограниченными возможностями.
Поддержка библиотек и развертывания
Qt поддерживает такие структуры macOS, как фреймворки и пакеты. Важно понимать эти структуры, так как они непосредственно влияют на развертывание приложений.
Qt предоставляет инструмент развертывания macdeployqt для упрощения процесса развертывания. Статья Qt для macOS — Развертывание более подробно описывает процесс развертывания.
Библиотеки Qt как фреймворки
По умолчанию Qt собирается как набор фреймворков. Фреймворки — предпочтительный способ распространения библиотек на macOS. На сайте Руководство Apple по программированию фреймворков есть гораздо больше информации о фреймворках.
Важно помнить, что фреймворки всегда связываются с релизными версиями библиотек. Если требуется отладочная версия фреймворка Qt, используйте переменные среды DYLD_IMAGE_SUFFIX, чтобы убедиться, что загружается отладочная версия:
export DYLD_IMAGE_SUFFIX=_debug
В качестве альтернативы вы можете временно поменять местами отладочные и релизные версии, что описано в техническом примечании Apple «Магия отладки».
Если вы не хотите использовать фреймворки, просто настройте Qt с -no-framework.
./configure -no-framework
Библиотеки, основанные на пакетах
Если вы хотите использовать динамические библиотеки в пакете приложения macOS (директория приложения), создайте поддиректорию с именем Frameworks в директории пакета приложения и поместите туда свои динамические библиотеки. Приложение найдёт динамическую библиотеку, если её имя установки будет @executable_path/../Frameworks/libname.dylib.
Если вы используете qmake и Makefiles, используйте настройку QMAKE_LFLAGS_SONAME:
QMAKE_LFLAGS_SONAME = -Wl,-install_name,@executable_path/../Frameworks/
В качестве альтернативы вы можете изменить имя установки с помощью install_name_tool(1) в командной строке.
Переменная среды DYLD_LIBRARY_PATH переопределит эти настройки и все другие пути по умолчанию, такие как поиск динамических библиотек в /usr/lib и аналогичных стандартных расположениях.
Если вы используете более старые версии GDB, вы должны запустить программу с полным путём к исполняемому файлу. Более новые версии позволяют передавать имя пакета в командной строке.
Объединение библиотек
Если вы хотите создать новую динамическую библиотеку, объединяющую динамические библиотеки Qt 4, вам необходимо добавить флаг ld -r. Затем информация о перемещении сохраняется в выходном файле, поэтому этот файл может стать объектом следующей операции ld. Это делается путём установки флага -r в файле .pro и настройках LFLAGS.
Порядок инициализации
dyld(1) вызывает глобальные статические инициализаторы в порядке их добавления в приложение. Если библиотека ссылается на Qt и использует глобальные переменные в Qt (из глобальных инициализаторов в вашей собственной библиотеке), подключите приложение к Qt перед подключением его к библиотеке. В противном случае результат будет неопределённым, потому что глобальные инициализаторы Qt ещё не были вызваны.
Флаги времени компиляции
Следующие флаги полезны, когда вы хотите определить код, специфичный для macOS:
-
Q_OS_DARWINопределён, когда Qt обнаруживает, что вы находитесь на системе на базе Darwin, такой как macOS или iOS. -
Q_OS_MACOSопределён, когда вы находитесь на системе macOS.
Примечание: Q_WS_MAC больше не определён в Qt 5.
Если вы хотите определить код для определённых версий macOS, используйте макросы доступности, определённые в /usr/include/AvailabilityMacros.h.
Документация к QSysInfo содержит информацию о проверке версии во время выполнения.
Доступ к родным API macOS
Доступ к пути пакета
Приложения macOS структурированы как директория (заканчивающаяся на .app). Эта директория содержит поддиректории и файлы. Может быть полезно поместить элементы, такие как плагины и онлайн-документацию, внутри этого пакета. Следующий код возвращает путь к пакету приложения:
#ifdef Q_OS_MAC
CFURLRef appUrlRef = CFBundleCopyBundleURL(CFBundleGetMainBundle());
CFStringRef macPath = CFURLCopyFileSystemPath(appUrlRef,
kCFURLPOSIXPathStyle);
const char *pathPtr = CFStringGetCStringPtr(macPath,
CFStringGetSystemEncoding());
qDebug("Path = %s", pathPtr);
CFRelease(appUrlRef);
CFRelease(macPath);
#endif Примечание: Когда macOS настроена на использование японского языка, из-за ошибки эта последовательность может завершиться неудачно и вернуть пустую строку. Поэтому всегда проверяйте возвращённую строку.
Дополнительную информацию об использовании API CFBundle см. на сайте разработчиков Apple.
QCoreApplication::applicationDirPath() можно использовать для определения пути к исполняемому файлу в пакете.
Перевод меню приложения и системных диалогов
Элементы меню приложения будут правильно объединены для локализованных приложений, но не будут отображаться как переведённые, пока пакет приложения не будет содержать папку с локализованными ресурсами. к пакету приложения.
В сущности, необходим файл с именем locversion.plist. Вот пример приложения с норвежской локализацией:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>LprojCompatibleVersion</key>
<string>123</string>
<key>LprojLocale</key>
<string>no</string>
<key>LprojRevisionLevel</key>
<string>1</string>
<key>LprojVersion</key>
<string>123</string>
</dict>
</plist> После этого, при запуске приложения с выбранным языком норвежский, элементы меню должны отображаться как Avslutt вместо Quit.
Руководство по программированию пакетов содержит информацию о пакетах и папке с локализованными ресурсами.
Использование Qt с кодом нативными языками
Для добавления нативных виджетов Cocoa и элементов управления в приложение Qt, или для встраивания Qt в нативное приложение Cocoa, доступны два класса: QMacCocoaViewContainer и QMacNativeWidget.
Использование нативных панелей Cocoa
Обработчик событий Qt более гибкий, чем предлагаемый Cocoa, и позволяет пользователю запускать обработчик событий (и выполнение QEventLoop::exec) без необходимости задумываться о том, отображаются ли модальные диалоговые окна на экране (в отличие от Cocoa). Поэтому нам нужно реализовать дополнительное управление в Qt для правильной обработки этого, что, к сожалению, затрудняет использование нативных панелей. На данный момент лучший способ сделать это — следовать приведенной ниже схеме, где мы отправляем вызов функции нативному коду, а не вызываем её напрямую. Таким образом, мы знаем, что Qt успешно обновил любые ожидающие рекурсии цикла событий перед отображением нативной панели:
#include <QtGui>
class NativeProxyObject : public QObject
{
Q_OBJECT
public slots:
void execNativeDialogLater()
{
QMetaObject::invokeMethod(this, "execNativeDialogNow", Qt::QueuedConnection);
}
void execNativeDialogNow()
{
NSRunAlertPanel(@"A Native dialog", @"", @"OK", @"", @"");
}
};
#include "main.moc"
int main(int argc, char **argv){
QApplication app(argc, argv);
NativeProxyObject proxy;
QPushButton button("Show native dialog");
QObject::connect(&button, SIGNAL(clicked()), &proxy, SLOT(execNativeDialogLater()));
button.show();
return app.exec();
} Ограничения
MySQL и macOS
По-видимому, возникает проблема, когда оба -prebind и -multi_module определены при связывании статических C библиотек с динамическими библиотеками. Если при связывании Qt вы получаете следующее сообщение об ошибке:
ld: common symbols not allowed with MH_DYLIB output format with the -multi_module option /usr/local/mysql/lib/libmysqlclient.a(my_error.o) definition of common _errbuff (size 512) /usr/bin/libtool: internal link edit command failed
пересвяжите Qt с помощью -single_module. Это проблема только при построении драйвера MySQL в Qt. Это не влияет на плагины или статические сборки.
D-Bus и macOS
Модуль QtDBus по умолчанию динамически загружает библиотеку libdbus-1 на macOS. Это означает, что приложения, связанные с модулем QtDBus, будут загружаться даже на системах macOS, на которых этих библиотек нет, но они не смогут подключиться к любому серверу D-Bus и не смогут открыть сервер с помощью QDBusServer.
Для использования функциональности D-Bus необходимо установить библиотеку libdbus-1, например, через Homebrew, Fink или MacPorts. Возможно, вам захочется включить эти библиотеки в пакет вашего приложения, если вы планируете развертывать его на других системах. Кроме того, обратите внимание, что на macOS нет системного шины, а шина сеанса будет запущена только после того, как launchd будет настроен для её управления.
Действия в меню
- Действия в QMenu с акцелеротами, имеющими более одного нажатия клавиши (QKeySequence), не будут отображаться корректно, когда QMenu переводится в нативное меню Mac. Будет отображена только первая клавиша. Однако, ярлык всё равно будет активироваться, как и на всех других платформах.
- Объекты QMenu, используемые в нативном меню, не могут обрабатывать события Qt через обычные обработчики событий. Установите делегата на сам объект меню, чтобы получать уведомления об этих изменениях. В качестве альтернативы рассмотрите использование сигналов QMenu::aboutToShow() и QMenu::aboutToHide() для отслеживания видимости меню; это предоставляет решение, которое должно работать на всех платформах, поддерживаемых Qt.
- По умолчанию Qt создаёт нативный элемент меню Quit, который будет реагировать на ярлык
CMD+Q. Создание QAction для роли QAction::QuitRole заменит этот элемент меню. Поэтому действие-замена должно быть подключено либо к слоту QCoreApplication::quit, либо к пользовательскому слоту, который останавливает приложение.
Нативные виджеты
Qt поддерживает вкладки, представленные флагом окна Qt::Sheet.
Обычно, когда речь идёт о нативном приложении macOS, нативное означает приложение, которое взаимодействует напрямую с базовой системой окон, а не с каким-то промежуточным слоем. Приложения Qt работают как полноправные пользователи, как приложения Cocoa и Carbon. Мы используем Cocoa во внутреннем взаимодействии с операционной системой.
© The Qt Company Ltd
Licensed under the GNU Free Documentation License, Version 1.3.
https://doc.qt.io/qt-5.13/macos-issues.html